LCOV - code coverage report
Current view: top level - core/db/sql - SPSqlQuery.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 381 562 67.8 %
Date: 2024-05-12 00:16:13 Functions: 48 68 70.6 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2018-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 "SPSqlQuery.h"
      25             : #include "SPSqlHandle.h"
      26             : #include "SPSqlDriver.h"
      27             : #include "SPDbScheme.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::db::sql {
      30             : 
      31       17650 : SqlQuery::SqlQuery(db::QueryInterface *iface, const Driver *driver) {
      32       17650 :         binder.setInterface(iface);
      33       17650 :         _driver = driver;
      34       17650 : }
      35             : 
      36        7375 : void SqlQuery::clear() {
      37        7375 :         stream.clear();
      38        7375 :         binder.clear();
      39        7375 : }
      40             : 
      41        2450 : static inline bool SqlQuery_comparationIsValid(const Driver *driver, const Field &f, Comparation comp) {
      42        2450 :         if (f.isIndexed() || comp == Comparation::IsNull || comp == Comparation::IsNotNull) {
      43        2450 :                 if (f.getType() == Type::Custom) {
      44         200 :                         auto c = f.getSlot<FieldCustom>();
      45         200 :                         if (auto info = driver->getCustomFieldInfo(c->getDriverTypeName())) {
      46         200 :                                 return info->isComparationAllowed(*c, comp);
      47             :                         }
      48             :                 } else {
      49        2250 :                         return db::checkIfComparationIsValid(f.getType(), comp, f.getFlags());
      50             :                 }
      51             :         }
      52           0 :         return false;
      53             : }
      54             : 
      55           0 : static inline auto SqlQuery_makeSoftLimitWith(SqlQuery::Context &ictx,
      56             :                 bool isSubField = false, const StringView &lName = StringView(), uint64_t oid = 0) {
      57           0 :         return [ctx = &ictx, isSubField, lName, oid] (SqlQuery::GenericQuery &subq) {
      58           0 :                 SqlQuery &query = *ctx->_this;
      59           0 :                 const Scheme &scheme = *ctx->scheme;
      60             : 
      61           0 :                 auto writeFullTextSel = [&] (SqlQuery::GenericQuery &subQ) {
      62           0 :                         auto sel = subQ.select(SqlQuery::Field(scheme.getName(), "__oid"));
      63           0 :                         auto ftsQuery = ctx->getAlt(ctx->softLimitField);
      64           0 :                         if (!ftsQuery.empty()) {
      65           0 :                                 if (sel.state == SqlQuery::State::Some) {
      66           0 :                                         sel.query->getStream() << ", ";
      67             :                                 }
      68           0 :                                 sel.query->writeBind(db::Binder::FullTextRank{scheme.getName(), scheme.getField(ctx->softLimitField), ftsQuery});
      69           0 :                                 sel.query->getStream() << " AS __ts_rank_" << ctx->softLimitField;
      70           0 :                                 sel.state = SqlQuery::State::Some;
      71             :                         }
      72           0 :                         return sel;
      73           0 :                 };
      74             : 
      75           0 :                 auto sel = (ctx->softLimitIsFts)
      76           0 :                                 ? writeFullTextSel(subq)
      77           0 :                                 : subq.select(SqlQuery::Field(scheme.getName(), "__oid"), SqlQuery::Field(scheme.getName(), ctx->softLimitField));
      78           0 :                 auto s = isSubField ? sel.from(scheme.getName()).innerJoinOn("s", [&] (SqlQuery::WhereBegin &q) {
      79           0 :                         q.where(SqlQuery::Field(scheme.getName(), "__oid"), Comparation::Equal, SqlQuery::Field("s", "id"));
      80           0 :                 }) : sel.from(scheme.getName());
      81             : 
      82           0 :                 if (auto &val = ctx->query->getSoftLimitValue()) {
      83           0 :                         if (ctx->softLimitIsFts) {
      84           0 :                                 auto ftsQuery = ctx->getAlt(ctx->softLimitField);
      85             : 
      86           0 :                                 s.query->getStream() << " WHERE(";
      87           0 :                                 s.query->writeBind(db::Binder::FullTextRank{ctx->scheme->getName(), ctx->scheme->getField(ctx->softLimitField), ftsQuery});
      88           0 :                                 s.query->getStream() << ((ctx->query->getOrdering() == Ordering::Ascending) ? '>' : '<');
      89           0 :                                 s.query->writeBind(val);
      90           0 :                                 s.query->getStream() << ')';
      91             : 
      92           0 :                                 SqlQuery::SelectWhere w(s.query, SqlQuery::State::Some);
      93           0 :                                 if (!lName.empty()) {
      94           0 :                                         w.where(Operator::And, SqlQuery::Field(scheme.getName(), lName), Comparation::Equal, oid);
      95             :                                 }
      96           0 :                                 query.writeWhere(w, Operator::And, scheme, *ctx->query);
      97             : 
      98             :                         } else {
      99           0 :                                 auto w = s.where(SqlQuery::Field(scheme.getName(), ctx->softLimitField),
     100           0 :                                                 ctx->query->getOrdering() == Ordering::Ascending ? Comparation::GreatherThen : Comparation::LessThen, val);
     101           0 :                                 if (!lName.empty()) {
     102           0 :                                         w.where(Operator::And, SqlQuery::Field(scheme.getName(), lName), Comparation::Equal, oid);
     103             :                                 }
     104           0 :                                 query.writeWhere(w, Operator::And, scheme, *ctx->query);
     105             :                         }
     106           0 :                 } else if (ctx->query->hasSelect() || !lName.empty()) {
     107           0 :                         auto w = lName.empty() ? s.where() : s.where(SqlQuery::Field(scheme.getName(), lName), Comparation::Equal, oid);
     108           0 :                         query.writeWhere(w, Operator::And, scheme, *ctx->query);
     109             :                 }
     110             : 
     111           0 :                 query.writeOrdering(s, scheme, *ctx->query);
     112           0 :         };
     113             : }
     114             : 
     115             : template <typename Clause>
     116        8275 : static void SqlQuery_makeCustomFrom(const Driver *driver, SqlQuery &q, Clause &tmp, const Query &query, const Scheme &scheme) {
     117        9800 :         for (auto &it : query.getSelectList()) {
     118        1525 :                 auto f = scheme.getField(it.field);
     119        1525 :                 switch (f->getType()) {
     120         100 :                 case db::Type::Custom: {
     121         100 :                         auto c = f->getSlot<FieldCustom>();
     122         100 :                         if (auto info = driver->getCustomFieldInfo(c->getDriverTypeName())) {
     123         100 :                                 if (info->writeFrom) {
     124         100 :                                         info->writeFrom(*c, scheme, tmp, it.compare, it.value1, it.value2);
     125             :                                 }
     126             :                         }
     127         100 :                         break;
     128             :                 }
     129         300 :                 case db::Type::FullTextView:
     130         300 :                         q.writeFullTextFrom(tmp, scheme, f, it);
     131         300 :                         break;
     132        1125 :                 default:
     133        1125 :                         break;
     134             :                 }
     135             :         }
     136        8275 : }
     137             : 
     138             : template <typename Clause>
     139        6350 : static inline auto SqlQuery_makeWhereClause(const Driver *driver, SqlQuery::Context &ctx, Clause &tmp, const StringView &lName = StringView(), uint64_t oid = 0) {
     140        6350 :         SqlQuery_makeCustomFrom(driver, *ctx._this, tmp, *ctx.query, *ctx.scheme);
     141             : 
     142        6350 :         bool isAsc = ctx.query->getOrdering() == Ordering::Ascending;
     143        6350 :         if (ctx.query->hasSelect() || !ctx.softLimitField.empty() || !lName.empty()) {
     144        6025 :                 if (ctx.softLimitField == "__oid" || !ctx.hasAltLimit) {
     145        6025 :                         if (auto &val = ctx.query->getSoftLimitValue()) {
     146         250 :                                 auto w = tmp.where(SqlQuery::Field(ctx.scheme->getName(), ctx.softLimitField),
     147         250 :                                                 isAsc ? Comparation::GreatherThen : Comparation::LessThen, val.asInteger());
     148         250 :                                 if (!lName.empty()) {
     149           0 :                                         w.where(Operator::And, SqlQuery::Field(ctx.scheme->getName(), lName), Comparation::Equal, oid);
     150             :                                 }
     151         250 :                                 ctx._this->writeWhere(w, Operator::And, *ctx.scheme, *ctx.query);
     152        5775 :                         } else if (ctx.query->hasSelect() || !lName.empty()) {
     153        5775 :                                 auto w = lName.empty() ? tmp.where() : tmp.where(SqlQuery::Field(ctx.scheme->getName(), lName), Comparation::Equal, oid);
     154        5775 :                                 ctx._this->writeWhere(w, Operator::And, *ctx.scheme, *ctx.query);
     155             :                         }
     156           0 :                 } else if (ctx.softLimitField.empty()) {
     157           0 :                         if (ctx.query->hasSelect() || !lName.empty()) {
     158           0 :                                 auto whi = lName.empty() ? tmp.where() : tmp.where(SqlQuery::Field(ctx.scheme->getName(), lName), Comparation::Equal, oid);
     159           0 :                                 ctx._this->writeWhere(whi, db::Operator::And, *ctx.scheme, *ctx.query);
     160             :                         }
     161             :                 } else {
     162           0 :                         auto softLimitFieldStr = (ctx.softLimitIsFts
     163           0 :                                 ? toString("__ts_rank_", ctx.softLimitField)
     164             :                                 : ctx.softLimitField.str<Interface>());
     165             : 
     166             :                         // write soft limit query like WHERE __oid IN (SELECT __oid FROM u) OR (field = (SELECT MAX(mtime) FROM u))
     167           0 :                         tmp.where(SqlQuery::Field(ctx.scheme->getName(), "__oid"), Comparation::In, Callback<void(SqlQuery::Select &)>([&] (SqlQuery::Select &subQ) {
     168           0 :                                 subQ.field(SqlQuery::Field("u", "__oid")).from("u").finalize();
     169           0 :                         })).parenthesis(Operator::Or, [&] (SqlQuery::WhereBegin &whi) {
     170           0 :                                 if (ctx.softLimitIsFts) {
     171           0 :                                         auto ftsQuery = ctx.getAlt(ctx.softLimitField);
     172             : 
     173           0 :                                         whi.query->getStream() << '(';
     174           0 :                                         whi.query->writeBind(db::Binder::FullTextRank{ctx.scheme->getName(), ctx.scheme->getField(ctx.softLimitField), ftsQuery});
     175           0 :                                         whi.query->getStream() << "=(SELECT " << (isAsc ? "MAX" : "MIN") << "(u.\"__ts_rank_" << ctx.softLimitField << "\") FROM u))";
     176             : 
     177           0 :                                         SqlQuery::WhereContinue w(whi.query, SqlQuery::State::Some);
     178           0 :                                         if (!lName.empty()) {
     179           0 :                                                 w.where(Operator::And, SqlQuery::Field(ctx.scheme->getName(), lName), Comparation::Equal, oid);
     180             :                                         }
     181           0 :                                         ctx._this->writeWhere(w, Operator::And, *ctx.scheme, *ctx.query);
     182             :                                 } else {
     183           0 :                                         auto w = whi.where(SqlQuery::Field(ctx.scheme->getName(), ctx.softLimitField), Comparation::Equal,
     184           0 :                                                         Callback<void(SqlQuery::Select &)>([&] (SqlQuery::Select &subQ) {
     185           0 :                                                 subQ.aggregate(isAsc ? "MAX" : "MIN", SqlQuery::Field("u", softLimitFieldStr)).from("u").finalize();
     186             :                                         }));
     187           0 :                                         if (!lName.empty()) {
     188           0 :                                                 w.where(Operator::And, SqlQuery::Field(ctx.scheme->getName(), lName), Comparation::Equal, oid);
     189             :                                         }
     190           0 :                                         ctx._this->writeWhere(w, Operator::And, *ctx.scheme, *ctx.query);
     191             :                                 }
     192             :                         });
     193           0 :                 }
     194             :         }
     195        6350 : }
     196             : 
     197        6200 : bool SqlQuery::writeQuery(Context &ctx) {
     198        6200 :         auto sel = (ctx.hasAltLimit)
     199           0 :                 ? with("u", SqlQuery_makeSoftLimitWith(ctx, false)).select()
     200        6200 :                 : select();
     201        6200 :         auto s = writeSelectFrom(sel, ctx);
     202             : 
     203        6200 :         SqlQuery_makeWhereClause(_driver, ctx, s);
     204             : 
     205        6200 :         writeOrdering(s, *ctx.scheme, *ctx.query, ctx.hasAltLimit);
     206        6200 :         if (ctx.query->isForUpdate()) { s.forUpdate(); }
     207        6200 :         s.finalize();
     208             : 
     209        6200 :         return true;
     210             : }
     211             : 
     212         150 : bool SqlQuery::writeQuery(Context &ctx, const db::Scheme &scheme, uint64_t oid, const db::Field &f) {
     213         150 :         StringView lName;
     214         150 :         if (f.getType() == Type::Set && !f.isReference()) {
     215          25 :                 if (auto l = scheme.getForeignLink(f)) {
     216          25 :                         lName = l->getName();
     217             :                 }
     218             :         }
     219             : 
     220         150 :         auto writeFields = [&, this] (Select &sel) {
     221         150 :                 writeFullTextRank(sel, *ctx.scheme, *ctx.query);
     222         150 :                 if (ctx.shouldIncludeAll()) {
     223           0 :                         sel = sel.field(SqlQuery::Field(ctx.scheme->getName(), "*"));
     224             :                 } else {
     225         150 :                         ctx.readFields([&] (const StringView &name, const db::Field *) {
     226         150 :                                 sel = sel.field(SqlQuery::Field(ctx.scheme->getName(), name));
     227         150 :                         });
     228             :                 }
     229         150 :         };
     230             : 
     231         300 :         auto writeSelect = [&, this] () -> Select {
     232         150 :                 if (f.getType() == Type::View || (f.getType() == Type::Set && f.isReference())) {
     233         125 :                         auto wtmp = with("s", [&] (SqlQuery::GenericQuery &q) {
     234         125 :                                 q.select(SqlQuery::Distinct::Distinct, SqlQuery::Field(toString(ctx.scheme->getName(), "_id")).as("id"))
     235         250 :                                         .from(toString(scheme.getName(), "_f_", f.getName(), (f.getType() == Type::View ? "_view" : "")))
     236         250 :                                         .where(toString(scheme.getName(), "_id"), Comparation::Equal, oid);
     237         125 :                         });
     238             : 
     239         125 :                         if (ctx.hasAltLimit) {
     240           0 :                                 wtmp.with("u", SqlQuery_makeSoftLimitWith(ctx, true, lName, oid));
     241             :                         }
     242             : 
     243         125 :                         return wtmp.select();
     244             :                 } else {
     245          25 :                         return (ctx.hasAltLimit)
     246          25 :                                 ? with("u", SqlQuery_makeSoftLimitWith(ctx, false, lName, oid)).select()
     247          25 :                                 : select();
     248             :                 }
     249         150 :         };
     250             : 
     251         150 :         auto sel = writeSelect();
     252         150 :         writeFields(sel);
     253             : 
     254         225 :         auto tmp = (f.getType() == Type::View || (f.getType() == Type::Set && f.isReference()))
     255         200 :                 ? sel.from(ctx.scheme->getName()).innerJoinOn("s", [&] (SqlQuery::WhereBegin &q) {
     256         125 :                                 q.where(SqlQuery::Field(ctx.scheme->getName(), "__oid"), Comparation::Equal, SqlQuery::Field("s", "id"));
     257         125 :                         })
     258         150 :                 : sel.from(ctx.scheme->getName());
     259             : 
     260         150 :         SqlQuery_makeWhereClause(_driver, ctx, tmp, lName, oid);
     261             : 
     262         150 :         writeOrdering(tmp, *ctx.scheme, *ctx.query, ctx.hasAltLimit);
     263         150 :         if (ctx.query->isForUpdate()) { tmp.forUpdate(); }
     264         150 :         finalize();
     265             : 
     266         150 :         return true;
     267             : }
     268             : 
     269        7775 : void SqlQuery::writeWhere(SqlQuery::SelectWhere &w, db::Operator op, const db::Scheme &scheme, const db::Query &q) {
     270        7775 :         SqlQuery::WhereContinue iw(w.query, w.state);
     271        7775 :         writeWhere(iw, op, scheme, q);
     272        7775 : }
     273             : 
     274        7775 : void SqlQuery::writeWhere(SqlQuery::WhereContinue &w, db::Operator op, const db::Scheme &scheme, const db::Query &q) {
     275        7775 :         if (q.getSingleSelectId()) {
     276        5900 :                 w.where(op, "__oid", db::Comparation::Equal, q.getSingleSelectId());
     277        1875 :         } else if (!q.getSelectIds().empty()) {
     278         125 :                 w.where(op, SqlQuery::Field(scheme.getName(), "__oid"), db::Comparation::In, q.getSelectIds());
     279        1750 :         } else if (!q.getSelectAlias().empty()) {
     280         300 :                 w.parenthesis(op, [&] (SqlQuery::WhereBegin &wh) {
     281         300 :                         auto whi = wh.where();
     282        4175 :                         for (auto &it : scheme.getFields()) {
     283        3875 :                                 if (it.second.getType() == db::Type::Text && it.second.getTransform() == db::Transform::Alias) {
     284         300 :                                         whi.where(db::Operator::Or, SqlQuery::Field(scheme.getName(), it.first), db::Comparation::Equal, q.getSelectAlias());
     285             :                                 }
     286             :                         }
     287         300 :                 });
     288        1450 :         } else if (q.hasSelectName()) {
     289             :                 // failsafe
     290           0 :                 w.parenthesis(op, [&] (SqlQuery::WhereBegin &wh) {
     291           0 :                         auto whi = wh.where();
     292           0 :                         whi.where(db::Operator::Or, SqlQuery::Field(scheme.getName(), "__oid"), db::Comparation::Equal, Value(0));
     293           0 :                 });
     294        1450 :         } else if (q.getSelectList().size() > 0) {
     295        1425 :                 w.parenthesis(op, [&, this] (SqlQuery::WhereBegin &wh) {
     296        1425 :                         auto whi = wh.where();
     297        2950 :                         for (auto &it : q.getSelectList()) {
     298        1525 :                                 writeWhereItem(whi, db::Operator::And, scheme, it);
     299             :                         }
     300        1425 :                 });
     301             :         }
     302        7775 : }
     303             : 
     304        1225 : static void SqlQuery_writeWhereData(const Driver *driver, SqlQuery::WhereContinue &whi, db::Operator op, const db::Scheme &scheme, const db::Field &f,
     305             :                 Comparation compare, const Value &value1, const Value &value2) {
     306        1225 :         if (SqlQuery_comparationIsValid(driver, f, compare)) {
     307        1225 :                 auto type = f.getType();
     308        1225 :                 if (type == Type::Custom) {
     309         100 :                         auto c = f.getSlot<FieldCustom>();
     310         100 :                         if (auto info = driver->getCustomFieldInfo(c->getDriverTypeName())) {
     311         100 :                                 info->writeQuery(*c, scheme, whi, op, f.getName(), compare, value1, value2);
     312             :                         }
     313             :                 } else {
     314         750 :                         if ((compare == Comparation::Equal || compare == db::Comparation::NotEqual)
     315         375 :                                         && (type == Type::Integer || type == Type::Float || type == Type::Object || type == Type::Text)
     316        2250 :                                         && value1.isArray()) {
     317           0 :                                 switch (type) {
     318           0 :                                 case Type::Integer:
     319             :                                 case Type::Object: {
     320           0 :                                         Vector<int64_t> vec;
     321           0 :                                         for (auto &it : value1.asArray()) {
     322           0 :                                                 vec.emplace_back(it.getInteger());
     323             :                                         }
     324           0 :                                         whi.where(op, SqlQuery::Field(scheme.getName(), f.getName()),
     325             :                                                         (compare == Comparation::Equal) ? Comparation::In : Comparation::NotIn, vec);
     326           0 :                                         break;
     327           0 :                                 }
     328           0 :                                 case Type::Float: {
     329           0 :                                         Vector<double> vec;
     330           0 :                                         for (auto &it : value1.asArray()) {
     331           0 :                                                 vec.emplace_back(it.getDouble());
     332             :                                         }
     333           0 :                                         whi.where(op, SqlQuery::Field(scheme.getName(), f.getName()),
     334             :                                                         (compare == Comparation::Equal) ? Comparation::In : Comparation::NotIn, vec);
     335           0 :                                         break;
     336           0 :                                 }
     337           0 :                                 case Type::Text: {
     338           0 :                                         Vector<StringView> vec;
     339           0 :                                         for (auto &it : value1.asArray()) {
     340           0 :                                                 vec.emplace_back(it.getString());
     341             :                                         }
     342           0 :                                         whi.where(op, SqlQuery::Field(scheme.getName(), f.getName()),
     343             :                                                         (compare == Comparation::Equal) ? Comparation::In : Comparation::NotIn, vec);
     344           0 :                                         break;
     345           0 :                                 }
     346           0 :                                 default:
     347           0 :                                         break;
     348             :                                 }
     349             :                         } else {
     350        1125 :                                 whi.where(op, SqlQuery::Field(scheme.getName(), f.getName()), compare, value1, value2);
     351             :                         }
     352             :                 }
     353             :         } else {
     354           0 :                 driver->getApplicationInterface()->error("Sql", "Condition is not applicable", Value({
     355           0 :                         stappler::pair("scheme", Value(scheme.getName())),
     356           0 :                         stappler::pair("field", Value(f.getName())),
     357           0 :                         stappler::pair("cmp", Value(encodeComparation(compare).first)),
     358           0 :                 }));
     359             :         }
     360        1225 : }
     361             : 
     362        1525 : void SqlQuery::writeWhereItem(SqlQuery::WhereContinue &whi, db::Operator op, const db::Scheme &scheme, const db::Query::Select &sel) {
     363        1525 :         if (auto f = scheme.getField(sel.field)) {
     364        1525 :                 auto type = f->getType();
     365        1525 :                 if (type == db::Type::FullTextView && sel.compare != db::Comparation::IsNull && sel.compare != db::Comparation::IsNotNull) {
     366         300 :                         auto ftsQuery = getFullTextQuery(scheme, *f, sel);
     367         300 :                         if (!ftsQuery.empty()) {
     368         300 :                                 writeFullTextWhere(whi, op, scheme, sel, ftsQuery);
     369             :                         }
     370        1525 :                 } else if (SqlQuery_comparationIsValid(_driver, *f, sel.compare)) {
     371        1225 :                         SqlQuery_writeWhereData(_driver, whi, op, scheme, *f, sel.compare, sel.value1, sel.value2);
     372             :                 }
     373             :         }
     374        1525 : }
     375             : 
     376           0 : void SqlQuery::writeWhereCond(SqlQuery::WhereContinue &whi, db::Operator op, const db::Scheme &scheme, const db::Worker::ConditionData &sel) {
     377           0 :         SqlQuery_writeWhereData(_driver, whi, op, scheme, *sel.field, sel.compare, sel.value1, sel.value2);
     378           0 : }
     379             : 
     380        8325 : void SqlQuery::writeOrdering(SqlQuery::SelectFrom &s, const db::Scheme &scheme, const db::Query &q, bool dropLimits) {
     381        8325 :         if (q.hasOrder() || q.hasLimit() || q.hasOffset()) {
     382        1175 :                 auto ordering = q.getOrdering();
     383        1175 :                 String orderField;
     384        1175 :                 String schemeName = scheme.getName().str<Interface>();
     385        1175 :                 if (q.hasOrder()) {
     386         925 :                         if (auto f = scheme.getField(q.getOrderField())) {
     387         925 :                                 if (f->getType() == db::Type::FullTextView) {
     388         275 :                                         orderField = toString("__ts_rank_", q.getOrderField());
     389         275 :                                         schemeName.clear();
     390             :                                 } else {
     391         650 :                                         orderField = q.getOrderField();
     392             :                                 }
     393             :                         } else {
     394           0 :                                 return;
     395             :                         }
     396         250 :                 } else if (q.getSelectList().size() == 1) {
     397         125 :                         orderField = q.getSelectList().back().field;
     398         125 :                         if (!scheme.getField(orderField)) {
     399           0 :                                 return;
     400             :                         }
     401             :                 } else {
     402         125 :                         orderField = "__oid";
     403             :                 }
     404             : 
     405        1175 :                 SelectOrder o = s.order(ordering, schemeName.empty() ? SqlQuery::Field(orderField) : SqlQuery::Field(scheme.getName(), orderField),
     406             :                                 ordering == db::Ordering::Descending ? stappler::sql::Nulls::Last : stappler::sql::Nulls::None);
     407             : 
     408        1175 :                 if (!dropLimits) {
     409        1175 :                         if (q.hasLimit() && q.hasOffset()) {
     410          50 :                                 o.limit(q.getLimitValue(), q.getOffsetValue());
     411        1125 :                         } else if (q.hasLimit()) {
     412         825 :                                 o.limit(q.getLimitValue());
     413         300 :                         } else if (q.hasOffset()) {
     414           0 :                                 o.offset(q.getOffsetValue());
     415             :                         }
     416             :                 }
     417        1175 :         }
     418             : }
     419             : 
     420        1975 : void SqlQuery::writeQueryReqest(SqlQuery::SelectFrom &s, const db::QueryList::Item &item) {
     421        1975 :         auto &q = item.query;
     422        1975 :         if (!item.all && !item.query.empty()) {
     423        1550 :                 SqlQuery_makeCustomFrom(_driver, *this, s, item.query, *item.scheme);
     424             : 
     425        1550 :                 auto w = s.where();
     426        1550 :                 writeWhere(w, db::Operator::And, *item.scheme, q);
     427             :         }
     428             : 
     429        1975 :         writeOrdering(s, *item.scheme, q);
     430        1975 : }
     431             : 
     432         150 : static void SqlQuery_writeJoin(SqlQuery::SelectFrom &s, const StringView &sqName, const StringView &schemeName, const db::QueryList::Item &item) {
     433         150 :         s.innerJoinOn(sqName, [&] (SqlQuery::WhereBegin &w) {
     434         275 :                 StringView fieldName = item.ref
     435         125 :                                 ? ( item.ref->getType() == db::Type::Set ? StringView("__oid") : item.ref->getName() )
     436         225 :                                 : StringView("__oid");
     437         150 :                 w.where(SqlQuery::Field(schemeName, fieldName), db::Comparation::Equal, SqlQuery::Field(sqName, "id"));
     438         150 :         });
     439         150 : }
     440             : 
     441        1975 : auto SqlQuery::writeSelectFrom(GenericQuery &q, const db::QueryList::Item &item, bool idOnly,
     442             :                 const StringView &schemeName, const StringView &fieldName, bool isSimpleGet) -> SelectFrom {
     443        1975 :         if (idOnly) {
     444         900 :                 auto sel = q.select();
     445         900 :                 sel.field(SqlQuery::Field(schemeName, fieldName).as("id"));
     446         900 :                 writeFullTextRank(sel, *item.scheme, item.query);
     447         900 :                 return sel.from(schemeName);
     448             :         }
     449             : 
     450        1075 :         auto sel = q.select();
     451        1075 :         writeFullTextRank(sel, *item.scheme, item.query);
     452        1075 :         FieldResolver resv(*item.scheme, item.query, item.getQueryFields());
     453        1075 :         resv.readFields([&] (const StringView &name, const db::Field *) {
     454        1400 :                 sel = sel.field(SqlQuery::Field(schemeName, name));
     455        1400 :         }, isSimpleGet);
     456        1075 :         return sel.from(schemeName);
     457        1075 : }
     458             : 
     459        6200 : auto SqlQuery::writeSelectFrom(Select &sel, Context &ctx) -> SelectFrom {
     460        6200 :         writeFullTextRank(sel, *ctx.scheme, *ctx.query);
     461        6200 :         ctx.readFields([&] (const StringView &name, const db::Field *) {
     462       18700 :                 sel = sel.field(SqlQuery::Field(name));
     463       18700 :         });
     464        6200 :         return sel.from(ctx.scheme->getName());
     465             : }
     466             : 
     467        1975 : void SqlQuery::writeQueryListItem(GenericQuery &q, const db::QueryList &list, size_t idx, bool idOnly, const db::Field *field, bool forSubquery) {
     468        1975 :         auto &items = list.getItems();
     469        1975 :         const db::QueryList::Item &item = items.at(idx);
     470        1975 :         const db::Field *sourceField = nullptr;
     471        1975 :         const db::FieldView *viewField = nullptr;
     472        1975 :         String refQueryTag;
     473        1975 :         if (idx > 0) {
     474         150 :                 sourceField = items.at(idx - 1).field;
     475             :         }
     476             : 
     477        1975 :         if (idx > 0 && !item.ref && sourceField && sourceField->getType() != db::Type::Object) {
     478          25 :                 String prevSq = toString("sq", idx - 1);
     479          25 :                 const db::QueryList::Item &prevItem = items.at(idx - 1);
     480             : 
     481          25 :                 if (sourceField->getType() == db::Type::View) {
     482           0 :                         viewField = static_cast<const db::FieldView *>(sourceField->getSlot());
     483             :                 }
     484             :                 String tname = viewField
     485          25 :                                 ? toString(prevItem.scheme->getName(), "_f_", prevItem.field->getName(), "_view")
     486          25 :                                 : toString(prevItem.scheme->getName(), "_f_", prevItem.field->getName());
     487             : 
     488          25 :                 String targetIdField = toString(item.scheme->getName(), "_id");
     489          25 :                 String sourceIdField = toString(prevItem.scheme->getName(), "_id");
     490             : 
     491          25 :                 if (idOnly && item.query.empty()) { // optimize id-only empty request
     492           0 :                         q.select(SqlQuery::Field(targetIdField).as("id"))
     493           0 :                                         .from(tname)
     494           0 :                                         .innerJoinOn(prevSq, [&] (WhereBegin &w) {
     495           0 :                                 w.where(sourceIdField, stappler::sql::Comparation::Equal, SqlQuery::Field(prevSq, "id"));
     496           0 :                         });
     497           0 :                         return;
     498             :                 }
     499             : 
     500          25 :                 refQueryTag = toString("sq", idx, "_ref");
     501          25 :                 q.with(refQueryTag, [&] (GenericQuery &sq) {
     502          25 :                         sq.select(SqlQuery::Field(targetIdField).as("id"))
     503          50 :                                         .from(tname).innerJoinOn(prevSq, [&] (WhereBegin &w) {
     504          50 :                                 w.where(SqlQuery::Field(sourceIdField), stappler::sql::Comparation::Equal, SqlQuery::Field(prevSq, "id"));
     505          25 :                         });
     506          25 :                 });
     507          25 :         }
     508             : 
     509        1975 :         const db::Field * f = field?field:item.field;
     510             : 
     511        1975 :         StringView schemeName(item.scheme->getName());
     512         675 :         StringView fieldName( (f && (
     513         850 :                 (f->getType() == db::Type::Object && (forSubquery || !idOnly || idx + 1 == items.size()))
     514         600 :                 || f->isFile()))
     515        2050 :                         ? f->getName()
     516        1975 :                         : StringView("__oid") );
     517             : 
     518        1975 :         auto s = writeSelectFrom(q, item, idOnly, schemeName, fieldName, list.hasFlag(db::QueryList::SimpleGet));
     519        1975 :         if (idx > 0) {
     520         150 :                 if (refQueryTag.empty()) {
     521         125 :                         SqlQuery_writeJoin(s, toString("sq", idx - 1), item.scheme->getName(), item);
     522             :                 } else {
     523          25 :                         SqlQuery_writeJoin(s, refQueryTag, item.scheme->getName(), item);
     524             :                 }
     525             :         }
     526        1975 :         writeQueryReqest(s, item);
     527        1975 : }
     528             : 
     529        1900 : void SqlQuery::writeQueryList(const db::QueryList &list, bool idOnly, size_t count) {
     530        1900 :         const db::QueryList::Item &item = list.getItems().back();
     531        1900 :         if (item.query.hasDelta() && list.isDeltaApplicable()) {
     532          75 :                 if (!list.isView()) {
     533          50 :                         writeQueryDelta(*item.scheme, stappler::Time::microseconds(item.query.getDeltaToken()), item.fields.getResolves(), false);
     534             :                 } else {
     535          25 :                         writeQueryViewDelta(list, stappler::Time::microseconds(item.query.getDeltaToken()), item.fields.getResolves(), false);
     536             :                 }
     537          75 :                 return;
     538        1825 :         } else if (item.query.hasDelta()) {
     539           0 :                 list.getApplicationInterface()->error("Query", "Delta is not applicable for this query");
     540             :         }
     541             : 
     542        1825 :         auto &items = list.getItems();
     543        1825 :         count = std::min(items.size(), count);
     544             : 
     545        1825 :         GenericQuery q(this);
     546        1825 :         size_t i = 0;
     547        1825 :         if (count > 0) {
     548        1975 :                 for (; i < count - 1; ++ i) {
     549         150 :                         q.with(toString("sq", i), [&, this] (GenericQuery &sq) {
     550         150 :                                 writeQueryListItem(sq, list, i, true, nullptr, true);
     551         150 :                         });
     552             :                 }
     553             :         }
     554             : 
     555        1825 :         writeQueryListItem(q, list, i, idOnly, nullptr, false);
     556             : }
     557             : 
     558           0 : void SqlQuery::writeQueryFile(const ApplicationInterface *app, const db::QueryList &list, const db::Field *field) {
     559           0 :         auto &items = list.getItems();
     560           0 :         auto count = items.size();
     561           0 :         GenericQuery q(this);
     562           0 :         for (size_t i = 0; i < count - 1; ++ i) {
     563           0 :                 q.with(toString("sq", i), [&, this] (GenericQuery &sq) {
     564           0 :                         writeQueryListItem(sq, list, i, true);
     565           0 :                 });
     566             :         }
     567             : 
     568           0 :         q.with(toString("sq", count - 1), [&, this] (GenericQuery &sq) {
     569           0 :                 writeQueryListItem(sq, list, count - 1, true, field);
     570           0 :         });
     571             : 
     572           0 :         auto fileScheme = app->getFileScheme();
     573           0 :         q.select(SqlQuery::Field::all(fileScheme->getName()))
     574           0 :                         .from(fileScheme->getName())
     575           0 :                         .innerJoinOn(toString("sq", count - 1), [&] (SqlQuery::WhereBegin &w) {
     576           0 :                 w.where(SqlQuery::Field(fileScheme->getName(), "__oid"), db::Comparation::Equal, SqlQuery::Field(toString("sq", count - 1), "id"));
     577           0 :         });
     578           0 : }
     579             : 
     580           0 : void SqlQuery::writeQueryArray(const db::QueryList &list, const db::Field *field) {
     581           0 :         auto &items = list.getItems();
     582           0 :         auto count = items.size();
     583           0 :         GenericQuery q(this);
     584           0 :         for (size_t i = 0; i < count; ++ i) {
     585           0 :                 q.with(toString("sq", i), [&, this] (GenericQuery &sq) {
     586           0 :                         writeQueryListItem(sq, list, i, true);
     587           0 :                 });
     588             :         }
     589             : 
     590           0 :         auto scheme = items.back().scheme;
     591             : 
     592           0 :         q.select(SqlQuery::Field("t", "data"))
     593           0 :                         .from(SqlQuery::Field(toString(scheme->getName(), "_f_", field->getName())).as("t"))
     594           0 :                         .innerJoinOn(toString("sq", count - 1), [&] (SqlQuery::WhereBegin &w) {
     595           0 :                 w.where(SqlQuery::Field("t", toString(scheme->getName(), "_id")), db::Comparation::Equal, SqlQuery::Field(toString("sq", count - 1), "id"));
     596           0 :         });
     597           0 : }
     598             : 
     599          75 : void SqlQuery::writeQueryDelta(const db::Scheme &scheme, const stappler::Time &time, const Set<const db::Field *> &fields, bool idOnly) {
     600          75 :         GenericQuery q(this);
     601          75 :         auto s = q.with("d", [&] (SqlQuery::GenericQuery &sq) {
     602          75 :                 sq.select()
     603          75 :                         .aggregate("max", Field("time").as("time"))
     604          75 :                         .aggregate("max", Field("action").as("action"))
     605          75 :                         .field("object")
     606          75 :                         .from(SqlHandle::getNameForDelta(scheme))
     607          75 :                         .where("time", db::Comparation::GreatherThen, time.toMicroseconds())
     608          75 :                         .group("object")
     609          75 :                         .order(db::Ordering::Descending, "time");
     610         150 :         }).select();
     611          75 :         if (!idOnly) {
     612          75 :                 FieldResolver resv(scheme, fields);
     613          75 :                 resv.readFields([&] (const StringView &name, const db::Field *field) {
     614          75 :                         s.field(SqlQuery::Field("t", name));
     615          75 :                 });
     616          75 :         } else {
     617           0 :                 s.field(Field("t", "__oid"));
     618             :         }
     619          75 :         s.fields(Field("d", "action").as("__d_action"), Field("d", "time").as("__d_time"), Field("d", "object").as("__d_object"))
     620          75 :                 .from(Field(scheme.getName()).as("t"))
     621          75 :                 .rightJoinOn("d", [&] (SqlQuery::WhereBegin &w) {
     622          75 :                         w.where(Field("d", "object"), db::Comparation::Equal, Field("t", "__oid"));
     623          75 :         });
     624          75 : }
     625             : 
     626          50 : void SqlQuery::writeQueryViewDelta(const db::QueryList &list, const stappler::Time &time, const Set<const db::Field *> &fields, bool idOnly) {
     627          50 :         auto &items = list.getItems();
     628          50 :         const db::QueryList::Item &item = items.back();
     629          50 :         auto prevScheme = items.size() > 1 ? items.at(items.size() - 2).scheme : nullptr;
     630          50 :         auto viewField = items.size() > 1 ? items.at(items.size() - 2).field : items.back().field;
     631          50 :         auto view = static_cast<const db::FieldView *>(viewField->getSlot());
     632             : 
     633          50 :         GenericQuery q(this);
     634          50 :         const db::Scheme &scheme = *item.scheme;
     635          50 :         String deltaName = toString(prevScheme->getName(), "_f_", view->name, "_delta");
     636          50 :         String viewName = toString(prevScheme->getName(), "_f_", view->name, "_view");
     637          50 :         auto s = q.with("dv", [&] (SqlQuery::GenericQuery &sq) {
     638          50 :                 uint64_t id = 0;
     639          50 :                 String sqName;
     640             :                 // optimize id-only
     641          50 :                 if (items.size() != 2 || items.front().query.getSingleSelectId() == 0) {
     642           0 :                         size_t i = 0;
     643           0 :                         for (; i < items.size() - 1; ++ i) {
     644           0 :                                 sq.with(toString("sq", i), [&, this] (GenericQuery &sq) {
     645           0 :                                         writeQueryListItem(sq, list, i, true);
     646           0 :                                 });
     647             :                         }
     648           0 :                         sqName = toString("sq", i - 1);
     649             :                 } else {
     650          50 :                         id = items.front().query.getSingleSelectId();
     651             :                 }
     652             : 
     653          50 :                 sq.with("d", [&] (SqlQuery::GenericQuery &sq) {
     654          50 :                         if (id) {
     655          50 :                                 sq.select()
     656          50 :                                         .aggregate("max", Field("time").as("time"))
     657          50 :                                         .field("object")
     658          50 :                                         .field("tag")
     659          50 :                                         .from(deltaName)
     660          50 :                                         .where(SqlQuery::Field("tag"), db::Comparation::Equal, id)
     661         100 :                                         .where(db::Operator::And, "time", db::Comparation::GreatherThen, time.toMicroseconds())
     662          50 :                                         .group("object").field("tag");
     663             :                         } else {
     664           0 :                                 sq.select()
     665           0 :                                         .aggregate("max", Field("time").as("time"))
     666           0 :                                         .field("object")
     667           0 :                                         .field(Field(sqName, "id").as("tag"))
     668           0 :                                         .from(deltaName)
     669           0 :                                         .innerJoinOn(sqName, [&] (SqlQuery::WhereBegin &w) {
     670           0 :                                                 w.where(SqlQuery::Field(deltaName, "tag"), db::Comparation::Equal, SqlQuery::Field(sqName, "id"));
     671           0 :                                 })
     672           0 :                                         .where("time", db::Comparation::GreatherThen, time.toMicroseconds())
     673           0 :                                         .group("object").field(SqlQuery::Field(sqName, "id"));;
     674             :                         }
     675         100 :                 }).select().fields(Field("d", "time"), Field("d", "object"), Field("__vid"))
     676         100 :                         .from(viewName).rightJoinOn("d", [&] (SqlQuery::WhereBegin &w) {
     677          50 :                                 w.where(SqlQuery::Field("d", "tag"), db::Comparation::Equal, SqlQuery::Field(viewName, toString(prevScheme->getName(), "_id")))
     678         100 :                                                 .where(db::Operator::And, SqlQuery::Field("d", "object"), db::Comparation::Equal, SqlQuery::Field(viewName, toString(scheme.getName(), "_id")));
     679          50 :                         });
     680         100 :         }).select();
     681             : 
     682          50 :         if (!idOnly) {
     683          50 :                 FieldResolver resv(scheme, fields);
     684          50 :                 resv.readFields([&] (const StringView &name, const db::Field *field) {
     685          50 :                         s.field(SqlQuery::Field("t", name));
     686          50 :                 });
     687          50 :         } else {
     688           0 :                 s.field(Field("t", "__oid"));
     689             :         }
     690          50 :         s.fields(Field("dv", "time").as("__d_time"), Field("dv", "object").as("__d_object"), Field("dv", "__vid"))
     691          50 :                 .from(Field(view->scheme->getName()).as("t"))
     692          50 :                 .rightJoinOn("dv", [&] (SqlQuery::WhereBegin &w) {
     693          50 :                         w.where(Field("dv", "object"), Comparation::Equal, Field("t", "__oid"));
     694          50 :         });
     695          50 : }
     696             : 
     697       18850 : const StringStream &SqlQuery::getQuery() const {
     698       18850 :         return stream;
     699             : }
     700             : 
     701       22750 : db::QueryInterface * SqlQuery::getInterface() const {
     702       22750 :         return binder.getInterface();
     703             : }
     704             : 
     705         300 : void SqlQuery::writeFullTextFrom(SelectFrom &sel, const Scheme &scheme, const db::Field *f, const db::Query::Select &it) {
     706         300 :         auto ftsQuery = getFullTextQuery(scheme, *f, it);
     707         300 :         if (!ftsQuery.empty()) {
     708         300 :                 sel.query->writeBind(db::Binder::FullTextFrom{scheme.getName(), f, ftsQuery});
     709             :         }
     710         300 : }
     711             : 
     712        8325 : void SqlQuery::writeFullTextRank(Select &sel, const db::Scheme &scheme, const db::Query &q) {
     713        8325 :         Set<const db::Field *> fields;
     714        9600 :         for (auto &it : q.getSelectList()) {
     715        1275 :                 if (auto f = scheme.getField(it.field)) {
     716        1275 :                         if (f->getType() == db::Type::FullTextView) {
     717         300 :                                 if (fields.find(f) == fields.end()) {
     718         300 :                                         fields.emplace(f);
     719         300 :                                         auto ftsQuery = getFullTextQuery(scheme, *f, it);
     720         300 :                                         if (!ftsQuery.empty()) {
     721         300 :                                                 if (sel.state == SqlQuery::State::Some) {
     722           0 :                                                         sel.query->getStream() << ", ";
     723             :                                                 }
     724         300 :                                                 sel.query->writeBind(db::Binder::FullTextRank{scheme.getName(), f, ftsQuery});
     725         300 :                                                 sel.query->getStream() << " AS __ts_rank_" << it.field;
     726         300 :                                                 sel.state = SqlQuery::State::Some;
     727             :                                         }
     728             :                                 }
     729             :                         }
     730             :                 }
     731             :         }
     732        8325 : }
     733             : 
     734          50 : void SqlQuery::writeFullTextWhere(WhereContinue &whi, db::Operator op, const db::Scheme &scheme, const db::Query::Select &sel, StringView ftsQuery) {
     735          50 :         whi.where(op, SqlQuery::Field(scheme.getName(), sel.field), Comparation::Includes, RawStringView{ftsQuery});
     736          50 : }
     737             : 
     738         900 : StringView SqlQuery::getFullTextQuery(const Scheme &scheme, const db::Field &f, const db::Query::Select &it) {
     739         900 :         if (f.getType() != Type::FullTextView) {
     740           0 :                 return StringView();
     741             :         }
     742             : 
     743         900 :         String key = toString(scheme.getName(), ":", f.getName());
     744             : 
     745         900 :         auto fit = _fulltextQueries.find(key);
     746         900 :         if (fit != _fulltextQueries.end()) {
     747         600 :                 return fit->second;
     748             :         }
     749             : 
     750         300 :         if (!it.textQuery.empty()) {
     751         275 :                 StringStream queryFrom;
     752         275 :                 binder.writeBind(queryFrom, Binder::FullTextQueryRef{scheme.getName(), &f, it.textQuery});
     753         275 :                 return _fulltextQueries.emplace(std::move(key), queryFrom.str()).first->second;
     754         300 :         } else if (it.value1) {
     755          25 :                 auto d = f.getSlot<db::FieldFullTextView>();
     756          25 :                 auto q = d->parseQuery(it.value1);
     757          25 :                 if (!q.empty()) {
     758          25 :                         auto &it = _parsedQueries.emplace_front(move(q));
     759          25 :                         StringStream queryFrom;
     760          25 :                         binder.writeBind(queryFrom, Binder::FullTextQueryRef{scheme.getName(), &f, it});
     761          25 :                         return _fulltextQueries.emplace(std::move(key), queryFrom.str()).first->second;
     762          25 :                 }
     763          25 :         }
     764             : 
     765           0 :         return StringView();
     766         900 : }
     767             : 
     768             : 
     769        6350 : SqlQuery::Context::Context(SqlQuery &sql, const Scheme &s, const Worker &w, const db::Query &q)
     770        6350 : : FieldResolver(s, w, q) {
     771        6350 :         _this = &sql;
     772             : 
     773        6350 :         hasAltLimit = false;
     774        6350 :         if (query->isSoftLimit()) {
     775         425 :                 auto &field = query->getOrderField();
     776         425 :                 auto f = scheme->getField(field);
     777         425 :                 if (field == "__oid") {
     778         300 :                         softLimitField = field;
     779         125 :                 } else if (f) {
     780         125 :                         softLimitField = f->getName();
     781         125 :                         hasAltLimit = (f->getType() == Type::FullTextView || !f->hasFlag(Flags::Unique));
     782         125 :                         softLimitIsFts = (f->getType() == Type::FullTextView);
     783             :                 } else {
     784           0 :                         sql._driver->getApplicationInterface()->error("SqlQuery", "Invalid soft limit field", Value(field));
     785             :                 }
     786             :         }
     787        6350 : }
     788             : 
     789           0 : StringView SqlQuery::Context::getAlt(StringView key) {
     790           0 :         auto f = scheme->getField(key);
     791           0 :         for (auto &it : query->getSelectList()) {
     792           0 :                 if (it.field == key) {
     793           0 :                         return _this->getFullTextQuery(*scheme, *f, it);
     794             :                 }
     795             :         }
     796           0 :         return StringView();
     797             : }
     798             : 
     799             : }

Generated by: LCOV version 1.14