summary refs log tree commit diff stats
path: root/lib/statement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/statement.cpp')
-rw-r--r--lib/statement.cpp290
1 files changed, 145 insertions, 145 deletions
diff --git a/lib/statement.cpp b/lib/statement.cpp index 222a8eb..846b9de 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp
@@ -12,22 +12,22 @@
12#include "pronunciation.h" 12#include "pronunciation.h"
13 13
14namespace verbly { 14namespace verbly {
15 15
16 statement::statement( 16 statement::statement(
17 object context, 17 object context,
18 filter queryFilter) : 18 filter queryFilter) :
19 statement(getTableForContext(context), queryFilter.compact().normalize(context)) 19 statement(getTableForContext(context), queryFilter.compact().normalize(context))
20 { 20 {
21 } 21 }
22 22
23 std::string statement::getQueryString(std::list<std::string> select, bool random, int limit) const 23 std::string statement::getQueryString(std::list<std::string> select, bool random, int limit) const
24 { 24 {
25 std::stringstream queryStream; 25 std::stringstream queryStream;
26 26
27 if (!withs_.empty()) 27 if (!withs_.empty())
28 { 28 {
29 queryStream << "WITH RECURSIVE "; 29 queryStream << "WITH RECURSIVE ";
30 30
31 std::list<std::string> ctes; 31 std::list<std::string> ctes;
32 for (const with& cte : withs_) 32 for (const with& cte : withs_)
33 { 33 {
@@ -39,19 +39,19 @@ namespace verbly {
39 cteStream << cte.getTableForId(cte.getTopTable()); 39 cteStream << cte.getTableForId(cte.getTopTable());
40 cteStream << " AS "; 40 cteStream << " AS ";
41 cteStream << cte.getTopTable(); 41 cteStream << cte.getTopTable();
42 42
43 for (const join& j : cte.getJoins()) 43 for (const join& j : cte.getJoins())
44 { 44 {
45 cteStream << " "; 45 cteStream << " ";
46 cteStream << j; 46 cteStream << j;
47 } 47 }
48 48
49 if (cte.getCondition().getType() != condition::type::empty) 49 if (cte.getCondition().getType() != condition::type::empty)
50 { 50 {
51 cteStream << " WHERE "; 51 cteStream << " WHERE ";
52 cteStream << cte.getCondition().toSql(); 52 cteStream << cte.getCondition().toSql();
53 } 53 }
54 54
55 if (cte.isRecursive()) 55 if (cte.isRecursive())
56 { 56 {
57 cteStream << " UNION SELECT l.* FROM "; 57 cteStream << " UNION SELECT l.* FROM ";
@@ -69,59 +69,59 @@ namespace verbly {
69 cteStream << " = l."; 69 cteStream << " = l.";
70 cteStream << cte.getField().getColumn(); 70 cteStream << cte.getField().getColumn();
71 } 71 }
72 72
73 cteStream << ")"; 73 cteStream << ")";
74 74
75 ctes.push_back(cteStream.str()); 75 ctes.push_back(cteStream.str());
76 } 76 }
77 77
78 queryStream << implode(std::begin(ctes), std::end(ctes), ", "); 78 queryStream << implode(std::begin(ctes), std::end(ctes), ", ");
79 queryStream << " "; 79 queryStream << " ";
80 } 80 }
81 81
82 std::list<std::string> realSelect; 82 std::list<std::string> realSelect;
83 for (std::string& s : select) 83 for (std::string& s : select)
84 { 84 {
85 realSelect.push_back(topTable_ + "." + s); 85 realSelect.push_back(topTable_ + "." + s);
86 } 86 }
87 87
88 queryStream << "SELECT "; 88 queryStream << "SELECT ";
89 queryStream << implode(std::begin(realSelect), std::end(realSelect), ", "); 89 queryStream << implode(std::begin(realSelect), std::end(realSelect), ", ");
90 queryStream << " FROM "; 90 queryStream << " FROM ";
91 queryStream << tables_.at(topTable_); 91 queryStream << tables_.at(topTable_);
92 queryStream << " AS "; 92 queryStream << " AS ";
93 queryStream << topTable_; 93 queryStream << topTable_;
94 94
95 for (const join& j : joins_) 95 for (const join& j : joins_)
96 { 96 {
97 queryStream << " "; 97 queryStream << " ";
98 queryStream << j; 98 queryStream << j;
99 } 99 }
100 100
101 if (topCondition_.getType() != condition::type::empty) 101 if (topCondition_.getType() != condition::type::empty)
102 { 102 {
103 queryStream << " WHERE "; 103 queryStream << " WHERE ";
104 queryStream << topCondition_.toSql(); 104 queryStream << topCondition_.toSql();
105 } 105 }
106 106
107 if (random) 107 if (random)
108 { 108 {
109 queryStream << " ORDER BY RANDOM()"; 109 queryStream << " ORDER BY RANDOM()";
110 } 110 }
111 111
112 if (limit > 0) 112 if (limit > 0)
113 { 113 {
114 queryStream << " LIMIT "; 114 queryStream << " LIMIT ";
115 queryStream << limit; 115 queryStream << limit;
116 } 116 }
117 117
118 return queryStream.str(); 118 return queryStream.str();
119 } 119 }
120 120
121 std::list<binding> statement::getBindings() const 121 std::list<binding> statement::getBindings() const
122 { 122 {
123 std::list<binding> result; 123 std::list<binding> result;
124 124
125 for (const with& w : withs_) 125 for (const with& w : withs_)
126 { 126 {
127 for (binding value : w.getCondition().flattenBindings()) 127 for (binding value : w.getCondition().flattenBindings())
@@ -129,15 +129,15 @@ namespace verbly {
129 result.push_back(std::move(value)); 129 result.push_back(std::move(value));
130 } 130 }
131 } 131 }
132 132
133 for (binding value : topCondition_.flattenBindings()) 133 for (binding value : topCondition_.flattenBindings())
134 { 134 {
135 result.push_back(std::move(value)); 135 result.push_back(std::move(value));
136 } 136 }
137 137
138 return result; 138 return result;
139 } 139 }
140 140
141 statement::statement( 141 statement::statement(
142 std::string tableName, 142 std::string tableName,
143 filter clause, 143 filter clause,
@@ -149,7 +149,7 @@ namespace verbly {
149 topCondition_(parseFilter(std::move(clause))) 149 topCondition_(parseFilter(std::move(clause)))
150 { 150 {
151 } 151 }
152 152
153 /** 153 /**
154 * This function recursively parses the query's filter condition. It is not 154 * This function recursively parses the query's filter condition. It is not
155 * idempotent. It returns a condition object representing the passed filter, 155 * idempotent. It returns a condition object representing the passed filter,
@@ -165,7 +165,7 @@ namespace verbly {
165 { 165 {
166 return {}; 166 return {};
167 } 167 }
168 168
169 case filter::type::singleton: 169 case filter::type::singleton:
170 { 170 {
171 switch (clause.getField().getType()) 171 switch (clause.getField().getType())
@@ -174,7 +174,7 @@ namespace verbly {
174 { 174 {
175 return {}; 175 return {};
176 } 176 }
177 177
178 // For primitive type filters, all we need to do is translate the 178 // For primitive type filters, all we need to do is translate the
179 // filter object directly into a condition object. No joins are 179 // filter object directly into a condition object. No joins are
180 // necessary. 180 // necessary.
@@ -188,67 +188,67 @@ namespace verbly {
188 { 188 {
189 return condition(topTable_, clause.getField().getColumn(), true); 189 return condition(topTable_, clause.getField().getColumn(), true);
190 } 190 }
191 191
192 case filter::comparison::is_not_null: 192 case filter::comparison::is_not_null:
193 { 193 {
194 return condition(topTable_, clause.getField().getColumn(), false); 194 return condition(topTable_, clause.getField().getColumn(), false);
195 } 195 }
196 196
197 case filter::comparison::int_equals: 197 case filter::comparison::int_equals:
198 { 198 {
199 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getIntegerArgument()); 199 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getIntegerArgument());
200 } 200 }
201 201
202 case filter::comparison::int_does_not_equal: 202 case filter::comparison::int_does_not_equal:
203 { 203 {
204 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getIntegerArgument()); 204 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getIntegerArgument());
205 } 205 }
206 206
207 case filter::comparison::int_is_at_least: 207 case filter::comparison::int_is_at_least:
208 { 208 {
209 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_least, clause.getIntegerArgument()); 209 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_least, clause.getIntegerArgument());
210 } 210 }
211 211
212 case filter::comparison::int_is_greater_than: 212 case filter::comparison::int_is_greater_than:
213 { 213 {
214 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_greater_than, clause.getIntegerArgument()); 214 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_greater_than, clause.getIntegerArgument());
215 } 215 }
216 216
217 case filter::comparison::int_is_at_most: 217 case filter::comparison::int_is_at_most:
218 { 218 {
219 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_most, clause.getIntegerArgument()); 219 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_at_most, clause.getIntegerArgument());
220 } 220 }
221 221
222 case filter::comparison::int_is_less_than: 222 case filter::comparison::int_is_less_than:
223 { 223 {
224 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_less_than, clause.getIntegerArgument()); 224 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_less_than, clause.getIntegerArgument());
225 } 225 }
226 226
227 case filter::comparison::boolean_equals: 227 case filter::comparison::boolean_equals:
228 { 228 {
229 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getBooleanArgument() ? 1 : 0); 229 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getBooleanArgument() ? 1 : 0);
230 } 230 }
231 231
232 case filter::comparison::string_equals: 232 case filter::comparison::string_equals:
233 { 233 {
234 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getStringArgument()); 234 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, clause.getStringArgument());
235 } 235 }
236 236
237 case filter::comparison::string_does_not_equal: 237 case filter::comparison::string_does_not_equal:
238 { 238 {
239 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getStringArgument()); 239 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, clause.getStringArgument());
240 } 240 }
241 241
242 case filter::comparison::string_is_like: 242 case filter::comparison::string_is_like:
243 { 243 {
244 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_like, clause.getStringArgument()); 244 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_like, clause.getStringArgument());
245 } 245 }
246 246
247 case filter::comparison::string_is_not_like: 247 case filter::comparison::string_is_not_like:
248 { 248 {
249 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); 249 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument());
250 } 250 }
251 251
252 case filter::comparison::matches: 252 case filter::comparison::matches:
253 case filter::comparison::does_not_match: 253 case filter::comparison::does_not_match:
254 case filter::comparison::hierarchally_matches: 254 case filter::comparison::hierarchally_matches:
@@ -258,7 +258,7 @@ namespace verbly {
258 } 258 }
259 } 259 }
260 } 260 }
261 261
262 case field::type::join: 262 case field::type::join:
263 { 263 {
264 // First, figure out what table we need to join against. 264 // First, figure out what table we need to join against.
@@ -278,9 +278,9 @@ namespace verbly {
278 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 278 clause.getJoinCondition().normalize(clause.getField().getJoinObject()),
279 nextTableId_, 279 nextTableId_,
280 nextWithId_); 280 nextWithId_);
281 281
282 std::string joinTable = joinStmt.topTable_; 282 std::string joinTable = joinStmt.topTable_;
283 283
284 if (clause.getComparison() == filter::comparison::does_not_match) 284 if (clause.getComparison() == filter::comparison::does_not_match)
285 { 285 {
286 // If the comparison is actually a negative filter, we can't just 286 // If the comparison is actually a negative filter, we can't just
@@ -293,7 +293,7 @@ namespace verbly {
293 // condition on the join column being NULL as before. 293 // condition on the join column being NULL as before.
294 std::string withName = instantiateWith(clause.getField().getTable()); 294 std::string withName = instantiateWith(clause.getField().getTable());
295 std::string withInstName = instantiateTable(withName); 295 std::string withInstName = instantiateTable(withName);
296 296
297 // LEFT JOIN against the CTE. 297 // LEFT JOIN against the CTE.
298 joins_.emplace_back( 298 joins_.emplace_back(
299 true, 299 true,
@@ -310,7 +310,7 @@ namespace verbly {
310 std::map<std::string, std::string> cteTables = std::move(joinStmt.tables_); 310 std::map<std::string, std::string> cteTables = std::move(joinStmt.tables_);
311 std::list<join> cteJoins = std::move(joinStmt.joins_); 311 std::list<join> cteJoins = std::move(joinStmt.joins_);
312 condition cteCondition = integrate(std::move(joinStmt), true); 312 condition cteCondition = integrate(std::move(joinStmt), true);
313 313
314 withs_.emplace_back( 314 withs_.emplace_back(
315 std::move(withName), 315 std::move(withName),
316 clause.getField(), 316 clause.getField(),
@@ -319,7 +319,7 @@ namespace verbly {
319 std::move(cteCondition), 319 std::move(cteCondition),
320 std::move(cteJoins), 320 std::move(cteJoins),
321 false); 321 false);
322 322
323 // Condition on the join column being NULL, which causes the query 323 // Condition on the join column being NULL, which causes the query
324 // to only return results that do not match the subquery. 324 // to only return results that do not match the subquery.
325 return condition(std::move(withInstName), clause.getField().getColumn(), true); 325 return condition(std::move(withInstName), clause.getField().getColumn(), true);
@@ -332,14 +332,14 @@ namespace verbly {
332 clause.getField().getColumn(), 332 clause.getField().getColumn(),
333 std::move(joinTable), 333 std::move(joinTable),
334 clause.getField().getColumn()); 334 clause.getField().getColumn());
335 335
336 // Integrate the subquery's table mappings, joins, and CTEs into 336 // Integrate the subquery's table mappings, joins, and CTEs into
337 // this statement, and return the subquery condition as our 337 // this statement, and return the subquery condition as our
338 // condition. 338 // condition.
339 return integrate(std::move(joinStmt)); 339 return integrate(std::move(joinStmt));
340 } 340 }
341 } 341 }
342 342
343 case field::type::join_through: 343 case field::type::join_through:
344 { 344 {
345 // Recursively parse the subquery, and therefore obtain an 345 // Recursively parse the subquery, and therefore obtain an
@@ -350,9 +350,9 @@ namespace verbly {
350 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 350 clause.getJoinCondition().normalize(clause.getField().getJoinObject()),
351 nextTableId_, 351 nextTableId_,
352 nextWithId_); 352 nextWithId_);
353 353
354 std::string joinTable = joinStmt.topTable_; 354 std::string joinTable = joinStmt.topTable_;
355 355
356 if (clause.getComparison() == filter::comparison::does_not_match) 356 if (clause.getComparison() == filter::comparison::does_not_match)
357 { 357 {
358 // If the comparison is actually a negative filter, we can't just 358 // If the comparison is actually a negative filter, we can't just
@@ -366,7 +366,7 @@ namespace verbly {
366 // condition on the join column being NULL as before. 366 // condition on the join column being NULL as before.
367 std::string withName = instantiateWith(clause.getField().getTable()); 367 std::string withName = instantiateWith(clause.getField().getTable());
368 std::string withInstName = instantiateTable(withName); 368 std::string withInstName = instantiateTable(withName);
369 369
370 // LEFT JOIN against the CTE. 370 // LEFT JOIN against the CTE.
371 joins_.emplace_back( 371 joins_.emplace_back(
372 true, 372 true,
@@ -375,7 +375,7 @@ namespace verbly {
375 clause.getField().getColumn(), 375 clause.getField().getColumn(),
376 withInstName, 376 withInstName,
377 clause.getField().getJoinColumn()); 377 clause.getField().getJoinColumn());
378 378
379 // Modify the substatement such that the through table is the top 379 // Modify the substatement such that the through table is the top
380 // table, and such that it joins against the previous top table. 380 // table, and such that it joins against the previous top table.
381 std::string throughTable = joinStmt.instantiateTable(clause.getField().getTable()); 381 std::string throughTable = joinStmt.instantiateTable(clause.getField().getTable());
@@ -386,7 +386,7 @@ namespace verbly {
386 clause.getField().getForeignJoinColumn(), 386 clause.getField().getForeignJoinColumn(),
387 std::move(joinTable), 387 std::move(joinTable),
388 clause.getField().getForeignColumn()); 388 clause.getField().getForeignColumn());
389 389
390 joinStmt.topTable_ = throughTable; 390 joinStmt.topTable_ = throughTable;
391 391
392 // All CTEs have to be in the main statement, so integrate any 392 // All CTEs have to be in the main statement, so integrate any
@@ -396,7 +396,7 @@ namespace verbly {
396 std::map<std::string, std::string> cteTables = std::move(joinStmt.tables_); 396 std::map<std::string, std::string> cteTables = std::move(joinStmt.tables_);
397 std::list<join> cteJoins = std::move(joinStmt.joins_); 397 std::list<join> cteJoins = std::move(joinStmt.joins_);
398 condition cteCondition = integrate(std::move(joinStmt), true); 398 condition cteCondition = integrate(std::move(joinStmt), true);
399 399
400 withs_.emplace_back( 400 withs_.emplace_back(
401 std::move(withName), 401 std::move(withName),
402 clause.getField(), 402 clause.getField(),
@@ -405,14 +405,14 @@ namespace verbly {
405 std::move(cteCondition), 405 std::move(cteCondition),
406 std::move(cteJoins), 406 std::move(cteJoins),
407 false); 407 false);
408 408
409 // Condition on the join column being NULL, which causes the query 409 // Condition on the join column being NULL, which causes the query
410 // to only return results that do not match the subquery. 410 // to only return results that do not match the subquery.
411 return condition(std::move(withInstName), clause.getField().getJoinColumn(), true); 411 return condition(std::move(withInstName), clause.getField().getJoinColumn(), true);
412 } else { 412 } else {
413 // Instantiate the through table. 413 // Instantiate the through table.
414 std::string throughTable = instantiateTable(clause.getField().getTable()); 414 std::string throughTable = instantiateTable(clause.getField().getTable());
415 415
416 // INNER JOIN against the through table. 416 // INNER JOIN against the through table.
417 joins_.emplace_back( 417 joins_.emplace_back(
418 false, 418 false,
@@ -421,7 +421,7 @@ namespace verbly {
421 clause.getField().getColumn(), 421 clause.getField().getColumn(),
422 throughTable, 422 throughTable,
423 clause.getField().getJoinColumn()); 423 clause.getField().getJoinColumn());
424 424
425 // INNER JOIN from the through table to the top table of the subquery. 425 // INNER JOIN from the through table to the top table of the subquery.
426 joins_.emplace_back( 426 joins_.emplace_back(
427 false, 427 false,
@@ -430,20 +430,20 @@ namespace verbly {
430 clause.getField().getForeignJoinColumn(), 430 clause.getField().getForeignJoinColumn(),
431 std::move(joinTable), 431 std::move(joinTable),
432 clause.getField().getForeignColumn()); 432 clause.getField().getForeignColumn());
433 433
434 // Integrate the subquery's table mappings, joins, and CTEs into 434 // Integrate the subquery's table mappings, joins, and CTEs into
435 // this statement, and return the subquery condition as our 435 // this statement, and return the subquery condition as our
436 // condition. 436 // condition.
437 return integrate(std::move(joinStmt)); 437 return integrate(std::move(joinStmt));
438 } 438 }
439 } 439 }
440 440
441 case field::type::hierarchal_join: 441 case field::type::hierarchal_join:
442 { 442 {
443 // Create a recursive CTE that represents the results of the subquery. 443 // Create a recursive CTE that represents the results of the subquery.
444 std::string withName = instantiateWith(clause.getField().getTable()); 444 std::string withName = instantiateWith(clause.getField().getTable());
445 std::string withInstName = instantiateTable(withName); 445 std::string withInstName = instantiateTable(withName);
446 446
447 // If we are matching against the subquery, we INNER JOIN with the 447 // If we are matching against the subquery, we INNER JOIN with the
448 // CTE. If we are negatively matching the subquery, we LEFT JOIN 448 // CTE. If we are negatively matching the subquery, we LEFT JOIN
449 // with the CTE. 449 // with the CTE.
@@ -452,7 +452,7 @@ namespace verbly {
452 { 452 {
453 outer = true; 453 outer = true;
454 } 454 }
455 455
456 // Join against the CTE. 456 // Join against the CTE.
457 joins_.emplace_back( 457 joins_.emplace_back(
458 outer, 458 outer,
@@ -461,14 +461,14 @@ namespace verbly {
461 clause.getField().getColumn(), 461 clause.getField().getColumn(),
462 withInstName, 462 withInstName,
463 clause.getField().getColumn()); 463 clause.getField().getColumn());
464 464
465 // Recursively parse the subquery in order to create the CTE. 465 // Recursively parse the subquery in order to create the CTE.
466 statement withStmt( 466 statement withStmt(
467 getTableForContext(clause.getField().getObject()), 467 getTableForContext(clause.getField().getObject()),
468 clause.getJoinCondition().normalize(clause.getField().getObject()), 468 clause.getJoinCondition().normalize(clause.getField().getObject()),
469 nextTableId_, 469 nextTableId_,
470 nextWithId_); 470 nextWithId_);
471 471
472 // All CTEs have to be in the main statement, so integrate any CTEs 472 // All CTEs have to be in the main statement, so integrate any CTEs
473 // that our subquery uses. Also, retrieve the table mapping, joins 473 // that our subquery uses. Also, retrieve the table mapping, joins
474 // list, and subquery condition, and use them to create the CTE. 474 // list, and subquery condition, and use them to create the CTE.
@@ -476,7 +476,7 @@ namespace verbly {
476 std::map<std::string, std::string> cteTables = std::move(withStmt.tables_); 476 std::map<std::string, std::string> cteTables = std::move(withStmt.tables_);
477 std::list<join> cteJoins = std::move(withStmt.joins_); 477 std::list<join> cteJoins = std::move(withStmt.joins_);
478 condition cteCondition = integrate(std::move(withStmt), true); 478 condition cteCondition = integrate(std::move(withStmt), true);
479 479
480 withs_.emplace_back( 480 withs_.emplace_back(
481 std::move(withName), 481 std::move(withName),
482 clause.getField(), 482 clause.getField(),
@@ -498,11 +498,11 @@ namespace verbly {
498 } 498 }
499 } 499 }
500 } 500 }
501 501
502 case filter::type::group: 502 case filter::type::group:
503 { 503 {
504 condition grp(clause.getOrlogic()); 504 condition grp(clause.getOrlogic());
505 505
506 for (const filter& child : clause) 506 for (const filter& child : clause)
507 { 507 {
508 condition newChild = parseFilter(child); 508 condition newChild = parseFilter(child);
@@ -511,30 +511,30 @@ namespace verbly {
511 grp += std::move(newChild); 511 grp += std::move(newChild);
512 } 512 }
513 } 513 }
514 514
515 if (grp.getChildren().empty()) 515 if (grp.getChildren().empty())
516 { 516 {
517 grp = {}; 517 grp = {};
518 } 518 }
519 519
520 return grp; 520 return grp;
521 } 521 }
522 } 522 }
523 } 523 }
524 524
525 std::string statement::instantiateTable(std::string name) 525 std::string statement::instantiateTable(std::string name)
526 { 526 {
527 std::string identifier = name + "_" + std::to_string(nextTableId_++); 527 std::string identifier = name + "_" + std::to_string(nextTableId_++);
528 tables_[identifier] = name; 528 tables_[identifier] = name;
529 529
530 return identifier; 530 return identifier;
531 } 531 }
532 532
533 std::string statement::instantiateWith(std::string name) 533 std::string statement::instantiateWith(std::string name)
534 { 534 {
535 return name + "_tree_" + std::to_string(nextWithId_++); 535 return name + "_tree_" + std::to_string(nextWithId_++);
536 } 536 }
537 537
538 /** 538 /**
539 * This method integrates the parts of a recursively generated statement into 539 * This method integrates the parts of a recursively generated statement into
540 * this statement. This is used because filters are recursive objects, but 540 * this statement. This is used because filters are recursive objects, but
@@ -551,13 +551,13 @@ namespace verbly {
551 { 551 {
552 tables_[mapping.first] = mapping.second; 552 tables_[mapping.first] = mapping.second;
553 } 553 }
554 554
555 for (auto& j : subStmt.joins_) 555 for (auto& j : subStmt.joins_)
556 { 556 {
557 joins_.push_back(j); 557 joins_.push_back(j);
558 } 558 }
559 } 559 }
560 560
561 for (auto& w : subStmt.withs_) 561 for (auto& w : subStmt.withs_)
562 { 562 {
563 withs_.push_back(w); 563 withs_.push_back(w);
@@ -565,10 +565,10 @@ namespace verbly {
565 565
566 nextTableId_ = subStmt.nextTableId_; 566 nextTableId_ = subStmt.nextTableId_;
567 nextWithId_ = subStmt.nextWithId_; 567 nextWithId_ = subStmt.nextWithId_;
568 568
569 return subStmt.topCondition_; 569 return subStmt.topCondition_;
570 } 570 }
571 571
572 std::ostream& operator<<(std::ostream& oss, const statement::join& j) 572 std::ostream& operator<<(std::ostream& oss, const statement::join& j)
573 { 573 {
574 if (j.isOuterJoin()) 574 if (j.isOuterJoin())
@@ -577,7 +577,7 @@ namespace verbly {
577 } else { 577 } else {
578 oss << "INNER"; 578 oss << "INNER";
579 } 579 }
580 580
581 return oss 581 return oss
582 << " JOIN " 582 << " JOIN "
583 << j.getForeignTableName() 583 << j.getForeignTableName()
@@ -592,55 +592,55 @@ namespace verbly {
592 << "." 592 << "."
593 << j.getJoinColumn(); 593 << j.getJoinColumn();
594 } 594 }
595 595
596 statement::condition::condition(const condition& other) 596 statement::condition::condition(const condition& other)
597 { 597 {
598 type_ = other.type_; 598 type_ = other.type_;
599 599
600 switch (type_) 600 switch (type_)
601 { 601 {
602 case type::empty: 602 case type::empty:
603 { 603 {
604 break; 604 break;
605 } 605 }
606 606
607 case type::singleton: 607 case type::singleton:
608 { 608 {
609 new(&singleton_.table_) std::string(other.singleton_.table_); 609 new(&singleton_.table_) std::string(other.singleton_.table_);
610 new(&singleton_.column_) std::string(other.singleton_.column_); 610 new(&singleton_.column_) std::string(other.singleton_.column_);
611 singleton_.comparison_ = other.singleton_.comparison_; 611 singleton_.comparison_ = other.singleton_.comparison_;
612 new(&singleton_.value_) binding(other.singleton_.value_); 612 new(&singleton_.value_) binding(other.singleton_.value_);
613 613
614 break; 614 break;
615 } 615 }
616 616
617 case type::group: 617 case type::group:
618 { 618 {
619 new(&group_.children_) std::list<condition>(other.group_.children_); 619 new(&group_.children_) std::list<condition>(other.group_.children_);
620 group_.orlogic_ = other.group_.orlogic_; 620 group_.orlogic_ = other.group_.orlogic_;
621 621
622 break; 622 break;
623 } 623 }
624 } 624 }
625 } 625 }
626 626
627 statement::condition::condition(condition&& other) : condition() 627 statement::condition::condition(condition&& other) : condition()
628 { 628 {
629 swap(*this, other); 629 swap(*this, other);
630 } 630 }
631 631
632 statement::condition& statement::condition::operator=(condition other) 632 statement::condition& statement::condition::operator=(condition other)
633 { 633 {
634 swap(*this, other); 634 swap(*this, other);
635 635
636 return *this; 636 return *this;
637 } 637 }
638 638
639 void swap(statement::condition& first, statement::condition& second) 639 void swap(statement::condition& first, statement::condition& second)
640 { 640 {
641 using type = statement::condition::type; 641 using type = statement::condition::type;
642 using condition = statement::condition; 642 using condition = statement::condition;
643 643
644 type tempType = first.type_; 644 type tempType = first.type_;
645 std::string tempTable; 645 std::string tempTable;
646 std::string tempColumn; 646 std::string tempColumn;
@@ -648,94 +648,94 @@ namespace verbly {
648 binding tempBinding; 648 binding tempBinding;
649 std::list<condition> tempChildren; 649 std::list<condition> tempChildren;
650 bool tempOrlogic; 650 bool tempOrlogic;
651 651
652 switch (tempType) 652 switch (tempType)
653 { 653 {
654 case type::empty: 654 case type::empty:
655 { 655 {
656 break; 656 break;
657 } 657 }
658 658
659 case type::singleton: 659 case type::singleton:
660 { 660 {
661 tempTable = std::move(first.singleton_.table_); 661 tempTable = std::move(first.singleton_.table_);
662 tempColumn = std::move(first.singleton_.column_); 662 tempColumn = std::move(first.singleton_.column_);
663 tempComparison = first.singleton_.comparison_; 663 tempComparison = first.singleton_.comparison_;
664 tempBinding = std::move(first.singleton_.value_); 664 tempBinding = std::move(first.singleton_.value_);
665 665
666 break; 666 break;
667 } 667 }
668 668
669 case type::group: 669 case type::group:
670 { 670 {
671 tempChildren = std::move(first.group_.children_); 671 tempChildren = std::move(first.group_.children_);
672 tempOrlogic = first.group_.orlogic_; 672 tempOrlogic = first.group_.orlogic_;
673 673
674 break; 674 break;
675 } 675 }
676 } 676 }
677 677
678 first.~condition(); 678 first.~condition();
679 679
680 first.type_ = second.type_; 680 first.type_ = second.type_;
681 681
682 switch (first.type_) 682 switch (first.type_)
683 { 683 {
684 case type::empty: 684 case type::empty:
685 { 685 {
686 break; 686 break;
687 } 687 }
688 688
689 case type::singleton: 689 case type::singleton:
690 { 690 {
691 new(&first.singleton_.table_) std::string(std::move(second.singleton_.table_)); 691 new(&first.singleton_.table_) std::string(std::move(second.singleton_.table_));
692 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_)); 692 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_));
693 first.singleton_.comparison_ = second.singleton_.comparison_; 693 first.singleton_.comparison_ = second.singleton_.comparison_;
694 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_)); 694 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_));
695 695
696 break; 696 break;
697 } 697 }
698 698
699 case type::group: 699 case type::group:
700 { 700 {
701 new(&first.group_.children_) std::list<condition>(std::move(second.group_.children_)); 701 new(&first.group_.children_) std::list<condition>(std::move(second.group_.children_));
702 first.group_.orlogic_ = second.group_.orlogic_; 702 first.group_.orlogic_ = second.group_.orlogic_;
703 703
704 break; 704 break;
705 } 705 }
706 } 706 }
707 707
708 second.~condition(); 708 second.~condition();
709 709
710 second.type_ = tempType; 710 second.type_ = tempType;
711 711
712 switch (second.type_) 712 switch (second.type_)
713 { 713 {
714 case type::empty: 714 case type::empty:
715 { 715 {
716 break; 716 break;
717 } 717 }
718 718
719 case type::singleton: 719 case type::singleton:
720 { 720 {
721 new(&second.singleton_.table_) std::string(std::move(tempTable)); 721 new(&second.singleton_.table_) std::string(std::move(tempTable));
722 new(&second.singleton_.column_) std::string(std::move(tempColumn)); 722 new(&second.singleton_.column_) std::string(std::move(tempColumn));
723 second.singleton_.comparison_ = tempComparison; 723 second.singleton_.comparison_ = tempComparison;
724 new(&second.singleton_.value_) binding(std::move(tempBinding)); 724 new(&second.singleton_.value_) binding(std::move(tempBinding));
725 725
726 break; 726 break;
727 } 727 }
728 728
729 case type::group: 729 case type::group:
730 { 730 {
731 new(&second.group_.children_) std::list<condition>(std::move(tempChildren)); 731 new(&second.group_.children_) std::list<condition>(std::move(tempChildren));
732 second.group_.orlogic_ = tempOrlogic; 732 second.group_.orlogic_ = tempOrlogic;
733 733
734 break; 734 break;
735 } 735 }
736 } 736 }
737 } 737 }
738 738
739 statement::condition::~condition() 739 statement::condition::~condition()
740 { 740 {
741 switch (type_) 741 switch (type_)
@@ -744,33 +744,33 @@ namespace verbly {
744 { 744 {
745 break; 745 break;
746 } 746 }
747 747
748 case type::singleton: 748 case type::singleton:
749 { 749 {
750 using string_type = std::string; 750 using string_type = std::string;
751 751
752 singleton_.table_.~string_type(); 752 singleton_.table_.~string_type();
753 singleton_.column_.~string_type(); 753 singleton_.column_.~string_type();
754 singleton_.value_.~binding(); 754 singleton_.value_.~binding();
755 755
756 break; 756 break;
757 } 757 }
758 758
759 case type::group: 759 case type::group:
760 { 760 {
761 using list_type = std::list<condition>; 761 using list_type = std::list<condition>;
762 762
763 group_.children_.~list_type(); 763 group_.children_.~list_type();
764 764
765 break; 765 break;
766 } 766 }
767 } 767 }
768 } 768 }
769 769
770 statement::condition::condition() : type_(type::empty) 770 statement::condition::condition() : type_(type::empty)
771 { 771 {
772 } 772 }
773 773
774 statement::condition::condition( 774 statement::condition::condition(
775 std::string table, 775 std::string table,
776 std::string column, 776 std::string column,
@@ -779,7 +779,7 @@ namespace verbly {
779 { 779 {
780 new(&singleton_.table_) std::string(std::move(table)); 780 new(&singleton_.table_) std::string(std::move(table));
781 new(&singleton_.column_) std::string(std::move(column)); 781 new(&singleton_.column_) std::string(std::move(column));
782 782
783 if (isNull) 783 if (isNull)
784 { 784 {
785 singleton_.comparison_ = comparison::is_null; 785 singleton_.comparison_ = comparison::is_null;
@@ -787,7 +787,7 @@ namespace verbly {
787 singleton_.comparison_ = comparison::is_not_null; 787 singleton_.comparison_ = comparison::is_not_null;
788 } 788 }
789 } 789 }
790 790
791 statement::condition::condition( 791 statement::condition::condition(
792 std::string table, 792 std::string table,
793 std::string column, 793 std::string column,
@@ -800,7 +800,7 @@ namespace verbly {
800 singleton_.comparison_ = comp; 800 singleton_.comparison_ = comp;
801 new(&singleton_.value_) binding(std::move(value)); 801 new(&singleton_.value_) binding(std::move(value));
802 } 802 }
803 803
804 std::string statement::condition::toSql() const 804 std::string statement::condition::toSql() const
805 { 805 {
806 switch (type_) 806 switch (type_)
@@ -809,7 +809,7 @@ namespace verbly {
809 { 809 {
810 return ""; 810 return "";
811 } 811 }
812 812
813 case type::singleton: 813 case type::singleton:
814 { 814 {
815 switch (singleton_.comparison_) 815 switch (singleton_.comparison_)
@@ -818,54 +818,54 @@ namespace verbly {
818 { 818 {
819 return singleton_.table_ + "." + singleton_.column_ + " = ?"; 819 return singleton_.table_ + "." + singleton_.column_ + " = ?";
820 } 820 }
821 821
822 case comparison::does_not_equal: 822 case comparison::does_not_equal:
823 { 823 {
824 return singleton_.table_ + "." + singleton_.column_ + " != ?"; 824 return singleton_.table_ + "." + singleton_.column_ + " != ?";
825 } 825 }
826 826
827 case comparison::is_greater_than: 827 case comparison::is_greater_than:
828 { 828 {
829 return singleton_.table_ + "." + singleton_.column_ + " > ?"; 829 return singleton_.table_ + "." + singleton_.column_ + " > ?";
830 } 830 }
831 831
832 case comparison::is_at_most: 832 case comparison::is_at_most:
833 { 833 {
834 return singleton_.table_ + "." + singleton_.column_ + " <= ?"; 834 return singleton_.table_ + "." + singleton_.column_ + " <= ?";
835 } 835 }
836 836
837 case comparison::is_less_than: 837 case comparison::is_less_than:
838 { 838 {
839 return singleton_.table_ + "." + singleton_.column_ + " < ?"; 839 return singleton_.table_ + "." + singleton_.column_ + " < ?";
840 } 840 }
841 841
842 case comparison::is_at_least: 842 case comparison::is_at_least:
843 { 843 {
844 return singleton_.table_ + "." + singleton_.column_ + " >= ?"; 844 return singleton_.table_ + "." + singleton_.column_ + " >= ?";
845 } 845 }
846 846
847 case comparison::is_like: 847 case comparison::is_like:
848 { 848 {
849 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; 849 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?";
850 } 850 }
851 851
852 case comparison::is_not_like: 852 case comparison::is_not_like:
853 { 853 {
854 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; 854 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?";
855 } 855 }
856 856
857 case comparison::is_not_null: 857 case comparison::is_not_null:
858 { 858 {
859 return singleton_.table_ + "." + singleton_.column_ + " IS NOT NULL"; 859 return singleton_.table_ + "." + singleton_.column_ + " IS NOT NULL";
860 } 860 }
861 861
862 case comparison::is_null: 862 case comparison::is_null:
863 { 863 {
864 return singleton_.table_ + "." + singleton_.column_ + " IS NULL"; 864 return singleton_.table_ + "." + singleton_.column_ + " IS NULL";
865 } 865 }
866 } 866 }
867 } 867 }
868 868
869 case type::group: 869 case type::group:
870 { 870 {
871 std::list<std::string> clauses; 871 std::list<std::string> clauses;
@@ -873,12 +873,12 @@ namespace verbly {
873 { 873 {
874 clauses.push_back(cond.toSql()); 874 clauses.push_back(cond.toSql());
875 } 875 }
876 876
877 return implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 877 return implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND ");
878 } 878 }
879 } 879 }
880 } 880 }
881 881
882 std::list<binding> statement::condition::flattenBindings() const 882 std::list<binding> statement::condition::flattenBindings() const
883 { 883 {
884 switch (type_) 884 switch (type_)
@@ -887,7 +887,7 @@ namespace verbly {
887 { 887 {
888 return {}; 888 return {};
889 } 889 }
890 890
891 case type::singleton: 891 case type::singleton:
892 { 892 {
893 switch (singleton_.comparison_) 893 switch (singleton_.comparison_)
@@ -903,7 +903,7 @@ namespace verbly {
903 { 903 {
904 return {singleton_.value_}; 904 return {singleton_.value_};
905 } 905 }
906 906
907 case comparison::is_not_null: 907 case comparison::is_not_null:
908 case comparison::is_null: 908 case comparison::is_null:
909 { 909 {
@@ -911,7 +911,7 @@ namespace verbly {
911 } 911 }
912 } 912 }
913 } 913 }
914 914
915 case type::group: 915 case type::group:
916 { 916 {
917 std::list<binding> bindings; 917 std::list<binding> bindings;
@@ -922,30 +922,30 @@ namespace verbly {
922 bindings.push_back(std::move(value)); 922 bindings.push_back(std::move(value));
923 } 923 }
924 } 924 }
925 925
926 return bindings; 926 return bindings;
927 } 927 }
928 } 928 }
929 } 929 }
930 930
931 statement::condition::condition(bool orlogic) : type_(type::group) 931 statement::condition::condition(bool orlogic) : type_(type::group)
932 { 932 {
933 new(&group_.children_) std::list<condition>(); 933 new(&group_.children_) std::list<condition>();
934 group_.orlogic_ = orlogic; 934 group_.orlogic_ = orlogic;
935 } 935 }
936 936
937 statement::condition& statement::condition::operator+=(condition n) 937 statement::condition& statement::condition::operator+=(condition n)
938 { 938 {
939 if (type_ == type::group) 939 if (type_ == type::group)
940 { 940 {
941 group_.children_.push_back(std::move(n)); 941 group_.children_.push_back(std::move(n));
942 942
943 return *this; 943 return *this;
944 } else { 944 } else {
945 throw std::domain_error("Cannot add condition to non-group condition"); 945 throw std::domain_error("Cannot add condition to non-group condition");
946 } 946 }
947 } 947 }
948 948
949 statement::condition& statement::condition::operator&=(condition n) 949 statement::condition& statement::condition::operator&=(condition n)
950 { 950 {
951 switch (type_) 951 switch (type_)
@@ -953,32 +953,32 @@ namespace verbly {
953 case type::empty: 953 case type::empty:
954 { 954 {
955 *this = std::move(n); 955 *this = std::move(n);
956 956
957 break; 957 break;
958 } 958 }
959 959
960 case type::singleton: 960 case type::singleton:
961 { 961 {
962 condition grp(false); 962 condition grp(false);
963 grp += *this; 963 grp += *this;
964 grp += std::move(n); 964 grp += std::move(n);
965 965
966 *this = grp; 966 *this = grp;
967 967
968 break; 968 break;
969 } 969 }
970 970
971 case type::group: 971 case type::group:
972 { 972 {
973 *this += std::move(n); 973 *this += std::move(n);
974 974
975 break; 975 break;
976 } 976 }
977 } 977 }
978 978
979 return *this; 979 return *this;
980 } 980 }
981 981
982 const std::list<statement::condition>& statement::condition::getChildren() const 982 const std::list<statement::condition>& statement::condition::getChildren() const
983 { 983 {
984 if (type_ == type::group) 984 if (type_ == type::group)
@@ -988,5 +988,5 @@ namespace verbly {
988 throw std::domain_error("Cannot get children of non-group condition"); 988 throw std::domain_error("Cannot get children of non-group condition");
989 } 989 }
990 } 990 }
991 991
992}; 992};