summary refs log tree commit diff stats
path: root/lib/statement.cpp
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2017-01-28 12:59:42 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2017-01-28 12:59:42 -0500
commita7645346293ed6a912c26d0c50b6f7943f1f3072 (patch)
treed4d144e03a5e2dfcebbad2692fa71e790719d8fd /lib/statement.cpp
parent6ba8989bbbd497f949a3e8b17abed1d0bd048347 (diff)
downloadverbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.tar.gz
verbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.tar.bz2
verbly-a7645346293ed6a912c26d0c50b6f7943f1f3072.zip
Restructured verb frame schema to be more queryable
Groups are much less significant now, and they no longer have a database
table, nor are they considered a top level object anymore. Instead of
containing their own role data, that data is folded into the frames so
that it's easier to query; as a result, each group has its own copy of
the frames that it contains. Additionally, parts are considered top
level objects now, and you can query for frames based on attributes of
their indexed parts. Synrestrs are also contained in their own table
now, so that parts can be filtered against their synrestrs; they are
however not considered top level objects.

Created a new type of field, the "join where" or "condition join" field,
which is a normal join field that has a built in condition on a
specified field. This is used to allow creating multiple distinct join
fields from one object to another. This is required for the lemma::form
and frame::part joins, because filters for forms of separate inflections
should not be coalesced; similarly, filters on differently indexed frame
parts should not be coalesced.

Queries can now be ordered, ascending or descending, by a field, in
addition to randomly as before. This is necessary for accessing the
parts of a verb frame in the correct order, but may be useful to an end
user as well.

Fixed a bug with statement generation in that condition groups were not
being surrounded in parentheses, which made mixing OR groups and AND
groups generate inaccurate statements. This has been fixed;
additionally, parentheses are not placed around the top level condition,
and nested condition groups with the same logic type are coalesced, to
make query strings as easy to read as possible.

Also simplified the form::lemma field; it no longer conditions on the
inflection of the form like the lemma::form field does.

Also added a debug flag to statement::getQueryString that makes it
return a query string with all of the bindings filled in, for debug use
only.
Diffstat (limited to 'lib/statement.cpp')
-rw-r--r--lib/statement.cpp164
1 files changed, 145 insertions, 19 deletions
diff --git a/lib/statement.cpp b/lib/statement.cpp index 846b9de..1512aa5 100644 --- a/lib/statement.cpp +++ b/lib/statement.cpp
@@ -5,11 +5,12 @@
5#include "util.h" 5#include "util.h"
6#include "notion.h" 6#include "notion.h"
7#include "word.h" 7#include "word.h"
8#include "group.h"
9#include "frame.h" 8#include "frame.h"
9#include "part.h"
10#include "lemma.h" 10#include "lemma.h"
11#include "form.h" 11#include "form.h"
12#include "pronunciation.h" 12#include "pronunciation.h"
13#include "order.h"
13 14
14namespace verbly { 15namespace verbly {
15 16
@@ -20,7 +21,7 @@ namespace verbly {
20 { 21 {
21 } 22 }
22 23
23 std::string statement::getQueryString(std::list<std::string> select, bool random, int limit) const 24 std::string statement::getQueryString(std::list<std::string> select, order sortOrder, int limit, bool debug) const
24 { 25 {
25 std::stringstream queryStream; 26 std::stringstream queryStream;
26 27
@@ -49,7 +50,7 @@ namespace verbly {
49 if (cte.getCondition().getType() != condition::type::empty) 50 if (cte.getCondition().getType() != condition::type::empty)
50 { 51 {
51 cteStream << " WHERE "; 52 cteStream << " WHERE ";
52 cteStream << cte.getCondition().toSql(); 53 cteStream << cte.getCondition().flatten().toSql(true, debug);
53 } 54 }
54 55
55 if (cte.isRecursive()) 56 if (cte.isRecursive())
@@ -101,12 +102,28 @@ namespace verbly {
101 if (topCondition_.getType() != condition::type::empty) 102 if (topCondition_.getType() != condition::type::empty)
102 { 103 {
103 queryStream << " WHERE "; 104 queryStream << " WHERE ";
104 queryStream << topCondition_.toSql(); 105 queryStream << topCondition_.flatten().toSql(true, debug);
105 } 106 }
106 107
107 if (random) 108 queryStream << " ORDER BY ";
109
110 switch (sortOrder.getType())
108 { 111 {
109 queryStream << " ORDER BY RANDOM()"; 112 case order::type::random:
113 {
114 queryStream << "RANDOM()";
115
116 break;
117 }
118
119 case order::type::field:
120 {
121 queryStream << topTable_;
122 queryStream << ".";
123 queryStream << sortOrder.getSortField().getColumn();
124
125 break;
126 }
110 } 127 }
111 128
112 if (limit > 0) 129 if (limit > 0)
@@ -260,6 +277,7 @@ namespace verbly {
260 } 277 }
261 278
262 case field::type::join: 279 case field::type::join:
280 case field::type::join_where:
263 { 281 {
264 // First, figure out what table we need to join against. 282 // First, figure out what table we need to join against.
265 std::string joinTableName; 283 std::string joinTableName;
@@ -269,13 +287,22 @@ namespace verbly {
269 } else { 287 } else {
270 joinTableName = getTableForContext(clause.getField().getJoinObject()); 288 joinTableName = getTableForContext(clause.getField().getJoinObject());
271 } 289 }
290
291 filter joinCondition = clause.getJoinCondition();
292
293 // If this is a condition join, we need to add the field join
294 // condition to the clause.
295 if (clause.getField().getType() == field::type::join_where)
296 {
297 joinCondition &= (clause.getField().getConditionField() == clause.getField().getConditionValue());
298 }
272 299
273 // Recursively parse the subquery, and therefore obtain an 300 // Recursively parse the subquery, and therefore obtain an
274 // instantiated table to join against, as well as any joins or CTEs 301 // instantiated table to join against, as well as any joins or CTEs
275 // that the subquery may require to function. 302 // that the subquery may require to function.
276 statement joinStmt( 303 statement joinStmt(
277 joinTableName, 304 joinTableName,
278 clause.getJoinCondition().normalize(clause.getField().getJoinObject()), 305 std::move(joinCondition).normalize(clause.getField().getJoinObject()),
279 nextTableId_, 306 nextTableId_,
280 nextWithId_); 307 nextWithId_);
281 308
@@ -801,7 +828,7 @@ namespace verbly {
801 new(&singleton_.value_) binding(std::move(value)); 828 new(&singleton_.value_) binding(std::move(value));
802 } 829 }
803 830
804 std::string statement::condition::toSql() const 831 std::string statement::condition::toSql(bool toplevel, bool debug) const
805 { 832 {
806 switch (type_) 833 switch (type_)
807 { 834 {
@@ -816,42 +843,92 @@ namespace verbly {
816 { 843 {
817 case comparison::equals: 844 case comparison::equals:
818 { 845 {
819 return singleton_.table_ + "." + singleton_.column_ + " = ?"; 846 if (debug)
847 {
848 if (singleton_.value_.getType() == binding::type::string)
849 {
850 return singleton_.table_ + "." + singleton_.column_ + " = \"" + singleton_.value_.getString() + "\"";
851 } else {
852 return singleton_.table_ + "." + singleton_.column_ + " = " + std::to_string(singleton_.value_.getInteger());
853 }
854 } else {
855 return singleton_.table_ + "." + singleton_.column_ + " = ?";
856 }
820 } 857 }
821 858
822 case comparison::does_not_equal: 859 case comparison::does_not_equal:
823 { 860 {
824 return singleton_.table_ + "." + singleton_.column_ + " != ?"; 861 if (debug)
862 {
863 if (singleton_.value_.getType() == binding::type::string)
864 {
865 return singleton_.table_ + "." + singleton_.column_ + " != \"" + singleton_.value_.getString() + "\"";
866 } else {
867 return singleton_.table_ + "." + singleton_.column_ + " != " + std::to_string(singleton_.value_.getInteger());
868 }
869 } else {
870 return singleton_.table_ + "." + singleton_.column_ + " != ?";
871 }
825 } 872 }
826 873
827 case comparison::is_greater_than: 874 case comparison::is_greater_than:
828 { 875 {
829 return singleton_.table_ + "." + singleton_.column_ + " > ?"; 876 if (debug)
877 {
878 return singleton_.table_ + "." + singleton_.column_ + " > " + std::to_string(singleton_.value_.getInteger());
879 } else {
880 return singleton_.table_ + "." + singleton_.column_ + " > ?";
881 }
830 } 882 }
831 883
832 case comparison::is_at_most: 884 case comparison::is_at_most:
833 { 885 {
834 return singleton_.table_ + "." + singleton_.column_ + " <= ?"; 886 if (debug)
887 {
888 return singleton_.table_ + "." + singleton_.column_ + " <= " + std::to_string(singleton_.value_.getInteger());
889 } else {
890 return singleton_.table_ + "." + singleton_.column_ + " <= ?";
891 }
835 } 892 }
836 893
837 case comparison::is_less_than: 894 case comparison::is_less_than:
838 { 895 {
839 return singleton_.table_ + "." + singleton_.column_ + " < ?"; 896 if (debug)
897 {
898 return singleton_.table_ + "." + singleton_.column_ + " < " + std::to_string(singleton_.value_.getInteger());
899 } else {
900 return singleton_.table_ + "." + singleton_.column_ + " < ?";
901 }
840 } 902 }
841 903
842 case comparison::is_at_least: 904 case comparison::is_at_least:
843 { 905 {
844 return singleton_.table_ + "." + singleton_.column_ + " >= ?"; 906 if (debug)
907 {
908 return singleton_.table_ + "." + singleton_.column_ + " >= " + std::to_string(singleton_.value_.getInteger());
909 } else {
910 return singleton_.table_ + "." + singleton_.column_ + " >= ?";
911 }
845 } 912 }
846 913
847 case comparison::is_like: 914 case comparison::is_like:
848 { 915 {
849 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?"; 916 if (debug)
917 {
918 return singleton_.table_ + "." + singleton_.column_ + " LIKE \"" + singleton_.value_.getString() + "\"";
919 } else {
920 return singleton_.table_ + "." + singleton_.column_ + " LIKE ?";
921 }
850 } 922 }
851 923
852 case comparison::is_not_like: 924 case comparison::is_not_like:
853 { 925 {
854 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?"; 926 if (debug)
927 {
928 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE \"" + singleton_.value_.getString() + "\"";
929 } else {
930 return singleton_.table_ + "." + singleton_.column_ + " NOT LIKE ?";
931 }
855 } 932 }
856 933
857 case comparison::is_not_null: 934 case comparison::is_not_null:
@@ -871,10 +948,25 @@ namespace verbly {
871 std::list<std::string> clauses; 948 std::list<std::string> clauses;
872 for (const condition& cond : group_.children_) 949 for (const condition& cond : group_.children_)
873 { 950 {
874 clauses.push_back(cond.toSql()); 951 clauses.push_back(cond.toSql(false, debug));
875 } 952 }
876 953
877 return implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND "); 954 if (clauses.empty())
955 {
956 return "";
957 } else if (clauses.size() == 1)
958 {
959 return clauses.front();
960 } else {
961 std::string result = implode(std::begin(clauses), std::end(clauses), group_.orlogic_ ? " OR " : " AND ");
962
963 if (toplevel)
964 {
965 return result;
966 } else {
967 return "(" + result + ")";
968 }
969 }
878 } 970 }
879 } 971 }
880 } 972 }
@@ -988,5 +1080,39 @@ namespace verbly {
988 throw std::domain_error("Cannot get children of non-group condition"); 1080 throw std::domain_error("Cannot get children of non-group condition");
989 } 1081 }
990 } 1082 }
1083
1084 statement::condition statement::condition::flatten() const
1085 {
1086 switch (type_)
1087 {
1088 case type::empty:
1089 case type::singleton:
1090 {
1091 return *this;
1092 }
1093
1094 case type::group:
1095 {
1096 condition result(group_.orlogic_);
1097
1098 for (const condition& child : group_.children_)
1099 {
1100 condition newChild = child.flatten();
1101
1102 if ((newChild.type_ == type::group) && (newChild.group_.orlogic_ == group_.orlogic_))
1103 {
1104 for (condition subChild : std::move(newChild.group_.children_))
1105 {
1106 result += std::move(subChild);
1107 }
1108 } else {
1109 result += std::move(newChild);
1110 }
1111 }
1112
1113 return result;
1114 }
1115 }
1116 }
991 1117
992}; 1118};