Skip to content

Commit 14a2aa0

Browse files
committed
Ability to specify connection control options when making 'storage'
A user-provided 'on open' handler and connection control options can now be specified in a declarative way when making the 'storage' object.
1 parent 23f66e0 commit 14a2aa0

File tree

9 files changed

+343
-55
lines changed

9 files changed

+343
-55
lines changed

dev/connection_holder.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ namespace sqlite_orm {
6666

6767
// first one opens and sets up the connection.
6868

69-
if (int rc = sqlite3_open(this->filename.c_str(), &this->db); rc != SQLITE_OK)
70-
[[unlikely]] /*possible, but unexpected*/ {
69+
if (int rc = sqlite3_open_v2(this->filename.c_str(),
70+
&this->db,
71+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
72+
nullptr);
73+
rc != SQLITE_OK) [[unlikely]] /*possible, but unexpected*/ {
7174
throw_translated_sqlite_error(this->db);
7275
}
7376

@@ -143,7 +146,10 @@ namespace sqlite_orm {
143146
// we presume that the connection is opened once in a single-threaded context [also open forever].
144147
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
145148
if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) {
146-
int rc = sqlite3_open(this->filename.c_str(), &this->db);
149+
int rc = sqlite3_open_v2(this->filename.c_str(),
150+
&this->db,
151+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
152+
nullptr);
147153
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ {
148154
throw_translated_sqlite_error(this->db);
149155
}

dev/functional/cxx_core_features.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
#define SQLITE_ORM_ALIGNED_NEW_SUPPORTED
5454
#endif
5555

56+
#if __cpp_deduction_guides >= 201703L
57+
#define SQLITE_ORM_CTAD_SUPPORTED
58+
#endif
59+
5660
#if __cpp_generic_lambdas >= 201707L
5761
#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED
5862
#endif

dev/functional/mpl.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ namespace sqlite_orm {
453453
* Commonly used named abbreviation for `check_if<std::is_same, Type>`.
454454
*/
455455
template<class Type>
456-
using check_if_is_type = mpl::bind_front_fn<std::is_same, Type>;
456+
using check_if_is_type = check_if<std::is_same, Type>;
457457

458458
/*
459459
* Quoted trait metafunction that checks if a type's template matches the specified template
@@ -463,6 +463,18 @@ namespace sqlite_orm {
463463
using check_if_is_template =
464464
mpl::pass_extracted_fn_to<mpl::bind_front_fn<std::is_same, mpl::quote_fn<Template>>>;
465465

466+
/*
467+
* Quoted trait metafunction that checks if a type names a nested type determined by `Op`.
468+
*/
469+
template<template<typename...> class Op>
470+
using check_if_names = mpl::bind_front_higherorder_fn<polyfill::is_detected, Op>;
471+
472+
/*
473+
* Quoted trait metafunction that checks if a type does not name a nested type determined by `Op`.
474+
*/
475+
template<template<typename...> class Op>
476+
using check_if_lacks = mpl::not_<check_if_names<Op>>;
477+
466478
/*
467479
* Quoted metafunction that finds the index of the given type in a tuple.
468480
*/

dev/storage.h

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,56 @@ namespace sqlite_orm {
8181
polyfill::void_t<indirectly_test_preparable<decltype(std::declval<S>().prepare(std::declval<E>()))>>> =
8282
true;
8383

84+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
85+
template<class PropsTpl>
86+
on_open on_open_spec_or_default(PropsTpl& properties) {
87+
if constexpr (tuple_has_type<PropsTpl, on_open>::value) {
88+
return std::move(std::get<on_open>(properties));
89+
} else {
90+
return {};
91+
}
92+
}
93+
94+
template<class PropsTpl>
95+
connection_control connection_control_or_default(PropsTpl& properties) {
96+
if constexpr (tuple_has_type<PropsTpl, connection_control>::value) {
97+
return std::move(std::get<connection_control>(properties));
98+
} else {
99+
return {};
100+
}
101+
}
102+
#else
103+
template<class PropsTpl>
104+
on_open on_open_spec_or_default(PropsTpl&) {
105+
return {};
106+
}
107+
108+
template<class PropsTpl>
109+
connection_control connection_control_or_default(PropsTpl&) {
110+
return {};
111+
}
112+
#endif
113+
84114
/**
85115
* Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage`
86116
* function.
87117
*/
88118
template<class... DBO>
89119
struct storage_t : storage_base {
90-
using self = storage_t<DBO...>;
120+
using self = storage_t;
91121
using db_objects_type = db_objects_tuple<DBO...>;
92122

93123
/**
94124
* @param filename database filename.
95125
* @param dbObjects db_objects_tuple
96126
*/
97-
storage_t(std::string filename, db_objects_type dbObjects) :
98-
storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {}
127+
template<class PropsTpl>
128+
storage_t(std::string filename, db_objects_type dbObjects, PropsTpl properties) :
129+
storage_base{std::move(filename),
130+
on_open_spec_or_default(properties),
131+
connection_control_or_default(properties),
132+
foreign_keys_count(dbObjects)},
133+
db_objects{std::move(dbObjects)} {}
99134

100135
storage_t(const storage_t&) = default;
101136

@@ -1702,17 +1737,45 @@ namespace sqlite_orm {
17021737
}
17031738
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED
17041739
}; // struct storage_t
1740+
1741+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
1742+
template<typename T>
1743+
using storage_prop_tag_t = typename T::storage_prop_tag;
1744+
1745+
template<class Elements>
1746+
using prop_index_sequence = filter_tuple_sequence_t<Elements, check_if_names<storage_prop_tag_t>::template fn>;
1747+
1748+
template<class Elements>
1749+
using dbo_index_sequence = filter_tuple_sequence_t<Elements, check_if_lacks<storage_prop_tag_t>::template fn>;
1750+
1751+
template<class... DBO, class PropsTpl>
1752+
storage_t<DBO...> make_storage(std::string filename, std::tuple<DBO...> dbObjects, PropsTpl properties) {
1753+
return {std::move(filename), std::move(dbObjects), std::move(properties)};
1754+
}
1755+
#endif
17051756
}
17061757
}
17071758

17081759
SQLITE_ORM_EXPORT namespace sqlite_orm {
1760+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
17091761
/*
17101762
* Factory function for a storage, from a database file and a bunch of database object definitions.
17111763
*/
1764+
template<class... Spec>
1765+
auto make_storage(std::string filename, Spec... arguments) {
1766+
using namespace ::sqlite_orm::internal;
1767+
1768+
std::tuple args{std::forward<Spec>(arguments)...};
1769+
return make_storage(std::move(filename),
1770+
create_from_tuple<std::tuple>(std::move(args), dbo_index_sequence<decltype(args)>{}),
1771+
create_from_tuple<std::tuple>(std::move(args), prop_index_sequence<decltype(args)>{}));
1772+
}
1773+
#else
17121774
template<class... DBO>
17131775
internal::storage_t<DBO...> make_storage(std::string filename, DBO... dbObjects) {
1714-
return {std::move(filename), internal::db_objects_tuple<DBO...>{std::forward<DBO>(dbObjects)...}};
1776+
return {std::move(filename), {std::forward<DBO>(dbObjects)...}, std::tuple<>{}};
17151777
}
1778+
#endif
17161779

17171780
/**
17181781
* sqlite3_threadsafe() interface.

dev/storage_base.h

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,26 @@
3535
#include "serializing_util.h"
3636
#include "table_info.h"
3737

38-
namespace sqlite_orm {
38+
SQLITE_ORM_EXPORT namespace sqlite_orm {
39+
struct on_open {
40+
using storage_prop_tag = int;
41+
42+
#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED
43+
on_open() = default;
44+
on_open(std::function<void(sqlite3*)> onOpen) : onOpen{std::move(onOpen)} {}
45+
#endif
3946

47+
std::function<void(sqlite3*)> onOpen;
48+
};
49+
50+
struct connection_control {
51+
using storage_prop_tag = int;
52+
53+
bool openForever = false;
54+
};
55+
}
56+
57+
namespace sqlite_orm {
4058
namespace internal {
4159

4260
struct storage_base {
@@ -289,17 +307,19 @@ namespace sqlite_orm {
289307
* needed and closes when it is not needed. This function breaks this rule. In memory storage always
290308
* keeps connection opened so calling this for in-memory storage changes nothing.
291309
* Note about multithreading: in multithreading context avoiding using this function for not in-memory
292-
* storage may lead to data races. If you have data races in such a configuration try to call `open_forever`
310+
* storage may lead to data races. If you have data races in such a configuration try to call `open_forever()`
293311
* before accessing your storage - it may fix data races.
294312
*/
295313
void open_forever() {
296-
this->isOpenedForever = true;
297-
this->connection->retain();
314+
if (!this->isOpenedForever) {
315+
this->isOpenedForever = true;
316+
this->connection->retain();
317+
}
298318
}
299319

300320
/**
301321
* Create an application-defined scalar SQL function.
302-
* Can be called at any time no matter whether the database connection is opened or not.
322+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not.
303323
*
304324
* Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object,
305325
* together with a copy of the passed initialization arguments.
@@ -342,7 +362,7 @@ namespace sqlite_orm {
342362
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
343363
/**
344364
* Create an application-defined scalar function.
345-
* Can be called at any time no matter whether the database connection is opened or not.
365+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not.
346366
*
347367
* Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object,
348368
* together with a copy of the passed initialization arguments.
@@ -357,7 +377,7 @@ namespace sqlite_orm {
357377

358378
/**
359379
* Create an application-defined scalar function.
360-
* Can be called at any time no matter whether the database connection is opened or not.
380+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not.
361381
*
362382
* If `quotedF` contains a freestanding function, stateless lambda or stateless function object,
363383
* `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects;
@@ -398,7 +418,7 @@ namespace sqlite_orm {
398418

399419
/**
400420
* Create an application-defined aggregate SQL function.
401-
* Can be called at any time no matter whether the database connection is opened or not.
421+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not.
402422
*
403423
* Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object,
404424
* together with a copy of the passed initialization arguments.
@@ -447,7 +467,7 @@ namespace sqlite_orm {
447467
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
448468
/**
449469
* Create an application-defined aggregate function.
450-
* Can be called at any time no matter whether the database connection is opened or not.
470+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not.
451471
*
452472
* Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object,
453473
* together with a copy of the passed initialization arguments.
@@ -462,7 +482,7 @@ namespace sqlite_orm {
462482

463483
/**
464484
* Delete a scalar function you created before.
465-
* Can be called at any time no matter whether the database connection is open or not.
485+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not.
466486
*/
467487
template<class F>
468488
void delete_scalar_function() {
@@ -474,7 +494,7 @@ namespace sqlite_orm {
474494
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
475495
/**
476496
* Delete a scalar function you created before.
477-
* Can be called at any time no matter whether the database connection is open or not.
497+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not.
478498
*/
479499
template<orm_scalar_function auto f>
480500
void delete_scalar_function() {
@@ -483,7 +503,7 @@ namespace sqlite_orm {
483503

484504
/**
485505
* Delete a quoted scalar function you created before.
486-
* Can be called at any time no matter whether the database connection is open or not.
506+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not.
487507
*/
488508
template<orm_quoted_scalar_function auto quotedF>
489509
void delete_scalar_function() {
@@ -493,7 +513,7 @@ namespace sqlite_orm {
493513

494514
/**
495515
* Delete aggregate function you created before.
496-
* Can be called at any time no matter whether the database connection is open or not.
516+
* Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not.
497517
*/
498518
template<class F>
499519
void delete_aggregate_function() {
@@ -661,30 +681,41 @@ namespace sqlite_orm {
661681
}
662682

663683
protected:
664-
storage_base(std::string filename, int foreignKeysCount) :
684+
storage_base(std::string filename,
685+
sqlite_orm::on_open onOpenSpec,
686+
connection_control connectionCtrl,
687+
int foreignKeysCount) :
688+
on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.openForever},
665689
pragma(std::bind(&storage_base::get_connection, this)),
666690
limit(std::bind(&storage_base::get_connection, this)),
667691
inMemory(filename.empty() || filename == ":memory:"),
668692
connection(std::make_unique<connection_holder>(
669693
std::move(filename),
670-
inMemory,
694+
inMemory || isOpenedForever,
671695
std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))),
672696
cachedForeignKeysCount(foreignKeysCount) {
673697
if (this->inMemory) {
674698
this->connection->retain();
675699
}
700+
if (this->isOpenedForever) {
701+
this->connection->retain();
702+
}
676703
}
677704

678705
storage_base(const storage_base& other) :
679706
on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)),
680707
limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory),
708+
isOpenedForever{other.isOpenedForever},
681709
connection(std::make_unique<connection_holder>(
682710
*other.connection,
683711
std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))),
684712
cachedForeignKeysCount(other.cachedForeignKeysCount) {
685713
if (this->inMemory) {
686714
this->connection->retain();
687715
}
716+
if (this->isOpenedForever) {
717+
this->connection->retain();
718+
}
688719
}
689720

690721
~storage_base() {
@@ -719,15 +750,15 @@ namespace sqlite_orm {
719750
perform_exec(db, "PRAGMA foreign_keys", extract_single_value<bool>, &result);
720751
return result;
721752
}
722-
723753
#endif
724-
void on_open_internal(sqlite3* db) {
725754

755+
void on_open_internal(sqlite3* db) {
726756
#if SQLITE_VERSION_NUMBER >= 3006019
727757
if (this->cachedForeignKeysCount) {
728758
this->foreign_keys(db, true);
729759
}
730760
#endif
761+
731762
if (this->pragma.synchronous_ != -1) {
732763
this->pragma.set_pragma("synchronous", this->pragma.synchronous_, db);
733764
}

dev/tuple_helper/tuple_transformer.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ namespace sqlite_orm {
103103
}
104104

105105
/*
106-
* Like `std::make_from_tuple`, but using a projection on the tuple elements.
106+
* Like `std::make_from_tuple()`, but using a projection on the tuple elements.
107107
*/
108108
template<class R, class Tpl, class Projection = polyfill::identity>
109109
constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) {
@@ -112,5 +112,23 @@ namespace sqlite_orm {
112112
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tpl>>::value>{},
113113
std::forward<Projection>(project));
114114
}
115+
116+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
117+
template<template<typename...> class R, class Tpl, size_t... Idx, class Projection = polyfill::identity>
118+
constexpr auto create_from_tuple(Tpl&& tpl, std::index_sequence<Idx...>, Projection project = {}) {
119+
return R{polyfill::invoke(project, std::get<Idx>(std::forward<Tpl>(tpl)))...};
120+
}
121+
122+
/*
123+
* Similar to `create_from_tuple()`, but the result type is specified as a template class.
124+
*/
125+
template<template<typename...> class R, class Tpl, class Projection = polyfill::identity>
126+
constexpr auto create_from_tuple(Tpl&& tpl, Projection project = {}) {
127+
return create_from_tuple<R>(
128+
std::forward<Tpl>(tpl),
129+
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tpl>>::value>{},
130+
std::forward<Projection>(project));
131+
}
132+
#endif
115133
}
116134
}

0 commit comments

Comments
 (0)