Line data Source code
1 : /**
2 : Copyright (c) 2019-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPDbFieldExtensions.h"
25 : #include "SPSqlQuery.h"
26 : #include "SPDbScheme.h"
27 : #include "SPPqHandle.h"
28 : #include "SPSqliteHandle.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::db {
31 :
32 1500 : static Value readPgIntArray(BytesViewNetwork r) {
33 1500 : SPUNUSED auto ndim = r.readUnsigned32();
34 1500 : r.offset(4); // ignored;
35 1500 : SPUNUSED auto oid = r.readUnsigned32();
36 1500 : auto size = r.readUnsigned32();
37 1500 : SPUNUSED auto index = r.readUnsigned32();
38 :
39 1500 : if (size > 0) {
40 1500 : Value ret(Value::Type::ARRAY); ret.getArray().reserve(size);
41 4500 : while (!r.empty()) {
42 3000 : auto width = r.readUnsigned32();
43 3000 : switch (width) {
44 0 : case 1: ret.addInteger(r.readUnsigned()); break;
45 0 : case 2: ret.addInteger(r.readUnsigned16()); break;
46 1500 : case 4: ret.addInteger(r.readUnsigned32()); break;
47 1500 : case 8: ret.addInteger(r.readUnsigned64()); break;
48 0 : default: break;
49 : }
50 : }
51 1500 : return ret;
52 1500 : }
53 0 : return Value();
54 : }
55 :
56 1500 : static bool writePgIntArray(StringStream &query, const Value &val) {
57 1500 : if (val.isArray()) {
58 1500 : query << "'{";
59 1500 : bool init = true;
60 4500 : for (auto &it : val.asArray()) {
61 3000 : if (init) { init = false; } else { query << ","; }
62 3000 : query << it.asInteger();
63 : }
64 1500 : query << "}'";
65 1500 : return true;
66 : }
67 0 : return false;
68 : }
69 :
70 50 : bool FieldIntArray::registerForPostgres(CustomFieldInfo &info) {
71 50 : info.isIndexable = true;
72 50 : info.typeName = "integer[]";
73 750 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
74 750 : return readPgIntArray(BytesViewNetwork(iface.toBytes(field)));
75 50 : };
76 750 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
77 750 : return writePgIntArray(query, val);
78 50 : };
79 0 : info.getIndexName = [] (const FieldCustom &field) {
80 0 : return toString(field.name, "_gin_int");
81 50 : };
82 0 : info.getIndexDefinition = [] (const FieldCustom &field) {
83 0 : return toString("USING GIN ( \"", field.name, "\" gin__int_ops)");
84 50 : };
85 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
86 0 : switch (c) {
87 0 : case db::Comparation::Includes:
88 : case db::Comparation::Equal:
89 : case db::Comparation::IsNotNull:
90 : case db::Comparation::IsNull:
91 0 : return true;
92 : break;
93 0 : default:
94 0 : break;
95 : }
96 0 : return false;
97 50 : };
98 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
99 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
100 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
101 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
102 : } else {
103 0 : if (val.isInteger()) {
104 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "@>", db::sql::SqlQuery::RawString{toString("ARRAY[", val.asInteger(), ']')});
105 0 : } else if (val.isArray()) {
106 0 : StringStream str; str << "ARRAY[";
107 0 : bool init = false;
108 0 : for (auto &it : val.asArray()) {
109 0 : if (it.isInteger()) {
110 0 : if (init) { str << ","; } else { init = true; }
111 0 : str << it.getInteger();
112 : }
113 : }
114 0 : str << "]";
115 0 : if (init) {
116 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "&&", db::sql::SqlQuery::RawString{str.str()});
117 : }
118 0 : }
119 : }
120 50 : };
121 50 : return true;
122 : }
123 :
124 100 : bool FieldIntArray::registerForSqlite(CustomFieldInfo &info) {
125 100 : info.isIndexable = false;
126 100 : info.typeName = "BLOB";
127 2000 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
128 2000 : auto d = BytesViewNetwork(iface.toBytes(field));
129 2000 : return data::read<Interface>(d);
130 100 : };
131 1700 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
132 1700 : auto &it = static_cast<sqlite::SqliteQueryInterface &>(iface);
133 1700 : it.push(query, val, true, false);
134 1700 : return true;
135 100 : };
136 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
137 0 : switch (c) {
138 0 : case db::Comparation::Includes:
139 : case db::Comparation::Equal:
140 : case db::Comparation::IsNotNull:
141 : case db::Comparation::IsNull:
142 0 : return true;
143 : break;
144 0 : default:
145 0 : break;
146 : }
147 0 : return false;
148 100 : };
149 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
150 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
151 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
152 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
153 : } else {
154 0 : if (val.isInteger()) {
155 0 : auto unwrapTable = StringView(toString(s.getName(), "_", f, "_unwrap")).pdup();
156 0 : whi.where(op, db::sql::SqlQuery::Field(unwrapTable, StringView("__unwrap_value")), "=?", Value(val));
157 : }
158 : }
159 100 : };
160 0 : info.writeFrom = [] (const FieldCustom &field, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::SelectFrom &from,
161 : Comparation cmp, const Value &val, const Value &) {
162 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
163 : } else {
164 0 : if (val.isString()) {
165 0 : auto name = toString("sp_unwrap(", s.getName(), ".\"", field.name, "\")");
166 0 : auto unwrapTable = toString(s.getName(), "_", field.name, "_unwrap");
167 0 : from.from(stappler::sql::Query<db::Binder, Interface>::Field(name).as(unwrapTable));
168 0 : }
169 : }
170 100 : };
171 100 : return true;
172 : }
173 :
174 2450 : bool FieldIntArray::transformValue(const db::Scheme &, const Value &obj, Value &val, bool isCreate) const {
175 2450 : if (val.isArray()) {
176 7350 : for (auto &it : val.asArray()) {
177 4900 : if (!it.isInteger()) {
178 0 : return false;
179 : }
180 : }
181 2450 : return true;
182 : }
183 0 : return false;
184 : }
185 :
186 5575 : bool FieldIntArray::isSimpleLayout() const {
187 5575 : return true;
188 : }
189 :
190 50 : bool FieldBigIntArray::registerForPostgres(CustomFieldInfo &info) {
191 50 : info.isIndexable = true;
192 50 : info.typeName = "bigint[]";
193 750 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
194 750 : return readPgIntArray(BytesViewNetwork(iface.toBytes(field)));
195 50 : };
196 750 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
197 750 : return writePgIntArray(query, val);
198 50 : };
199 0 : info.getIndexName = [] (const FieldCustom &field) {
200 0 : return toString(field.name, "_gin_bigint");
201 50 : };
202 0 : info.getIndexDefinition = [] (const FieldCustom &field) {
203 0 : return toString("USING GIN ( \"", field.name, "\" array_ops)");
204 50 : };
205 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
206 0 : switch (c) {
207 0 : case db::Comparation::Includes:
208 : case db::Comparation::Equal:
209 : case db::Comparation::IsNotNull:
210 : case db::Comparation::IsNull:
211 0 : return true;
212 : break;
213 0 : default:
214 0 : break;
215 : }
216 0 : return false;
217 50 : };
218 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
219 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
220 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
221 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
222 : } else {
223 0 : if (val.isInteger()) {
224 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "@>", db::sql::SqlQuery::RawString{toString("ARRAY[", val.asInteger(), "::bigint]")});
225 0 : } else if (val.isArray()) {
226 0 : StringStream str; str << "ARRAY[";
227 0 : bool init = false;
228 0 : for (auto &it : val.asArray()) {
229 0 : if (it.isInteger()) {
230 0 : if (init) { str << ","; } else { init = true; }
231 0 : str << it.getInteger() << "::bigint";
232 : }
233 : }
234 0 : str << "]";
235 0 : if (init) {
236 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "&&", db::sql::SqlQuery::RawString{str.str()});
237 : }
238 0 : }
239 : }
240 50 : };
241 50 : return true;
242 : }
243 :
244 100 : bool FieldBigIntArray::registerForSqlite(CustomFieldInfo &info) {
245 100 : info.isIndexable = false;
246 100 : info.typeName = "BLOB";
247 2000 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
248 2000 : auto d = BytesViewNetwork(iface.toBytes(field));
249 2000 : return data::read<Interface>(d);
250 100 : };
251 1700 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
252 1700 : auto &it = static_cast<sqlite::SqliteQueryInterface &>(iface);
253 1700 : it.push(query, val, true, false);
254 1700 : return true;
255 100 : };
256 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
257 0 : switch (c) {
258 0 : case db::Comparation::Includes:
259 : case db::Comparation::Equal:
260 : case db::Comparation::IsNotNull:
261 : case db::Comparation::IsNull:
262 0 : return true;
263 : break;
264 0 : default:
265 0 : break;
266 : }
267 0 : return false;
268 100 : };
269 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
270 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
271 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
272 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
273 : } else {
274 0 : if (val.isInteger()) {
275 0 : auto unwrapTable = StringView(toString(s.getName(), "_", f, "_unwrap")).pdup();
276 0 : whi.where(op, db::sql::SqlQuery::Field(unwrapTable, StringView("__unwrap_value")), "=?", Value(val));
277 : }
278 : }
279 100 : };
280 0 : info.writeFrom = [] (const FieldCustom &field, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::SelectFrom &from,
281 : Comparation cmp, const Value &val, const Value &) {
282 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
283 : } else {
284 0 : if (val.isString()) {
285 0 : auto name = toString("sp_unwrap(", s.getName(), ".\"", field.name, "\")");
286 0 : auto unwrapTable = toString(s.getName(), "_", field.name, "_unwrap");
287 0 : from.from(stappler::sql::Query<db::Binder, Interface>::Field(name).as(unwrapTable));
288 0 : }
289 : }
290 100 : };
291 100 : return true;
292 : }
293 :
294 2450 : bool FieldBigIntArray::transformValue(const db::Scheme &, const Value &obj, Value &val, bool isCreate) const {
295 2450 : if (val.isArray()) {
296 7350 : for (auto &it : val.asArray()) {
297 4900 : if (!it.isInteger()) {
298 0 : return false;
299 : }
300 : }
301 2450 : return true;
302 : }
303 0 : return false;
304 : }
305 :
306 5575 : bool FieldBigIntArray::isSimpleLayout() const {
307 5575 : return true;
308 : }
309 :
310 50 : bool FieldPoint::registerForPostgres(CustomFieldInfo &info) {
311 50 : info.isIndexable = true;
312 50 : info.typeName = "point";
313 0 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
314 0 : auto r = stappler::BytesViewNetwork(iface.toBytes(field));
315 :
316 0 : if (r.size() == 16) {
317 0 : auto x = r.readFloat64();
318 0 : auto y = r.readFloat64();
319 :
320 : return Value({
321 : Value(x),
322 : Value(y),
323 0 : });
324 : }
325 0 : return Value();
326 50 : };
327 0 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
328 0 : if (val.isArray() && val.size() == 2 && val.isDouble(0) && val.isDouble(1)) {
329 0 : query << std::setprecision(std::numeric_limits<double>::max_digits10) << "point(" << val.getDouble(0) << "," << val.getDouble(1) << ")";
330 0 : return true;
331 : }
332 0 : return false;
333 50 : };
334 0 : info.getIndexName = [] (const FieldCustom &field) {
335 0 : return toString(field.name, "_gist_point");
336 50 : };
337 0 : info.getIndexDefinition = [] (const FieldCustom &field) {
338 0 : return toString("USING GIST( \"", field.name, "\")");
339 50 : };
340 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
341 0 : return c == db::Comparation::Includes || c == db::Comparation::Equal || c == db::Comparation::In;
342 50 : };
343 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
344 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
345 0 : if (val.isArray() && val.size() == 4) {
346 0 : if (whi.state == stappler::sql::Query<db::Binder, Interface>::State::None) {
347 0 : whi.state = stappler::sql::Query<db::Binder, Interface>::State::Some;
348 : } else {
349 0 : Query_writeOperator(whi.query->getStream(), op);
350 : }
351 0 : auto &stream = whi.query->getStream();
352 0 : stream << "(" << s.getName() << ".\"" << f << "\" <@ box '("
353 0 : << std::setprecision(std::numeric_limits<double>::max_digits10)
354 0 : << val.getDouble(0) << "," << val.getDouble(1) << "),(" << val.getDouble(2) << "," << val.getDouble(3) << ")')";
355 : }
356 50 : };
357 50 : return true;
358 : }
359 :
360 100 : bool FieldPoint::registerForSqlite(CustomFieldInfo &info) {
361 100 : info.isIndexable = false;
362 100 : info.typeName = "BLOB";
363 0 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
364 0 : auto d = BytesViewNetwork(iface.toBytes(field));
365 0 : return data::read<Interface>(d);
366 100 : };
367 0 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
368 0 : auto &it = static_cast<sqlite::SqliteQueryInterface &>(iface);
369 0 : it.push(query, val, true, false);
370 0 : return true;
371 100 : };
372 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
373 0 : return false;
374 100 : };
375 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
376 100 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) { };
377 100 : return true;
378 : }
379 :
380 2450 : bool FieldPoint::transformValue(const db::Scheme &, const Value &obj, Value &val, bool isCreate) const {
381 2450 : if (val.isArray() && val.size() == 2 && val.isDouble(0) && val.isDouble(1)) {
382 0 : return true;
383 : }
384 2450 : return false;
385 : }
386 :
387 5575 : bool FieldPoint::isSimpleLayout() const {
388 5575 : return true;
389 : }
390 :
391 50 : bool FieldTextArray::registerForPostgres(CustomFieldInfo &info) {
392 50 : info.isIndexable = true;
393 50 : info.typeName = "text[]";
394 1500 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
395 1500 : auto r = stappler::BytesViewNetwork(iface.toBytes(field));
396 1500 : SPUNUSED auto ndim = r.readUnsigned32();
397 1500 : r.offset(4); // ignored;
398 1500 : SPUNUSED auto oid = r.readUnsigned32();
399 1500 : auto size = r.readUnsigned32();
400 1500 : SPUNUSED auto index = r.readUnsigned32();
401 :
402 1500 : if (size > 0) {
403 1500 : Value ret(Value::Type::ARRAY); ret.getArray().reserve(size);
404 6000 : while (!r.empty()) {
405 4500 : auto size = r.readUnsigned32();
406 4500 : auto str = r.readString(size);
407 :
408 4500 : ret.addString(str);
409 : }
410 1500 : return ret;
411 1500 : }
412 0 : return Value();
413 50 : };
414 750 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
415 750 : if (val.isArray()) {
416 750 : query << "ARRAY[";
417 750 : bool init = true;
418 3000 : for (auto &it : val.asArray()) {
419 2250 : if (init) { init = false; } else { query << ","; }
420 2250 : if (auto q = dynamic_cast<db::pq::PgQueryInterface *>(&iface)) {
421 2250 : q->push(query, it, false, false);
422 : }
423 : }
424 750 : query << "]";
425 750 : return true;
426 : }
427 0 : return false;
428 50 : };
429 25 : info.getIndexName = [] (const FieldCustom &field) {
430 25 : return toString(field.name, "_gin_text");
431 50 : };
432 25 : info.getIndexDefinition = [] (const FieldCustom &field) {
433 25 : return toString("USING GIN ( \"", field.name, "\" array_ops)");
434 50 : };
435 0 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
436 0 : switch (c) {
437 0 : case db::Comparation::Includes:
438 : case db::Comparation::Equal:
439 : case db::Comparation::IsNotNull:
440 : case db::Comparation::IsNull:
441 0 : return true;
442 : break;
443 0 : default:
444 0 : break;
445 : }
446 0 : return false;
447 50 : };
448 0 : info.writeQuery = [] (const FieldCustom &, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
449 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
450 0 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
451 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
452 : } else {
453 0 : if (val.isString()) {
454 0 : if (auto q = dynamic_cast<db::pq::PgQueryInterface *>(whi.query->getBinder().getInterface())) {
455 0 : auto id = q->push(val.asString());
456 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "@>", db::sql::SqlQuery::RawString{toString("ARRAY[$", id, "::text]")});
457 : }
458 0 : } else if (val.isArray()) {
459 0 : if (auto q = dynamic_cast<db::pq::PgQueryInterface *>(whi.query->getBinder().getInterface())) {
460 0 : StringStream str; str << "ARRAY[";
461 0 : bool init = false;
462 0 : for (auto &it : val.asArray()) {
463 0 : if (it.isInteger()) {
464 0 : if (init) { str << ","; } else { init = true; }
465 0 : str << "$" << q->push(val.asString()) << "::text";
466 : }
467 : }
468 0 : str << "]";
469 0 : if (init) {
470 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), "&&", db::sql::SqlQuery::RawString{str.str()});
471 : }
472 0 : }
473 : }
474 : }
475 50 : };
476 50 : return true;
477 : }
478 :
479 100 : bool FieldTextArray::registerForSqlite(CustomFieldInfo &info) {
480 100 : info.isIndexable = false;
481 100 : info.typeName = "BLOB";
482 3125 : info.readFromStorage = [] (const FieldCustom &, const ResultCursor &iface, size_t field) -> Value {
483 3125 : auto d = BytesViewNetwork(iface.toBytes(field));
484 3125 : return data::read<Interface>(d);
485 100 : };
486 1800 : info.writeToStorage = [] (const FieldCustom &, QueryInterface &iface, StringStream &query, const Value &val) -> bool {
487 1800 : auto &it = static_cast<sqlite::SqliteQueryInterface &>(iface);
488 1800 : it.push(query, val, true, false);
489 1800 : return true;
490 100 : };
491 200 : info.isComparationAllowed = [] (const FieldCustom &, Comparation c) {
492 200 : switch (c) {
493 200 : case db::Comparation::Includes:
494 : case db::Comparation::Equal:
495 : case db::Comparation::IsNotNull:
496 : case db::Comparation::IsNull:
497 200 : return true;
498 : break;
499 0 : default:
500 0 : break;
501 : }
502 0 : return false;
503 100 : };
504 100 : info.writeQuery = [] (const FieldCustom &field, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::WhereContinue &whi,
505 : Operator op, const StringView &f, Comparation cmp, const Value &val, const Value &) {
506 100 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
507 0 : whi.where(op, db::sql::SqlQuery::Field(s.getName(), f), cmp, val);
508 : } else {
509 100 : if (val.isString()) {
510 100 : if (auto q = dynamic_cast<db::sqlite::SqliteQueryInterface *>(whi.query->getBinder().getInterface())) {
511 100 : auto id = q->push(val.asString());
512 100 : auto unwrapTable = toString(s.getName(), "_", f, "_unwrap");
513 100 : whi.where(op, db::sql::SqlQuery::Field(unwrapTable, StringView("__unwrap_value")), "=?", Value(id));
514 100 : }
515 : }
516 : }
517 200 : };
518 100 : info.writeFrom = [] (const FieldCustom &field, const Scheme &s, stappler::sql::Query<db::Binder, Interface>::SelectFrom &from,
519 : Comparation cmp, const Value &val, const Value &) {
520 100 : if (cmp == db::Comparation::IsNull || cmp == db::Comparation::IsNotNull) {
521 : } else {
522 100 : if (val.isString()) {
523 100 : auto name = toString("sp_unwrap(", s.getName(), ".\"", field.name, "\")");
524 100 : auto unwrapTable = toString(s.getName(), "_", field.name, "_unwrap");
525 100 : from.from(stappler::sql::Query<db::Binder, Interface>::Field(name).as(unwrapTable));
526 100 : }
527 : }
528 200 : };
529 100 : return true;
530 : }
531 :
532 2550 : bool FieldTextArray::transformValue(const db::Scheme &, const Value &obj, Value &val, bool isCreate) const {
533 2550 : if (val.isArray()) {
534 10075 : for (auto &it : val.asArray()) {
535 7525 : if (!it.isString()) {
536 0 : auto str = it.asString();
537 0 : if (!str.empty()) {
538 0 : it = Value(str);
539 : } else {
540 0 : return false;
541 : }
542 0 : }
543 : }
544 2550 : return true;
545 : }
546 0 : return false;
547 : }
548 :
549 5575 : bool FieldTextArray::isSimpleLayout() const {
550 5575 : return true;
551 : }
552 :
553 : }
|