Skip to content

Commit 6249684

Browse files
authored
Merge pull request #1399 from fnc12/connection_control
Database connection control options
2 parents 92d95b9 + cb39f45 commit 6249684

File tree

12 files changed

+495
-122
lines changed

12 files changed

+495
-122
lines changed

dev/connection_holder.h

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <sqlite3.h>
44
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
55
#include <atomic>
6+
#include <functional> // std::function
67
#include <string> // std::string
78
#endif
89

@@ -12,25 +13,38 @@ namespace sqlite_orm {
1213
namespace internal {
1314

1415
struct connection_holder {
15-
connection_holder(std::string filename) : filename(std::move(filename)) {}
16+
connection_holder(std::string filename, std::function<void(sqlite3*)> didOpenDb) :
17+
_didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)) {}
18+
19+
connection_holder(const connection_holder&) = delete;
20+
21+
connection_holder(const connection_holder& other, std::function<void(sqlite3*)> didOpenDb) :
22+
_didOpenDb{std::move(didOpenDb)}, filename{other.filename} {}
1623

1724
void retain() {
1825
// first one opens the connection.
1926
// we presume that the connection is opened once in a single-threaded context [also open forever].
2027
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
21-
if (this->_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) {
22-
int rc = sqlite3_open(this->filename.c_str(), &this->db);
28+
if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) {
29+
int rc = sqlite3_open_v2(this->filename.c_str(),
30+
&this->db,
31+
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
32+
nullptr);
2333
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ {
2434
throw_translated_sqlite_error(this->db);
2535
}
36+
37+
if (_didOpenDb) {
38+
_didOpenDb(this->db);
39+
}
2640
}
2741
}
2842

2943
void release() {
3044
// last one closes the connection.
3145
// we assume that this might happen by any thread, therefore the counter must serve as a synchronization point.
32-
if (this->_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
33-
int rc = sqlite3_close(this->db);
46+
if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
47+
int rc = sqlite3_close_v2(this->db);
3448
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY {
3549
throw_translated_sqlite_error(this->db);
3650
} else {
@@ -48,16 +62,20 @@ namespace sqlite_orm {
4862
* @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code.
4963
*/
5064
int retain_count() const {
51-
return this->_retain_count.load(std::memory_order_relaxed);
65+
return _retainCount.load(std::memory_order_relaxed);
5266
}
5367

54-
const std::string filename;
55-
5668
protected:
5769
sqlite3* db = nullptr;
5870

5971
private:
60-
std::atomic_int _retain_count{};
72+
std::atomic_int _retainCount{};
73+
74+
private:
75+
const std::function<void(sqlite3* db)> _didOpenDb;
76+
77+
public:
78+
const std::string filename;
6179
};
6280

6381
struct connection_ref {

dev/functional/cxx_core_features.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@
4949
#define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED
5050
#endif
5151

52+
#if __cpp_deduction_guides >= 201703L
53+
#define SQLITE_ORM_CTAD_SUPPORTED
54+
#endif
55+
5256
#if __cpp_generic_lambdas >= 201707L
5357
#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED
54-
#else
5558
#endif
5659

5760
#if __cpp_init_captures >= 201803L

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/pragma.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,12 @@ namespace sqlite_orm {
258258
}
259259

260260
void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) {
261-
auto con = this->get_connection();
262-
if (db == nullptr) {
263-
db = con.get();
261+
if (db) {
262+
perform_void_exec(db, query);
263+
} else {
264+
auto con = this->get_connection();
265+
perform_void_exec(con.get(), query);
264266
}
265-
perform_void_exec(db, query);
266267
}
267268
};
268269
}

dev/storage.h

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

84+
template<class Opt, class OptionsTpl>
85+
decltype(auto) storage_opt_or_default(OptionsTpl& options) {
86+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
87+
if constexpr (tuple_has_type<OptionsTpl, Opt>::value) {
88+
return std::move(std::get<Opt>(options));
89+
} else {
90+
return Opt{};
91+
}
92+
#else
93+
return Opt{};
94+
#endif
95+
}
96+
8497
/**
8598
* Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage`
8699
* function.
87100
*/
88101
template<class... DBO>
89102
struct storage_t : storage_base {
90-
using self = storage_t<DBO...>;
103+
using self_type = storage_t;
91104
using db_objects_type = db_objects_tuple<DBO...>;
92105

93106
/**
94107
* @param filename database filename.
95108
* @param dbObjects db_objects_tuple
96109
*/
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)} {}
110+
template<class OptionsTpl>
111+
storage_t(std::string filename, db_objects_type dbObjects, OptionsTpl options) :
112+
storage_base{std::move(filename),
113+
storage_opt_or_default<connection_control>(options),
114+
storage_opt_or_default<on_open_spec>(options),
115+
foreign_keys_count(dbObjects)},
116+
db_objects{std::move(dbObjects)} {}
99117

100118
storage_t(const storage_t&) = default;
101119

@@ -114,7 +132,7 @@ namespace sqlite_orm {
114132
*
115133
* Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`.
116134
*/
117-
friend const db_objects_type& obtain_db_objects(const self& storage) noexcept {
135+
friend const db_objects_type& obtain_db_objects(const self_type& storage) noexcept {
118136
return storage.db_objects;
119137
}
120138

@@ -246,7 +264,7 @@ namespace sqlite_orm {
246264

247265
public:
248266
template<class T, class O = mapped_type_proxy_t<T>, class... Args>
249-
mapped_view<O, self, Args...> iterate(Args&&... args) {
267+
mapped_view<O, self_type, Args...> iterate(Args&&... args) {
250268
this->assert_mapped_type<O>();
251269

252270
auto con = this->get_connection();
@@ -781,7 +799,7 @@ namespace sqlite_orm {
781799
std::enable_if_t<!is_prepared_statement<Ex>::value && !is_mapped<db_objects_type, Ex>::value,
782800
bool> = true>
783801
std::string dump(E&& expression, bool parametrized = false) const {
784-
static_assert(is_preparable_v<self, Ex>, "Expression must be a high-level statement");
802+
static_assert(is_preparable_v<self_type, Ex>, "Expression must be a high-level statement");
785803

786804
decltype(auto) e2 = static_if<is_select<Ex>::value>(
787805
[](auto expression) -> auto {
@@ -1702,17 +1720,50 @@ namespace sqlite_orm {
17021720
}
17031721
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED
17041722
}; // struct storage_t
1723+
1724+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
1725+
template<class Elements>
1726+
using dbo_index_sequence = filter_tuple_sequence_t<Elements, check_if_lacks<storage_opt_tag_t>::template fn>;
1727+
1728+
template<class Elements>
1729+
using opt_index_sequence = filter_tuple_sequence_t<Elements, check_if_names<storage_opt_tag_t>::template fn>;
1730+
1731+
template<class... DBO, class OptionsTpl>
1732+
storage_t<DBO...> make_storage(std::string filename, std::tuple<DBO...> dbObjects, OptionsTpl options) {
1733+
return {std::move(filename), std::move(dbObjects), std::move(options)};
1734+
}
1735+
#endif
17051736
}
17061737
}
17071738

17081739
SQLITE_ORM_EXPORT namespace sqlite_orm {
1740+
#ifdef SQLITE_ORM_CTAD_SUPPORTED
1741+
/*
1742+
* Factory function for a storage instance, from a database file, a set of database object definitions
1743+
* and option storage options like connection control options and an 'on open' callback.
1744+
*
1745+
* E.g.
1746+
* auto storage = make_storage("", connection_control{.open_forever = true}, on_open([](sqlite3* db) {}));
1747+
*/
1748+
template<class... Spec>
1749+
auto make_storage(std::string filename, Spec... specifications) {
1750+
using namespace ::sqlite_orm::internal;
1751+
1752+
std::tuple specTuple{std::forward<Spec>(specifications)...};
1753+
return internal::make_storage(
1754+
std::move(filename),
1755+
create_from_tuple<std::tuple>(std::move(specTuple), dbo_index_sequence<decltype(specTuple)>{}),
1756+
create_from_tuple<std::tuple>(std::move(specTuple), opt_index_sequence<decltype(specTuple)>{}));
1757+
}
1758+
#else
17091759
/*
1710-
* Factory function for a storage, from a database file and a bunch of database object definitions.
1760+
* Factory function for a storage instance, from a database file and a bunch of database object definitions.
17111761
*/
17121762
template<class... DBO>
17131763
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)...}};
1764+
return {std::move(filename), {std::forward<DBO>(dbObjects)...}, std::tuple<>{}};
17151765
}
1766+
#endif
17161767

17171768
/**
17181769
* sqlite3_threadsafe() interface.

0 commit comments

Comments
 (0)