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