4#include "Stroika/Foundation/StroikaPreComp.h"
10#include "Stroika/Foundation/Common/GUID.h"
21using namespace Characters;
22using namespace Containers;
23using namespace Common;
25using namespace DataExchange;
26using namespace Database;
27using namespace Database::Document::SQLite;
28using namespace Execution;
36using Database::Document::kID;
37using Database::Document::kOnlyIDs;
44#if qStroika_HasComponent_sqlite
46 struct ModuleShutdown_ {
49 Verify (::sqlite3_shutdown () == SQLITE_OK);
52 [[noreturn]]
void ThrowSQLiteError_ (
int errCode, sqlite3* sqliteConnection)
54 Require (errCode != SQLITE_OK);
55 optional<String> errMsgDetails;
56 if (sqliteConnection !=
nullptr) {
57 errMsgDetails = String::FromUTF8 (::sqlite3_errmsg (sqliteConnection));
62 Throw (system_error{make_error_code (errc::device_or_resource_busy)});
66 Throw (system_error{make_error_code (errc::device_or_resource_busy)});
68 case SQLITE_CONSTRAINT: {
73 static const auto kEx_ =
Exception{
"SQLITE_CONSTRAINT"sv};
78 static const auto kEx_ =
Exception{
"SQLITE_TOOBIG"sv};
83 Throw (system_error{make_error_code (errc::no_space_on_device)});
85 case SQLITE_READONLY: {
86 static const auto kEx_ =
Exception{
"SQLITE_READONLY"sv};
94 static const auto kEx_ =
Exception{
"SQLITE_MISUSE"sv};
103 static const auto kEx_ =
Exception{
"SQLITE_ERROR"sv};
108 DbgTrace (
"SQLITE_NOMEM translated to bad_alloc"_f);
113 Throw (
Exception{
"SQLite Error: {} (code {})"_f(errMsgDetails, errCode)});
119 void ThrowSQLiteErrorIfNotOK_ (
int errCode, sqlite3* sqliteConnection)
121 static_assert (SQLITE_OK == 0);
122 if (errCode != SQLITE_OK) [[unlikely]] {
123 ThrowSQLiteError_ (errCode, sqliteConnection);
130 template <invocable<
int,
char**,
char**> CB>
131 struct SQLiteCallback_ {
134 using STATIC_FUNCTION_TYPE = int (*) (
void*, int,
char**,
char**);
136 SQLiteCallback_ (CB&& cb)
137 : fCallback_{forward<CB> (cb)}
140 STATIC_FUNCTION_TYPE GetStaticFunction ()
150 static int STATICFUNC_ (
void* SQLiteCallbackData,
int argc,
char** argv,
char** azColName)
152 SQLiteCallback_* sqc =
reinterpret_cast<SQLiteCallback_*
> (SQLiteCallbackData);
153 return sqc->fCallback_ (argc, argv, azColName);
158 struct MyPreparedStatement_ {
159 MyPreparedStatement_ () =
default;
160 MyPreparedStatement_ (::sqlite3* db,
const String& statement)
163#if USE_NOISY_TRACE_IN_THIS_MODULE_
166 const char* pzTail =
nullptr;
167 string utfStatement = statement.
AsUTF8<
string> ();
168 ThrowSQLiteErrorIfNotOK_ (::sqlite3_prepare_v2 (db, utfStatement.c_str (), -1, &fObj_, &pzTail), db);
169 Assert (pzTail !=
nullptr);
170 Require (*pzTail ==
'\0');
173 MyPreparedStatement_ (
const MyPreparedStatement_&) =
delete;
174 MyPreparedStatement_ (MyPreparedStatement_&&) noexcept = default;
175 ~MyPreparedStatement_ ()
177 if (fObj_ !=
nullptr) {
178 (void)::sqlite3_finalize (fObj_);
181 MyPreparedStatement_& operator= (
const MyPreparedStatement_&) =
delete;
182 MyPreparedStatement_& operator= (MyPreparedStatement_&& rhs)
noexcept
188 operator ::sqlite3_stmt* ()
const
194 ::sqlite3_stmt* fObj_{
nullptr};
196 static_assert (movable<MyPreparedStatement_>);
197 static_assert (not copyable<MyPreparedStatement_>);
206 struct VerifyFlags_ {
209 Assert (CompiledOptions::kThe.ENABLE_NORMALIZE == !!::sqlite3_compileoption_used (
"ENABLE_NORMALIZE"));
210 Assert (CompiledOptions::kThe.THREADSAFE == !!::sqlite3_compileoption_used (
"THREADSAFE"));
211#if SQLITE_VERSION_NUMBER < 3038000
212 Assert (::sqlite3_compileoption_used (
"ENABLE_JSON1"));
214 Assert (!::sqlite3_compileoption_used (
"OMIT_JSON1"));
224 tuple<optional<String>, optional<Filter>> Partition_ (
const optional<Filter>& filter)
236 bool transferred =
false;
237 if (
const Document::FilterElements::Equals* eqOp = get_if<Document::FilterElements::Equals> (&op)) {
239 if (not whereClause.
empty ()) {
240 whereClause <<
" AND "sv;
243 if (eqOp->fLHS == Database::Document::kID) {
244 whereClause <<
"{} = '{}'"_f(Database::Document::kID, rhsValue->As<
String> ());
249 if (rhsValue->GetType () == VariantValue::eString or rhsValue->GetType () == VariantValue::eDate or
250 rhsValue->GetType () == VariantValue::eDateTime) {
251 vSQL =
"'"sv + vSQL +
"'"sv;
253 whereClause <<
"json_extract(json, '$.{}') = {}"_f(
String{eqOp->fLHS}, vSQL);
258 if (not transferred) {
262 if (not whereClause.
empty ()) {
264 return make_tuple (whereClause, clientSideOps.
empty () ? optional<Filter>{} : make_optional (
Filter{clientSideOps}));
267 return make_tuple (nullopt, filter);
269 return make_tuple (nullopt, nullopt);
278 tuple<optional<tuple<String, Sequence<String>>>, optional<Projection>> Partition_ (
const optional<Projection>& p)
284 tuple<Document::Projection::Flag, Set<String>> fields = p->GetFields ();
286 if (get<Document::Projection::Flag> (fields) == Document::Projection::Flag::eInclude) {
289 for (
String f : get<1> (fields)) {
290 String mongoFieldName = f;
291 Require (f != Document::kID);
292 if (fieldNames.
empty ()) {
293 projectionQuery <<
"json_extract(json,'"sv;
296 projectionQuery <<
","sv;
298 projectionQuery <<
"$."sv << mongoFieldName;
301 if (not fieldNames.
empty ()) {
302 projectionQuery <<
"')"sv;
304 return make_tuple (make_tuple (projectionQuery, fieldNames), nullopt);
307 return make_tuple (nullopt, p);
310 return make_tuple (nullopt, nullopt);
314 Document::Document ExtractRowValueAfterStep_ (::sqlite3_stmt* statement,
int dataCol,
const IDType&
id,
316 const optional<Projection>& remainingProjection)
322 static const auto kJSONReader_ = Variant::JSON::Reader{};
324 kJSONReader_.Read (String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, dataCol))));
326 if (sqliteProjection == nullopt) {
330 auto arrayOfFieldNames = get<Sequence<String>> (*sqliteProjection);
331 if (arrayOfFieldNames.size () == 1) {
335 Assert (valueReadBackFromDB.GetType () == VariantValue::eArray);
340 Assert (nameI == arrayOfFieldNames.end ());
343 if ((sqliteProjection and get<
Sequence<String>> (*sqliteProjection).
Contains (Document::kID)) or remainingProjection == nullopt or
344 remainingProjection->Includes (Document::kID)) {
345 dr.
Add (Document::kID,
id);
347 if (remainingProjection) {
348 dr = remainingProjection->
Apply (dr);
355 using Connection::Options;
356 struct ConnectionRep_ final : Database::Document::SQLite::Connection::IRep {
359 const Options fOptions_;
360 const bool fAllowUserDefinedRowID_{
false};
364 shared_ptr<ConnectionRep_> fConnectionRep_;
367 MyPreparedStatement_ fAddStatement_{};
368 MyPreparedStatement_ fGetOneStatement_{};
369 MyPreparedStatement_ fRemoveStatement_{};
370 MyPreparedStatement_ fUpdateStatement_{};
372 CollectionRep_ (
const shared_ptr<ConnectionRep_>& connectionRep,
const String& collectionName)
373 : fConnectionRep_{connectionRep}
374 , fTableName_{collectionName}
376#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
377 fAssertExternallySynchronizedMutex_.SetAssertExternallySynchronizedMutexContext (
378 connectionRep->fAssertExternallySynchronizedMutex_.GetSharedContext ());
381 virtual ~CollectionRep_ () =
default;
384#if USE_NOISY_TRACE_IN_THIS_MODULE_
387 Require (not v.
ContainsKey (Database::Document::kID) or fConnectionRep_->fOptions_.fAddAllowsExternallySpecifiedIDs);
395 if (fConnectionRep_->fAllowUserDefinedRowID_) {
396 if (
auto o = v.
Lookup (Database::Document::kID)) {
398 jsonValue2Write.
RemoveIf (Database::Document::kID);
404 Assert (not jsonValue2Write.
ContainsKey (Database::Document::kID));
414 if (fAddStatement_ ==
nullptr) [[unlikely]] {
415 if (fConnectionRep_->fAllowUserDefinedRowID_) {
416 fAddStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"insert into {} (id, json) values(?,?);"_f(fTableName_)};
419 fAddStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"insert into {} (json) values(?);"_f(fTableName_)};
422 static const auto kJSONWriter_ = Variant::JSON::Writer{};
423 string jsonText = kJSONWriter_.WriteAsString (
VariantValue{jsonValue2Write}).AsUTF8<string> ();
424 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fAddStatement_), fConnectionRep_->fDB_);
425 if (fConnectionRep_->fAllowUserDefinedRowID_) {
426 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fAddStatement_, 1, id->AsUTF8<
string> ().c_str (),
427 static_cast<int> (id->AsUTF8 ().length ()), SQLITE_TRANSIENT),
428 fConnectionRep_->fDB_);
430 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fAddStatement_, fConnectionRep_->fAllowUserDefinedRowID_ ? 2 : 1,
431 jsonText.c_str (), static_cast<int> (jsonText.length ()), SQLITE_TRANSIENT),
432 fConnectionRep_->fDB_);
433 int rc = ::sqlite3_step (fAddStatement_);
434 if (rc != SQLITE_DONE) {
435 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
437 return fConnectionRep_->fAllowUserDefinedRowID_ ? *id :
"{}"_f(::sqlite3_last_insert_rowid (fConnectionRep_->fDB_));
439 virtual optional<Document::Document> Get (
const IDType&
id,
const optional<Projection>& projection)
override
441#if USE_NOISY_TRACE_IN_THIS_MODULE_
442 TraceContextBumper ctx{
"SQLite::CollectionRep_::Get",
"id={}, projection={}"_f, id, projection};
447 optional<Projection> projectionWithoutID = projection;
448 bool includeIDInProjection =
true;
449 if (projectionWithoutID) {
450 auto [flags, fieldNames] = projectionWithoutID->GetFields ();
452 case Document::Projection::Flag::eInclude: {
453 if (fieldNames.
Contains (Document::kID)) {
454 fieldNames.
Remove (Document::kID);
455 projectionWithoutID =
Projection{flags, fieldNames};
458 includeIDInProjection =
false;
461 case Document::Projection::Flag::eOmit: {
462 if (fieldNames.
Contains (Document::kID)) {
463 includeIDInProjection =
false;
464 projectionWithoutID =
Projection{flags, fieldNames};
469 auto [sqliteProjection, remainingAfterProjection] = Partition_ (projectionWithoutID);
470 optional<MyPreparedStatement_> sqliteProjectionStatement;
471 bool ignoreResult =
false;
472 if (sqliteProjection) {
474 if (get<String> (*sqliteProjection).empty ()) {
475 sqliteProjectionStatement.emplace (fConnectionRep_->fDB_,
"select NULL from {} where id=?;"_f(fTableName_));
479 sqliteProjectionStatement.emplace (fConnectionRep_->fDB_,
480 "select {} from {} where id=?;"_f(get<String> (*sqliteProjection), fTableName_));
483 else if (fGetOneStatement_ ==
nullptr) [[unlikely]] {
484 fGetOneStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"select json from {} where id=?;"_f(fTableName_)};
486 ::sqlite3_stmt* useStatment = sqliteProjectionStatement.has_value () ? *sqliteProjectionStatement : fGetOneStatement_;
489 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (useStatment), fConnectionRep_->fDB_);
490 string idAsUTFSTR =
id.AsUTF8<
string> ();
491 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (useStatment, 1, idAsUTFSTR.c_str (),
static_cast<int> (idAsUTFSTR.length ()), SQLITE_TRANSIENT),
492 fConnectionRep_->fDB_);
494 int rc = ::sqlite3_step (useStatment);
495 optional<Document::Document> result;
496 if (rc == SQLITE_ROW) [[likely]] {
501 result = ExtractRowValueAfterStep_ (useStatment, 0,
id, sqliteProjection, remainingAfterProjection);
503 rc = ::sqlite3_step (useStatment);
505 if (rc != SQLITE_DONE) [[unlikely]] {
506 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
508 if (includeIDInProjection) {
509 result->Add (Document::kID,
id);
515#if USE_NOISY_TRACE_IN_THIS_MODULE_
516 TraceContextBumper ctx{
"SQLite::CollectionRep_::GetAll",
"filter={}, projection={}"_f, filter, projection};
522 if (filter == nullopt and projection == nullopt) {
523 MyPreparedStatement_ statement{fConnectionRep_->fDB_,
"select id,json from {};"_f(fTableName_)};
524 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
526 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
527 static const auto kJSONReader_ = Variant::JSON::Reader{};
528 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
530 kJSONReader_.Read (String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 1))));
532 vDoc.
Add (Document::kID,
id);
535 if (rc != SQLITE_DONE) [[unlikely]] {
536 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
539 else if (filter == nullopt and projection == kOnlyIDs) {
540 MyPreparedStatement_ statement{fConnectionRep_->fDB_,
"select id from {};"_f(fTableName_)};
541 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
543 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
544 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
546 vDoc.
Add (Document::kID,
id);
549 if (rc != SQLITE_DONE) [[unlikely]] {
550 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
555 auto [sqliteWhereClause, remainingFilter] = Partition_ (filter);
561 auto [sqliteProjection, remainingAfterProjection] = remainingFilter ? make_tuple (nullopt, projection) : Partition_ (projection);
563 MyPreparedStatement_ statement{
564 fConnectionRep_->fDB_,
565 "select id,{} from {} {};"_f(sqliteProjection == nullopt ?
"json"_k : get<
String> (*sqliteProjection), fTableName_,
566 sqliteWhereClause == nullopt ?
"" : (
"where "_k + *sqliteWhereClause))};
568 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (statement), fConnectionRep_->fDB_);
570 while ((rc = ::sqlite3_step (statement)) == SQLITE_ROW) {
571 String id = String::FromUTF8 (
reinterpret_cast<const char*
> (::sqlite3_column_text (statement, 0)));
572 Document::Document vDoc = ExtractRowValueAfterStep_ (statement, 1,
id, sqliteProjection, remainingAfterProjection);
573 if (remainingFilter == nullopt or remainingFilter->Matches (vDoc)) {
574 if (remainingAfterProjection) {
575 vDoc = remainingAfterProjection->
Apply (vDoc);
580 if (rc != SQLITE_DONE) [[unlikely]] {
581 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
588#if USE_NOISY_TRACE_IN_THIS_MODULE_
593 if (onlyTheseFields) {
598 Document::Document d2Update = onlyTheseFields ? Memory::ValueOfOrThrow (Get (
id, nullopt)) : uploadDoc;
600 if (onlyTheseFields) {
601 d2Update.
AddAll (uploadDoc);
607 if (fUpdateStatement_ ==
nullptr) [[unlikely]] {
608 fUpdateStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"update {} SET json=? where id=?;"_f(fTableName_)};
610 static const auto kJSONWriter_ = Variant::JSON::Writer{};
611 string r = kJSONWriter_.WriteAsString (
VariantValue{d2Update}).AsUTF8<string> ();
612 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fUpdateStatement_), fConnectionRep_->fDB_);
613 string idText =
id.AsUTF8<
string> ();
614 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fUpdateStatement_, 1, r.c_str (),
static_cast<int> (r.length ()), SQLITE_TRANSIENT),
615 fConnectionRep_->fDB_);
616 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fUpdateStatement_, 2, idText.c_str (),
static_cast<int> (idText.length ()), SQLITE_TRANSIENT),
617 fConnectionRep_->fDB_);
618 int rc = ::sqlite3_step (fUpdateStatement_);
619 if (rc != SQLITE_DONE) {
620 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
623 virtual void Remove (
const IDType&
id)
override
625#if USE_NOISY_TRACE_IN_THIS_MODULE_
629 if (fRemoveStatement_ ==
nullptr) [[unlikely]] {
630 fRemoveStatement_ = MyPreparedStatement_{fConnectionRep_->fDB_,
"delete from {} where id=?;"_f(fTableName_)};
632 ThrowSQLiteErrorIfNotOK_ (::sqlite3_reset (fRemoveStatement_), fConnectionRep_->fDB_);
633 string idText =
id.AsUTF8<
string> ();
634 ThrowSQLiteErrorIfNotOK_ (::sqlite3_bind_text (fRemoveStatement_, 1, idText.c_str (),
static_cast<int> (idText.length ()), SQLITE_TRANSIENT),
635 fConnectionRep_->fDB_);
636 int rc = ::sqlite3_step (fRemoveStatement_);
637 if (rc != SQLITE_DONE) {
638 ThrowSQLiteErrorIfNotOK_ (rc, fConnectionRep_->fDB_);
643 ConnectionRep_ (
const Options& options)
645 , fAllowUserDefinedRowID_{options.fAddAllowsExternallySpecifiedIDs}
651 switch (options.fThreadingMode.value_or (Options::kDefault_ThreadingMode)) {
652 case Options::ThreadingMode::eSingleThread:
654 case Options::ThreadingMode::eMultiThread:
655 Require (CompiledOptions::kThe.THREADSAFE);
656 Require (::sqlite3_threadsafe ());
657 flags |= SQLITE_OPEN_NOMUTEX;
659 case Options::ThreadingMode::eSerialized:
660 Require (CompiledOptions::kThe.THREADSAFE);
661 Require (::sqlite3_threadsafe ());
662 flags |= SQLITE_OPEN_FULLMUTEX;
666 if (options.fImmutable) {
669 Require (options.fReadOnly);
671 flags |= options.fReadOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
675 [[maybe_unused]]
int n{};
676 if (options.fDBPath) {
679 if (options.fTemporaryDB) {
682 if (options.fInMemoryDB) {
687 if (options.fDBPath) {
688 uriArg = options.fDBPath->generic_string ();
689 if (uriArg[0] ==
':') {
690 uriArg =
"./" + uriArg;
693 if (options.fTemporaryDB) {
697 Require (not options.fTemporaryDB->empty ());
699 if (options.fInMemoryDB) {
706 flags |= SQLITE_OPEN_MEMORY;
707 flags |= SQLITE_OPEN_URI;
708 flags |= SQLITE_OPEN_SHAREDCACHE;
709 Require (not options.fReadOnly);
710 Require (options.fCreateDBPathIfDoesNotExist);
711 uriArg = options.fInMemoryDB->AsNarrowSDKString ();
712 if (uriArg.empty ()) {
716 u8string safeCharURI = IO::Network::UniformResourceIdentification::PCTEncode (u8string{uriArg.begin (), uriArg.end ()}, {});
717 uriArg =
"file:" +
string{safeCharURI.begin (), safeCharURI.end ()} +
"?mode=memory&cache=shared";
723 if ((e = ::sqlite3_open_v2 (uriArg.c_str (), &fDB_, flags, options.fVFS ? options.fVFS->AsNarrowSDKString ().c_str () : nullptr)) == SQLITE_CANTOPEN) {
724 if (options.fCreateDBPathIfDoesNotExist) {
725 if (fDB_ !=
nullptr) {
726 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
729 if ((e = ::sqlite3_open_v2 (uriArg.c_str (), &fDB_, SQLITE_OPEN_CREATE | flags,
730 options.fVFS ? options.fVFS->AsNarrowSDKString ().c_str () : nullptr)) == SQLITE_OK) {
735 if (e != SQLITE_OK) [[unlikely]] {
736 [[maybe_unused]]
auto&& cleanup =
Finally ([
this] ()
noexcept {
737 if (fDB_ !=
nullptr) {
738 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
741 ThrowSQLiteError_ (e, fDB_);
743 SetBusyTimeout (options.fBusyTimeout.value_or (Options::kBusyTimeout_Default));
744 if (options.fJournalMode) {
745 SetJournalMode (*options.fJournalMode);
752 Verify (::sqlite3_close (fDB_) == SQLITE_OK);
754 virtual shared_ptr<const EngineProperties> GetEngineProperties ()
const override
757 virtual String GetEngineName ()
const override
762 static const shared_ptr<const EngineProperties> kProps_ = Memory::MakeSharedPtr<const MyEngineProperties_> ();
765 virtual Database::Document::Connection::Options GetOptions ()
const override
773 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
775 results.
Add (String::FromUTF8 (argv[0]));
778 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"SELECT name FROM sqlite_master WHERE type='table';",
779 callback.GetStaticFunction (), callback.GetData (),
nullptr),
786 if (fAllowUserDefinedRowID_) {
787 Exec (
"create table if not exists {} (id TEXT PRIMARY KEY, json NOT NULL) WITHOUT ROWID;"_f(name));
790 Exec (
"create table if not exists {} (id INTEGER PRIMARY KEY, json NOT NULL);"_f(name));
793 Memory::MakeSharedPtr<CollectionRep_> (Debug::UncheckedDynamicPointerCast<ConnectionRep_> (shared_from_this ()), name)};
795 virtual void DropCollection (
const String& name)
override
797 Exec (
"drop table {};"_f(name));
802 Require (GetCollections ().Contains (name));
804 Memory::MakeSharedPtr<CollectionRep_> (Debug::UncheckedDynamicPointerCast<ConnectionRep_> (shared_from_this ()), name)};
808 Connection::Ptr conn = Connection::Ptr{Debug::UncheckedDynamicPointerCast<Connection::IRep> (shared_from_this ())};
809 return Database::Document::SQLite::Transaction{conn};
811 virtual void Exec (
const String& sql)
override
814 int e = ::sqlite3_exec (fDB_, sql.
AsUTF8<
string> ().c_str (),
nullptr,
nullptr,
nullptr);
815 if (e != SQLITE_OK) [[unlikely]] {
816 ThrowSQLiteErrorIfNotOK_ (e, fDB_);
819 virtual ::sqlite3* Peek ()
override
824 virtual Duration GetBusyTimeout ()
const override
828 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
830 Assert (::strcmp (azColName[0],
"timeout") == 0);
831 int val = ::atoi (argv[0]);
836 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma busy_timeout;", callback.GetStaticFunction (), callback.GetData (),
nullptr), fDB_);
838 return Duration{double (*d) / 1000.0};
840 virtual void SetBusyTimeout (
const Duration& timeout)
override
843 ThrowSQLiteErrorIfNotOK_ (::sqlite3_busy_timeout (fDB_, (
int)(timeout.
As<
float> () * 1000)), fDB_);
845 virtual JournalModeType GetJournalMode ()
const override
848 auto callback = SQLiteCallback_{[&] ([[maybe_unused]]
int argc,
char** argv, [[maybe_unused]]
char** azColName) {
850 Assert (::strcmp (azColName[0],
"journal_mode") == 0);
854 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode;", callback.GetStaticFunction (), callback.GetData (),
nullptr), fDB_);
856 if (d ==
"delete"sv) {
857 return JournalModeType::eDelete;
859 if (d ==
"truncate"sv) {
860 return JournalModeType::eTruncate;
862 if (d ==
"persist"sv) {
863 return JournalModeType::ePersist;
865 if (d ==
"memory"sv) {
866 return JournalModeType::eMemory;
869 return JournalModeType::eWAL;
872 return JournalModeType::eWAL2;
875 return JournalModeType::eOff;
878 return JournalModeType::eDelete;
880 virtual void SetJournalMode (JournalModeType journalMode)
override
883 switch (journalMode) {
884 case JournalModeType::eDelete:
885 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'delete';",
nullptr, 0,
nullptr), fDB_);
887 case JournalModeType::eTruncate:
888 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'truncate';",
nullptr, 0,
nullptr), fDB_);
890 case JournalModeType::ePersist:
891 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'persist';",
nullptr, 0,
nullptr), fDB_);
893 case JournalModeType::eMemory:
894 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'memory';",
nullptr, 0,
nullptr), fDB_);
896 case JournalModeType::eWAL:
897 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'wal';",
nullptr, 0,
nullptr), fDB_);
899 case JournalModeType::eWAL2:
900 if (GetJournalMode () == JournalModeType::eWAL) {
901 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'delete';",
nullptr, 0,
nullptr), fDB_);
903 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'wal2';",
nullptr, 0,
nullptr), fDB_);
905 case JournalModeType::eOff:
906 ThrowSQLiteErrorIfNotOK_ (::sqlite3_exec (fDB_,
"pragma journal_mode = 'off';",
nullptr, 0,
nullptr), fDB_);
920Document::SQLite::Connection::Ptr::Ptr (
const shared_ptr<IRep>& src)
922 , busyTimeout{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) {
923 const Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::busyTimeout);
925 return thisObj->operator->()->GetBusyTimeout ();
927 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
auto* property,
auto timeout) {
928 Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::busyTimeout);
930 thisObj->operator->()->SetBusyTimeout (timeout);
932 , journalMode{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
const auto* property) {
933 const Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::journalMode);
935 return thisObj->operator->()->GetJournalMode ();
937 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]
auto* property,
auto journalMode) {
938 Ptr* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Ptr::journalMode);
940 thisObj->operator->()->SetJournalMode (journalMode);
943#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
944 if (src !=
nullptr) {
945 _fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (src->fAssertExternallySynchronizedMutex.GetSharedContext ());
955auto Document::SQLite::Connection::New (
const Options& options) -> Ptr
957 return Ptr{Memory::MakeSharedPtr<ConnectionRep_> (options)};
966 MyRep_ (
const Connection::Ptr& db, Flag f)
967 : fConnectionPtr_{db}
970 case Flag::eDeferred:
971 db->Exec (
"BEGIN DEFERRED TRANSACTION;"sv);
973 case Flag::eExclusive:
974 db->Exec (
"BEGIN EXCLUSIVE TRANSACTION;"sv);
976 case Flag::eImmediate:
977 db->Exec (
"BEGIN IMMEDIATE TRANSACTION;"sv);
983 virtual void Commit ()
override
985 Require (not fCompleted_);
987 fConnectionPtr_->Exec (
"COMMIT TRANSACTION;"sv);
991 Require (not fCompleted_);
993 fConnectionPtr_->Exec (
"ROLLBACK TRANSACTION;"sv);
998 return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
1000 Connection::Ptr fConnectionPtr_;
1001 bool fCompleted_{
false};
1003Transaction::Transaction (
const Connection::Ptr& db, Flag f)
1004 : 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 CONTAINER_OF_Key_T As() const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual bool ContainsKey(ArgByValueType< key_type > key) const
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
nonvirtual unsigned int AddAll(ITERABLE_OF_ADDABLE &&items, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual bool RemoveIf(ArgByValueType< key_type > key)
Remove the given item, if it exists. Return true if found and removed.
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 Remove(size_t i)
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 >
static GUID GenerateNew() noexcept