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.cpp 6.8KB

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