8 #ifndef Sawyer_DatabaseSqlite_H
9 #define Sawyer_DatabaseSqlite_H
11 #if __cplusplus >= 201103L
13 #include <Sawyer/Database.h>
15 #include <boost/filesystem.hpp>
16 #include <boost/lexical_cast.hpp>
17 #include <Sawyer/Optional.h>
21 #if SQLITE_VERSION_NUMBER < 3007015
22 std::string sqlite3_errstr(
int status) {
23 return "sqlite3 error " + boost::lexical_cast<std::string>(status);
31 class Sqlite:
public Connection {
39 Sqlite(
const boost::filesystem::path &fileName) {
44 Sqlite& open(
const boost::filesystem::path &fileName);
51 class SqliteStatement;
57 class SqliteConnection:
public ConnectionBase {
58 friend class ::Sawyer::Database::Sqlite;
59 friend class ::Sawyer::Database::Detail::SqliteStatement;
61 sqlite3 *connection =
nullptr;
69 void open(
const boost::filesystem::path &filename) {
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 );
79 void close()
override {
84 int status = sqlite3_close(connection);
86 if (SQLITE_OK != status && SQLITE_BUSY != status)
87 throw Exception(sqlite3_errstr(status));
91 std::string driverName()
const override {
95 Statement prepareStatement(
const std::string &sql)
override;
97 size_t lastInsert()
const override {
98 ASSERT_not_null(connection);
99 return boost::numeric_cast<
size_t>(sqlite3_last_insert_rowid(connection));
107 class SqliteStatement:
public StatementBase {
108 friend class ::Sawyer::Database::Detail::SqliteConnection;
110 sqlite3_stmt *stmt =
nullptr;
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))
125 sqlite3_finalize(stmt);
127 throw Exception(
"extraneous text after end of SQL statement");
134 sqlite3_finalize(stmt);
140 void reset(
bool doUnbind)
override {
142 throw Exception(
"connection is closed");
143 if (state() == Statement::DEAD)
144 throw Exception(
"statement is dead");
147 int status = SQLITE_OK;
148 if (Statement::EXECUTING == state() || Statement::FINISHED == state())
149 status = sqlite3_reset(stmt);
152 StatementBase::reset(doUnbind);
155 if (SQLITE_OK != status) {
156 state(Statement::DEAD);
157 throw Exception(sqlite3_errstr(status));
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));
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));
173 void bindLow(
size_t idx,
size_t value)
override {
174 bindLow(idx, boost::numeric_cast<int64_t>(value));
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));
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));
189 void bindLow(
size_t idx,
const char *value)
override {
190 value ? bindLow(idx, std::string(value)) : bindLow(idx, Nothing());
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));
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));
205 Iterator beginLow()
override {
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);
217 state(Statement::DEAD);
218 throw Exception(sqlite3_errstr(status));
222 size_t nColumns()
const override {
223 return boost::numeric_cast<
size_t>(sqlite3_column_count(stmt));
227 if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
229 size_t nBytes = sqlite3_column_bytes(stmt, idx);
230 const unsigned char *s = sqlite3_column_text(stmt, idx);
232 return std::string(s, s+nBytes);
236 if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
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);
250 SqliteConnection::prepareStatement(
const std::string &sql) {
251 auto detail = std::shared_ptr<SqliteStatement>(
new SqliteStatement(shared_from_this(), sql));
252 return makeStatement(detail);
262 Sqlite::open(
const boost::filesystem::path &fileName) {
263 auto pimpl = std::shared_ptr<Detail::SqliteConnection>(
new Detail::SqliteConnection);
264 pimpl->open(fileName);
Name space for the entire library.