Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
SQL/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 * \note using a named in-memory-db allows two separate threads in the same process, to share the same database.
155 */
156 optional<String> fInMemoryDB;
157
158 /**
159 * @see https://www.sqlite.org/compile.html#threadsafe
160 *
161 * Note this refers to the threading mode for the underlying database. A Connection object is always single-threaded/externally
162 * synchronized.
163 */
164 enum class ThreadingMode {
165 /**
166 * SQLITE_OPEN_FULLMUTEX
167 * In this mode, all mutexes are disabled and SQLite is unsafe to use in more than a single thread at once
168 */
169 eSingleThread,
170
171 /**
172 * SQLITE_OPEN_NOMUTEX
173 * 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.
174 * (Stroika Debug::AssertExternallySynchronizedMutex enforces this)
175 *
176 * This may not always be available depending on how SQLite was compiled, but we dont have access to SQLITE_THREADSAFE at compile time
177 * (since just defined in C file from Stroika/ThirdPartyComponents/sqlite/Makefile);
178 * call sqlite3_threadsafe, to see if this is enabled
179 */
180 eMultiThread,
181
182 /**
183 * SQLITE_OPEN_FULLMUTEX
184 * In serialized mode, SQLite can be safely used by multiple threads with no restriction.
185 * (note even in this mode, each connection is Debug::AssertExternallySynchronizedMutex)
186 *
187 * This may not always be available depending on how SQLite was compiled, but we dont have access to SQLITE_THREADSAFE at compile time
188 * (since just defined in C file from Stroika/ThirdPartyComponents/sqlite/Makefile);
189 * call sqlite3_threadsafe, to see if this is enabled
190 *
191 * \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
192 * issues - maybe we need to do lock around call to each function to avoid making this mode nearly pointless).
193 */
194 eSerialized,
195 };
196 optional<ThreadingMode> fThreadingMode;
197
198 /**
199 * I'm quite unsure I have this right, since seems to work so badly (frequent busy timeouts) - but from the docs this seems
200 * clear to be the best answer (best fit with the rest of how Stroika works).
201 */
202 static inline constexpr auto kDefault_ThreadingMode = ThreadingMode::eMultiThread;
203
204 /**
205 * This can generally be ignored, and primarily affects low level OS interface locking choices.
206 * @see https://www.sqlite.org/vfs.html
207 */
208 optional<String> fVFS;
209
210 /**
211 * If a database is opened readonly, updates will fail, and if the database doesn't exist, it will not be automatically created.
212 */
213 bool fReadOnly{false};
214
215 /**
216 * The immutable query parameter is a boolean that signals to SQLite that the underlying database file is held on read-only media and
217 * cannot be modified, even by another process with elevated privileges.
218 *
219 * \pre fImmutable ==> fReadOnly
220 */
221 bool fImmutable{false};
222
223 /**
224 * This is only useful if the database can be opened by multiple threads of control (multiple threads with connections
225 * within the same app, or multiple applications).
226 *
227 * @see also https://www.sqlite.org/c3ref/busy_timeout.html
228 *
229 * This seems black magic. I try different values at random, and get inscrutable results when used (or not used).
230 * Example in sqlite docs says 100ms. It turns out, in my limited testing, that appears to work best. But what makes
231 * no sense, is that I generally get MORE busy timeout errors if I use a much larger value (like 500ms, or 5000ms).
232 * Its ALMOST as if the database was holding a lock (one connection) while retrying?
233 *
234 * SQLITE appears to default this value to 0s, but that causes sporadic busy_timeouts in many tests. I think (empirically) - 1s, or maybe 10s
235 * maybe better. Still early to say. Specify your own if you care, else see kBusyTimeout_Default.
236 */
237 optional<Duration> fBusyTimeout;
238
239 /**
240 */
241 static constexpr inline chrono::seconds kBusyTimeout_Default = 10s;
242
243 /**
244 * \note - see JournalModeType and Connection::Ptr::journalMode
245 */
246 optional<JournalModeType> fJournalMode;
247 };
248
249 /**
250 * Connection provides an API for accessing an SQLite database.
251 *
252 * A new Connection::Ptr is typically created SQLite::Connection::New()
253 *
254 * \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>
255 * But though each connection can only be accessed from a single thread at a time, the underlying database may be
256 * threadsafe (even if accessed across processes) - depending on its construction Options::ThreadSafety
257 *
258 * The Connection itself is standardC++ thread safety. The thread-safety of the underlying database depends on the setting
259 * of Options::fThreadingMode when the database is constructed.
260 *
261 * @see https://www.sqlite.org/threadsafe.html
262 * We set SQLITE_OPEN_NOMUTEX on open (so mode Multi-thread, but not Serialized).
263 *
264 * NOTE - two Connection::Ptr objects referring to the same underlying REP is NOT (probably) safe with SQLITE. But referring
265 * to the same database is safe.
266 *
267 */
268 class Ptr : public SQL::Connection::Ptr {
269 private:
270 using inherited = SQL::Connection::Ptr;
271
272 public:
273 /**
274 */
275 Ptr (const Ptr& src);
276 Ptr (const shared_ptr<IRep>& src = nullptr);
277
278 public:
279 ~Ptr () = default;
280
281 public:
282 /**
283 */
284 nonvirtual Ptr& operator= (const Ptr& src);
285 nonvirtual Ptr& operator= (Ptr&& src) noexcept;
286
287 public:
288 /**
289 */
290 nonvirtual IRep* operator->() const noexcept;
291
292 public:
293 /**
294 * Use of Peek () is discouraged, and unsafe, but allowed for now because we don't have a full wrapper on the SQLite API.
295 */
296 nonvirtual ::sqlite3* Peek () const;
297
298 public:
299 /**
300 * When doing a query that would have failed due to SQL_BUSY timeout, SQLite will wait
301 * and retry up to this long, to avoid the timeout.
302 */
303 Common::Property<Duration> busyTimeout;
304
305 public:
306 /**
307 * This can significantly affect database performance, and reliability.
308 */
309 Common::Property<JournalModeType> journalMode;
310
311 private:
312 friend class Statement;
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 Options::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 SQL::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 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySynchronizedMutex;
370
371 private:
372 friend class Ptr;
373 };
374
375 }
376
377 /**
378 * \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>
379 * See notes about thread safety for Connection::Ptr - since this copies around a Connection::Ptr.
380 */
381 class Statement : public SQL::Statement {
382 private:
383 using inherited = SQL::Statement;
384
385 public:
386 /**
387 */
388 Statement () = delete;
389 Statement (const Connection::Ptr& db, const String& query);
390 Statement (const Statement&) = delete;
391
392 private:
393 struct MyRep_;
394 };
395
396 /**
397 * \see https://www.sqlite.org/lang_transaction.html
398 *
399 * \note Transactions are not required. This is for explicit transactions. If you omit
400 * using transactions, SQLite creates mini transactions automatically for each statement.
401 *
402 * \note Nested transactions not supported
403 *
404 * \todo Consider supporting SQLITE SAVEPOINT (like nested transaction)
405 */
406 class Transaction : public SQL::Transaction {
407 private:
408 using inherited = SQL::Transaction;
409
410 public:
411 enum Flag {
412 /**
413 * Don't really start the transaction until the command to read/update the database
414 */
415 eDeferred,
416
417 /**
418 * Start writing to the DB immediately (as of the transaction start); note this affects when you might
419 * get SQL_BUSY errors.
420 */
421 eImmediate,
422
423 /**
424 * Depends on WAL mode, but generally prevents other database connections from reading the
425 * database while the transaction is underway.
426 */
427 eExclusive,
428
429 eDEFAULT = eDeferred
430 };
431
432 public:
433 /**
434 */
435 Transaction () = delete;
436 Transaction (const Connection::Ptr& db, Flag f = Flag::eDEFAULT);
437 Transaction (const Transaction&) = delete;
438
439 private:
440 struct MyRep_;
441 };
442#endif
443
444}
445
446/*
447 ********************************************************************************
448 ***************************** Implementation Details ***************************
449 ********************************************************************************
450 */
451#include "SQLite.inl"
452
453#endif /*_Stroika_Foundation_Database_SQL_SQLite_h_*/