include/simdb/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
264 / 275
96.0%
Branches:
253 / 313
80.8%
Functions:
46 / 46
100.0%

sqlite/Query.hpp
Line Branch Exec Source
1 // <Query.hpp> -*- C++ -*-
2
3 #pragma once
4
5 #include "simdb/Assert.hpp"
6 #include "simdb/sqlite/Constraints.hpp"
7 #include "simdb/sqlite/Iterator.hpp"
8 #include <iomanip>
9 #include <iostream>
10 #include <limits>
11
12 namespace simdb {
13
14 /// Used in query->orderBy("ColA", ASC|DESC)
15 enum class QueryOrder { ASC, DESC };
16
17 /// Stringify QueryOrder enums for SELECT commands
18 6 inline std::ostream& operator<<(std::ostream& os, const QueryOrder order)
19 {
20
2/3
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 5 taken 2 times.
✗ Branch 2 → 7 not taken.
6 switch (order)
21 {
22 4 case QueryOrder::ASC:
23 4 os << "ASC";
24 4 break;
25 2 case QueryOrder::DESC:
26 2 os << "DESC";
27 2 break;
28 }
29
30 6 return os;
31 }
32
33 /*!
34 * \class SqlQuery
35 *
36 * \brief This class issues SELECT statements with Constraints, and is used
37 * in order to iterate over the result set and automatically write
38 * record values into users' local variables.
39 */
40 class SqlQuery
41 {
42 public:
43 19478 SqlQuery(const char* table_name, sqlite3* db_conn) :
44
1/1
✓ Branch 4 → 5 taken 19478 times.
38956 table_name_(table_name),
45 19478 db_conn_(db_conn)
46 {
47 19478 }
48
49 /// Query for at most N matching records.
50 1 void setLimit(uint32_t limit) { limit_ = limit; }
51
52 /// Remove the LIMIT clause.
53 1 void resetLimit() { limit_ = 0; }
54
55 /// Order the query result set by the given column.
56 ///
57 /// // SELECT ... ORDER BY foo DESC
58 /// query->orderBy("foo", QueryOrder::DESC);
59 ///
60 /// // SELECT ... ORDER BY foo DESC, bar ASC
61 /// query->orderBy("bar", QueryOrder::ASC);
62 ///
63 /// // Remove the ORDER BY clauses
64 /// query->resetOrderBy();
65 ///
66 /// // SELECT ... ORDER BY bar ASC
67 /// query->orderBy("bar", QueryOrder::ASC);
68 6 void orderBy(const char* col_name, const QueryOrder order) { order_clauses_.emplace_back(col_name, order); }
69
70 /// Remove the ORDER BY clauses.
71 1 void resetOrderBy() { order_clauses_.clear(); }
72
73 /// Add a GROUP BY clause. Only one allowed.
74 void groupBy(const char* col_name)
75 {
76 simdb_assert(group_by_column_.empty(), "Only one GROUP BY clause allowed");
77 group_by_column_ = col_name;
78 }
79
80 /// Remove the GROUP BY clause.
81 void resetGroupBy() { group_by_column_.clear(); }
82
83 /// Add a constraint to this query specific to integer types.
84 8240 template <typename T> void addConstraintForInt(const char* col_name, const Constraints constraint, const T target)
85 {
86 static_assert(!std::is_same<T, uint64_t>::value, "Wrong addConstraint*() API");
87
88
2/2
void simdb::SqlQuery::addConstraintForInt<int>(char const*, simdb::Constraints, int):
✓ Branch 2 → 3 taken 8239 times.
void simdb::SqlQuery::addConstraintForInt<unsigned int>(char const*, simdb::Constraints, unsigned int):
✓ Branch 2 → 3 taken 1 time.
8240 std::ostringstream oss;
89
8/8
void simdb::SqlQuery::addConstraintForInt<int>(char const*, simdb::Constraints, int):
✓ Branch 3 → 4 taken 8239 times.
✓ Branch 4 → 5 taken 8239 times.
✓ Branch 5 → 6 taken 8239 times.
✓ Branch 6 → 7 taken 8239 times.
void simdb::SqlQuery::addConstraintForInt<unsigned int>(char const*, simdb::Constraints, unsigned int):
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
8240 oss << col_name << stringify(constraint) << target;
90
4/4
void simdb::SqlQuery::addConstraintForInt<int>(char const*, simdb::Constraints, int):
✓ Branch 8 → 9 taken 8239 times.
✓ Branch 9 → 10 taken 8239 times.
void simdb::SqlQuery::addConstraintForInt<unsigned int>(char const*, simdb::Constraints, unsigned int):
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
8240 constraint_clauses_.emplace_back(oss.str());
91 8240 }
92
93 /// Same as addConstraintForInt() but for users who want explicit APIs.
94 void addConstraintForInt32(const char* col_name, const Constraints constraint, const int32_t target)
95 {
96 addConstraintForInt<int32_t>(col_name, constraint, target);
97 }
98
99 /// Same as addConstraintForInt() but for users who want explicit APIs.
100 1 void addConstraintForUInt32(const char* col_name, const Constraints constraint, const uint32_t target)
101 {
102 1 addConstraintForInt<uint32_t>(col_name, constraint, target);
103 1 }
104
105 /// Same as addConstraintForInt() but for users who want explicit APIs.
106 void addConstraintForInt64(const char* col_name, const Constraints constraint, const int64_t target)
107 {
108 addConstraintForInt<int64_t>(col_name, constraint, target);
109 }
110
111 /// Add a uint64_t constraint to this query.
112 3015 void addConstraintForUInt64(const char* col_name, const Constraints constraint, uint64_t target)
113 {
114 char digits[21];
115
2/2
✓ Branch 4 → 3 taken 60300 times.
✓ Branch 4 → 5 taken 3015 times.
63315 for (int i = 19; i >= 0; --i)
116 {
117 60300 digits[i] = '0' + (target % 10);
118 60300 target /= 10;
119 }
120 3015 digits[20] = '\0';
121
122 3015 std::string clause;
123
1/1
✓ Branch 6 → 7 taken 3015 times.
3015 auto constraint_str = stringify(constraint);
124
125 // column + op + digits + quotes
126
1/1
✓ Branch 8 → 9 taken 3015 times.
3015 clause.reserve(strlen(col_name) + constraint_str.size() + 20 + 2);
127
128
1/1
✓ Branch 9 → 10 taken 3015 times.
3015 clause += col_name;
129
1/1
✓ Branch 10 → 11 taken 3015 times.
3015 clause += constraint_str;
130
1/1
✓ Branch 11 → 12 taken 3015 times.
3015 clause += '"';
131
1/1
✓ Branch 12 → 13 taken 3015 times.
3015 clause += digits;
132
1/1
✓ Branch 13 → 14 taken 3015 times.
3015 clause += '"';
133
134
1/1
✓ Branch 15 → 16 taken 3015 times.
3015 constraint_clauses_.emplace_back(std::move(clause));
135 3015 }
136
137 /// Add a constraint to this query specific to floating-point types.
138 /// Pass in fuzzy=TRUE to tell SQLite to look for matches that are
139 /// within EPS of the target value.
140 template <typename T>
141 45 void addConstraintForDouble(const char* col_name, const Constraints constraint, const T target, bool fuzzy = false)
142 {
143 static_assert(std::is_floating_point<T>::value, "Wrong addConstraint*() API");
144
145
1/1
✓ Branch 2 → 3 taken 45 times.
45 std::ostringstream oss;
146
2/2
✓ Branch 3 → 4 taken 22 times.
✓ Branch 3 → 13 taken 23 times.
45 if (fuzzy)
147 {
148
3/3
✓ Branch 4 → 5 taken 22 times.
✓ Branch 5 → 6 taken 22 times.
✓ Branch 6 → 7 taken 22 times.
22 oss << "fuzzyMatch(" << col_name << ",";
149 22 oss << std::setprecision(std::numeric_limits<T>::max_digits10);
150
2/2
✓ Branch 9 → 10 taken 22 times.
✓ Branch 10 → 11 taken 22 times.
22 oss << target << ",";
151
2/2
✓ Branch 11 → 12 taken 22 times.
✓ Branch 12 → 20 taken 22 times.
22 oss << static_cast<int>(constraint) << ")";
152 } else
153 {
154
3/3
✓ Branch 13 → 14 taken 23 times.
✓ Branch 14 → 15 taken 23 times.
✓ Branch 15 → 16 taken 23 times.
23 oss << col_name << stringify(constraint);
155
1/1
✓ Branch 19 → 20 taken 23 times.
23 oss << std::setprecision(std::numeric_limits<T>::max_digits10) << target;
156 }
157
158
2/2
✓ Branch 20 → 21 taken 45 times.
✓ Branch 21 → 22 taken 45 times.
45 constraint_clauses_.emplace_back(oss.str());
159 45 }
160
161 /// Add a constraint to this query specific to string types.
162 8186 void addConstraintForString(const char* col_name, const Constraints constraint, const std::string& target)
163 {
164 8186 addConstraintForString(col_name, constraint, target.c_str());
165 8186 }
166
167 /// Add a constraint to this query specific to string types.
168 8193 void addConstraintForString(const char* col_name, const Constraints constraint, const char* target)
169 {
170
1/1
✓ Branch 2 → 3 taken 8193 times.
8193 std::ostringstream oss;
171
6/6
✓ Branch 3 → 4 taken 8193 times.
✓ Branch 4 → 5 taken 8193 times.
✓ Branch 5 → 6 taken 8193 times.
✓ Branch 6 → 7 taken 8193 times.
✓ Branch 7 → 8 taken 8193 times.
✓ Branch 8 → 9 taken 8193 times.
8193 oss << col_name << stringify(constraint) << "'" << target << "'";
172
2/2
✓ Branch 10 → 11 taken 8193 times.
✓ Branch 11 → 12 taken 8193 times.
8193 constraint_clauses_.emplace_back(oss.str());
173 8193 }
174
175 /// Add a constraint to this query specific to integer types (except
176 /// uint64_t) and multiple target values.
177 template <typename T>
178 1 void addConstraintForInt(const char* col_name, const SetConstraints constraint,
179 const std::initializer_list<T>& targets)
180 {
181
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
2 addConstraintForInt(col_name, constraint, std::vector<T>{targets.begin(), targets.end()});
182 1 }
183
184 /// Add a constraint to this query specific to integer types (except
185 /// uint64_t) and multiple target values.
186 template <typename T>
187 6 void addConstraintForInt(const char* col_name, const SetConstraints constraint, const std::vector<T>& targets)
188 {
189 static_assert(!std::is_same<T, uint64_t>::value, "Wrong addConstraint*() API");
190
191
1/1
✓ Branch 2 → 3 taken 6 times.
6 std::ostringstream oss;
192
4/4
✓ Branch 3 → 4 taken 6 times.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 6 → 7 taken 6 times.
6 oss << col_name << stringify(constraint) << "(";
193
194
2/2
✓ Branch 16 → 9 taken 93 times.
✓ Branch 16 → 17 taken 6 times.
99 for (size_t idx = 0; idx < targets.size(); ++idx)
195 {
196
1/1
✓ Branch 10 → 11 taken 93 times.
93 oss << targets[idx];
197
2/2
✓ Branch 12 → 13 taken 88 times.
✓ Branch 12 → 14 taken 5 times.
93 if (idx != targets.size() - 1)
198 {
199
1/1
✓ Branch 13 → 14 taken 88 times.
88 oss << ",";
200 }
201 }
202
203
1/1
✓ Branch 17 → 18 taken 6 times.
6 oss << ")";
204
2/2
✓ Branch 18 → 19 taken 6 times.
✓ Branch 19 → 20 taken 6 times.
6 constraint_clauses_.emplace_back(oss.str());
205 6 }
206
207 /// Add a constraint to this query for uint64_t types and multiple target
208 /// values.
209 2 void addConstraintForUInt64(const char* col_name, const SetConstraints constraint,
210 const std::initializer_list<uint64_t>& targets)
211 {
212
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
4 addConstraintForUInt64(col_name, constraint, std::vector<uint64_t>{targets.begin(), targets.end()});
213 2 }
214
215 /// Add a constraint to this query for uint64_t types and multiple target
216 /// values.
217 2 void addConstraintForUInt64(const char* col_name, const SetConstraints constraint,
218 const std::vector<uint64_t>& targets)
219 {
220 2 std::string clause;
221
1/1
✓ Branch 3 → 4 taken 2 times.
2 auto constraint_str = stringify(constraint);
222
223 // column + op + digits + quotes + commas + parens
224
1/1
✓ Branch 6 → 7 taken 2 times.
2 clause.reserve(strlen(col_name) + constraint_str.size() + 20 + 2 + (targets.size() - 1) + 2);
225
1/1
✓ Branch 7 → 8 taken 2 times.
2 clause += col_name;
226
1/1
✓ Branch 8 → 9 taken 2 times.
2 clause += constraint_str;
227
1/1
✓ Branch 9 → 10 taken 2 times.
2 clause += "(";
228
229
2/2
✓ Branch 23 → 11 taken 2 times.
✓ Branch 23 → 24 taken 2 times.
4 for (size_t i = 0; i < targets.size(); ++i)
230 {
231 2 auto target = targets[i];
232 char digits[21];
233
2/2
✓ Branch 14 → 13 taken 40 times.
✓ Branch 14 → 15 taken 2 times.
42 for (int i = 19; i >= 0; --i)
234 {
235 40 digits[i] = '0' + (target % 10);
236 40 target /= 10;
237 }
238 2 digits[20] = '\0';
239
1/1
✓ Branch 15 → 16 taken 2 times.
2 clause += '"';
240
1/1
✓ Branch 16 → 17 taken 2 times.
2 clause += digits;
241
1/1
✓ Branch 17 → 18 taken 2 times.
2 clause += '"';
242
243
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 2 times.
2 if (i != targets.size() - 1)
244 {
245
0/1
✗ Branch 20 → 21 not taken.
✗ clause += ",";
246 }
247 }
248
1/1
✓ Branch 24 → 25 taken 2 times.
2 clause += ")";
249
250
1/1
✓ Branch 26 → 27 taken 2 times.
2 constraint_clauses_.emplace_back(std::move(clause));
251 2 }
252
253 /// Add a constraint to this query specific to floating-point types
254 /// and multiple target values.
255 ///
256 /// Pass in fuzzy=TRUE to tell SQLite to look for matches that are
257 /// within EPS of the target values.
258 template <typename T>
259 24 void addConstraintForDouble(const char* col_name, const SetConstraints constraint,
260 const std::initializer_list<T>& targets, bool fuzzy = false)
261 {
262
2/2
✓ Branch 6 → 7 taken 24 times.
✓ Branch 7 → 8 taken 24 times.
48 addConstraintForDouble(col_name, constraint, std::vector<T>{targets.begin(), targets.end()}, fuzzy);
263 24 }
264
265 /// Add a constraint to this query specific to floating-point types
266 /// and multiple target values.
267 ///
268 /// Pass in fuzzy=TRUE to tell SQLite to look for matches that are
269 /// within EPS of the target values.
270 template <typename T>
271 24 void addConstraintForDouble(const char* col_name, const SetConstraints constraint, const std::vector<T>& targets,
272 bool fuzzy = false)
273 {
274 static_assert(std::is_floating_point<T>::value, "Wrong addConstraint*() API");
275
276
1/1
✓ Branch 2 → 3 taken 24 times.
24 std::ostringstream oss;
277
2/2
✓ Branch 3 → 4 taken 12 times.
✓ Branch 3 → 32 taken 12 times.
24 if (fuzzy)
278 {
279
1/1
✓ Branch 4 → 5 taken 12 times.
12 oss << "(";
280
281
2/2
✓ Branch 30 → 6 taken 12 times.
✓ Branch 30 → 31 taken 12 times.
24 for (size_t idx = 0; idx < targets.size(); ++idx)
282 {
283
1/1
✓ Branch 6 → 7 taken 12 times.
12 std::ostringstream target_oss;
284
3/3
✓ Branch 7 → 8 taken 12 times.
✓ Branch 8 → 9 taken 12 times.
✓ Branch 9 → 10 taken 12 times.
12 target_oss << "fuzzyMatch(" << col_name << ",";
285 12 target_oss << std::setprecision(std::numeric_limits<T>::max_digits10);
286
2/2
✓ Branch 13 → 14 taken 12 times.
✓ Branch 14 → 15 taken 12 times.
12 target_oss << targets[idx] << ",";
287
288
2/2
✓ Branch 15 → 16 taken 6 times.
✓ Branch 15 → 17 taken 6 times.
12 if (constraint == SetConstraints::IN_SET)
289 {
290
1/1
✓ Branch 16 → 18 taken 6 times.
6 target_oss << static_cast<int>(Constraints::EQUAL);
291 } else
292 {
293
1/1
✓ Branch 17 → 18 taken 6 times.
6 target_oss << static_cast<int>(Constraints::NOT_EQUAL);
294 }
295
296
1/1
✓ Branch 18 → 19 taken 12 times.
12 target_oss << ")";
297
2/2
✓ Branch 19 → 20 taken 12 times.
✓ Branch 20 → 21 taken 12 times.
12 oss << target_oss.str();
298
299
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 27 taken 12 times.
12 if (idx != targets.size() - 1)
300 {
301
0/2
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ if (constraint == SetConstraints::IN_SET)
302 {
303
0/1
✗ Branch 25 → 27 not taken.
✗ oss << " OR ";
304 } else
305 {
306
0/1
✗ Branch 26 → 27 not taken.
✗ oss << " AND ";
307 }
308 }
309 }
310
311
1/1
✓ Branch 31 → 49 taken 12 times.
12 oss << ")";
312 } else
313 {
314
4/4
✓ Branch 32 → 33 taken 12 times.
✓ Branch 33 → 34 taken 12 times.
✓ Branch 34 → 35 taken 12 times.
✓ Branch 35 → 36 taken 12 times.
12 oss << col_name << stringify(constraint) << " (";
315
2/2
✓ Branch 47 → 38 taken 12 times.
✓ Branch 47 → 48 taken 12 times.
24 for (size_t idx = 0; idx < targets.size(); ++idx)
316 {
317
1/1
✓ Branch 41 → 42 taken 12 times.
12 oss << std::setprecision(std::numeric_limits<T>::max_digits10) << targets[idx];
318
1/2
✗ Branch 43 → 44 not taken.
✓ Branch 43 → 45 taken 12 times.
12 if (idx != targets.size() - 1)
319 {
320
0/1
✗ Branch 44 → 45 not taken.
✗ oss << ",";
321 }
322 }
323
1/1
✓ Branch 48 → 49 taken 12 times.
12 oss << ")";
324 }
325
326
2/2
✓ Branch 49 → 50 taken 24 times.
✓ Branch 50 → 51 taken 24 times.
24 constraint_clauses_.emplace_back(oss.str());
327 24 }
328
329 /// Add a constraint to this query specific to string types and
330 /// multiple target values.
331 2 void addConstraintForString(const char* col_name, const SetConstraints constraint,
332 const std::initializer_list<const char*>& targets)
333 {
334
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
4 addConstraintForString(col_name, constraint, std::vector<std::string>{targets.begin(), targets.end()});
335 2 }
336
337 /// Add a constraint to this query specific to string types and
338 /// multiple target values.
339 2 void addConstraintForString(const char* col_name, const SetConstraints constraint,
340 const std::vector<std::string>& targets)
341 {
342
1/1
✓ Branch 2 → 3 taken 2 times.
2 std::ostringstream oss;
343
4/4
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 6 → 7 taken 2 times.
2 oss << col_name << stringify(constraint) << "(";
344
345
2/2
✓ Branch 18 → 9 taken 7 times.
✓ Branch 18 → 19 taken 2 times.
9 for (size_t idx = 0; idx < targets.size(); ++idx)
346 {
347
3/3
✓ Branch 9 → 10 taken 7 times.
✓ Branch 11 → 12 taken 7 times.
✓ Branch 12 → 13 taken 7 times.
7 oss << "'" << targets[idx] << "'";
348
2/2
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 16 taken 2 times.
7 if (idx != targets.size() - 1)
349 {
350
1/1
✓ Branch 15 → 16 taken 5 times.
5 oss << ",";
351 }
352 }
353
354
1/1
✓ Branch 19 → 20 taken 2 times.
2 oss << ")";
355
2/2
✓ Branch 20 → 21 taken 2 times.
✓ Branch 21 → 22 taken 2 times.
2 constraint_clauses_.emplace_back(oss.str());
356 2 }
357
358 /// After one or more calls to addConstraint*(), this function can be called
359 /// to create a compound constraint using AND or OR.
360 ///
361 /// // SELECT ... (WHERE ColA = 1 AND ColB = 2) OR (ColC = 3)
362 /// query->addConstraintForInt("ColA", Constraints::EQUAL, 1);
363 /// query->addConstraintForInt("ColB", Constraints::EQUAL, 2);
364 /// auto clause1 = query->releaseConstraintClauses();
365 ///
366 /// query->addConstraintForInt("ColC", Constraints::EQUAL, 3);
367 /// auto clause2 = query->releaseConstraintClauses();
368 ///
369 /// query->addCompoundConstraint(clause1, QueryOperator::OR, clause2);
370 1 void addCompoundConstraint(const std::vector<std::string>& clause1, const QueryOperator compound_constraint,
371 const std::vector<std::string>& clause2)
372 {
373
1/1
✓ Branch 2 → 3 taken 1 time.
1 std::ostringstream oss;
374
1/1
✓ Branch 3 → 4 taken 1 time.
1 oss << "(";
375
2/2
✓ Branch 12 → 5 taken 2 times.
✓ Branch 12 → 13 taken 1 time.
3 for (size_t idx = 0; idx < clause1.size(); ++idx)
376 {
377
1/1
✓ Branch 6 → 7 taken 2 times.
2 oss << clause1[idx];
378
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 1 time.
2 if (idx != clause1.size() - 1)
379 {
380
1/1
✓ Branch 9 → 10 taken 1 time.
1 oss << " AND ";
381 }
382 }
383
384
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1 time.
1 if (compound_constraint == QueryOperator::AND)
385 {
386
0/1
✗ Branch 14 → 16 not taken.
✗ oss << ") AND (";
387 } else
388 {
389
1/1
✓ Branch 15 → 16 taken 1 time.
1 oss << ") OR (";
390 }
391
392
2/2
✓ Branch 24 → 17 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
2 for (size_t idx = 0; idx < clause2.size(); ++idx)
393 {
394
1/1
✓ Branch 18 → 19 taken 1 time.
1 oss << clause2[idx];
395
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 1 time.
1 if (idx != clause2.size() - 1)
396 {
397
0/1
✗ Branch 21 → 22 not taken.
✗ oss << " AND ";
398 }
399 }
400
401
1/1
✓ Branch 25 → 26 taken 1 time.
1 oss << ")";
402
2/2
✓ Branch 26 → 27 taken 1 time.
✓ Branch 27 → 28 taken 1 time.
1 constraint_clauses_.emplace_back(oss.str());
403 1 }
404
405 /// Release the current constraint clauses.
406 2 std::vector<std::string> releaseConstraintClauses() { return std::move(constraint_clauses_); }
407
408 /// Reset the query constraints.
409 84 void resetConstraints() { constraint_clauses_.clear(); }
410
411 /// Non-optional scalar SELECT overloads throw if a column value is SQL NULL.
412 /// Use the std::optional overloads when a column may be unset.
413 ///
414 /// SELECT column values and write to the local variable on each iteration
415 /// (int32_t).
416 ///
417 /// int32_t val;
418 /// query->select("Col", val);
419 11195 void select(const char* col_name, int32_t& user_var)
420 {
421
3/5
✓ Branch 2 → 3 taken 11195 times.
✓ Branch 3 → 4 taken 11195 times.
✓ Branch 4 → 5 taken 11195 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
11195 result_writers_.emplace_back(new ResultWriterInt32(col_name, &user_var));
422 11195 }
423
424 /// Use std::optional overload to query record values that may be NULL/unset
425 17 void select(const char* col_name, std::optional<int32_t>& user_var)
426 {
427
3/5
✓ Branch 2 → 3 taken 17 times.
✓ Branch 3 → 4 taken 17 times.
✓ Branch 4 → 5 taken 17 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
17 result_writers_.emplace_back(new ResultWriterInt32<std::optional<int32_t>>(col_name, &user_var));
428 17 }
429
430 /// SELECT column values and write to the local variable on each iteration
431 /// (uint32_t).
432 15 void select(const char* col_name, uint32_t& user_var)
433 {
434
3/5
✓ Branch 2 → 3 taken 15 times.
✓ Branch 3 → 4 taken 15 times.
✓ Branch 4 → 5 taken 15 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
15 result_writers_.emplace_back(new ResultWriterUInt32(col_name, &user_var));
435 15 }
436
437 /// Use std::optional overload to query record values that may be NULL/unset
438 4 void select(const char* col_name, std::optional<uint32_t>& user_var)
439 {
440
3/5
✓ Branch 2 → 3 taken 4 times.
✓ Branch 3 → 4 taken 4 times.
✓ Branch 4 → 5 taken 4 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
4 result_writers_.emplace_back(new ResultWriterUInt32<std::optional<uint32_t>>(col_name, &user_var));
441 4 }
442
443 /// SELECT column values and write to the local variable on each iteration
444 /// (int64_t).
445 ///
446 /// int64_t val;
447 /// query->select("Col", val);
448 17 void select(const char* col_name, int64_t& user_var)
449 {
450
3/5
✓ Branch 2 → 3 taken 17 times.
✓ Branch 3 → 4 taken 17 times.
✓ Branch 4 → 5 taken 17 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
17 result_writers_.emplace_back(new ResultWriterInt64(col_name, &user_var));
451 17 }
452
453 /// Use std::optional overload to query record values that may be NULL/unset
454 1 void select(const char* col_name, std::optional<int64_t>& user_var)
455 {
456
3/5
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
1 result_writers_.emplace_back(new ResultWriterInt64<std::optional<int64_t>>(col_name, &user_var));
457 1 }
458
459 /// SELECT column values and write to the local variable on each iteration
460 /// (uint64_t).
461 ///
462 /// uint64_t val;
463 /// query->select("Col", val);
464 13 void select(const char* col_name, uint64_t& user_var)
465 {
466
3/5
✓ Branch 2 → 3 taken 13 times.
✓ Branch 3 → 4 taken 13 times.
✓ Branch 4 → 5 taken 13 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
13 result_writers_.emplace_back(new ResultWriterUInt64(col_name, &user_var));
467 13 }
468
469 /// Use std::optional overload to query record values that may be NULL/unset
470 2 void select(const char* col_name, std::optional<uint64_t>& user_var)
471 {
472
3/5
✓ Branch 2 → 3 taken 2 times.
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
2 result_writers_.emplace_back(new ResultWriterUInt64<std::optional<uint64_t>>(col_name, &user_var));
473 2 }
474
475 /// SELECT column values and write to the local variable on each iteration
476 /// (double).
477 ///
478 /// double val;
479 /// query->select("Col", val);
480 7 void select(const char* col_name, double& user_var)
481 {
482
3/5
✓ Branch 2 → 3 taken 7 times.
✓ Branch 3 → 4 taken 7 times.
✓ Branch 4 → 5 taken 7 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
7 result_writers_.emplace_back(new ResultWriterDouble(col_name, &user_var));
483 7 }
484
485 /// Use std::optional overload to query record values that may be NULL/unset
486 1 void select(const char* col_name, std::optional<double>& user_var)
487 {
488
3/5
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
1 result_writers_.emplace_back(new ResultWriterDouble<std::optional<double>>(col_name, &user_var));
489 1 }
490
491 /// SELECT column values and write to the local variable on each iteration
492 /// (string).
493 ///
494 /// std::string val;
495 /// query->select("Col", val);
496 ///
497 /// NULL columns are written as the empty string. Use the std::optional overload
498 /// to distinguish SQL NULL from an explicitly stored empty string.
499 105 void select(const char* col_name, std::string& user_var)
500 {
501
3/5
✓ Branch 2 → 3 taken 105 times.
✓ Branch 3 → 4 taken 105 times.
✓ Branch 4 → 5 taken 105 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
105 result_writers_.emplace_back(new ResultWriterString(col_name, &user_var));
502 105 }
503
504 /// Use std::optional overload to distinguish SQL NULL from "".
505 13 void select(const char* col_name, std::optional<std::string>& user_var)
506 {
507
3/5
✓ Branch 2 → 3 taken 13 times.
✓ Branch 3 → 4 taken 13 times.
✓ Branch 4 → 5 taken 13 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
13 result_writers_.emplace_back(new ResultWriterString<std::optional<std::string>>(col_name, &user_var));
508 13 }
509
510 /// SELECT column values and write to the local variable on each iteration
511 /// (blob).
512 ///
513 /// std::vector<int> val;
514 /// query->select("Col", val);
515 ///
516 /// Note that this does not have a std::optional overload since std::vector::empty()
517 /// makes NULL obvious.
518 8190 template <typename T> void select(const char* col_name, std::vector<T>& user_var)
519 {
520
6/10
void simdb::SqlQuery::select<char>(char const*, std::vector<char, std::allocator<char> >&):
✓ Branch 2 → 3 taken 8176 times.
✓ Branch 3 → 4 taken 8176 times.
✓ Branch 4 → 5 taken 8176 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void simdb::SqlQuery::select<int>(char const*, std::vector<int, std::allocator<int> >&):
✓ Branch 2 → 3 taken 14 times.
✓ Branch 3 → 4 taken 14 times.
✓ Branch 4 → 5 taken 14 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
8190 result_writers_.emplace_back(new ResultWriterBlob<T>(col_name, &user_var));
521 8190 }
522
523 /// Deselect all record property values.
524 void resetSelections() { result_writers_.clear(); }
525
526 /// Count the number of records matching this query's search constraints.
527 /// If no constraints were added, counts all records in the table.
528 87 uint64_t count()
529 {
530
1/1
✓ Branch 2 → 3 taken 87 times.
87 std::ostringstream oss;
531
3/3
✓ Branch 3 → 4 taken 87 times.
✓ Branch 4 → 5 taken 87 times.
✓ Branch 5 → 6 taken 87 times.
87 oss << "SELECT COUNT(*) FROM " << table_name_ << " ";
532
1/1
✓ Branch 6 → 7 taken 87 times.
87 appendConstraintClauses_(oss);
533
534
1/1
✓ Branch 7 → 8 taken 87 times.
87 const auto cmd = oss.str();
535
1/1
✓ Branch 9 → 10 taken 87 times.
87 auto stmt = SQLitePreparedStatement(db_conn_, cmd);
536
2/2
✓ Branch 11 → 12 taken 87 times.
✓ Branch 12 → 13 taken 87 times.
87 auto rc = SQLiteReturnCode(sqlite3_step(stmt));
537
538
1/2
✓ Branch 14 → 15 taken 87 times.
✗ Branch 14 → 18 not taken.
87 if (rc == SQLITE_ROW)
539 {
540
1/1
✓ Branch 16 → 17 taken 87 times.
87 return sqlite3_column_int64(stmt, 0);
541 }
542
543
0/2
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
✗ if (rc == SQLITE_DONE)
544 {
545 ✗ return 0;
546 }
547
548
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 24 → 25 not taken.
✗ throw DBException(sqlite3_errmsg(db_conn_));
549 87 }
550
551 /// DELETE all records that match the query's current search constraints.
552 2 void deleteResultSet()
553 {
554
1/1
✓ Branch 2 → 3 taken 2 times.
2 std::ostringstream oss;
555
3/3
✓ Branch 3 → 4 taken 2 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
2 oss << "DELETE FROM " << table_name_ << " ";
556
1/1
✓ Branch 6 → 7 taken 2 times.
2 appendConstraintClauses_(oss);
557
558
1/1
✓ Branch 7 → 8 taken 2 times.
2 const auto cmd = oss.str();
559
1/1
✓ Branch 9 → 10 taken 2 times.
2 auto stmt = SQLitePreparedStatement(db_conn_, cmd);
560
2/14
✓ Branch 11 → 12 taken 2 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 32 taken 2 times.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 30 → 31 not taken.
2 simdb_assert(sqlite3_step(stmt) == SQLITE_DONE, sqlite3_errmsg(db_conn_));
561 2 }
562
563 /// Execute the query. Returns an iterator to walk the result set
564 /// row-by-row and populate the user's local variables given to
565 /// the select() calls.
566 19487 SqlResultIterator getResultSet()
567 {
568
1/1
✓ Branch 2 → 3 taken 19487 times.
19487 std::ostringstream oss;
569
1/1
✓ Branch 3 → 4 taken 19487 times.
19487 oss << "SELECT ";
570
2/2
✓ Branch 14 → 5 taken 19611 times.
✓ Branch 14 → 15 taken 19487 times.
39098 for (size_t idx = 0; idx < result_writers_.size(); ++idx)
571 {
572
1/1
✓ Branch 8 → 9 taken 19611 times.
19611 oss << result_writers_[idx]->getColName();
573
2/2
✓ Branch 10 → 11 taken 124 times.
✓ Branch 10 → 12 taken 19487 times.
19611 if (idx != result_writers_.size() - 1)
574 {
575
1/1
✓ Branch 11 → 12 taken 124 times.
124 oss << ",";
576 }
577 }
578
579
3/3
✓ Branch 15 → 16 taken 19487 times.
✓ Branch 16 → 17 taken 19487 times.
✓ Branch 17 → 18 taken 19487 times.
19487 oss << " FROM " << table_name_ << " ";
580
581
1/1
✓ Branch 18 → 19 taken 19487 times.
19487 appendConstraintClauses_(oss);
582
1/1
✓ Branch 19 → 20 taken 19487 times.
19487 appendGroupByClause_(oss);
583
1/1
✓ Branch 20 → 21 taken 19487 times.
19487 appendOrderByClauses_(oss);
584
1/1
✓ Branch 21 → 22 taken 19487 times.
19487 appendLimitClause_(oss);
585
586
1/1
✓ Branch 22 → 23 taken 19487 times.
19487 const auto cmd = oss.str();
587
1/1
✓ Branch 24 → 25 taken 19486 times.
19487 auto stmt = SQLitePreparedStatement(db_conn_, cmd);
588
589 19486 std::vector<std::shared_ptr<ResultWriterBase>> result_writers;
590
2/2
✓ Branch 34 → 28 taken 19608 times.
✓ Branch 34 → 35 taken 19486 times.
39094 for (const auto& writer : result_writers_)
591 {
592
2/2
✓ Branch 30 → 31 taken 19608 times.
✓ Branch 31 → 32 taken 19608 times.
19608 result_writers.emplace_back(writer->clone());
593 }
594
595 38972 return SqlResultIterator(stmt.release(), std::move(result_writers));
596 19488 }
597
598 private:
599 /// Append WHERE clause(s).
600 19576 void appendConstraintClauses_(std::ostringstream& oss) const
601 {
602
2/2
✓ Branch 3 → 4 taken 19519 times.
✓ Branch 3 → 16 taken 57 times.
19576 if (!constraint_clauses_.empty())
603 {
604 19519 oss << " WHERE ";
605
2/2
✓ Branch 14 → 6 taken 19526 times.
✓ Branch 14 → 15 taken 19519 times.
39045 for (size_t idx = 0; idx < constraint_clauses_.size(); ++idx)
606 {
607 19526 oss << constraint_clauses_[idx] << " ";
608
2/2
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 19519 times.
19526 if (idx != constraint_clauses_.size() - 1)
609 {
610 7 oss << " AND ";
611 }
612 }
613 19519 oss << " ";
614 }
615 19576 }
616
617 /// Append GROUP BY clause.
618 19487 void appendGroupByClause_(std::ostringstream& oss) const
619 {
620
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 19487 times.
19487 if (!group_by_column_.empty())
621 {
622 ✗ oss << " GROUP BY " << group_by_column_ << " ";
623 }
624 19487 }
625
626 /// Append ORDER BY clause(s).
627 19487 void appendOrderByClauses_(std::ostringstream& oss) const
628 {
629
2/2
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 18 taken 19482 times.
19487 if (!order_clauses_.empty())
630 {
631 5 oss << " ORDER BY ";
632
2/2
✓ Branch 16 → 6 taken 6 times.
✓ Branch 16 → 17 taken 5 times.
11 for (size_t idx = 0; idx < order_clauses_.size(); ++idx)
633 {
634 6 oss << order_clauses_[idx].col_name << " " << order_clauses_[idx].order;
635
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 5 times.
6 if (idx != order_clauses_.size() - 1)
636 {
637 1 oss << ",";
638 }
639 }
640 5 oss << " ";
641 }
642 19487 }
643
644 /// Append LIMIT clause.
645 19487 void appendLimitClause_(std::ostringstream& oss) const
646 {
647
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 19486 times.
19487 if (limit_)
648 {
649 1 oss << " LIMIT " << limit_;
650 }
651 19487 }
652
653 /// \struct QueryOrderClause
654 /// \brief Used to build clauses like "ORDER BY ColA ASC, ColB DESC"
655 struct QueryOrderClause
656 {
657 std::string col_name;
658 QueryOrder order;
659
660 6 QueryOrderClause(const char* col_name, const QueryOrder order) :
661
1/1
✓ Branch 4 → 5 taken 6 times.
12 col_name(col_name),
662 6 order(order)
663 {
664 6 }
665 };
666
667 /// SELECT ColA,ColB FROM <table_name_> WHERE ...
668 const std::string table_name_;
669
670 /// Underlying sqlite3 database
671 sqlite3* const db_conn_;
672
673 /// SELECT ColA,ColB FROM Table WHERE ... LIMIT <limit_>
674 uint32_t limit_ = 0;
675
676 /// SELECT ColA,ColB FROM Table WHERE ... ORDER BY <order_clauses_>
677 std::vector<QueryOrderClause> order_clauses_;
678
679 /// SELECT ColA,ColB FROM Table WHERE ... GROUP BY <group_by_column_>
680 std::string group_by_column_;
681
682 /// SELECT ColA,ColB FROM Table WHERE <constraint_clauses_>
683 std::vector<std::string> constraint_clauses_;
684
685 /// SELECT <result_writers_> FROM Table WHERE ...
686 std::vector<std::shared_ptr<ResultWriterBase>> result_writers_;
687 };
688
689 } // namespace simdb
690