summary refs log tree commit diff stats
path: root/lib/statement.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-02-06 20:58:37 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-02-06 20:58:37 -0500
commitf1f67e62cebb4144f0599196263cd93b41fa972e (patch)
tree5d0f8fceeec63f47fb85846f5568bcb36f4a975f /lib/statement.cpp
parent6cc23ba387d0813b801ba094709673a61bac889c (diff)
downloadverbly-f1f67e62cebb4144f0599196263cd93b41fa972e.tar.gz
verbly-f1f67e62cebb4144f0599196263cd93b41fa972e.tar.bz2
verbly-f1f67e62cebb4144f0599196263cd93b41fa972e.zip
Made pronunciation::rhymes join dynamic
This involved adding a new type of filter; one that compares (currently
only equality and inequality) a field with another field located in an
enclosing join context.

In the process, it was discovered that simplifying the lemma::forms join
field earlier actually made some queries return inaccurate results
because the inflection of the form was being ignored and anything in the
lemma would be used because of the inner join. Because the existing
condition join did not allow for the condition field to be on the from
side of the join, two things were done: a condition version of
joinThrough was made, and lemma was finally eliminated as a top-level
object, replaced instead with a condition join between word and form
through lemmas_forms.

Queries are also now grouped by the first select field (assumed to be
the primary ID) of the top table, in order to eliminate duplicates
created by inner joins, so that there is a uniform distribution between
results for random queries.

Created a database index on pronunciations(rhyme) which decreases query
time for rhyming filters. The new database version is
backwards-compatible because no data or structure changed.
Diffstat (limited to 'lib/statement.cpp')
-rw-r--r--lib/statement.cpp232
1 files changed, 180 insertions, 52 deletions
diff --git a/lib/statement.cpp b/lib/statement.cpp index 562eef2..b892cab 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp
@@ -3,13 +3,6 @@
3#include <utility> 3#include <utility>
4#include "filter.h" 4#include "filter.h"
5#include "util.h" 5#include "util.h"
6#include "notion.h"
7#include "word.h"
8#include "frame.h"
9#include "part.h"
10#include "lemma.h"
11#include "form.h"
12#include "pronunciation.h"
13#include "order.h" 6#include "order.h"
14 7
15namespace verbly { 8namespace verbly {
@@ -17,7 +10,7 @@ namespace verbly {
17 statement::statement( 10 statement::statement(
18 object context, 11 object context,
19 filter queryFilter) : 12 filter queryFilter) :
20 statement(getTableForContext(context), queryFilter.compact().normalize(context)) 13 statement(context, getTableForContext(context), queryFilter.compact().normalize(context))
21 { 14 {
22 } 15 }
23 16
@@ -104,24 +97,29 @@ namespace verbly {
104 queryStream << " WHERE "; 97 queryStream << " WHERE ";
105 queryStream << topCondition_.flatten().toSql(true, debug); 98 queryStream << topCondition_.flatten().toSql(true, debug);
106 } 99 }
107 100
101 queryStream << " GROUP BY ";
102 queryStream << topTable_;
103 queryStream << ".";
104 queryStream << select.front();
105
108 queryStream << " ORDER BY "; 106 queryStream << " ORDER BY ";
109 107
110 switch (sortOrder.getType()) 108 switch (sortOrder.getType())
111 { 109 {
112 case order::type::random: 110 case order::type::random:
113 { 111 {
114 queryStream << "RANDOM()"; 112 queryStream << "RANDOM()";
115 113
116 break; 114 break;
117 } 115 }
118 116
119 case order::type::field: 117 case order::type::field:
120 { 118 {
121 queryStream << topTable_; 119 queryStream << topTable_;
122 queryStream << "."; 120 queryStream << ".";
123 queryStream << sortOrder.getSortField().getColumn(); 121 queryStream << sortOrder.getSortField().getColumn();
124 122
125 break; 123 break;
126 } 124 }
127 } 125 }
@@ -156,10 +154,12 @@ namespace verbly {
156 } 154 }
157 155
158 statement::statement( 156 statement::statement(
157 object context,
159 std::string tableName, 158 std::string tableName,
160 filter clause, 159 filter clause,
161 int nextTableId, 160 int nextTableId,
162 int nextWithId) : 161 int nextWithId) :
162 context_(context),
163 nextTableId_(nextTableId), 163 nextTableId_(nextTableId),
164 nextWithId_(nextWithId), 164 nextWithId_(nextWithId),
165 topTable_(instantiateTable(std::move(tableName))), 165 topTable_(instantiateTable(std::move(tableName))),
@@ -266,6 +266,16 @@ namespace verbly {
266 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument()); 266 return condition(topTable_, clause.getField().getColumn(), condition::comparison::is_not_like, clause.getStringArgument());
267 } 267 }
268 268
269 case filter::comparison::field_equals:
270 {
271 return condition(topTable_, clause.getField().getColumn(), condition::comparison::equals, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject());
272 }
273
274 case filter::comparison::field_does_not_equal:
275 {
276 return condition(topTable_, clause.getField().getColumn(), condition::comparison::does_not_equal, {"", clause.getCompareField().getColumn()}, clause.getCompareField().getObject());
277 }
278
269 case filter::comparison::matches: 279 case filter::comparison::matches:
270 case filter::comparison::does_not_match: 280 case filter::comparison::does_not_match:
271 case filter::comparison::hierarchally_matches: 281 case filter::comparison::hierarchally_matches:
@@ -281,26 +291,29 @@ namespace verbly {
281 { 291 {
282 // First, figure out what table we need to join against. 292 // First, figure out what table we need to join against.
283 std::string joinTableName; 293 std::string joinTableName;
294 object joinContext = object::undefined;
284 if (clause.getField().hasTable()) 295 if (clause.getField().hasTable())
285 { 296 {
286 joinTableName = clause.getField().getTable(); 297 joinTableName = clause.getField().getTable();
287 } else { 298 } else {
288 joinTableName = getTableForContext(clause.getField().getJoinObject()); 299 joinContext = clause.getField().getJoinObject();
300 joinTableName = getTableForContext(joinContext);
289 } 301 }
290 302
291 filter joinCondition = clause.getJoinCondition(); 303 filter joinCondition = clause.getJoinCondition();
292 304
293 // If this is a condition join, we need to add the field join 305 // If this is a condition join, we need to add the field join
294 // condition to the clause. 306 // condition to the clause.
295 if (clause.getField().getType() == field::type::join_where) 307 if (clause.getField().getType() == field::type::join_where)
296 { 308 {
297 joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue()); 309 joinCondition &= (field::integerField(joinTableName.c_str(), clause.getField().getConditionColumn()) == clause.getField().getConditionValue());
298 } 310 }
299 311
300 // Recursively parse the subquery, and therefore obtain an 312 // Recursively parse the subquery, and therefore obtain an
301 // instantiated table to join against, as well as any joins or CTEs 313 // instantiated table to join against, as well as any joins or CTEs
302 // that the subquery may require to function. 314 // that the subquery may require to function.
303 statement joinStmt( 315 statement joinStmt(
316 joinContext,
304 joinTableName, 317 joinTableName,
305 std::move(joinCondition).normalize(clause.getField().getJoinObject()), 318 std::move(joinCondition).normalize(clause.getField().getJoinObject()),
306 nextTableId_, 319 nextTableId_,
@@ -368,11 +381,13 @@ namespace verbly {
368 } 381 }
369 382
370 case field::type::join_through: 383 case field::type::join_through:
384 case field::type::join_through_where:
371 { 385 {
372 // Recursively parse the subquery, and therefore obtain an 386 // Recursively parse the subquery, and therefore obtain an
373 // instantiated table to join against, as well as any joins or CTEs 387 // instantiated table to join against, as well as any joins or CTEs
374 // that the subquery may require to function. 388 // that the subquery may require to function.
375 statement joinStmt( 389 statement joinStmt(
390 clause.getField().getJoinObject(),
376 getTableForContext(clause.getField().getJoinObject()), 391 getTableForContext(clause.getField().getJoinObject()),
377 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 392 clause.getJoinCondition().normalize(clause.getField().getJoinObject()),
378 nextTableId_, 393 nextTableId_,
@@ -424,6 +439,17 @@ namespace verbly {
424 std::list<join> cteJoins = std::move(joinStmt.joins_); 439 std::list<join> cteJoins = std::move(joinStmt.joins_);
425 condition cteCondition = integrate(std::move(joinStmt), true); 440 condition cteCondition = integrate(std::move(joinStmt), true);
426 441
442 // If this is a condition join, add the condition.
443 if (clause.getField().getType() == field::type::join_through_where)
444 {
445 cteCondition &=
446 condition(
447 throughTable,
448 clause.getField().getConditionColumn(),
449 condition::comparison::equals,
450 clause.getField().getConditionValue());
451 }
452
427 withs_.emplace_back( 453 withs_.emplace_back(
428 std::move(withName), 454 std::move(withName),
429 clause.getField(), 455 clause.getField(),
@@ -453,7 +479,7 @@ namespace verbly {
453 joins_.emplace_back( 479 joins_.emplace_back(
454 false, 480 false,
455 getTableForContext(clause.getField().getJoinObject()), 481 getTableForContext(clause.getField().getJoinObject()),
456 std::move(throughTable), 482 throughTable,
457 clause.getField().getForeignJoinColumn(), 483 clause.getField().getForeignJoinColumn(),
458 std::move(joinTable), 484 std::move(joinTable),
459 clause.getField().getForeignColumn()); 485 clause.getField().getForeignColumn());
@@ -461,7 +487,20 @@ namespace verbly {
461 // Integrate the subquery's table mappings, joins, and CTEs into 487 // Integrate the subquery's table mappings, joins, and CTEs into
462 // this statement, and return the subquery condition as our 488 // this statement, and return the subquery condition as our
463 // condition. 489 // condition.
464 return integrate(std::move(joinStmt)); 490 condition resultCond = integrate(std::move(joinStmt));
491
492 // If this is a condition join, add the condition.
493 if (clause.getField().getType() == field::type::join_through_where)
494 {
495 resultCond &=
496 condition(
497 throughTable,
498 clause.getField().getConditionColumn(),
499 condition::comparison::equals,
500 clause.getField().getConditionValue());
501 }
502
503 return std::move(resultCond);
465 } 504 }
466 } 505 }
467 506
@@ -491,6 +530,7 @@ namespace verbly {
491 530
492 // Recursively parse the subquery in order to create the CTE. 531 // Recursively parse the subquery in order to create the CTE.
493 statement withStmt( 532 statement withStmt(
533 clause.getField().getObject(),
494 getTableForContext(clause.getField().getObject()), 534 getTableForContext(clause.getField().getObject()),
495 clause.getJoinCondition().normalize(clause.getField().getObject()), 535 clause.getJoinCondition().normalize(clause.getField().getObject()),
496 nextTableId_, 536 nextTableId_,
@@ -593,7 +633,7 @@ namespace verbly {
593 nextTableId_ = subStmt.nextTableId_; 633 nextTableId_ = subStmt.nextTableId_;
594 nextWithId_ = subStmt.nextWithId_; 634 nextWithId_ = subStmt.nextWithId_;
595 635
596 return subStmt.topCondition_; 636 return subStmt.topCondition_.resolveCompareFields(context_, topTable_);
597 } 637 }
598 638
599 std::ostream& operator<<(std::ostream& oss, const statement::join& j) 639 std::ostream& operator<<(std::ostream& oss, const statement::join& j)
@@ -637,6 +677,7 @@ namespace verbly {
637 new(&singleton_.column_) std::string(other.singleton_.column_); 677 new(&singleton_.column_) std::string(other.singleton_.column_);
638 singleton_.comparison_ = other.singleton_.comparison_; 678 singleton_.comparison_ = other.singleton_.comparison_;
639 new(&singleton_.value_) binding(other.singleton_.value_); 679 new(&singleton_.value_) binding(other.singleton_.value_);
680 singleton_.parentObject_ = other.singleton_.parentObject_;
640 681
641 break; 682 break;
642 } 683 }
@@ -673,6 +714,7 @@ namespace verbly {
673 std::string tempColumn; 714 std::string tempColumn;
674 condition::comparison tempComparison; 715 condition::comparison tempComparison;
675 binding tempBinding; 716 binding tempBinding;
717 object tempParentObject;
676 std::list<condition> tempChildren; 718 std::list<condition> tempChildren;
677 bool tempOrlogic; 719 bool tempOrlogic;
678 720
@@ -689,6 +731,7 @@ namespace verbly {
689 tempColumn = std::move(first.singleton_.column_); 731 tempColumn = std::move(first.singleton_.column_);
690 tempComparison = first.singleton_.comparison_; 732 tempComparison = first.singleton_.comparison_;
691 tempBinding = std::move(first.singleton_.value_); 733 tempBinding = std::move(first.singleton_.value_);
734 tempParentObject = first.singleton_.parentObject_;
692 735
693 break; 736 break;
694 } 737 }
@@ -719,6 +762,7 @@ namespace verbly {
719 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_)); 762 new(&first.singleton_.column_) std::string(std::move(second.singleton_.column_));
720 first.singleton_.comparison_ = second.singleton_.comparison_; 763 first.singleton_.comparison_ = second.singleton_.comparison_;
721 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_)); 764 new(&first.singleton_.value_) binding(std::move(second.singleton_.value_));
765 first.singleton_.parentObject_ = second.singleton_.parentObject_;
722 766
723 break; 767 break;
724 } 768 }
@@ -749,6 +793,7 @@ namespace verbly {
749 new(&second.singleton_.column_) std::string(std::move(tempColumn)); 793 new(&second.singleton_.column_) std::string(std::move(tempColumn));
750 second.singleton_.comparison_ = tempComparison; 794 second.singleton_.comparison_ = tempComparison;
751 new(&second.singleton_.value_) binding(std::move(tempBinding)); 795 new(&second.singleton_.value_) binding(std::move(tempBinding));
796 second.singleton_.parentObject_ = tempParentObject;
752 797
753 break; 798 break;
754 } 799 }
@@ -813,19 +858,23 @@ namespace verbly {
813 } else { 858 } else {
814 singleton_.comparison_ = comparison::is_not_null; 859 singleton_.comparison_ = comparison::is_not_null;
815 } 860 }
861
862 singleton_.parentObject_ = object::undefined;
816 } 863 }
817 864
818 statement::condition::condition( 865 statement::condition::condition(
819 std::string table, 866 std::string table,
820 std::string column, 867 std::string column,
821 comparison comp, 868 comparison comp,
822 binding value) : 869 binding value,
870 object parentObject) :
823 type_(type::singleton) 871 type_(type::singleton)
824 { 872 {
825 new(&singleton_.table_) std::string(std::move(table)); 873 new(&singleton_.table_) std::string(std::move(table));
826 new(&singleton_.column_) std::string(std::move(column)); 874 new(&singleton_.column_) std::string(std::move(column));
827 singleton_.comparison_ = comp; 875 singleton_.comparison_ = comp;
828 new(&singleton_.value_) binding(std::move(value)); 876 new(&singleton_.value_) binding(std::move(value));
877 singleton_.parentObject_ = parentObject;
829 } 878 }
830 879
831 std::string statement::condition::toSql(bool toplevel, bool debug) const 880 std::string statement::condition::toSql(bool toplevel, bool debug) const
@@ -845,14 +894,35 @@ namespace verbly {
845 { 894 {
846 if (debug) 895 if (debug)
847 { 896 {
848 if (singleton_.value_.getType() == binding::type::string) 897 switch (singleton_.value_.getType())
849 { 898 {
850 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\""; 899 case binding::type::string:
851 } else { 900 {
852 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger()); 901 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\"";
902 }
903
904 case binding::type::integer:
905 {
906 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger());
907 }
908
909 case binding::type::field:
910 {
911 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
912 }
913
914 case binding::type::invalid:
915 {
916 throw std::logic_error("Invalid binding in statement generation");
917 }
853 } 918 }
854 } else { 919 } else {
855 return singleton_.table_ + "." + singleton_.column_ + " = ?"; 920 if (singleton_.value_.getType() == binding::type::field)
921 {
922 return singleton_.table_ + "." + singleton_.column_ + " = " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
923 } else {
924 return singleton_.table_ + "." + singleton_.column_ + " = ?";
925 }
856 } 926 }
857 } 927 }
858 928
@@ -860,14 +930,35 @@ namespace verbly {
860 { 930 {
861 if (debug) 931 if (debug)
862 { 932 {
863 if (singleton_.value_.getType() == binding::type::string) 933 switch (singleton_.value_.getType())
864 { 934 {
865 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\""; 935 case binding::type::string:
866 } else { 936 {
867 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger()); 937 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\"";
938 }
939
940 case binding::type::integer:
941 {
942 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger());
943 }
944
945 case binding::type::field:
946 {
947 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
948 }
949
950 case binding::type::invalid:
951 {
952 throw std::logic_error("Invalid binding in statement generation");
953 }
868 } 954 }
869 } else { 955 } else {
870 return singleton_.table_ + "." + singleton_.column_ + " != ?"; 956 if (singleton_.value_.getType() == binding::type::field)
957 {
958 return singleton_.table_ + "." + singleton_.column_ + " != " + singleton_.value_.getTable() + "." + singleton_.value_.getColumn();
959 } else {
960 return singleton_.table_ + "." + singleton_.column_ + " != ?";
961 }
871 } 962 }
872 } 963 }
873 964
@@ -959,7 +1050,7 @@ namespace verbly {
959 return clauses.front(); 1050 return clauses.front();
960 } else { 1051 } else {
961 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 1052 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND ");
962 1053
963 if (toplevel) 1054 if (toplevel)
964 { 1055 {
965 return result; 1056 return result;
@@ -982,24 +1073,29 @@ namespace verbly {
982 1073
983 case type::singleton: 1074 case type::singleton:
984 { 1075 {
985 switch (singleton_.comparison_) 1076 if (singleton_.value_.getType() == binding::type::field)
986 { 1077 {
987 case comparison::equals: 1078 return {};
988 case comparison::does_not_equal: 1079 } else {
989 case comparison::is_greater_than: 1080 switch (singleton_.comparison_)
990 case comparison::is_at_most:
991 case comparison::is_less_than:
992 case comparison::is_at_least:
993 case comparison::is_like:
994 case comparison::is_not_like:
995 { 1081 {
996 return {singleton_.value_}; 1082 case comparison::equals:
997 } 1083 case comparison::does_not_equal:
1084 case comparison::is_greater_than:
1085 case comparison::is_at_most:
1086 case comparison::is_less_than:
1087 case comparison::is_at_least:
1088 case comparison::is_like:
1089 case comparison::is_not_like:
1090 {
1091 return {singleton_.value_};
1092 }
998 1093
999 case comparison::is_not_null: 1094 case comparison::is_not_null:
1000 case comparison::is_null: 1095 case comparison::is_null:
1001 { 1096 {
1002 return {}; 1097 return {};
1098 }
1003 } 1099 }
1004 } 1100 }
1005 } 1101 }
@@ -1080,7 +1176,7 @@ namespace verbly {
1080 throw std::domain_error("Cannot get children of non-group condition"); 1176 throw std::domain_error("Cannot get children of non-group condition");
1081 } 1177 }
1082 } 1178 }
1083 1179
1084 statement::condition statement::condition::flatten() const 1180 statement::condition statement::condition::flatten() const
1085 { 1181 {
1086 switch (type_) 1182 switch (type_)
@@ -1090,15 +1186,15 @@ namespace verbly {
1090 { 1186 {
1091 return *this; 1187 return *this;
1092 } 1188 }
1093 1189
1094 case type::group: 1190 case type::group:
1095 { 1191 {
1096 condition result(group_.orlogic_); 1192 condition result(group_.orlogic_);
1097 1193
1098 for (const condition& child : group_.children_) 1194 for (const condition& child : group_.children_)
1099 { 1195 {
1100 condition newChild = child.flatten(); 1196 condition newChild = child.flatten();
1101 1197
1102 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_)) 1198 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_))
1103 { 1199 {
1104 for (condition subChild : std::move(newChild.group_.children_)) 1200 for (condition subChild : std::move(newChild.group_.children_))
@@ -1109,7 +1205,39 @@ namespace verbly {
1109 result += std::move(newChild); 1205 result += std::move(newChild);
1110 } 1206 }
1111 } 1207 }
1112 1208
1209 return result;
1210 }
1211 }
1212 }
1213
1214 statement::condition statement::condition::resolveCompareFields(object context, std::string tableName) const
1215 {
1216 switch (type_)
1217 {
1218 case type::empty:
1219 {
1220 return *this;
1221 }
1222
1223 case type::singleton:
1224 {
1225 if ((singleton_.parentObject_ != object::undefined) && (singleton_.parentObject_ == context))
1226 {
1227 return condition(singleton_.table_, singleton_.column_, singleton_.comparison_, {tableName, singleton_.value_.getColumn()});
1228 } else {
1229 return *this;
1230 }
1231 }
1232
1233 case type::group:
1234 {
1235 condition result(group_.orlogic_);
1236 for (const condition& cond : group_.children_)
1237 {
1238 result += cond.resolveCompareFields(context, tableName);
1239 }
1240
1113 return result; 1241 return result;
1114 } 1242 }
1115 } 1243 }