Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SQLite.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Database_SQL_SQLite_h_
5#define _Stroika_Foundation_Database_SQL_SQLite_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <filesystem>
10#include <optional>
11
12#if qStroika_HasComponent_sqlite
13#include <sqlite/sqlite3.h>
14#endif
15
17#include "Stroika/Foundation/Common/Property.h"
18#include "Stroika/Foundation/Containers/Mapping.h"
19#include "Stroika/Foundation/Containers/Sequence.h"
27
28/**
29 * \file
30 *
31 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
32 *
33 * TODO
34 * @todo Create SQLite Exception class and use sqlite3_errstr () to generate good string
35 * message (that seems to return threadsafe static const strings)
36 */
37
38namespace Stroika::Foundation::Database::SQL::SQLite {
39
40 using Characters::String;
41 using Containers::Mapping;
42 using Containers::Sequence;
43 using DataExchange::VariantValue;
44 using IO::Network::URI;
45 using Time::Duration;
46
47#if qStroika_HasComponent_sqlite
48
49 /**
50 * This defines what options sqlite was compiled with.
51 *
52 * For a full list of possible options, see <https://www.sqlite.org/compile.html>
53 * (though we only capture a limited subset of these). To check the rest, callers
54 * can use ::sqlite3_compileoption_used ()
55 *
56 * Fields correspond to names looked up with ::sqlite3_compileoption_used () - only this is constexpr (and an incomplete replica).
57 * This is checked to correspond to the sqlite3_compileoption_used() values at startup with assertions.
58 *
59 * \todo Find a better way to define! We want this to be available as a constexpr object. But the defines
60 * are just done in the .c file that gets defined and the API to lookup values is very non-constexpr.
61 *
62 * So instead we take a good guess at the values (based on defaults and #defines done in this file)
63 * and check with asserts we got the right value.
64 */
65 class CompiledOptions final {
66 public:
67 /**
68 * c++ #define SQLITE_ENABLE_NORMALIZE (not in docs file but does appear as a compile time option - we need to check)
69 */
70 bool ENABLE_NORMALIZE;
71
72 /**
73 * SQLITE_THREADSAFE = 0, 1, 2 (0 means no)
74 */
75 uint8_t THREADSAFE;
76
77 /**
78 * SQLITE_ENABLE_JSON1 = true (for now required)
79 */
80 bool ENABLE_JSON1;
81
82 /**
83 * Defined constexpr
84 */
85 static const CompiledOptions kThe;
86 };
87
88 /**
89 * https://www.sqlite.org/pragma.html#pragma_journal_mode
90 *
91 * In my experience, it appears WAL provides the best performance, for multithreaded applications.
92 * \see https://sqlite.org/wal.html
93 * "WAL provides more concurrency as readers do not block writers and a writer"
94 * "does not block readers. Reading and writing can proceed concurrently."
95 */
96 enum JournalModeType {
97 eDelete,
98 eTruncate,
99 ePersist,
100 eMemory,
101 eWAL,
102 eWAL2,
103 eOff
104 };
105
106 class Statement;
107
108 /**
109 * \brief SQLite::Connection namespace contains SQL::Connection::Ptr subclass, specific to SQLite, and ::New function factory.
110 */
111 namespace Connection {
112
113 using namespace SQL::Connection;
114
115 class IRep;
116
117 /**
118 * These are options used to create a database Connection::Ptr object (with Connection::New).
119 *
120 * Since this is also how you create a database, in a sense, its those options too.
121 */
122 struct Options final {
123 /**
124 * NOTE - we choose to only support a PATH, and not the URI syntax, because the URI syntax is used to pass
125 * extra parameters (as from a GUI) and those can conflict with what is specified here (making it unclear or
126 * surprising how to interpret). @todo perhaps provide an API to 'parse' an sqlite URI into one of these Stroika
127 * SQLite options objects?
128 *
129 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
130 */
131 optional<filesystem::path> fDBPath;
132
133 /**
134 * This option only applies if fDBPath is set.
135 * \pre fCreateDBPathIfDoesNotExist => not fReadOnly
136 */
137 bool fCreateDBPathIfDoesNotExist{true};
138
139 /**
140 * fTemporaryDB is just like fInMemoryDB, except that it will be written to disk. But its like temporaryDB in that
141 * it will be automatically deleted when this connection (that created it) closes.
142 *
143 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
144 */
145 optional<String> fTemporaryDB;
146
147 /**
148 * If provided, the database will not be stored to disk, but just saved in memory. The name still must be provided to allow
149 * for sharing the same (in memory) database between different connections). If the name is the empty string (String{}) then
150 * it is guaranteed unique.
151 *
152 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
153 */
154 optional<String> fInMemoryDB;
155
156 /**
157 * @see https://www.sqlite.org/compile.html#threadsafe
158 *
159 * Note this refers to the threading mode for the underlying database. A Connection object is always single-threaded/externally
160 * synchronized.
161 */
162 enum class ThreadingMode {
163 /**
164 * SQLITE_OPEN_FULLMUTEX
165 * In this mode, all mutexes are disabled and SQLite is unsafe to use in more than a single thread at once
166 */
167 eSingleThread,
168
169 /**
170 * SQLITE_OPEN_NOMUTEX
171 * In this mode, SQLite can be safely used by multiple threads provided that no single database connection is used simultaneously in two or more threads.
172 * (Stroika Debug::AssertExternallySynchronizedMutex enforces this)
173 *
174 * This may not always be available depending on how sqlite was compiled, but we dont have access to SQLITE_THREADSAFE at compile time
175 * (since just defined in C file from Stroika/ThirdPartyComponents/sqlite/Makefile);
176 * call sqlite3_threadsafe, to see if this is enabled
177 */
178 eMultiThread,
179
180 /**
181 * SQLITE_OPEN_FULLMUTEX
182 * In serialized mode, SQLite can be safely used by multiple threads with no restriction.
183 * (note even in this mode, each connection is Debug::AssertExternallySynchronizedMutex)
184 *
185 * This may not always be available depending on how sqlite was compiled, but we dont have access to SQLITE_THREADSAFE at compile time
186 * (since just defined in C file from Stroika/ThirdPartyComponents/sqlite/Makefile);
187 * call sqlite3_threadsafe, to see if this is enabled
188 *
189 * \note Use of this API, as of Stroika 2.1b12, may result in poor error messages, due to how errors are stored (and maybe other such
190 * issues - maybe we need to do lock around call to each function to avoid making this mode nearly pointless).
191 */
192 eSerialized,
193 };
194 optional<ThreadingMode> fThreadingMode;
195
196 /**
197 * This can generally be ignored, and primarily affects low level OS interface locking choices.
198 * @see https://www.sqlite.org/vfs.html
199 */
200 optional<String> fVFS;
201
202 /**
203 * If a database is opened readonly, updates will fail, and if the database doesn't exist, it will not be automatically created.
204 */
205 bool fReadOnly{false};
206
207 /**
208 * The immutable query parameter is a boolean that signals to SQLite that the underlying database file is held on read-only media and
209 * cannot be modified, even by another process with elevated privileges.
210 *
211 * \pre fImmutable ==> fReadOnly
212 */
213 bool fImmutable{false};
214
215 /**
216 * This is only useful if the database can be opened by multiple threads of control (multiple threads with connections
217 * within the same app, or multiple applications).
218 */
219 optional<Duration> fBusyTimeout;
220
221 /**
222 * \note - see JournalModeType and Connection::Ptr::journalMode
223 */
224 optional<JournalModeType> fJournalMode;
225 };
226
227 /**
228 * Connection provides an API for accessing an SQLite database.
229 *
230 * A new Connection::Ptr is typically created SQLite::Connection::New()
231 *
232 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-Plus-Must-Externally-Synchronize-Letter">C++-Standard-Thread-Safety-For-Envelope-Plus-Must-Externally-Synchronize-Letter</a>
233 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
234 * threadsafe (even if accessed across processes) - depending on its construction OPtions::ThreadSafety
235 *
236 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
237 * of Options::fThreadingMode when the database is constructed.
238 *
239 * @see https://www.sqlite.org/threadsafe.html
240 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
241 *
242 * NOTE - two Connection::Ptr objects refering to the same underlying REP is NOT (probably) safe with SQLITE. But referring
243 * to the same database is safe.
244 *
245 */
246 class Ptr : public SQL::Connection::Ptr {
247 private:
248 using inherited = SQL::Connection::Ptr;
249
250 public:
251 /**
252 */
253 Ptr (const Ptr& src);
254 Ptr (const shared_ptr<IRep>& src = nullptr);
255
256 public:
257 ~Ptr () = default;
258
259 public:
260 /**
261 */
262 nonvirtual Ptr& operator= (const Ptr& src);
263 nonvirtual Ptr& operator= (Ptr&& src) noexcept;
264
265 public:
266 /**
267 */
268 nonvirtual IRep* operator->() const noexcept;
269
270 public:
271 /**
272 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the sqlite API.
273 */
274 nonvirtual ::sqlite3* Peek () const;
275
276 public:
277 /**
278 * When doing a query that would have failed due to SQL_BUSY timeout, sqlite will wait
279 * and retry up to this long, to avoid the timeout.
280 */
281 Common::Property<Duration> busyTimeout;
282
283 public:
284 /**
285 * This can significantly affect database performance, and reliability.
286 */
287 Common::Property<JournalModeType> journalMode;
288
289 private:
290 friend class Statement;
291 };
292
293 /**
294 * \brief create an SQLite database connection object, guided by argument Options.
295 */
296 Ptr New (const Options& options);
297
298 /**
299 * Connection provides an API for accessing an SQLite database.
300 *
301 * Typically don't use this directly, but use Connection::Ptr, a smart ptr wrapper on this interface.
302 *
303 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
304 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
305 * threadsafe (even if accessed across processes) - depending on its construction OPtions::ThreadSafety
306 *
307 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
308 * of Options::fThreadingMode when the database is constructed.
309 *
310 * @see https://www.sqlite.org/threadsafe.html
311 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
312 *
313 * NOTE ALSO - its POSSIBLE we could lift this Debug::AssertExternallySynchronizedMutex code / restriction.
314 * But sqlite docs not super clear. Maybe I need to use thier locking APIs myself internally to use
315 * those locks to make a sequence of bindings safe? But for now just don't assume this is threadsafe and we'll be OK.
316 */
317 class IRep : public SQL::Connection::IRep {
318 public:
319 /**
320 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the sqlite API.
321 */
322 virtual ::sqlite3* Peek () = 0;
323
324 public:
325 /**
326 * Fetched dynamically with pragma busy_timeout;
327 */
328 virtual Duration GetBusyTimeout () const = 0;
329
330 public:
331 /**
332 * \pre timeout >= 0
333 */
334 virtual void SetBusyTimeout (const Duration& timeout) = 0;
335
336 public:
337 /**
338 */
339 virtual JournalModeType GetJournalMode () const = 0;
340
341 public:
342 /**
343 */
344 virtual void SetJournalMode (JournalModeType journalMode) = 0;
345
346 public:
347 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySynchronizedMutex;
348
349 private:
350 friend class Ptr;
351 };
352
353 }
354
355 /**
356 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-Plus-Must-Externally-Synchronize-Letter">C++-Standard-Thread-Safety-For-Envelope-Plus-Must-Externally-Synchronize-Letter</a>
357 * See notes about thread safety for Connection::Ptr - since this copies around a Connection::Ptr.
358 */
359 class Statement : public SQL::Statement {
360 private:
361 using inherited = SQL::Statement;
362
363 public:
364 /**
365 */
366 Statement () = delete;
367 Statement (const Connection::Ptr& db, const String& query);
368 Statement (const Statement&) = delete;
369
370 private:
371 struct MyRep_;
372 };
373
374 /**
375 * \see https://www.sqlite.org/lang_transaction.html
376 *
377 * \note Transactions are not required. This is for explicit transactions. If you omit
378 * using transactions, sqlite creates mini transactions automatically for each statement.
379 *
380 * \note Nested transactions not supported
381 *
382 * \todo Consider supporting SQLITE SAVEPOINT (like nested transaction)
383 */
384 class Transaction : public SQL::Transaction {
385 private:
386 using inherited = SQL::Transaction;
387
388 public:
389 enum Flag {
390 /**
391 * Don't really start the transaction until the command to read/update the database
392 */
393 eDeferred,
394
395 /**
396 * Start writing to the DB immediately (as of the transaction start); note this affects when you might
397 * get SQL_BUSY errors.
398 */
399 eImmediate,
400
401 /**
402 * Depends on WAL mode, but generally prevents other database connections from reading the
403 * database while the transaction is underway.
404 */
405 eExclusive,
406
407 eDEFAULT = eDeferred
408 };
409
410 public:
411 /**
412 */
413 Transaction () = delete;
414 Transaction (const Connection::Ptr& db, Flag f = Flag::eDEFAULT);
415 Transaction (const Transaction&) = delete;
416
417 private:
418 struct MyRep_;
419 };
420#endif
421
422}
423
424/*
425 ********************************************************************************
426 ***************************** Implementation Details ***************************
427 ********************************************************************************
428 */
429#include "SQLite.inl"
430
431#endif /*_Stroika_Foundation_Database_SQL_SQLite_h_*/