123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include "jliball.hpp"
- #include "hql.hpp"
- #include "platform.h"
- #include "jlib.hpp"
- #include "jmisc.hpp"
- #include "jstream.ipp"
- #include "jdebug.hpp"
- #include "eclrtl_imp.hpp"
- #include "rtlkey.hpp"
- #include "hql.hpp"
- #include "hqlattr.hpp"
- #include "hqlmeta.hpp"
- #include "hqlthql.hpp"
- #include "hqlutil.hpp"
- #include "hqlthql.hpp"
- #include "hqlfold.hpp"
- #include "hqltrans.ipp"
- #include "hqlpmap.hpp"
- #include "hqlfilter.hpp"
- #include "hqlerrors.hpp"
- //-----------------------------------------------------------------------------------------------
- //-- Index processing
- //-----------------------------------------------------------------------------------------------
- /*
- Note on generating segment monitors for conditions (ty)x = Y
- 1) casting from Tx to Ty loses information. E.g., (int1)string2field = int1value.
- In this case it is almost impossible to generate a segment monitor because we would need to work out all
- the possibly values for x which could generate the value y.
- The only exception is an inequality (and no_notin), which we can use to remove some of the candidates. The test will always need
- duplicating since we cannot remove all the expected values.
- 2) Casting from Ty to Tx loses information. E.g., (string)string2field = stringvalue
- In this case we can process the filter without prefiltering by testing whether isExact: (Ty)(Tx)Y == Y, and following the following rules:
- a) no_eq. If isExact, add value else nothing.
- b) no_ne. If isExtact, remove value else nothing.
- c) no_gt. Always add > value
- d) no_ge. If isExact add >= value else add > value
- e) no_lt. If isExact add < value else add <= value
- f) no_le. Always add <= value
- 3) Note casts must be present on both sides to indicate exactly what type the comparison will be done as. This includes (string)which would
- normally be missing if the field was of type string<n>.
- */
- static node_operator getModifiedOp(node_operator op, bool duplicate)
- {
- if (!duplicate)
- return op;
- switch (op)
- {
- case no_eq:
- case no_le:
- case no_ge:
- case no_in:
- return op;
- //err on the side of caution for the segment monitors -
- //the test later which check it more accurately.
- case no_gt:
- return no_ge;
- case no_lt:
- return no_le;
- case no_ne:
- case no_notin:
- return no_none;
- default:
- UNIMPLEMENTED;
- }
- }
- static IHqlExpression * querySubStringRange(IHqlExpression * expr)
- {
- for(;;)
- {
- switch (expr->getOperator())
- {
- case no_substring:
- return expr->queryChild(1);
- case no_cast:
- case no_implicitcast:
- break;
- default:
- return nullptr;
- }
- expr = expr->queryChild(0);
- }
- }
- void KeyFailureInfo::merge(const KeyFailureInfo & other)
- {
- if (code < other.code)
- set(other.code, other.field);
- }
- void KeyFailureInfo::reportError(IErrorReceiver & errorReceiver, IHqlExpression * condition)
- {
- StringBuffer ecl;
- getExprECL(condition, ecl);
- switch (code)
- {
- case KFRunknown:
- errorReceiver.throwError1(HQLERR_KeyedJoinTooComplex, ecl.str());
- case KFRnokey:
- errorReceiver.throwError1(HQLERR_KeyAccessNoKeyField, ecl.str());
- case KFRtoocomplex:
- errorReceiver.throwError1(HQLERR_KeyedJoinTooComplex, ecl.str());
- case KFRcast:
- errorReceiver.throwError2(HQLERR_KeyAccessNeedCast, ecl.str(), str(field->queryName()));
- case KFRor:
- errorReceiver.throwError1(HQLERR_OrMultipleKeyfields, ecl.str());
- }
- }
- IHqlExpression * KeyConditionInfo::createConjunction()
- {
- LinkedHqlExpr result = preFilter;
- ForEachItemIn(i, conditions)
- extendAndCondition(result, conditions.item(i).expr);
- extendAndCondition(result, postFilter);
- return result.getClear();
- }
- bool KeyConditionInfo::isSingleMatchCondition() const
- {
- if (preFilter || postFilter)
- return false;
- IHqlExpression * prevSelector = nullptr;
- ForEachItemIn(i, conditions)
- {
- KeyCondition & condition = conditions.item(i);
- IHqlExpression * selector = condition.selector;
- if (!prevSelector)
- prevSelector = selector;
- else if (prevSelector != selector)
- return false;
- }
- return (prevSelector != nullptr);
- }
- //---------------------------------------------------------------------------------------------------------------------
- const char * KeySelectorInfo::getFFOptions()
- {
- switch (keyedKind)
- {
- case KeyedExtend:
- return "FFopt";
- default:
- return "FFkeyed";
- }
- }
- //---------------------------------------------------------------------------------------------------------------------
- IHqlExpression * getExplicitlyPromotedCompare(IHqlExpression * filter)
- {
- switch (filter->getOperator())
- {
- case no_in:
- case no_notin:
- return LINK(filter);
- }
- IHqlExpression * l = filter->queryChild(0);
- IHqlExpression * r = filter->queryChild(1);
- ITypeInfo * lType = queryUnqualifiedType(l->queryType());
- ITypeInfo * rType = queryUnqualifiedType(r->queryType());
- if (lType == rType)
- return LINK(filter);
- //Add explicit casts to the type. ensureExprType won't add a (string) to a string2 field.
- Owned<ITypeInfo> promotedType = getPromotedECLType(lType, rType);
- HqlExprArray args;
- if (lType == promotedType)
- args.append(*LINK(l));
- else
- args.append(*createValue(no_implicitcast, LINK(promotedType), LINK(l)));
- args.append(*ensureExprType(r, promotedType));
- return filter->clone(args);
- }
- //---------------------------------------------------------------------------------------------------------------------
- static IHqlExpression * createExpandedRecord(IHqlExpression * expr);
- static ITypeInfo * getExpandedFieldType(ITypeInfo * type, IHqlExpression * expr)
- {
- Linked<ITypeInfo> expandedType = type;
- if (type->getSize() == UNKNOWN_LENGTH)
- expandedType.clear();
- switch (type->getTypeCode())
- {
- case type_packedint:
- expandedType.setown(makeIntType(type->queryPromotedType()->getSize(), type->isSigned()));
- break;
- case type_bitfield:
- expandedType.set(type->queryPromotedType());
- break;
- case type_varstring:
- case type_varunicode:
- #if 0
- if (type->getSize() != UNKNOWN_LENGTH)
- {
- unsigned len = type->getStringLen();
- switch (type->getTypeCode())
- {
- case type_varstring:
- expandedType.setown(makeStringType(len, LINK(type->queryCharset()), LINK(type->queryCollation())));
- break;
- case type_varunicode:
- expandedType.setown(makeUnicodeType(len, type->queryLocale()));
- break;
- }
- break;
- }
- #endif //fall through
- case type_data:
- case type_qstring:
- case type_string:
- case type_unicode:
- case type_utf8:
- if (type->getSize() == UNKNOWN_LENGTH)
- {
- unsigned maxLength = UNKNOWN_LENGTH;
- IHqlExpression * maxSizeExpr = expr ? queryAttributeChild(expr, maxSizeAtom, 0) : NULL;
- if (maxSizeExpr)
- {
- unsigned maxSize = (unsigned)maxSizeExpr->queryValue()->getIntValue();
- switch (type->getTypeCode())
- {
- case type_data:
- case type_string:
- maxLength = maxSize - sizeof(size32_t);
- break;
- case type_qstring:
- maxLength = rtlQStrLength(maxSize - sizeof(size32_t));
- break;
- case type_unicode:
- maxLength = (maxSize-sizeof(size32_t))/sizeof(UChar);
- break;
- case type_utf8:
- maxLength = (maxSize-sizeof(size32_t))/4;
- break;
- case type_varstring:
- maxLength = maxSize - 1;
- break;
- case type_varunicode:
- maxLength = (maxSize/sizeof(UChar)) - 1;
- break;
- }
- }
- else
- {
- IHqlExpression * maxLengthExpr = expr ? queryAttributeChild(expr, maxLengthAtom, 0) : NULL;
- if (maxLengthExpr)
- maxLength = (unsigned)maxLengthExpr->queryValue()->getIntValue();
- }
- if (maxLength != UNKNOWN_LENGTH)
- {
- switch (type->getTypeCode())
- {
- case type_data:
- expandedType.setown(makeDataType(maxLength));
- break;
- case type_qstring:
- expandedType.setown(makeQStringType(maxLength));
- break;
- case type_string:
- expandedType.setown(makeStringType(maxLength, LINK(type->queryCharset()), LINK(type->queryCollation())));
- break;
- case type_unicode:
- expandedType.setown(makeUnicodeType(maxLength, type->queryLocale()));
- break;
- case type_utf8:
- expandedType.setown(makeUtf8Type(maxLength, type->queryLocale()));
- break;
- case type_varstring:
- expandedType.setown(makeVarStringType(maxLength, LINK(type->queryCharset()), LINK(type->queryCollation())));
- break;
- case type_varunicode:
- expandedType.setown(makeVarUnicodeType(maxLength, type->queryLocale()));
- break;
- }
- }
- }
- else
- {
- //This could ensure the strings are ascii, but the ebcdic strings are still comparable, and the order will be more logical
- //if they remain as ebcdic.
- }
- break;
- case type_table:
- case type_groupedtable:
- case type_set:
- expandedType.clear();
- break;
- case type_row:
- {
- OwnedHqlExpr newRecord = createExpandedRecord(queryRecord(type));
- if (isEmptyRecord(newRecord))
- expandedType.clear();
- else
- expandedType.setown(makeRowType(LINK(newRecord->queryRecordType())));
- break;
- }
- case type_alien:
- {
- IHqlAlienTypeInfo * alien = queryAlienType(type);
- expandedType.set(alien->queryLogicalType());
- break;
- }
- }
- return expandedType.getClear();
- }
- static void createExpanded(HqlExprArray & fields, IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_ifblock:
- //if blocks need to generate translated segment monitors to be keyed, so don't expand them
- break;
- case no_record:
- {
- ForEachChild(i, expr)
- createExpanded(fields, expr->queryChild(i));
- break;
- }
- case no_field:
- {
- ITypeInfo * type = expr->queryType();
- Owned<ITypeInfo> expandedType = getExpandedFieldType(type, expr);
- if (expandedType)
- {
- if (expandedType == type)
- fields.append(*LINK(expr));
- else
- {
- HqlExprArray attrs;
- unwindChildren(attrs, expr);
- //MORE: Any default will now have the wrong type => remove it for the moment (ideally it would be projected)
- removeAttribute(attrs, defaultAtom);
- fields.append(*createField(expr->queryId(), LINK(expandedType), attrs));
- }
- }
- break;
- }
- case no_attr:
- case no_attr_link:
- case no_attr_expr:
- fields.append(*LINK(expr));
- break;
- }
- }
- static IHqlExpression * createExpandedRecord(IHqlExpression * expr)
- {
- HqlExprArray fields;
- createExpanded(fields, expr);
- return cloneOrLink(expr, fields);
- }
- IHqlExpression * castToFieldAndBack(IHqlExpression * left, IHqlExpression * right)
- {
- node_operator op = left->getOperator();
- switch (op)
- {
- case no_cast:
- case no_implicitcast:
- {
- IHqlExpression * uncast = left->queryChild(0);
- ITypeInfo * castType = right->queryType();
- ITypeInfo * uncastType = uncast->queryType();
- OwnedHqlExpr castRight = ensureExprType(right, uncastType);
- OwnedHqlExpr base = castToFieldAndBack(uncast, castRight);
- //If this cast doesn't lose any information and child didn't change then don't bother
- //casting back and forwards.
- if ((base == castRight) && !castLosesInformation(uncastType, castType))
- return LINK(right);
- return ensureExprType(base, castType);
- }
- case no_select:
- {
- ITypeInfo * leftType = left->queryType();
- ITypeInfo * rightType = right->queryType();
- if (leftType == rightType || !castLosesInformation(leftType, rightType))
- return LINK(right);
- OwnedHqlExpr castToField = ensureExprType(right, leftType);
- return ensureExprType(castToField, rightType);
- }
- case no_substring:
- {
- OwnedHqlExpr cast = castToFieldAndBack(left->queryChild(0), right);
- //Theoretically needed for all types. In practice this only makes a difference for data since strings
- //ignore trailing spaces
- if (left->queryType()->getTypeCode() == type_data)
- return replaceChild(left, 0, cast.getClear());
- return cast.getClear();
- }
- case no_add:
- case no_sub:
- return castToFieldAndBack(left->queryChild(0), right);
- default:
- throwUnexpected();
- }
- }
- //---------------------------------------------------------------------------------------------------------------------
- FilterExtractor::FilterExtractor(IErrorReceiver & _errorReceiver, IHqlExpression * _tableExpr, int _numKeyableFields, bool _isDiskRead, bool forceValueSets)
- : errorReceiver(_errorReceiver), createValueSets(forceValueSets)
- {
- tableExpr = _tableExpr;
- if (_numKeyableFields <= 0)
- {
- //-ve number means remove a certain number of fields from the record
- IHqlExpression * record = tableExpr->queryRecord();
- numKeyableFields = 0;
- ForEachChild(i, record)
- if (!record->queryChild(i)->isAttribute())
- numKeyableFields++;
- numKeyableFields += _numKeyableFields; // remove payload fields.
- }
- else
- numKeyableFields = (unsigned)_numKeyableFields;
- onlyHozedCompares = !_isDiskRead;
- excludeVirtuals = _isDiskRead;
- expandKeyableFields();
- cleanlyKeyedExplicitly = false;
- keyedExplicitly = false;
- allowDynamicFormatChange = !tableExpr->hasAttribute(fixedAtom);
- }
- bool FilterExtractor::isSingleMatchCondition() const
- {
- return keyed.isSingleMatchCondition();
- }
- void FilterExtractor::expandKeyableFields()
- {
- HqlExprArray fields;
- IHqlExpression * tableRecord = tableExpr->queryRecord();
- unsigned cnt = 0;
- ForEachChild(i, tableRecord)
- {
- if (cnt == numKeyableFields)
- break;
- IHqlExpression * cur = tableRecord->queryChild(i);
- if (!cur->isAttribute())
- {
- if (!(excludeVirtuals && cur->hasAttribute(virtualAtom)))
- {
- fields.append(*LINK(cur));
- cnt++;
- }
- }
- }
- keyableRecord.setown(createRecord(fields));
- if (createValueSets)
- expandedRecord.set(keyableRecord);
- else
- expandedRecord.setown(createExpandedRecord(keyableRecord));
- IHqlExpression * selector = tableExpr->queryNormalizedSelector();
- OwnedHqlExpr expandedSelector = createDataset(no_anon, LINK(expandedRecord), createUniqueId());
- firstOffsetField = NotFound;
- expandSelects(keyableRecord, expandedRecord->querySimpleScope(), selector, expandedSelector);
- if (firstOffsetField == NotFound)
- firstOffsetField = keyableSelects.ordinality();
- }
- void FilterExtractor::expandSelects(IHqlExpression * expr, IHqlSimpleScope * expandedScope, IHqlExpression * keySelector, IHqlExpression * expandedSelector)
- {
- switch (expr->getOperator())
- {
- case no_record:
- {
- ForEachChild(i, expr)
- expandSelects(expr->queryChild(i), expandedScope, keySelector, expandedSelector);
- break;
- }
- case no_ifblock:
- expandSelects(expr->queryChild(1), expandedScope, keySelector, expandedSelector);
- break;
- case no_field:
- {
- OwnedHqlExpr match = expandedScope->lookupSymbol(expr->queryId());
- if (match)
- {
- OwnedHqlExpr keySelected = createSelectExpr(LINK(keySelector), LINK(expr));
- OwnedHqlExpr expandedSelected = createSelectExpr(LINK(expandedSelector), LINK(match));
- IHqlExpression * record = expr->queryRecord();
- if (expr->isDatarow())
- expandSelects(record, match->queryRecord()->querySimpleScope(), keySelected, expandedSelected);
- else
- {
- if ((expr != match) && (firstOffsetField == NotFound))
- {
- ITypeInfo * exprType = expr->queryType();
- ITypeInfo * matchType = match->queryType();
- if ((exprType->getSize() != matchType->getSize()) ||
- (exprType->getTypeCode() == type_bitfield || matchType->getTypeCode() == type_bitfield))
- firstOffsetField = keyableSelects.ordinality();
- }
- keyableSelects.append(*LINK(keySelected));
- expandedSelects.append(*LINK(expandedSelected));
- }
- }
- else
- {
- if (firstOffsetField == NotFound)
- firstOffsetField = keyableSelects.ordinality();
- }
- break;
- }
- }
- }
- IHqlExpression * FilterExtractor::unwindConjunction(HqlExprArray & matches, IHqlExpression * expr)
- {
- node_operator op = expr->getOperator();
- expr->unwindList(matches, op);
- OwnedHqlExpr invariant;
- ForEachItemInRev(i, matches)
- {
- IHqlExpression & cur = matches.item(i);
- if (isIndexInvariant(&cur, false))
- {
- invariant.setown(extendConditionOwn(op, LINK(&cur), invariant.getClear()));
- matches.remove(i);
- }
- }
- return invariant.getClear();
- }
- bool FilterExtractor::isKeySelect(IHqlExpression * select)
- {
- return (keyableSelects.find(*select) != NotFound);
- }
- bool FilterExtractor::isEqualityFilter(IHqlExpression * search)
- {
- bool matched = false;
- ForEachItemIn(cond, keyed.conditions)
- {
- KeyCondition & cur = keyed.conditions.item(cond);
- if (cur.selector == search)
- {
- if (!cur.isWild)
- {
- if (matched)
- return false;
- matched = true;
- IHqlExpression * matchExpr = cur.expr;
- if (matchExpr->getOperator() != no_eq)
- return false;
- }
- }
- }
- return matched;
- }
- bool FilterExtractor::isEqualityFilterBefore(IHqlExpression * select)
- {
- ForEachItemIn(i, keyableSelects)
- {
- IHqlExpression & cur = keyableSelects.item(i);
- if (select == &cur)
- return true;
- if (!isEqualityFilter(&cur))
- return false;
- }
- throwUnexpected();
- }
- bool FilterExtractor::isPrevSelectKeyed(IHqlExpression * select)
- {
- unsigned match = keyableSelects.find(*select);
- assertex(match != NotFound);
- if (match == 0)
- return true;
- IHqlExpression * prev = &keyableSelects.item(match-1);
- ForEachItemIn(i, keyed.conditions)
- {
- KeyCondition & cur = keyed.conditions.item(i);
- if (cur.selector == prev)
- {
- if (!cur.isWild && cur.isKeyed())
- return true;
- if (cur.wasKeyed)
- return true;
- }
- }
- return false;
- }
- bool FilterExtractor::okToKey(IHqlExpression * select, KeyedKind keyedKind)
- {
- if (keyedKind == KeyedYes)
- return true;
- ForEachItemIn(i, keyed.conditions)
- {
- KeyCondition & cur = keyed.conditions.item(i);
- if (cur.selector == select && cur.isWild)
- return false;
- }
- return true;
- }
- bool FilterExtractor::isIndexInvariant(IHqlExpression * expr, bool includeRoot)
- {
- if (containsAssertKeyed(expr))
- return false;
- HqlExprCopyArray scopeUsed;
- expr->gatherTablesUsed(scopeUsed);
- IHqlExpression * search = tableExpr->queryNormalizedSelector();
- ForEachItemIn(i, scopeUsed)
- {
- IHqlExpression * cur = &scopeUsed.item(i);
- for (;;)
- {
- if (cur == search)
- return false;
- if (includeRoot && (queryRoot(cur) == search))
- return false;
- IHqlExpression * parent = queryNextMultiLevelDataset(cur, true);
- if (!parent)
- break;
- cur = parent;
- }
- }
- return true;
- }
- IHqlExpression * FilterExtractor::invertTransforms(IHqlExpression * left, IHqlExpression * right)
- {
- node_operator op = left->getOperator();
- switch (op)
- {
- case no_cast:
- case no_implicitcast:
- {
- assertex(right->queryType()->getTypeCode() != type_set);
- IHqlExpression * uncast = left->queryChild(0);
- ITypeInfo * uncastType = uncast->queryType();
- OwnedHqlExpr castRight = ensureExprType(right, uncastType);
- return invertTransforms(uncast, castRight);
- }
- case no_select:
- {
- assertex(isKeySelect(left));
- ITypeInfo * leftType = left->queryType();
- ITypeInfo * rightType = right->queryType();
- if (!createValueSets)
- {
- if (leftType == rightType || !castLosesInformation(leftType, rightType))
- return LINK(right);
- }
- return ensureExprType(right, leftType);
- }
- case no_add:
- case no_sub:
- {
- assertex(right->getOperator() != no_list);
- OwnedHqlExpr adjusted = createValue(op == no_sub ? no_add : no_sub, right->getType(), LINK(right), LINK(left->queryChild(1)));
- return invertTransforms(left->queryChild(0), adjusted);
- }
- case no_substring:
- {
- assertex(right->getOperator() != no_list);
- return invertTransforms(left->queryChild(0), right);
- }
- default:
- UNIMPLEMENTED;
- }
- }
- IHqlExpression * FilterExtractor::queryKeyableSelector(IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_cast:
- case no_implicitcast:
- case no_add:
- case no_sub:
- return queryKeyableSelector(expr->queryChild(0));
- case no_select:
- if (isKeySelect(expr))
- return expr;
- return NULL;
- }
- return NULL;
- }
- IHqlExpression * FilterExtractor::isKeyableFilter(IHqlExpression * left, IHqlExpression * right, bool & duplicate, node_operator compareOp, KeyFailureInfo & reason, KeyedKind keyedKind)
- {
- node_operator op = left->getOperator();
- switch (op)
- {
- case no_cast:
- case no_implicitcast:
- {
- IHqlExpression * uncast = left->queryChild(0);
- ITypeInfo * castType = left->queryType();
- ITypeInfo * uncastType = uncast->queryType();
- //Keyed filters on alien datatypes do not work, and can trigger an internal error in ensureExprType()
- if (uncastType->getTypeCode() == type_alien)
- {
- reason.set(KFRtoocomplex, left);
- return nullptr;
- }
- //(ty)x = y. E.g., (int1)string2field = int1value
- //if more than one value of x[uncastType] corresponds to a single value in y[castType] then we can't sensibly create
- //the key segment monitor. Because we will get false negatives. If it is an inverse then duplicate (see below)
- bool canRemoveCast = true;
- if (castLosesInformation(castType, uncastType))
- {
- if ((compareOp != no_ne) && (compareOp != no_notin))
- canRemoveCast = false;
- duplicate = true;
- }
- //if more than one value of y corresponds to a single value of x then need to duplicate the test condition.
- //or pretest whether (ty)(tx)y == y.
- //if (castLosesInformation(uncastType, castType))
- //Now taken care of when the segment monitors are created
- //If the comparison is non equality and the cast changes the collation sequence then you can't remove it.
- switch (compareOp)
- {
- case no_eq:
- case no_ne:
- case no_in:
- case no_notin:
- break;
- default:
- if (!preservesOrder(castType, uncastType))
- canRemoveCast = false;
- break;
- }
- Linked<ITypeInfo> newType = uncastType;
- if (right->queryType()->getTypeCode() == type_set)
- newType.setown(makeSetType(newType.getLink()));
- OwnedHqlExpr castRight = ensureExprType(right, newType);
- IHqlExpression * ret = isKeyableFilter(uncast, castRight, duplicate, compareOp, reason, keyedKind);
- if (canRemoveCast || !ret)
- return ret;
- reason.set(KFRcast, ret->queryChild(1));
- return NULL;
- }
- case no_select:
- if (isKeySelect(left) && okToKey(left, keyedKind))
- {
- if (isIndexInvariant(right, false))
- return left;
- reason.set(KFRtoocomplex, left);
- }
- else
- reason.set(KFRnokey);
- return NULL;
- case no_substring:
- {
- IHqlExpression * range = left->queryChild(1);
- if (range->getOperator() == no_rangeto)
- {
- IValue *end = range->queryChild(0)->queryValue();
- if (!createValueSets && !end)
- break;
- return isKeyableFilter(left->queryChild(0), right, duplicate, compareOp, reason, keyedKind);
- }
- else if (range->getOperator() == no_range)
- {
- if (!matchesConstantValue(range->queryChild(0), 1))
- break;
- if (!createValueSets && !range->queryChild(1)->queryValue())
- break;
- return isKeyableFilter(left->queryChild(0), right, duplicate, compareOp, reason, keyedKind);
- }
- reason.set(KFRtoocomplex, right);
- return NULL;
- }
- case no_add:
- case no_sub:
- if (isIndexInvariant(left->queryChild(1), false))
- return isKeyableFilter(left->queryChild(0), right, duplicate, compareOp, reason, keyedKind);
- reason.set(KFRtoocomplex, left);
- return NULL;
- }
- reason.set(KFRnokey);
- return NULL;
- }
- static IHqlExpression * getCompareValue(ITypeInfo * fieldType, unsigned subStringLen, IValue * value, int whichBoundary)
- {
- type_t ftc = fieldType->getTypeCode();
- unsigned fieldLen = fieldType->getStringLen();
- unsigned lenValue = value->queryType()->getStringLen();
- const void * rawValue = value->queryValue();
- size32_t resultLen;
- rtlDataAttr result;
- if (whichBoundary < 0)
- {
- switch (ftc)
- {
- case type_qstring:
- rtlCreateQStrRangeLow(resultLen, result.refstr(), fieldLen, subStringLen, lenValue, static_cast<const char *>(rawValue));
- break;
- case type_string:
- rtlCreateStrRangeLow(resultLen, result.refstr(), fieldLen, subStringLen, lenValue, static_cast<const char *>(rawValue));
- break;
- case type_data:
- rtlCreateDataRangeLow(resultLen, result.refdata(), fieldLen, subStringLen, lenValue, rawValue);
- break;
- case type_unicode:
- rtlCreateUnicodeRangeLow(resultLen, result.refustr(), fieldLen, subStringLen, lenValue, static_cast<const UChar *>(rawValue));
- break;
- default:
- //should this generate a warning/error instead?
- rtlCreateRange(resultLen, result.refstr(), fieldLen, subStringLen, fieldType->getSize(), static_cast<const char *>(rawValue), 0, 0);
- break;
- }
- }
- else
- {
- switch (ftc)
- {
- case type_qstring:
- rtlCreateQStrRangeHigh(resultLen, result.refstr(), fieldLen, subStringLen, lenValue, static_cast<const char *>(rawValue));
- break;
- case type_string:
- rtlCreateStrRangeHigh(resultLen, result.refstr(), fieldLen, subStringLen, lenValue, static_cast<const char *>(rawValue));
- break;
- case type_data:
- rtlCreateDataRangeHigh(resultLen, result.refdata(), fieldLen, subStringLen, lenValue, rawValue);
- break;
- case type_unicode:
- rtlCreateUnicodeRangeHigh(resultLen, result.refustr(), fieldLen, subStringLen, lenValue, static_cast<const UChar *>(rawValue));
- break;
- default:
- rtlCreateRange(resultLen, result.refstr(), fieldLen, subStringLen, fieldType->getSize(), static_cast<const char *>(rawValue), 255, 0);
- break;
- }
- }
- assertex(resultLen == fieldLen);
- return createConstant(createValueFromMem(LINK(fieldType), result.getdata()));
- }
- static IHqlExpression * removeCastTrim(IHqlExpression * expr)
- {
- for (;;)
- {
- if ((expr->getOperator() == no_trim) && !expr->queryChild(1))
- expr = expr->queryChild(0);
- else if (isLengthPreservingCast(expr))
- expr = expr->queryChild(0);
- else
- return expr;
- expr = queryNonAliased(expr);
- }
- }
- static IHqlExpression * queryLengthFromRange(IHqlExpression * range)
- {
- switch (range->getOperator())
- {
- case no_rangefrom:
- case no_rangecommon:
- return NULL;
- case no_rangeto:
- return range->queryChild(0);
- case no_range:
- if (getIntValue(range->queryChild(0), 0) == 1)
- return range->queryChild(1);
- return NULL;
- default:
- if (getIntValue(range, 0) == 1)
- return range;
- return NULL;
- }
- }
- static void extendRangeCheck(SharedHqlExpr & globalGuard, SharedHqlExpr & localCond, IHqlExpression * selector, IHqlExpression * lengthExpr, bool compareEqual)
- {
- #if 0
- //This might be a good idea, but probably doesn't make a great deal of difference at runtime
- //Optimize the case where you check zero length to use a wild carded range instead
- //x[1..len=0] = y from x in range 0000000..FFFFFF to len==0 || x in range....
- if (compareEqual)
- {
- if (!lengthExpr->queryValue())
- {
- OwnedHqlExpr testLength = createBoolExpr(no_eq, LINK(lengthExpr), ensureExprType(queryZero(), lengthExpr->queryType()));
- localCond.setown(createBoolExpr(no_or, testLength.getClear(), LINK(localCond)));
- }
- }
- #endif
- //For a range check x[1..m] = y we generate
- //x in range(sizeof(x), m, y, 0) and range(maxlength(x), m, y, 255)
- //need to guard with condition len(trim(y)) <= m
- //If x[1..length(trim(y))] == y then don't add a condition
- IHqlExpression * cur = queryNonAliased(lengthExpr);
- IHqlExpression * compare = removeCastTrim(queryNonAliased(selector));
- if (cur->getOperator() == no_charlen)
- {
- cur = queryNonAliased(cur->queryChild(0));
- if (cur->getOperator() == no_trim)
- {
- cur = queryNonAliased(cur->queryChild(0));
- while (isLengthPreservingCast(cur))
- cur = queryNonAliased(cur->queryChild(0));
- IHqlExpression * compare = selector;
- for (;;)
- {
- compare = removeCastTrim(queryNonAliased(compare));
- if (cur->queryBody() == compare->queryBody())
- return;
- //Casts between strings that reduce/increase the number of characters don't matter as long as
- //they eventually match the search string
- if (!isCast(compare) || !isStringType(compare->queryType()))
- break;
- compare = compare->queryChild(0);
- if (!isStringType(compare->queryType()))
- break;
- }
- }
- }
- // if x[1..n] = z[1..n] then no need for a condition
- if (compare->getOperator() == no_substring)
- {
- IHqlExpression * range = queryLengthFromRange(compare->queryChild(1));
- if (range == lengthExpr)
- return;
- }
- // if x[1..n] = (string<m>y) where m<n then no need for condition
- unsigned selectorLength = selector->queryType()->getStringLen();
- if (selectorLength <= getIntValue(lengthExpr, 0))
- return;
- //otherwise, if x[1..y] == z then add check length(trim(z)) <= y
- OwnedHqlExpr trim = createTrimExpr(selector, NULL);
- OwnedITypeInfo unsignedType = makeIntType(sizeof(unsigned), false);
- OwnedHqlExpr len = createValue(no_charlen, LINK(unsignedType), LINK(trim));
- ITypeInfo * lengthType = lengthExpr->queryType();
- Owned<ITypeInfo> compareType = getPromotedECLCompareType(unsignedType, lengthType);
- OwnedHqlExpr positiveLen = createValue(no_maxlist, lengthExpr->getType(), createValue(no_list, makeSetType(LINK(lengthType)), LINK(lengthExpr), createConstant(lengthType->castFrom(false, I64C(0)))));
- OwnedHqlExpr test = createValue(no_le, makeBoolType(), ensureExprType(len, compareType), ensureExprType(positiveLen, compareType));
- test.setown(foldHqlExpression(test));
- if (compareEqual)
- extendConditionOwn(globalGuard, no_and, test.getClear());
- else
- extendConditionOwn(localCond, no_or, getInverse(test));
- }
- IHqlExpression * FilterExtractor::getRangeLimit(ITypeInfo * fieldType, IHqlExpression * lengthExpr, IHqlExpression * value, int whichBoundary)
- {
- unsigned fieldLength = fieldType->getStringLen();
- IValue * constValue = value->queryValue();
- if (constValue && lengthExpr->queryValue())
- {
- unsigned subStringLen = (unsigned)lengthExpr->queryValue()->getIntValue();
- if ((int)subStringLen < 0) subStringLen = 0;
- if (subStringLen > fieldLength)
- errorReceiver.throwError1(HQLERR_SubstringOutOfRange, subStringLen);
- return getCompareValue(fieldType, subStringLen, constValue, whichBoundary);
- }
- return nullptr;
- }
- IHqlExpression * FilterExtractor::createRangeCompare(IHqlExpression * selector, IHqlExpression * value, IHqlExpression * lengthExpr, bool compareEqual)
- {
- OwnedHqlExpr foldedValue = foldHqlExpression(value);
- if (createValueSets)
- {
- OwnedHqlExpr rangeExpr = createValue(no_rangeto, makeNullType(), LINK(lengthExpr));
- OwnedHqlExpr substr = createValue(no_substring, getStretchedType(UNKNOWN_LENGTH, selector->queryType()), LINK(selector), rangeExpr.getClear());
- return createValue(compareEqual ? no_eq : no_ne, makeBoolType(), LINK(substr), foldedValue.getClear());
- }
- ITypeInfo * fieldType = selector->queryType();
- OwnedHqlExpr lowExpr = getRangeLimit(fieldType, lengthExpr, foldedValue, -1);
- OwnedHqlExpr highExpr = getRangeLimit(fieldType, lengthExpr, foldedValue, +1);
- if (!lowExpr || !highExpr)
- errorReceiver.throwError(HQLERR_NonConstantRange);
- //Could convert to two separate tests, but code is worse, and boundary conditions aren't going to happen.
- return createValue(compareEqual ? no_between : no_notbetween, makeBoolType(), LINK(selector), lowExpr.getClear(), highExpr.getClear());
- }
- bool FilterExtractor::matchSubstringFilter(KeyConditionInfo & matches, node_operator op, IHqlExpression * left, IHqlExpression * right, KeyedKind keyedKind, bool & duplicate)
- {
- LinkedHqlExpr value = right;
- duplicate = false;
- OwnedHqlExpr guard;
- ITypeInfo * guardCastType = NULL;
- if ((left->getOperator() == no_cast) || (left->getOperator() == no_implicitcast))
- {
- //code is extracted and simplified from isKeyableFilter() above - should be commoned up.
- IHqlExpression * uncast = left->queryChild(0);
- ITypeInfo * castType = left->queryType();
- ITypeInfo * uncastType = uncast->queryType();
- //(ty)x = y.
- //if more than one value of x[uncastType] corresponds to a single value in y[castType] then we can't sensibly create
- //the key segment monitor. Because we will get false negatives. If it is an inverse then duplicate (see below)
- bool canRemoveCast = true;
- if (castLosesInformation(castType, uncastType))
- canRemoveCast = false;
- //if more than one value of y corresponds to a single value of x then need to duplicate the test condition.
- if (!preservesOrder(castType, uncastType))
- canRemoveCast = false;
- if (!canRemoveCast)
- {
- // reason.set(KFRcast, ret->queryChild(1));
- return false;
- }
- if ((op != no_in) && (op != no_notin))
- {
- value.setown(ensureExprType(right, uncastType));
- //If a simple equality test then create a global guard to check that we aren't matching a false positive
- if (castLosesInformation(uncastType, castType))
- guard.setown(createBoolExpr(no_eq, ensureExprType(value, castType), LINK(right)));
- }
- else
- {
- //if an IN then add guards to each comparison - generated later...
- if (castLosesInformation(uncastType, castType))
- guardCastType = uncastType;
- else
- {
- Owned<ITypeInfo> targetType = makeSetType(LINK(uncastType));
- value.setown(ensureExprType(right, targetType));
- }
- }
- left = uncast;
- }
- if (left->getOperator() != no_substring)
- return false;
- if ((op == no_in) || (op == no_notin))
- {
- value.setown(normalizeListCasts(value));
- if (value->getOperator() != no_list)
- return false;
- }
- IHqlExpression * selector = left->queryChild(0);
- if (!isKeySelect(selector) || !okToKey(selector, keyedKind))
- return false;
- if (!isIndexInvariant(right, false))
- return false;
- ITypeInfo * fieldType = selector->queryType();
- unsigned fieldLength = fieldType->getStringLen();
- if (!createValueSets && (fieldLength == UNKNOWN_LENGTH))
- return false;
- OwnedHqlExpr range = foldHqlExpression(left->queryChild(1));
- IHqlExpression * lengthExpr = queryLengthFromRange(range);
- if (!lengthExpr)
- return false;
- OwnedHqlExpr newTest;
- if ((op == no_eq) || (op == no_ne))
- {
- newTest.setown(createRangeCompare(selector, value, lengthExpr, op == no_eq));
- extendRangeCheck(guard, newTest, right, lengthExpr, op == no_eq);
- }
- else //no_in, no_notin
- {
- HqlExprArray compares;
- ForEachChild(i, value)
- {
- IHqlExpression * cur = value->queryChild(i);
- LinkedHqlExpr castValue = cur;
- OwnedHqlExpr valueGuard;
- if (guardCastType)
- {
- castValue.setown(ensureExprType(castValue, guardCastType));
- valueGuard.setown(createBoolExpr(no_eq, ensureExprType(castValue, cur->queryType()), LINK(cur)));
- extendRangeCheck(valueGuard, valueGuard, cur, lengthExpr, (op == no_in));
- }
- OwnedHqlExpr cond = createRangeCompare(selector, castValue, lengthExpr, (op == no_in));
- if (valueGuard)
- cond.setown(createValue(no_and, makeBoolType(), valueGuard.getClear(), cond.getClear()));
- compares.append(*cond.getClear());
- }
- node_operator combineOp = (op == no_in) ? no_or : no_and;
- OwnedITypeInfo boolType = makeBoolType();
- newTest.setown(createBalanced(combineOp, boolType, compares));
- }
- KeyCondition * entry = new KeyCondition(selector, newTest, keyedKind, left->queryChild(1));
- matches.appendCondition(*entry);
- if (guard)
- matches.appendPreFilter(guard);
- return true;
- }
- bool FilterExtractor::extractSimpleCompareFilter(KeyConditionInfo & matches, IHqlExpression * expr, KeyedKind keyedKind)
- {
- OwnedHqlExpr promoted = getExplicitlyPromotedCompare(expr);
- IHqlExpression * l = promoted->queryChild(0);
- IHqlExpression * r = promoted->queryChild(1);
- bool duplicate = false;
- KeyFailureInfo reasonl, reasonr;
- node_operator op = expr->getOperator();
- IHqlExpression * matchedSelector = isKeyableFilter(l, r, duplicate, op, reasonl, keyedKind);
- Owned<KeyCondition> result;
- if (matchedSelector)
- {
- node_operator newOp = getModifiedOp(op, duplicate);
- if (newOp != no_none)
- {
- OwnedHqlExpr newFilter = createValue(newOp, expr->getType(), LINK(l), LINK(r));
- result.setown(new KeyCondition(matchedSelector, newFilter, keyedKind, querySubStringRange(l)));
- }
- }
- else
- {
- duplicate = false;
- matchedSelector = isKeyableFilter(r, l, duplicate, op, reasonr, keyedKind);
- if (matchedSelector)
- {
- node_operator newOp = getModifiedOp(getReverseOp(op), duplicate);
- if (newOp != no_none)
- {
- OwnedHqlExpr newFilter = createValue(newOp, expr->getType(), LINK(r), LINK(l));
- result.setown(new KeyCondition(matchedSelector, newFilter, keyedKind, querySubStringRange(r)));
- }
- }
- }
- bool extracted = (result != NULL);
- if (extracted)
- {
- matches.appendCondition(*result.getClear());
- }
- else
- {
- failReason.merge(reasonl);
- failReason.merge(reasonr);
- }
- if (duplicate || !extracted)
- matches.appendPostFilter(expr);
- return extracted;
- }
- bool FilterExtractor::extractOrFilter(KeyConditionInfo & matches, IHqlExpression * expr, KeyedKind keyedKind)
- {
- HqlExprArray conds;
- expr->unwindList(conds, no_or);
- bool validOrFilter = true;
- HqlExprAttr invariant;
- CIArrayOf<KeyConditionInfo> branches;
- ForEachItemIn(idx, conds)
- {
- IHqlExpression & cur = conds.item(idx);
- if (isIndexInvariant(&cur, false))
- extendOrCondition(invariant, &cur);
- else
- {
- KeyConditionInfo & branch = * new KeyConditionInfo;
- branches.append(branch);
- //Can't generate an OR with a pure post-filter
- if (!extractFilters(branch, &cur, keyedKind))
- validOrFilter = false;
- }
- }
- //check all the conditions that are ORd together don't contain references to multiple fields.
- KeyCondition * firstBranch = NULL;
- bool multipleBranches = branches.ordinality() > 1;
- bool multipleSelectors = false;
- bool multipleConditions = false;
- bool hasPostFilter = false;
- ForEachItemIn(i1, branches)
- {
- KeyConditionInfo & branch = branches.item(i1);
- if (branch.postFilter)
- hasPostFilter = true;
- ForEachItemIn(i2, branch.conditions)
- {
- KeyCondition & cur = branch.conditions.item(i2);
- if (!firstBranch)
- firstBranch = &cur;
- else
- {
- multipleConditions = true;
- if (i1 == 0)
- {
- //Check for ((i.x = 3) AND (i.y = 4))
- //Which can only create a keyed filter if it is ORd with an invariant expression
- if (firstBranch->selector != cur.selector)
- multipleSelectors = true;
- }
- else
- {
- if ((firstBranch->selector != cur.selector) || multipleSelectors)
- validOrFilter = false;
- }
- }
- }
- }
- if (multipleBranches && hasPostFilter)
- validOrFilter = false;
- if (validOrFilter && firstBranch)
- {
- bool optimizeSingleBranch = true;
- if (multipleSelectors || hasPostFilter || (optimizeSingleBranch && !multipleConditions))
- {
- //Invariant ored with a conjunction
- //X or (A and B) -> (X or A) AND (X or B)
- assertex(branches.ordinality() == 1);
- KeyConditionInfo & branch = branches.item(0);
- OwnedHqlExpr preFilter = branch.preFilter ? extendCondition(no_or, invariant, branch.preFilter) : NULL;
- OwnedHqlExpr postFilter = branch.postFilter ? extendCondition(no_or, invariant, branch.postFilter) : NULL;
- matches.appendPreFilter(preFilter);
- matches.appendPostFilter(postFilter);
- ForEachItemIn(i2, branch.conditions)
- {
- KeyCondition & cur = branch.conditions.item(i2);
- OwnedHqlExpr filter = extendCondition(no_or, invariant, cur.expr);
- matches.conditions.append(*new KeyCondition(cur.selector, filter, keyedKind, cur.subrange));
- }
- }
- else
- {
- LinkedHqlExpr combinedCondition = invariant;
- ForEachItemIn(i1, branches)
- {
- KeyConditionInfo & branch = branches.item(i1);
- OwnedHqlExpr conjunction = branch.createConjunction();
- extendOrCondition(combinedCondition, conjunction);
- }
- matches.conditions.append(*new KeyCondition(firstBranch->selector, combinedCondition, keyedKind, nullptr));
- }
- return true;
- }
- else
- {
- matches.appendPostFilter(expr);
- KeyFailureInfo reason;
- reason.set(KFRor);
- failReason.merge(reason);
- return false;
- }
- }
- bool FilterExtractor::extractIfFilter(KeyConditionInfo & matches, IHqlExpression * expr, KeyedKind keyedKind)
- {
- //MORE: This could generate better code, but I don't think it is worth the effort at the moment.
- //Really, I should analyse left and right. Iterate each selector referenced. If there are no post conditions then
- //generate IF(a, X, Y) compound expression, otherwise generate the default below.
- IHqlExpression * cond = expr->queryChild(0);
- if ((keyedKind != KeyedNo) && isIndexInvariant(cond, false))
- {
- //Convert IF(a, X, Y) to... IF (a, X, true) AND IF (a, true, Y) to... (NOT a OR X) AND (a OR Y)
- OwnedHqlExpr inverseCond = getInverse(cond);
- OwnedHqlExpr trueValue = createBoolExpr(no_or, LINK(inverseCond), LINK(expr->queryChild(1)));
- OwnedHqlExpr falseValue = createBoolExpr(no_or, LINK(cond), LINK(expr->queryChild(2)));
- OwnedHqlExpr combined = createBoolExpr(no_and, LINK(trueValue), LINK(falseValue));
- return extractFilters(matches, combined, keyedKind);
- }
- matches.appendPostFilter(expr);
- return false;
- }
- bool FilterExtractor::containsTableSelects(IHqlExpression * expr)
- {
- HqlExprCopyArray inScope;
- expr->gatherTablesUsed(inScope);
- //Check that cursors for all inScope tables are already bound in the start context
- return inScope.find(*tableExpr->queryNormalizedSelector()) != NotFound;
- }
- void FilterExtractor::extractFilters(IHqlExpression * expr, SharedHqlExpr & extraFilter)
- {
- HqlExprArray conds;
- expr->unwindList(conds, no_and);
- extractFilters(conds, extraFilter);
- }
- void FilterExtractor::extractFilters(HqlExprArray & exprs, SharedHqlExpr & extraFilter)
- {
- OwnedHqlExpr savedFilter = keyed.postFilter.getClear();
- ForEachItemIn(i1, exprs)
- {
- IHqlExpression & cur = exprs.item(i1);
- switch (cur.getOperator())
- {
- case no_assertkeyed:
- case no_assertwild:
- extractFilters(keyed, &cur, KeyedNo);
- break;
- }
- }
- keyedExplicitly = (keyed.conditions.ordinality() != 0);
- cleanlyKeyedExplicitly = keyedExplicitly && !keyed.postFilter;
- ForEachItemIn(i2, exprs)
- {
- IHqlExpression & cur = exprs.item(i2);
- switch (cur.getOperator())
- {
- case no_assertkeyed:
- case no_assertwild:
- break;
- default:
- if (!keyedExplicitly)
- extractFilters(keyed, &cur, KeyedNo);
- else if (!cur.isAttribute() && isIndexInvariant(&cur, true))
- keyed.appendPreFilter(&cur);
- else
- keyed.appendPostFilter(&cur);
- break;
- }
- }
- extraFilter.set(keyed.postFilter);
- keyed.postFilter.setown(extendConditionOwn(no_and, savedFilter.getClear(), LINK(extraFilter)));
- }
- void FilterExtractor::extractFiltersFromFilterDs(IHqlExpression * expr)
- {
- HqlExprArray conds;
- HqlExprAttr dummy;
- unwindFilterConditions(conds, expr);
- extractFilters(conds, dummy);
- }
- void FilterExtractor::extractFoldedWildFields(IHqlExpression * expr)
- {
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_cast:
- case no_implicitcast:
- case no_add:
- case no_sub:
- //fields may have been transformed since folding...
- extractFoldedWildFields(expr->queryChild(0));
- break;
- case no_select:
- if (isKeySelect(expr))
- {
- KeyCondition * condition = new KeyCondition;
- condition->selector.set(expr);
- condition->isWild = true;
- condition->wasKeyed = true;
- keyed.conditions.append(*condition);
- }
- break;
- }
- }
- bool FilterExtractor::extractBoolFieldFilter(KeyConditionInfo & matches, IHqlExpression * selector, KeyedKind keyedKind, bool compareValue)
- {
- if (selector->isBoolean())
- {
- if (isKeySelect(selector) && okToKey(selector, keyedKind))
- {
- OwnedHqlExpr newFilter = createValue(no_eq, makeBoolType(), LINK(selector), createConstant(compareValue));
- matches.appendCondition(*new KeyCondition(selector, newFilter, keyedKind, nullptr));
- return true;
- }
- }
- return false;
- }
- bool FilterExtractor::extractFilters(KeyConditionInfo & matches, IHqlExpression * expr, KeyedKind keyedKind)
- {
- if (!expr->isAttribute() && isIndexInvariant(expr, true))
- {
- extendAndCondition(matches.preFilter, expr);
- return true;
- }
- IHqlExpression *l = expr->queryChild(0);
- IHqlExpression *r = expr->queryChild(1);
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_and:
- {
- bool extracted = extractFilters(matches, l, keyedKind);
- if (!extractFilters(matches, r, keyedKind)) extracted = false;
- return extracted;
- }
- case no_or:
- return extractOrFilter(matches, expr, keyedKind);
- case no_attr:
- case no_attr_expr:
- case no_attr_link:
- return true;
- case no_not:
- {
- IHqlExpression * arg = expr->queryChild(0);
- OwnedHqlExpr inverse = getInverse(arg);
- if (inverse->queryBody() != expr->queryBody())
- return extractFilters(matches, inverse, keyedKind);
- if ((arg->getOperator() == no_select) && arg->isBoolean() &&
- extractBoolFieldFilter(matches, arg, keyedKind, false))
- return true;
- matches.appendPostFilter(expr);
- return false;
- }
- case no_between:
- case no_notbetween:
- {
- //Convert this into two comparisons because that will handle weird boundary conditions much better.
- OwnedHqlExpr normalized = expandBetween(expr);
- return extractFilters(matches, normalized, keyedKind);
- }
- case no_eq:
- case no_ne:
- {
- bool duplicate = false;
- if (matchSubstringFilter(matches, op, l, r, keyedKind, duplicate) || matchSubstringFilter(matches, op, r, l, keyedKind, duplicate))
- {
- if (duplicate)
- matches.appendPostFilter(expr);
- return true;
- }
- return extractSimpleCompareFilter(matches, expr, keyedKind);
- }
- case no_in:
- case no_notin:
- {
- bool duplicate = false;
- if (matchSubstringFilter(matches, op, l, r, keyedKind, duplicate))
- {
- if (duplicate)
- matches.appendPostFilter(expr);
- return true;
- }
- return extractSimpleCompareFilter(matches, expr, keyedKind);
- }
- case no_gt:
- case no_lt:
- case no_ge:
- case no_le:
- return extractSimpleCompareFilter(matches, expr, keyedKind);
- case no_assertkeyed:
- {
- KeyFailureInfo reason;
- reason.merge(failReason);
- failReason.clear();
- bool extend = expr->hasAttribute(extendAtom);
- if (!extractFilters(matches, l, extend ? KeyedExtend : KeyedYes))
- {
- if (!extend)
- failReason.reportError(errorReceiver, expr);
- }
- IHqlExpression * original = expr->queryAttribute(_selectors_Atom);
- if (original)
- {
- ForEachChild(i, original)
- extractFoldedWildFields(original->queryChild(i));
- }
- failReason.merge(reason);
- return true;
- }
- case no_assertwild:
- {
- if (l->getOperator() == no_all)
- {
- IHqlExpression * original = expr->queryAttribute(_selectors_Atom);
- assertex(original);
- ForEachChild(i, original)
- extractFoldedWildFields(original->queryChild(i));
- }
- else
- {
- IHqlExpression * selector = queryKeyableSelector(l);
- if (!selector)
- {
- StringBuffer keyname;
- errorReceiver.throwError1(HQLERR_WildNotReferenceIndex, queryKeyName(keyname));
- }
- KeyCondition * condition = new KeyCondition;
- condition->selector.set(selector);
- condition->isWild = true;
- matches.appendCondition(*condition);
- }
- return true;
- }
- case no_if:
- return extractIfFilter(matches, expr, keyedKind);
- case no_select:
- {
- if (expr->isBoolean() && extractBoolFieldFilter(matches, expr, keyedKind, true))
- return true;
- matches.appendPostFilter(expr);
- return false;
- }
- default:
- // Add this condition to the catchall expr
- matches.appendPostFilter(expr);
- return false;
- }
- }
- void FilterExtractor::extractAllFilters(IHqlExpression * dataset)
- {
- for (;;)
- {
- switch (dataset->getOperator())
- {
- case no_newkeyindex:
- return;
- case no_filter:
- extractAllFilters(dataset->queryChild(0));
- extractFiltersFromFilterDs(dataset);
- return;
- case no_compound_indexread:
- case no_newusertable:
- case no_hqlproject:
- case no_distributed:
- case no_preservemeta:
- case no_unordered:
- case no_sorted:
- case no_stepped:
- case no_grouped:
- case no_alias_scope:
- case no_dataset_alias:
- break;
- default:
- UNIMPLEMENTED;
- }
- dataset = dataset->queryChild(0);
- }
- }
- bool FilterExtractor::isKeyed()
- {
- ForEachItemIn(i, keyed.conditions)
- {
- if (!keyed.conditions.item(i).isWild)
- return true;
- }
- return false;
- }
- bool expandFilename(StringBuffer & s, IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_constant:
- expr->toString(s);
- return true;
- case no_getresult:
- //more
- break;
- case no_concat:
- {
- bool hadString = expandFilename(s, expr->queryChild(0));
- unsigned oldLength = s.length();
- s.append("+");
- if (expandFilename(s, expr->queryChild(1)) || hadString)
- return true;
- s.setLength(oldLength);
- return false;
- }
- case no_alias:
- case no_cast:
- case no_implicitcast:
- return expandFilename(s, expr->queryChild(0));
- }
- if (hasNamedSymbol(expr))
- {
- s.append(expr->queryName());
- return true;
- }
- s.append("...");
- return false;
- }
- void FilterExtractor::reportFailureReason(IHqlExpression * cond)
- {
- failReason.reportError(errorReceiver, cond);
- }
- const char * FilterExtractor::queryKeyName(StringBuffer & s)
- {
- IAtom * name = tableExpr->queryName();
- if (name)
- s.append(" \'").append(name).append("'");
- else
- {
- IHqlExpression * filename = queryTableFilename(tableExpr);
- if (filename)
- {
- if (!expandFilename(s.append(' '), filename))
- s.clear();
- }
- }
- return s.str();
- }
- IHqlExpression * FilterExtractor::querySimpleJoinValue(IHqlExpression * selector)
- {
- IHqlExpression * matched = NULL;
- ForEachItemIn(cond, keyed.conditions)
- {
- KeyCondition & cur = keyed.conditions.item(cond);
- if (cur.selector == selector)
- {
- if (!cur.isWild)
- {
- if (matched)
- return NULL;
- IHqlExpression * matchExpr = cur.expr;
- if (matchExpr->getOperator() != no_eq)
- return NULL;
- if (matchExpr->queryChild(0) != selector)
- return NULL;
- matched = matchExpr->queryChild(1);
- }
- }
- }
- return matched;
- }
- //-- Runtime filter generation
- static __declspec(noreturn) void throwTooComplex(IHqlExpression * expr) __attribute__((noreturn));
- static void throwTooComplex(IHqlExpression * expr)
- {
- StringBuffer ecl;
- getExprECL(expr, ecl);
- throwError1(HQLERR_ExprTooComplexForValueSet, ecl.str());
- }
- static IHqlExpression * getNormalizedCompareValue(IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_cast:
- case no_implicitcast:
- {
- OwnedHqlExpr arg = getNormalizedCompareValue(expr->queryChild(0));
- return ensureExprType(arg, expr->queryType());
- }
- case no_nofold:
- return getNormalizedCompareValue(expr->queryChild(0));
- default:
- return LINK(expr);
- }
- }
- static bool normalizeValueCompare(OwnedHqlExpr & normalized, bool & truncated, IHqlExpression * lhs, IHqlExpression * value)
- {
- //Primarily to aid creating test cases....
- OwnedHqlExpr rhs = getNormalizedCompareValue(value);
- if (!rhs->queryValue())
- return false;
- truncated = false;
- LinkedHqlExpr compareValue = rhs->queryBody();
- OwnedHqlExpr recastValue;
- if ((lhs->getOperator() != no_select) || (lhs->queryType() != compareValue->queryType()))
- {
- OwnedHqlExpr temp = castToFieldAndBack(lhs, compareValue);
- if (temp != compareValue)
- {
- truncated = true;
- }
- }
- normalized.set(rhs);
- return true;
- }
- IValueSet * FilterExtractor::createValueSetInExpr(IHqlExpression * selector, const RtlTypeInfo & type, IHqlExpression * expr) const
- {
- if (!exprReferencesDataset(expr, tableExpr))
- throwTooComplex(expr); //MORE: Possibly report another error
- IHqlExpression * rhs = expr->queryChild(1);
- Owned<IValueSet> values = createValueSet(type);
- switch (rhs->getOperator())
- {
- case no_null:
- return values.getClear();
- case no_all:
- values->addAll();
- return values.getClear();
- case no_list:
- break;
- default:
- throwTooComplex(expr);
- }
- ForEachChild(i, rhs)
- {
- OwnedHqlExpr normalized;
- bool truncated = false;
- if (!normalizeValueCompare(normalized, truncated, selector, rhs->queryChild(i)))
- throwTooComplex(expr); //MORE: Possibly report another error
- if (!normalized->queryValue())
- throwTooComplex(expr);
- if (!truncated)
- {
- MemoryBuffer compareValue;
- if (!createConstantField(compareValue, selector, normalized))
- throwTooComplex(expr);
- const char * compareRaw = compareValue.toByteArray();
- values->addRawRange(compareRaw, compareRaw);
- }
- }
- if (expr->getOperator() == no_notin)
- values->invertSet();
- return values.getClear();
- }
- IValueSet * FilterExtractor::createValueSetCompareExpr(IHqlExpression * selector, const RtlTypeInfo & type, IHqlExpression * expr) const
- {
- if (!exprReferencesDataset(expr, tableExpr))
- throwTooComplex(expr); //MORE: Possibly report another error
- OwnedHqlExpr normalized;
- bool truncated = false;
- if (!normalizeValueCompare(normalized, truncated, selector, expr->queryChild(1)))
- throwTooComplex(expr); //MORE: Possibly report another error
- Owned<IValueSet> values = createValueSet(type);
- MemoryBuffer compareValue;
- if (!normalized->queryValue())
- throwTooComplex(expr);
- if (!createConstantField(compareValue, selector, normalized))
- throwTooComplex(expr);
- //TBD: Support substring matches
- size32_t subLength = MatchFullString;
- node_operator op = expr->getOperator();
- const char * compareRaw = compareValue.toByteArray();
- switch (op)
- {
- case no_eq:
- if (!truncated)
- values->addRawRangeEx(compareRaw, compareRaw, subLength);
- break;
- case no_ne:
- values->addAll();
- if (!truncated)
- values->killRawRangeEx(compareRaw, compareRaw, subLength);
- break;
- case no_le:
- {
- Owned<IValueTransition> upper = values->createRawTransitionEx(CMPle, compareRaw, subLength);
- values->addRange(nullptr, upper);
- break;
- }
- case no_lt:
- {
- Owned<IValueTransition> upper = values->createRawTransitionEx(truncated ? CMPle: CMPlt, compareRaw, subLength);
- values->addRange(nullptr, upper);
- break;
- }
- case no_ge:
- {
- Owned<IValueTransition> lower = values->createRawTransitionEx(truncated ? CMPgt : CMPge, compareRaw, subLength);
- values->addRange(lower, nullptr);
- break;
- }
- case no_gt:
- {
- Owned<IValueTransition> lower = values->createRawTransitionEx(CMPgt, compareRaw, subLength);
- values->addRange(lower, nullptr);
- break;
- }
- case no_between:
- case no_notbetween:
- {
- //NB: This should only be generated for substring queries. User betweens are converted
- //to two separate comparisons to cope with range issues.
- throwUnexpectedOp(op);
- }
- default:
- throwUnexpectedOp(op);
- }
- return values.getClear();
- }
- IValueSet * FilterExtractor::createValueSetExpr(IHqlExpression * selector, const RtlTypeInfo & type, IHqlExpression * expr) const
- {
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_in:
- case no_notin:
- return createValueSetInExpr(selector, type, expr);
- case no_if:
- break; // Report an error
- case no_and:
- {
- Owned<IValueSet> left = createValueSetExpr(selector, type, expr->queryChild(0));
- Owned<IValueSet> right = createValueSetExpr(selector, type, expr->queryChild(1));
- left->intersectSet(right);
- return left.getClear();
- }
- case no_or:
- {
- Owned<IValueSet> left = createValueSetExpr(selector, type, expr->queryChild(0));
- Owned<IValueSet> right = createValueSetExpr(selector, type, expr->queryChild(1));
- left->unionSet(right);
- return left.getClear();
- }
- case no_eq:
- case no_ne:
- case no_gt:
- case no_ge:
- case no_lt:
- case no_le:
- return createValueSetCompareExpr(selector, type, expr);
- }
- throwTooComplex(expr);
- }
- IFieldFilter * FilterExtractor::createSingleFieldFilter(IRtlFieldTypeDeserializer &deserializer) const
- {
- IHqlExpression * selector = keyed.conditions.item(0).selector;
- return createFieldFilter(deserializer, selector);
- }
- IFieldFilter * FilterExtractor::createFieldFilter(IRtlFieldTypeDeserializer &deserializer, IHqlExpression * selector) const
- {
- const RtlTypeInfo * fieldType = buildRtlType(deserializer, selector->queryType());
- Owned<IValueSet> values;
- HqlExprArray conditions;
- ForEachItemIn(i, keyed.conditions)
- {
- KeyCondition & cur = keyed.conditions.item(i);
- if (cur.selector == selector)
- conditions.append(*LINK(cur.expr));
- }
- if (conditions.ordinality())
- {
- OwnedITypeInfo boolType = makeBoolType();
- OwnedHqlExpr fullExpr = createBalanced(no_and, boolType, conditions);
- values.setown(createValueSetExpr(selector, *fieldType, fullExpr));
- }
- unsigned fieldIndex = keyableSelects.find(*selector);
- if (values)
- return ::createFieldFilter(fieldIndex, values);
- return createWildFieldFilter(fieldIndex, *fieldType);
- }
|