Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
Document/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_Document_SQLite_h_
5#define _Stroika_Foundation_Database_Document_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"
28
29/**
30 * \file
31 *
32 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
33 *
34 * TODO
35 * @todo Create SQLite Exception class and use sqlite3_errstr () to generate good string
36 * message (that seems to return threadsafe static const strings)
37 */
38
39namespace Stroika::Foundation::Database::Document::SQLite {
40
41 using Characters::String;
42 using Containers::Mapping;
43 using Containers::Sequence;
44 using DataExchange::VariantValue;
45 using IO::Network::URI;
46 using Time::Duration;
47
48#if qStroika_HasComponent_sqlite
49
50 /**
51 * This defines what options SQLite was compiled with.
52 *
53 * For a full list of possible options, see <https://www.sqlite.org/compile.html>
54 * (though we only capture a limited subset of these). To check the rest, callers
55 * can use ::sqlite3_compileoption_used ()
56 *
57 * Fields correspond to names looked up with ::sqlite3_compileoption_used () - only this is constexpr (and an incomplete replica).
58 * This is checked to correspond to the sqlite3_compileoption_used() values at startup with assertions.
59 *
60 * \todo Find a better way to define! We want this to be available as a constexpr object. But the defines
61 * are just done in the .c file that gets defined and the API to lookup values is very non-constexpr.
62 *
63 * So instead we take a good guess at the values (based on defaults and #defines done in this file)
64 * and check with asserts we got the right value.
65 */
66 class CompiledOptions final {
67 public:
68 /**
69 * c++ #define SQLITE_ENABLE_NORMALIZE (not in docs file but does appear as a compile time option - we need to check)
70 */
71 bool ENABLE_NORMALIZE;
72
73 /**
74 * SQLITE_THREADSAFE = 0, 1, 2 (0 means no)
75 */
76 uint8_t THREADSAFE;
77
78 /**
79 * SQLITE_ENABLE_JSON1 = true (required)
80 */
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 /**
107 * \brief SQLite::Connection namespace contains SQL::Connection::Ptr subclass, specific to SQLite, and ::New function factory.
108 */
109 namespace Connection {
110
111 using namespace Database::Document::Connection;
112
113 class IRep;
114
115 /**
116 * These are options used to create a database Connection::Ptr object (with Connection::New).
117 *
118 * Since this is also how you create a database, in a sense, its those options too.
119 */
120 struct Options final {
121 /**
122 * NOTE - we choose to only support a PATH, and not the URI syntax, because the URI syntax is used to pass
123 * extra parameters (as from a GUI) and those can conflict with what is specified here (making it unclear or
124 * surprising how to interpret). @todo perhaps provide an API to 'parse' an SQLite URI into one of these Stroika
125 * SQLite options objects?
126 *
127 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
128 */
129 optional<filesystem::path> fDBPath;
130
131 /**
132 * This option only applies if fDBPath is set.
133 * \pre fCreateDBPathIfDoesNotExist => not fReadOnly
134 */
135 bool fCreateDBPathIfDoesNotExist{true};
136
137 /**
138 * fTemporaryDB is just like fInMemoryDB, except that it will be written to disk. But its like temporaryDB in that
139 * it will be automatically deleted when this connection (that created it) closes.
140 *
141 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
142 */
143 optional<String> fTemporaryDB;
144
145 /**
146 * If provided, the database will not be stored to disk, but just saved in memory. The name still must be provided to allow
147 * for sharing the same (in memory) database between different connections). If the name is the empty string (String{}) then
148 * it is guaranteed unique.
149 *
150 * \note - fInMemoryDB and fDBPath and fTemporaryDB are mutually exclusive options.
151 *
152 * \note using a named in-memory-db allows two separate threads in the same process, to share the same database.
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 * I'm quite unsure I have this right, since seems to work so badly (frequent busy timeouts) - but from the docs this seems
198 * clear to be the best answer (best fit with the rest of how Stroika works).
199 */
200 static inline constexpr auto kDefault_ThreadingMode = ThreadingMode::eMultiThread;
201
202 /**
203 * This can generally be ignored, and primarily affects low level OS interface locking choices.
204 * @see https://www.sqlite.org/vfs.html
205 */
206 optional<String> fVFS;
207
208 /**
209 * If a database is opened readonly, updates will fail, and if the database doesn't exist, it will not be automatically created.
210 */
211 bool fReadOnly{false};
212
213 /**
214 * The immutable query parameter is a boolean that signals to SQLite that the underlying database file is held on read-only media and
215 * cannot be modified, even by another process with elevated privileges.
216 *
217 * \pre fImmutable ==> fReadOnly
218 */
219 bool fImmutable{false};
220
221 /**
222 * This is only useful if the database can be opened by multiple threads of control (multiple threads with connections
223 * within the same app, or multiple applications).
224 *
225 * @see also https://www.sqlite.org/c3ref/busy_timeout.html
226 *
227 * This seems black magic. I try different values at random, and get inscrutable results when used (or not used).
228 * Example in SQLite docs says 100ms. It turns out, in my limited testing, that appears to work best. But what makes
229 * no sense, is that I generally get MORE busy timeout errors if I use a much larger value (like 500ms, or 5000ms).
230 * Its ALMOST as if the database was holding a lock (one connection) while retrying?
231 *
232 * SQLITE appears to default this value to 0s, but that causes sporadic busy_timeouts in many tests. I think (empirically) - 1s, or maybe 10s
233 * maybe better. Still early to say. Specify your own if you care, else see kBusyTimeout_Default.
234 */
235 optional<Duration> fBusyTimeout;
236
237 /**
238 */
239 static constexpr inline chrono::seconds kBusyTimeout_Default = 10s;
240
241 /**
242 * \note - see JournalModeType and Connection::Ptr::journalMode
243 */
244 optional<JournalModeType> fJournalMode;
245 };
246
247 /**
248 * Connection provides an API for accessing an SQLite database.
249 *
250 * A new Connection::Ptr is typically created SQLite::Connection::New()
251 *
252 * \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>
253 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
254 * threadsafe (even if accessed across processes) - depending on its construction Options::ThreadSafety
255 *
256 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
257 * of Options::fThreadingMode when the database is constructed.
258 *
259 * @see https://www.sqlite.org/threadsafe.html
260 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
261 *
262 * NOTE - two Connection::Ptr objects referring to the same underlying REP is NOT (probably) safe with SQLITE. But referring
263 * to the same database is safe.
264 *
265 */
266 class Ptr : public Database::Document::Connection::Ptr {
267 private:
268 using inherited = Database::Document::Connection::Ptr;
269
270 public:
271 /**
272 */
273 Ptr (const Ptr& src);
274 Ptr (const shared_ptr<IRep>& src = nullptr);
275
276 public:
277 ~Ptr () = default;
278
279 public:
280 /**
281 */
282 nonvirtual Ptr& operator= (const Ptr& src);
283 nonvirtual Ptr& operator= (Ptr&& src) noexcept;
284
285 public:
286 /**
287 */
288 nonvirtual IRep* operator->() const noexcept;
289
290 public:
291 /**
292 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the SQLite API.
293 */
294 nonvirtual ::sqlite3* Peek () const;
295
296 public:
297 /**
298 * When doing a query that would have failed due to SQL_BUSY timeout, SQLite will wait
299 * and retry up to this long, to avoid the timeout.
300 */
301 Common::Property<Duration> busyTimeout;
302
303 public:
304 /**
305 * This can significantly affect database performance, and reliability.
306 */
307 Common::Property<JournalModeType> journalMode;
308
309 public:
310 /**
311 */
312 nonvirtual void Exec (const String& sql);
313 };
314
315 /**
316 * \brief create an SQLite database connection object, guided by argument Options.
317 */
318 Ptr New (const Options& options);
319
320 /**
321 * Connection provides an API for accessing an SQLite database.
322 *
323 * Typically don't use this directly, but use Connection::Ptr, a smart ptr wrapper on this interface.
324 *
325 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
326 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
327 * threadsafe (even if accessed across processes) - depending on its construction OOptions::ThreadSafety
328 *
329 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
330 * of Options::fThreadingMode when the database is constructed.
331 *
332 * @see https://www.sqlite.org/threadsafe.html
333 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
334 *
335 * NOTE ALSO - its POSSIBLE we could lift this Debug::AssertExternallySynchronizedMutex code / restriction.
336 * But sqlite docs not super clear. Maybe I need to use their locking APIs myself internally to use
337 * those locks to make a sequence of bindings safe? But for now just don't assume this is threadsafe and we'll be OK.
338 */
339 class IRep : public Database::Document::Connection::IRep {
340 public:
341 /**
342 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the SQLite API.
343 */
344 virtual ::sqlite3* Peek () = 0;
345
346 public:
347 /**
348 * Fetched dynamically with pragma busy_timeout;
349 */
350 virtual Duration GetBusyTimeout () const = 0;
351
352 public:
353 /**
354 * \pre timeout >= 0
355 */
356 virtual void SetBusyTimeout (const Duration& timeout) = 0;
357
358 public:
359 /**
360 */
361 virtual JournalModeType GetJournalMode () const = 0;
362
363 public:
364 /**
365 */
366 virtual void SetJournalMode (JournalModeType journalMode) = 0;
367
368 public:
369 /**
370 */
371 virtual void Exec (const String& sql) = 0;
372
373 public:
374 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySynchronizedMutex;
375
376 private:
377 friend class Ptr;
378 };
379
380 }
381
382 /**
383 * \see https://www.sqlite.org/lang_transaction.html
384 *
385 * \note Transactions are not required. This is for explicit transactions. If you omit
386 * using transactions, SQLite creates mini transactions automatically for each statement.
387 *
388 * \note Nested transactions not supported
389 *
390 * \todo Consider supporting SQLITE SAVEPOINT (like nested transaction)
391 */
392 class Transaction : public Database::Document::Transaction {
393 private:
394 using inherited = Database::Document::Transaction;
395
396 public:
397 enum Flag {
398 /**
399 * Don't really start the transaction until the command to read/update the database
400 */
401 eDeferred,
402
403 /**
404 * Start writing to the DB immediately (as of the transaction start); note this affects when you might
405 * get SQL_BUSY errors.
406 */
407 eImmediate,
408
409 /**
410 * Depends on WAL mode, but generally prevents other database connections from reading the
411 * database while the transaction is underway.
412 */
413 eExclusive,
414
415 eDEFAULT = eDeferred
416 };
417
418 public:
419 /**
420 */
421 Transaction () = delete;
422 Transaction (const Connection::Ptr& db, Flag f = Flag::eDEFAULT);
423 Transaction (const Transaction&) = delete;
424
425 private:
426 struct MyRep_;
427 };
428#endif
429
430}
431
432/*
433 ********************************************************************************
434 ***************************** Implementation Details ***************************
435 ********************************************************************************
436 */
437#include "SQLite.inl"
438
439#endif /*_Stroika_Foundation_Database_Document_SQLite_h_*/