Stroika Library 3.0d18
 
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 optional<Duration> fBusyTimeout;
233
234 /**
235 * \note - see JournalModeType and Connection::Ptr::journalMode
236 */
237 optional<JournalModeType> fJournalMode;
238 };
239
240 /**
241 * Connection provides an API for accessing an SQLite database.
242 *
243 * A new Connection::Ptr is typically created SQLite::Connection::New()
244 *
245 * \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>
246 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
247 * threadsafe (even if accessed across processes) - depending on its construction Options::ThreadSafety
248 *
249 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
250 * of Options::fThreadingMode when the database is constructed.
251 *
252 * @see https://www.sqlite.org/threadsafe.html
253 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
254 *
255 * NOTE - two Connection::Ptr objects referring to the same underlying REP is NOT (probably) safe with SQLITE. But referring
256 * to the same database is safe.
257 *
258 */
259 class Ptr : public Database::Document::Connection::Ptr {
260 private:
261 using inherited = Database::Document::Connection::Ptr;
262
263 public:
264 /**
265 */
266 Ptr (const Ptr& src);
267 Ptr (const shared_ptr<IRep>& src = nullptr);
268
269 public:
270 ~Ptr () = default;
271
272 public:
273 /**
274 */
275 nonvirtual Ptr& operator= (const Ptr& src);
276 nonvirtual Ptr& operator= (Ptr&& src) noexcept;
277
278 public:
279 /**
280 */
281 nonvirtual IRep* operator->() const noexcept;
282
283 public:
284 /**
285 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the sqlite API.
286 */
287 nonvirtual ::sqlite3* Peek () const;
288
289 public:
290 /**
291 * When doing a query that would have failed due to SQL_BUSY timeout, sqlite will wait
292 * and retry up to this long, to avoid the timeout.
293 */
294 Common::Property<Duration> busyTimeout;
295
296 public:
297 /**
298 * This can significantly affect database performance, and reliability.
299 */
300 Common::Property<JournalModeType> journalMode;
301
302 public:
303 /**
304 */
305 nonvirtual void Exec (const String& sql);
306 };
307
308 /**
309 * \brief create an SQLite database connection object, guided by argument Options.
310 */
311 Ptr New (const Options& options);
312
313 /**
314 * Connection provides an API for accessing an SQLite database.
315 *
316 * Typically don't use this directly, but use Connection::Ptr, a smart ptr wrapper on this interface.
317 *
318 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
319 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
320 * threadsafe (even if accessed across processes) - depending on its construction OOptions::ThreadSafety
321 *
322 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
323 * of Options::fThreadingMode when the database is constructed.
324 *
325 * @see https://www.sqlite.org/threadsafe.html
326 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
327 *
328 * NOTE ALSO - its POSSIBLE we could lift this Debug::AssertExternallySynchronizedMutex code / restriction.
329 * But sqlite docs not super clear. Maybe I need to use their locking APIs myself internally to use
330 * those locks to make a sequence of bindings safe? But for now just don't assume this is threadsafe and we'll be OK.
331 */
332 class IRep : public Database::Document::Connection::IRep {
333 public:
334 /**
335 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the sqlite API.
336 */
337 virtual ::sqlite3* Peek () = 0;
338
339 public:
340 /**
341 * Fetched dynamically with pragma busy_timeout;
342 */
343 virtual Duration GetBusyTimeout () const = 0;
344
345 public:
346 /**
347 * \pre timeout >= 0
348 */
349 virtual void SetBusyTimeout (const Duration& timeout) = 0;
350
351 public:
352 /**
353 */
354 virtual JournalModeType GetJournalMode () const = 0;
355
356 public:
357 /**
358 */
359 virtual void SetJournalMode (JournalModeType journalMode) = 0;
360
361 public:
362 /**
363 */
364 virtual void Exec (const String& sql) = 0;
365
366 public:
367 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySynchronizedMutex;
368
369 private:
370 friend class Ptr;
371 };
372
373 }
374
375 /**
376 * \see https://www.sqlite.org/lang_transaction.html
377 *
378 * \note Transactions are not required. This is for explicit transactions. If you omit
379 * using transactions, sqlite creates mini transactions automatically for each statement.
380 *
381 * \note Nested transactions not supported
382 *
383 * \todo Consider supporting SQLITE SAVEPOINT (like nested transaction)
384 */
385 class Transaction : public Database::Document::Transaction {
386 private:
387 using inherited = Database::Document::Transaction;
388
389 public:
390 enum Flag {
391 /**
392 * Don't really start the transaction until the command to read/update the database
393 */
394 eDeferred,
395
396 /**
397 * Start writing to the DB immediately (as of the transaction start); note this affects when you might
398 * get SQL_BUSY errors.
399 */
400 eImmediate,
401
402 /**
403 * Depends on WAL mode, but generally prevents other database connections from reading the
404 * database while the transaction is underway.
405 */
406 eExclusive,
407
408 eDEFAULT = eDeferred
409 };
410
411 public:
412 /**
413 */
414 Transaction () = delete;
415 Transaction (const Connection::Ptr& db, Flag f = Flag::eDEFAULT);
416 Transaction (const Transaction&) = delete;
417
418 private:
419 struct MyRep_;
420 };
421#endif
422
423}
424
425/*
426 ********************************************************************************
427 ***************************** Implementation Details ***************************
428 ********************************************************************************
429 */
430#include "SQLite.inl"
431
432#endif /*_Stroika_Foundation_Database_Document_SQLite_h_*/