Botan 3.13.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 sqlite3* db = nullptr;
24 const int rc = ::sqlite3_open_v2(std::string(db_filename).c_str(), &db, open_flags, nullptr);
25
26 if(rc != 0) [[unlikely]] {
27 const std::string err_msg = (db != nullptr) ? ::sqlite3_errmsg(db) : "unknown error";
28 ::sqlite3_close_v2(db);
29 throw SQL_DB_Error("sqlite3_open failed - " + err_msg);
30 }
31
32 m_db = std::shared_ptr<sqlite3>(db, [](sqlite3* p) noexcept { ::sqlite3_close_v2(p); });
33}
34
36
37std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::new_statement(std::string_view base_sql) const {
38 return std::make_shared<Sqlite3_Statement>(m_db, base_sql);
39}
40
41std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::upsert(
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");
44
45 std::string sql = "INSERT OR REPLACE INTO ";
46 sql += table;
47 sql += " (";
48 bool first = true;
49 for(const auto& col : columns) {
50 if(!first) {
51 sql += ", ";
52 }
53 sql += col;
54 first = false;
55 }
56 sql += ") VALUES (";
57 for(size_t i = 1; i <= columns.size(); ++i) {
58 if(i > 1) {
59 sql += ", ";
60 }
61 sql += fmt("?{}", i);
62 }
63 sql += ")";
64
65 return new_statement(sql);
66}
67
68size_t Sqlite3_Database::row_count(std::string_view table_name) {
69 auto stmt = new_statement(fmt("select count(*) from {}", table_name));
70
71 if(stmt->step()) {
72 return stmt->get_size_t(0);
73 } else {
74 throw SQL_DB_Error(fmt("Querying size of table '{}' failed", table_name));
75 }
76}
77
79 BOTAN_ARG_CHECK(!schema.name().empty(), "create_table requires a table name");
80 BOTAN_ARG_CHECK(!schema.columns().empty(), "create_table requires at least one column");
81
82 std::string sql = "CREATE TABLE ";
83 if(schema.is_if_not_exists()) {
84 sql += "IF NOT EXISTS ";
85 }
86 sql += schema.name();
87 sql += " (";
88 bool first = true;
89 for(const auto& col : schema.columns()) {
90 if(!first) {
91 sql += ", ";
92 }
93 sql += col.name();
94 sql += ' ';
95 switch(col.type()) {
97 sql += "BLOB";
98 break;
100 sql += "TEXT";
101 break;
103 sql += "INTEGER";
104 break;
105 }
106 if(col.is_primary_key()) {
107 sql += " PRIMARY KEY";
108 }
109 if(col.is_unique()) {
110 sql += " UNIQUE";
111 }
112 if(col.is_not_null()) {
113 sql += " NOT NULL";
114 }
115 first = false;
116 }
117 sql += ")";
118
119 char* errmsg = nullptr;
120 const int rc = ::sqlite3_exec(m_db.get(), sql.c_str(), nullptr, nullptr, &errmsg);
121
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);
126 }
127}
128
130 const auto result = ::sqlite3_changes64(m_db.get());
131 BOTAN_ASSERT_NOMSG(result >= 0);
132 return static_cast<size_t>(result);
133}
134
136 // sqlite3_db_mutex() returns the connection's mutex if the connection is in
137 // serialized mode, and nullptr otherwise. This reflects both the compile-time
138 // SQLITE_THREADSAFE setting and the per-connection SQLITE_OPEN_(FULL|NO)MUTEX
139 // open flags actually used.
140 //
141 // https://www.sqlite.org/c3ref/db_mutex.html
142 return ::sqlite3_db_mutex(m_db.get()) != nullptr;
143}
144
145Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(std::shared_ptr<sqlite3> db, std::string_view base_sql) :
146 m_db(std::move(db)), m_stmt{} {
147 const int rc =
148 ::sqlite3_prepare_v2(m_db.get(), base_sql.data(), static_cast<int>(base_sql.size()), &m_stmt, nullptr);
149
150 if(rc != SQLITE_OK) {
151 throw SQL_DB_Error(fmt("sqlite3_prepare failed on '{}' with err {}", base_sql, rc), rc);
152 }
153}
154
155void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::string_view val) {
156 if(val.data() == nullptr) {
157 bind_null(column);
158 return;
159 }
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);
163 }
164}
165
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) {
169 throw SQL_DB_Error("sqlite3_bind_int failed", rc);
170 }
171}
172
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));
176}
177
178void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<uint8_t>& val) {
179 bind(column, val.data(), val.size());
180}
181
182void Sqlite3_Database::Sqlite3_Statement::bind(int column, const uint8_t* p, size_t len) {
183 if(p == nullptr) {
184 bind_null(column);
185 return;
186 }
187 const int rc = ::sqlite3_bind_blob64(m_stmt, column, p, len, SQLITE_TRANSIENT);
188 if(rc != SQLITE_OK) {
189 throw SQL_DB_Error("sqlite3_bind_blob failed", rc);
190 }
191}
192
193void Sqlite3_Database::Sqlite3_Statement::bind_null(int column) {
194 const int rc = ::sqlite3_bind_null(m_stmt, column);
195 if(rc != SQLITE_OK) {
196 throw SQL_DB_Error("sqlite3_bind_null failed", rc);
197 }
198}
199
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) {
203 return {};
204 }
205
206 BOTAN_ASSERT(column_type == SQLITE_BLOB, "Return value is a blob");
207
208 const void* session_blob = ::sqlite3_column_blob(m_stmt, column);
209 const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column);
210
211 BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative");
212
213 return {static_cast<const uint8_t*>(session_blob), static_cast<size_t>(session_blob_size)};
214}
215
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) {
219 return std::nullopt;
220 }
221
222 BOTAN_ASSERT(column_type == SQLITE_TEXT, "Return value is text");
223
224 const unsigned char* str = ::sqlite3_column_text(m_stmt, column);
225 const int len = ::sqlite3_column_bytes(m_stmt, column);
226 BOTAN_ASSERT(len >= 0, "Text length is non-negative");
227
228 return std::string(cast_uint8_ptr_to_char(str), static_cast<size_t>(len));
229}
230
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");
233
234 return checked_cast_to<size_t>(::sqlite3_column_int64(m_stmt, column));
235}
236
238 size_t steps = 0;
239 while(step()) {
240 ++steps;
241 }
242
243 return steps;
244}
245
247 const int rc = ::sqlite3_step(m_stmt);
248 if(rc == SQLITE_ROW) {
249 return true;
250 }
251 if(rc == SQLITE_DONE) {
252 return false;
253 }
254 throw SQL_DB_Error(fmt("sqlite3_step failed - {}", ::sqlite3_errmsg(::sqlite3_db_handle(m_stmt))), rc);
255}
256
257Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement() {
258 ::sqlite3_finalize(m_stmt);
259}
260
261} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
const std::string & name() const
Definition database.h:305
const std::vector< Column > & columns() const
Definition database.h:311
std::shared_ptr< Statement > upsert(std::string_view table, std::initializer_list< std::string_view > columns) const override
Definition sqlite3.cpp:41
~Sqlite3_Database() override
std::shared_ptr< Statement > new_statement(std::string_view sql) const override
Definition sqlite3.cpp:37
size_t rows_changed_by_last_statement() override
Definition sqlite3.cpp:129
bool is_threadsafe() const override
Definition sqlite3.cpp:135
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:68
void create_table(const Table_Schema &schema) override
Definition sqlite3.cpp:78
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:104
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:323
Public Header.