ROSE  0.11.2.0
DatabaseSqlite.h
1 // WARNING: Changes to this file must be contributed back to Sawyer or else they will
2 // be clobbered by the next update from Sawyer. The Sawyer repository is at
3 // https://github.com/matzke1/sawyer.
4 
5 
6 
7 
8 #ifndef Sawyer_DatabaseSqlite_H
9 #define Sawyer_DatabaseSqlite_H
10 
11 #if __cplusplus >= 201103L
12 
13 #include <Sawyer/Database.h>
14 
15 #include <boost/filesystem.hpp>
16 #include <boost/lexical_cast.hpp>
17 #include <Sawyer/Optional.h>
18 #include <sqlite3.h>
19 #include <string>
20 
21 #if SQLITE_VERSION_NUMBER < 3007015
22 std::string sqlite3_errstr(int status) {
23  return "sqlite3 error " + boost::lexical_cast<std::string>(status);
24 }
25 #endif
26 
27 namespace Sawyer {
28 namespace Database {
29 
31 class Sqlite: public Connection {
32 public:
34  Sqlite() {}
35 
39  Sqlite(const boost::filesystem::path &fileName) {
40  open(fileName);
41  }
42 
44  Sqlite& open(const boost::filesystem::path &fileName);
45 };
46 
47 
48 // Only implementation details beyond this point -- no public APIs
49 namespace Detail {
50 
51 class SqliteStatement;
52 
54 // SQLite3 connection details
56 
57 class SqliteConnection: public ConnectionBase {
58  friend class ::Sawyer::Database::Sqlite;
59  friend class ::Sawyer::Database::Detail::SqliteStatement;
60 
61  sqlite3 *connection = nullptr;
62 
63 public:
64  ~SqliteConnection() {
65  close();
66  }
67 
68 private:
69  void open(const boost::filesystem::path &filename) {
70  close();
71  if (strlen(filename.string().c_str()) != filename.string().size())
72  throw Exception("invalid database name: internal NUL character");
73  int status = sqlite3_open(filename.string().c_str(), &connection);
74  if (SQLITE_OK != status)
75  throw Exception(sqlite3_errstr(status));
76  sqlite3_busy_timeout(connection, 1000 /*ms*/);
77  }
78 
79  void close() override {
80  if (connection) {
81  // Will return SQLITE_BUSY (a.k.a., "database is locked") if there are outstanding prepared statements. Due to
82  // reference counting, this "close" function will only be called when none of those statements are in the EXECUTING
83  // state. Note that SQLITE_LOCKED is an entirely different error.
84  int status = sqlite3_close(connection);
85  connection = nullptr;
86  if (SQLITE_OK != status && SQLITE_BUSY != status)
87  throw Exception(sqlite3_errstr(status));
88  }
89  }
90 
91  std::string driverName() const override {
92  return "sqlite";
93  }
94 
95  Statement prepareStatement(const std::string &sql) override;
96 
97  size_t lastInsert() const override {
98  ASSERT_not_null(connection);
99  return boost::numeric_cast<size_t>(sqlite3_last_insert_rowid(connection));
100  }
101 };
102 
104 // SQLite3 statement details
106 
107 class SqliteStatement: public StatementBase {
108  friend class ::Sawyer::Database::Detail::SqliteConnection;
109 
110  sqlite3_stmt *stmt = nullptr; // underlying SQLite3 statement
111 
112 private:
113  SqliteStatement(const std::shared_ptr<ConnectionBase> &db, const std::string &sql)
114  : StatementBase(db) {
115  std::string lowSql = parseParameters(sql).first;
116  const char *rest = nullptr;
117  std::shared_ptr<SqliteConnection> sqlite = std::dynamic_pointer_cast<SqliteConnection>(db);
118  ASSERT_not_null(sqlite);
119  int status = sqlite3_prepare_v2(sqlite->connection, lowSql.c_str(), lowSql.size()+1, &stmt, &rest);
120  if (SQLITE_OK != status)
121  throw Exception(sqlite3_errstr(status));
122  while (rest && ::isspace(*rest))
123  ++rest;
124  if (rest && *rest) {
125  sqlite3_finalize(stmt); // clean up if possible; ignore error otherwise
126  stmt = nullptr;
127  throw Exception("extraneous text after end of SQL statement");
128  }
129  }
130 
131 public:
132  ~SqliteStatement() {
133  if (stmt) {
134  sqlite3_finalize(stmt);
135  stmt = nullptr;
136  }
137  }
138 
139 private:
140  void reset(bool doUnbind) override {
141  if (!connection())
142  throw Exception("connection is closed");
143  if (state() == Statement::DEAD)
144  throw Exception("statement is dead");
145 
146  // Reset the SQL statement with delayed error reporting
147  int status = SQLITE_OK;
148  if (Statement::EXECUTING == state() || Statement::FINISHED == state())
149  status = sqlite3_reset(stmt); // doesn't actually unbind parameters
150 
151  // Do the higher-level part of the reset
152  StatementBase::reset(doUnbind);
153 
154  // Finally report errors from above
155  if (SQLITE_OK != status) {
156  state(Statement::DEAD); // we no longer know the SQLite3 state
157  throw Exception(sqlite3_errstr(status));
158  }
159  }
160 
161  void bindLow(size_t idx, int value) override {
162  int status = sqlite3_bind_int(stmt, idx+1, value);
163  if (SQLITE_OK != status)
164  throw Exception(sqlite3_errstr(status));
165  }
166 
167  void bindLow(size_t idx, int64_t value) override {
168  int status = sqlite3_bind_int64(stmt, idx+1, value);
169  if (SQLITE_OK != status)
170  throw Exception(sqlite3_errstr(status));
171  }
172 
173  void bindLow(size_t idx, size_t value) override {
174  bindLow(idx, boost::numeric_cast<int64_t>(value));
175  }
176 
177  void bindLow(size_t idx, double value) override {
178  int status = sqlite3_bind_double(stmt, idx+1, value);
179  if (SQLITE_OK != status)
180  throw Exception(sqlite3_errstr(status));
181  }
182 
183  void bindLow(size_t idx, const std::string &value) override {
184  int status = sqlite3_bind_text(stmt, idx+1, value.c_str(), value.size(), SQLITE_TRANSIENT);
185  if (SQLITE_OK != status)
186  throw Exception(sqlite3_errstr(status));
187  }
188 
189  void bindLow(size_t idx, const char *value) override {
190  value ? bindLow(idx, std::string(value)) : bindLow(idx, Nothing());
191  }
192 
193  void bindLow(size_t idx, const std::vector<uint8_t> &value) override {
194  int status = sqlite3_bind_blob(stmt, idx+1, value.data(), value.size(), SQLITE_TRANSIENT);
195  if (SQLITE_OK != status)
196  throw Exception(sqlite3_errstr(status));
197  }
198 
199  void bindLow(size_t idx, Nothing) override {
200  int status = sqlite3_bind_null(stmt, idx+1);
201  if (SQLITE_OK != status)
202  throw Exception(sqlite3_errstr(status));
203  }
204 
205  Iterator beginLow() override {
206  return nextLow();
207  }
208 
209  Iterator nextLow() override {
210  int status = sqlite3_step(stmt);
211  if (SQLITE_ROW == status) {
212  return makeIterator();
213  } else if (SQLITE_DONE == status) {
214  state(Statement::FINISHED);
215  return Iterator();
216  } else {
217  state(Statement::DEAD);
218  throw Exception(sqlite3_errstr(status));
219  }
220  }
221 
222  size_t nColumns() const override {
223  return boost::numeric_cast<size_t>(sqlite3_column_count(stmt));
224  }
225 
226  Sawyer::Optional<std::string> getString(size_t idx) override {
227  if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
228  return Nothing();
229  size_t nBytes = sqlite3_column_bytes(stmt, idx);
230  const unsigned char *s = sqlite3_column_text(stmt, idx);
231  ASSERT_not_null(s);
232  return std::string(s, s+nBytes);
233  }
234 
235  Sawyer::Optional<std::vector<uint8_t>> getBlob(size_t idx) override {
236  if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
237  return Nothing();
238  size_t nBytes = sqlite3_column_bytes(stmt, idx);
239  const uint8_t *data = static_cast<const uint8_t*>(sqlite3_column_blob(stmt, idx));
240  ASSERT_not_null(data);
241  return std::vector<uint8_t>(data, data+nBytes);
242  }
243 };
244 
246 // SqliteConnection implementations
248 
249 inline Statement
250 SqliteConnection::prepareStatement(const std::string &sql) {
251  auto detail = std::shared_ptr<SqliteStatement>(new SqliteStatement(shared_from_this(), sql));
252  return makeStatement(detail);
253 }
254 
255 } // namespace
256 
258 // Top-level Sqlite connection
260 
261 inline Sqlite&
262 Sqlite::open(const boost::filesystem::path &fileName) {
263  auto pimpl = std::shared_ptr<Detail::SqliteConnection>(new Detail::SqliteConnection);
264  pimpl->open(fileName);
265  this->pimpl(pimpl);
266  return *this;
267 }
268 
269 } // namespace
270 } // namespace
271 
272 #endif
273 #endif
Name space for the entire library.