diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-02-06 20:58:37 -0500 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-02-06 20:58:37 -0500 |
commit | f1f67e62cebb4144f0599196263cd93b41fa972e (patch) | |
tree | 5d0f8fceeec63f47fb85846f5568bcb36f4a975f /lib | |
parent | 6cc23ba387d0813b801ba094709673a61bac889c (diff) | |
download | verbly-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')
-rw-r--r-- | lib/binding.cpp | 71 | ||||
-rw-r--r-- | lib/binding.h | 14 | ||||
-rw-r--r-- | lib/database.cpp | 5 | ||||
-rw-r--r-- | lib/database.h | 3 | ||||
-rw-r--r-- | lib/enums.h | 5 | ||||
-rw-r--r-- | lib/field.h | 61 | ||||
-rw-r--r-- | lib/filter.cpp | 201 | ||||
-rw-r--r-- | lib/filter.h | 8 | ||||
-rw-r--r-- | lib/form.cpp | 6 | ||||
-rw-r--r-- | lib/form.h | 2 | ||||
-rw-r--r-- | lib/frame.cpp | 2 | ||||
-rw-r--r-- | lib/lemma.cpp | 67 | ||||
-rw-r--r-- | lib/lemma.h | 97 | ||||
-rw-r--r-- | lib/pronunciation.cpp | 37 | ||||
-rw-r--r-- | lib/pronunciation.h | 23 | ||||
-rw-r--r-- | lib/query.h | 5 | ||||
-rw-r--r-- | lib/statement.cpp | 232 | ||||
-rw-r--r-- | lib/statement.h | 9 | ||||
-rw-r--r-- | lib/token.cpp | 2 | ||||
-rw-r--r-- | lib/verbly.h | 1 | ||||
-rw-r--r-- | lib/word.cpp | 65 | ||||
-rw-r--r-- | lib/word.h | 19 |
22 files changed, 571 insertions, 364 deletions
diff --git a/lib/binding.cpp b/lib/binding.cpp index 349cd6f..0b58785 100644 --- a/lib/binding.cpp +++ b/lib/binding.cpp | |||
@@ -24,6 +24,14 @@ namespace verbly { | |||
24 | break; | 24 | break; |
25 | } | 25 | } |
26 | 26 | ||
27 | case type::field: | ||
28 | { | ||
29 | new(&field_.table_) std::string(other.field_.table_); | ||
30 | new(&field_.column_) std::string(other.field_.column_); | ||
31 | |||
32 | break; | ||
33 | } | ||
34 | |||
27 | case type::invalid: | 35 | case type::invalid: |
28 | { | 36 | { |
29 | break; | 37 | break; |
@@ -50,6 +58,8 @@ namespace verbly { | |||
50 | type tempType = first.type_; | 58 | type tempType = first.type_; |
51 | int tempInteger; | 59 | int tempInteger; |
52 | std::string tempString; | 60 | std::string tempString; |
61 | std::string tempTable; | ||
62 | std::string tempColumn; | ||
53 | 63 | ||
54 | switch (first.type_) | 64 | switch (first.type_) |
55 | { | 65 | { |
@@ -67,6 +77,14 @@ namespace verbly { | |||
67 | break; | 77 | break; |
68 | } | 78 | } |
69 | 79 | ||
80 | case type::field: | ||
81 | { | ||
82 | tempTable = std::move(first.field_.table_); | ||
83 | tempColumn = std::move(first.field_.column_); | ||
84 | |||
85 | break; | ||
86 | } | ||
87 | |||
70 | case type::invalid: | 88 | case type::invalid: |
71 | { | 89 | { |
72 | break; | 90 | break; |
@@ -93,6 +111,14 @@ namespace verbly { | |||
93 | break; | 111 | break; |
94 | } | 112 | } |
95 | 113 | ||
114 | case type::field: | ||
115 | { | ||
116 | new(&first.field_.table_) std::string(std::move(second.field_.table_)); | ||
117 | new(&first.field_.column_) std::string(std::move(second.field_.column_)); | ||
118 | |||
119 | break; | ||
120 | } | ||
121 | |||
96 | case type::invalid: | 122 | case type::invalid: |
97 | { | 123 | { |
98 | break; | 124 | break; |
@@ -119,6 +145,14 @@ namespace verbly { | |||
119 | break; | 145 | break; |
120 | } | 146 | } |
121 | 147 | ||
148 | case type::field: | ||
149 | { | ||
150 | new(&first.field_.table_) std::string(std::move(tempTable)); | ||
151 | new(&first.field_.column_) std::string(std::move(tempColumn)); | ||
152 | |||
153 | break; | ||
154 | } | ||
155 | |||
122 | case type::invalid: | 156 | case type::invalid: |
123 | { | 157 | { |
124 | break; | 158 | break; |
@@ -138,6 +172,15 @@ namespace verbly { | |||
138 | break; | 172 | break; |
139 | } | 173 | } |
140 | 174 | ||
175 | case type::field: | ||
176 | { | ||
177 | using string_type = std::string; | ||
178 | field_.table_.~string_type(); | ||
179 | field_.column_.~string_type(); | ||
180 | |||
181 | break; | ||
182 | } | ||
183 | |||
141 | case type::integer: | 184 | case type::integer: |
142 | case type::invalid: | 185 | case type::invalid: |
143 | { | 186 | { |
@@ -164,7 +207,7 @@ namespace verbly { | |||
164 | 207 | ||
165 | binding::binding(std::string arg) : type_(type::string) | 208 | binding::binding(std::string arg) : type_(type::string) |
166 | { | 209 | { |
167 | new(&string_) std::string(arg); | 210 | new(&string_) std::string(std::move(arg)); |
168 | } | 211 | } |
169 | 212 | ||
170 | std::string binding::getString() const | 213 | std::string binding::getString() const |
@@ -177,4 +220,30 @@ namespace verbly { | |||
177 | return string_; | 220 | return string_; |
178 | } | 221 | } |
179 | 222 | ||
223 | binding::binding(std::string table, std::string column) : type_(type::field) | ||
224 | { | ||
225 | new(&field_.table_) std::string(std::move(table)); | ||
226 | new(&field_.column_) std::string(std::move(column)); | ||
227 | } | ||
228 | |||
229 | std::string binding::getTable() const | ||
230 | { | ||
231 | if (type_ != type::field) | ||
232 | { | ||
233 | throw std::domain_error("binding::getTable called on non-field binding"); | ||
234 | } | ||
235 | |||
236 | return field_.table_; | ||
237 | } | ||
238 | |||
239 | std::string binding::getColumn() const | ||
240 | { | ||
241 | if (type_ != type::field) | ||
242 | { | ||
243 | throw std::domain_error("binding::getColumn called on non-field binding"); | ||
244 | } | ||
245 | |||
246 | return field_.column_; | ||
247 | } | ||
248 | |||
180 | }; | 249 | }; |
diff --git a/lib/binding.h b/lib/binding.h index 5578a09..5da1e71 100644 --- a/lib/binding.h +++ b/lib/binding.h | |||
@@ -10,7 +10,8 @@ namespace verbly { | |||
10 | enum class type { | 10 | enum class type { |
11 | invalid, | 11 | invalid, |
12 | integer, | 12 | integer, |
13 | string | 13 | string, |
14 | field | ||
14 | }; | 15 | }; |
15 | 16 | ||
16 | // Default constructor | 17 | // Default constructor |
@@ -55,11 +56,22 @@ namespace verbly { | |||
55 | 56 | ||
56 | std::string getString() const; | 57 | std::string getString() const; |
57 | 58 | ||
59 | // Field | ||
60 | |||
61 | binding(std::string table, std::string column); | ||
62 | |||
63 | std::string getTable() const; | ||
64 | std::string getColumn() const; | ||
65 | |||
58 | private: | 66 | private: |
59 | 67 | ||
60 | union { | 68 | union { |
61 | int integer_; | 69 | int integer_; |
62 | std::string string_; | 70 | std::string string_; |
71 | struct { | ||
72 | std::string table_; | ||
73 | std::string column_; | ||
74 | } field_; | ||
63 | }; | 75 | }; |
64 | 76 | ||
65 | type type_ = type::invalid; | 77 | type type_ = type::invalid; |
diff --git a/lib/database.cpp b/lib/database.cpp index c7b37ec..a4d056d 100644 --- a/lib/database.cpp +++ b/lib/database.cpp | |||
@@ -61,11 +61,6 @@ namespace verbly { | |||
61 | return query<part>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); | 61 | return query<part>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
62 | } | 62 | } |
63 | 63 | ||
64 | query<lemma> database::lemmas(filter where, order sortOrder, int limit) const | ||
65 | { | ||
66 | return query<lemma>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); | ||
67 | } | ||
68 | |||
69 | query<form> database::forms(filter where, order sortOrder, int limit) const | 64 | query<form> database::forms(filter where, order sortOrder, int limit) const |
70 | { | 65 | { |
71 | return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); | 66 | return query<form>(*this, ppdb_, std::move(where), std::move(sortOrder), limit); |
diff --git a/lib/database.h b/lib/database.h index 5567061..ef5ff87 100644 --- a/lib/database.h +++ b/lib/database.h | |||
@@ -9,7 +9,6 @@ | |||
9 | #include "word.h" | 9 | #include "word.h" |
10 | #include "frame.h" | 10 | #include "frame.h" |
11 | #include "part.h" | 11 | #include "part.h" |
12 | #include "lemma.h" | ||
13 | #include "form.h" | 12 | #include "form.h" |
14 | #include "pronunciation.h" | 13 | #include "pronunciation.h" |
15 | #include "order.h" | 14 | #include "order.h" |
@@ -56,8 +55,6 @@ namespace verbly { | |||
56 | 55 | ||
57 | query<part> parts(filter where, order sortOrder = {}, int limit = 1) const; | 56 | query<part> parts(filter where, order sortOrder = {}, int limit = 1) const; |
58 | 57 | ||
59 | query<lemma> lemmas(filter where, order sortOrder = {}, int limit = 1) const; | ||
60 | |||
61 | query<form> forms(filter where, order sortOrder = {}, int limit = 1) const; | 58 | query<form> forms(filter where, order sortOrder = {}, int limit = 1) const; |
62 | 59 | ||
63 | query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const; | 60 | query<pronunciation> pronunciations(filter where, order sortOrder = {}, int limit = 1) const; |
diff --git a/lib/enums.h b/lib/enums.h index 2646fa4..55a1bda 100644 --- a/lib/enums.h +++ b/lib/enums.h | |||
@@ -35,9 +35,8 @@ namespace verbly { | |||
35 | word = 1, | 35 | word = 1, |
36 | frame = 2, | 36 | frame = 2, |
37 | part = 3, | 37 | part = 3, |
38 | lemma = 4, | 38 | form = 4, |
39 | form = 5, | 39 | pronunciation = 5 |
40 | pronunciation = 6 | ||
41 | }; | 40 | }; |
42 | 41 | ||
43 | enum class part_type { | 42 | enum class part_type { |
diff --git a/lib/field.h b/lib/field.h index bec0618..f799900 100644 --- a/lib/field.h +++ b/lib/field.h | |||
@@ -19,6 +19,7 @@ namespace verbly { | |||
19 | join, | 19 | join, |
20 | join_where, | 20 | join_where, |
21 | join_through, | 21 | join_through, |
22 | join_through_where, | ||
22 | hierarchal_join | 23 | hierarchal_join |
23 | }; | 24 | }; |
24 | 25 | ||
@@ -100,11 +101,11 @@ namespace verbly { | |||
100 | object obj, | 101 | object obj, |
101 | const char* name, | 102 | const char* name, |
102 | object joinWith, | 103 | object joinWith, |
103 | const field& conditionField, | 104 | const char* conditionColumn, |
104 | int conditionValue, | 105 | int conditionValue, |
105 | bool nullable = false) | 106 | bool nullable = false) |
106 | { | 107 | { |
107 | return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, &conditionField, conditionValue); | 108 | return field(obj, type::join_where, name, nullable, 0, joinWith, 0, 0, 0, conditionColumn, conditionValue); |
108 | } | 109 | } |
109 | 110 | ||
110 | static field joinThrough( | 111 | static field joinThrough( |
@@ -129,6 +130,19 @@ namespace verbly { | |||
129 | return field(obj, type::join_through, name, true, joinTable, joinWith, foreignColumn, joinColumn, foreignJoinColumn); | 130 | return field(obj, type::join_through, name, true, joinTable, joinWith, foreignColumn, joinColumn, foreignJoinColumn); |
130 | } | 131 | } |
131 | 132 | ||
133 | static field joinThroughWhere( | ||
134 | object obj, | ||
135 | const char* name, | ||
136 | object joinWith, | ||
137 | const char* joinTable, | ||
138 | const char* foreignColumn, | ||
139 | const char* conditionColumn, | ||
140 | int conditionValue, | ||
141 | bool nullable = false) | ||
142 | { | ||
143 | return field(obj, type::join_through_where, name, nullable, joinTable, joinWith, foreignColumn, name, foreignColumn, conditionColumn, conditionValue); | ||
144 | } | ||
145 | |||
132 | static field selfJoin( | 146 | static field selfJoin( |
133 | object obj, | 147 | object obj, |
134 | const char* name, | 148 | const char* name, |
@@ -166,6 +180,7 @@ namespace verbly { | |||
166 | return ((type_ == type::join) | 180 | return ((type_ == type::join) |
167 | || (type_ == type::join_where) | 181 | || (type_ == type::join_where) |
168 | || (type_ == type::join_through) | 182 | || (type_ == type::join_through) |
183 | || (type_ == type::join_through_where) | ||
169 | || (type_ == type::hierarchal_join)); | 184 | || (type_ == type::hierarchal_join)); |
170 | } | 185 | } |
171 | 186 | ||
@@ -195,7 +210,7 @@ namespace verbly { | |||
195 | { | 210 | { |
196 | return (type_ == type::hierarchal_join) | 211 | return (type_ == type::hierarchal_join) |
197 | ? object_ | 212 | ? object_ |
198 | : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through)) | 213 | : ((type_ == type::join) || (type_ == type::join_where) || (type_ == type::join_through) || (type_ == type::join_through_where)) |
199 | ? joinObject_ | 214 | ? joinObject_ |
200 | : throw std::domain_error("Non-join fields don't have join objects"); | 215 | : throw std::domain_error("Non-join fields don't have join objects"); |
201 | } | 216 | } |
@@ -205,40 +220,40 @@ namespace verbly { | |||
205 | const char* getForeignColumn() const | 220 | const char* getForeignColumn() const |
206 | { | 221 | { |
207 | // We ignore hierarchal joins because they are always self joins. | 222 | // We ignore hierarchal joins because they are always self joins. |
208 | return (type_ == type::join_through) | 223 | return ((type_ == type::join_through) || (type_ == type::join_through_where)) |
209 | ? foreignColumn_ | 224 | ? foreignColumn_ |
210 | : throw std::domain_error("Only many-to-many join fields have a foreign column"); | 225 | : throw std::domain_error("Only many-to-many join fields have a foreign column"); |
211 | } | 226 | } |
212 | 227 | ||
213 | const char* getJoinColumn() const | 228 | const char* getJoinColumn() const |
214 | { | 229 | { |
215 | return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) | 230 | return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join)) |
216 | ? joinColumn_ | 231 | ? joinColumn_ |
217 | : throw std::domain_error("Only many-to-many join fields have a join column"); | 232 | : throw std::domain_error("Only many-to-many join fields have a join column"); |
218 | } | 233 | } |
219 | 234 | ||
220 | const char* getForeignJoinColumn() const | 235 | const char* getForeignJoinColumn() const |
221 | { | 236 | { |
222 | return ((type_ == type::join_through) || (type_ == type::hierarchal_join)) | 237 | return ((type_ == type::join_through) || (type_ == type::join_through_where) || (type_ == type::hierarchal_join)) |
223 | ? foreignJoinColumn_ | 238 | ? foreignJoinColumn_ |
224 | : throw std::domain_error("Only many-to-many join fields have a foreign join column"); | 239 | : throw std::domain_error("Only many-to-many join fields have a foreign join column"); |
225 | } | 240 | } |
226 | 241 | ||
227 | // Condition joins | 242 | // Condition joins |
228 | 243 | ||
229 | const field& getConditionField() const | 244 | const char* getConditionColumn() const |
230 | { | 245 | { |
231 | if (type_ == type::join_where) | 246 | if ((type_ == type::join_where) || (type_ == type::join_through_where)) |
232 | { | 247 | { |
233 | return *conditionField_; | 248 | return conditionColumn_; |
234 | } else { | 249 | } else { |
235 | throw std::domain_error("Only condition join fields have a condition field"); | 250 | throw std::domain_error("Only condition join fields have a condition column"); |
236 | } | 251 | } |
237 | } | 252 | } |
238 | 253 | ||
239 | int getConditionValue() const | 254 | int getConditionValue() const |
240 | { | 255 | { |
241 | return (type_ == type::join_where) | 256 | return ((type_ == type::join_where) || (type_ == type::join_through_where)) |
242 | ? conditionValue_ | 257 | ? conditionValue_ |
243 | : throw std::domain_error("Only condition join fields have a condition value"); | 258 | : throw std::domain_error("Only condition join fields have a condition value"); |
244 | } | 259 | } |
@@ -254,13 +269,8 @@ namespace verbly { | |||
254 | // table (hypernymy); however, they have different join columns. For | 269 | // table (hypernymy); however, they have different join columns. For |
255 | // condition joins, the condition field and condition value are also | 270 | // condition joins, the condition field and condition value are also |
256 | // significant. | 271 | // significant. |
257 | if (conditionField_) | 272 | return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_) |
258 | { | 273 | < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_); |
259 | return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) | ||
260 | < std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); | ||
261 | } else { | ||
262 | return std::tie(object_, column_, table_, joinColumn_) < std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | ||
263 | } | ||
264 | } | 274 | } |
265 | 275 | ||
266 | // Equality | 276 | // Equality |
@@ -268,13 +278,8 @@ namespace verbly { | |||
268 | bool operator==(const field& other) const | 278 | bool operator==(const field& other) const |
269 | { | 279 | { |
270 | // See operator<() for documentation. | 280 | // See operator<() for documentation. |
271 | if (conditionField_) | 281 | return std::tie(object_, column_, table_, joinColumn_, conditionColumn_, conditionValue_) |
272 | { | 282 | == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, other.conditionColumn_, other.conditionValue_); |
273 | return std::tie(object_, column_, table_, joinColumn_, *conditionField_, conditionValue_) | ||
274 | == std::tie(other.object_, other.column_, other.table_, other.joinColumn_, *other.conditionField_, other.conditionValue_); | ||
275 | } else { | ||
276 | return std::tie(object_, column_, table_, joinColumn_) == std::tie(other.object_, other.column_, other.table_, other.joinColumn_); | ||
277 | } | ||
278 | } | 283 | } |
279 | 284 | ||
280 | // Filter construction | 285 | // Filter construction |
@@ -325,7 +330,7 @@ namespace verbly { | |||
325 | const char* foreignColumn = 0, | 330 | const char* foreignColumn = 0, |
326 | const char* joinColumn = 0, | 331 | const char* joinColumn = 0, |
327 | const char* foreignJoinColumn = 0, | 332 | const char* foreignJoinColumn = 0, |
328 | const field* conditionField = 0, | 333 | const char* conditionColumn = 0, |
329 | int conditionValue = 0) : | 334 | int conditionValue = 0) : |
330 | object_(obj), | 335 | object_(obj), |
331 | type_(datatype), | 336 | type_(datatype), |
@@ -336,7 +341,7 @@ namespace verbly { | |||
336 | foreignColumn_(foreignColumn), | 341 | foreignColumn_(foreignColumn), |
337 | joinColumn_(joinColumn), | 342 | joinColumn_(joinColumn), |
338 | foreignJoinColumn_(foreignJoinColumn), | 343 | foreignJoinColumn_(foreignJoinColumn), |
339 | conditionField_(conditionField), | 344 | conditionColumn_(conditionColumn), |
340 | conditionValue_(conditionValue) | 345 | conditionValue_(conditionValue) |
341 | { | 346 | { |
342 | } | 347 | } |
@@ -359,7 +364,7 @@ namespace verbly { | |||
359 | const char* foreignJoinColumn_ = 0; | 364 | const char* foreignJoinColumn_ = 0; |
360 | 365 | ||
361 | // Condition joins | 366 | // Condition joins |
362 | const field* conditionField_ = 0; | 367 | const char* conditionColumn_ = 0; |
363 | int conditionValue_ = 0; | 368 | int conditionValue_ = 0; |
364 | 369 | ||
365 | }; | 370 | }; |
diff --git a/lib/filter.cpp b/lib/filter.cpp index ecff8ac..c201618 100644 --- a/lib/filter.cpp +++ b/lib/filter.cpp | |||
@@ -5,7 +5,6 @@ | |||
5 | #include "word.h" | 5 | #include "word.h" |
6 | #include "frame.h" | 6 | #include "frame.h" |
7 | #include "part.h" | 7 | #include "part.h" |
8 | #include "lemma.h" | ||
9 | #include "form.h" | 8 | #include "form.h" |
10 | #include "pronunciation.h" | 9 | #include "pronunciation.h" |
11 | 10 | ||
@@ -73,6 +72,14 @@ namespace verbly { | |||
73 | 72 | ||
74 | break; | 73 | break; |
75 | } | 74 | } |
75 | |||
76 | case comparison::field_equals: | ||
77 | case comparison::field_does_not_equal: | ||
78 | { | ||
79 | new(&singleton_.compareField) field(other.singleton_.compareField); | ||
80 | |||
81 | break; | ||
82 | } | ||
76 | } | 83 | } |
77 | 84 | ||
78 | break; | 85 | break; |
@@ -112,6 +119,7 @@ namespace verbly { | |||
112 | std::string tempStringValue; | 119 | std::string tempStringValue; |
113 | int tempIntValue; | 120 | int tempIntValue; |
114 | bool tempBoolValue; | 121 | bool tempBoolValue; |
122 | field tempCompareField; | ||
115 | std::list<filter> tempChildren; | 123 | std::list<filter> tempChildren; |
116 | bool tempOrlogic; | 124 | bool tempOrlogic; |
117 | 125 | ||
@@ -173,6 +181,14 @@ namespace verbly { | |||
173 | 181 | ||
174 | break; | 182 | break; |
175 | } | 183 | } |
184 | |||
185 | case comparison::field_equals: | ||
186 | case comparison::field_does_not_equal: | ||
187 | { | ||
188 | tempCompareField = std::move(first.singleton_.compareField); | ||
189 | |||
190 | break; | ||
191 | } | ||
176 | } | 192 | } |
177 | 193 | ||
178 | break; | 194 | break; |
@@ -249,6 +265,14 @@ namespace verbly { | |||
249 | 265 | ||
250 | break; | 266 | break; |
251 | } | 267 | } |
268 | |||
269 | case comparison::field_equals: | ||
270 | case comparison::field_does_not_equal: | ||
271 | { | ||
272 | new(&first.singleton_.compareField) field(std::move(second.singleton_.compareField)); | ||
273 | |||
274 | break; | ||
275 | } | ||
252 | } | 276 | } |
253 | 277 | ||
254 | break; | 278 | break; |
@@ -325,6 +349,14 @@ namespace verbly { | |||
325 | 349 | ||
326 | break; | 350 | break; |
327 | } | 351 | } |
352 | |||
353 | case comparison::field_equals: | ||
354 | case comparison::field_does_not_equal: | ||
355 | { | ||
356 | new(&second.singleton_.compareField) field(std::move(tempCompareField)); | ||
357 | |||
358 | break; | ||
359 | } | ||
328 | } | 360 | } |
329 | 361 | ||
330 | break; | 362 | break; |
@@ -391,6 +423,14 @@ namespace verbly { | |||
391 | 423 | ||
392 | break; | 424 | break; |
393 | } | 425 | } |
426 | |||
427 | case comparison::field_equals: | ||
428 | case comparison::field_does_not_equal: | ||
429 | { | ||
430 | singleton_.compareField.~field(); | ||
431 | |||
432 | break; | ||
433 | } | ||
394 | } | 434 | } |
395 | 435 | ||
396 | break; | 436 | break; |
@@ -446,6 +486,8 @@ namespace verbly { | |||
446 | case comparison::does_not_match: | 486 | case comparison::does_not_match: |
447 | case comparison::hierarchally_matches: | 487 | case comparison::hierarchally_matches: |
448 | case comparison::does_not_hierarchally_match: | 488 | case comparison::does_not_hierarchally_match: |
489 | case comparison::field_equals: | ||
490 | case comparison::field_does_not_equal: | ||
449 | { | 491 | { |
450 | throw std::invalid_argument("Invalid comparison for integer field"); | 492 | throw std::invalid_argument("Invalid comparison for integer field"); |
451 | } | 493 | } |
@@ -490,6 +532,8 @@ namespace verbly { | |||
490 | case comparison::does_not_match: | 532 | case comparison::does_not_match: |
491 | case comparison::hierarchally_matches: | 533 | case comparison::hierarchally_matches: |
492 | case comparison::does_not_hierarchally_match: | 534 | case comparison::does_not_hierarchally_match: |
535 | case comparison::field_equals: | ||
536 | case comparison::field_does_not_equal: | ||
493 | { | 537 | { |
494 | throw std::invalid_argument("Invalid comparison for string field"); | 538 | throw std::invalid_argument("Invalid comparison for string field"); |
495 | } | 539 | } |
@@ -534,6 +578,8 @@ namespace verbly { | |||
534 | case comparison::does_not_match: | 578 | case comparison::does_not_match: |
535 | case comparison::hierarchally_matches: | 579 | case comparison::hierarchally_matches: |
536 | case comparison::does_not_hierarchally_match: | 580 | case comparison::does_not_hierarchally_match: |
581 | case comparison::field_equals: | ||
582 | case comparison::field_does_not_equal: | ||
537 | { | 583 | { |
538 | throw std::invalid_argument("Invalid comparison for boolean field"); | 584 | throw std::invalid_argument("Invalid comparison for boolean field"); |
539 | } | 585 | } |
@@ -576,6 +622,8 @@ namespace verbly { | |||
576 | case comparison::does_not_match: | 622 | case comparison::does_not_match: |
577 | case comparison::hierarchally_matches: | 623 | case comparison::hierarchally_matches: |
578 | case comparison::does_not_hierarchally_match: | 624 | case comparison::does_not_hierarchally_match: |
625 | case comparison::field_equals: | ||
626 | case comparison::field_does_not_equal: | ||
579 | { | 627 | { |
580 | throw std::invalid_argument("Incorrect constructor for given comparison"); | 628 | throw std::invalid_argument("Incorrect constructor for given comparison"); |
581 | } | 629 | } |
@@ -596,6 +644,7 @@ namespace verbly { | |||
596 | case field::type::join: | 644 | case field::type::join: |
597 | case field::type::join_where: | 645 | case field::type::join_where: |
598 | case field::type::join_through: | 646 | case field::type::join_through: |
647 | case field::type::join_through_where: | ||
599 | { | 648 | { |
600 | switch (filterType) | 649 | switch (filterType) |
601 | { | 650 | { |
@@ -624,6 +673,8 @@ namespace verbly { | |||
624 | case comparison::is_not_null: | 673 | case comparison::is_not_null: |
625 | case comparison::hierarchally_matches: | 674 | case comparison::hierarchally_matches: |
626 | case comparison::does_not_hierarchally_match: | 675 | case comparison::does_not_hierarchally_match: |
676 | case comparison::field_equals: | ||
677 | case comparison::field_does_not_equal: | ||
627 | { | 678 | { |
628 | throw std::invalid_argument("Incorrect constructor for given comparison"); | 679 | throw std::invalid_argument("Incorrect constructor for given comparison"); |
629 | } | 680 | } |
@@ -661,6 +712,8 @@ namespace verbly { | |||
661 | case comparison::is_not_null: | 712 | case comparison::is_not_null: |
662 | case comparison::matches: | 713 | case comparison::matches: |
663 | case comparison::does_not_match: | 714 | case comparison::does_not_match: |
715 | case comparison::field_equals: | ||
716 | case comparison::field_does_not_equal: | ||
664 | { | 717 | { |
665 | throw std::invalid_argument("Incorrect constructor for given comparison"); | 718 | throw std::invalid_argument("Incorrect constructor for given comparison"); |
666 | } | 719 | } |
@@ -679,6 +732,57 @@ namespace verbly { | |||
679 | } | 732 | } |
680 | } | 733 | } |
681 | 734 | ||
735 | filter::filter( | ||
736 | field filterField, | ||
737 | comparison filterType, | ||
738 | field compareField) : | ||
739 | type_(type::singleton) | ||
740 | { | ||
741 | switch (filterType) | ||
742 | { | ||
743 | case comparison::field_equals: | ||
744 | case comparison::field_does_not_equal: | ||
745 | { | ||
746 | if (filterField.getType() != compareField.getType()) | ||
747 | { | ||
748 | throw std::invalid_argument("Cannot compare two fields of different types"); | ||
749 | } | ||
750 | |||
751 | if (filterField.isJoin()) | ||
752 | { | ||
753 | throw std::domain_error("Cannot compare join fields"); | ||
754 | } | ||
755 | |||
756 | new(&singleton_.filterField) field(std::move(filterField)); | ||
757 | singleton_.filterType = filterType; | ||
758 | new(&singleton_.compareField) field(std::move(compareField)); | ||
759 | |||
760 | break; | ||
761 | } | ||
762 | |||
763 | case comparison::int_equals: | ||
764 | case comparison::int_does_not_equal: | ||
765 | case comparison::int_is_at_least: | ||
766 | case comparison::int_is_greater_than: | ||
767 | case comparison::int_is_at_most: | ||
768 | case comparison::int_is_less_than: | ||
769 | case comparison::boolean_equals: | ||
770 | case comparison::string_equals: | ||
771 | case comparison::string_does_not_equal: | ||
772 | case comparison::string_is_like: | ||
773 | case comparison::string_is_not_like: | ||
774 | case comparison::is_null: | ||
775 | case comparison::is_not_null: | ||
776 | case comparison::matches: | ||
777 | case comparison::does_not_match: | ||
778 | case comparison::hierarchally_matches: | ||
779 | case comparison::does_not_hierarchally_match: | ||
780 | { | ||
781 | throw std::domain_error("Incorrect constructor for given comparison"); | ||
782 | } | ||
783 | } | ||
784 | } | ||
785 | |||
682 | field filter::getField() const | 786 | field filter::getField() const |
683 | { | 787 | { |
684 | if (type_ == type::singleton) | 788 | if (type_ == type::singleton) |
@@ -726,6 +830,8 @@ namespace verbly { | |||
726 | case comparison::boolean_equals: | 830 | case comparison::boolean_equals: |
727 | case comparison::is_null: | 831 | case comparison::is_null: |
728 | case comparison::is_not_null: | 832 | case comparison::is_not_null: |
833 | case comparison::field_equals: | ||
834 | case comparison::field_does_not_equal: | ||
729 | { | 835 | { |
730 | throw std::domain_error("This filter does not have a join condition"); | 836 | throw std::domain_error("This filter does not have a join condition"); |
731 | } | 837 | } |
@@ -762,6 +868,8 @@ namespace verbly { | |||
762 | case comparison::does_not_match: | 868 | case comparison::does_not_match: |
763 | case comparison::hierarchally_matches: | 869 | case comparison::hierarchally_matches: |
764 | case comparison::does_not_hierarchally_match: | 870 | case comparison::does_not_hierarchally_match: |
871 | case comparison::field_equals: | ||
872 | case comparison::field_does_not_equal: | ||
765 | { | 873 | { |
766 | throw std::domain_error("This filter does not have a string argument"); | 874 | throw std::domain_error("This filter does not have a string argument"); |
767 | } | 875 | } |
@@ -798,6 +906,8 @@ namespace verbly { | |||
798 | case comparison::does_not_match: | 906 | case comparison::does_not_match: |
799 | case comparison::hierarchally_matches: | 907 | case comparison::hierarchally_matches: |
800 | case comparison::does_not_hierarchally_match: | 908 | case comparison::does_not_hierarchally_match: |
909 | case comparison::field_equals: | ||
910 | case comparison::field_does_not_equal: | ||
801 | { | 911 | { |
802 | throw std::domain_error("This filter does not have an integer argument"); | 912 | throw std::domain_error("This filter does not have an integer argument"); |
803 | } | 913 | } |
@@ -817,6 +927,47 @@ namespace verbly { | |||
817 | } | 927 | } |
818 | } | 928 | } |
819 | 929 | ||
930 | field filter::getCompareField() const | ||
931 | { | ||
932 | if (type_ != type::singleton) | ||
933 | { | ||
934 | throw std::domain_error("This filter does not have a compare field"); | ||
935 | } | ||
936 | |||
937 | switch (singleton_.filterType) | ||
938 | { | ||
939 | case comparison::field_equals: | ||
940 | case comparison::field_does_not_equal: | ||
941 | { | ||
942 | return singleton_.compareField; | ||
943 | |||
944 | break; | ||
945 | } | ||
946 | |||
947 | case comparison::int_equals: | ||
948 | case comparison::int_does_not_equal: | ||
949 | case comparison::int_is_at_least: | ||
950 | case comparison::int_is_greater_than: | ||
951 | case comparison::int_is_at_most: | ||
952 | case comparison::int_is_less_than: | ||
953 | case comparison::boolean_equals: | ||
954 | case comparison::string_equals: | ||
955 | case comparison::string_does_not_equal: | ||
956 | case comparison::string_is_like: | ||
957 | case comparison::string_is_not_like: | ||
958 | case comparison::is_null: | ||
959 | case comparison::is_not_null: | ||
960 | case comparison::matches: | ||
961 | case comparison::does_not_match: | ||
962 | case comparison::hierarchally_matches: | ||
963 | case comparison::does_not_hierarchally_match: | ||
964 | { | ||
965 | throw std::domain_error("This filter doesn't have a compare field"); | ||
966 | } | ||
967 | } | ||
968 | |||
969 | } | ||
970 | |||
820 | filter::filter(bool orlogic) : type_(type::group) | 971 | filter::filter(bool orlogic) : type_(type::group) |
821 | { | 972 | { |
822 | new(&group_.children) std::list<filter>(); | 973 | new(&group_.children) std::list<filter>(); |
@@ -970,6 +1121,16 @@ namespace verbly { | |||
970 | { | 1121 | { |
971 | return filter(singleton_.filterField, comparison::hierarchally_matches, *singleton_.join); | 1122 | return filter(singleton_.filterField, comparison::hierarchally_matches, *singleton_.join); |
972 | } | 1123 | } |
1124 | |||
1125 | case comparison::field_equals: | ||
1126 | { | ||
1127 | return filter(singleton_.filterField, comparison::field_does_not_equal, singleton_.compareField); | ||
1128 | } | ||
1129 | |||
1130 | case comparison::field_does_not_equal: | ||
1131 | { | ||
1132 | return filter(singleton_.filterField, comparison::field_equals, singleton_.compareField); | ||
1133 | } | ||
973 | } | 1134 | } |
974 | } | 1135 | } |
975 | 1136 | ||
@@ -1111,7 +1272,6 @@ namespace verbly { | |||
1111 | case object::word: | 1272 | case object::word: |
1112 | case object::frame: | 1273 | case object::frame: |
1113 | case object::part: | 1274 | case object::part: |
1114 | case object::lemma: | ||
1115 | case object::form: | 1275 | case object::form: |
1116 | case object::pronunciation: | 1276 | case object::pronunciation: |
1117 | { | 1277 | { |
@@ -1141,11 +1301,10 @@ namespace verbly { | |||
1141 | return (verbly::word::frames %= *this); | 1301 | return (verbly::word::frames %= *this); |
1142 | } | 1302 | } |
1143 | 1303 | ||
1144 | case object::lemma: | ||
1145 | case object::form: | 1304 | case object::form: |
1146 | case object::pronunciation: | 1305 | case object::pronunciation: |
1147 | { | 1306 | { |
1148 | return (verbly::word::lemmas %= *this); | 1307 | return (verbly::word::forms(inflection::base) %= *this); |
1149 | } | 1308 | } |
1150 | } | 1309 | } |
1151 | 1310 | ||
@@ -1161,7 +1320,6 @@ namespace verbly { | |||
1161 | 1320 | ||
1162 | case object::notion: | 1321 | case object::notion: |
1163 | case object::word: | 1322 | case object::word: |
1164 | case object::lemma: | ||
1165 | case object::form: | 1323 | case object::form: |
1166 | case object::pronunciation: | 1324 | case object::pronunciation: |
1167 | { | 1325 | { |
@@ -1188,7 +1346,6 @@ namespace verbly { | |||
1188 | case object::notion: | 1346 | case object::notion: |
1189 | case object::word: | 1347 | case object::word: |
1190 | case object::frame: | 1348 | case object::frame: |
1191 | case object::lemma: | ||
1192 | case object::form: | 1349 | case object::form: |
1193 | case object::pronunciation: | 1350 | case object::pronunciation: |
1194 | { | 1351 | { |
@@ -1197,32 +1354,6 @@ namespace verbly { | |||
1197 | } | 1354 | } |
1198 | } | 1355 | } |
1199 | 1356 | ||
1200 | case object::lemma: | ||
1201 | { | ||
1202 | switch (singleton_.filterField.getObject()) | ||
1203 | { | ||
1204 | case object::notion: | ||
1205 | case object::word: | ||
1206 | case object::frame: | ||
1207 | case object::part: | ||
1208 | { | ||
1209 | return verbly::lemma::words %= *this; | ||
1210 | } | ||
1211 | |||
1212 | case object::undefined: | ||
1213 | case object::lemma: | ||
1214 | { | ||
1215 | return *this; | ||
1216 | } | ||
1217 | |||
1218 | case object::form: | ||
1219 | case object::pronunciation: | ||
1220 | { | ||
1221 | return (verbly::lemma::forms(inflection::base) %= *this); | ||
1222 | } | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | case object::form: | 1357 | case object::form: |
1227 | { | 1358 | { |
1228 | switch (singleton_.filterField.getObject()) | 1359 | switch (singleton_.filterField.getObject()) |
@@ -1231,9 +1362,8 @@ namespace verbly { | |||
1231 | case object::word: | 1362 | case object::word: |
1232 | case object::frame: | 1363 | case object::frame: |
1233 | case object::part: | 1364 | case object::part: |
1234 | case object::lemma: | ||
1235 | { | 1365 | { |
1236 | return verbly::form::lemmas %= *this; | 1366 | return verbly::form::words(inflection::base) %= *this; |
1237 | } | 1367 | } |
1238 | 1368 | ||
1239 | case object::undefined: | 1369 | case object::undefined: |
@@ -1257,7 +1387,6 @@ namespace verbly { | |||
1257 | case object::word: | 1387 | case object::word: |
1258 | case object::frame: | 1388 | case object::frame: |
1259 | case object::part: | 1389 | case object::part: |
1260 | case object::lemma: | ||
1261 | case object::form: | 1390 | case object::form: |
1262 | { | 1391 | { |
1263 | return verbly::pronunciation::forms %= *this; | 1392 | return verbly::pronunciation::forms %= *this; |
@@ -1354,6 +1483,8 @@ namespace verbly { | |||
1354 | case comparison::string_is_not_like: | 1483 | case comparison::string_is_not_like: |
1355 | case comparison::is_null: | 1484 | case comparison::is_null: |
1356 | case comparison::is_not_null: | 1485 | case comparison::is_not_null: |
1486 | case comparison::field_equals: | ||
1487 | case comparison::field_does_not_equal: | ||
1357 | { | 1488 | { |
1358 | result += std::move(normalized); | 1489 | result += std::move(normalized); |
1359 | 1490 | ||
diff --git a/lib/filter.h b/lib/filter.h index dcadf95..a12a822 100644 --- a/lib/filter.h +++ b/lib/filter.h | |||
@@ -34,7 +34,9 @@ namespace verbly { | |||
34 | matches, | 34 | matches, |
35 | does_not_match, | 35 | does_not_match, |
36 | hierarchally_matches, | 36 | hierarchally_matches, |
37 | does_not_hierarchally_match | 37 | does_not_hierarchally_match, |
38 | field_equals, | ||
39 | field_does_not_equal | ||
38 | }; | 40 | }; |
39 | 41 | ||
40 | // Copy and move constructors | 42 | // Copy and move constructors |
@@ -72,6 +74,7 @@ namespace verbly { | |||
72 | filter(field filterField, comparison filterType, bool filterValue); | 74 | filter(field filterField, comparison filterType, bool filterValue); |
73 | filter(field filterField, comparison filterType); | 75 | filter(field filterField, comparison filterType); |
74 | filter(field joinOn, comparison filterType, filter joinCondition); | 76 | filter(field joinOn, comparison filterType, filter joinCondition); |
77 | filter(field filterField, comparison filterType, field compareField); | ||
75 | 78 | ||
76 | field getField() const; | 79 | field getField() const; |
77 | 80 | ||
@@ -85,6 +88,8 @@ namespace verbly { | |||
85 | 88 | ||
86 | bool getBooleanArgument() const; | 89 | bool getBooleanArgument() const; |
87 | 90 | ||
91 | field getCompareField() const; | ||
92 | |||
88 | // Group | 93 | // Group |
89 | 94 | ||
90 | explicit filter(bool orlogic); | 95 | explicit filter(bool orlogic); |
@@ -129,6 +134,7 @@ namespace verbly { | |||
129 | std::string stringValue; | 134 | std::string stringValue; |
130 | int intValue; | 135 | int intValue; |
131 | bool boolValue; | 136 | bool boolValue; |
137 | field compareField; | ||
132 | }; | 138 | }; |
133 | } singleton_; | 139 | } singleton_; |
134 | struct { | 140 | struct { |
diff --git a/lib/form.cpp b/lib/form.cpp index 4548582..2f9509f 100644 --- a/lib/form.cpp +++ b/lib/form.cpp | |||
@@ -17,9 +17,13 @@ namespace verbly { | |||
17 | const field form::complexity = field::integerField(object::form, "complexity"); | 17 | const field form::complexity = field::integerField(object::form, "complexity"); |
18 | const field form::proper = field::booleanField(object::form, "proper"); | 18 | const field form::proper = field::booleanField(object::form, "proper"); |
19 | 19 | ||
20 | const field form::lemmas = field::joinField(object::form, "form_id", object::lemma); | ||
21 | const field form::pronunciations = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); | 20 | const field form::pronunciations = field::joinThrough(object::form, "form_id", object::pronunciation, "forms_pronunciations", "pronunciation_id"); |
22 | 21 | ||
22 | field form::words(inflection category) | ||
23 | { | ||
24 | return field::joinThroughWhere(object::form, "form_id", object::word, "lemmas_forms", "lemma_id", "category", static_cast<int>(category)); | ||
25 | } | ||
26 | |||
23 | form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 27 | form::form(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
24 | { | 28 | { |
25 | id_ = sqlite3_column_int(row, 0); | 29 | id_ = sqlite3_column_int(row, 0); |
diff --git a/lib/form.h b/lib/form.h index a05d015..fe0886c 100644 --- a/lib/form.h +++ b/lib/form.h | |||
@@ -104,7 +104,7 @@ namespace verbly { | |||
104 | 104 | ||
105 | // Relationships to other objects | 105 | // Relationships to other objects |
106 | 106 | ||
107 | static const field lemmas; | 107 | static field words(inflection category); |
108 | 108 | ||
109 | static const field pronunciations; | 109 | static const field pronunciations; |
110 | 110 | ||
diff --git a/lib/frame.cpp b/lib/frame.cpp index b5ba914..2351973 100644 --- a/lib/frame.cpp +++ b/lib/frame.cpp | |||
@@ -21,7 +21,7 @@ namespace verbly { | |||
21 | 21 | ||
22 | field frame::parts(int index) | 22 | field frame::parts(int index) |
23 | { | 23 | { |
24 | return field::joinWhere(object::frame, "frame_id", object::part, part::index, index); | 24 | return field::joinWhere(object::frame, "frame_id", object::part, "part_index", index); |
25 | } | 25 | } |
26 | 26 | ||
27 | frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 27 | frame::frame(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
diff --git a/lib/lemma.cpp b/lib/lemma.cpp deleted file mode 100644 index ea7b0ea..0000000 --- a/lib/lemma.cpp +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | #include "lemma.h" | ||
2 | #include <sqlite3.h> | ||
3 | #include "database.h" | ||
4 | #include "query.h" | ||
5 | |||
6 | namespace verbly { | ||
7 | |||
8 | const object lemma::objectType = object::lemma; | ||
9 | |||
10 | const std::list<std::string> lemma::select = {"lemma_id"}; | ||
11 | |||
12 | const field lemma::id = field::integerField(object::lemma, "lemma_id"); | ||
13 | const field lemma::inflectionCategory = field::integerField(object::lemma, "category"); | ||
14 | |||
15 | const field lemma::words = field::joinField(object::lemma, "lemma_id", object::word); | ||
16 | |||
17 | field lemma::forms(inflection category) | ||
18 | { | ||
19 | return field::joinWhere(object::lemma, "form_id", object::form, inflectionCategory, static_cast<int>(category)); | ||
20 | } | ||
21 | |||
22 | lemma::lemma(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | ||
23 | { | ||
24 | id_ = sqlite3_column_int(row, 0); | ||
25 | } | ||
26 | |||
27 | const form& lemma::getBaseForm() const | ||
28 | { | ||
29 | if (!valid_) | ||
30 | { | ||
31 | throw std::domain_error("Bad access to uninitialized lemma"); | ||
32 | } | ||
33 | |||
34 | if (!forms_.count(inflection::base)) | ||
35 | { | ||
36 | initializeForm(inflection::base); | ||
37 | } | ||
38 | |||
39 | return forms_.at(inflection::base).front(); | ||
40 | } | ||
41 | |||
42 | bool lemma::hasInflection(inflection category) const | ||
43 | { | ||
44 | return !getInflections(category).empty(); | ||
45 | } | ||
46 | |||
47 | const std::vector<form>& lemma::getInflections(inflection category) const | ||
48 | { | ||
49 | if (!valid_) | ||
50 | { | ||
51 | throw std::domain_error("Bad access to uninitialized lemma"); | ||
52 | } | ||
53 | |||
54 | if (!forms_.count(category)) | ||
55 | { | ||
56 | initializeForm(category); | ||
57 | } | ||
58 | |||
59 | return forms_.at(category); | ||
60 | } | ||
61 | |||
62 | void lemma::initializeForm(inflection infl) const | ||
63 | { | ||
64 | forms_[infl] = db_->forms(form::lemmas %= ((inflectionCategory == infl) && *this), verbly::form::id, -1).all(); | ||
65 | } | ||
66 | |||
67 | }; | ||
diff --git a/lib/lemma.h b/lib/lemma.h deleted file mode 100644 index bba5572..0000000 --- a/lib/lemma.h +++ /dev/null | |||
@@ -1,97 +0,0 @@ | |||
1 | #ifndef LEMMA_H_0A180D30 | ||
2 | #define LEMMA_H_0A180D30 | ||
3 | |||
4 | #include <stdexcept> | ||
5 | #include <vector> | ||
6 | #include <list> | ||
7 | #include <map> | ||
8 | #include "field.h" | ||
9 | #include "form.h" | ||
10 | #include "enums.h" | ||
11 | #include "filter.h" | ||
12 | |||
13 | struct sqlite3_stmt; | ||
14 | |||
15 | namespace verbly { | ||
16 | |||
17 | class database; | ||
18 | |||
19 | class lemma { | ||
20 | public: | ||
21 | |||
22 | // Default constructor | ||
23 | |||
24 | lemma() = default; | ||
25 | |||
26 | // Construct from database | ||
27 | |||
28 | lemma(const database& db, sqlite3_stmt* row); | ||
29 | |||
30 | // Accessors | ||
31 | |||
32 | operator bool() const | ||
33 | { | ||
34 | return valid_; | ||
35 | } | ||
36 | |||
37 | int getId() const | ||
38 | { | ||
39 | if (!valid_) | ||
40 | { | ||
41 | throw std::domain_error("Bad access to uninitialized lemma"); | ||
42 | } | ||
43 | |||
44 | return id_; | ||
45 | } | ||
46 | |||
47 | const form& getBaseForm() const; | ||
48 | |||
49 | bool hasInflection(inflection category) const; | ||
50 | |||
51 | const std::vector<form>& getInflections(inflection category) const; | ||
52 | |||
53 | // Type info | ||
54 | |||
55 | static const object objectType; | ||
56 | |||
57 | static const std::list<std::string> select; | ||
58 | |||
59 | // Query fields | ||
60 | |||
61 | static const field id; | ||
62 | |||
63 | operator filter() const | ||
64 | { | ||
65 | if (!valid_) | ||
66 | { | ||
67 | throw std::domain_error("Bad access to uninitialized lemma"); | ||
68 | } | ||
69 | |||
70 | return (id == id_); | ||
71 | } | ||
72 | |||
73 | // Relationships to other objects | ||
74 | |||
75 | static const field words; | ||
76 | |||
77 | static field forms(inflection category); | ||
78 | |||
79 | private: | ||
80 | |||
81 | void initializeForm(inflection category) const; | ||
82 | |||
83 | bool valid_ = false; | ||
84 | |||
85 | int id_; | ||
86 | |||
87 | mutable std::map<inflection, std::vector<form>> forms_; | ||
88 | |||
89 | const database* db_; | ||
90 | |||
91 | static const field inflectionCategory; | ||
92 | |||
93 | }; | ||
94 | |||
95 | }; | ||
96 | |||
97 | #endif /* end of include guard: LEMMA_H_0A180D30 */ | ||
diff --git a/lib/pronunciation.cpp b/lib/pronunciation.cpp index 3ddb1c5..fa471ec 100644 --- a/lib/pronunciation.cpp +++ b/lib/pronunciation.cpp | |||
@@ -1,7 +1,6 @@ | |||
1 | #include "pronunciation.h" | 1 | #include "pronunciation.h" |
2 | #include <sqlite3.h> | 2 | #include <sqlite3.h> |
3 | #include "form.h" | 3 | #include "form.h" |
4 | #include "lemma.h" | ||
5 | #include "word.h" | 4 | #include "word.h" |
6 | #include "util.h" | 5 | #include "util.h" |
7 | 6 | ||
@@ -20,6 +19,9 @@ namespace verbly { | |||
20 | const field pronunciation::prerhyme = field::stringField(object::pronunciation, "prerhyme", true); | 19 | const field pronunciation::prerhyme = field::stringField(object::pronunciation, "prerhyme", true); |
21 | const field pronunciation::rhyme = field::stringField(object::pronunciation, "rhyme", true); | 20 | const field pronunciation::rhyme = field::stringField(object::pronunciation, "rhyme", true); |
22 | 21 | ||
22 | const field pronunciation::rhymes_field::rhymeJoin = field::joinField(object::pronunciation, "rhyme", object::pronunciation); | ||
23 | const pronunciation::rhymes_field pronunciation::rhymes = {}; | ||
24 | |||
23 | pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 25 | pronunciation::pronunciation(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
24 | { | 26 | { |
25 | id_ = sqlite3_column_int(row, 0); | 27 | id_ = sqlite3_column_int(row, 0); |
@@ -39,31 +41,22 @@ namespace verbly { | |||
39 | } | 41 | } |
40 | } | 42 | } |
41 | 43 | ||
42 | filter pronunciation::rhymesWith(const pronunciation& arg) | 44 | filter pronunciation::rhymes_field::operator%=(filter joinCondition) const |
43 | { | 45 | { |
44 | return (prerhyme != arg.getPrerhyme()) && (rhyme == arg.getRhyme()); | 46 | return (rhymeJoin %= ( |
45 | } | 47 | joinCondition |
46 | 48 | && filter( | |
47 | /*filter pronunciation::rhymesWith(const class form& arg) | 49 | verbly::pronunciation::prerhyme, |
48 | { | 50 | filter::comparison::field_does_not_equal, |
49 | filter result; | 51 | verbly::pronunciation::prerhyme))); |
50 | |||
51 | for (const pronunciation& p : arg.getPronunciations()) | ||
52 | { | ||
53 | result |= rhymesWith(p); | ||
54 | } | ||
55 | |||
56 | return result; | ||
57 | } | 52 | } |
58 | 53 | ||
59 | filter pronunciation::rhymesWith(const lemma& arg) | 54 | pronunciation::rhymes_field::operator filter() const |
60 | { | 55 | { |
61 | return rhymesWith(arg.getBaseForm()); | 56 | return (rhymeJoin %= filter( |
57 | verbly::pronunciation::prerhyme, | ||
58 | filter::comparison::field_does_not_equal, | ||
59 | verbly::pronunciation::prerhyme)); | ||
62 | } | 60 | } |
63 | 61 | ||
64 | filter pronunciation::rhymesWith(const word& arg) | ||
65 | { | ||
66 | return rhymesWith(arg.getLemma()); | ||
67 | }*/ | ||
68 | |||
69 | }; | 62 | }; |
diff --git a/lib/pronunciation.h b/lib/pronunciation.h index e171fe8..1162360 100644 --- a/lib/pronunciation.h +++ b/lib/pronunciation.h | |||
@@ -12,7 +12,6 @@ struct sqlite3_stmt; | |||
12 | namespace verbly { | 12 | namespace verbly { |
13 | 13 | ||
14 | class form; | 14 | class form; |
15 | class lemma; | ||
16 | class word; | 15 | class word; |
17 | class database; | 16 | class database; |
18 | 17 | ||
@@ -131,14 +130,26 @@ namespace verbly { | |||
131 | return (id == id_); | 130 | return (id == id_); |
132 | } | 131 | } |
133 | 132 | ||
134 | static filter rhymesWith(const pronunciation& arg); | ||
135 | static filter rhymesWith(const class form& arg); | ||
136 | static filter rhymesWith(const lemma& arg); | ||
137 | static filter rhymesWith(const word& arg); | ||
138 | |||
139 | // Relationships to other objects | 133 | // Relationships to other objects |
140 | 134 | ||
141 | static const field forms; | 135 | static const field forms; |
136 | |||
137 | // Rhyming relationship | ||
138 | |||
139 | class rhymes_field { | ||
140 | public: | ||
141 | |||
142 | filter operator%=(filter joinCondition) const; | ||
143 | |||
144 | operator filter() const; | ||
145 | |||
146 | private: | ||
147 | |||
148 | static const field rhymeJoin; | ||
149 | |||
150 | }; | ||
151 | |||
152 | static const rhymes_field rhymes; | ||
142 | 153 | ||
143 | private: | 154 | private: |
144 | bool valid_ = false; | 155 | bool valid_ = false; |
diff --git a/lib/query.h b/lib/query.h index 75651f6..0f490ed 100644 --- a/lib/query.h +++ b/lib/query.h | |||
@@ -81,6 +81,11 @@ namespace verbly { | |||
81 | { | 81 | { |
82 | throw std::logic_error("Cannot use invalid bindings"); | 82 | throw std::logic_error("Cannot use invalid bindings"); |
83 | } | 83 | } |
84 | |||
85 | case binding::type::field: | ||
86 | { | ||
87 | throw std::logic_error("Compare field binding made it past statement generation"); | ||
88 | } | ||
84 | } | 89 | } |
85 | 90 | ||
86 | i++; | 91 | i++; |
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 | ||
15 | namespace verbly { | 8 | namespace 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 | } |
diff --git a/lib/statement.h b/lib/statement.h index 15c4ac3..2fadf05 100644 --- a/lib/statement.h +++ b/lib/statement.h | |||
@@ -140,7 +140,7 @@ namespace verbly { | |||
140 | 140 | ||
141 | condition(std::string table, std::string column, bool isNull); | 141 | condition(std::string table, std::string column, bool isNull); |
142 | 142 | ||
143 | condition(std::string table, std::string column, comparison comp, binding value); | 143 | condition(std::string table, std::string column, comparison comp, binding value, object parentObject = object::undefined); |
144 | 144 | ||
145 | // Group | 145 | // Group |
146 | 146 | ||
@@ -160,6 +160,8 @@ namespace verbly { | |||
160 | 160 | ||
161 | condition flatten() const; | 161 | condition flatten() const; |
162 | 162 | ||
163 | condition resolveCompareFields(object context, std::string tableName) const; | ||
164 | |||
163 | private: | 165 | private: |
164 | union { | 166 | union { |
165 | struct { | 167 | struct { |
@@ -167,6 +169,7 @@ namespace verbly { | |||
167 | std::string column_; | 169 | std::string column_; |
168 | comparison comparison_; | 170 | comparison comparison_; |
169 | binding value_; | 171 | binding value_; |
172 | object parentObject_; | ||
170 | } singleton_; | 173 | } singleton_; |
171 | struct { | 174 | struct { |
172 | std::list<condition> children_; | 175 | std::list<condition> children_; |
@@ -251,7 +254,6 @@ namespace verbly { | |||
251 | : (context == object::word) ? "words" | 254 | : (context == object::word) ? "words" |
252 | : (context == object::frame) ? "frames" | 255 | : (context == object::frame) ? "frames" |
253 | : (context == object::part) ? "parts" | 256 | : (context == object::part) ? "parts" |
254 | : (context == object::lemma) ? "lemmas_forms" | ||
255 | : (context == object::form) ? "forms" | 257 | : (context == object::form) ? "forms" |
256 | : (context == object::pronunciation) ? "pronunciations" | 258 | : (context == object::pronunciation) ? "pronunciations" |
257 | : throw std::domain_error("Provided context has no associated table"); | 259 | : throw std::domain_error("Provided context has no associated table"); |
@@ -259,7 +261,7 @@ namespace verbly { | |||
259 | 261 | ||
260 | static const std::list<field> getSelectForContext(object context); | 262 | static const std::list<field> getSelectForContext(object context); |
261 | 263 | ||
262 | statement(std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0); | 264 | statement(object context, std::string tableName, filter clause, int nextTableId = 0, int nextWithId = 0); |
263 | 265 | ||
264 | condition parseFilter(filter queryFilter); | 266 | condition parseFilter(filter queryFilter); |
265 | 267 | ||
@@ -272,6 +274,7 @@ namespace verbly { | |||
272 | int nextTableId_; | 274 | int nextTableId_; |
273 | int nextWithId_; | 275 | int nextWithId_; |
274 | 276 | ||
277 | object context_; | ||
275 | std::map<std::string, std::string> tables_; | 278 | std::map<std::string, std::string> tables_; |
276 | std::string topTable_; | 279 | std::string topTable_; |
277 | std::list<join> joins_; | 280 | std::list<join> joins_; |
diff --git a/lib/token.cpp b/lib/token.cpp index d2024b8..735aa7e 100644 --- a/lib/token.cpp +++ b/lib/token.cpp | |||
@@ -261,7 +261,7 @@ namespace verbly { | |||
261 | { | 261 | { |
262 | switch (type_) | 262 | switch (type_) |
263 | { | 263 | { |
264 | case type::word: return word_.word_.getInflections(word_.category_).front(); | 264 | case type::word: return word_.word_.getInflections(word_.category_).front().getText(); |
265 | case type::literal: return literal_; | 265 | case type::literal: return literal_; |
266 | case type::part: throw std::domain_error("Cannot compile incomplete token"); | 266 | case type::part: throw std::domain_error("Cannot compile incomplete token"); |
267 | case type::fillin: throw std::domain_error("Cannot compile incomplete token"); | 267 | case type::fillin: throw std::domain_error("Cannot compile incomplete token"); |
diff --git a/lib/verbly.h b/lib/verbly.h index 0f48a8c..5bf204d 100644 --- a/lib/verbly.h +++ b/lib/verbly.h | |||
@@ -11,7 +11,6 @@ | |||
11 | #include "word.h" | 11 | #include "word.h" |
12 | #include "frame.h" | 12 | #include "frame.h" |
13 | #include "part.h" | 13 | #include "part.h" |
14 | #include "lemma.h" | ||
15 | #include "form.h" | 14 | #include "form.h" |
16 | #include "pronunciation.h" | 15 | #include "pronunciation.h" |
17 | #include "token.h" | 16 | #include "token.h" |
diff --git a/lib/word.cpp b/lib/word.cpp index d75159c..4dea569 100644 --- a/lib/word.cpp +++ b/lib/word.cpp | |||
@@ -16,7 +16,6 @@ namespace verbly { | |||
16 | const field word::adjectivePosition = field::integerField(object::word, "position", true); | 16 | const field word::adjectivePosition = field::integerField(object::word, "position", true); |
17 | 17 | ||
18 | const field word::notions = field::joinField(object::word, "notion_id", object::notion); | 18 | const field word::notions = field::joinField(object::word, "notion_id", object::notion); |
19 | const field word::lemmas = field::joinField(object::word, "lemma_id", object::lemma); | ||
20 | const field word::frames = field::joinField(object::word, "group_id", object::frame, true); | 19 | const field word::frames = field::joinField(object::word, "group_id", object::frame, true); |
21 | 20 | ||
22 | const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); | 21 | const field word::antonyms = field::selfJoin(object::word, "word_id", "antonymy", "antonym_2_id", "antonym_1_id"); |
@@ -39,6 +38,11 @@ namespace verbly { | |||
39 | const field word::regionalTerms = field::selfJoin(object::word, "word_id", "regionality", "domain_id", "term_id"); | 38 | const field word::regionalTerms = field::selfJoin(object::word, "word_id", "regionality", "domain_id", "term_id"); |
40 | const field word::regionalDomains = field::selfJoin(object::word, "word_id", "regionality", "term_id", "domain_id"); | 39 | const field word::regionalDomains = field::selfJoin(object::word, "word_id", "regionality", "term_id", "domain_id"); |
41 | 40 | ||
41 | field word::forms(inflection category) | ||
42 | { | ||
43 | return field::joinThroughWhere(object::word, "lemma_id", object::form, "lemmas_forms", "form_id", "category", static_cast<int>(category)); | ||
44 | } | ||
45 | |||
42 | word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) | 46 | word::word(const database& db, sqlite3_stmt* row) : db_(&db), valid_(true) |
43 | { | 47 | { |
44 | id_ = sqlite3_column_int(row, 0); | 48 | id_ = sqlite3_column_int(row, 0); |
@@ -78,21 +82,6 @@ namespace verbly { | |||
78 | return notion_; | 82 | return notion_; |
79 | } | 83 | } |
80 | 84 | ||
81 | const lemma& word::getLemma() const | ||
82 | { | ||
83 | if (!valid_) | ||
84 | { | ||
85 | throw std::domain_error("Bad access to uninitialized word"); | ||
86 | } | ||
87 | |||
88 | if (!lemma_) | ||
89 | { | ||
90 | lemma_ = db_->lemmas(lemma::id == lemmaId_).first(); | ||
91 | } | ||
92 | |||
93 | return lemma_; | ||
94 | } | ||
95 | |||
96 | bool word::hasFrames() const | 85 | bool word::hasFrames() const |
97 | { | 86 | { |
98 | if (!valid_) | 87 | if (!valid_) |
@@ -133,26 +122,50 @@ namespace verbly { | |||
133 | return frames_; | 122 | return frames_; |
134 | } | 123 | } |
135 | 124 | ||
136 | std::string word::getBaseForm() const | 125 | void word::initializeFrames() const |
137 | { | 126 | { |
138 | return getLemma().getBaseForm().getText(); | 127 | initializedFrames_ = true; |
128 | frames_ = db_->frames(*this, {}, -1).all(); | ||
139 | } | 129 | } |
140 | 130 | ||
141 | std::vector<std::string> word::getInflections(inflection category) const | 131 | const form& word::getBaseForm() const |
142 | { | 132 | { |
143 | std::vector<std::string> result; | 133 | if (!valid_) |
144 | for (const form& infl : getLemma().getInflections(category)) | ||
145 | { | 134 | { |
146 | result.push_back(infl.getText()); | 135 | throw std::domain_error("Bad access to uninitialized word"); |
147 | } | 136 | } |
148 | 137 | ||
149 | return result; | 138 | if (!forms_.count(inflection::base)) |
139 | { | ||
140 | initializeForm(inflection::base); | ||
141 | } | ||
142 | |||
143 | return forms_.at(inflection::base).front(); | ||
150 | } | 144 | } |
151 | 145 | ||
152 | void word::initializeFrames() const | 146 | bool word::hasInflection(inflection category) const |
153 | { | 147 | { |
154 | initializedFrames_ = true; | 148 | return !getInflections(category).empty(); |
155 | frames_ = db_->frames(*this, {}, -1).all(); | 149 | } |
150 | |||
151 | const std::vector<form>& word::getInflections(inflection category) const | ||
152 | { | ||
153 | if (!valid_) | ||
154 | { | ||
155 | throw std::domain_error("Bad access to uninitialized word"); | ||
156 | } | ||
157 | |||
158 | if (!forms_.count(category)) | ||
159 | { | ||
160 | initializeForm(category); | ||
161 | } | ||
162 | |||
163 | return forms_.at(category); | ||
164 | } | ||
165 | |||
166 | void word::initializeForm(inflection infl) const | ||
167 | { | ||
168 | forms_[infl] = db_->forms(form::words(infl) %= *this, verbly::form::id, -1).all(); | ||
156 | } | 169 | } |
157 | 170 | ||
158 | }; | 171 | }; |
diff --git a/lib/word.h b/lib/word.h index 864cee1..a16319d 100644 --- a/lib/word.h +++ b/lib/word.h | |||
@@ -6,8 +6,8 @@ | |||
6 | #include "field.h" | 6 | #include "field.h" |
7 | #include "filter.h" | 7 | #include "filter.h" |
8 | #include "notion.h" | 8 | #include "notion.h" |
9 | #include "lemma.h" | ||
10 | #include "frame.h" | 9 | #include "frame.h" |
10 | #include "form.h" | ||
11 | 11 | ||
12 | struct sqlite3_stmt; | 12 | struct sqlite3_stmt; |
13 | 13 | ||
@@ -95,17 +95,17 @@ namespace verbly { | |||
95 | 95 | ||
96 | const notion& getNotion() const; | 96 | const notion& getNotion() const; |
97 | 97 | ||
98 | const lemma& getLemma() const; | ||
99 | |||
100 | bool hasFrames() const; | 98 | bool hasFrames() const; |
101 | 99 | ||
102 | const std::vector<frame>& getFrames() const; | 100 | const std::vector<frame>& getFrames() const; |
103 | 101 | ||
104 | // Convenience accessors | 102 | const form& getBaseForm() const; |
103 | |||
104 | bool hasInflection(inflection category) const; | ||
105 | |||
106 | const std::vector<form>& getInflections(inflection category) const; | ||
105 | 107 | ||
106 | std::string getBaseForm() const; | ||
107 | 108 | ||
108 | std::vector<std::string> getInflections(inflection infl) const; | ||
109 | 109 | ||
110 | // Type info | 110 | // Type info |
111 | 111 | ||
@@ -127,9 +127,10 @@ namespace verbly { | |||
127 | // Relationships with other objects | 127 | // Relationships with other objects |
128 | 128 | ||
129 | static const field notions; | 129 | static const field notions; |
130 | static const field lemmas; | ||
131 | static const field frames; | 130 | static const field frames; |
132 | 131 | ||
132 | static field forms(inflection category); | ||
133 | |||
133 | // Relationships with self | 134 | // Relationships with self |
134 | 135 | ||
135 | static const field antonyms; | 136 | static const field antonyms; |
@@ -154,6 +155,7 @@ namespace verbly { | |||
154 | 155 | ||
155 | private: | 156 | private: |
156 | 157 | ||
158 | void initializeForm(inflection category) const; | ||
157 | void initializeFrames() const; | 159 | void initializeFrames() const; |
158 | 160 | ||
159 | bool valid_ = false; | 161 | bool valid_ = false; |
@@ -170,10 +172,9 @@ namespace verbly { | |||
170 | const database* db_; | 172 | const database* db_; |
171 | 173 | ||
172 | mutable notion notion_; | 174 | mutable notion notion_; |
173 | mutable lemma lemma_; | ||
174 | |||
175 | mutable bool initializedFrames_ = false; | 175 | mutable bool initializedFrames_ = false; |
176 | mutable std::vector<frame> frames_; | 176 | mutable std::vector<frame> frames_; |
177 | mutable std::map<inflection, std::vector<form>> forms_; | ||
177 | 178 | ||
178 | }; | 179 | }; |
179 | 180 | ||