diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | generator/schema.sql | 2 | ||||
-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 |
24 files changed, 574 insertions, 365 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea601e..5d4f476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -6,7 +6,7 @@ pkg_check_modules(sqlite3 sqlite3>=3.8.3 REQUIRED) | |||
6 | 6 | ||
7 | set(CMAKE_BUILD_TYPE Debug) | 7 | set(CMAKE_BUILD_TYPE Debug) |
8 | 8 | ||
9 | add_library(verbly lib/filter.cpp lib/field.cpp lib/notion.cpp lib/word.cpp lib/frame.cpp lib/part.cpp lib/lemma.cpp lib/form.cpp lib/pronunciation.cpp lib/statement.cpp lib/binding.cpp lib/database.cpp lib/token.cpp lib/part.cpp) | 9 | add_library(verbly lib/filter.cpp lib/field.cpp lib/notion.cpp lib/word.cpp lib/frame.cpp lib/part.cpp lib/form.cpp lib/pronunciation.cpp lib/statement.cpp lib/binding.cpp lib/database.cpp lib/token.cpp lib/part.cpp) |
10 | set_property(TARGET verbly PROPERTY CXX_STANDARD 11) | 10 | set_property(TARGET verbly PROPERTY CXX_STANDARD 11) |
11 | set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON) | 11 | set_property(TARGET verbly PROPERTY CXX_STANDARD_REQUIRED ON) |
12 | target_link_libraries(verbly ${sqlite3_LIBRARIES}) | 12 | target_link_libraries(verbly ${sqlite3_LIBRARIES}) |
diff --git a/generator/schema.sql b/generator/schema.sql index 2ac658c..c07bf57 100644 --- a/generator/schema.sql +++ b/generator/schema.sql | |||
@@ -178,6 +178,8 @@ CREATE TABLE `pronunciations` ( | |||
178 | `stress` VARCHAR(64) NOT NULL | 178 | `stress` VARCHAR(64) NOT NULL |
179 | ); | 179 | ); |
180 | 180 | ||
181 | CREATE INDEX `rhymes_with` ON `pronunciations`(`rhyme`); | ||
182 | |||
181 | CREATE TABLE `forms_pronunciations` ( | 183 | CREATE TABLE `forms_pronunciations` ( |
182 | `form_id` INTEGER NOT NULL, | 184 | `form_id` INTEGER NOT NULL, |
183 | `pronunciation_id` INTEGER NOT NULL | 185 | `pronunciation_id` INTEGER NOT NULL |
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 | ||