Botan 3.10.0
Crypto and TLS for C&
sqlite3.cpp
Go to the documentation of this file.
1/*
2* SQLite wrapper
3* (C) 2012 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/sqlite3.h>
9
10#include <botan/exceptn.h>
11#include <botan/mem_ops.h>
12#include <botan/internal/fmt.h>
13#include <botan/internal/int_utils.h>
14#include <sqlite3.h>
15
16namespace Botan {
17
18Sqlite3_Database::Sqlite3_Database(std::string_view db_filename, std::optional<int> sqlite_open_flags) {
19 // SQLITE_OPEN_FULLMUTEX ensures that the database object can be used
20 // concurrently from multiple threads.
21 const int open_flags =
22 sqlite_open_flags.value_or(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX);
23 int rc = ::sqlite3_open_v2(std::string(db_filename).c_str(), &m_db, open_flags, nullptr);
24
25 if(rc != 0) [[unlikely]] {
26 const std::string err_msg = ::sqlite3_errmsg(m_db);
27 ::sqlite3_close(m_db);
28 m_db = nullptr;
29 throw SQL_DB_Error("sqlite3_open failed - " + err_msg);
30 }
31}
32
34 if(m_db != nullptr) [[likely]] {
35 ::sqlite3_close(m_db);
36 }
37 m_db = nullptr;
38}
39
40std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::new_statement(std::string_view base_sql) const {
41 return std::make_shared<Sqlite3_Statement>(m_db, base_sql);
42}
43
44size_t Sqlite3_Database::row_count(std::string_view table_name) {
45 auto stmt = new_statement(fmt("select count(*) from {}", table_name));
46
47 if(stmt->step()) {
48 return stmt->get_size_t(0);
49 } else {
50 throw SQL_DB_Error(fmt("Querying size of table '{}' failed", table_name));
51 }
52}
53
54void Sqlite3_Database::create_table(std::string_view table_schema) {
55 char* errmsg = nullptr;
56 int rc = ::sqlite3_exec(m_db, std::string(table_schema).c_str(), nullptr, nullptr, &errmsg);
57
58 if(rc != SQLITE_OK) {
59 const std::string err_msg = errmsg;
60 ::sqlite3_free(errmsg);
61 ::sqlite3_close(m_db);
62 m_db = nullptr;
63 throw SQL_DB_Error("sqlite3_exec for table failed - " + err_msg);
64 }
65}
66
68 const auto result = ::sqlite3_changes64(m_db);
69 BOTAN_ASSERT_NOMSG(result >= 0);
70 return static_cast<size_t>(result);
71}
72
74 const int flag = sqlite3_threadsafe();
75
76 // `flag` can have three values:
77 //
78 // 0 - single threaded: no locking is done inside the SQLite code
79 // 1 - serialized: all SQLite database features can be used safely
80 // from multiple threads
81 // 2 - reduced locking: application must ensure not to use a single
82 // database connection across threads
83 //
84 // https://www.sqlite.org/c3ref/threadsafe.html
85
86 // When opening the database connection we explicitly request
87 // SQLITE_OPEN_FULLMUTEX to ensure restrictive locking in SQLite.
88 return flag >= 1;
89}
90
91Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, std::string_view base_sql) : m_stmt{} {
92 int rc = ::sqlite3_prepare_v2(db, base_sql.data(), static_cast<int>(base_sql.size()), &m_stmt, nullptr);
93
94 if(rc != SQLITE_OK) {
95 throw SQL_DB_Error(fmt("sqlite3_prepare failed on '{}' with err {}", base_sql, rc), rc);
96 }
97}
98
99void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::string_view val) {
100 int rc = ::sqlite3_bind_text64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT, SQLITE_UTF8);
101 if(rc != SQLITE_OK) {
102 throw SQL_DB_Error("sqlite3_bind_text failed", rc);
103 }
104}
105
106void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val) {
107 int rc = ::sqlite3_bind_int64(m_stmt, column, val);
108 if(rc != SQLITE_OK) {
109 throw SQL_DB_Error("sqlite3_bind_int failed", rc);
110 }
111}
112
113void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::chrono::system_clock::time_point time) {
114 const uint64_t timeval = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count();
115 bind(column, static_cast<size_t>(timeval));
116}
117
118void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<uint8_t>& val) {
119 int rc = ::sqlite3_bind_blob64(m_stmt, column, val.data(), val.size(), SQLITE_TRANSIENT);
120 if(rc != SQLITE_OK) {
121 throw SQL_DB_Error("sqlite3_bind_text failed", rc);
122 }
123}
124
125void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len) {
126 int rc = ::sqlite3_bind_blob64(m_stmt, column, p, len, SQLITE_TRANSIENT);
127 if(rc != SQLITE_OK) {
128 throw SQL_DB_Error("sqlite3_bind_text failed", rc);
129 }
130}
131
132std::pair<const uint8_t*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column) {
133 const auto column_type = ::sqlite3_column_type(m_stmt, column);
134 if(column_type == SQLITE_NULL) {
135 return {nullptr, 0};
136 }
137
138 BOTAN_ASSERT(column_type == SQLITE_BLOB, "Return value is a blob");
139
140 const void* session_blob = ::sqlite3_column_blob(m_stmt, column);
141 const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column);
142
143 BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative");
144
145 return std::make_pair(static_cast<const uint8_t*>(session_blob), static_cast<size_t>(session_blob_size));
146}
147
148std::string Sqlite3_Database::Sqlite3_Statement::get_str(int column) {
149 BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_TEXT, "Return value is text");
150
151 const unsigned char* str = ::sqlite3_column_text(m_stmt, column);
152
153 return std::string(cast_uint8_ptr_to_char(str));
154}
155
156size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column) {
157 BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, "Return count is an integer");
158
159 return checked_cast_to<size_t>(::sqlite3_column_int64(m_stmt, column));
160}
161
163 size_t steps = 0;
164 while(step()) {
165 ++steps;
166 }
167
168 return steps;
169}
170
172 return (::sqlite3_step(m_stmt) == SQLITE_ROW);
173}
174
175Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement() {
176 ::sqlite3_finalize(m_stmt);
177}
178
179} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
~Sqlite3_Database() override
Definition sqlite3.cpp:33
std::shared_ptr< Statement > new_statement(std::string_view sql) const override
Definition sqlite3.cpp:40
size_t rows_changed_by_last_statement() override
Definition sqlite3.cpp:67
bool is_threadsafe() const override
Definition sqlite3.cpp:73
void create_table(std::string_view table_schema) override
Definition sqlite3.cpp:54
BOTAN_FUTURE_EXPLICIT Sqlite3_Database(std::string_view file, std::optional< int > sqlite_open_flags=std::nullopt)
Definition sqlite3.cpp:18
size_t row_count(std::string_view table_name) override
Definition sqlite3.cpp:44
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr RT checked_cast_to(AT i)
Definition int_utils.h:74
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:282
Public Header.