C++ binding for the SQLite library https://code.ireas.org/sqlitepp/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sqlitepp.cc 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright (C) 2014--2015 Robin Krahl <robin.krahl@ireas.org>
  2. // MIT license -- http://opensource.org/licenses/MIT
  3. #include "sqlitepp/sqlitepp.h"
  4. #include <exception>
  5. #include <iostream>
  6. #include <sstream>
  7. #include <string>
  8. namespace sqlitepp {
  9. Openable::Openable(const bool open, const std::string& name)
  10. : m_open(open), m_name(name) {
  11. }
  12. bool Openable::isOpen() const {
  13. return m_open;
  14. }
  15. void Openable::requireOpen() const {
  16. if (!m_open) {
  17. throw std::logic_error(m_name + " is not open.");
  18. }
  19. }
  20. void Openable::setOpen(const bool open) {
  21. m_open = open;
  22. }
  23. std::string DatabaseError::getErrorMessage(const int errorCode,
  24. const std::string& errorMessage) {
  25. std::ostringstream stringStream;
  26. stringStream << "Caught SQLite3 error " << errorCode << " meaning: "
  27. << errorMessage;
  28. return stringStream.str();
  29. }
  30. DatabaseError::DatabaseError(const int errorCode)
  31. : DatabaseError(errorCode, sqlite3_errstr(errorCode)) {
  32. }
  33. DatabaseError::DatabaseError(const int errorCode,
  34. const std::string& errorMessage)
  35. : std::runtime_error(getErrorMessage(errorCode, errorMessage)),
  36. m_errorCode(errorCode) {
  37. }
  38. int DatabaseError::errorCode() const {
  39. return m_errorCode;
  40. }
  41. Statement::Statement(sqlite3_stmt* handle)
  42. : Openable(true, "Statement"), m_canRead(false), m_handle(handle) {
  43. }
  44. Statement::~Statement() {
  45. if (isOpen()) {
  46. // errors that could occur during finalizing are ignored as they have
  47. // already been handled!
  48. sqlite3_finalize(m_handle);
  49. setOpen(false);
  50. }
  51. }
  52. void Statement::bind(const int index, const double value) {
  53. requireOpen();
  54. handleBindResult(index, sqlite3_bind_double(m_handle, index, value));
  55. }
  56. void Statement::bind(const std::string& name, const double value) {
  57. bind(getParameterIndex(name), value);
  58. }
  59. void Statement::bind(const int index, const int value) {
  60. requireOpen();
  61. handleBindResult(index, sqlite3_bind_int(m_handle, index, value));
  62. }
  63. void Statement::bind(const std::string& name, const int value) {
  64. bind(getParameterIndex(name), value);
  65. }
  66. void Statement::bind(const int index, const std::string& value) {
  67. requireOpen();
  68. handleBindResult(index, sqlite3_bind_text(m_handle, index, value.c_str(),
  69. value.size(), NULL));
  70. }
  71. void Statement::bind(const std::string& name, const std::string& value) {
  72. bind(getParameterIndex(name), value);
  73. }
  74. ResultSet Statement::execute() {
  75. step();
  76. return ResultSet(m_instancePointer.lock());
  77. }
  78. void Statement::requireCanRead() const {
  79. if (!m_canRead) {
  80. throw std::logic_error("Trying to read from statement without data");
  81. }
  82. }
  83. void Statement::setInstancePointer(
  84. const std::weak_ptr<Statement>& instancePointer) {
  85. m_instancePointer = instancePointer;
  86. }
  87. bool Statement::step() {
  88. requireOpen();
  89. int result = sqlite3_step(m_handle);
  90. if (result == SQLITE_ROW) {
  91. m_canRead = true;
  92. } else if (result == SQLITE_DONE) {
  93. m_canRead = false;
  94. } else {
  95. throw DatabaseError(result);
  96. }
  97. return m_canRead;
  98. }
  99. void Statement::close() {
  100. if (isOpen()) {
  101. // errors that could occur during finalizing are ignored as they have
  102. // already been handled!
  103. sqlite3_finalize(m_handle);
  104. setOpen(false);
  105. }
  106. }
  107. bool Statement::reset() {
  108. requireOpen();
  109. return sqlite3_reset(m_handle) == SQLITE_OK;
  110. }
  111. int Statement::getParameterIndex(const std::string& name) const {
  112. requireOpen();
  113. int index = sqlite3_bind_parameter_index(m_handle, name.c_str());
  114. if (index == 0) {
  115. throw std::invalid_argument("No such parameter: " + name);
  116. }
  117. return index;
  118. }
  119. void Statement::handleBindResult(const int index, const int result) const {
  120. switch (result) {
  121. case SQLITE_OK:
  122. break;
  123. case SQLITE_RANGE:
  124. throw std::out_of_range("Bind index out of range: " + index);
  125. case SQLITE_NOMEM:
  126. throw std::runtime_error("No memory to bind parameter");
  127. default:
  128. throw DatabaseError(result);
  129. }
  130. }
  131. Database::Database() : Openable(false, "Database") {
  132. }
  133. Database::Database(const std::string & file) : Database() {
  134. open(file);
  135. }
  136. Database::~Database() {
  137. if (isOpen()) {
  138. sqlite3_close(m_handle);
  139. setOpen(false);
  140. }
  141. // m_handle is deleted by sqlite3_close
  142. }
  143. void Database::close() {
  144. if (isOpen()) {
  145. int result = sqlite3_close(m_handle);
  146. if (result == SQLITE_OK) {
  147. setOpen(false);
  148. } else {
  149. throw sqlitepp::DatabaseError(result);
  150. }
  151. }
  152. }
  153. void Database::execute(const std::string& sql) {
  154. requireOpen();
  155. std::shared_ptr<Statement> statement = prepare(sql);
  156. statement->step();
  157. }
  158. void Database::open(const std::string& file) {
  159. if (isOpen()) {
  160. throw std::logic_error("sqlitepp::Database::open(std::string&): "
  161. "Database already open");
  162. }
  163. int result = sqlite3_open(file.c_str(), &m_handle);
  164. if (m_handle == NULL) {
  165. throw std::runtime_error("sqlitepp::Database::open(std::string&): "
  166. "Can't allocate memory");
  167. }
  168. if (result == SQLITE_OK) {
  169. setOpen(true);
  170. } else {
  171. std::string errorMessage = sqlite3_errmsg(m_handle);
  172. sqlite3_close(m_handle);
  173. throw sqlitepp::DatabaseError(result, errorMessage);
  174. }
  175. }
  176. std::shared_ptr<Statement> Database::prepare(const std::string& sql) {
  177. requireOpen();
  178. sqlite3_stmt* statementHandle;
  179. int result = sqlite3_prepare_v2(m_handle, sql.c_str(), sql.size(),
  180. &statementHandle, NULL);
  181. if (result != SQLITE_OK) {
  182. throw DatabaseError(result, sqlite3_errmsg(m_handle));
  183. }
  184. if (statementHandle == NULL) {
  185. throw std::runtime_error("Statement handle is NULL");
  186. }
  187. auto statement = std::shared_ptr<Statement>(new Statement(statementHandle));
  188. statement->setInstancePointer(std::weak_ptr<Statement>(statement));
  189. return statement;
  190. }
  191. ResultSet::ResultSet(const std::shared_ptr<Statement> statement)
  192. : m_statement(statement) {
  193. }
  194. bool ResultSet::canRead() const {
  195. return m_statement->m_canRead;
  196. }
  197. int ResultSet::columnCount() const {
  198. m_statement->requireOpen();
  199. m_statement->requireCanRead();
  200. return sqlite3_column_count(m_statement->m_handle);
  201. }
  202. double ResultSet::readDouble(const int column) const {
  203. m_statement->requireOpen();
  204. m_statement->requireCanRead();
  205. return sqlite3_column_double(m_statement->m_handle, column);
  206. }
  207. int ResultSet::readInt(const int column) const {
  208. m_statement->requireOpen();
  209. m_statement->requireCanRead();
  210. return sqlite3_column_int(m_statement->m_handle, column);
  211. }
  212. std::string ResultSet::readString(const int column) const {
  213. m_statement->requireOpen();
  214. m_statement->requireCanRead();
  215. return std::string((const char*) sqlite3_column_text(m_statement->m_handle,
  216. column));
  217. }
  218. bool ResultSet::next() {
  219. return m_statement->step();
  220. }
  221. } // namespace sqlitepp