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/format.hpp> 
   17#include <boost/lexical_cast.hpp> 
   18#include <Sawyer/Optional.h> 
   22#if SQLITE_VERSION_NUMBER < 3007015 
   23std::string sqlite3_errstr(
int status) {
 
   24    return "sqlite3 error " + boost::lexical_cast<std::string>(status);
 
   32class Sqlite: 
public Connection {
 
   40    Sqlite(
const boost::filesystem::path &fileName) {
 
   45    Sqlite& open(
const boost::filesystem::path &fileName);
 
   58class SqliteConnection: 
public ConnectionBase {
 
   59    friend class ::Sawyer::Database::Sqlite;
 
   60    friend class ::Sawyer::Database::Detail::SqliteStatement;
 
   62    sqlite3 *connection = 
nullptr;
 
   70    void open(
const boost::filesystem::path &filename) {
 
   72        if (strlen(filename.string().c_str()) != filename.string().size())
 
   73            throw Exception(
"invalid database name: internal NUL character");
 
   74        int status = sqlite3_open(filename.string().c_str(), &connection);
 
   75        if (SQLITE_OK != status)
 
   76            throw Exception(
"SqliteConnection::open(" + boost::lexical_cast<std::string>(filename) + 
"): " + sqlite3_errstr(status));
 
   77        sqlite3_busy_timeout(connection, 1000 );
 
   80    void close()
 override {
 
   85            int status = sqlite3_close(connection);
 
   87            if (SQLITE_OK != status && SQLITE_BUSY != status)
 
   88                throw Exception(
"SqliteConnection::close(): " + std::string(sqlite3_errstr(status)));
 
   92    std::string driverName()
 const override {
 
   96    Statement prepareStatement(
const std::string &sql) 
override;
 
   98    size_t lastInsert()
 const override {
 
   99        ASSERT_not_null(connection);
 
  100        return boost::numeric_cast<size_t>(sqlite3_last_insert_rowid(connection));
 
  108class SqliteStatement: 
public StatementBase {
 
  109    friend class ::Sawyer::Database::Detail::SqliteConnection;
 
  111    sqlite3_stmt *stmt = 
nullptr;                       
 
  114    SqliteStatement(
const std::shared_ptr<ConnectionBase> &db, 
const std::string &sql)
 
  115        : StatementBase(db) {
 
  116        std::string lowSql = parseParameters(sql).first;
 
  117        const char *rest = 
nullptr;
 
  118        std::shared_ptr<SqliteConnection> sqlite = std::dynamic_pointer_cast<SqliteConnection>(db);
 
  119        ASSERT_not_null(sqlite);
 
  120        int status = sqlite3_prepare_v2(sqlite->connection, lowSql.c_str(), lowSql.size()+1, &stmt, &rest);
 
  121        if (SQLITE_OK != status)
 
  122            throw Exception(
"SqliteStatement(" + sql + 
"): " + sqlite3_errstr(status));
 
  123        while (rest && ::isspace(*rest))
 
  126            sqlite3_finalize(stmt);                     
 
  128            throw Exception(
"SqliteStatement(" + sql + 
"): extraneous text after end of SQL statement");
 
  135            sqlite3_finalize(stmt);
 
  141    void reset(
bool doUnbind)
 override {
 
  143            throw Exception(
"SqliteStatement::reset(): connection is closed");
 
  144        if (state() == Statement::DEAD)
 
  145            throw Exception(
"SqliteStatement::reset(): statement is dead");
 
  148        int status = SQLITE_OK;
 
  149        if (Statement::EXECUTING == state() || Statement::FINISHED == state())
 
  150            status = sqlite3_reset(stmt);               
 
  153        StatementBase::reset(doUnbind);
 
  156        if (SQLITE_OK != status) {
 
  157            state(Statement::DEAD);                     
 
  158            throw Exception(
"SqliteStatement::reset(): " + std::string(sqlite3_errstr(status)));
 
  162    void bindLow(
size_t idx, 
int value)
 override {
 
  163        int status = sqlite3_bind_int(stmt, idx+1, value);
 
  164        if (SQLITE_OK != status)
 
  165            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  168    void bindLow(
size_t idx, int64_t value)
 override {
 
  169        int status = sqlite3_bind_int64(stmt, idx+1, value);
 
  170        if (SQLITE_OK != status)
 
  171            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  174    void bindLow(
size_t idx, 
size_t value)
 override {
 
  177        assert(
sizeof value <= 8);
 
  178        int64_t reinterpreted = (int64_t)(uint64_t)value; 
 
  179        bindLow(idx, reinterpreted);
 
  182    void bindLow(
size_t idx, 
double value)
 override {
 
  183        int status = sqlite3_bind_double(stmt, idx+1, value);
 
  184        if (SQLITE_OK != status)
 
  185            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  188    void bindLow(
size_t idx, 
const std::string &value)
 override {
 
  189        int status = sqlite3_bind_text(stmt, idx+1, value.c_str(), value.size(), SQLITE_TRANSIENT);
 
  190        if (SQLITE_OK != status)
 
  191            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  194    void bindLow(
size_t idx, 
const char *value)
 override {
 
  195        value ? bindLow(idx, std::string(value)) : bindLow(idx, Nothing());
 
  198    void bindLow(
size_t idx, 
const std::vector<uint8_t> &value)
 override {
 
  199        int status = sqlite3_bind_blob(stmt, idx+1, value.data(), value.size(), SQLITE_TRANSIENT);
 
  200        if (SQLITE_OK != status)
 
  201            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  204    void bindLow(
size_t idx, Nothing)
 override {
 
  205        int status = sqlite3_bind_null(stmt, idx+1);
 
  206        if (SQLITE_OK != status)
 
  207            throw Exception((boost::format(
"SqliteStatement::bindLow(idx=%d): %s") % idx % sqlite3_errstr(status)).str());
 
  210    Iterator beginLow()
 override {
 
  214    Iterator nextLow()
 override {
 
  215        int status = sqlite3_step(stmt);
 
  216        if (SQLITE_ROW == status) {
 
  217            return makeIterator();
 
  218        } 
else if (SQLITE_DONE == status) {
 
  219            state(Statement::FINISHED);
 
  222            state(Statement::DEAD);
 
  223            throw Exception(
"SqliteStatement::nextLow(): " + std::string(sqlite3_errstr(status)));
 
  227    size_t nColumns()
 const override {
 
  228        return boost::numeric_cast<size_t>(sqlite3_column_count(stmt));
 
  232        if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
 
  234        size_t nBytes = sqlite3_column_bytes(stmt, idx);
 
  235        const unsigned char *s = sqlite3_column_text(stmt, idx);
 
  237        return std::string(s, s+nBytes);
 
  241        if (SQLITE_NULL == sqlite3_column_type(stmt, idx))
 
  243        size_t nBytes = sqlite3_column_bytes(stmt, idx);
 
  244        const uint8_t *data = 
static_cast<const uint8_t*
>(sqlite3_column_blob(stmt, idx));
 
  245        ASSERT_not_null(data);
 
  246        return std::vector<uint8_t>(data, data+nBytes);
 
  255SqliteConnection::prepareStatement(
const std::string &sql) {
 
  256    auto detail = std::shared_ptr<SqliteStatement>(
new SqliteStatement(shared_from_this(), sql));
 
  257    return makeStatement(detail);
 
  267Sqlite::open(
const boost::filesystem::path &fileName) {
 
  268    auto pimpl = std::shared_ptr<Detail::SqliteConnection>(
new Detail::SqliteConnection);
 
  269    pimpl->open(fileName);
 
Holds a value or nothing.