queuecreator.cpp 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850
  1. //
  2. // Copyright (c) 2017-2026, Manticore Software LTD (https://manticoresearch.com)
  3. // Copyright (c) 2001-2016, Andrew Aksyonoff
  4. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  5. // All rights reserved
  6. //
  7. // This program is free software; you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License. You should have
  9. // received a copy of the GPL license along with this program; if you
  10. // did not, you can find it at http://www.gnu.org/
  11. //
  12. #include "queuecreator.h"
  13. #include "std/openhash.h"
  14. #include "schema/rset.h"
  15. #include "columnargrouper.h"
  16. #include "columnarsort.h"
  17. #include "exprgeodist.h"
  18. #include "exprremap.h"
  19. #include "exprdocstore.h"
  20. #include "sphinxjson.h"
  21. #include "joinsorter.h"
  22. #include "columnarexpr.h"
  23. #include "sphinxfilter.h"
  24. #include "queryprofile.h"
  25. #include "knnmisc.h"
  26. #include "sorterscroll.h"
  27. #include "sphinxquery/sphinxquery.h"
  28. static const char g_sIntAttrPrefix[] = "@int_attr_";
  29. static const char g_sIntJsonPrefix[] = "@groupbystr_";
  30. bool HasImplicitGrouping ( const CSphQuery & tQuery )
  31. {
  32. auto fnIsImplicit = [] ( const CSphQueryItem & t )
  33. {
  34. return ( t.m_eAggrFunc!=SPH_AGGR_NONE ) || t.m_sExpr=="count(*)" || t.m_sExpr=="@distinct";
  35. };
  36. return tQuery.m_sGroupBy.IsEmpty() ? tQuery.m_dItems.any_of(fnIsImplicit) : false;
  37. }
  38. bool sphHasExpressions ( const CSphQuery & tQuery, const CSphSchema & tSchema )
  39. {
  40. return !tQuery.m_dItems.all_of ( [&tSchema] ( const CSphQueryItem& tItem )
  41. {
  42. const CSphString & sExpr = tItem.m_sExpr;
  43. // all expressions that come from parser are automatically aliased
  44. assert ( !tItem.m_sAlias.IsEmpty() );
  45. return sExpr=="*"
  46. || ( tSchema.GetAttrIndex ( sExpr.cstr() )>=0 && tItem.m_eAggrFunc==SPH_AGGR_NONE && tItem.m_sAlias==sExpr )
  47. || IsGroupbyMagic ( sExpr );
  48. });
  49. }
  50. int GetAliasedAttrIndex ( const CSphString & sAttr, const CSphQuery & tQuery, const ISphSchema & tSchema )
  51. {
  52. int iAttr = tSchema.GetAttrIndex ( sAttr.cstr() );
  53. if ( iAttr>=0 )
  54. return iAttr;
  55. // try aliased groupby attr (facets)
  56. ARRAY_FOREACH ( i, tQuery.m_dItems )
  57. {
  58. if ( sAttr==tQuery.m_dItems[i].m_sExpr )
  59. return tSchema.GetAttrIndex ( tQuery.m_dItems[i].m_sAlias.cstr() );
  60. else if ( sAttr==tQuery.m_dItems[i].m_sAlias )
  61. return tSchema.GetAttrIndex ( tQuery.m_dItems[i].m_sExpr.cstr() );
  62. }
  63. return iAttr;
  64. }
  65. static bool IsCount ( const CSphString & s )
  66. {
  67. return s=="@count" || s=="count(*)";
  68. }
  69. static bool IsGroupby ( const CSphString & s )
  70. {
  71. return s=="@groupby"
  72. || s=="@distinct"
  73. || s=="groupby()"
  74. || IsSortJsonInternal(s);
  75. }
  76. bool IsGroupbyMagic ( const CSphString & s )
  77. {
  78. return IsGroupby ( s ) || IsCount ( s );
  79. }
  80. ESphAttr DetermineNullMaskType ( int iNumAttrs )
  81. {
  82. if ( iNumAttrs<=32 )
  83. return SPH_ATTR_INTEGER;
  84. if ( iNumAttrs<=64 )
  85. return SPH_ATTR_BIGINT;
  86. return SPH_ATTR_STRINGPTR;
  87. }
  88. const char * GetInternalAttrPrefix()
  89. {
  90. return g_sIntAttrPrefix;
  91. }
  92. const char * GetInternalJsonPrefix()
  93. {
  94. return g_sIntJsonPrefix;
  95. }
  96. bool IsSortStringInternal ( const CSphString & sColumnName )
  97. {
  98. assert ( sColumnName.cstr ());
  99. return ( strncmp ( sColumnName.cstr (), g_sIntAttrPrefix, sizeof ( g_sIntAttrPrefix )-1 )==0 );
  100. }
  101. bool IsSortJsonInternal ( const CSphString& sColumnName )
  102. {
  103. assert ( sColumnName.cstr ());
  104. return ( strncmp ( sColumnName.cstr (), g_sIntJsonPrefix, sizeof ( g_sIntJsonPrefix )-1 )==0 );
  105. }
  106. CSphString SortJsonInternalSet ( const CSphString& sColumnName )
  107. {
  108. CSphString sName;
  109. if ( !sColumnName.IsEmpty() )
  110. ( StringBuilder_c () << g_sIntJsonPrefix << sColumnName ).MoveTo ( sName );
  111. return sName;
  112. }
  113. ///////////////////////////////////////////////////////////////////////////////
  114. static bool ExprHasJoinPrefix ( const CSphString & sExpr, const JoinArgs_t * pArgs )
  115. {
  116. if ( !pArgs )
  117. return false;
  118. CSphString sPrefix;
  119. sPrefix.SetSprintf ( "%s.", pArgs->m_sIndex2.cstr() );
  120. const char * szFound = strstr ( sExpr.cstr(), sPrefix.cstr() );
  121. if ( !szFound )
  122. return false;
  123. if ( szFound > sExpr.cstr() )
  124. {
  125. char c = *(szFound-1);
  126. if ( ( c>='0' && c<='9' ) || ( c>='a' && c<='z' ) || ( c>='A' && c<='Z' ) || c=='_' )
  127. return false;
  128. }
  129. return true;
  130. }
  131. static inline ESphSortKeyPart Attr2Keypart ( ESphAttr eType )
  132. {
  133. switch ( eType )
  134. {
  135. case SPH_ATTR_FLOAT:
  136. return SPH_KEYPART_FLOAT;
  137. case SPH_ATTR_DOUBLE:
  138. return SPH_KEYPART_DOUBLE;
  139. case SPH_ATTR_STRING:
  140. return SPH_KEYPART_STRING;
  141. case SPH_ATTR_JSON:
  142. case SPH_ATTR_JSON_PTR:
  143. case SPH_ATTR_JSON_FIELD:
  144. case SPH_ATTR_JSON_FIELD_PTR:
  145. case SPH_ATTR_STRINGPTR:
  146. return SPH_KEYPART_STRINGPTR;
  147. default:
  148. return SPH_KEYPART_INT;
  149. }
  150. }
  151. ///////////////////////////////////////////////////////////////////////////////
  152. void CSphGroupSorterSettings::FixupLocators ( const ISphSchema * pOldSchema, const ISphSchema * pNewSchema )
  153. {
  154. sphFixupLocator ( m_tLocGroupby, pOldSchema, pNewSchema );
  155. sphFixupLocator ( m_tLocCount, pOldSchema, pNewSchema );
  156. sphFixupLocator ( m_tLocDistinct, pOldSchema, pNewSchema );
  157. sphFixupLocator ( m_tLocGroupbyStr, pOldSchema, pNewSchema );
  158. if ( m_pDistinctFetcher )
  159. m_pDistinctFetcher->FixupLocators ( pOldSchema, pNewSchema );
  160. }
  161. void CSphGroupSorterSettings::SetupDistinctAccuracy ( int iThresh )
  162. {
  163. if ( !iThresh )
  164. {
  165. m_iDistinctAccuracy = 0;
  166. return;
  167. }
  168. iThresh = int ( float(iThresh) / OpenHashTable_T<int,int>::GetLoadFactor() ) + 1;
  169. m_iDistinctAccuracy = iThresh ? sphLog2(iThresh) + 4 : 0;
  170. m_iDistinctAccuracy = Min ( m_iDistinctAccuracy, 18 );
  171. m_iDistinctAccuracy = Max ( m_iDistinctAccuracy, 14 );
  172. }
  173. ///////////////////////////////////////////////////////////////////////////////
  174. class QueueCreator_c
  175. {
  176. public:
  177. bool m_bMulti = false;
  178. bool m_bCreate = true;
  179. bool m_bZonespanlist = false;
  180. DWORD m_uPackedFactorFlags = SPH_FACTOR_DISABLE;
  181. bool m_bJoinedGroupSort = false; // do we need joined attrs for sorting/grouping?
  182. QueueCreator_c ( const SphQueueSettings_t & tSettings, const CSphQuery & tQuery, CSphString & sError, StrVec_t * pExtra, QueryProfile_c * pProfile, const char * szParent );
  183. bool SetupComputeQueue();
  184. bool SetupGroupQueue();
  185. bool SetupQueue();
  186. CSphRsetSchema & SorterSchema() const { return *m_pSorterSchema; }
  187. bool HasJson() const { return m_tGroupSorterSettings.m_bJson; }
  188. bool SetSchemaGroupQueue ( const CSphRsetSchema & tNewSchema );
  189. /// creates proper queue for given query
  190. /// may return NULL on error; in this case, error message is placed in sError
  191. /// if the pUpdate is given, creates the updater's queue and perform the index update
  192. /// instead of searching
  193. ISphMatchSorter * CreateQueue();
  194. private:
  195. const SphQueueSettings_t & m_tSettings;
  196. const CSphQuery & m_tQuery;
  197. CSphString & m_sError;
  198. StrVec_t * m_pExtra = nullptr;
  199. QueryProfile_c * m_pProfile = nullptr;
  200. const char * m_szParent = nullptr;
  201. bool m_bHasCount = false;
  202. bool m_bHasGroupByExpr = false;
  203. sph::StringSet m_hQueryAttrs;
  204. std::unique_ptr<CSphRsetSchema> m_pSorterSchema;
  205. bool m_bGotGroupby;
  206. bool m_bRandomize;
  207. ESphSortFunc m_eMatchFunc = FUNC_REL_DESC;
  208. ESphSortFunc m_eGroupFunc = FUNC_REL_DESC;
  209. CSphMatchComparatorState m_tStateMatch;
  210. CSphVector<ExtraSortExpr_t> m_dMatchJsonExprs;
  211. CSphMatchComparatorState m_tStateGroup;
  212. CSphVector<ExtraSortExpr_t> m_dGroupJsonExprs;
  213. CSphGroupSorterSettings m_tGroupSorterSettings;
  214. CSphVector<std::pair<int,bool>> m_dGroupColumns;
  215. StrVec_t m_dGroupJsonAttrs;
  216. bool m_bHeadWOGroup;
  217. bool m_bGotDistinct;
  218. bool m_bExprsNeedDocids = false;
  219. // for sorter to create pooled attributes
  220. bool m_bHaveStar = false;
  221. // fixme! transform to StringSet on end of merge!
  222. sph::StringSet m_hQueryColumns; // FIXME!!! unify with Extra schema after merge master into branch
  223. sph::StringSet m_hQueryDups;
  224. sph::StringSet m_hExtra;
  225. bool ParseQueryItem ( const CSphQueryItem & tItem );
  226. bool MaybeAddGeodistColumn();
  227. bool MaybeAddExprColumn();
  228. bool MaybeAddExpressionsFromSelectList();
  229. bool AddExpressionsForUpdates();
  230. bool MaybeAddGroupbyMagic ( bool bGotDistinct );
  231. bool AddKNNDistColumn();
  232. bool AddKNNRescoreColumn();
  233. bool AddJoinAttrs();
  234. bool CheckJoinOnTypeCast ( const CSphString & sIdx, const CSphString & sAttr, ESphAttr eTypeCast );
  235. bool AddJoinFilterAttrs();
  236. bool AddJsonJoinOnFilter ( const CSphString & sAttr1, const CSphString & sAttr2, ESphAttr eTypeCast );
  237. bool AddNullBitmask();
  238. bool AddColumnarJoinOnFilter ( const CSphString & sAttr );
  239. bool CheckHavingConstraints() const;
  240. bool SetupGroupbySettings ( bool bHasImplicitGrouping );
  241. void AssignOrderByToPresortStage ( const int * pAttrs, int iAttrCount );
  242. void AddAttrsFromSchema ( const ISphSchema & tSchema, const CSphString & sPrefix );
  243. void ModifyExprForJoin ( CSphColumnInfo & tExprCol, const CSphString & sExpr );
  244. void SelectExprEvalStage ( CSphColumnInfo & tExprCol );
  245. void ReplaceGroupbyStrWithExprs ( CSphMatchComparatorState & tState, int iNumOldAttrs );
  246. void ReplaceStaticStringsWithExprs ( CSphMatchComparatorState & tState );
  247. void ReplaceJsonWithExprs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs );
  248. void AddColumnarExprsAsAttrs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs );
  249. void RemapAttrs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs );
  250. static void SetupRemapColJson ( CSphColumnInfo & tRemapCol, CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs, int iStateAttr ) ;
  251. const CSphColumnInfo * GetGroupbyStr ( int iAttr, int iNumOldAttrs ) const;
  252. bool SetupMatchesSortingFunc();
  253. bool SetupGroupSortingFunc ( bool bGotDistinct );
  254. bool AddGroupbyStuff();
  255. void AddKnnDistSort ( CSphString & sSortBy );
  256. bool ParseJoinExpr ( CSphColumnInfo & tExprCol, const CSphString & sAttr, const CSphString & sExpr ) const;
  257. bool SetGroupSorting();
  258. void ExtraAddSortkeys ( const int * dAttrs );
  259. bool AddStoredFieldExpressions();
  260. bool AddColumnarAttributeExpressions();
  261. void CreateGrouperByAttr ( ESphAttr eType, const CSphColumnInfo & tGroupByAttr, bool & bGrouperUsesAttrs );
  262. void SelectStageForColumnarExpr ( CSphColumnInfo & tExprCol );
  263. void FetchDependencyChains ( StrVec_t & dDependentCols );
  264. void PropagateEvalStage ( CSphColumnInfo & tExprCol, StrVec_t & dDependentCols );
  265. bool SetupDistinctAttr();
  266. bool PredictAggregates() const;
  267. bool ReplaceWithColumnarItem ( const CSphString & sAttr, ESphEvalStage eStage );
  268. int ReduceOrIncreaseMaxMatches() const;
  269. int AdjustMaxMatches ( int iMaxMatches ) const;
  270. bool ConvertColumnarToDocstore();
  271. bool SetupAggregateExpr ( CSphColumnInfo & tExprCol, const CSphString & sExpr, DWORD uQueryPackedFactorFlags );
  272. bool SetupColumnarAggregates ( CSphColumnInfo & tExprCol );
  273. bool IsJoinAttr ( const CSphString & sAttr ) const;
  274. void ReplaceJsonGroupbyWithStrings ( CSphString & sJsonGroupBy );
  275. void UpdateAggregateDependencies ( CSphColumnInfo & tExprCol );
  276. int GetGroupbyAttrIndex() const { return GetAliasedAttrIndex ( m_tQuery.m_sGroupBy, m_tQuery, *m_pSorterSchema ); }
  277. int GetGroupDistinctAttrIndex() const { return GetAliasedAttrIndex ( m_tQuery.m_sGroupDistinct, m_tQuery, *m_pSorterSchema ); }
  278. bool CanCalcFastCountDistinct() const;
  279. bool CanCalcFastCountFilter() const;
  280. bool CanCalcFastCount() const;
  281. PrecalculatedSorterResults_t FetchPrecalculatedValues() const;
  282. ISphMatchSorter * SpawnQueue();
  283. std::unique_ptr<ISphFilter> CreateAggrFilter() const;
  284. void SetupCollation();
  285. bool Err ( const char * sFmt, ... ) const;
  286. };
  287. QueueCreator_c::QueueCreator_c ( const SphQueueSettings_t & tSettings, const CSphQuery & tQuery, CSphString & sError, StrVec_t * pExtra, QueryProfile_c * pProfile, const char * szParent )
  288. : m_tSettings ( tSettings )
  289. , m_tQuery ( tQuery )
  290. , m_sError ( sError )
  291. , m_pExtra ( pExtra )
  292. , m_pProfile ( pProfile )
  293. , m_szParent ( szParent )
  294. , m_pSorterSchema { std::make_unique<CSphRsetSchema>() }
  295. {
  296. // short-cuts
  297. m_sError = "";
  298. *m_pSorterSchema = m_tSettings.m_tSchema;
  299. m_dMatchJsonExprs.Resize ( CSphMatchComparatorState::MAX_ATTRS );
  300. m_dGroupJsonExprs.Resize ( CSphMatchComparatorState::MAX_ATTRS );
  301. }
  302. static CSphString GetAliasedColumnarAttrName ( const CSphColumnInfo & tAttr )
  303. {
  304. if ( !tAttr.IsColumnarExpr() )
  305. return tAttr.m_sName;
  306. CSphString sAliasedCol;
  307. tAttr.m_pExpr->Command ( SPH_EXPR_GET_COLUMNAR_COL, &sAliasedCol );
  308. return sAliasedCol;
  309. }
  310. void QueueCreator_c::CreateGrouperByAttr ( ESphAttr eType, const CSphColumnInfo & tGroupByAttr, bool & bGrouperUsesAttrs )
  311. {
  312. assert ( m_pSorterSchema );
  313. auto & tSchema = *m_pSorterSchema;
  314. const CSphAttrLocator & tLoc = tGroupByAttr.m_tLocator;
  315. switch ( eType )
  316. {
  317. case SPH_ATTR_JSON:
  318. case SPH_ATTR_JSON_FIELD:
  319. {
  320. ExprParseArgs_t tExprArgs;
  321. tExprArgs.m_eCollation = m_tQuery.m_eCollation;
  322. ISphExprRefPtr_c pExpr { sphExprParse ( m_tQuery.m_sGroupBy.cstr(), tSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprArgs ) };
  323. m_tGroupSorterSettings.m_pGrouper = CreateGrouperJsonField ( tLoc, pExpr );
  324. m_tGroupSorterSettings.m_bJson = true;
  325. }
  326. break;
  327. case SPH_ATTR_STRING:
  328. case SPH_ATTR_STRINGPTR:
  329. // percolate select list push matches with string_ptr
  330. // check if it is a columnar attr or an expression spawned instead of a columnar attr
  331. // even if it is an expression, spawn a new one, because a specialized grouper works a lot faster because it doesn't allocate and store string in the match
  332. if ( tGroupByAttr.IsColumnar() || tGroupByAttr.IsColumnarExpr() )
  333. {
  334. m_tGroupSorterSettings.m_pGrouper = CreateGrouperColumnarString ( GetAliasedColumnarAttrName(tGroupByAttr), m_tQuery.m_eCollation );
  335. bGrouperUsesAttrs = false;
  336. }
  337. else if ( tGroupByAttr.m_pExpr && !tGroupByAttr.m_pExpr->IsDataPtrAttr() )
  338. {
  339. m_tGroupSorterSettings.m_pGrouper = CreateGrouperStringExpr ( tGroupByAttr.m_pExpr, m_tQuery.m_eCollation );
  340. bGrouperUsesAttrs = false;
  341. }
  342. else
  343. m_tGroupSorterSettings.m_pGrouper = CreateGrouperString ( tLoc, m_tQuery.m_eCollation );
  344. break;
  345. case SPH_ATTR_UINT32SET:
  346. case SPH_ATTR_INT64SET:
  347. case SPH_ATTR_UINT32SET_PTR:
  348. case SPH_ATTR_INT64SET_PTR:
  349. if ( tGroupByAttr.IsColumnar() || tGroupByAttr.IsColumnarExpr() )
  350. {
  351. m_tGroupSorterSettings.m_pGrouper = CreateGrouperColumnarMVA ( GetAliasedColumnarAttrName(tGroupByAttr), eType );
  352. bGrouperUsesAttrs = false;
  353. }
  354. else
  355. {
  356. if ( eType==SPH_ATTR_UINT32SET || eType==SPH_ATTR_UINT32SET_PTR )
  357. m_tGroupSorterSettings.m_pGrouper = CreateGrouperMVA32(tLoc);
  358. else
  359. m_tGroupSorterSettings.m_pGrouper = CreateGrouperMVA64(tLoc);
  360. }
  361. break;
  362. case SPH_ATTR_BOOL:
  363. case SPH_ATTR_INTEGER:
  364. case SPH_ATTR_BIGINT:
  365. case SPH_ATTR_FLOAT:
  366. if ( tGroupByAttr.IsColumnar() || ( tGroupByAttr.IsColumnarExpr() && tGroupByAttr.m_eStage>SPH_EVAL_PREFILTER ) )
  367. {
  368. m_tGroupSorterSettings.m_pGrouper = CreateGrouperColumnarInt ( GetAliasedColumnarAttrName(tGroupByAttr), eType );
  369. bGrouperUsesAttrs = false;
  370. }
  371. break;
  372. default:
  373. break;
  374. }
  375. if ( !m_tGroupSorterSettings.m_pGrouper )
  376. m_tGroupSorterSettings.m_pGrouper = CreateGrouperAttr(tLoc);
  377. }
  378. bool QueueCreator_c::SetupDistinctAttr()
  379. {
  380. const CSphString & sDistinct = m_tQuery.m_sGroupDistinct;
  381. if ( sDistinct.IsEmpty() )
  382. return true;
  383. assert ( m_pSorterSchema );
  384. auto & tSchema = *m_pSorterSchema;
  385. int iDistinct = tSchema.GetAttrIndex ( sDistinct.cstr() );
  386. if ( iDistinct<0 )
  387. {
  388. CSphString sJsonCol;
  389. if ( !sphJsonNameSplit ( sDistinct.cstr(), m_tQuery.m_sJoinIdx.cstr(), &sJsonCol ) )
  390. {
  391. return Err ( "group-count-distinct attribute '%s' not found", sDistinct.cstr() );
  392. return false;
  393. }
  394. CSphColumnInfo tExprCol ( sDistinct.cstr(), SPH_ATTR_JSON_FIELD_PTR );
  395. tExprCol.m_eStage = SPH_EVAL_SORTER;
  396. tExprCol.m_uAttrFlags = CSphColumnInfo::ATTR_JOINED;
  397. m_pSorterSchema->AddAttr ( tExprCol, true );
  398. iDistinct = m_pSorterSchema->GetAttrIndex ( tExprCol.m_sName.cstr() );
  399. }
  400. const auto & tDistinctAttr = tSchema.GetAttr(iDistinct);
  401. if ( IsNotRealAttribute(tDistinctAttr) )
  402. return Err ( "group-count-distinct attribute '%s' not found", sDistinct.cstr() );
  403. if ( tDistinctAttr.IsColumnar() )
  404. m_tGroupSorterSettings.m_pDistinctFetcher = CreateColumnarDistinctFetcher ( tDistinctAttr.m_sName, tDistinctAttr.m_eAttrType, m_tQuery.m_eCollation );
  405. else
  406. m_tGroupSorterSettings.m_pDistinctFetcher = CreateDistinctFetcher ( tDistinctAttr.m_sName, tDistinctAttr.m_tLocator, tDistinctAttr.m_eAttrType );
  407. m_bJoinedGroupSort |= IsJoinAttr(tDistinctAttr.m_sName);
  408. return true;
  409. }
  410. static bool IsMvaGroupBy ( const ISphSchema & tSchema, const CSphColumnInfo & tAttr, const CSphString & sGroupBy )
  411. {
  412. if ( IsMvaAttr ( tAttr.m_eAttrType ) )
  413. return true;
  414. if ( tAttr.IsColumnarExpr() )
  415. {
  416. CSphString sCol = GetAliasedColumnarAttrName ( tAttr );
  417. int iCol = tSchema.GetAttrIndex ( sCol.cstr() );
  418. return iCol>=0 && IsMvaAttr ( tSchema.GetAttr(iCol).m_eAttrType );
  419. }
  420. return false;
  421. };
  422. bool QueueCreator_c::SetupGroupbySettings ( bool bHasImplicitGrouping )
  423. {
  424. if ( m_tQuery.m_sGroupBy.IsEmpty() && !bHasImplicitGrouping )
  425. return true;
  426. if ( m_tQuery.m_eGroupFunc==SPH_GROUPBY_ATTRPAIR )
  427. return Err ( "SPH_GROUPBY_ATTRPAIR is not supported any more (just group on 'bigint' attribute)" );
  428. assert ( m_pSorterSchema );
  429. auto & tSchema = *m_pSorterSchema;
  430. m_tGroupSorterSettings.m_iMaxMatches = m_tSettings.m_iMaxMatches;
  431. if ( !SetupDistinctAttr() )
  432. return false;
  433. CSphString sJsonColumn;
  434. if ( m_tQuery.m_eGroupFunc==SPH_GROUPBY_MULTIPLE )
  435. {
  436. CSphVector<CSphColumnInfo> dAttrs;
  437. VecRefPtrs_t<ISphExpr *> dJsonKeys;
  438. StrVec_t dGroupBy;
  439. sph::Split ( m_tQuery.m_sGroupBy.cstr (), -1, ",", [&] ( const char * sToken, int iLen )
  440. {
  441. CSphString sGroupBy ( sToken, iLen );
  442. sGroupBy.Trim ();
  443. dGroupBy.Add ( std::move ( sGroupBy ));
  444. } );
  445. dGroupBy.Uniq();
  446. for ( auto & sGroupBy : dGroupBy )
  447. {
  448. int iAttr = GetAliasedAttrIndex ( sGroupBy, m_tQuery, tSchema );
  449. bool bJoined = iAttr>=0 && tSchema.GetAttr(iAttr).IsJoined();
  450. CSphString sJsonExpr;
  451. if ( ( iAttr<0 || bJoined ) && sphJsonNameSplit ( sGroupBy.cstr(), m_tQuery.m_sJoinIdx.cstr(), &sJsonColumn ) )
  452. {
  453. sJsonExpr = sGroupBy;
  454. sGroupBy = sJsonColumn;
  455. iAttr = tSchema.GetAttrIndex ( sGroupBy.cstr() );
  456. }
  457. if ( iAttr<0 )
  458. return Err( "group-by attribute '%s' not found", sGroupBy.cstr() );
  459. auto tAttr = tSchema.GetAttr ( iAttr );
  460. ESphAttr eType = tAttr.m_eAttrType;
  461. if ( IsMvaGroupBy ( tSchema, tAttr, sGroupBy ) )
  462. return Err ( "MVA values can't be used in multiple group-by" );
  463. if ( eType==SPH_ATTR_JSON && sJsonExpr.IsEmpty() )
  464. return Err ( "JSON blob can't be used in multiple group-by" );
  465. dAttrs.Add ( tAttr );
  466. m_dGroupColumns.Add ( { iAttr, true } );
  467. m_dGroupJsonAttrs.Add(sJsonExpr);
  468. if ( !sJsonExpr.IsEmpty() )
  469. {
  470. ExprParseArgs_t tExprArgs;
  471. dJsonKeys.Add ( sphExprParse ( sJsonExpr.cstr(), tSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprArgs ) );
  472. }
  473. else if ( tAttr.m_eAttrType==SPH_ATTR_JSON_FIELD )
  474. {
  475. assert ( tAttr.m_pExpr );
  476. dJsonKeys.Add ( tAttr.m_pExpr->Clone() );
  477. } else
  478. {
  479. dJsonKeys.Add ( nullptr );
  480. }
  481. m_bJoinedGroupSort |= IsJoinAttr(sGroupBy);
  482. }
  483. m_tGroupSorterSettings.m_pGrouper = CreateGrouperMulti ( dAttrs, std::move(dJsonKeys), m_tQuery.m_eCollation );
  484. return true;
  485. }
  486. int iGroupBy = GetGroupbyAttrIndex();
  487. bool bJoined = iGroupBy>=0 && m_pSorterSchema->GetAttr(iGroupBy).IsJoined();
  488. if ( ( iGroupBy<0 || bJoined ) && sphJsonNameSplit ( m_tQuery.m_sGroupBy.cstr(), m_tQuery.m_sJoinIdx.cstr(), &sJsonColumn ) )
  489. {
  490. const int iAttr = tSchema.GetAttrIndex ( sJsonColumn.cstr() );
  491. if ( iAttr<0 )
  492. return Err ( "groupby: no such attribute '%s'", sJsonColumn.cstr ());
  493. if ( tSchema.GetAttr(iAttr).m_eAttrType!=SPH_ATTR_JSON
  494. && tSchema.GetAttr(iAttr).m_eAttrType!=SPH_ATTR_JSON_PTR )
  495. return Err ( "groupby: attribute '%s' does not have subfields (must be sql_attr_json)", sJsonColumn.cstr() );
  496. if ( m_tQuery.m_eGroupFunc!=SPH_GROUPBY_ATTR )
  497. return Err ( "groupby: legacy groupby modes are not supported on JSON attributes" );
  498. m_dGroupColumns.Add ( { iAttr, true } );
  499. ExprParseArgs_t tExprArgs;
  500. tExprArgs.m_eCollation = m_tQuery.m_eCollation;
  501. ISphExprRefPtr_c pExpr { sphExprParse ( m_tQuery.m_sGroupBy.cstr(), tSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprArgs ) };
  502. m_tGroupSorterSettings.m_pGrouper = CreateGrouperJsonField ( tSchema.GetAttr(iAttr).m_tLocator, pExpr );
  503. m_tGroupSorterSettings.m_bJson = true;
  504. m_bJoinedGroupSort |= IsJoinAttr(sJsonColumn);
  505. return true;
  506. }
  507. if ( bHasImplicitGrouping )
  508. {
  509. m_tGroupSorterSettings.m_bImplicit = true;
  510. return true;
  511. }
  512. // setup groupby attr
  513. if ( iGroupBy<0 )
  514. return Err ( "group-by attribute '%s' not found", m_tQuery.m_sGroupBy.cstr() );
  515. const CSphColumnInfo & tGroupByAttr = tSchema.GetAttr(iGroupBy);
  516. if ( m_tSettings.m_bComputeItems && tGroupByAttr.m_pExpr && tGroupByAttr.m_pExpr->UsesDocstore() )
  517. return Err ( "unable to group by stored field '%s'", m_tQuery.m_sGroupBy.cstr() );
  518. ESphAttr eType = tGroupByAttr.m_eAttrType;
  519. CSphAttrLocator tLoc = tGroupByAttr.m_tLocator;
  520. m_bJoinedGroupSort |= IsJoinAttr ( tGroupByAttr.m_sName );
  521. bool bGrouperUsesAttrs = true;
  522. switch (m_tQuery.m_eGroupFunc )
  523. {
  524. case SPH_GROUPBY_DAY:
  525. m_tGroupSorterSettings.m_pGrouper = CreateGrouperDay(tLoc); break;
  526. case SPH_GROUPBY_WEEK:
  527. m_tGroupSorterSettings.m_pGrouper = CreateGrouperWeek(tLoc); break;
  528. case SPH_GROUPBY_MONTH:
  529. m_tGroupSorterSettings.m_pGrouper = CreateGrouperMonth(tLoc); break;
  530. case SPH_GROUPBY_YEAR:
  531. m_tGroupSorterSettings.m_pGrouper = CreateGrouperYear(tLoc); break;
  532. case SPH_GROUPBY_ATTR:
  533. CreateGrouperByAttr ( eType, tGroupByAttr, bGrouperUsesAttrs );
  534. break;
  535. default:
  536. return Err ( "invalid group-by mode (mode=%d)", m_tQuery.m_eGroupFunc );
  537. }
  538. m_dGroupColumns.Add ( { iGroupBy, bGrouperUsesAttrs } );
  539. return true;
  540. }
  541. // move expressions used in ORDER BY or WITHIN GROUP ORDER BY to presort phase
  542. void QueueCreator_c::AssignOrderByToPresortStage ( const int * pAttrs, int iAttrCount )
  543. {
  544. if ( !iAttrCount )
  545. return;
  546. assert ( pAttrs );
  547. assert ( m_pSorterSchema );
  548. StrVec_t dCur;
  549. sph::StringSet hProcessed;
  550. // add valid attributes to processing list
  551. for ( int i=0; i<iAttrCount; ++i )
  552. if ( pAttrs[i]>=0 )
  553. dCur.Add ( m_pSorterSchema->GetAttr ( pAttrs[i] ).m_sName );
  554. // collect columns which affect current expressions
  555. ARRAY_FOREACH ( i, dCur )
  556. {
  557. if ( hProcessed[dCur[i]] )
  558. continue;
  559. const CSphColumnInfo * pCol = m_pSorterSchema->GetAttr ( dCur[i].cstr() );
  560. assert(pCol);
  561. if ( pCol->m_eStage>SPH_EVAL_PRESORT && pCol->m_pExpr )
  562. pCol->m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dCur );
  563. // filter out duplicates to avoid circular dependencies
  564. hProcessed.Add ( dCur[i] );
  565. }
  566. // get rid of dupes
  567. dCur.Uniq();
  568. // fix up of attributes stages
  569. for ( const auto & sAttr : dCur )
  570. {
  571. auto pCol = const_cast<CSphColumnInfo *>( m_pSorterSchema->GetAttr ( sAttr.cstr() ) );
  572. assert(pCol);
  573. if ( pCol->m_eStage==SPH_EVAL_FINAL )
  574. pCol->m_eStage = SPH_EVAL_PRESORT;
  575. }
  576. }
  577. void QueueCreator_c::ExtraAddSortkeys ( const int * dAttrs )
  578. {
  579. for ( int i=0; i<CSphMatchComparatorState::MAX_ATTRS; ++i )
  580. if ( dAttrs[i]>=0 )
  581. {
  582. const auto & tAttr = m_pSorterSchema->GetAttr ( dAttrs[i] );
  583. m_bJoinedGroupSort |= tAttr.IsJoined();
  584. m_hExtra.Add ( tAttr.m_sName );
  585. if ( m_tSettings.m_bComputeItems )
  586. {
  587. // check if dependent columns are joined
  588. StrVec_t dCols;
  589. dCols.Add ( tAttr.m_sName );
  590. FetchDependencyChains(dCols);
  591. for ( const auto & sAttr : dCols )
  592. {
  593. const CSphColumnInfo * pAttr = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  594. assert(pAttr);
  595. m_bJoinedGroupSort |= pAttr->IsJoined();
  596. }
  597. }
  598. }
  599. }
  600. bool QueueCreator_c::Err ( const char * sFmt, ... ) const
  601. {
  602. va_list ap;
  603. va_start ( ap, sFmt );
  604. m_sError.SetSprintfVa ( sFmt, ap );
  605. va_end ( ap );
  606. return false;
  607. }
  608. void QueueCreator_c::SelectStageForColumnarExpr ( CSphColumnInfo & tExprCol )
  609. {
  610. if ( !tExprCol.IsColumnarExpr() )
  611. {
  612. tExprCol.m_eStage = SPH_EVAL_PREFILTER;
  613. return;
  614. }
  615. // columnar expressions are a special case
  616. // it is sometimes faster to evaluate them in the filter than to evaluate the expression, store it in the match and then use it in the filter
  617. // FIXME: add sorters?
  618. int iRank = 0;
  619. iRank += tExprCol.m_sName==m_tQuery.m_sGroupBy ? 1 : 0;
  620. iRank += m_tQuery.m_dFilters.any_of ( [&tExprCol]( const CSphFilterSettings & tFilter ) { return tFilter.m_sAttrName==tExprCol.m_sName; } ) ? 1 : 0;
  621. if ( iRank>1 )
  622. tExprCol.m_eStage = SPH_EVAL_PREFILTER;
  623. }
  624. void QueueCreator_c::FetchDependencyChains ( StrVec_t & dDependentCols )
  625. {
  626. ARRAY_FOREACH ( i, dDependentCols )
  627. {
  628. const CSphString & sAttr = dDependentCols[i];
  629. int iAttr = m_pSorterSchema->GetAttrIndex ( sAttr.cstr() );
  630. assert ( iAttr>=0 );
  631. if ( m_pSorterSchema->IsRemovedAttr(iAttr) )
  632. continue;
  633. const CSphColumnInfo & tCol = m_pSorterSchema->GetAttr(iAttr);
  634. int iOldLen = dDependentCols.GetLength();
  635. // handle chains of dependencies (e.g. SELECT 1+attr f1, f1-1 f2 ... WHERE f2>5)
  636. if ( tCol.m_pExpr )
  637. tCol.m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dDependentCols );
  638. // some expressions depend on the column they are attached to (json fast key)
  639. // so filter out duplicates to avoid circular dependencies
  640. for ( int iNewAttr = iOldLen; iNewAttr < dDependentCols.GetLength(); iNewAttr++ )
  641. if ( dDependentCols[iNewAttr]==dDependentCols[i] )
  642. dDependentCols.Remove(iNewAttr);
  643. }
  644. dDependentCols.Uniq();
  645. }
  646. void QueueCreator_c::PropagateEvalStage ( CSphColumnInfo & tExprCol, StrVec_t & dDependentCols )
  647. {
  648. bool bWeight = false;
  649. for ( const auto & sAttr : dDependentCols )
  650. {
  651. const CSphColumnInfo * pCol = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  652. assert(pCol);
  653. bWeight |= pCol->m_bWeight;
  654. }
  655. if ( bWeight )
  656. {
  657. tExprCol.m_eStage = SPH_EVAL_PRESORT;
  658. tExprCol.m_bWeight = true;
  659. }
  660. for ( const auto & sAttr : dDependentCols )
  661. {
  662. auto pDep = const_cast<CSphColumnInfo *> ( m_pSorterSchema->GetAttr ( sAttr.cstr() ) );
  663. if ( pDep->IsJoined() )
  664. continue;
  665. if ( pDep->m_eStage > tExprCol.m_eStage )
  666. pDep->m_eStage = tExprCol.m_eStage;
  667. }
  668. }
  669. bool QueueCreator_c::SetupAggregateExpr ( CSphColumnInfo & tExprCol, const CSphString & sExpr, DWORD uQueryPackedFactorFlags )
  670. {
  671. // validate that MAX/MIN/SUM/AVG cannot be used on string/text columns
  672. // This check must happen BEFORE the switch statement that may modify tExprCol.m_eAttrType
  673. if ( tExprCol.m_eAggrFunc==SPH_AGGR_MAX || tExprCol.m_eAggrFunc==SPH_AGGR_MIN
  674. || tExprCol.m_eAggrFunc==SPH_AGGR_SUM || tExprCol.m_eAggrFunc==SPH_AGGR_AVG )
  675. {
  676. if ( tExprCol.m_eAttrType==SPH_ATTR_STRING || tExprCol.m_eAttrType==SPH_ATTR_STRINGPTR )
  677. {
  678. const char * sFunc = ( tExprCol.m_eAggrFunc==SPH_AGGR_MAX ) ? "MAX"
  679. : ( tExprCol.m_eAggrFunc==SPH_AGGR_MIN ) ? "MIN"
  680. : ( tExprCol.m_eAggrFunc==SPH_AGGR_SUM ) ? "SUM" : "AVG";
  681. return Err ( "%s() cannot be used on text/string column '%s'", sFunc, sExpr.cstr() );
  682. }
  683. }
  684. switch ( tExprCol.m_eAggrFunc )
  685. {
  686. case SPH_AGGR_AVG:
  687. // force AVG() to be computed in doubles
  688. tExprCol.m_eAttrType = SPH_ATTR_DOUBLE;
  689. tExprCol.m_tLocator.m_iBitCount = 64;
  690. break;
  691. case SPH_AGGR_CAT:
  692. // force GROUP_CONCAT() to be computed as strings
  693. tExprCol.m_eAttrType = SPH_ATTR_STRINGPTR;
  694. tExprCol.m_tLocator.m_iBitCount = ROWITEMPTR_BITS;
  695. break;
  696. case SPH_AGGR_SUM:
  697. if ( tExprCol.m_eAttrType==SPH_ATTR_BOOL )
  698. {
  699. tExprCol.m_eAttrType = SPH_ATTR_INTEGER;
  700. tExprCol.m_tLocator.m_iBitCount = 32;
  701. } else if ( tExprCol.m_eAttrType==SPH_ATTR_INTEGER )
  702. {
  703. tExprCol.m_eAttrType = SPH_ATTR_BIGINT;
  704. tExprCol.m_tLocator.m_iBitCount = 64;
  705. }
  706. break;
  707. default:
  708. break;
  709. }
  710. // force explicit type conversion for JSON attributes
  711. if ( tExprCol.m_eAggrFunc!=SPH_AGGR_NONE && tExprCol.m_eAttrType==SPH_ATTR_JSON_FIELD )
  712. return Err ( "ambiguous attribute type '%s', use INTEGER(), BIGINT() or DOUBLE() conversion functions", sExpr.cstr() );
  713. if ( uQueryPackedFactorFlags & SPH_FACTOR_JSON_OUT )
  714. tExprCol.m_eAttrType = SPH_ATTR_FACTORS_JSON;
  715. return true;
  716. }
  717. bool QueueCreator_c::SetupColumnarAggregates ( CSphColumnInfo & tExprCol )
  718. {
  719. StrVec_t dDependentCols;
  720. tExprCol.m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dDependentCols );
  721. FetchDependencyChains(dDependentCols);
  722. if ( !dDependentCols.GetLength() )
  723. return tExprCol.IsColumnarExpr();
  724. if ( dDependentCols.GetLength()==1 )
  725. {
  726. int iAttr = m_pSorterSchema->GetAttrIndex ( dDependentCols[0].cstr() );
  727. assert ( iAttr>=0 );
  728. if ( m_pSorterSchema->IsRemovedAttr(iAttr) )
  729. return false;
  730. const CSphColumnInfo & tColumnarAttr = m_pSorterSchema->GetAttr(iAttr);
  731. if ( tColumnarAttr.IsColumnarExpr() )
  732. {
  733. CSphString sColumnarCol;
  734. tColumnarAttr.m_pExpr->Command ( SPH_EXPR_GET_COLUMNAR_COL, &sColumnarCol );
  735. // let aggregate expression know that it is working with that columnar attribute
  736. tExprCol.m_pExpr->Command ( SPH_EXPR_SET_COLUMNAR_COL, &sColumnarCol );
  737. return true;
  738. }
  739. }
  740. return false;
  741. }
  742. void QueueCreator_c::UpdateAggregateDependencies ( CSphColumnInfo & tExprCol )
  743. {
  744. /// update aggregate dependencies (e.g. SELECT 1+attr f1, min(f1), ...)
  745. StrVec_t dDependentCols;
  746. tExprCol.m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dDependentCols );
  747. FetchDependencyChains ( dDependentCols );
  748. for ( const auto & sAttr : dDependentCols )
  749. {
  750. auto pDep = const_cast<CSphColumnInfo *>( m_pSorterSchema->GetAttr ( sAttr.cstr() ) );
  751. assert(pDep);
  752. bool bJoinedAttr = ExprHasJoinPrefix ( pDep->m_sName, m_tSettings.m_pJoinArgs.get() ) && pDep->m_eStage==SPH_EVAL_SORTER && pDep->m_tLocator.m_bDynamic && !pDep->m_pExpr;
  753. if ( pDep->m_eStage>tExprCol.m_eStage && !bJoinedAttr )
  754. pDep->m_eStage = tExprCol.m_eStage;
  755. }
  756. }
  757. void QueueCreator_c::AddAttrsFromSchema ( const ISphSchema & tSchema, const CSphString & sPrefix )
  758. {
  759. for ( int i=0; i<tSchema.GetAttrsCount(); i++ )
  760. {
  761. CSphString sAttrName = tSchema.GetAttr(i).m_sName;
  762. sAttrName.SetSprintf ( "%s%s", sPrefix.scstr(), sAttrName.cstr() );
  763. m_hQueryDups.Add ( sAttrName );
  764. m_hQueryColumns.Add ( sAttrName );
  765. }
  766. }
  767. void QueueCreator_c::ModifyExprForJoin ( CSphColumnInfo & tExprCol, const CSphString & sExpr )
  768. {
  769. // even if it's over a join expr, it references another attr, so don't remove the expression
  770. if ( tExprCol.m_eAggrFunc!=SPH_AGGR_NONE )
  771. return;
  772. // check expr and its alias
  773. if ( !ExprHasJoinPrefix ( tExprCol.m_sName, m_tSettings.m_pJoinArgs.get() ) && !ExprHasJoinPrefix ( sExpr, m_tSettings.m_pJoinArgs.get() ) )
  774. return;
  775. if ( ExprHasLeftTableAttrs ( tExprCol.m_sName, *m_pSorterSchema ) || ExprHasLeftTableAttrs ( sExpr, *m_pSorterSchema ) )
  776. return;
  777. // we receive already precalculated JSON field expressions from JOIN
  778. // we don't need to evaluate them once again
  779. tExprCol.m_eAttrType = sphPlainAttrToPtrAttr ( tExprCol.m_eAttrType );
  780. tExprCol.m_pExpr = nullptr;
  781. tExprCol.m_eStage = SPH_EVAL_SORTER;
  782. tExprCol.m_uAttrFlags |= CSphColumnInfo::ATTR_JOINED;
  783. }
  784. void QueueCreator_c::SelectExprEvalStage ( CSphColumnInfo & tExprCol )
  785. {
  786. // is this expression used in filter?
  787. // OPTIMIZE? hash filters and do hash lookups?
  788. if ( tExprCol.m_eAttrType==SPH_ATTR_JSON_FIELD )
  789. return;
  790. if ( tExprCol.IsJoined() )
  791. return;
  792. ARRAY_FOREACH ( i, m_tQuery.m_dFilters )
  793. if ( m_tQuery.m_dFilters[i].m_sAttrName==tExprCol.m_sName )
  794. {
  795. // is this a hack?
  796. // m_bWeight is computed after EarlyReject() get called
  797. // that means we can't evaluate expressions with WEIGHT() in prefilter phase
  798. if ( tExprCol.m_bWeight )
  799. {
  800. tExprCol.m_eStage = SPH_EVAL_PRESORT; // special, weight filter ( short cut )
  801. break;
  802. }
  803. // so we are about to add a filter condition,
  804. // but it might depend on some preceding columns (e.g. SELECT 1+attr f1 ... WHERE f1>5)
  805. // lets detect those and move them to prefilter \ presort phase too
  806. StrVec_t dDependentCols;
  807. tExprCol.m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dDependentCols );
  808. SelectStageForColumnarExpr(tExprCol);
  809. FetchDependencyChains ( dDependentCols );
  810. PropagateEvalStage ( tExprCol, dDependentCols );
  811. break;
  812. }
  813. }
  814. bool QueueCreator_c::ParseQueryItem ( const CSphQueryItem & tItem )
  815. {
  816. assert ( m_pSorterSchema );
  817. const CSphString & sExpr = tItem.m_sExpr;
  818. bool bIsCount = IsCount(sExpr);
  819. m_bHasCount |= bIsCount;
  820. if ( sExpr=="*" )
  821. {
  822. m_bHaveStar = true;
  823. CSphString sPrefix;
  824. if ( m_tSettings.m_pJoinArgs )
  825. sPrefix.SetSprintf ( "%s.", m_tSettings.m_pJoinArgs->m_sIndex1.cstr() );
  826. AddAttrsFromSchema ( m_tSettings.m_tSchema, sPrefix );
  827. if ( m_tSettings.m_pJoinArgs )
  828. {
  829. sPrefix.SetSprintf ( "%s.", m_tSettings.m_pJoinArgs->m_sIndex2.cstr() );
  830. AddAttrsFromSchema ( m_tSettings.m_pJoinArgs->m_tJoinedSchema, sPrefix );
  831. }
  832. }
  833. // for now, just always pass "plain" attrs from index to sorter; they will be filtered on searchd level
  834. int iAttrIdx = m_tSettings.m_tSchema.GetAttrIndex ( sExpr.cstr() );
  835. bool bColumnar = iAttrIdx>=0 && m_tSettings.m_tSchema.GetAttr(iAttrIdx).IsColumnar();
  836. bool bPlainAttr = ( ( sExpr=="*" || ( iAttrIdx>=0 && tItem.m_eAggrFunc==SPH_AGGR_NONE && !bColumnar ) ) &&
  837. ( tItem.m_sAlias.IsEmpty() || tItem.m_sAlias==tItem.m_sExpr ) );
  838. if ( iAttrIdx>=0 )
  839. {
  840. ESphAttr eAttr = m_tSettings.m_tSchema.GetAttr ( iAttrIdx ).m_eAttrType;
  841. if ( eAttr==SPH_ATTR_STRING || eAttr==SPH_ATTR_STRINGPTR
  842. || eAttr==SPH_ATTR_UINT32SET || eAttr==SPH_ATTR_INT64SET )
  843. {
  844. if ( tItem.m_eAggrFunc!=SPH_AGGR_NONE )
  845. return Err ( "can not aggregate non-scalar attribute '%s'", tItem.m_sExpr.cstr() );
  846. }
  847. }
  848. if ( bPlainAttr || IsGroupby ( sExpr ) || bIsCount )
  849. {
  850. if ( sExpr!="*" && !tItem.m_sAlias.IsEmpty() )
  851. {
  852. m_hQueryDups.Add ( tItem.m_sAlias );
  853. if ( bPlainAttr )
  854. m_hQueryColumns.Add ( tItem.m_sExpr );
  855. }
  856. m_bHasGroupByExpr = IsGroupby ( sExpr );
  857. return true;
  858. }
  859. if ( IsKnnDist(sExpr) && m_pSorterSchema->GetAttrIndex ( GetKnnDistAttrName() )<0 )
  860. return Err ( "KNN_DIST() is only allowed for KNN() queries" );
  861. // not an attribute? must be an expression, and must be aliased by query parser
  862. assert ( !tItem.m_sAlias.IsEmpty() );
  863. // tricky part
  864. // we might be fed with precomputed matches, but it's all or nothing
  865. // the incoming match either does not have anything computed, or it has everything
  866. // unless it is a JOIN - then we have partially precomputed matches
  867. int iSorterAttr = m_pSorterSchema->GetAttrIndex ( tItem.m_sAlias.cstr() );
  868. if ( iSorterAttr>=0 )
  869. {
  870. if ( m_hQueryDups[tItem.m_sAlias] )
  871. {
  872. bool bJoined = !!(m_pSorterSchema->GetAttr(iSorterAttr).m_uAttrFlags & CSphColumnInfo::ATTR_JOINED);
  873. if ( bColumnar || bJoined ) // we might have several similar aliases for columnar attributes (and they are not plain attrs but expressions)
  874. return true;
  875. else
  876. return Err ( "alias '%s' must be unique (conflicts with another alias)", tItem.m_sAlias.cstr() );
  877. }
  878. }
  879. // a new and shiny expression, lets parse
  880. CSphColumnInfo tExprCol ( tItem.m_sAlias.cstr(), SPH_ATTR_NONE );
  881. DWORD uQueryPackedFactorFlags = SPH_FACTOR_DISABLE;
  882. bool bHasZonespanlist = false;
  883. bool bExprsNeedDocids = false;
  884. ExprParseArgs_t tExprParseArgs;
  885. tExprParseArgs.m_pAttrType = &tExprCol.m_eAttrType;
  886. tExprParseArgs.m_pUsesWeight = &tExprCol.m_bWeight;
  887. tExprParseArgs.m_pProfiler = m_tSettings.m_pProfiler;
  888. tExprParseArgs.m_eCollation = m_tQuery.m_eCollation;
  889. tExprParseArgs.m_pHook = m_tSettings.m_pHook;
  890. tExprParseArgs.m_pZonespanlist = &bHasZonespanlist;
  891. tExprParseArgs.m_pPackedFactorsFlags = &uQueryPackedFactorFlags;
  892. tExprParseArgs.m_pEvalStage = &tExprCol.m_eStage;
  893. tExprParseArgs.m_pStoredField = &tExprCol.m_uFieldFlags;
  894. tExprParseArgs.m_pNeedDocIds = &bExprsNeedDocids;
  895. // tricky bit
  896. // GROUP_CONCAT() adds an implicit TO_STRING() conversion on top of its argument
  897. // and then the aggregate operation simply concatenates strings as matches arrive
  898. // ideally, we would instead pass ownership of the expression to G_C() implementation
  899. // and also the original expression type, and let the string conversion happen in G_C() itself
  900. // but that ideal route seems somewhat more complicated in the current architecture
  901. if ( tItem.m_eAggrFunc==SPH_AGGR_CAT )
  902. {
  903. CSphString sExpr2;
  904. sExpr2.SetSprintf ( "TO_STRING(%s)", sExpr.cstr() );
  905. tExprCol.m_pExpr = sphExprParse ( sExpr2.cstr(), *m_pSorterSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprParseArgs );
  906. }
  907. else
  908. tExprCol.m_pExpr = sphExprParse ( sExpr.cstr(), *m_pSorterSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprParseArgs );
  909. m_uPackedFactorFlags |= uQueryPackedFactorFlags;
  910. m_bZonespanlist |= bHasZonespanlist;
  911. m_bExprsNeedDocids |= bExprsNeedDocids;
  912. tExprCol.m_eAggrFunc = tItem.m_eAggrFunc;
  913. tExprCol.m_iIndex = iSorterAttr>= 0 ? m_pSorterSchema->GetAttrIndexOriginal ( tItem.m_sAlias.cstr() ) : -1;
  914. if ( !tExprCol.m_pExpr )
  915. return Err ( "parse error: %s", m_sError.cstr() );
  916. if ( !SetupAggregateExpr ( tExprCol, tItem.m_sExpr, uQueryPackedFactorFlags ) )
  917. return false;
  918. ModifyExprForJoin ( tExprCol, tItem.m_sExpr );
  919. // postpone aggregates, add non-aggregates
  920. if ( tExprCol.m_eAggrFunc==SPH_AGGR_NONE )
  921. {
  922. SelectExprEvalStage(tExprCol);
  923. // add it!
  924. // NOTE, "final" stage might need to be fixed up later
  925. // we'll do that when parsing sorting clause
  926. m_pSorterSchema->AddAttr ( tExprCol, true );
  927. // remove original column after new attribute added or shadows this one
  928. if ( iSorterAttr>=0 )
  929. m_pSorterSchema->RemoveStaticAttr ( iSorterAttr );
  930. }
  931. else // some aggregate
  932. {
  933. bool bColumnarAggregate = SetupColumnarAggregates(tExprCol);
  934. bool bJoinAggregate = ExprHasJoinPrefix ( tExprCol.m_sName, m_tSettings.m_pJoinArgs.get() );
  935. // columnar aggregates have their own code path; no need to calculate them in presort
  936. // and aggregates over joined attrs are calculated in the join sorter
  937. tExprCol.m_eStage = ( bColumnarAggregate || bJoinAggregate ) ? SPH_EVAL_SORTER : SPH_EVAL_PRESORT;
  938. m_pSorterSchema->AddAttr ( tExprCol, true );
  939. m_hExtra.Add ( tExprCol.m_sName );
  940. if ( !bColumnarAggregate )
  941. UpdateAggregateDependencies ( tExprCol );
  942. // remove original column after new attribute added or shadows this one
  943. if ( iSorterAttr>=0 )
  944. m_pSorterSchema->RemoveStaticAttr ( iSorterAttr );
  945. }
  946. m_hQueryDups.Add ( tExprCol.m_sName );
  947. m_hQueryColumns.Add ( tExprCol.m_sName );
  948. // need to add all dependent columns for post limit expressions
  949. if ( tExprCol.m_eStage==SPH_EVAL_POSTLIMIT && tExprCol.m_pExpr )
  950. {
  951. StrVec_t dCur;
  952. tExprCol.m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dCur );
  953. ARRAY_FOREACH ( j, dCur )
  954. {
  955. const CSphColumnInfo * pCol = m_pSorterSchema->GetAttr ( dCur[j].cstr() );
  956. if ( pCol && pCol->m_pExpr )
  957. pCol->m_pExpr->Command ( SPH_EXPR_GET_DEPENDENT_COLS, &dCur );
  958. }
  959. dCur.Uniq();
  960. for ( const auto & sAttr : dCur )
  961. {
  962. const CSphColumnInfo * pDep = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  963. assert(pDep);
  964. m_hQueryColumns.Add ( pDep->m_sName );
  965. }
  966. }
  967. return true;
  968. }
  969. bool QueueCreator_c::ReplaceWithColumnarItem ( const CSphString & sAttr, ESphEvalStage eStage )
  970. {
  971. const CSphColumnInfo * pAttr = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  972. if ( !pAttr->IsColumnar() )
  973. return true;
  974. m_hQueryDups.Delete(sAttr);
  975. CSphQueryItem tItem;
  976. tItem.m_sExpr = tItem.m_sAlias = sAttr;
  977. if ( !ParseQueryItem ( tItem ) )
  978. return false;
  979. // force stage
  980. const CSphColumnInfo * pNewAttr = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  981. const_cast<CSphColumnInfo *>(pNewAttr)->m_eStage = Min ( pNewAttr->m_eStage, eStage );
  982. return true;
  983. }
  984. // Test for @geodist and setup, if any
  985. bool QueueCreator_c::MaybeAddGeodistColumn ()
  986. {
  987. if ( !m_tQuery.m_bGeoAnchor || m_pSorterSchema->GetAttrIndex ( "@geodist" )>=0 )
  988. return true;
  989. // replace columnar lat/lon with expressions before adding geodist
  990. if ( !ReplaceWithColumnarItem ( m_tQuery.m_sGeoLatAttr, SPH_EVAL_PREFILTER ) ) return false;
  991. if ( !ReplaceWithColumnarItem ( m_tQuery.m_sGeoLongAttr, SPH_EVAL_PREFILTER ) ) return false;
  992. auto pExpr = CreateExprGeodist ( m_tQuery, *m_pSorterSchema, m_sError );
  993. if ( !pExpr )
  994. return false;
  995. CSphColumnInfo tCol ( "@geodist", SPH_ATTR_FLOAT );
  996. tCol.m_pExpr = pExpr; // takes ownership, no need to for explicit pExpr release
  997. tCol.m_eStage = SPH_EVAL_PREFILTER; // OPTIMIZE? actual stage depends on usage
  998. m_pSorterSchema->AddAttr ( tCol, true );
  999. m_hExtra.Add ( tCol.m_sName );
  1000. m_hQueryAttrs.Add ( tCol.m_sName );
  1001. return true;
  1002. }
  1003. // Test for @expr and setup, if any
  1004. bool QueueCreator_c::MaybeAddExprColumn ()
  1005. {
  1006. if ( m_tQuery.m_eSort!=SPH_SORT_EXPR || m_pSorterSchema->GetAttrIndex ( "@expr" )>=0 )
  1007. return true;
  1008. CSphColumnInfo tCol ( "@expr", SPH_ATTR_FLOAT ); // enforce float type for backwards compatibility
  1009. // (i.e. too lazy to fix those tests right now)
  1010. bool bHasZonespanlist;
  1011. ExprParseArgs_t tExprArgs;
  1012. tExprArgs.m_pProfiler = m_tSettings.m_pProfiler;
  1013. tExprArgs.m_eCollation = m_tQuery.m_eCollation;
  1014. tExprArgs.m_pZonespanlist = &bHasZonespanlist;
  1015. tCol.m_pExpr = sphExprParse ( m_tQuery.m_sSortBy.cstr (), *m_pSorterSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprArgs );
  1016. if ( !tCol.m_pExpr )
  1017. return false;
  1018. m_bZonespanlist |= bHasZonespanlist;
  1019. tCol.m_eStage = SPH_EVAL_PRESORT;
  1020. m_pSorterSchema->AddAttr ( tCol, true );
  1021. m_hQueryAttrs.Add ( tCol.m_sName );
  1022. return true;
  1023. }
  1024. bool QueueCreator_c::AddStoredFieldExpressions()
  1025. {
  1026. for ( int i = 0; i<m_tSettings.m_tSchema.GetFieldsCount(); i++ )
  1027. {
  1028. const CSphColumnInfo & tField = m_tSettings.m_tSchema.GetField(i);
  1029. if ( !(tField.m_uFieldFlags & CSphColumnInfo::FIELD_STORED) )
  1030. continue;
  1031. CSphQueryItem tItem;
  1032. tItem.m_sExpr = tField.m_sName;
  1033. tItem.m_sAlias = tField.m_sName;
  1034. if ( !ParseQueryItem ( tItem ) )
  1035. return false;
  1036. }
  1037. return true;
  1038. }
  1039. bool QueueCreator_c::AddColumnarAttributeExpressions()
  1040. {
  1041. for ( int i = 0; i<m_tSettings.m_tSchema.GetAttrsCount(); i++ )
  1042. {
  1043. const CSphColumnInfo & tAttr = m_tSettings.m_tSchema.GetAttr(i);
  1044. const CSphColumnInfo * pSorterAttr = m_pSorterSchema->GetAttr ( tAttr.m_sName.cstr() );
  1045. if ( !tAttr.IsColumnar() || ( pSorterAttr && !pSorterAttr->IsColumnar() ) )
  1046. continue;
  1047. m_hQueryDups.Delete ( tAttr.m_sName );
  1048. CSphQueryItem tItem;
  1049. tItem.m_sExpr = tItem.m_sAlias = tAttr.m_sName;
  1050. if ( !ParseQueryItem ( tItem ) )
  1051. return false;
  1052. // copy knn settings
  1053. pSorterAttr = m_pSorterSchema->GetAttr ( tAttr.m_sName.cstr() );
  1054. const_cast<CSphColumnInfo *>(pSorterAttr)->m_tKNN = tAttr.m_tKNN;
  1055. }
  1056. return true;
  1057. }
  1058. // Add computed items
  1059. bool QueueCreator_c::MaybeAddExpressionsFromSelectList ()
  1060. {
  1061. // expressions from select items
  1062. if ( !m_tSettings.m_bComputeItems )
  1063. return true;
  1064. if ( !m_tQuery.m_dItems.all_of ( [&] ( const CSphQueryItem & v ) { return ParseQueryItem ( v ); } ))
  1065. return false;
  1066. if ( m_bHaveStar )
  1067. {
  1068. if ( !AddColumnarAttributeExpressions() )
  1069. return false;
  1070. if ( !AddStoredFieldExpressions() )
  1071. return false;
  1072. }
  1073. return true;
  1074. }
  1075. bool QueueCreator_c::AddExpressionsForUpdates()
  1076. {
  1077. if ( !m_tSettings.m_pCollection )
  1078. return true;
  1079. const CSphColumnInfo * pOldDocId = m_pSorterSchema->GetAttr ( sphGetDocidName() );
  1080. if ( !pOldDocId->IsColumnar() && !pOldDocId->IsColumnarExpr() )
  1081. return true;
  1082. if ( pOldDocId->IsColumnar() )
  1083. {
  1084. // add columnar id expressions to update queue. otherwise we won't be able to fetch docids which are needed to run updates/deletes
  1085. CSphQueryItem tItem;
  1086. tItem.m_sExpr = tItem.m_sAlias = sphGetDocidName();
  1087. if ( !ParseQueryItem ( tItem ) )
  1088. return false;
  1089. }
  1090. auto * pDocId = const_cast<CSphColumnInfo *> ( m_pSorterSchema->GetAttr ( sphGetDocidName() ) );
  1091. assert(pDocId);
  1092. pDocId->m_eStage = SPH_EVAL_PRESORT; // update/delete queues don't have real Finalize(), so just evaluate it at presort stage
  1093. return true;
  1094. }
  1095. bool QueueCreator_c::IsJoinAttr ( const CSphString & sAttr ) const
  1096. {
  1097. if ( !m_tSettings.m_pJoinArgs )
  1098. return false;
  1099. CSphString sPrefix;
  1100. sPrefix.SetSprintf ( "%s.", m_tSettings.m_pJoinArgs->m_sIndex2.cstr() );
  1101. return sAttr.Begins ( sPrefix.cstr() );
  1102. }
  1103. void QueueCreator_c::ReplaceJsonGroupbyWithStrings ( CSphString & sJsonGroupBy )
  1104. {
  1105. auto AddColumn = [this] ( const CSphColumnInfo & tCol )
  1106. {
  1107. m_pSorterSchema->AddAttr ( tCol, true );
  1108. m_hQueryColumns.Add ( tCol.m_sName );
  1109. };
  1110. if ( m_tGroupSorterSettings.m_bJson )
  1111. {
  1112. bool bJoinAttr = IsJoinAttr ( m_tQuery.m_sGroupBy );
  1113. sJsonGroupBy = SortJsonInternalSet ( m_tQuery.m_sGroupBy );
  1114. if ( !m_pSorterSchema->GetAttr ( sJsonGroupBy.cstr() ) )
  1115. {
  1116. CSphColumnInfo tGroupbyStr ( sJsonGroupBy.cstr() );
  1117. if ( bJoinAttr )
  1118. tGroupbyStr.m_eAttrType = SPH_ATTR_STRINGPTR;
  1119. else
  1120. tGroupbyStr.m_eAttrType = SPH_ATTR_JSON_FIELD;
  1121. tGroupbyStr.m_eStage = SPH_EVAL_SORTER;
  1122. AddColumn ( tGroupbyStr );
  1123. }
  1124. if ( bJoinAttr )
  1125. {
  1126. // we can't do grouping directly on joined JSON fields
  1127. // so we need to change the grouper
  1128. // fixme! this will not work on stuff that generates multiple groupby keys (like JSON arrays)
  1129. const CSphColumnInfo * pRemapped = m_pSorterSchema->GetAttr ( sJsonGroupBy.cstr() );
  1130. assert(pRemapped);
  1131. m_tGroupSorterSettings.m_pGrouper = CreateGrouperString ( pRemapped->m_tLocator, m_tQuery.m_eCollation );
  1132. m_tGroupSorterSettings.m_bJson = false;
  1133. }
  1134. }
  1135. else if ( m_tQuery.m_eGroupFunc==SPH_GROUPBY_MULTIPLE && m_bJoinedGroupSort )
  1136. {
  1137. bool bGrouperChanged = false;
  1138. ARRAY_FOREACH ( i, m_dGroupColumns )
  1139. {
  1140. const CSphColumnInfo & tAttr = m_pSorterSchema->GetAttr ( m_dGroupColumns[i].first );
  1141. bool bJoinAttr = IsJoinAttr ( tAttr.m_sName );
  1142. bool bJson = tAttr.m_eAttrType==SPH_ATTR_JSON_PTR || tAttr.m_eAttrType==SPH_ATTR_JSON_FIELD_PTR;
  1143. if ( bJoinAttr && bJson )
  1144. {
  1145. sJsonGroupBy = SortJsonInternalSet ( m_dGroupJsonAttrs[i] );
  1146. if ( !m_pSorterSchema->GetAttr ( sJsonGroupBy.cstr() ) )
  1147. {
  1148. CSphColumnInfo tGroupbyStr ( sJsonGroupBy.cstr() );
  1149. tGroupbyStr.m_eAttrType = SPH_ATTR_STRINGPTR;
  1150. tGroupbyStr.m_eStage = SPH_EVAL_SORTER;
  1151. AddColumn ( tGroupbyStr );
  1152. }
  1153. m_dGroupColumns[i].first = m_pSorterSchema->GetAttrIndex ( sJsonGroupBy.cstr() );
  1154. m_dGroupJsonAttrs[i] = "";
  1155. bGrouperChanged = true;
  1156. }
  1157. }
  1158. if ( bGrouperChanged )
  1159. {
  1160. CSphVector<CSphColumnInfo> dAttrs;
  1161. VecRefPtrs_t<ISphExpr *> dJsonKeys;
  1162. ARRAY_FOREACH ( i, m_dGroupColumns )
  1163. {
  1164. dAttrs.Add ( m_pSorterSchema->GetAttr ( m_dGroupColumns[i].first ) );
  1165. const CSphString & sJsonExpr = m_dGroupJsonAttrs[i];
  1166. if ( !sJsonExpr.IsEmpty() )
  1167. {
  1168. ExprParseArgs_t tExprArgs;
  1169. dJsonKeys.Add ( sphExprParse ( sJsonExpr.cstr(), *m_pSorterSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprArgs ) );
  1170. }
  1171. else
  1172. dJsonKeys.Add(nullptr);
  1173. }
  1174. m_tGroupSorterSettings.m_pGrouper = CreateGrouperMulti ( dAttrs, std::move(dJsonKeys), m_tQuery.m_eCollation );
  1175. }
  1176. }
  1177. }
  1178. bool QueueCreator_c::MaybeAddGroupbyMagic ( bool bGotDistinct )
  1179. {
  1180. CSphString sJsonGroupBy;
  1181. // now let's add @groupby etc. if needed
  1182. if ( m_bGotGroupby && m_pSorterSchema->GetAttrIndex ( "@groupby" )<0 )
  1183. {
  1184. ESphAttr eGroupByResult = ( !m_tGroupSorterSettings.m_bImplicit )
  1185. ? m_tGroupSorterSettings.m_pGrouper->GetResultType ()
  1186. : SPH_ATTR_INTEGER; // implicit do not have grouper
  1187. // all FACET group by should be the widest possible type
  1188. if ( m_tQuery.m_bFacet || m_tQuery.m_bFacetHead || m_bMulti )
  1189. eGroupByResult = SPH_ATTR_BIGINT;
  1190. CSphColumnInfo tGroupby ( "@groupby", eGroupByResult );
  1191. CSphColumnInfo tCount ( "@count", SPH_ATTR_BIGINT );
  1192. tGroupby.m_eStage = SPH_EVAL_SORTER;
  1193. tCount.m_eStage = SPH_EVAL_SORTER;
  1194. auto AddColumn = [this] ( const CSphColumnInfo & tCol )
  1195. {
  1196. m_pSorterSchema->AddAttr ( tCol, true );
  1197. m_hQueryColumns.Add ( tCol.m_sName );
  1198. };
  1199. AddColumn ( tGroupby );
  1200. AddColumn ( tCount );
  1201. if ( bGotDistinct )
  1202. {
  1203. CSphColumnInfo tDistinct ( "@distinct", SPH_ATTR_INTEGER );
  1204. tDistinct.m_eStage = SPH_EVAL_SORTER;
  1205. AddColumn ( tDistinct );
  1206. }
  1207. // add @groupbystr last in case we need to skip it on sending (like @int_attr_*)
  1208. ReplaceJsonGroupbyWithStrings ( sJsonGroupBy );
  1209. }
  1210. #define LOC_CHECK( _cond, _msg ) if (!(_cond)) { m_sError = "invalid schema: " _msg; return false; }
  1211. int iGroupby = m_pSorterSchema->GetAttrIndex ( "@groupby" );
  1212. if ( iGroupby>=0 )
  1213. {
  1214. m_tGroupSorterSettings.m_bDistinct = bGotDistinct;
  1215. m_tGroupSorterSettings.m_tLocGroupby = m_pSorterSchema->GetAttr ( iGroupby ).m_tLocator;
  1216. LOC_CHECK ( m_tGroupSorterSettings.m_tLocGroupby.m_bDynamic, "@groupby must be dynamic" );
  1217. int iCount = m_pSorterSchema->GetAttrIndex ( "@count" );
  1218. LOC_CHECK ( iCount>=0, "missing @count" );
  1219. m_tGroupSorterSettings.m_tLocCount = m_pSorterSchema->GetAttr ( iCount ).m_tLocator;
  1220. LOC_CHECK ( m_tGroupSorterSettings.m_tLocCount.m_bDynamic, "@count must be dynamic" );
  1221. int iDistinct = m_pSorterSchema->GetAttrIndex ( "@distinct" );
  1222. if ( bGotDistinct )
  1223. {
  1224. LOC_CHECK ( iDistinct>=0, "missing @distinct" );
  1225. m_tGroupSorterSettings.m_tLocDistinct = m_pSorterSchema->GetAttr ( iDistinct ).m_tLocator;
  1226. LOC_CHECK ( m_tGroupSorterSettings.m_tLocDistinct.m_bDynamic, "@distinct must be dynamic" );
  1227. }
  1228. else
  1229. LOC_CHECK ( iDistinct<=0, "unexpected @distinct" );
  1230. int iGroupbyStr = m_pSorterSchema->GetAttrIndex ( sJsonGroupBy.cstr() );
  1231. if ( iGroupbyStr>=0 )
  1232. m_tGroupSorterSettings.m_tLocGroupbyStr = m_pSorterSchema->GetAttr ( iGroupbyStr ).m_tLocator;
  1233. }
  1234. if ( m_bHasCount )
  1235. LOC_CHECK ( m_pSorterSchema->GetAttrIndex ( "@count" )>=0, "Count(*) or @count is queried, but not available in the schema" );
  1236. #undef LOC_CHECK
  1237. return true;
  1238. }
  1239. bool QueueCreator_c::AddKNNDistColumn()
  1240. {
  1241. const auto & tKNN = m_tQuery.m_tKnnSettings;
  1242. if ( tKNN.m_sAttr.IsEmpty() || m_pSorterSchema->GetAttrIndex ( GetKnnDistAttrName() )>=0 )
  1243. return true;
  1244. auto pAttr = m_pSorterSchema->GetAttr ( tKNN.m_sAttr.cstr() );
  1245. if ( !pAttr )
  1246. {
  1247. m_sError.SetSprintf ( "requested KNN search attribute '%s' not found", tKNN.m_sAttr.cstr() );
  1248. return false;
  1249. }
  1250. if ( !pAttr->IsIndexedKNN() )
  1251. {
  1252. m_sError.SetSprintf ( "KNN index not enabled for attribute '%s'", tKNN.m_sAttr.cstr() );
  1253. return false;
  1254. }
  1255. if ( !tKNN.m_sEmbStr && pAttr->m_tKNN.m_iDims!=tKNN.m_dVec.GetLength() )
  1256. {
  1257. m_sError.SetSprintf ( "KNN index '%s' requires a vector of %d entries; %d entries specified", tKNN.m_sAttr.cstr(), pAttr->m_tKNN.m_iDims, tKNN.m_dVec.GetLength() );
  1258. return false;
  1259. }
  1260. CSphColumnInfo tKNNDist ( GetKnnDistAttrName(), SPH_ATTR_FLOAT );
  1261. tKNNDist.m_eStage = SPH_EVAL_PRESORT;
  1262. tKNNDist.m_pExpr = CreateExpr_KNNDist ( tKNN.m_dVec, *pAttr );
  1263. m_pSorterSchema->AddAttr ( tKNNDist, true );
  1264. m_hQueryColumns.Add ( tKNNDist.m_sName );
  1265. return true;
  1266. }
  1267. bool QueueCreator_c::AddKNNRescoreColumn()
  1268. {
  1269. const auto & tKNN = m_tQuery.m_tKnnSettings;
  1270. if ( tKNN.m_sAttr.IsEmpty() )
  1271. return true;
  1272. if ( !tKNN.m_bRescore )
  1273. return true;
  1274. auto pAttr = m_pSorterSchema->GetAttr ( tKNN.m_sAttr.cstr() );
  1275. CSphColumnInfo tKNNDistRescored ( GetKnnDistRescoreAttrName(), SPH_ATTR_FLOAT );
  1276. tKNNDistRescored.m_eStage = SPH_EVAL_FINAL;
  1277. tKNNDistRescored.m_pExpr = CreateExpr_KNNDistRescore ( tKNN.m_dVec, *pAttr );
  1278. m_pSorterSchema->AddAttr ( tKNNDistRescored, true );
  1279. m_hQueryColumns.Add ( tKNNDistRescored.m_sName );
  1280. return true;
  1281. }
  1282. bool QueueCreator_c::ParseJoinExpr ( CSphColumnInfo & tExprCol, const CSphString & sAttr, const CSphString & sExpr ) const
  1283. {
  1284. tExprCol = CSphColumnInfo ( sAttr.cstr() );
  1285. ExprParseArgs_t tExprParseArgs;
  1286. tExprParseArgs.m_pAttrType = &tExprCol.m_eAttrType;
  1287. tExprParseArgs.m_pProfiler = m_tSettings.m_pProfiler;
  1288. tExprParseArgs.m_eCollation = m_tQuery.m_eCollation;
  1289. tExprCol.m_eStage = SPH_EVAL_PRESORT;
  1290. tExprCol.m_pExpr = sphExprParse ( sExpr.cstr(), *m_pSorterSchema, m_tSettings.m_pJoinArgs ? &(m_tSettings.m_pJoinArgs->m_sIndex2) : nullptr, m_sError, tExprParseArgs );
  1291. tExprCol.m_uAttrFlags |= CSphColumnInfo::ATTR_JOINED;
  1292. return !!tExprCol.m_pExpr;
  1293. }
  1294. bool QueueCreator_c::AddJsonJoinOnFilter ( const CSphString & sAttr1, const CSphString & sAttr2, ESphAttr eTypeCast )
  1295. {
  1296. const CSphColumnInfo * pAttr = m_pSorterSchema->GetAttr ( sAttr1.cstr() );
  1297. if ( pAttr )
  1298. {
  1299. if ( pAttr->m_pExpr && pAttr->m_pExpr->UsesDocstore() )
  1300. return true;
  1301. const_cast<CSphColumnInfo *>(pAttr)->m_eStage = Min ( pAttr->m_eStage, SPH_EVAL_PRESORT );
  1302. return true;
  1303. }
  1304. if ( !sphJsonNameSplit ( sAttr1.cstr(), nullptr ) )
  1305. {
  1306. const CSphColumnInfo * pField = m_pSorterSchema->GetField ( sAttr1.cstr() );
  1307. if ( pField && ( pField->m_uFieldFlags & CSphColumnInfo::FIELD_STORED ) )
  1308. m_sError.SetSprintf ( "Unable to perform join on a stored field '%s.%s'", m_tSettings.m_pJoinArgs->m_sIndex1.cstr(), sAttr1.cstr() );
  1309. else
  1310. m_sError.SetSprintf ( "Unable to perform join on '%s'", sAttr1.cstr() );
  1311. return false;
  1312. }
  1313. CSphColumnInfo tExprCol;
  1314. if ( !ParseJoinExpr ( tExprCol, sAttr1, sAttr1 ) )
  1315. return false;
  1316. const auto & tSchema = m_tSettings.m_pJoinArgs->m_tJoinedSchema;
  1317. // convert JSON fields to join attr type
  1318. if ( tExprCol.m_eAttrType==SPH_ATTR_JSON_FIELD )
  1319. {
  1320. // try to determine type if it was not explicitly specified
  1321. if ( eTypeCast==SPH_ATTR_NONE )
  1322. {
  1323. auto * pJoinAttr = tSchema.GetAttr ( sAttr2.cstr() );
  1324. if ( !pJoinAttr )
  1325. {
  1326. if ( sphJsonNameSplit ( sAttr2.cstr() ) )
  1327. m_sError.SetSprintf ( "use implicit type conversion on join-on attribute '%s'", sAttr2.cstr() );
  1328. else
  1329. m_sError.SetSprintf ( "join-on attribute '%s' not found", sAttr2.cstr() );
  1330. return false;
  1331. }
  1332. eTypeCast = pJoinAttr->m_eAttrType;
  1333. }
  1334. CSphString sConverted;
  1335. switch ( eTypeCast )
  1336. {
  1337. case SPH_ATTR_STRING:
  1338. sConverted.SetSprintf ( "TO_STRING(%s)", sAttr1.cstr() );
  1339. break;
  1340. case SPH_ATTR_FLOAT:
  1341. sConverted.SetSprintf ( "DOUBLE(%s)", sAttr1.cstr() );
  1342. break;
  1343. default:
  1344. sConverted.SetSprintf ( "BIGINT(%s)", sAttr1.cstr() );
  1345. break;
  1346. }
  1347. if ( !ParseJoinExpr ( tExprCol, sAttr1, sConverted ) )
  1348. return false;
  1349. }
  1350. m_pSorterSchema->AddAttr ( tExprCol, true );
  1351. return true;
  1352. }
  1353. bool QueueCreator_c::AddColumnarJoinOnFilter ( const CSphString & sAttr )
  1354. {
  1355. const CSphColumnInfo * pAttr = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  1356. if ( pAttr && pAttr->m_pExpr && !pAttr->m_pExpr->UsesDocstore() )
  1357. {
  1358. const_cast<CSphColumnInfo *>(pAttr)->m_eStage = Min ( pAttr->m_eStage, SPH_EVAL_PRESORT );
  1359. return true;
  1360. }
  1361. return ReplaceWithColumnarItem ( sAttr, SPH_EVAL_PRESORT );
  1362. }
  1363. bool QueueCreator_c::AddJoinAttrs()
  1364. {
  1365. if ( !m_tSettings.m_pJoinArgs )
  1366. return true;
  1367. const auto & tSchema = m_tSettings.m_pJoinArgs->m_tJoinedSchema;
  1368. for ( int i = 0; i < tSchema.GetAttrsCount(); i++ )
  1369. if ( !sphIsInternalAttr ( tSchema.GetAttr(i).m_sName ) )
  1370. {
  1371. CSphColumnInfo tAttr = tSchema.GetAttr(i);
  1372. tAttr.m_sName.SetSprintf ( "%s.%s", m_tSettings.m_pJoinArgs->m_sIndex2.cstr(), tAttr.m_sName.cstr() );
  1373. tAttr.m_eAttrType = sphPlainAttrToPtrAttr ( tAttr.m_eAttrType );
  1374. tAttr.m_tLocator.Reset();
  1375. tAttr.m_eStage = SPH_EVAL_SORTER;
  1376. tAttr.m_uAttrFlags &= ~( CSphColumnInfo::ATTR_COLUMNAR | CSphColumnInfo::ATTR_COLUMNAR_HASHES );
  1377. tAttr.m_uAttrFlags |= CSphColumnInfo::ATTR_JOINED;
  1378. m_pSorterSchema->AddAttr ( tAttr, true );
  1379. m_hQueryDups.Add ( tAttr.m_sName );
  1380. m_hQueryColumns.Add ( tAttr.m_sName );
  1381. }
  1382. for ( int i = 0; i < tSchema.GetFieldsCount(); i++ )
  1383. {
  1384. const CSphColumnInfo & tField = tSchema.GetField(i);
  1385. if ( tField.m_uFieldFlags & CSphColumnInfo::FIELD_STORED )
  1386. {
  1387. if ( tSchema.GetAttrIndex ( tField.m_sName.cstr() )!=-1 )
  1388. continue;
  1389. CSphColumnInfo tAttr;
  1390. tAttr.m_sName.SetSprintf ( "%s.%s", m_tSettings.m_pJoinArgs->m_sIndex2.cstr(), tField.m_sName.cstr() );
  1391. tAttr.m_eAttrType = SPH_ATTR_STRINGPTR;
  1392. tAttr.m_tLocator.Reset();
  1393. tAttr.m_eStage = SPH_EVAL_SORTER;
  1394. tAttr.m_uAttrFlags = CSphColumnInfo::ATTR_JOINED;
  1395. m_pSorterSchema->AddAttr ( tAttr, true );
  1396. m_hQueryDups.Add ( tAttr.m_sName );
  1397. m_hQueryColumns.Add ( tAttr.m_sName );
  1398. }
  1399. }
  1400. CSphColumnInfo tAttr;
  1401. tAttr.m_sName.SetSprintf ( "%s.weight()", m_tSettings.m_pJoinArgs->m_sIndex2.cstr() );
  1402. tAttr.m_eAttrType = SPH_ATTR_INTEGER;
  1403. tAttr.m_tLocator.Reset();
  1404. tAttr.m_eStage = SPH_EVAL_SORTER;
  1405. tAttr.m_uAttrFlags = CSphColumnInfo::ATTR_JOINED;
  1406. m_pSorterSchema->AddAttr ( tAttr, true );
  1407. m_hQueryDups.Add ( tAttr.m_sName );
  1408. m_hQueryColumns.Add ( tAttr.m_sName );
  1409. return true;
  1410. }
  1411. static ESphAttr FilterType2AttrType ( ESphFilter eFilter )
  1412. {
  1413. switch ( eFilter )
  1414. {
  1415. case SPH_FILTER_FLOATRANGE:
  1416. return SPH_ATTR_FLOAT;
  1417. case SPH_FILTER_STRING:
  1418. case SPH_FILTER_STRING_LIST:
  1419. return SPH_ATTR_STRINGPTR;
  1420. default:
  1421. return SPH_ATTR_BIGINT;
  1422. }
  1423. }
  1424. bool QueueCreator_c::CheckJoinOnTypeCast ( const CSphString & sIdx, const CSphString & sAttr, ESphAttr eTypeCast )
  1425. {
  1426. if ( eTypeCast==SPH_ATTR_NONE )
  1427. return true;
  1428. if ( !sphJsonNameSplit ( sAttr.cstr() ) )
  1429. {
  1430. m_sError.SetSprintf ( "Explicit type conversion used on non-json attribute '%s.%s'", sIdx.cstr(), sAttr.cstr() );
  1431. return false;
  1432. }
  1433. return true;
  1434. }
  1435. bool QueueCreator_c::AddJoinFilterAttrs()
  1436. {
  1437. if ( !m_tSettings.m_pJoinArgs )
  1438. return true;
  1439. const CSphString & sLeftIndex = m_tSettings.m_pJoinArgs->m_sIndex1;
  1440. const CSphString & sRightIndex = m_tSettings.m_pJoinArgs->m_sIndex2;
  1441. for ( const auto & i : m_tQuery.m_dOnFilters )
  1442. {
  1443. if ( !CheckJoinOnTypeCast ( i.m_sIdx1, i.m_sAttr1, i.m_eTypeCast1 ) ) return false;
  1444. if ( !CheckJoinOnTypeCast ( i.m_sIdx2, i.m_sAttr2, i.m_eTypeCast2 ) ) return false;
  1445. ESphAttr eTypeCast = i.m_eTypeCast1!=SPH_ATTR_NONE ? i.m_eTypeCast1 : i.m_eTypeCast2;
  1446. if ( i.m_sIdx1==sLeftIndex || ( m_szParent && i.m_sIdx1==m_szParent ) )
  1447. {
  1448. if ( !AddJsonJoinOnFilter ( i.m_sAttr1, i.m_sAttr2, eTypeCast ) ) return false;
  1449. if ( !AddColumnarJoinOnFilter ( i.m_sAttr1 ) ) return false;
  1450. }
  1451. if ( i.m_sIdx2==sLeftIndex )
  1452. {
  1453. if ( !AddJsonJoinOnFilter ( i.m_sAttr2, i.m_sAttr1, eTypeCast ) ) return false;
  1454. if ( !AddColumnarJoinOnFilter ( i.m_sAttr2 ) ) return false;
  1455. }
  1456. }
  1457. if ( NeedPostJoinFilterEvaluation ( m_tQuery, *m_pSorterSchema ) )
  1458. for ( const auto & i : m_tQuery.m_dFilters )
  1459. {
  1460. const CSphString & sAttr = i.m_sAttrName;
  1461. const CSphColumnInfo * pAttr = m_pSorterSchema->GetAttr ( sAttr.cstr() );
  1462. CSphString sSplitAttrName;
  1463. bool bIndexPrefix = false;
  1464. if ( pAttr || !sphJsonNameSplit ( sAttr.cstr(), sRightIndex.cstr(), &sSplitAttrName, &bIndexPrefix ) )
  1465. continue;
  1466. // check if it's a json attribute from the left table and not a joined attribute
  1467. if ( !bIndexPrefix )
  1468. continue;
  1469. CSphColumnInfo tExprCol ( sAttr.cstr(), FilterType2AttrType ( i.m_eType ) );
  1470. tExprCol.m_eStage = SPH_EVAL_SORTER;
  1471. tExprCol.m_uAttrFlags |= CSphColumnInfo::ATTR_JOINED;
  1472. m_pSorterSchema->AddAttr ( tExprCol, true );
  1473. m_hQueryDups.Add(sAttr);
  1474. m_hQueryColumns.Add(sAttr);
  1475. }
  1476. return true;
  1477. }
  1478. bool QueueCreator_c::AddNullBitmask()
  1479. {
  1480. if ( !m_tSettings.m_pJoinArgs || m_tQuery.m_eJoinType!=JoinType_e::LEFT )
  1481. return true;
  1482. int iNumJoinAttrs = 0;
  1483. int iDynamic = 0;
  1484. for ( int i = 0; i < m_pSorterSchema->GetAttrsCount(); i++ )
  1485. {
  1486. const auto & tAttr = m_pSorterSchema->GetAttr(i);
  1487. if ( !tAttr.m_tLocator.m_bDynamic )
  1488. continue;
  1489. iDynamic++;
  1490. if ( tAttr.IsJoined() )
  1491. iNumJoinAttrs = Max ( iNumJoinAttrs, iDynamic );
  1492. }
  1493. CSphColumnInfo tAttr ( GetNullMaskAttrName(), DetermineNullMaskType(iNumJoinAttrs) );
  1494. tAttr.m_eStage = SPH_EVAL_SORTER;
  1495. m_pSorterSchema->AddAttr ( tAttr, true );
  1496. m_hQueryDups.Add ( tAttr.m_sName );
  1497. m_hQueryColumns.Add ( tAttr.m_sName );
  1498. return true;
  1499. }
  1500. bool QueueCreator_c::CheckHavingConstraints () const
  1501. {
  1502. if ( m_tSettings.m_pAggrFilter && !m_tSettings.m_pAggrFilter->m_sAttrName.IsEmpty () )
  1503. {
  1504. if ( !m_bGotGroupby )
  1505. return Err ( "can not use HAVING without GROUP BY" );
  1506. // should be column named at group by, or it's alias or aggregate
  1507. const CSphString & sHaving = m_tSettings.m_pAggrFilter->m_sAttrName;
  1508. if ( !IsGroupbyMagic ( sHaving ) )
  1509. {
  1510. bool bValidHaving = false;
  1511. for ( const CSphQueryItem & tItem : m_tQuery.m_dItems )
  1512. {
  1513. if ( tItem.m_sAlias!=sHaving )
  1514. continue;
  1515. bValidHaving = ( IsGroupbyMagic ( tItem.m_sExpr ) || tItem.m_eAggrFunc!=SPH_AGGR_NONE );
  1516. break;
  1517. }
  1518. if ( !bValidHaving )
  1519. return Err ( "can not use HAVING with attribute not related to GROUP BY" );
  1520. }
  1521. }
  1522. return true;
  1523. }
  1524. void QueueCreator_c::SetupRemapColJson ( CSphColumnInfo & tRemapCol, CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs, int iStateAttr )
  1525. {
  1526. bool bFunc = dExtraExprs[iStateAttr].m_tKey.m_uMask==0;
  1527. tRemapCol.m_eStage = SPH_EVAL_PRESORT;
  1528. if ( bFunc )
  1529. {
  1530. tRemapCol.m_pExpr = dExtraExprs[iStateAttr].m_pExpr;
  1531. tRemapCol.m_eAttrType = dExtraExprs[iStateAttr].m_eType;
  1532. tState.m_eKeypart[iStateAttr] = Attr2Keypart ( tRemapCol.m_eAttrType );
  1533. }
  1534. else
  1535. tRemapCol.m_pExpr = CreateExprSortJson2String ( tState.m_tLocator[iStateAttr], dExtraExprs[iStateAttr].m_pExpr );
  1536. }
  1537. const CSphColumnInfo * QueueCreator_c::GetGroupbyStr ( int iAttr, int iNumOldAttrs ) const
  1538. {
  1539. assert ( m_pSorterSchema );
  1540. auto & tSorterSchema = *m_pSorterSchema;
  1541. if ( m_tSettings.m_bComputeItems && iAttr>=0 && iAttr<iNumOldAttrs && tSorterSchema.GetAttr(iAttr).m_sName=="@groupby" && m_dGroupColumns.GetLength() )
  1542. {
  1543. // FIXME!!! add support of multi group by
  1544. const CSphColumnInfo & tGroupCol = tSorterSchema.GetAttr ( m_dGroupColumns[0].first );
  1545. if ( tGroupCol.m_eAttrType==SPH_ATTR_STRING || tGroupCol.m_eAttrType==SPH_ATTR_STRINGPTR )
  1546. return &tGroupCol;
  1547. }
  1548. return nullptr;
  1549. }
  1550. void QueueCreator_c::ReplaceGroupbyStrWithExprs ( CSphMatchComparatorState & tState, int iNumOldAttrs )
  1551. {
  1552. assert ( m_pSorterSchema );
  1553. auto & tSorterSchema = *m_pSorterSchema;
  1554. for ( int i = 0; i<CSphMatchComparatorState::MAX_ATTRS; i++ )
  1555. {
  1556. const CSphColumnInfo * pGroupStrBase = GetGroupbyStr ( tState.m_dAttrs[i], iNumOldAttrs );
  1557. if ( !pGroupStrBase )
  1558. continue;
  1559. assert ( tState.m_dAttrs[i]>=0 && tState.m_dAttrs[i]<iNumOldAttrs );
  1560. int iRemap = -1;
  1561. if ( pGroupStrBase->m_eAttrType==SPH_ATTR_STRINGPTR )
  1562. {
  1563. // grouping by (columnar) string; and the same string is used in sorting
  1564. // correct the locator and change the evaluation stage to PRESORT
  1565. iRemap = tSorterSchema.GetAttrIndex ( pGroupStrBase->m_sName.cstr() );
  1566. assert ( iRemap>=0 );
  1567. const CSphColumnInfo & tAttr = tSorterSchema.GetAttr(iRemap);
  1568. const_cast<CSphColumnInfo &>(tAttr).m_eStage = SPH_EVAL_PRESORT;
  1569. }
  1570. else if ( !pGroupStrBase->IsColumnar() )
  1571. {
  1572. CSphString sRemapCol;
  1573. sRemapCol.SetSprintf ( "%s%s", GetInternalAttrPrefix(), pGroupStrBase->m_sName.cstr() );
  1574. iRemap = tSorterSchema.GetAttrIndex ( sRemapCol.cstr() );
  1575. if ( iRemap==-1 )
  1576. {
  1577. CSphColumnInfo tRemapCol ( sRemapCol.cstr(), SPH_ATTR_STRINGPTR );
  1578. tRemapCol.m_pExpr = CreateExprSortStringFixup ( pGroupStrBase->m_tLocator );
  1579. tRemapCol.m_eStage = SPH_EVAL_PRESORT;
  1580. iRemap = tSorterSchema.GetAttrsCount();
  1581. tSorterSchema.AddAttr ( tRemapCol, true );
  1582. }
  1583. }
  1584. if ( iRemap!=-1 )
  1585. {
  1586. tState.m_eKeypart[i] = SPH_KEYPART_STRINGPTR;
  1587. tState.m_tLocator[i] = tSorterSchema.GetAttr(iRemap).m_tLocator;
  1588. tState.m_dAttrs[i] = iRemap;
  1589. tState.m_dRemapped.BitSet ( i );
  1590. }
  1591. }
  1592. }
  1593. void QueueCreator_c::ReplaceStaticStringsWithExprs ( CSphMatchComparatorState & tState )
  1594. {
  1595. assert ( m_pSorterSchema );
  1596. auto & tSorterSchema = *m_pSorterSchema;
  1597. for ( int i = 0; i<CSphMatchComparatorState::MAX_ATTRS; i++ )
  1598. {
  1599. if ( tState.m_dRemapped.BitGet ( i ) )
  1600. continue;
  1601. if ( tState.m_eKeypart[i]!=SPH_KEYPART_STRING )
  1602. continue;
  1603. int iRemap = -1;
  1604. int iAttrId = tState.m_dAttrs[i];
  1605. const CSphColumnInfo & tAttr = tSorterSchema.GetAttr(iAttrId);
  1606. if ( tAttr.IsColumnar() )
  1607. {
  1608. CSphString sAttrName = tAttr.m_sName;
  1609. tSorterSchema.RemoveStaticAttr(iAttrId);
  1610. CSphColumnInfo tRemapCol ( sAttrName.cstr(), SPH_ATTR_STRINGPTR );
  1611. tRemapCol.m_eStage = SPH_EVAL_PRESORT;
  1612. tRemapCol.m_pExpr = CreateExpr_GetColumnarString ( sAttrName, tAttr.IsStored() );
  1613. tSorterSchema.AddAttr ( tRemapCol, true );
  1614. iRemap = tSorterSchema.GetAttrIndex ( sAttrName.cstr() );
  1615. }
  1616. else
  1617. {
  1618. CSphString sRemapCol;
  1619. sRemapCol.SetSprintf ( "%s%s", GetInternalAttrPrefix(), tSorterSchema.GetAttr(iAttrId).m_sName.cstr() );
  1620. iRemap = tSorterSchema.GetAttrIndex ( sRemapCol.cstr() );
  1621. if ( iRemap==-1 )
  1622. {
  1623. CSphColumnInfo tRemapCol ( sRemapCol.cstr(), SPH_ATTR_STRINGPTR );
  1624. tRemapCol.m_eStage = SPH_EVAL_PRESORT;
  1625. tRemapCol.m_pExpr = CreateExprSortStringFixup ( tState.m_tLocator[i] );
  1626. iRemap = tSorterSchema.GetAttrsCount();
  1627. tSorterSchema.AddAttr ( tRemapCol, true );
  1628. }
  1629. }
  1630. tState.m_tLocator[i] = tSorterSchema.GetAttr ( iRemap ).m_tLocator;
  1631. tState.m_dAttrs[i] = iRemap;
  1632. tState.m_eKeypart[i] = SPH_KEYPART_STRINGPTR;
  1633. tState.m_dRemapped.BitSet ( i );
  1634. }
  1635. }
  1636. void QueueCreator_c::ReplaceJsonWithExprs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs )
  1637. {
  1638. assert ( m_pSorterSchema );
  1639. auto & tSorterSchema = *m_pSorterSchema;
  1640. for ( int i = 0; i<CSphMatchComparatorState::MAX_ATTRS; i++ )
  1641. {
  1642. if ( tState.m_dRemapped.BitGet ( i ) )
  1643. continue;
  1644. const CSphString & sKey = dExtraExprs[i].m_tKey.m_sKey;
  1645. if ( sKey.IsEmpty() )
  1646. continue;
  1647. CSphString sRemapCol;
  1648. sRemapCol.SetSprintf ( "%s%s", GetInternalAttrPrefix(), sKey.cstr() );
  1649. int iRemap = tSorterSchema.GetAttrIndex ( sRemapCol.cstr() );
  1650. if ( iRemap==-1 )
  1651. {
  1652. CSphString sRemapLowercase = sRemapCol;
  1653. sRemapLowercase.ToLower();
  1654. iRemap = tSorterSchema.GetAttrIndex ( sRemapLowercase.cstr() );
  1655. }
  1656. if ( iRemap==-1 )
  1657. {
  1658. CSphColumnInfo tRemapCol ( sRemapCol.cstr(), SPH_ATTR_STRINGPTR );
  1659. SetupRemapColJson ( tRemapCol, tState, dExtraExprs, i );
  1660. iRemap = tSorterSchema.GetAttrsCount();
  1661. ModifyExprForJoin ( tRemapCol, sKey );
  1662. tSorterSchema.AddAttr ( tRemapCol, true );
  1663. }
  1664. tState.m_tLocator[i] = tSorterSchema.GetAttr(iRemap).m_tLocator;
  1665. tState.m_dAttrs[i] = iRemap;
  1666. tState.m_dRemapped.BitSet ( i );
  1667. }
  1668. }
  1669. void QueueCreator_c::AddColumnarExprsAsAttrs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs )
  1670. {
  1671. assert ( m_pSorterSchema );
  1672. auto & tSorterSchema = *m_pSorterSchema;
  1673. for ( int i = 0; i<CSphMatchComparatorState::MAX_ATTRS; i++ )
  1674. {
  1675. if ( tState.m_dRemapped.BitGet ( i ) )
  1676. continue;
  1677. ISphExpr * pExpr = dExtraExprs[i].m_pExpr;
  1678. if ( !pExpr || !pExpr->IsColumnar() )
  1679. continue;
  1680. const CSphString & sAttrName = tSorterSchema.GetAttr ( tState.m_dAttrs[i] ).m_sName;
  1681. CSphColumnInfo tRemapCol ( sAttrName.cstr(), dExtraExprs[i].m_eType );
  1682. tRemapCol.m_eStage = SPH_EVAL_PRESORT;
  1683. tRemapCol.m_pExpr = pExpr;
  1684. tRemapCol.m_pExpr->AddRef();
  1685. int iRemap = tSorterSchema.GetAttrsCount();
  1686. tSorterSchema.AddAttr ( tRemapCol, true );
  1687. // remove initial attribute from m_hExtra
  1688. // that way it won't be evaluated twice when it is not in select list
  1689. m_hExtra.Delete(sAttrName);
  1690. tState.m_tLocator[i] = tSorterSchema.GetAttr ( iRemap ).m_tLocator;
  1691. tState.m_dAttrs[i] = iRemap;
  1692. tState.m_eKeypart[i] = Attr2Keypart ( dExtraExprs[i].m_eType );
  1693. tState.m_dRemapped.BitSet ( i );
  1694. }
  1695. }
  1696. void QueueCreator_c::RemapAttrs ( CSphMatchComparatorState & tState, CSphVector<ExtraSortExpr_t> & dExtraExprs )
  1697. {
  1698. // we have extra attrs (expressions) that we created while parsing the sort clause
  1699. // we couldn't add them to the schema at that stage,
  1700. // but now we can. we create attributes, assign internal names and set their expressions
  1701. assert ( m_pSorterSchema );
  1702. auto & tSorterSchema = *m_pSorterSchema;
  1703. int iNumOldAttrs = tSorterSchema.GetAttrsCount();
  1704. ReplaceGroupbyStrWithExprs ( tState, iNumOldAttrs );
  1705. ReplaceStaticStringsWithExprs ( tState );
  1706. ReplaceJsonWithExprs ( tState, dExtraExprs );
  1707. AddColumnarExprsAsAttrs ( tState, dExtraExprs );
  1708. // need another sort keys add after setup remap
  1709. if ( iNumOldAttrs!=tSorterSchema.GetAttrsCount() )
  1710. ExtraAddSortkeys ( tState.m_dAttrs );
  1711. }
  1712. void QueueCreator_c::AddKnnDistSort ( CSphString & sSortBy )
  1713. {
  1714. if ( m_pSorterSchema->GetAttr ( GetKnnDistAttrName() ) && !strstr ( sSortBy.cstr(), "knn_dist" ) )
  1715. sSortBy.SetSprintf ( "knn_dist() asc, %s", sSortBy.cstr() );
  1716. }
  1717. // matches sorting function
  1718. bool QueueCreator_c::SetupMatchesSortingFunc()
  1719. {
  1720. m_bRandomize = false;
  1721. if ( m_tQuery.m_eSort==SPH_SORT_EXTENDED )
  1722. {
  1723. CSphString sSortBy = m_tQuery.m_sSortBy;
  1724. AddKnnDistSort ( sSortBy );
  1725. ESortClauseParseResult eRes = sphParseSortClause ( m_tQuery, sSortBy.cstr(), *m_pSorterSchema, m_eMatchFunc, m_tStateMatch, m_dMatchJsonExprs, m_tSettings.m_pJoinArgs.get(), m_sError );
  1726. if ( eRes==SORT_CLAUSE_ERROR )
  1727. return false;
  1728. if ( eRes==SORT_CLAUSE_RANDOM )
  1729. m_bRandomize = true;
  1730. ExtraAddSortkeys ( m_tStateMatch.m_dAttrs );
  1731. AssignOrderByToPresortStage ( m_tStateMatch.m_dAttrs, CSphMatchComparatorState::MAX_ATTRS );
  1732. RemapAttrs ( m_tStateMatch, m_dMatchJsonExprs );
  1733. return true;
  1734. }
  1735. if ( m_tQuery.m_eSort==SPH_SORT_EXPR )
  1736. {
  1737. m_tStateMatch.m_eKeypart[0] = SPH_KEYPART_INT;
  1738. m_tStateMatch.m_tLocator[0] = m_pSorterSchema->GetAttr ( m_pSorterSchema->GetAttrIndex ( "@expr" ) ).m_tLocator;
  1739. m_tStateMatch.m_eKeypart[1] = SPH_KEYPART_ROWID;
  1740. m_tStateMatch.m_uAttrDesc = 1;
  1741. m_eMatchFunc = FUNC_EXPR;
  1742. return true;
  1743. }
  1744. // check sort-by attribute
  1745. if ( m_tQuery.m_eSort!=SPH_SORT_RELEVANCE )
  1746. {
  1747. int iSortAttr = m_pSorterSchema->GetAttrIndex ( m_tQuery.m_sSortBy.cstr() );
  1748. if ( iSortAttr<0 )
  1749. {
  1750. Err ( "sort-by attribute '%s' not found", m_tQuery.m_sSortBy.cstr() );
  1751. return false;
  1752. }
  1753. const CSphColumnInfo & tAttr = m_pSorterSchema->GetAttr ( iSortAttr );
  1754. m_tStateMatch.m_eKeypart[0] = Attr2Keypart ( tAttr.m_eAttrType );
  1755. m_tStateMatch.m_tLocator[0] = tAttr.m_tLocator;
  1756. m_tStateMatch.m_dAttrs[0] = iSortAttr;
  1757. RemapAttrs ( m_tStateMatch, m_dMatchJsonExprs );
  1758. }
  1759. ExtraAddSortkeys ( m_tStateMatch.m_dAttrs );
  1760. // find out what function to use and whether it needs attributes
  1761. switch (m_tQuery.m_eSort )
  1762. {
  1763. case SPH_SORT_TIME_SEGMENTS: m_eMatchFunc = FUNC_TIMESEGS; break;
  1764. case SPH_SORT_RELEVANCE: m_eMatchFunc = FUNC_REL_DESC; break;
  1765. default:
  1766. Err ( "unknown sorting mode %d", m_tQuery.m_eSort );
  1767. return false;
  1768. }
  1769. return true;
  1770. }
  1771. bool QueueCreator_c::SetupGroupSortingFunc ( bool bGotDistinct )
  1772. {
  1773. assert ( m_bGotGroupby );
  1774. CSphString sGroupOrderBy = m_tQuery.m_sGroupSortBy;
  1775. if ( sGroupOrderBy=="@weight desc" )
  1776. AddKnnDistSort ( sGroupOrderBy );
  1777. ESortClauseParseResult eRes = sphParseSortClause ( m_tQuery, sGroupOrderBy.cstr(), *m_pSorterSchema, m_eGroupFunc, m_tStateGroup, m_dGroupJsonExprs, m_tSettings.m_pJoinArgs.get(), m_sError );
  1778. if ( eRes==SORT_CLAUSE_ERROR || eRes==SORT_CLAUSE_RANDOM )
  1779. {
  1780. if ( eRes==SORT_CLAUSE_RANDOM )
  1781. m_sError = "groups can not be sorted by @random";
  1782. return false;
  1783. }
  1784. ExtraAddSortkeys ( m_tStateGroup.m_dAttrs );
  1785. if ( !m_tGroupSorterSettings.m_bImplicit )
  1786. {
  1787. for ( const auto & tGroupColumn : m_dGroupColumns )
  1788. m_hExtra.Add ( m_pSorterSchema->GetAttr ( tGroupColumn.first ).m_sName );
  1789. }
  1790. if ( bGotDistinct )
  1791. {
  1792. m_dGroupColumns.Add ( { m_pSorterSchema->GetAttrIndex ( m_tQuery.m_sGroupDistinct.cstr() ), true } );
  1793. assert ( m_dGroupColumns.Last().first>=0 );
  1794. m_hExtra.Add ( m_pSorterSchema->GetAttr ( m_dGroupColumns.Last().first ).m_sName );
  1795. }
  1796. // implicit case
  1797. CSphVector<int> dGroupByCols;
  1798. for ( const auto & i : m_dGroupColumns )
  1799. if ( i.second )
  1800. dGroupByCols.Add ( i.first );
  1801. AssignOrderByToPresortStage ( dGroupByCols.Begin(), dGroupByCols.GetLength() );
  1802. AssignOrderByToPresortStage ( m_tStateGroup.m_dAttrs, CSphMatchComparatorState::MAX_ATTRS );
  1803. // GroupSortBy str attributes setup
  1804. RemapAttrs ( m_tStateGroup, m_dGroupJsonExprs );
  1805. return true;
  1806. }
  1807. // set up aggregate filter for grouper
  1808. std::unique_ptr<ISphFilter> QueueCreator_c::CreateAggrFilter () const
  1809. {
  1810. assert ( m_bGotGroupby );
  1811. if ( m_pSorterSchema->GetAttr ( m_tSettings.m_pAggrFilter->m_sAttrName.cstr() ) )
  1812. return sphCreateAggrFilter ( m_tSettings.m_pAggrFilter, m_tSettings.m_pAggrFilter->m_sAttrName, *m_pSorterSchema, m_sError );
  1813. // having might reference aliased attributes but @* attributes got stored without alias in sorter schema
  1814. CSphString sHaving;
  1815. for ( const auto & tItem : m_tQuery.m_dItems )
  1816. if ( tItem.m_sAlias==m_tSettings.m_pAggrFilter->m_sAttrName )
  1817. {
  1818. sHaving = tItem.m_sExpr;
  1819. break;
  1820. }
  1821. if ( sHaving=="groupby()" )
  1822. sHaving = "@groupby";
  1823. else if ( sHaving=="count(*)" )
  1824. sHaving = "@count";
  1825. return sphCreateAggrFilter ( m_tSettings.m_pAggrFilter, sHaving, *m_pSorterSchema, m_sError );
  1826. }
  1827. void QueueCreator_c::SetupCollation()
  1828. {
  1829. SphStringCmp_fn fnCmp = GetStringCmpFunc ( m_tQuery.m_eCollation );
  1830. m_tStateMatch.m_fnStrCmp = fnCmp;
  1831. m_tStateGroup.m_fnStrCmp = fnCmp;
  1832. }
  1833. bool QueueCreator_c::AddGroupbyStuff ()
  1834. {
  1835. // need schema with group related columns however not need grouper
  1836. m_bHeadWOGroup = ( m_tQuery.m_sGroupBy.IsEmpty () && m_tQuery.m_bFacetHead );
  1837. auto fnIsImplicit = [] ( const CSphQueryItem & t )
  1838. {
  1839. return ( t.m_eAggrFunc!=SPH_AGGR_NONE ) || t.m_sExpr=="count(*)" || t.m_sExpr=="@distinct";
  1840. };
  1841. bool bHasImplicitGrouping = HasImplicitGrouping(m_tQuery);
  1842. // count(*) and distinct wo group by at main query should keep implicit flag
  1843. if ( bHasImplicitGrouping && m_bHeadWOGroup )
  1844. m_bHeadWOGroup = !m_tQuery.m_dRefItems.any_of ( fnIsImplicit );
  1845. if ( !SetupGroupbySettings(bHasImplicitGrouping) )
  1846. return false;
  1847. // or else, check in SetupGroupbySettings() would already fail
  1848. m_bGotGroupby = !m_tQuery.m_sGroupBy.IsEmpty () || m_tGroupSorterSettings.m_bImplicit;
  1849. m_bGotDistinct = !!m_tGroupSorterSettings.m_pDistinctFetcher;
  1850. if ( m_bHasGroupByExpr && !m_bGotGroupby )
  1851. return Err ( "GROUPBY() is allowed only in GROUP BY queries" );
  1852. // check for HAVING constrains
  1853. if ( !CheckHavingConstraints() )
  1854. return false;
  1855. // now let's add @groupby stuff, if necessary
  1856. return MaybeAddGroupbyMagic(m_bGotDistinct);
  1857. }
  1858. bool QueueCreator_c::SetGroupSorting()
  1859. {
  1860. if ( m_bGotGroupby )
  1861. {
  1862. if ( !SetupGroupSortingFunc ( m_bGotDistinct ) )
  1863. return false;
  1864. if ( m_tSettings.m_pAggrFilter && !m_tSettings.m_pAggrFilter->m_sAttrName.IsEmpty() )
  1865. {
  1866. auto pFilter = CreateAggrFilter ();
  1867. if ( !pFilter )
  1868. return false;
  1869. m_tGroupSorterSettings.m_pAggrFilterTrait = pFilter.release();
  1870. }
  1871. int iDistinctAccuracyThresh = m_tQuery.m_bExplicitDistinctThresh ? m_tQuery.m_iDistinctThresh : GetDistinctThreshDefault();
  1872. m_tGroupSorterSettings.SetupDistinctAccuracy ( iDistinctAccuracyThresh );
  1873. }
  1874. for ( auto & tIdx: m_hExtra )
  1875. {
  1876. m_hQueryColumns.Add ( tIdx.first );
  1877. if ( m_pExtra )
  1878. m_pExtra->Add ( tIdx.first );
  1879. }
  1880. return true;
  1881. }
  1882. bool QueueCreator_c::PredictAggregates() const
  1883. {
  1884. for ( int i = 0; i < m_pSorterSchema->GetAttrsCount(); i++ )
  1885. {
  1886. const CSphColumnInfo & tAttr = m_pSorterSchema->GetAttr(i);
  1887. if ( !(tAttr.m_eAggrFunc==SPH_AGGR_NONE || IsGroupbyMagic ( tAttr.m_sName ) || IsSortStringInternal ( tAttr.m_sName.cstr () )) )
  1888. return true;
  1889. }
  1890. return false;
  1891. }
  1892. int QueueCreator_c::ReduceOrIncreaseMaxMatches() const
  1893. {
  1894. assert ( !m_bGotGroupby );
  1895. const auto & tKNN = m_tQuery.m_tKnnSettings;
  1896. if ( !tKNN.m_sAttr.IsEmpty() && tKNN.m_fOversampling > 1.0f )
  1897. {
  1898. int64_t iRequested = tKNN.GetRequestedDocs();
  1899. if ( !tKNN.m_sAttr.IsEmpty() && iRequested > tKNN.m_iK )
  1900. return Max ( Max ( m_tSettings.m_iMaxMatches, iRequested ), 1 );
  1901. }
  1902. if ( m_tQuery.m_bExplicitMaxMatches || m_tQuery.m_bHasOuter || !m_tSettings.m_bComputeItems )
  1903. return Max ( m_tSettings.m_iMaxMatches, 1 );
  1904. return Max ( Min ( m_tSettings.m_iMaxMatches, m_tQuery.m_iLimit+m_tQuery.m_iOffset ), 1 );
  1905. }
  1906. int QueueCreator_c::AdjustMaxMatches ( int iMaxMatches ) const
  1907. {
  1908. assert ( m_bGotGroupby );
  1909. if ( m_tQuery.m_bExplicitMaxMatches || m_tSettings.m_bForceSingleThread )
  1910. return iMaxMatches;
  1911. int iGroupbyAttr = GetGroupbyAttrIndex();
  1912. if ( iGroupbyAttr<0 )
  1913. return iMaxMatches;
  1914. CSphString sModifiedAttr;
  1915. int iCountDistinct = m_tSettings.m_fnGetCountDistinct ? m_tSettings.m_fnGetCountDistinct ( m_pSorterSchema->GetAttr(iGroupbyAttr).m_sName, sModifiedAttr ) : -1;
  1916. if ( iCountDistinct > m_tQuery.m_iMaxMatchThresh )
  1917. return iMaxMatches;
  1918. return Max ( iCountDistinct, iMaxMatches );
  1919. }
  1920. bool QueueCreator_c::CanCalcFastCountDistinct() const
  1921. {
  1922. bool bHasAggregates = PredictAggregates();
  1923. return !bHasAggregates && m_tGroupSorterSettings.m_bImplicit && m_tGroupSorterSettings.m_bDistinct && m_tQuery.m_dFilters.IsEmpty() && m_tQuery.m_sQuery.IsEmpty() && m_tQuery.m_tKnnSettings.m_sAttr.IsEmpty() && m_tQuery.m_eJoinType!=JoinType_e::INNER;
  1924. }
  1925. bool QueueCreator_c::CanCalcFastCountFilter() const
  1926. {
  1927. bool bHasAggregates = PredictAggregates();
  1928. return !bHasAggregates && m_tGroupSorterSettings.m_bImplicit && !m_tGroupSorterSettings.m_bDistinct && m_tQuery.m_dFilters.GetLength()==1 && m_tQuery.m_sQuery.IsEmpty() && m_tQuery.m_tKnnSettings.m_sAttr.IsEmpty() && m_tQuery.m_eJoinType!=JoinType_e::INNER;
  1929. }
  1930. bool QueueCreator_c::CanCalcFastCount() const
  1931. {
  1932. bool bHasAggregates = PredictAggregates();
  1933. return !bHasAggregates && m_tGroupSorterSettings.m_bImplicit && !m_tGroupSorterSettings.m_bDistinct && m_tQuery.m_dFilters.IsEmpty() && m_tQuery.m_sQuery.IsEmpty() && m_tQuery.m_tKnnSettings.m_sAttr.IsEmpty() && m_tQuery.m_eJoinType!=JoinType_e::INNER;
  1934. }
  1935. PrecalculatedSorterResults_t QueueCreator_c::FetchPrecalculatedValues() const
  1936. {
  1937. PrecalculatedSorterResults_t tPrecalc;
  1938. if ( CanCalcFastCountDistinct() )
  1939. {
  1940. int iCountDistinctAttr = GetGroupDistinctAttrIndex();
  1941. if ( iCountDistinctAttr>0 && m_tSettings.m_bEnableFastDistinct )
  1942. tPrecalc.m_iCountDistinct = m_tSettings.m_fnGetCountDistinct ? m_tSettings.m_fnGetCountDistinct ( m_pSorterSchema->GetAttr(iCountDistinctAttr).m_sName, tPrecalc.m_sAttr ) : -1;
  1943. }
  1944. if ( CanCalcFastCountFilter() )
  1945. tPrecalc.m_iCountFilter = m_tSettings.m_fnGetCountFilter ? m_tSettings.m_fnGetCountFilter ( m_tQuery.m_dFilters[0], tPrecalc.m_sAttr ) : -1;
  1946. if ( CanCalcFastCount() )
  1947. tPrecalc.m_iCount = m_tSettings.m_fnGetCount ? m_tSettings.m_fnGetCount() : -1;
  1948. return tPrecalc;
  1949. }
  1950. ISphMatchSorter * QueueCreator_c::SpawnQueue()
  1951. {
  1952. bool bNeedFactors = !!(m_uPackedFactorFlags & SPH_FACTOR_ENABLE);
  1953. if ( m_bGotGroupby )
  1954. {
  1955. m_tGroupSorterSettings.m_bGrouped = m_tSettings.m_bGrouped;
  1956. m_tGroupSorterSettings.m_iMaxMatches = AdjustMaxMatches ( m_tGroupSorterSettings.m_iMaxMatches );
  1957. if ( m_pProfile )
  1958. m_pProfile->m_iMaxMatches = m_tGroupSorterSettings.m_iMaxMatches;
  1959. PrecalculatedSorterResults_t tPrecalc = FetchPrecalculatedValues();
  1960. return CreateSorter ( m_eMatchFunc, m_eGroupFunc, &m_tQuery, m_tGroupSorterSettings, bNeedFactors, PredictAggregates(), tPrecalc );
  1961. }
  1962. if ( m_tQuery.m_iLimit == -1 && m_tSettings.m_pSqlRowBuffer )
  1963. return CreateDirectSqlQueue ( m_tSettings.m_pSqlRowBuffer, m_tSettings.m_ppOpaque1, m_tSettings.m_ppOpaque2, std::move (m_tSettings.m_dCreateSchema) );
  1964. if ( m_tSettings.m_pCollection )
  1965. return CreateCollectQueue ( m_tSettings.m_iMaxMatches, *m_tSettings.m_pCollection );
  1966. int iMaxMatches = ReduceOrIncreaseMaxMatches();
  1967. if ( m_pProfile )
  1968. m_pProfile->m_iMaxMatches = iMaxMatches;
  1969. ISphMatchSorter * pSorter = CreatePlainSorter ( m_eMatchFunc, m_tQuery.m_bSortKbuffer, iMaxMatches, bNeedFactors );
  1970. if ( !pSorter )
  1971. return nullptr;
  1972. pSorter = CreateScrollSorter ( pSorter, *m_pSorterSchema, m_eMatchFunc, m_tQuery.m_tScrollSettings, m_bMulti );
  1973. if ( !pSorter )
  1974. return nullptr;
  1975. pSorter = CreateKNNRescoreSorter ( pSorter, m_tQuery.m_tKnnSettings );
  1976. if ( !pSorter )
  1977. return nullptr;
  1978. return CreateColumnarProxySorter ( pSorter, iMaxMatches, *m_pSorterSchema, m_tStateMatch, m_eMatchFunc, bNeedFactors, m_tSettings.m_bComputeItems, m_bMulti );
  1979. }
  1980. bool QueueCreator_c::SetupComputeQueue ()
  1981. {
  1982. return AddJoinAttrs()
  1983. && AddJoinFilterAttrs()
  1984. && MaybeAddGeodistColumn ()
  1985. && AddKNNDistColumn()
  1986. && MaybeAddExprColumn ()
  1987. && MaybeAddExpressionsFromSelectList ()
  1988. && AddExpressionsForUpdates()
  1989. && AddNullBitmask()
  1990. && AddKNNRescoreColumn();
  1991. }
  1992. bool QueueCreator_c::SetupGroupQueue ()
  1993. {
  1994. return AddGroupbyStuff ()
  1995. && SetupMatchesSortingFunc ()
  1996. && SetGroupSorting ();
  1997. }
  1998. bool QueueCreator_c::ConvertColumnarToDocstore()
  1999. {
  2000. // don't use docstore (need to try to keep schemas similar for multiquery to work)
  2001. if ( m_tQuery.m_bFacet || m_tQuery.m_bFacetHead )
  2002. return true;
  2003. auto & tSchema = *m_pSorterSchema;
  2004. // early-out if nothing to process
  2005. bool bFound = false;
  2006. for ( int i = 0; i < tSchema.GetAttrsCount(); i++ )
  2007. {
  2008. auto & tAttr = tSchema.GetAttr(i);
  2009. bool bStored = false;
  2010. bool bColumnar = tAttr.m_pExpr && tAttr.m_pExpr->IsColumnar(&bStored);
  2011. if ( bColumnar && bStored && tAttr.m_eStage==SPH_EVAL_FINAL )
  2012. {
  2013. bFound = true;
  2014. break;
  2015. }
  2016. }
  2017. if ( !bFound )
  2018. return true;
  2019. // try to guess the implicit cutoff (no sorters yet)
  2020. int iCutoff = ApplyImplicitCutoff ( m_tQuery, {}, !m_tQuery.m_pQueryParser->IsFullscan(m_tQuery) );
  2021. bool bEvalAllInFinal = ( iCutoff>=0 && iCutoff<=m_tQuery.m_iLimit ) || ( m_tQuery.m_iLimit==m_tQuery.m_iMaxMatches );
  2022. // check for columnar attributes that have FINAL eval stage
  2023. // if we have more than 1 of such attributes (and they are also stored), we replace columnar expressions with columnar expressions
  2024. IntVec_t dStoredColumnarFinal, dStoredColumnarPostlimit;
  2025. AttrDependencyMap_c tDepMap(tSchema);
  2026. for ( int i = 0; i < tSchema.GetAttrsCount(); i++ )
  2027. {
  2028. auto & tAttr = tSchema.GetAttr(i);
  2029. bool bStored = false;
  2030. bool bColumnar = tAttr.m_pExpr && tAttr.m_pExpr->IsColumnar(&bStored);
  2031. if ( bColumnar && bStored && tAttr.m_eStage==SPH_EVAL_FINAL )
  2032. {
  2033. // we need docids at the final stage if we want to fetch from docstore. so they must be evaluated before that
  2034. if ( !bEvalAllInFinal && tDepMap.IsIndependent ( tAttr.m_sName ) && tAttr.m_sName!=sphGetDocidName() )
  2035. dStoredColumnarPostlimit.Add(i);
  2036. else
  2037. dStoredColumnarFinal.Add(i);
  2038. }
  2039. }
  2040. const int MIN_FINAL_THRESH = 10;
  2041. if ( dStoredColumnarFinal.GetLength()>MIN_FINAL_THRESH )
  2042. for ( auto i : dStoredColumnarFinal )
  2043. {
  2044. auto & tAttr = const_cast<CSphColumnInfo&>( tSchema.GetAttr(i) );
  2045. CSphString sColumnarAttrName;
  2046. tAttr.m_pExpr->Command ( SPH_EXPR_GET_COLUMNAR_COL, &sColumnarAttrName );
  2047. tAttr.m_pExpr = CreateExpr_GetStoredAttr ( sColumnarAttrName, tAttr.m_eAttrType, false );
  2048. }
  2049. // fixme! benchmark and maybe remove the >1 condition
  2050. const int MIN_POSTLIMIT_THRESH = 1;
  2051. bool bDirectQueue = m_tQuery.m_iLimit == -1 && m_tSettings.m_pSqlRowBuffer;
  2052. if ( dStoredColumnarPostlimit.GetLength()>MIN_POSTLIMIT_THRESH && !bDirectQueue )
  2053. {
  2054. for ( auto i : dStoredColumnarPostlimit )
  2055. {
  2056. auto & tAttr = const_cast<CSphColumnInfo&>( tSchema.GetAttr(i) );
  2057. CSphString sColumnarAttrName;
  2058. tAttr.m_pExpr->Command ( SPH_EXPR_GET_COLUMNAR_COL, &sColumnarAttrName );
  2059. tAttr.m_pExpr = CreateExpr_GetStoredAttr ( sColumnarAttrName, tAttr.m_eAttrType, true );
  2060. tAttr.m_eStage = SPH_EVAL_POSTLIMIT;
  2061. }
  2062. m_bExprsNeedDocids = true;
  2063. }
  2064. return true;
  2065. }
  2066. bool QueueCreator_c::SetupQueue ()
  2067. {
  2068. return SetupComputeQueue ()
  2069. && SetupGroupQueue ()
  2070. && ConvertColumnarToDocstore();
  2071. }
  2072. ISphMatchSorter * QueueCreator_c::CreateQueue ()
  2073. {
  2074. SetupCollation();
  2075. if ( m_bHeadWOGroup && m_tGroupSorterSettings.m_bImplicit )
  2076. {
  2077. m_tGroupSorterSettings.m_bImplicit = false;
  2078. m_bGotGroupby = false;
  2079. }
  2080. ///////////////////
  2081. // spawn the queue
  2082. ///////////////////
  2083. ISphMatchSorter * pTop = SpawnQueue();
  2084. if ( !pTop )
  2085. {
  2086. Err ( "internal error: unhandled sorting mode (match-sort=%d, group=%d, group-sort=%d)", m_eMatchFunc, m_bGotGroupby, m_eGroupFunc );
  2087. return nullptr;
  2088. }
  2089. assert ( pTop );
  2090. pTop->SetSchema ( m_pSorterSchema.release(), false );
  2091. pTop->SetState ( m_tStateMatch );
  2092. pTop->SetGroupState ( m_tStateGroup );
  2093. pTop->SetRandom ( m_bRandomize );
  2094. if ( !m_bHaveStar && m_hQueryColumns.GetLength() )
  2095. pTop->SetFilteredAttrs ( m_hQueryColumns, m_tSettings.m_bNeedDocids || m_bExprsNeedDocids );
  2096. if ( m_bRandomize )
  2097. {
  2098. if ( m_tQuery.m_iRandSeed>=0 )
  2099. sphSrand ( (DWORD)m_tQuery.m_iRandSeed );
  2100. else
  2101. sphAutoSrand();
  2102. }
  2103. return pTop;
  2104. }
  2105. static void ResetRemaps ( CSphMatchComparatorState & tState, const CSphRsetSchema & tOldSchema )
  2106. {
  2107. for ( int i = 0; i<CSphMatchComparatorState::MAX_ATTRS; i++ )
  2108. {
  2109. if ( tState.m_dRemapped.BitGet ( i ) )
  2110. {
  2111. bool bStrAttr = ( tState.m_eKeypart[i]==SPH_KEYPART_STRINGPTR );
  2112. bool bJsonAttr = false;
  2113. if ( !bStrAttr )
  2114. {
  2115. const CSphColumnInfo & tCol = tOldSchema.GetAttr ( tState.m_dAttrs[i] );
  2116. bool bTmp = false;
  2117. bJsonAttr = ( tCol.m_eAttrType==SPH_ATTR_JSON || tCol.m_eAttrType==SPH_ATTR_JSON_FIELD || ( tCol.m_pExpr && tCol.m_pExpr->IsJson ( bTmp ) ) );
  2118. }
  2119. if ( bStrAttr || bJsonAttr )
  2120. tState.m_dRemapped.BitClear ( i );
  2121. }
  2122. }
  2123. }
  2124. bool QueueCreator_c::SetSchemaGroupQueue ( const CSphRsetSchema & tNewSchema )
  2125. {
  2126. // need to reissue remap but with existed attributes
  2127. ResetRemaps ( m_tStateMatch, *m_pSorterSchema );
  2128. ResetRemaps ( m_tStateGroup, *m_pSorterSchema );
  2129. *m_pSorterSchema = tNewSchema;
  2130. return SetupGroupQueue();
  2131. }
  2132. ///////////////////////////////////////////////////////////////////////////////
  2133. static ISphMatchSorter * CreateQueue ( QueueCreator_c & tCreator, SphQueueRes_t & tRes )
  2134. {
  2135. ISphMatchSorter * pSorter = tCreator.CreateQueue ();
  2136. tRes.m_bZonespanlist = tCreator.m_bZonespanlist;
  2137. tRes.m_uPackedFactorFlags = tCreator.m_uPackedFactorFlags;
  2138. tRes.m_bJoinedGroupSort = tCreator.m_bJoinedGroupSort;
  2139. return pSorter;
  2140. }
  2141. static void CreateSorters ( const VecTraits_T<CSphQuery> & dQueries, const VecTraits_T<ISphMatchSorter*> & dSorters, const VecTraits_T<QueueCreator_c> & dCreators, const VecTraits_T<CSphString> & dErrors, SphQueueRes_t & tRes )
  2142. {
  2143. ARRAY_FOREACH ( i, dCreators )
  2144. {
  2145. if ( !dCreators[i].m_bCreate )
  2146. continue;
  2147. dSorters[i] = CreateQueue ( dCreators[i], tRes );
  2148. assert ( dSorters[i]!=nullptr );
  2149. }
  2150. if ( tRes.m_bAlowMulti )
  2151. {
  2152. ISphMatchSorter * pSorter0 = nullptr;
  2153. for ( int iCheck=0; iCheck<dSorters.GetLength(); ++iCheck )
  2154. {
  2155. if ( !dCreators[iCheck].m_bCreate )
  2156. continue;
  2157. assert ( dSorters[iCheck] );
  2158. if ( !pSorter0 )
  2159. {
  2160. pSorter0 = dSorters[iCheck];
  2161. continue;
  2162. }
  2163. assert ( dSorters[iCheck]->GetSchema()->GetAttrsCount()==pSorter0->GetSchema()->GetAttrsCount() );
  2164. }
  2165. }
  2166. }
  2167. static void CreateMultiQueue ( RawVector_T<QueueCreator_c> & dCreators, const SphQueueSettings_t & tQueue, const VecTraits_T<CSphQuery> & dQueries, VecTraits_T<ISphMatchSorter*> & dSorters, VecTraits_T<CSphString> & dErrors, SphQueueRes_t & tRes, StrVec_t * pExtra, QueryProfile_c * pProfile, const char * szParent )
  2168. {
  2169. assert ( dSorters.GetLength()>1 );
  2170. assert ( dSorters.GetLength()==dQueries.GetLength() );
  2171. assert ( dSorters.GetLength()==dErrors.GetLength() );
  2172. dCreators.Reserve_static ( dSorters.GetLength () );
  2173. dCreators.Emplace_back ( tQueue, dQueries[0], dErrors[0], pExtra, pProfile, szParent );
  2174. dCreators[0].m_bMulti = true;
  2175. // same as SetupQueue
  2176. bool bSuccess = dCreators[0].SetupComputeQueue ();
  2177. // copy schema WO group by and internals
  2178. CSphRsetSchema tRefSchema = dCreators[0].SorterSchema();
  2179. bool bHasJson = dCreators[0].HasJson();
  2180. bool bJsonMixed = false;
  2181. if ( bSuccess )
  2182. bSuccess &= dCreators[0].SetupGroupQueue ();
  2183. dCreators[0].m_bCreate = bSuccess;
  2184. // create rest of schemas
  2185. for ( int i=1; i<dSorters.GetLength(); ++i )
  2186. {
  2187. // fill extra only for initial pass
  2188. dCreators.Emplace_back ( tQueue, dQueries[i], dErrors[i], pExtra, pProfile, szParent );
  2189. dCreators[i].m_bMulti = true;
  2190. if ( !dCreators[i].SetupQueue () )
  2191. {
  2192. dCreators[i].m_bCreate = false;
  2193. continue;
  2194. }
  2195. bJsonMixed |= ( bHasJson!=dCreators[i].HasJson () );
  2196. bHasJson |= dCreators[i].HasJson();
  2197. }
  2198. // FIXME!!! check attributes and expressions matches
  2199. bool bSame = !bJsonMixed;
  2200. const auto& tSchema0 = dCreators[0].SorterSchema();
  2201. for ( int i=1; i<dCreators.GetLength() && bSame; ++i )
  2202. {
  2203. const auto & tCur = dCreators[i].SorterSchema();
  2204. bSame &= ( tSchema0.GetDynamicSize()==tCur.GetDynamicSize() && tSchema0.GetAttrsCount()==tCur.GetAttrsCount() );
  2205. }
  2206. // same schemes
  2207. if ( bSame )
  2208. return;
  2209. CSphRsetSchema tMultiSchema = tRefSchema;
  2210. int iMinGroups = INT_MAX;
  2211. int iMaxGroups = 0;
  2212. bool bHasMulti = false;
  2213. ARRAY_FOREACH ( iSchema, dCreators )
  2214. {
  2215. if ( !dCreators[iSchema].m_bCreate )
  2216. continue;
  2217. int iGroups = 0;
  2218. const CSphRsetSchema & tSchema = dCreators[iSchema].SorterSchema();
  2219. for ( int iCol=0; iCol<tSchema.GetAttrsCount(); ++iCol )
  2220. {
  2221. const CSphColumnInfo & tCol = tSchema.GetAttr ( iCol );
  2222. if ( !tCol.m_tLocator.m_bDynamic && !tCol.IsColumnar() )
  2223. continue;
  2224. if ( IsGroupbyMagic ( tCol.m_sName ) )
  2225. {
  2226. ++iGroups;
  2227. if ( !IsSortJsonInternal ( tCol.m_sName ))
  2228. continue;
  2229. }
  2230. const CSphColumnInfo * pMultiCol = tMultiSchema.GetAttr ( tCol.m_sName.cstr() );
  2231. if ( pMultiCol )
  2232. {
  2233. bool bDisable1 = false;
  2234. bool bDisable2 = false;
  2235. // no need to add attributes that already exists
  2236. if ( pMultiCol->m_eAttrType==tCol.m_eAttrType &&
  2237. ( ( !pMultiCol->m_pExpr && !tCol.m_pExpr ) ||
  2238. ( pMultiCol->m_pExpr && tCol.m_pExpr
  2239. && pMultiCol->m_pExpr->GetHash ( tMultiSchema, SPH_FNV64_SEED, bDisable1 )==tCol.m_pExpr->GetHash ( tSchema, SPH_FNV64_SEED, bDisable2 ) )
  2240. ) )
  2241. continue;
  2242. // no need to add a new column, but we need the same schema for the sorters
  2243. if ( tCol.IsColumnar() && pMultiCol->IsColumnarExpr() )
  2244. {
  2245. bHasMulti = true;
  2246. continue;
  2247. }
  2248. if ( !tCol.IsColumnarExpr() || !pMultiCol->IsColumnar() ) // need a new column
  2249. {
  2250. tRes.m_bAlowMulti = false; // if attr or expr differs need to create regular sorters and issue search WO multi-query
  2251. return;
  2252. }
  2253. }
  2254. bHasMulti = true;
  2255. tMultiSchema.AddAttr ( tCol, true );
  2256. if ( tCol.m_pExpr )
  2257. tCol.m_pExpr->FixupLocator ( &tSchema, &tMultiSchema );
  2258. }
  2259. iMinGroups = Min ( iMinGroups, iGroups );
  2260. iMaxGroups = Max ( iMaxGroups, iGroups );
  2261. }
  2262. // usual multi query should all have similar group by
  2263. if ( iMinGroups!=iMaxGroups && !dQueries[0].m_bFacetHead && !dQueries[0].m_bFacet )
  2264. {
  2265. tRes.m_bAlowMulti = false;
  2266. return;
  2267. }
  2268. // only group attributes differs - create regular sorters
  2269. if ( !bHasMulti && !bJsonMixed )
  2270. return;
  2271. // setup common schemas
  2272. for ( QueueCreator_c & tCreator : dCreators )
  2273. {
  2274. if ( !tCreator.m_bCreate )
  2275. continue;
  2276. if ( !tCreator.SetSchemaGroupQueue ( tMultiSchema ) )
  2277. tCreator.m_bCreate = false;
  2278. }
  2279. }
  2280. ///////////////////////////////////////////////////////////////////////////////
  2281. ISphMatchSorter * sphCreateQueue ( const SphQueueSettings_t & tQueue, const CSphQuery & tQuery, CSphString & sError, SphQueueRes_t & tRes, StrVec_t * pExtra, QueryProfile_c * pProfile, const char * szParent )
  2282. {
  2283. QueueCreator_c tCreator ( tQueue, tQuery, sError, pExtra, pProfile, szParent );
  2284. if ( !tCreator.SetupQueue () )
  2285. return nullptr;
  2286. return CreateQueue ( tCreator, tRes );
  2287. }
  2288. void sphCreateMultiQueue ( const SphQueueSettings_t & tQueue, const VecTraits_T<CSphQuery> & dQueries, VecTraits_T<ISphMatchSorter *> & dSorters, VecTraits_T<CSphString> & dErrors, SphQueueRes_t & tRes, StrVec_t * pExtra, QueryProfile_c * pProfile, const char * szParent )
  2289. {
  2290. RawVector_T<QueueCreator_c> dCreators;
  2291. CreateMultiQueue ( dCreators, tQueue, dQueries, dSorters, dErrors, tRes, pExtra, pProfile, szParent );
  2292. CreateSorters ( dQueries, dSorters, dCreators, dErrors, tRes );
  2293. }