/* * SQLite wrapper * (C) 2012 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include namespace Botan { Sqlite3_Database::Sqlite3_Database(const std::string& db_filename) { int rc = ::sqlite3_open(db_filename.c_str(), &m_db); if(rc) { const std::string err_msg = ::sqlite3_errmsg(m_db); ::sqlite3_close(m_db); m_db = nullptr; throw SQL_DB_Error("sqlite3_open failed - " + err_msg); } } Sqlite3_Database::~Sqlite3_Database() { if(m_db) ::sqlite3_close(m_db); m_db = nullptr; } std::shared_ptr Sqlite3_Database::new_statement(const std::string& base_sql) const { return std::make_shared(m_db, base_sql); } size_t Sqlite3_Database::row_count(const std::string& table_name) { auto stmt = new_statement("select count(*) from " + table_name); if(stmt->step()) return stmt->get_size_t(0); else throw SQL_DB_Error("Querying size of table " + table_name + " failed"); } void Sqlite3_Database::create_table(const std::string& table_schema) { char* errmsg = nullptr; int rc = ::sqlite3_exec(m_db, table_schema.c_str(), nullptr, nullptr, &errmsg); if(rc != SQLITE_OK) { const std::string err_msg = errmsg; ::sqlite3_free(errmsg); ::sqlite3_close(m_db); m_db = nullptr; throw SQL_DB_Error("sqlite3_exec for table failed - " + err_msg); } } Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, const std::string& base_sql) { int rc = ::sqlite3_prepare_v2(db, base_sql.c_str(), -1, &m_stmt, nullptr); if(rc != SQLITE_OK) throw SQL_DB_Error("sqlite3_prepare failed " + base_sql + ", code " + std::to_string(rc)); } void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::string& val) { int rc = ::sqlite3_bind_text(m_stmt, column, val.c_str(), -1, SQLITE_TRANSIENT); if(rc != SQLITE_OK) throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc)); } void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val) { if(val != static_cast(static_cast(val))) // is this legit? throw SQL_DB_Error("sqlite3 cannot store " + std::to_string(val) + " without truncation"); int rc = ::sqlite3_bind_int(m_stmt, column, val); if(rc != SQLITE_OK) throw SQL_DB_Error("sqlite3_bind_int failed, code " + std::to_string(rc)); } void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::chrono::system_clock::time_point time) { const int timeval = std::chrono::duration_cast(time.time_since_epoch()).count(); bind(column, timeval); } void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector& val) { int rc = ::sqlite3_bind_blob(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT); if(rc != SQLITE_OK) throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc)); } void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len) { int rc = ::sqlite3_bind_blob(m_stmt, column, p, len, SQLITE_TRANSIENT); if(rc != SQLITE_OK) throw SQL_DB_Error("sqlite3_bind_text failed, code " + std::to_string(rc)); } std::pair Sqlite3_Database::Sqlite3_Statement::get_blob(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, "Return value is a blob"); const void* session_blob = ::sqlite3_column_blob(m_stmt, column); const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column); BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative"); return std::make_pair(static_cast(session_blob), static_cast(session_blob_size)); } size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column) { BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, "Return count is an integer"); const int sessions_int = ::sqlite3_column_int(m_stmt, column); BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative"); return static_cast(sessions_int); } size_t Sqlite3_Database::Sqlite3_Statement::spin() { size_t steps = 0; while(step()) { ++steps; } return steps; } bool Sqlite3_Database::Sqlite3_Statement::step() { return (::sqlite3_step(m_stmt) == SQLITE_ROW); } Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement() { ::sqlite3_finalize(m_stmt); } }