LCOV - code coverage report
Current view: top level - core/db - SPDbFieldExtensions.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 202 381 53.0 %
Date: 2024-05-12 00:16:13 Functions: 35 61 57.4 %

          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             : }

Generated by: LCOV version 1.14