ECLEngine.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "ECLEngine.hpp"
  14. #include <limits> // std::numeric_limits
  15. const char * ECLEngine::SELECTOUTPUTNAME = "WSSQLSelectQueryResult";
  16. ECLEngine::ECLEngine(){}
  17. ECLEngine::~ECLEngine(){}
  18. void ECLEngine::generateECL(HPCCSQLTreeWalker * sqlobj, StringBuffer & out)
  19. {
  20. if (sqlobj)
  21. {
  22. switch (sqlobj->getSqlType())
  23. {
  24. case SQLTypeSelect:
  25. generateSelectECL(sqlobj, out);
  26. break;
  27. case SQLTypeCall:
  28. break;
  29. case SQLTypeCreateAndLoad:
  30. generateCreateAndLoad(sqlobj, out);
  31. break;
  32. case SQLTypeUnknown:
  33. default:
  34. break;
  35. }
  36. }
  37. }
  38. void ECLEngine::generateIndexSetupAndFetch(HPCCFilePtr file, SQLTable * table, int tableindex, HPCCSQLTreeWalker * selectsqlobj, IProperties* eclEntities)
  39. {
  40. bool isPayloadIndex = false;
  41. bool avoidindex = false;
  42. StringBuffer indexname;
  43. if (!table)
  44. return;
  45. const char * tname = table->getName();
  46. StringBuffer indexHintFromSQL;
  47. if (table->hasIndexHint())
  48. {
  49. indexHintFromSQL.set(table->getIndexhint());
  50. if (strncmp(indexHintFromSQL.trim().str(), "0", 1)==0)
  51. {
  52. avoidindex = true;
  53. WARNLOG("Will not use any index.");
  54. return;
  55. }
  56. else
  57. WARNLOG("Empty index hint found!");
  58. }
  59. findAppropriateIndex(file, indexHintFromSQL.str(), selectsqlobj, indexname);
  60. if (indexHintFromSQL.length() > 0 && indexname.length() == 0)
  61. WARNLOG("Unusable index hint detected.");
  62. if (indexname.length()>0)
  63. {
  64. HPCCFilePtr indexfile = selectsqlobj->queryHPCCFileCache()->getHpccFileByName(indexname);
  65. if (indexfile && file)
  66. {
  67. StringBuffer idxsetupstr;
  68. StringBuffer idxrecdefname;
  69. idxrecdefname.set("TblDS").append(tableindex).append("RecDef");
  70. StringBuffer indexPosField;
  71. indexPosField.set(indexfile->getIdxFilePosField());
  72. HPCCColumnMetaData * poscol = indexfile->getColumn(indexPosField);
  73. file->getFileRecDefwithIndexpos(poscol, idxsetupstr, idxrecdefname.str());
  74. eclEntities->appendProp("INDEXFILERECDEF", idxsetupstr.str());
  75. StringBuffer keyedAndWild;
  76. isPayloadIndex = processIndex(indexfile, keyedAndWild, selectsqlobj);
  77. if (keyedAndWild.length() > 0)
  78. eclEntities->appendProp("KEYEDWILD", keyedAndWild.str());
  79. if (isPayloadIndex)
  80. eclEntities->appendProp("PAYLOADINDEX", "true");
  81. idxsetupstr.clear();
  82. idxsetupstr.appendf("Idx%d := INDEX(TblDS%d, {", tableindex, tableindex);
  83. indexfile->getKeyedFieldsAsDelimitedString(',', "", idxsetupstr);
  84. idxsetupstr.append("}");
  85. if (indexfile->getNonKeyedColumnsCount() > 0)
  86. {
  87. idxsetupstr.append(",{ ");
  88. indexfile->getNonKeyedFieldsAsDelmitedString(',', "", idxsetupstr);
  89. idxsetupstr.append(" }");
  90. }
  91. //Note, currently '~' is not valid char, if it is ever allowed, we'd have verify that the file name does not lead off with ~
  92. idxsetupstr.appendf(",\'~%s\');\n",indexfile->getFullname());
  93. eclEntities->appendProp("IndexDef", idxsetupstr.str());
  94. idxsetupstr.clear();
  95. if (isPayloadIndex)
  96. {
  97. WARNLOG(" as PAYLOAD");
  98. idxsetupstr.appendf("IdxDS%d := Idx%d(%s", tableindex, tableindex, keyedAndWild.str());
  99. }
  100. else
  101. {
  102. WARNLOG(" Not as PAYLOAD");
  103. idxsetupstr.appendf("IdxDS%d := FETCH(TblDS%d, Idx%d( %s ), RIGHT.%s", tableindex, tableindex, tableindex, keyedAndWild.str(), indexfile->getIdxFilePosField());
  104. }
  105. idxsetupstr.append(");\n");
  106. eclEntities->appendProp("IndexRead", idxsetupstr.str());
  107. }
  108. else
  109. WARNLOG("NOT USING INDEX!");
  110. }
  111. }
  112. void ECLEngine::generateCreateAndLoad(HPCCSQLTreeWalker * sqlobj, StringBuffer & out)
  113. {
  114. const char * targetTableName = sqlobj->getTableName();
  115. if (!targetTableName || !*targetTableName)
  116. throw MakeStringException(-1, "Error: TableName cannot be empty.");
  117. if (!HPCCFile::validateFileName(targetTableName))
  118. throw MakeStringException(-1, "Error: Target TableName is invalid: %s.", targetTableName);
  119. StringBuffer sourceFileName;
  120. sourceFileName.set(sqlobj->getSourceDataTableName()).trim();
  121. StringBuffer landingZoneIP(sqlobj->getLandingZoneIp());
  122. if (landingZoneIP.length())
  123. {
  124. StringBuffer landingZonePath(sqlobj->getLandingZonePath());
  125. if (landingZonePath.length())
  126. {
  127. addPathSepChar(landingZonePath);
  128. RemoteFilename rfn;
  129. SocketEndpoint ep(landingZoneIP);
  130. rfn.setPath(ep, landingZonePath.append(sourceFileName.str()).str());
  131. CDfsLogicalFileName dlfn;
  132. dlfn.setExternal(rfn);
  133. dlfn.get(sourceFileName.clear(), false, false);
  134. }
  135. }
  136. out.appendf("import std;\nTABLERECORDDEF := RECORD\n%s\nEND;\n", sqlobj->getRecordDefinition());
  137. out.appendf("FILEDATASET := DATASET('~%s', TABLERECORDDEF, %s);\n",sourceFileName.str(), sqlobj->getSourceDataType());
  138. out.appendf("OUTPUT(true, NAMED(\'%s\'));\n", SELECTOUTPUTNAME); //THOR= WU results written to file
  139. out.appendf("OUTPUT(COUNT(FILEDATASET), NAMED(\'%sCount\'));\n", SELECTOUTPUTNAME);
  140. out.appendf("OUTPUT(FILEDATASET, ,'~%s'%s);\n", targetTableName, sqlobj->isOverwrite() ? ", OVERWRITE" : "");
  141. const char * description = sqlobj->getComment();
  142. if (description && * description)
  143. out.appendf("Std.file.setfiledescription('~%s','%s')\n", targetTableName, description);
  144. }
  145. void ECLEngine::generateSelectECL(HPCCSQLTreeWalker * selectsqlobj, StringBuffer & out)
  146. {
  147. StringBuffer latestDS("TblDS0");
  148. Owned<IProperties> eclEntities = createProperties(true);
  149. Owned<IProperties> eclDSSourceMapping = createProperties(true);
  150. Owned<IProperties> translator = createProperties(true);
  151. out.clear();
  152. out.append("import std;\n"); /* ALL Generated ECL will import std, even if std lib not used */
  153. //Prepared statement parameters are handled by ECL STORED service workflow statements
  154. if (selectsqlobj->hasWhereClause())
  155. selectsqlobj->getWhereClause()->eclDeclarePlaceHolders(out, 0,0);
  156. if (selectsqlobj->hasHavingClause())
  157. selectsqlobj->getHavingClause()->eclDeclarePlaceHolders(out, 0,0);
  158. const IArrayOf<SQLTable> * tables = selectsqlobj->getTableList();
  159. ForEachItemIn(tableidx, *tables)
  160. {
  161. SQLTable table = tables->item(tableidx);
  162. const char * tname = table.getName();
  163. HPCCFilePtr file = selectsqlobj->queryHPCCFileCache()->getHpccFileByName(tname);
  164. if (file)
  165. {
  166. translator->setProp(tname, "LEFT");
  167. StringBuffer currntTblDS("TblDS");
  168. currntTblDS.append(tableidx);
  169. StringBuffer currntTblRecDef(currntTblDS);
  170. currntTblRecDef.append("RecDef");
  171. StringBuffer currntJoin("JndDS");
  172. currntJoin.append(tableidx);
  173. out.append("\n");
  174. if (tableidx == 0)
  175. {
  176. //Currently only utilizing index fetch/read for single table queries
  177. if ((table.hasIndexHint() || file->getRelatedIndexCount()) && tables->length() == 1 && !file->isFileKeyed())
  178. generateIndexSetupAndFetch(file, &table, tableidx, selectsqlobj, eclEntities);
  179. if (eclEntities->hasProp("INDEXFILERECDEF"))
  180. {
  181. eclDSSourceMapping->appendProp(tname, "IdxDS0");
  182. eclEntities->getProp("INDEXFILERECDEF", out);
  183. latestDS.set("IdxDS0");
  184. }
  185. else
  186. file->getFileRecDef(out, currntTblRecDef);
  187. }
  188. else
  189. file->getFileRecDef(out, currntTblRecDef);
  190. out.append("\n");
  191. if (!file->isFileKeyed())
  192. {
  193. out.appendf("%s := DATASET(\'~%s\', %s, %s);\n", currntTblDS.str(), file->getFullname(), currntTblRecDef.str(), file->getFormat());
  194. }
  195. else
  196. {
  197. out.appendf("%s := INDEX( {", currntTblDS.str());
  198. file->getKeyedFieldsAsDelimitedString(',', currntTblRecDef.str(), out);
  199. out.append("},{");
  200. file->getNonKeyedFieldsAsDelmitedString(',', currntTblRecDef.str(), out);
  201. //Note, currently '~' is not valid char, if it is ever allowed, we'd have verify that the file name does not lead off with ~
  202. out.appendf("},\'~%s\');\n",file->getFullname());
  203. }
  204. if (tableidx > 0)
  205. {
  206. out.append("\n").append(currntJoin).append(" := JOIN( ");
  207. translator->setProp(tname, "RIGHT");
  208. if (tableidx == 1)
  209. {
  210. //First Join, previous DS is TblDS0
  211. out.append("TblDS0");
  212. latestDS.set("JndDS1");
  213. }
  214. else
  215. {
  216. //Nth Join, previous DS is JndDS(N-1)
  217. out.appendf("JndDS%d",tableidx-1);
  218. latestDS.setf("JndDS%d",tableidx);
  219. }
  220. StringBuffer translatedAndFilteredOnClause;
  221. SQLJoin * tablejoin = table.getJoin();
  222. if (tablejoin && tablejoin->doesHaveOnclause())
  223. {
  224. tablejoin->getOnClause()->toECLStringTranslateSource(translatedAndFilteredOnClause, translator,true, false, false, false);
  225. if (selectsqlobj->hasWhereClause())
  226. {
  227. translatedAndFilteredOnClause.append(" AND ");
  228. selectsqlobj->getWhereClause()->toECLStringTranslateSource(translatedAndFilteredOnClause, translator, true, true, false, false);
  229. }
  230. }
  231. else if ( tablejoin && tablejoin->getType() == SQLJoinTypeImplicit && selectsqlobj->hasWhereClause())
  232. {
  233. if (translatedAndFilteredOnClause.length() > 0)
  234. translatedAndFilteredOnClause.append(" AND ");
  235. selectsqlobj->getWhereClause()->toECLStringTranslateSource(translatedAndFilteredOnClause, translator, true, true, false, false);
  236. }
  237. else
  238. throw MakeStringException(-1,"No join condition between tables %s, and earlier table", tname);
  239. if (translatedAndFilteredOnClause.length() <= 0)
  240. throw MakeStringException(-1,"Join condition does not contain proper join condition between tables %s, and earlier table", tname);
  241. out.appendf(", %s, %s, ", currntTblDS.str(), translatedAndFilteredOnClause.length() > 0 ? translatedAndFilteredOnClause.str() : "TRUE");
  242. tablejoin->getECLTypeStr(out);
  243. if (tablejoin->getOnClause() != NULL && !tablejoin->getOnClause()->containsEqualityCondition(translator, "LEFT", "RIGHT"))
  244. {
  245. WARNLOG("Warning: No Join EQUALITY CONDITION detected!, using ECL ALL option");
  246. out.append(", ALL");
  247. }
  248. out.append(" );\n");
  249. //move this file to LEFT for possible next iteration
  250. translator->setProp(tname, "LEFT");
  251. }
  252. eclEntities->setProp("JoinQuery", "1");
  253. }
  254. }
  255. int limit=selectsqlobj->getLimit();
  256. int offset=selectsqlobj->getOffset();
  257. if (!eclEntities->hasProp("IndexDef"))
  258. {
  259. //Create filtered DS if there's a where clause, and no join clause,
  260. //because filtering is applied while performing join.
  261. //if (sqlParser.getWhereClause() != null && !eclEntities.containsKey("JoinQuery"))
  262. if (selectsqlobj->hasWhereClause())
  263. {
  264. out.appendf("%sFiltered := %s",latestDS.str(), latestDS.str());
  265. addFilterClause(selectsqlobj, out);
  266. out.append(";\n");
  267. latestDS.append("Filtered");
  268. }
  269. generateSelectStruct(selectsqlobj, eclEntities.getLink(), *selectsqlobj->getSelectList(),latestDS.str());
  270. out.append(eclEntities->queryProp("SELECTSTRUCT"));
  271. if (tables->length() > 0)
  272. {
  273. out.append(latestDS).append("Table").append(" := TABLE( ");
  274. out.append(latestDS);
  275. out.append(", SelectStruct ");
  276. // If group by contains HAVING clause, use ECL 'HAVING' function,
  277. // otherwise group can be done implicitly in table step.
  278. // since the implicit approach has better performance.
  279. if (selectsqlobj->hasGroupByColumns() && !selectsqlobj->hasHavingClause())
  280. {
  281. out.append(", ");
  282. selectsqlobj->getGroupByString(out);
  283. out.append(" /*grouped by this field*/");
  284. }
  285. out.append(");\n");
  286. latestDS.append("Table");
  287. if (selectsqlobj->hasGroupByColumns() && selectsqlobj->hasHavingClause())
  288. {
  289. out.appendf("%sGrouped := GROUP( %s, ", latestDS.str(), latestDS.str());
  290. selectsqlobj->getGroupByString(out);
  291. out.append(", ALL);\n");
  292. latestDS.append("Grouped");
  293. if (appendTranslatedHavingClause(selectsqlobj, out, latestDS.str()))
  294. latestDS.append("Having");
  295. }
  296. }
  297. else
  298. {
  299. generateConstSelectDataset(selectsqlobj, eclEntities.getLink(), *selectsqlobj->getSelectList(),latestDS.str());
  300. out.append(latestDS).append(" := ").append(eclEntities->queryProp("CONSTDATASETSTRUCT")).append(";\n");
  301. }
  302. }
  303. else //PROCESSING FOR INDEX BASED FETCH
  304. {
  305. eclEntities->getProp("IndexDef",out);
  306. eclEntities->getProp("IndexRead",out);
  307. // If group by contains HAVING clause, use ECL 'HAVING' function,
  308. // otherwise group can be done implicitly in table step.
  309. // since the implicit approach has better performance.
  310. if (selectsqlobj->hasGroupByColumns() && selectsqlobj->hasHavingClause())
  311. {
  312. out.appendf("%sGrouped := GROUP( %s, ", latestDS.str(), latestDS.str());
  313. selectsqlobj->getGroupByString(out);
  314. out.append(", ALL);\n");
  315. latestDS.append("Grouped");
  316. if (appendTranslatedHavingClause(selectsqlobj, out, latestDS.str()))
  317. latestDS.append("Having");
  318. }
  319. generateSelectStruct(selectsqlobj, eclEntities.get(), *selectsqlobj->getSelectList(),latestDS.str());
  320. out.append(eclEntities->queryProp("SELECTSTRUCT"));
  321. out.appendf("%sTable := TABLE(%s",latestDS.str(), latestDS.str());
  322. //Filtering all non-payload index, because the original filter applied at fetch could
  323. //be incomplete due to non-keyed fields
  324. if (!eclEntities->hasProp("PAYLOADINDEX"))
  325. {
  326. addFilterClause(selectsqlobj, out);
  327. }
  328. out.append(", SelectStruct ");
  329. if (selectsqlobj->hasGroupByColumns() && !selectsqlobj->hasHavingClause())
  330. {
  331. out.append(", ");
  332. selectsqlobj->getGroupByString(out);
  333. out.append(" /*grouped by this field*/");
  334. }
  335. out.append(");\n");
  336. latestDS.append("Table");
  337. }
  338. if (selectsqlobj->isSelectDistinct())
  339. {
  340. out.appendf("%sDeduped := Dedup( %s, HASH);\n",latestDS.str(),latestDS.str());
  341. latestDS.append("Deduped");
  342. }
  343. out.appendf("%sOut := CHOOSEN(", latestDS.str());
  344. if (selectsqlobj->hasOrderByColumns())
  345. out.append("SORT(");
  346. out.append(latestDS);
  347. if (selectsqlobj->hasOrderByColumns())
  348. {
  349. out.append(",");
  350. selectsqlobj->getOrderByString(out);
  351. out.append(")");
  352. }
  353. latestDS.append("Out");
  354. if (!eclEntities->hasProp("NONSCALAREXPECTED") && !selectsqlobj->hasGroupByColumns())
  355. {
  356. out.append(", 1);\n");
  357. }
  358. else
  359. {
  360. out.append(",");
  361. if (limit>0)
  362. {
  363. out.append(limit);
  364. if (offset>0)
  365. {
  366. out.append(",");
  367. out.append(offset);
  368. }
  369. }
  370. else
  371. out.append("ALL");
  372. out.append(");\n");
  373. }
  374. out.appendf("OUTPUT(%s, NAMED(\'%s\'), THOR);\n", latestDS.str(), SELECTOUTPUTNAME); //THOR= WU results written to file
  375. out.appendf("OUTPUT( COUNT(%s), NAMED(\'%sCount\'));\n", latestDS.str(), SELECTOUTPUTNAME);
  376. }
  377. void ECLEngine::generateConstSelectDataset(HPCCSQLTreeWalker * selectsqlobj, IProperties* eclEntities, const IArrayOf<ISQLExpression> & expectedcolumns, const char * datasource)
  378. {
  379. StringBuffer datasetStructSB("DATASET([{ ");
  380. ForEachItemIn(i, expectedcolumns)
  381. {
  382. ISQLExpression * col = &expectedcolumns.item(i);
  383. col->toString(datasetStructSB, true);
  384. if (i < expectedcolumns.length()-1)
  385. datasetStructSB.append(", ");
  386. }
  387. datasetStructSB.append("}],SelectStruct)");
  388. eclEntities->setProp("CONSTDATASETSTRUCT", datasetStructSB.str());
  389. }
  390. void ECLEngine::generateSelectStruct(HPCCSQLTreeWalker * selectsqlobj, IProperties* eclEntities, const IArrayOf<ISQLExpression> & expectedcolumns, const char * datasource)
  391. {
  392. StringBuffer selectStructSB("SelectStruct := RECORD\n");
  393. ForEachItemIn(i, expectedcolumns)
  394. {
  395. selectStructSB.append(" ");
  396. ISQLExpression * col = &expectedcolumns.item(i);
  397. if (col->getExpType() == Value_ExpressionType)
  398. {
  399. const char * alias = col->getAlias();
  400. if (alias && *alias)
  401. selectStructSB.appendf("%s %s := ", col->getECLType(), alias);
  402. else
  403. selectStructSB.appendf("%s %s%d := ", col->getECLType(), col->getName(), i);
  404. col->toString(selectStructSB, false);
  405. selectStructSB.append("; ");
  406. if (i == 0 && expectedcolumns.length() == 1)
  407. eclEntities->setProp("SCALAROUTNAME", col->getNameOrAlias());
  408. }
  409. else if (col->getExpType() == Function_ExpressionType)
  410. {
  411. SQLFunctionExpression * funcexp = static_cast<SQLFunctionExpression *>(col);
  412. IArrayOf<ISQLExpression> * funccols = funcexp->getParams();
  413. ECLFunctionDefCfg func = ECLFunctions::getEclFuntionDef(funcexp->getName());
  414. if (func.functionType == CONTENT_MODIFIER_FUNCTION_TYPE )
  415. {
  416. if (funccols->length() > 0)
  417. {
  418. ISQLExpression * param = &funccols->item(0);
  419. int paramtype = param->getExpType();
  420. const char * alias = col->getAlias();
  421. if (alias && *alias)
  422. selectStructSB.append(alias);
  423. else
  424. selectStructSB.append(param->getName());
  425. selectStructSB.append(" := ");
  426. selectStructSB.append(func.eclFunctionName).append("( ");
  427. if (paramtype == FieldValue_ExpressionType)
  428. {
  429. eclEntities->setProp("NONSCALAREXPECTED", "TRUE");
  430. selectStructSB.append(datasource);
  431. selectStructSB.append(".");
  432. selectStructSB.append(param->getNameOrAlias());
  433. }
  434. else
  435. param->toString(selectStructSB, false);
  436. }
  437. }
  438. else
  439. {
  440. const char * alias = col->getAlias();
  441. if (alias && *alias)
  442. selectStructSB.append(alias);
  443. else
  444. {
  445. selectStructSB.append(col->getName());
  446. selectStructSB.append("out");
  447. selectStructSB.append(i+1);
  448. }
  449. selectStructSB.append(" := ");
  450. selectStructSB.append(func.eclFunctionName).append("( ");
  451. if (selectsqlobj->hasGroupByColumns())
  452. {
  453. selectStructSB.append("GROUP ");
  454. }
  455. else
  456. {
  457. if (funcexp->isDistinct())
  458. {
  459. selectStructSB.append("DEDUP( ");
  460. selectStructSB.append(datasource);
  461. addFilterClause(selectsqlobj, selectStructSB);
  462. for (int j = 0; j < funccols->length(); j++)
  463. {
  464. StringBuffer paramname(funccols->item(j).getName());
  465. selectStructSB.append(", ");
  466. selectStructSB.append(paramname);
  467. }
  468. selectStructSB.append(", HASH)");
  469. }
  470. else
  471. {
  472. selectStructSB.append(datasource);
  473. addFilterClause(selectsqlobj, selectStructSB);
  474. }
  475. }
  476. if ((strcmp(func.name,"COUNT"))!=0 && funccols->length() > 0)
  477. {
  478. ISQLExpression &funccol = funccols->item(0);
  479. const char * paramname = funccol.getName();
  480. if (paramname && paramname[0]!='*')
  481. {
  482. selectStructSB.append(", ");
  483. if (funccol.getExpType() != Value_ExpressionType)
  484. {
  485. selectStructSB.append(datasource);
  486. selectStructSB.append(".");
  487. selectStructSB.append(paramname);
  488. }
  489. else
  490. {
  491. funccol.toString(selectStructSB, false);
  492. }
  493. }
  494. }
  495. }
  496. //AS OF community_3.8.6-4 this is causing error:
  497. // (0,0): error C3000: assert(!cond) failed - file: /var/jenkins/workspace/<build number>/HPCC-Platform/ecl/hqlcpp/hqlhtcpp.cpp, line XXXXX
  498. //Bug reported: https://track.hpccsystems.com/browse/HPCC-8268
  499. //Leaving this code out until fix is produced.
  500. //UPDATE: Issue has been resolved as of 3.10.0
  501. //RODRIGO below if condition not completed yet
  502. //if (eclEntities.containsKey("PAYLOADINDEX") && !sqlParser.hasGroupByColumns() && !col.isDistinct())
  503. if (false && !selectsqlobj->hasGroupByColumns() && !funcexp->isDistinct())
  504. {
  505. selectStructSB.append(", KEYED");
  506. }
  507. selectStructSB.append(" );");
  508. }
  509. else
  510. {
  511. eclEntities->setProp("NONSCALAREXPECTED", "TRUE");
  512. selectStructSB.appendf("%s %s := %s.%s;", col->getECLType(), col->getNameOrAlias(), datasource, col->getName());
  513. }
  514. selectStructSB.append("\n");
  515. }
  516. selectStructSB.append("END;\n");
  517. eclEntities->setProp("SELECTSTRUCT", selectStructSB.str());
  518. }
  519. bool containsPayload(const HPCCFile * indexfiletotest, const HPCCSQLTreeWalker * selectsqlobj)
  520. {
  521. if (selectsqlobj)
  522. {
  523. const IArrayOf <ISQLExpression> * selectlist = selectsqlobj->getSelectList();
  524. for (int j = 0; j < selectlist->length(); j++)
  525. {
  526. ISQLExpression * exp = &selectlist->item(j);
  527. if (exp->getExpType() == FieldValue_ExpressionType)
  528. {
  529. SQLFieldValueExpression * currentselectcol = static_cast<SQLFieldValueExpression *>(exp);
  530. if (!indexfiletotest->containsField(currentselectcol->queryField(), true))
  531. return false;
  532. }
  533. else if (exp->getExpType() == Function_ExpressionType)
  534. {
  535. SQLFunctionExpression * currentfunccol = static_cast<SQLFunctionExpression *>(exp);
  536. IArrayOf<ISQLExpression> * funcparams = currentfunccol->getParams();
  537. ForEachItemIn(paramidx, *funcparams)
  538. {
  539. ISQLExpression * param = &(funcparams->item(paramidx));
  540. if (param->getExpType() == FieldValue_ExpressionType)
  541. {
  542. SQLFieldValueExpression * currentselectcol = static_cast<SQLFieldValueExpression *>(param);
  543. if (!indexfiletotest->containsField(currentselectcol->queryField(), true))
  544. return false;
  545. }
  546. }
  547. }
  548. }
  549. return true;
  550. }
  551. return false;
  552. }
  553. bool ECLEngine::processIndex(HPCCFile * indexfiletouse, StringBuffer & keyedandwild, HPCCSQLTreeWalker * selectsqlobj)
  554. {
  555. ISQLExpression * whereclause = selectsqlobj->getWhereClause();
  556. if (!whereclause)
  557. return false;
  558. bool isPayloadIndex = containsPayload(indexfiletouse, selectsqlobj);
  559. StringArray keyed;
  560. StringArray wild;
  561. StringArray filterclauseuniquenames;
  562. whereclause->getUniqueExpressionColumnNames(filterclauseuniquenames);
  563. bool allfieldsinfilterexistinindexfile = true;
  564. // Create keyed and wild string
  565. IArrayOf<HPCCColumnMetaData> * indexfilecolumns = indexfiletouse->getColumns();
  566. //We need to know if the filter clause contains only fields which exist in the index file
  567. ForEachItemIn(uniquenamesidx, filterclauseuniquenames)
  568. {
  569. bool currfilterfieldexistsinindexfile = false;
  570. for (int indexfilecolumnindex = 0; indexfilecolumnindex < indexfilecolumns->length(); indexfilecolumnindex++)
  571. {
  572. HPCCColumnMetaData currcol = indexfilecolumns->item(indexfilecolumnindex);
  573. const char * currindexfilecolname = currcol.getColumnName();
  574. if(stricmp( filterclauseuniquenames.item(uniquenamesidx), currindexfilecolname)==0)
  575. {
  576. currfilterfieldexistsinindexfile = true;
  577. break;
  578. }
  579. }
  580. if (!currfilterfieldexistsinindexfile)
  581. {
  582. allfieldsinfilterexistinindexfile = false;
  583. break;
  584. }
  585. }
  586. if (allfieldsinfilterexistinindexfile)
  587. {
  588. for (int indexfilecolumnindex = 0; indexfilecolumnindex < indexfilecolumns->length(); indexfilecolumnindex++)
  589. {
  590. HPCCColumnMetaData currcol = indexfilecolumns->item(indexfilecolumnindex);
  591. const char * currindexfilecolname = currcol.getColumnName();
  592. if (currcol.isKeyedField())
  593. {
  594. StringBuffer keyedorwild;
  595. whereclause->getExpressionFromColumnName(currindexfilecolname, keyedorwild);
  596. if (whereclause->containsKey(currindexfilecolname) && keyedorwild.length())
  597. {
  598. keyed.append(keyedorwild.str());
  599. }
  600. else
  601. {
  602. keyedorwild.setf(" %s ", currindexfilecolname);
  603. wild.append(keyedorwild.str());
  604. }
  605. }
  606. }
  607. int keyedlen = keyed.length();
  608. if (keyedlen > 0)
  609. {
  610. for (int keyedi = 0; keyedi < keyedlen; keyedi++)
  611. {
  612. keyedandwild.append(" KEYED( ");
  613. keyedandwild.append(keyed.item(keyedi));
  614. keyedandwild.append(" )");
  615. if (keyedi < keyedlen - 1)
  616. keyedandwild.append(", ");
  617. }
  618. if (wild.length() > 0)
  619. {
  620. for (int wildi = 0; wildi < wild.length(); wildi++)
  621. {
  622. if (keyedlen || wildi > 0)
  623. keyedandwild.append(" AND ");
  624. keyedandwild.appendf("WILD( %s )", wild.item(wildi));
  625. }
  626. }
  627. keyedandwild.append(" AND ");
  628. }
  629. keyedandwild.append(" (");
  630. whereclause->toString(keyedandwild, false);
  631. keyedandwild.append(") ");
  632. }
  633. /*
  634. * Even though filtering at the fetch is preferable,
  635. * the filter can only reference keyed/non-keyed fields from the index file
  636. * it is not always feasible to extract the algebraic condition based only on those fields
  637. * else {}
  638. *
  639. */
  640. //if the filter condition contains field not in the payload index file, we cannot do a payload read
  641. return isPayloadIndex && allfieldsinfilterexistinindexfile;
  642. }
  643. void ECLEngine::findAppropriateIndex(HPCCFilePtr file, const char * indexhint, HPCCSQLTreeWalker * selectsqlobj, StringBuffer & indexname)
  644. {
  645. StringArray indexhints;
  646. if (indexhint && *indexhint)
  647. indexhints.append(indexhint);
  648. if (file)
  649. file->getRelatedIndexes(indexhints);
  650. findAppropriateIndex(&indexhints, selectsqlobj, indexname);
  651. }
  652. void ECLEngine::findAppropriateIndex(StringArray * relindexes, HPCCSQLTreeWalker * selectsqlobj, StringBuffer & indexname)
  653. {
  654. StringArray uniquenames;
  655. ISQLExpression * whereclause = selectsqlobj->getWhereClause();
  656. if (whereclause)
  657. whereclause->getUniqueExpressionColumnNames(uniquenames);
  658. else
  659. return;
  660. int totalparamcount = uniquenames.length();
  661. if (relindexes->length() <= 0 || totalparamcount <= 0)
  662. return;
  663. bool payloadIdxWithAtLeast1KeyedFieldFound = false;
  664. IntArray scores;
  665. for (int indexcounter = 0; indexcounter < relindexes->length(); indexcounter++)
  666. {
  667. scores.add(std::numeric_limits<int>::min(), indexcounter);
  668. const char * indexname = relindexes->item(indexcounter);
  669. if (!selectsqlobj->queryHPCCFileCache()->isHpccFileCached(indexname))
  670. selectsqlobj->queryHPCCFileCache()->cacheHpccFileByName(indexname);
  671. HPCCFilePtr indexfile = selectsqlobj->queryHPCCFileCache()->getHpccFileByName(indexname);
  672. if (indexfile)
  673. {
  674. const IArrayOf<ISQLExpression> * expectedretcolumns =selectsqlobj->getSelectList();
  675. if (indexfile && indexfile->isFileKeyed() && indexfile->hasValidIdxFilePosField())
  676. {
  677. //The more fields this index has in common with the select columns higher score
  678. int commonparamscount = 0;
  679. for (int j = 0; j < expectedretcolumns->length(); j++)
  680. {
  681. ISQLExpression * exp = &expectedretcolumns->item(j);
  682. if (exp->getExpType() == FieldValue_ExpressionType)
  683. {
  684. SQLFieldValueExpression * fieldexp = static_cast<SQLFieldValueExpression *>(exp);
  685. if (indexfile->containsField(fieldexp->queryField(), true))
  686. commonparamscount++;
  687. }
  688. else if (exp->getExpType() == Function_ExpressionType)
  689. {
  690. SQLFunctionExpression * currentfunccol = static_cast<SQLFunctionExpression *>(exp);
  691. IArrayOf<ISQLExpression> * funcparams = currentfunccol->getParams();
  692. ForEachItemIn(paramidx, *funcparams)
  693. {
  694. ISQLExpression * param = &(funcparams->item(paramidx));
  695. if (param->getExpType() == FieldValue_ExpressionType)
  696. {
  697. SQLFieldValueExpression * currentselectcol = static_cast<SQLFieldValueExpression *>(param);
  698. if (indexfile->containsField(currentselectcol->queryField(), true))
  699. commonparamscount++;
  700. }
  701. }
  702. }
  703. }
  704. int commonparamsscore = commonparamscount * NumberOfCommonParamInThisIndex_WEIGHT;
  705. scores.replace(commonparamsscore, indexcounter);
  706. if (payloadIdxWithAtLeast1KeyedFieldFound && commonparamscount == 0)
  707. break; // Don't bother with this index
  708. //The more keyed fields this index has in common with the where clause, the higher score
  709. //int localleftmostindex = -1;
  710. int keycolscount = 0;
  711. IArrayOf<HPCCColumnMetaData> * columns = indexfile->getColumns();
  712. ForEachItemIn(colidx, *columns)
  713. {
  714. HPCCColumnMetaData currcol = columns->item(colidx);
  715. if (currcol.isKeyedField())
  716. {
  717. ForEachItemIn(uniqueidx, uniquenames)
  718. {
  719. if(strcmp( uniquenames.item(uniqueidx), currcol.getColumnName())==0)
  720. keycolscount++;
  721. }
  722. }
  723. }
  724. if (keycolscount == 0)
  725. {
  726. scores.replace(std::numeric_limits<int>::min(), indexcounter);
  727. continue;
  728. }
  729. int keycolsscore = keycolscount * NumberofColsKeyedInThisIndex_WEIGHT;
  730. scores.replace(keycolsscore + scores.item(indexcounter), indexcounter);
  731. if (commonparamscount == expectedretcolumns->length() && keycolscount > 0)
  732. payloadIdxWithAtLeast1KeyedFieldFound = true; // during scoring, give this priority
  733. }
  734. }
  735. }
  736. int highscore = std::numeric_limits<int>::min();
  737. int highscoreidx = -1;
  738. for (int i = 0; i < scores.length(); i++)
  739. {
  740. if (highscore < scores.item(i))
  741. {
  742. highscore = scores.item(i);
  743. highscoreidx = i;
  744. }
  745. }
  746. if (highscoreidx != -1 && highscoreidx < relindexes->length())
  747. indexname.set(relindexes->item(highscoreidx));
  748. }
  749. void ECLEngine::addFilterClause(HPCCSQLTreeWalker * sqlobj, StringBuffer & sb)
  750. {
  751. if (sqlobj->hasWhereClause())
  752. {
  753. StringBuffer where;
  754. sqlobj->getWhereClauseString(where);
  755. if (where.length()>0)
  756. {
  757. sb.append("( ").append(where.str()).append(" )");
  758. }
  759. }
  760. }
  761. void ECLEngine::addHavingCluse(HPCCSQLTreeWalker * sqlobj, StringBuffer & sb)
  762. {
  763. StringBuffer having;
  764. sqlobj->getHavingClauseString(having);
  765. if (having.length()>0)
  766. {
  767. sb.append("( ").append(having.str()).append(" )");
  768. }
  769. }
  770. bool ECLEngine::appendTranslatedHavingClause(HPCCSQLTreeWalker * sqlobj, StringBuffer & sb, const char * latesDSName)
  771. {
  772. bool success = false;
  773. if (sqlobj)
  774. {
  775. if (sqlobj->hasHavingClause())
  776. {
  777. Owned<IProperties> translator = createProperties(true);
  778. const IArrayOf<SQLTable> * tables = sqlobj->getTableList();
  779. ForEachItemIn(tableidx, *tables)
  780. {
  781. SQLTable table = tables->item(tableidx);
  782. translator->appendProp(table.getName(), "LEFT");
  783. }
  784. ISQLExpression * having = sqlobj->getHavingClause();
  785. StringBuffer havingclause;
  786. having->toECLStringTranslateSource(havingclause, translator, false, true, false, false);
  787. if (havingclause.length() > 0)
  788. {
  789. sb.appendf("%sHaving := HAVING( %s, %s );\n", latesDSName, latesDSName, havingclause.str());
  790. }
  791. success = true;
  792. }
  793. }
  794. return success;
  795. }