include/simdb/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
62 / 63
98.4%
Branches:
18 / 38
47.4%
Functions:
28 / 28
100.0%

sqlite/ValueContainer.hpp
Line Branch Exec Source
1 // <ValueContainer.hpp> -*- C++ -*-
2
3 #pragma once
4
5 #include "simdb/schema/Blob.hpp"
6 #include "simdb/utils/utf16.hpp"
7
8 #include <functional>
9 #include <memory>
10 #include <sqlite3.h>
11 #include <type_traits>
12 #include <vector>
13
14 namespace simdb {
15
16 /*!
17 * \class ValueContainerBase
18 *
19 * \brief This class is used for flexible varargs to SQL_VALUES(v1,v2,v3)
20 * where the types of v1/v2/v3 can all be different (int/double/blob...)
21 */
22 class ValueContainerBase
23 {
24 public:
25 617735 virtual ~ValueContainerBase() = default;
26 virtual int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const = 0;
27 };
28
29 /*!
30 * \class Integral32ValueContainer
31 *
32 * \brief Binds an int32_t value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
33 */
34 class Integral32ValueContainer : public ValueContainerBase
35 {
36 public:
37 200052 Integral32ValueContainer(int32_t val) :
38 200052 val_(val)
39 {
40 200052 }
41
42 200051 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override { return sqlite3_bind_int(stmt, col_idx, val_); }
43
44 private:
45 int32_t val_;
46 };
47
48 /*!
49 * \class IntegralU32ValueContainer
50 *
51 * \brief Binds a uint32_t value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
52 */
53 class IntegralU32ValueContainer : public ValueContainerBase
54 {
55 public:
56 9 IntegralU32ValueContainer(uint32_t val) :
57 9 val_(val)
58 {
59 9 }
60
61 9 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
62 {
63 9 return sqlite3_bind_int64(stmt, col_idx, static_cast<sqlite3_int64>(val_));
64 }
65
66 private:
67 uint32_t val_;
68 };
69
70 /*!
71 * \class Integral64ValueContainer
72 *
73 * \brief Binds an int64_t value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
74 */
75 class Integral64ValueContainer : public ValueContainerBase
76 {
77 public:
78 10 Integral64ValueContainer(int64_t val) :
79 10 val_(val)
80 {
81 10 }
82
83 10 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override { return sqlite3_bind_int64(stmt, col_idx, val_); }
84
85 private:
86 int64_t val_;
87 };
88
89 /*!
90 * \class IntegralU64ValueContainer
91 *
92 * \brief Binds a uint64_t value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
93 */
94 class IntegralU64ValueContainer : public ValueContainerBase
95 {
96 public:
97 3042 IntegralU64ValueContainer(uint64_t val) :
98
1/1
✓ Branch 3 → 4 taken 3042 times.
3042 u16_(utils::uint64_to_utf16(val))
99 {
100 3042 }
101
102 3042 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
103 {
104 3042 return sqlite3_bind_text16(stmt, col_idx, u16_.data(), 40, 0);
105 }
106
107 private:
108 std::u16string u16_;
109 };
110
111 /*!
112 * \class FloatingPointValueContainer
113 *
114 * \brief Binds a double value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
115 */
116 class FloatingPointValueContainer : public ValueContainerBase
117 {
118 public:
119 200027 FloatingPointValueContainer(double val) :
120 200027 val_(val)
121 {
122 200027 }
123
124 200027 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
125 {
126 200027 return sqlite3_bind_double(stmt, col_idx, val_);
127 }
128
129 private:
130 double val_;
131 };
132
133 /*!
134 * \class StringValueContainer
135 *
136 * \brief Binds a std::string value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
137 */
138 class StringValueContainer : public ValueContainerBase
139 {
140 public:
141 207321 StringValueContainer(const std::string& val) :
142
1/1
✓ Branch 3 → 4 taken 207321 times.
207321 val_(val)
143 {
144 207321 }
145
146 207313 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
147 {
148 207313 return sqlite3_bind_text(stmt, col_idx, val_.c_str(), -1, 0);
149 }
150
151 private:
152 std::string val_;
153 };
154
155 /*!
156 * \class BlobValueContainer
157 *
158 * \brief Binds a SqlBlob value to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
159 */
160 class BlobValueContainer : public ValueContainerBase
161 {
162 public:
163 7261 BlobValueContainer(const SqlBlob& val) :
164 7261 val_(val)
165 {
166 7261 }
167
168 7261 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
169 {
170 7261 return sqlite3_bind_blob(stmt, col_idx, val_.data_ptr, (int)val_.num_bytes, 0);
171 }
172
173 private:
174 SqlBlob val_;
175 };
176
177 /*!
178 * \class VectorValueContainer
179 *
180 * \brief Binds a std::vector<T> as a blob to an INSERT prepared statement (SQL_VALUES / PreparedINSERT).
181 * \tparam T Element type of the vector; stored as raw bytes in the BLOB column.
182 */
183 template <typename T> class VectorValueContainer : public ValueContainerBase
184 {
185 public:
186 2 VectorValueContainer(const std::vector<T>& val) :
187
1/1
✓ Branch 3 → 4 taken 2 times.
2 val_(val)
188 {
189 2 }
190
191 11 VectorValueContainer(std::vector<T>&& val) :
192 11 val_(std::move(val))
193 {
194 11 }
195
196 13 int32_t bind(sqlite3_stmt* stmt, int32_t col_idx) const override
197 {
198 13 return sqlite3_bind_blob(stmt, col_idx, val_.data(), (int)val_.size() * sizeof(T), 0);
199 }
200
201 private:
202 std::vector<T> val_;
203 };
204
205 using ValueContainerBasePtr = std::shared_ptr<ValueContainerBase>;
206
207 // Handles any integral type (int/unsigned, char/short/long/long long, size_t, enums'
208 // underlying types, etc.) by dispatching on signedness and size rather than requiring
209 // an exact fixed-width type match. This is important for portability: e.g. on macOS
210 // size_t is 'unsigned long' which is a distinct type from uint64_t ('unsigned long long'),
211 // so an exact std::is_same match against uint64_t would fail there even though both are
212 // 64-bit. Note bool is excluded and handled by its own overload below.
213 template <typename T>
214 inline typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, ValueContainerBasePtr>::type
215 203113 createValueContainer(T val)
216 {
217 if constexpr (std::is_signed<T>::value)
218 {
219 if constexpr (sizeof(T) <= sizeof(int32_t))
220 {
221
1/3
✓ Branch 4 → 5 taken 200052 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
200052 return ValueContainerBasePtr(new Integral32ValueContainer(static_cast<int32_t>(val)));
222 } else
223 {
224
1/3
✓ Branch 4 → 5 taken 10 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
10 return ValueContainerBasePtr(new Integral64ValueContainer(static_cast<int64_t>(val)));
225 }
226 } else
227 {
228 if constexpr (sizeof(T) <= sizeof(uint32_t))
229 {
230
1/3
✓ Branch 4 → 5 taken 9 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
9 return ValueContainerBasePtr(new IntegralU32ValueContainer(static_cast<uint32_t>(val)));
231 } else
232 {
233
2/4
✓ Branch 3 → 4 taken 3042 times.
✓ Branch 4 → 5 taken 3042 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
3042 return ValueContainerBasePtr(new IntegralU64ValueContainer(static_cast<uint64_t>(val)));
234 }
235 }
236 }
237
238 template <typename T>
239 inline typename std::enable_if<std::is_same<T, bool>::value, ValueContainerBasePtr>::type createValueContainer(T val)
240 {
241 return ValueContainerBasePtr(new Integral32ValueContainer(val ? 1 : 0));
242 }
243
244 template <typename T>
245 inline typename std::enable_if<std::is_floating_point<T>::value, ValueContainerBasePtr>::type
246 200027 createValueContainer(T val)
247 {
248
1/3
✓ Branch 4 → 5 taken 200027 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
200027 return ValueContainerBasePtr(new FloatingPointValueContainer(val));
249 }
250
251 template <typename T>
252 inline
253 typename std::enable_if<std::is_same<typename std::decay<T>::type, const char*>::value, ValueContainerBasePtr>::type
254 26 createValueContainer(T val)
255 {
256
3/5
✓ Branch 5 → 6 taken 26 times.
✓ Branch 6 → 7 taken 26 times.
✓ Branch 7 → 8 taken 26 times.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
78 return ValueContainerBasePtr(new StringValueContainer(val));
257 }
258
259 template <typename T>
260 inline typename std::enable_if<std::is_same<T, std::string>::value, ValueContainerBasePtr>::type
261 207295 createValueContainer(const T& val)
262 {
263
2/4
✓ Branch 3 → 4 taken 207295 times.
✓ Branch 4 → 5 taken 207295 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
207295 return ValueContainerBasePtr(new StringValueContainer(val));
264 }
265
266 template <typename T>
267 inline typename std::enable_if<std::is_same<T, SqlBlob>::value, ValueContainerBasePtr>::type
268 7261 createValueContainer(const T& val)
269 {
270
1/3
✓ Branch 4 → 5 taken 7261 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
7261 return ValueContainerBasePtr(new BlobValueContainer(val));
271 }
272
273 2 template <typename T> inline ValueContainerBasePtr createValueContainer(const std::vector<T>& val)
274 {
275
2/4
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
2 return ValueContainerBasePtr(new VectorValueContainer<T>(val));
276 }
277
278 11 template <typename T> inline ValueContainerBasePtr createValueContainer(std::vector<T>&& val)
279 {
280
1/3
✓ Branch 5 → 6 taken 11 times.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
11 return ValueContainerBasePtr(new VectorValueContainer<T>(std::move(val)));
281 }
282
283 template <typename T>
284 inline typename std::enable_if<std::is_same<T, ValueContainerBasePtr>::value, ValueContainerBasePtr>::type
285 createValueContainer(T val)
286 {
287 return val;
288 }
289
290 enum class ValueReaderTypes { BACKPOINTER, FUNCPOINTER };
291
292 /*!
293 * \class ScalarValueReader
294 *
295 * \brief Helper class to store either backpointers or function pointers
296 * in the same vector / data structure. Used for reading values
297 * from objects' member variables or getter functions.
298 */
299 template <typename T> class ScalarValueReader
300 {
301 public:
302 typedef struct
303 {
304 ValueReaderTypes getter_type;
305 const T* backpointer;
306 std::function<T()> funcpointer;
307 } ValueReader;
308
309 /// Construct with a backpointer to the data value.
310 ScalarValueReader(const T* data_ptr)
311 {
312 reader_.backpointer = data_ptr;
313 reader_.getter_type = ValueReaderTypes::BACKPOINTER;
314
315 static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value,
316 "ScalarValueReader only supports integral and "
317 "floating-point types!");
318 }
319
320 /// Construct with a function pointer to get the data.
321 ScalarValueReader(std::function<T()> func_ptr)
322 {
323 reader_.funcpointer = func_ptr;
324 reader_.getter_type = ValueReaderTypes::FUNCPOINTER;
325
326 static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value,
327 "ScalarValueReader only supports integral and "
328 "floating-point types!");
329 }
330
331 /// Read the data value.
332 T getValue() const
333 {
334 if (reader_.getter_type == ValueReaderTypes::BACKPOINTER)
335 {
336 return *reader_.backpointer;
337 } else
338 {
339 return reader_.funcpointer();
340 }
341 }
342
343 private:
344 ValueReader reader_;
345 };
346
347 } // namespace simdb
348