diff options
Diffstat (limited to 'lib/statement.cpp')
| -rw-r--r-- | lib/statement.cpp | 164 |
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 | ||
| 14 | namespace verbly { | 15 | namespace 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 | }; |
