Skip to content

Commit b5c7b27

Browse files
committed
Variant read support
1 parent f9693ac commit b5c7b27

File tree

3 files changed

+305
-17
lines changed

3 files changed

+305
-17
lines changed

hdr/sqlite_modern_cpp.h

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ namespace sqlite {
4242
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
4343
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
4444
int get_code() {return code;}
45-
std::string get_sql() {return sql;}
45+
std::string get_sql() {return sql;}
4646
private:
4747
int code;
48-
std::string sql;
48+
std::string sql;
4949
};
5050

5151
namespace exceptions {
@@ -115,7 +115,13 @@ namespace sqlite {
115115
else throw sqlite_exception(error_code, sql);
116116
}
117117
}
118+
}
119+
120+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
121+
#include "sqlite_modern_cpp/utility/variant.h"
122+
#endif
118123

124+
namespace sqlite {
119125
class database;
120126
class database_binder;
121127

@@ -165,19 +171,19 @@ namespace sqlite {
165171
used(true); /* prevent from executing again when goes out of scope */
166172
}
167173

168-
std::string sql() {
174+
std::string sql() {
169175
#if SQLITE_VERSION_NUMBER >= 3014000
170-
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
171-
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
172-
return str ? str.get() : original_sql();
176+
auto sqlite_deleter = [](void *ptr) {sqlite3_free(ptr);};
177+
std::unique_ptr<char, decltype(sqlite_deleter)> str(sqlite3_expanded_sql(_stmt.get()), sqlite_deleter);
178+
return str ? str.get() : original_sql();
173179
#else
174-
return original_sql();
180+
return original_sql();
175181
#endif
176-
}
182+
}
177183

178-
std::string original_sql() {
179-
return sqlite3_sql(_stmt.get());
180-
}
184+
std::string original_sql() {
185+
return sqlite3_sql(_stmt.get());
186+
}
181187

182188
void used(bool state) { execution_started = state; }
183189
bool used() const { return execution_started; }
@@ -259,6 +265,13 @@ namespace sqlite {
259265
|| std::is_integral<Type>::value
260266
|| std::is_same<sqlite_int64, Type>::value
261267
> { };
268+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
269+
template <typename ...Args>
270+
struct is_sqlite_value< std::variant<Args...> > : public std::integral_constant<
271+
bool,
272+
true
273+
> { };
274+
#endif
262275

263276

264277
template<typename T> friend database_binder& operator <<(database_binder& db, const T& val);
@@ -270,6 +283,10 @@ namespace sqlite {
270283
friend database_binder& operator <<(database_binder& db, std::nullptr_t);
271284
template<typename T> friend database_binder& operator <<(database_binder& db, const std::unique_ptr<T>& val);
272285
template<typename T> friend void get_col_from_db(database_binder& db, int inx, std::unique_ptr<T>& val);
286+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
287+
template<typename ...Args> friend database_binder& operator <<(database_binder& db, const std::variant<Args...>& val);
288+
template<typename ...Args> friend void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val);
289+
#endif
273290
template<typename T> friend T operator++(database_binder& db, int);
274291
// Overload instead of specializing function templates (http://www.gotw.ca/publications/mill17.htm)
275292
friend database_binder& operator<<(database_binder& db, const int& val);
@@ -902,13 +919,27 @@ namespace sqlite {
902919
}
903920
#endif
904921

922+
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
905923
template <typename ...Args> inline database_binder& operator <<(database_binder& db, const std::variant<Args...>& val) {
906-
std::visit([&](auto &&opt) {db << std::forward<decltype(opt)>(opt);}, val);
924+
std::visit([&](auto &&opt) {db << std::forward<decltype(opt)>(opt);}, val);
907925
return db;
908926
}
909927
template <typename ...Args> inline void store_result_in_db(sqlite3_context* db, const std::variant<Args...>& val) {
910-
std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
928+
std::visit([&](auto &&opt) {store_result_in_db(db, std::forward<decltype(opt)>(opt));}, val);
929+
}
930+
template <typename ...Args> inline void get_col_from_db(database_binder& db, int inx, std::variant<Args...>& val) {
931+
utility::variant_select<Args...>(sqlite3_column_type(db._stmt.get(), inx))([&](auto v) {
932+
get_col_from_db(db, inx, v);
933+
val = std::move(v);
934+
});
911935
}
936+
template <typename ...Args> inline void get_val_from_db(sqlite3_value *value, std::variant<Args...>& val) {
937+
utility::variant_select<Args...>(sqlite3_value_type(value))([&](auto v) {
938+
get_val_from_db(value, v);
939+
val = std::move(v);
940+
});
941+
}
942+
#endif
912943

913944
// Some ppl are lazy so we have a operator for proper prep. statemant handling.
914945
void inline operator++(database_binder& db, int) { db.execute(); db.reset(); }
@@ -937,7 +968,7 @@ namespace sqlite {
937968
if(!ctxt) return;
938969
try {
939970
if(!ctxt->constructed) new(ctxt) AggregateCtxt<ContextType>();
940-
step<Count, Functions>(db, count, vals, ctxt->obj);
971+
step<Count, Functions>(db, count, vals, ctxt->obj);
941972
return;
942973
} catch(sqlite_exception &e) {
943974
sqlite3_result_error_code(db, e.get_code());
@@ -948,7 +979,7 @@ namespace sqlite {
948979
sqlite3_result_error(db, "Unknown error", -1);
949980
}
950981
if(ctxt && ctxt->constructed)
951-
ctxt->~AggregateCtxt();
982+
ctxt->~AggregateCtxt();
952983
}
953984

954985
template<
@@ -1008,7 +1039,7 @@ namespace sqlite {
10081039
sqlite3_result_error(db, "Unknown error", -1);
10091040
}
10101041
if(ctxt && ctxt->constructed)
1011-
ctxt->~AggregateCtxt();
1042+
ctxt->~AggregateCtxt();
10121043
}
10131044

10141045
template<
@@ -1034,7 +1065,7 @@ namespace sqlite {
10341065

10351066
template<
10361067
std::size_t Count,
1037-
typename Function,
1068+
typename Function,
10381069
typename... Values
10391070
>
10401071
inline typename std::enable_if<(sizeof...(Values) == Count), void>::type scalar(
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#pragma once
2+
3+
#include <sqlite3.h>
4+
#include <optional>
5+
#include <variant>
6+
7+
namespace sqlite::utility {
8+
template<typename ...Options>
9+
struct VariantFirstNullable {
10+
using type = void;
11+
};
12+
template<typename T, typename ...Options>
13+
struct VariantFirstNullable<T, Options...> {
14+
using type = typename VariantFirstNullable<Options...>::type;
15+
};
16+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
17+
template<typename T, typename ...Options>
18+
struct VariantFirstNullable<std::optional<T>, Options...> {
19+
using type = std::optional<T>;
20+
};
21+
#endif
22+
template<typename T, typename ...Options>
23+
struct VariantFirstNullable<std::unique_ptr<T>, Options...> {
24+
using type = std::unique_ptr<T>;
25+
};
26+
template<typename ...Options>
27+
struct VariantFirstNullable<std::nullptr_t, Options...> {
28+
using type = std::nullptr_t;
29+
};
30+
template<typename Callback, typename ...Options>
31+
inline void variant_select_null(Callback&&callback) {
32+
if constexpr(std::is_same_v<typename VariantFirstNullable<Options...>::type, void>) {
33+
throw exceptions::mismatch("NULL is unsupported by this variant.", "", SQLITE_MISMATCH);
34+
} else {
35+
std::forward<Callback>(callback)(typename VariantFirstNullable<Options...>::type());
36+
}
37+
}
38+
39+
template<typename ...Options>
40+
struct VariantFirstIntegerable {
41+
using type = void;
42+
};
43+
template<typename T, typename ...Options>
44+
struct VariantFirstIntegerable<T, Options...> {
45+
using type = typename VariantFirstIntegerable<Options...>::type;
46+
};
47+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
48+
template<typename T, typename ...Options>
49+
struct VariantFirstIntegerable<std::optional<T>, Options...> {
50+
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstIntegerable<Options...>::type>;
51+
};
52+
#endif
53+
template<typename T, typename ...Options>
54+
struct VariantFirstIntegerable<std::enable_if_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>>, std::unique_ptr<T>, Options...> {
55+
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstIntegerable<Options...>::type>;
56+
};
57+
template<typename ...Options>
58+
struct VariantFirstIntegerable<int, Options...> {
59+
using type = int;
60+
};
61+
template<typename ...Options>
62+
struct VariantFirstIntegerable<sqlite_int64, Options...> {
63+
using type = sqlite_int64;
64+
};
65+
template<typename Callback, typename ...Options>
66+
inline auto variant_select_integer(Callback&&callback) {
67+
if constexpr(std::is_same_v<typename VariantFirstIntegerable<Options...>::type, void>) {
68+
throw exceptions::mismatch("Integer is unsupported by this variant.", "", SQLITE_MISMATCH);
69+
} else {
70+
std::forward<Callback>(callback)(typename VariantFirstIntegerable<Options...>::type());
71+
}
72+
}
73+
74+
template<typename ...Options>
75+
struct VariantFirstFloatable {
76+
using type = void;
77+
};
78+
template<typename T, typename ...Options>
79+
struct VariantFirstFloatable<T, Options...> {
80+
using type = typename VariantFirstFloatable<Options...>::type;
81+
};
82+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
83+
template<typename T, typename ...Options>
84+
struct VariantFirstFloatable<std::optional<T>, Options...> {
85+
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstFloatable<Options...>::type>;
86+
};
87+
#endif
88+
template<typename T, typename ...Options>
89+
struct VariantFirstFloatable<std::unique_ptr<T>, Options...> {
90+
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstFloatable<Options...>::type>;
91+
};
92+
template<typename ...Options>
93+
struct VariantFirstFloatable<float, Options...> {
94+
using type = float;
95+
};
96+
template<typename ...Options>
97+
struct VariantFirstFloatable<double, Options...> {
98+
using type = double;
99+
};
100+
template<typename Callback, typename ...Options>
101+
inline auto variant_select_float(Callback&&callback) {
102+
if constexpr(std::is_same_v<typename VariantFirstFloatable<Options...>::type, void>) {
103+
throw exceptions::mismatch("Real is unsupported by this variant.", "", SQLITE_MISMATCH);
104+
} else {
105+
std::forward<Callback>(callback)(typename VariantFirstFloatable<Options...>::type());
106+
}
107+
}
108+
109+
template<typename ...Options>
110+
struct VariantFirstTextable {
111+
using type = void;
112+
};
113+
template<typename T, typename ...Options>
114+
struct VariantFirstTextable<T, Options...> {
115+
using type = typename VariantFirstTextable<void, Options...>::type;
116+
};
117+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
118+
template<typename T, typename ...Options>
119+
struct VariantFirstTextable<std::optional<T>, Options...> {
120+
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstTextable<Options...>::type>;
121+
};
122+
#endif
123+
template<typename T, typename ...Options>
124+
struct VariantFirstTextable<std::unique_ptr<T>, Options...> {
125+
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstTextable<Options...>::type>;
126+
};
127+
template<typename ...Options>
128+
struct VariantFirstTextable<std::string, Options...> {
129+
using type = std::string;
130+
};
131+
template<typename ...Options>
132+
struct VariantFirstTextable<std::u16string, Options...> {
133+
using type = std::u16string;
134+
};
135+
template<typename Callback, typename ...Options>
136+
inline void variant_select_text(Callback&&callback) {
137+
if constexpr(std::is_same_v<typename VariantFirstTextable<Options...>::type, void>) {
138+
throw exceptions::mismatch("Text is unsupported by this variant.", "", SQLITE_MISMATCH);
139+
} else {
140+
std::forward<Callback>(callback)(typename VariantFirstTextable<Options...>::type());
141+
}
142+
}
143+
144+
template<typename ...Options>
145+
struct VariantFirstBlobable {
146+
using type = void;
147+
};
148+
template<typename T, typename ...Options>
149+
struct VariantFirstBlobable<T, Options...> {
150+
using type = typename VariantFirstBlobable<Options...>::type;
151+
};
152+
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
153+
template<typename T, typename ...Options>
154+
struct VariantFirstBlobable<std::optional<T>, Options...> {
155+
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstBlobable<Options...>::type>;
156+
};
157+
#endif
158+
template<typename T, typename ...Options>
159+
struct VariantFirstBlobable<std::unique_ptr<T>, Options...> {
160+
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstBlobable<Options...>::type>;
161+
};
162+
template<typename T, typename ...Options>
163+
struct VariantFirstBlobable<std::enable_if_t<std::is_pod_v<T>>, std::vector<T>, Options...> {
164+
using type = std::vector<T>;
165+
};
166+
template<typename Callback, typename ...Options>
167+
inline auto variant_select_blob(Callback&&callback) {
168+
if constexpr(std::is_same_v<typename VariantFirstBlobable<Options...>::type, void>) {
169+
throw exceptions::mismatch("Blob is unsupported by this variant.", "", SQLITE_MISMATCH);
170+
} else {
171+
std::forward<Callback>(callback)(typename VariantFirstBlobable<Options...>::type());
172+
}
173+
}
174+
175+
template<typename ...Options>
176+
inline auto variant_select(int type) {
177+
return [type](auto &&callback) {
178+
using Callback = decltype(callback);
179+
switch(type) {
180+
case SQLITE_NULL:
181+
variant_select_null<Callback, Options...>(std::forward<Callback>(callback));
182+
break;
183+
case SQLITE_INTEGER:
184+
variant_select_integer<Callback, Options...>(std::forward<Callback>(callback));
185+
break;
186+
case SQLITE_FLOAT:
187+
variant_select_float<Callback, Options...>(std::forward<Callback>(callback));
188+
break;
189+
case SQLITE_TEXT:
190+
variant_select_text<Callback, Options...>(std::forward<Callback>(callback));
191+
break;
192+
case SQLITE_BLOB:
193+
variant_select_blob<Callback, Options...>(std::forward<Callback>(callback));
194+
break;
195+
default:;
196+
/* assert(false); */
197+
}
198+
};
199+
}
200+
}

0 commit comments

Comments
 (0)