4#include "Stroika/Foundation/StroikaPreComp.h"
19using namespace Characters;
20using namespace Containers;
22using namespace DataExchange;
23using namespace Database;
24using namespace Database::Document::SQLite;
25using namespace Execution;
33using Database::Document::kID;
34using Database::Document::kOnlyIDs;
41#if qStroika_HasComponent_sqlite
43 struct ModuleShutdown_ {
46 Verify (::sqlite3_shutdown () == SQLITE_OK);
49 [[noreturn]]
void ThrowSQLiteError_ (
int errCode, sqlite3* sqliteConnection)
51 Require (errCode != SQLITE_OK);
52 optional<String> errMsgDetails;
53 if (sqliteConnection !=
nullptr) {
54 errMsgDetails = String::FromUTF8 (::sqlite3_errmsg (sqliteConnection));
59 Throw (system_error{make_error_code (errc::device_or_resource_busy)});
63 Throw (system_error{make_error_code (errc::device_or_resource_busy)});
65 case SQLITE_CONSTRAINT: {
70 static const auto kEx_ =
Exception{
"SQLITE_CONSTRAINT"sv};
75 static const auto kEx_ =
Exception{
"SQLITE_TOOBIG"sv};
80 Throw (system_error{make_error_code (errc::no_space_on_device)});
82 case SQLITE_READONLY: {
83 static const auto kEx_ =
Exception{
"SQLITE_READONLY"sv};
91 static const auto kEx_ =
Exception{
"SQLITE_MISUSE"sv};
100 static const auto kEx_ =
Exception{
"SQLITE_ERROR"sv};
105 DbgTrace (
"SQLITE_NOMEM translated to bad_alloc"_f);
110 Throw (
Exception{
"SQLite Error: {} (code {})"_f(errMsgDetails, errCode)});
116 void ThrowSQLiteErrorIfNotOK_ (
int errCode, sqlite3* sqliteConnection)
118 static_assert (SQLITE_OK == 0);
119 if (errCode != SQLITE_OK) [[unlikely]] {
120 ThrowSQLiteError_ (errCode, sqliteConnection);
127 template <invocable<
int,
char**,
char**> CB>
128 struct SQLiteCallback_ {
131 using STATIC_FUNCTION_TYPE = int (*) (
void*, int,
char**,
char**);
133 SQLiteCallback_ (CB&& cb)
134 : fCallback_{forward<CB> (cb)}
137 STATIC_FUNCTION_TYPE GetStaticFunction ()
147 static int STATICFUNC_ (
void* SQLiteCallbackData,
int argc,
char** argv,
char** azColName)
149 SQLiteCallback_* sqc =
reinterpret_cast<SQLiteCallback_*
> (SQLiteCallbackData);
150 return sqc->fCallback_ (argc, argv, azColName);
155 struct MyPreparedStatement_ {
156 MyPreparedStatement_ () =
default;
157 MyPreparedStatement_ (sqlite3* db,
const String& statement)
160#if USE_NOISY_TRACE_IN_THIS_MODULE_
163 const char* pzTail =
nullptr;
164 string utfStatement = statement.
AsUTF8<
string> ();
165 ThrowSQLiteErrorIfNotOK_ (::sqlite3_prepare_v2 (db, utfStatement.c_str (), -1, &fObj_, &pzTail), db);
166 Assert (pzTail !=
nullptr);
167 Require (*pzTail ==
'\0');
170 MyPreparedStatement_ (
const MyPreparedStatement_&) =
delete;
171 MyPreparedStatement_ (MyPreparedStatement_&&) noexcept = default;
172 ~MyPreparedStatement_ ()
174 if (fObj_ !=
nullptr) {
175 (void)::sqlite3_finalize (fObj_);
178 MyPreparedStatement_& operator= (
const MyPreparedStatement_&) =
delete;
179 MyPreparedStatement_& operator= (MyPreparedStatement_&& rhs)
noexcept
185 operator sqlite3_stmt* ()
const
191 sqlite3_stmt* fObj_{
nullptr};
193 static_assert (movable<MyPreparedStatement_>);
194 static_assert (not copyable<MyPreparedStatement_>);
203 struct VerifyFlags_ {
206 Assert (CompiledOptions::kThe.ENABLE_NORMALIZE == !!::sqlite3_compileoption_used (
"ENABLE_NORMALIZE"));
207 Assert (CompiledOptions::kThe.THREADSAFE == !!::sqlite3_compileoption_used (
"THREADSAFE"));
208#if SQLITE_VERSION_NUMBER < 3038000
209 Assert (::sqlite3_compileoption_used (
"ENABLE_JSON1"));
211 Assert (!::sqlite3_compileoption_used (
"OMIT_JSON1"));
221 tuple<optional<String>, optional<Filter>> Partition_ (
const optional<Filter>& filter)
233 bool transferred =
false;
234 if (
const Document::FilterElements::Equals* eqOp = get_if<Document::FilterElements::Equals> (&op)) {
236 if (not whereClause.
empty ()) {
237 whereClause <<
" AND "sv;
240 if (eqOp->fLHS == Database::Document::kID) {
241 whereClause <<
"{} = '{}'"_f(Database::Document::kID, rhsValue->As<
String> ());
246 if (rhsValue->GetType () == VariantValue::eString or rhsValue->GetType () == VariantValue::eDate or
247 rhsValue->GetType () == VariantValue::eDateTime) {
248 vSQL =
"'"sv + vSQL +
"'"sv;
250 whereClause <<
"json_extract(json, '$.{}') = {}"_f(
String{eqOp->fLHS}, vSQL);
255 if (not transferred) {
259 if (not whereClause.
empty ()) {
261 return make_tuple (whereClause, clientSideOps.
empty () ? optional<Filter>{} : make_optional (
Filter{clientSideOps}));
264 return make_tuple (nullopt, filter);
266 return make_tuple (nullopt, nullopt);
275 tuple<optional<tuple<String, Sequence<String>>>, optional<Projection>> Partition_ (
const optional<Projection>& p)
281 tuple<Document::Projection::Flag, Set<String>> fields = p->GetFields ();
282 Require (get<1> (fields).size () >= 1);
283 if (get<Document::Projection::Flag> (fields) == Document::Projection::Flag::eInclude) {
286 for (
String f : get<1> (fields)) {
287 String mongoFieldName = f;
288 if (fieldNames.
empty ()) {
289 projectionQuery <<
"json_extract(json,'";
292 projectionQuery <<
","sv;
294 projectionQuery <<
"$."sv << mongoFieldName;
297 if (not fieldNames.
empty ()) {
298 projectionQuery <<
"')"sv;
300 return make_tuple (make_tuple (projectionQuery, fieldNames), nullopt);
303 return make_tuple (nullopt, p);
306 return make_tuple (nullopt, nullopt);
310 Document::Document ExtractRowValueAfterStep_ (sqlite3_stmt* statement,
int dataCol,
const IDType&
id,
312 const optional<Projection>& remainingProjection)
319 Variant::JSON::Reader{}.Read (String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, dataCol))));
321 if (sqliteProjection == nullopt) {
325 auto arrayOfFieldNames = get<Sequence<String>> (*sqliteProjection);
326 if (arrayOfFieldNames.size () == 1) {
330 Assert (valueReadBackFromDB.GetType () == VariantValue::eArray);
335 Assert (nameI == arrayOfFieldNames.end ());
338 if ((sqliteProjection and get<
Sequence<String>> (*sqliteProjection).
Contains (Document::kID)) or remainingProjection == nullopt or
339 remainingProjection->Includes (Document::kID)) {
340 dr.
Add (Document::kID,
id);
342 if (remainingProjection) {
343 dr = remainingProjection->
Apply (dr);
350 using Connection::Options;
351 struct ConnectionRep_ final : Database::Document::SQLite::Connection::IRep {
357 shared_ptr<ConnectionRep_> fConnectionRep_;
360 MyPreparedStatement_ fAddStatement_{};
361 MyPreparedStatement_ fGetOneStatement_{};
362 MyPreparedStatement_ fRemoveStatement_{};
363 MyPreparedStatement_ fUpdateStatement_{};
365 CollectionRep_ (
const shared_ptr<ConnectionRep_>& connectionRep,
const String& collectionName)
366 : fConnectionRep_{connectionRep}
367 , fTableName_{collectionName}
369#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
370 fAssertExternallySynchronizedMutex_.SetAssertExternallySynchronizedMutexContext (
371 connectionRep->fAssertExternallySynchronizedMutex_.GetSharedContext ());
374 virtual ~CollectionRep_ () =
default;
377#if USE_NOISY_TRACE_IN_THIS_MODULE_
389 if (fAddStatement_ ==
nullptr) [[unlikely]] {
390 fAddStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"insert into {} (json) values(?);"_f(fTableName_)};
392 string jsonText = Variant::JSON::Writer{}.WriteAsString (
VariantValue{v}).AsUTF8<string> ();
393 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fAddStatement_), fConnectionRep_->fDB_);
394 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fAddStatement_, 1, jsonText.c_str (),
static_cast<int> (jsonText.length ()), SQLITE_TRANSIENT),
395 fConnectionRep_->fDB_);
396 int rc = ::sqlite3_step (fAddStatement_);
397 if (rc != SQLITE_DONE) {
398 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
400 return "{}"_f(sqlite3_last_insert_rowid (fConnectionRep_->fDB_));
402 virtual optional<Document::Document> GetOne (
const IDType&
id,
const optional<Projection>& projection)
override
404#if USE_NOISY_TRACE_IN_THIS_MODULE_
405 TraceContextBumper ctx{
"SQLite::CollectionRep_::GetOne()",
"id={}, projection={}"_f, id, projection};
410 auto [sqliteProjection, remainingAfterProjection] = Partition_ (projection);
411 optional<MyPreparedStatement_> sqliteProjectionStatement;
412 if (sqliteProjection) {
413 sqliteProjectionStatement.emplace (fConnectionRep_->fDB_,
414 "select {} from {} where id=?;"_f(get<String> (*sqliteProjection), fTableName_));
416 else if (fGetOneStatement_ ==
nullptr) [[unlikely]] {
417 fGetOneStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"select json from {} where id=?;"_f(fTableName_)};
419 sqlite3_stmt* useStatment = sqliteProjectionStatement.has_value () ? *sqliteProjectionStatement : fGetOneStatement_;
422 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (useStatment), fConnectionRep_->fDB_);
423 string idAsUTFSTR =
id.AsUTF8<
string> ();
424 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (useStatment, 1, idAsUTFSTR.c_str (),
static_cast<int> (idAsUTFSTR.length ()), SQLITE_TRANSIENT),
425 fConnectionRep_->fDB_);
427 int rc = ::sqlite3_step (useStatment);
428 optional<Document::Document> result;
429 if (rc == SQLITE_ROW) [[likely]] {
430 result = ExtractRowValueAfterStep_ (useStatment, 0,
id, sqliteProjection, remainingAfterProjection);
431 rc = ::sqlite3_step (useStatment);
433 if (rc != SQLITE_DONE) [[unlikely]] {
434 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
440#if USE_NOISY_TRACE_IN_THIS_MODULE_
441 TraceContextBumper ctx{
"SQLite::CollectionRep_::GetAll()",
"filter={}, projection={}"_f, filter, projection};
447 if (filter == nullopt and projection == nullopt) {
448 MyPreparedStatement_ statement{fConnectionRep_->fDB_,
"select id,json from {};"_f(fTableName_)};
449 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
451 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
452 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
454 Variant::JSON::Reader{}.Read (String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 1))));
456 vDoc.
Add (Document::kID,
id);
459 if (rc != SQLITE_DONE) [[unlikely]] {
460 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
463 else if (filter == nullopt and projection == kOnlyIDs) {
464 MyPreparedStatement_ statement{fConnectionRep_->fDB_,
"select id from {};"_f(fTableName_)};
465 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
467 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
468 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
470 vDoc.
Add (Document::kID,
id);
473 if (rc != SQLITE_DONE) [[unlikely]] {
474 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
479 auto [sqliteWhereClause, remainingFilter] = Partition_ (filter);
485 auto [sqliteProjection, remainingAfterProjection] = remainingFilter ? make_tuple (nullopt, projection) : Partition_ (projection);
487 MyPreparedStatement_ statement{
488 fConnectionRep_->fDB_,
489 "select id,{} from {} {};"_f(sqliteProjection == nullopt ?
"json"_k : get<
String> (*sqliteProjection), fTableName_,
490 sqliteWhereClause == nullopt ?
"" : (
"where "_k + *sqliteWhereClause))};
492 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
494 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
495 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
496 Document::Document vDoc = ExtractRowValueAfterStep_ (statement, 1,
id, sqliteProjection, remainingAfterProjection);
497 if (remainingFilter == nullopt or remainingFilter->Matches (vDoc)) {
498 if (remainingAfterProjection) {
499 vDoc = remainingAfterProjection->
Apply (vDoc);
504 if (rc != SQLITE_DONE) [[unlikely]] {
505 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
512#if USE_NOISY_TRACE_IN_THIS_MODULE_
517 if (onlyTheseFields) {
522 Document::Document d2Update = onlyTheseFields ? Memory::ValueOfOrThrow (this->GetOne (
id, nullopt)) : uploadDoc;
524 if (onlyTheseFields) {
525 d2Update.
AddAll (uploadDoc);
530 if (fUpdateStatement_ ==
nullptr) [[unlikely]] {
531 fUpdateStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"update {} SET json=? where id=?;"_f(fTableName_)};
533 string r = Variant::JSON::Writer{}.WriteAsString (
VariantValue{d2Update}).AsUTF8<string> ();
534 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fUpdateStatement_), fConnectionRep_->fDB_);
535 string idText =
id.AsUTF8<
string> ();
536 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fUpdateStatement_, 1, r.c_str (),
static_cast<int> (r.length ()), SQLITE_TRANSIENT),
537 fConnectionRep_->fDB_);
538 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fUpdateStatement_, 2, idText.c_str (),
static_cast<int> (idText.length ()), SQLITE_TRANSIENT),
539 fConnectionRep_->fDB_);
540 int rc = ::sqlite3_step (fUpdateStatement_);
541 if (rc != SQLITE_DONE) {
542 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
545 virtual void Remove (
const IDType&
id)
override
547#if USE_NOISY_TRACE_IN_THIS_MODULE_
551 if (fRemoveStatement_ ==
nullptr) [[unlikely]] {
552 fRemoveStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"delete from {} where id=?;"_f(fTableName_)};
554 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fRemoveStatement_), fConnectionRep_->fDB_);
555 string idText =
id.AsUTF8<
string> ();
556 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fRemoveStatement_, 1, idText.c_str (),
static_cast<int> (idText.length ()), SQLITE_TRANSIENT),
557 fConnectionRep_->fDB_);
558 int rc = ::sqlite3_step (fRemoveStatement_);
559 if (rc != SQLITE_DONE) {
560 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
565 ConnectionRep_ (
const Options& options)
571 switch (options.fThreadingMode.value_or (Options::kDefault_ThreadingMode)) {
572 case Options::ThreadingMode::eSingleThread:
574 case Options::ThreadingMode::eMultiThread:
575 Require (CompiledOptions::kThe.THREADSAFE);
576 Require (::sqlite3_threadsafe ());
577 flags |= SQLITE_OPEN_NOMUTEX;
579 case Options::ThreadingMode::eSerialized:
580 Require (CompiledOptions::kThe.THREADSAFE);
581 Require (::sqlite3_threadsafe ());
582 flags |= SQLITE_OPEN_FULLMUTEX;
586 if (options.fImmutable) {
589 Require (options.fReadOnly);
591 flags |= options.fReadOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
595 [[maybe_unused]]
int n{};
596 if (options.fDBPath) {
599 if (options.fTemporaryDB) {
602 if (options.fInMemoryDB) {
607 if (options.fDBPath) {
608 uriArg = options.fDBPath->generic_string ();
609 if (uriArg[0] ==
':') {
610 uriArg =
"./" + uriArg;
613 if (options.fTemporaryDB) {
617 Require (not options.fTemporaryDB->empty ());
619 if (options.fInMemoryDB) {
626 flags |= SQLITE_OPEN_MEMORY;
627 flags |= SQLITE_OPEN_URI;
628 flags |= SQLITE_OPEN_SHAREDCACHE;
629 Require (not options.fReadOnly);
630 Require (options.fCreateDBPathIfDoesNotExist);
631 uriArg = options.fInMemoryDB->AsNarrowSDKString ();
632 if (uriArg.empty ()) {
636 u8string safeCharURI = IO::Network::UniformResourceIdentification::PCTEncode (u8string{uriArg.begin (), uriArg.end ()}, {});
637 uriArg =
"file:" +
string{safeCharURI.begin (), safeCharURI.end ()} +
"?mode=memory&cache=shared";
643 if ((e = ::sqlite3_open_v2 (uriArg.c_str (), &fDB_, flags, options.fVFS ? options.fVFS->AsNarrowSDKString ().c_str () : nullptr)) == SQLITE_CANTOPEN) {
644 if (options.fCreateDBPathIfDoesNotExist) {
645 if (fDB_ !=
nullptr) {
646 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
649 if ((e = ::sqlite3_open_v2 (uriArg.c_str (), &fDB_, SQLITE_OPEN_CREATE | flags,
650 options.fVFS ? options.fVFS->AsNarrowSDKString ().c_str () : nullptr)) == SQLITE_OK) {
655 if (e != SQLITE_OK) [[unlikely]] {
656 [[maybe_unused]]
auto&& cleanup =
Finally ([
this] ()
noexcept {
657 if (fDB_ !=
nullptr) {
658 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
661 ThrowSQLiteError_ (e, fDB_);
663 SetBusyTimeout (options.fBusyTimeout.value_or (Options::kBusyTimeout_Default));
664 if (options.fJournalMode) {
665 SetJournalMode (*options.fJournalMode);
672 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
674 virtual shared_ptr<const EngineProperties> GetEngineProperties ()
const override
677 virtual String GetEngineName ()
const override
682 static const shared_ptr<const EngineProperties> kProps_ = make_shared<const MyEngineProperties_> ();
689 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
691 results.
Add (String::FromUTF8 (argv[0]));
694 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"SELECT name FROM sqlite_master WHERE type='table';",
695 callback.GetStaticFunction (), callback.GetData (),
nullptr),
699 virtual void CreateCollection (
const String& name)
override
701 Exec (
"create table if not exists {} (id INTEGER PRIMARY KEY, json NOT NULL);"_f(name));
703 virtual void DropCollection (
const String& name)
override
705 Exec (
"drop table {};"_f(name));
711 make_shared<CollectionRep_> (Debug::UncheckedDynamicPointerCast<ConnectionRep_> (shared_from_this ()), name)};
715 Connection::Ptr conn = Connection::Ptr{Debug::UncheckedDynamicPointerCast<Connection::IRep> (shared_from_this ())};
716 return Database::Document::SQLite::Transaction{conn};
718 virtual void Exec (
const String& sql)
override
721 int e = ::sqlite3_exec (fDB_, sql.
AsUTF8<
string> ().c_str (),
nullptr,
nullptr,
nullptr);
722 if (e != SQLITE_OK) [[unlikely]] {
723 ThrowSQLiteErrorIfNotOK_ (e, fDB_);
726 virtual ::sqlite3* Peek ()
override
731 virtual Duration GetBusyTimeout ()
const override
735 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
737 Assert (::strcmp (azColName[0],
"timeout") == 0);
738 int val = ::atoi (argv[0]);
743 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma busy_timeout;", callback.GetStaticFunction (), callback.GetData (),
nullptr), fDB_);
745 return Duration{double (*d) / 1000.0};
747 virtual void SetBusyTimeout (
const Duration& timeout)
override
750 ThrowSQLiteErrorIfNotOK_ (::sqlite3_busy_timeout (fDB_, (
int)(timeout.
As<
float> () * 1000)), fDB_);
752 virtual JournalModeType GetJournalMode ()
const override
755 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
757 Assert (::strcmp (azColName[0],
"journal_mode") == 0);
761 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode;", callback.GetStaticFunction (), callback.GetData (),
nullptr), fDB_);
763 if (d ==
"delete"sv) {
764 return JournalModeType::eDelete;
766 if (d ==
"truncate"sv) {
767 return JournalModeType::eTruncate;
769 if (d ==
"persist"sv) {
770 return JournalModeType::ePersist;
772 if (d ==
"memory"sv) {
773 return JournalModeType::eMemory;
776 return JournalModeType::eWAL;
779 return JournalModeType::eWAL2;
782 return JournalModeType::eOff;
785 return JournalModeType::eDelete;
787 virtual void SetJournalMode (JournalModeType journalMode)
override
790 switch (journalMode) {
791 case JournalModeType::eDelete:
792 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'delete';",
nullptr, 0,
nullptr), fDB_);
794 case JournalModeType::eTruncate:
795 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'truncate';",
nullptr, 0,
nullptr), fDB_);
797 case JournalModeType::ePersist:
798 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'persist';",
nullptr, 0,
nullptr), fDB_);
800 case JournalModeType::eMemory:
801 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'memory';",
nullptr, 0,
nullptr), fDB_);
803 case JournalModeType::eWAL:
804 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'wal';",
nullptr, 0,
nullptr), fDB_);
806 case JournalModeType::eWAL2:
807 if (GetJournalMode () == JournalModeType::eWAL) {
808 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'delete';",
nullptr, 0,
nullptr), fDB_);
810 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'wal2';",
nullptr, 0,
nullptr), fDB_);
812 case JournalModeType::eOff:
813 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'off';",
nullptr, 0,
nullptr), fDB_);
827Document::SQLite::Connection::Ptr::Ptr (
const shared_ptr<IRep>& src)
829 , busyTimeout{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) {
830 const Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::busyTimeout);
832 return thisObj->operator->()->GetBusyTimeout ();
834 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
auto* property,
auto timeout) {
835 Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::busyTimeout);
837 thisObj->operator->()->SetBusyTimeout (timeout);
839 , journalMode{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
const auto* property) {
840 const Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::journalMode);
842 return thisObj->operator->()->GetJournalMode ();
844 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
auto* property,
auto journalMode) {
845 Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::journalMode);
847 thisObj->operator->()->SetJournalMode (journalMode);
850#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
851 if (src !=
nullptr) {
852 _fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (src->fAssertExternallySynchronizedMutex.GetSharedContext ());
862auto Document::SQLite::Connection::New (
const Options& options) -> Ptr
864 return Ptr{make_shared<ConnectionRep_> (options)};
873 MyRep_ (
const Connection::Ptr& db, Flag f)
874 : fConnectionPtr_{db}
877 case Flag::eDeferred:
878 db->Exec (
"BEGIN DEFERRED TRANSACTION;"sv);
880 case Flag::eExclusive:
881 db->Exec (
"BEGIN EXCLUSIVE TRANSACTION;"sv);
883 case Flag::eImmediate:
884 db->Exec (
"BEGIN IMMEDIATE TRANSACTION;"sv);
890 virtual void Commit ()
override
892 Require (not fCompleted_);
894 fConnectionPtr_->Exec (
"COMMIT TRANSACTION;"sv);
898 Require (not fCompleted_);
900 fConnectionPtr_->Exec (
"ROLLBACK TRANSACTION;"sv);
905 return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
907 Connection::Ptr fConnectionPtr_;
908 bool fCompleted_{
false};
910Transaction::Transaction (
const Connection::Ptr& db, Flag f)
911 : inherited{make_unique<MyRep_> (db, f)}
#define RequireNotReached()
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define WeakAssertNotImplemented()
#define RequireNotNull(p)
#define AssertNotReached()
variant< Equals > Operation
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual bool empty() const noexcept
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual T AsUTF8() const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual unsigned int AddAll(ITERABLE_OF_ADDABLE &&items, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual void RemoveAll()
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
nonvirtual Iterable< key_type > Keys() const
nonvirtual void RetainAll(const ITERABLE_OF_KEY_TYPE &items)
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void push_back(ArgByValueType< value_type > item)
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Add(ArgByValueType< value_type > item)
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
nonvirtual RETURNTYPE As() const
EngineProperties captures the features associated with a given database engine (being talked to throu...
define a (simple) projection on a document, subsetting the fields of that document.
virtual Disposition GetDisposition() const =0
virtual void Rollback()=0
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
Duration is a chrono::duration<double> (=.
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual bool Contains(ArgByValueType< T > element, EQUALS_COMPARER &&equalsComparer=EQUALS_COMPARER{}) const
nonvirtual size_t size() const
Returns the number of items contained.
nonvirtual bool empty() const
Returns true iff size() == 0.
An Iterator<T> is a copyable object which allows traversing the contents of some container....
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >