8#include <botan/sqlite3.h>
10#include <botan/exceptn.h>
11#include <botan/mem_ops.h>
12#include <botan/internal/fmt.h>
13#include <botan/internal/int_utils.h>
21 const int open_flags =
22 sqlite_open_flags.value_or(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX);
23 sqlite3* db =
nullptr;
24 const int rc = ::sqlite3_open_v2(std::string(db_filename).c_str(), &db, open_flags,
nullptr);
26 if(rc != 0) [[unlikely]] {
27 const std::string err_msg = (db !=
nullptr) ? ::sqlite3_errmsg(db) :
"unknown error";
28 ::sqlite3_close_v2(db);
32 m_db = std::shared_ptr<sqlite3>(db, [](sqlite3* p)
noexcept { ::sqlite3_close_v2(p); });
38 return std::make_shared<Sqlite3_Statement>(m_db, base_sql);
42 std::string_view table, std::initializer_list<std::string_view> columns)
const {
43 BOTAN_ARG_CHECK(columns.size() > 0,
"upsert requires at least one column");
45 std::string sql =
"INSERT OR REPLACE INTO ";
49 for(
const auto& col : columns) {
57 for(
size_t i = 1; i <= columns.size(); ++i) {
72 return stmt->get_size_t(0);
74 throw SQL_DB_Error(
fmt(
"Querying size of table '{}' failed", table_name));
82 std::string sql =
"CREATE TABLE ";
84 sql +=
"IF NOT EXISTS ";
89 for(
const auto& col : schema.
columns()) {
106 if(col.is_primary_key()) {
107 sql +=
" PRIMARY KEY";
109 if(col.is_unique()) {
112 if(col.is_not_null()) {
119 char* errmsg =
nullptr;
120 const int rc = ::sqlite3_exec(m_db.get(), sql.c_str(),
nullptr,
nullptr, &errmsg);
122 if(rc != SQLITE_OK) {
123 const std::string err_msg = (errmsg !=
nullptr) ? errmsg :
"unknown error";
124 ::sqlite3_free(errmsg);
125 throw SQL_DB_Error(
"sqlite3_exec for create_table failed - " + err_msg, rc);
130 const auto result = ::sqlite3_changes64(m_db.get());
132 return static_cast<size_t>(result);
142 return ::sqlite3_db_mutex(m_db.get()) !=
nullptr;
145Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(std::shared_ptr<sqlite3> db, std::string_view base_sql) :
146 m_db(std::move(db)), m_stmt{} {
148 ::sqlite3_prepare_v2(m_db.get(), base_sql.data(),
static_cast<int>(base_sql.size()), &m_stmt,
nullptr);
150 if(rc != SQLITE_OK) {
151 throw SQL_DB_Error(
fmt(
"sqlite3_prepare failed on '{}' with err {}", base_sql, rc), rc);
155void Sqlite3_Database::Sqlite3_Statement::bind(
int column, std::string_view val) {
156 if(val.data() ==
nullptr) {
160 const int rc = ::sqlite3_bind_text64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT, SQLITE_UTF8);
161 if(rc != SQLITE_OK) {
162 throw SQL_DB_Error(
"sqlite3_bind_text failed", rc);
166void Sqlite3_Database::Sqlite3_Statement::bind(
int column,
size_t val) {
167 const int rc = ::sqlite3_bind_int64(m_stmt, column, val);
168 if(rc != SQLITE_OK) {
173void Sqlite3_Database::Sqlite3_Statement::bind(
int column, std::chrono::system_clock::time_point time) {
174 const uint64_t timeval = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count();
175 bind(column,
static_cast<size_t>(timeval));
178void Sqlite3_Database::Sqlite3_Statement::bind(
int column,
const std::vector<uint8_t>& val) {
179 bind(column, val.data(), val.size());
182void Sqlite3_Database::Sqlite3_Statement::bind(
int column,
const uint8_t* p,
size_t len) {
187 const int rc = ::sqlite3_bind_blob64(m_stmt, column, p, len, SQLITE_TRANSIENT);
188 if(rc != SQLITE_OK) {
193void Sqlite3_Database::Sqlite3_Statement::bind_null(
int column) {
194 const int rc = ::sqlite3_bind_null(m_stmt, column);
195 if(rc != SQLITE_OK) {
200std::span<const uint8_t> Sqlite3_Database::Sqlite3_Statement::get_blob(
int column) {
201 const auto column_type = ::sqlite3_column_type(m_stmt, column);
202 if(column_type == SQLITE_NULL) {
206 BOTAN_ASSERT(column_type == SQLITE_BLOB,
"Return value is a blob");
208 const void* session_blob = ::sqlite3_column_blob(m_stmt, column);
209 const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column);
211 BOTAN_ASSERT(session_blob_size >= 0,
"Blob size is non-negative");
213 return {
static_cast<const uint8_t*
>(session_blob),
static_cast<size_t>(session_blob_size)};
216std::optional<std::string> Sqlite3_Database::Sqlite3_Statement::get_str(
int column) {
217 const auto column_type = ::sqlite3_column_type(m_stmt, column);
218 if(column_type == SQLITE_NULL) {
222 BOTAN_ASSERT(column_type == SQLITE_TEXT,
"Return value is text");
224 const unsigned char* str = ::sqlite3_column_text(m_stmt, column);
225 const int len = ::sqlite3_column_bytes(m_stmt, column);
231size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(
int column) {
232 BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER,
"Return count is an integer");
247 const int rc = ::sqlite3_step(m_stmt);
248 if(rc == SQLITE_ROW) {
251 if(rc == SQLITE_DONE) {
254 throw SQL_DB_Error(
fmt(
"sqlite3_step failed - {}", ::sqlite3_errmsg(::sqlite3_db_handle(m_stmt))), rc);
257Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement() {
258 ::sqlite3_finalize(m_stmt);
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_ARG_CHECK(expr, msg)
#define BOTAN_ASSERT(expr, assertion_made)
bool is_if_not_exists() const
const std::string & name() const
const std::vector< Column > & columns() const
std::shared_ptr< Statement > upsert(std::string_view table, std::initializer_list< std::string_view > columns) const override
~Sqlite3_Database() override
std::shared_ptr< Statement > new_statement(std::string_view sql) const override
size_t rows_changed_by_last_statement() override
bool is_threadsafe() const override
BOTAN_FUTURE_EXPLICIT Sqlite3_Database(std::string_view file, std::optional< int > sqlite_open_flags=std::nullopt)
size_t row_count(std::string_view table_name) override
void create_table(const Table_Schema &schema) override
std::string fmt(std::string_view format, const T &... args)
constexpr RT checked_cast_to(AT i)
const char * cast_uint8_ptr_to_char(const uint8_t *b)