hqlhoist.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /*##############################################################################
  2. Copyright (C) 2012 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "platform.h"
  15. #include "jlib.hpp"
  16. #include "hqlexpr.hpp"
  17. #include "hqlfold.hpp"
  18. #include "hqlhoist.hpp"
  19. #include "hqlutil.hpp"
  20. #include "hqlcpputil.hpp"
  21. bool canSurroundWithAlias(IHqlExpression * expr)
  22. {
  23. switch (expr->getOperator())
  24. {
  25. case no_range:
  26. case no_rangefrom:
  27. case no_rangeto:
  28. case no_rangecommon:
  29. case no_mapto:
  30. case no_recordlist:
  31. case no_transformlist:
  32. case no_rowvalue:
  33. case no_sortlist:
  34. case no_attr_expr:
  35. case no_transform:
  36. case no_newtransform:
  37. return false;
  38. case no_alias_scope:
  39. return canSurroundWithAlias(expr->queryChild(0));
  40. default:
  41. return true;
  42. }
  43. }
  44. //---------------------------------------------------------------------------------------------------------------------
  45. static HqlTransformerInfo externalToInternalResultMapperInfo("ExternalToInternalResultMapper");
  46. class ExternalToInternalResultMapper : public NewHqlTransformer
  47. {
  48. public:
  49. ExternalToInternalResultMapper(IHqlExpression * _graph) : NewHqlTransformer(externalToInternalResultMapperInfo), graph(_graph)
  50. {
  51. }
  52. protected:
  53. virtual IHqlExpression * createTransformed(IHqlExpression * expr)
  54. {
  55. OwnedHqlExpr transformed = NewHqlTransformer::createTransformed(expr);
  56. if (transformed->getOperator() == no_getgraphresult)
  57. {
  58. if (hasOperand(transformed, graph))
  59. return removeProperty(transformed, externalAtom);
  60. }
  61. return transformed.getClear();
  62. }
  63. protected:
  64. IHqlExpression * graph;
  65. };
  66. IHqlExpression * mapExternalToInternalResults(IHqlExpression * expr, IHqlExpression * graph)
  67. {
  68. ExternalToInternalResultMapper mapper(graph);
  69. return mapper.transformRoot(expr);
  70. }
  71. //---------------------------------------------------------------------------------------------------------------------
  72. void CHqlExprMultiGuard::addGuarded(IHqlExpression * original)
  73. {
  74. addGuarded(queryBoolExpr(true), original, false);
  75. }
  76. void CHqlExprMultiGuard::addGuarded(IHqlExpression * cond, IHqlExpression * original, bool guardContainsCandidate)
  77. {
  78. guarded.append(*new CHqlExprGuard(cond, original, guardContainsCandidate));
  79. }
  80. void CHqlExprMultiGuard::combine(CHqlExprMultiGuard & other)
  81. {
  82. //Potentially O(N^2). If there are vast numbers of candidates this could become problematic.
  83. ForEachItemIn(i, other.guarded)
  84. {
  85. CHqlExprGuard & cur = other.guarded.item(i);
  86. combine(cur);
  87. }
  88. }
  89. void CHqlExprMultiGuard::combine(CHqlExprGuard & other)
  90. {
  91. ForEachItemIn(i, guarded)
  92. {
  93. CHqlExprGuard & cur = guarded.item(i);
  94. if (cur.original == other.original)
  95. {
  96. //condition is now (a || b)
  97. OwnedHqlExpr newCond;
  98. if (matchesBoolean(cur.guard, true) || matchesBoolean(other.guard, true))
  99. newCond.set(queryBoolExpr(true));
  100. else
  101. newCond.setown(createBoolExpr(no_or, LINK(cur.guard), LINK(other.guard)));
  102. //MORE: Could sometimes reuse the existing guard if this was created by this node
  103. Owned<CHqlExprGuard> newGuard = new CHqlExprGuard(newCond, cur.original, cur.guardContainsCandidate||other.guardContainsCandidate);
  104. guarded.replace(*newGuard.getClear(), i);
  105. return;
  106. }
  107. }
  108. guarded.append(OLINK(other));
  109. }
  110. void CHqlExprMultiGuard::gatherCandidates(HqlExprCopyArray & candidates) const
  111. {
  112. ForEachItemIn(i, guarded)
  113. {
  114. CHqlExprGuard & cur = guarded.item(i);
  115. if (!candidates.contains(*cur.original))
  116. candidates.append(*cur.original);
  117. }
  118. }
  119. IHqlExpression * CHqlExprMultiGuard::queryGuardCondition(IHqlExpression * original) const
  120. {
  121. ForEachItemIn(i, guarded)
  122. {
  123. CHqlExprGuard & cur = guarded.item(i);
  124. if (cur.original == original)
  125. return cur.guard;
  126. }
  127. return NULL;
  128. }
  129. bool CHqlExprMultiGuard::guardContainsCandidate(IHqlExpression * original) const
  130. {
  131. ForEachItemIn(i, guarded)
  132. {
  133. CHqlExprGuard & cur = guarded.item(i);
  134. if (cur.original == original)
  135. return cur.guardContainsCandidate;
  136. }
  137. return false;
  138. }
  139. void gatherCandidates(HqlExprCopyArray & candidates, CHqlExprMultiGuard * guards)
  140. {
  141. if (guards)
  142. guards->gatherCandidates(candidates);
  143. }
  144. IHqlExpression * queryGuardCondition(CHqlExprMultiGuard * guards, IHqlExpression * original)
  145. {
  146. if (guards)
  147. {
  148. IHqlExpression * condition = guards->queryGuardCondition(original);
  149. if (condition)
  150. return condition;
  151. }
  152. return queryBoolExpr(false);
  153. }
  154. //---------------------------------------------------------------------------------------------------------------------
  155. void ConditionalContextInfo::calcInheritedGuards()
  156. {
  157. if (guards && definitions.ordinality() != 0)
  158. {
  159. Owned<CHqlExprMultiGuard> newGuards = new CHqlExprMultiGuard;
  160. ForEachItemIn(i, guards->guarded)
  161. {
  162. CHqlExprGuard & cur = guards->guarded.item(i);
  163. if (!definitions.contains(*cur.original))
  164. newGuards->guarded.append(OLINK(cur));
  165. }
  166. if (newGuards->guarded.ordinality())
  167. inheritedGuards.setown(newGuards.getClear());
  168. }
  169. else
  170. inheritedGuards.set(guards);
  171. }
  172. bool ConditionalContextInfo::isCandidateThatMoves() const
  173. {
  174. if (!isCandidateExpr)
  175. return false;
  176. if (!changesLocation())
  177. return false;
  178. return true;
  179. }
  180. bool ConditionalContextInfo::usedOnMultiplePaths() const
  181. {
  182. if (extraParents.ordinality() != 0)
  183. return true;
  184. if (firstParent)
  185. return firstParent->usedOnMultiplePaths();
  186. return false;
  187. }
  188. //---------------------------------------------------------------------------------------------------------------------
  189. ConditionalContextTransformer::ConditionalContextTransformer(HqlTransformerInfo & info, bool _alwaysEvaluateGuardedTogether)
  190. : ConditionalHqlTransformer(info, CTFnoteor|CTFnoteand|CTFnotemap|CTFnoteifall),
  191. alwaysEvaluateGuardedTogether(_alwaysEvaluateGuardedTogether)
  192. {
  193. seq = 0;
  194. hasConditionalCandidate = false;
  195. noteManyUnconditionalParents = false;//true;
  196. createRootGraph = false;
  197. rootExpr.setown(createValue(no_null, makeVoidType(), createAttribute(_root_Atom)));
  198. activeParent = NULL; // cannot call queryBodyExtra(rootExpr) since in constructor
  199. }
  200. ANewTransformInfo * ConditionalContextTransformer::createTransformInfo(IHqlExpression * expr)
  201. {
  202. return CREATE_NEWTRANSFORMINFO(ConditionalContextInfo, expr);
  203. }
  204. void ConditionalContextTransformer::analyseExpr(IHqlExpression * expr)
  205. {
  206. switch (pass)
  207. {
  208. case PassFindConditions:
  209. ConditionalHqlTransformer::analyseExpr(expr);
  210. return;
  211. case PassFindParents:
  212. analyseConditionalParents(expr);
  213. return;
  214. case PassGatherGuards:
  215. analyseGatherGuards(expr);
  216. break;
  217. default:
  218. ConditionalHqlTransformer::analyseExpr(expr);
  219. break;
  220. }
  221. }
  222. bool ConditionalContextTransformer::hasSingleConditionalCandidate() const
  223. {
  224. if (candidates.ordinality() != 1)
  225. return false;
  226. ConditionalContextInfo & candidate = candidates.item(0);
  227. if (candidate.isUnconditional())
  228. return false;
  229. return !candidate.usedOnMultiplePaths();
  230. }
  231. // ---- pass 1 --------
  232. void ConditionalContextTransformer::noteCandidate(ConditionalContextInfo * extra)
  233. {
  234. extra->isCandidateExpr = true;
  235. //MORE: If is used unconditionally enough???? then mark as such. (don't mark as unconditional)
  236. if (!extra->isUnconditional())
  237. hasConditionalCandidate = true;
  238. candidates.append(*LINK(extra));
  239. }
  240. void ConditionalContextTransformer::analyseConditionalParents(IHqlExpression * expr)
  241. {
  242. ConditionalContextInfo * extra = queryBodyExtra(expr);
  243. if (extra->seq)
  244. {
  245. extra->addExtraParent(activeParent, noteManyUnconditionalParents);
  246. return;
  247. }
  248. if (!extra->firstAnnotatedExpr && (expr != expr->queryBody()))
  249. extra->firstAnnotatedExpr = expr;
  250. extra->setFirstParent(activeParent);
  251. extra->seq = ++seq;
  252. {
  253. ConditionalContextInfo * savedParent = activeParent;
  254. activeParent = extra;
  255. ConditionalHqlTransformer::analyseExpr(expr);
  256. activeParent = savedParent;
  257. }
  258. }
  259. // ---- pass 1b --------
  260. void ConditionalContextTransformer::addDefinition(ConditionalContextInfo * location, ConditionalContextInfo * candidate)
  261. {
  262. location->definitions.append(*LINK(candidate->original));
  263. candidate->moveTo = location;
  264. if (!insertLocations.contains(*location))
  265. insertLocations.append(*location);
  266. }
  267. void ConditionalContextTransformer::removeDefinition(ConditionalContextInfo * location, ConditionalContextInfo * candidate)
  268. {
  269. location->definitions.zap(*LINK(candidate->original));
  270. candidate->moveTo = NULL;
  271. }
  272. bool ConditionalContextTransformer::findCommonLocations()
  273. {
  274. bool changed = false;
  275. ForEachItemIn(idx, candidates)
  276. {
  277. ConditionalContextInfo& cur = candidates.item(idx);
  278. ConditionalContextInfo * candidateLocation = findCandidateLocation(&cur);
  279. //A null alias location forces an expression to stay where it is
  280. if (candidateLocation)
  281. {
  282. addDefinition(candidateLocation, &cur);
  283. changed = true;
  284. }
  285. }
  286. return changed;
  287. }
  288. //Find a single location to evaluate all the child items in
  289. bool ConditionalContextTransformer::findSingleCommonLocation()
  290. {
  291. ConditionalContextInfo * bestLocation = NULL;
  292. PointerArrayOf<ConditionalContextInfo> matched;
  293. ForEachItemIn(idx, candidates)
  294. {
  295. ConditionalContextInfo& cur = candidates.item(idx);
  296. ConditionalContextInfo * candidateLocation = findCandidateLocation(&cur);
  297. if (candidateLocation)
  298. {
  299. matched.append(&cur);
  300. if (!bestLocation)
  301. bestLocation = candidateLocation;
  302. else
  303. bestLocation = findCommonPath(bestLocation, candidateLocation);
  304. }
  305. }
  306. while (bestLocation)
  307. {
  308. if (canSurroundWithAlias(bestLocation->original))
  309. break;
  310. bestLocation = selectParent(bestLocation);
  311. }
  312. //If best location is NULL then all expressions are marked as staying where they are.
  313. if (!bestLocation)
  314. return false;
  315. ForEachItemIn(i, matched)
  316. addDefinition(bestLocation, matched.item(i));
  317. return true;
  318. }
  319. //Find a single location to evaluate all the child items in
  320. bool ConditionalContextTransformer::associateCandidatesWithRoot()
  321. {
  322. ConditionalContextInfo * rootLocation = queryBodyExtra(rootExpr);
  323. PointerArrayOf<ConditionalContextInfo> matched;
  324. ForEachItemIn(idx, candidates)
  325. {
  326. ConditionalContextInfo& cur = candidates.item(idx);
  327. ConditionalContextInfo * candidateLocation = findCandidateLocation(&cur);
  328. if (candidateLocation)
  329. addDefinition(rootLocation, &cur);
  330. }
  331. return rootLocation->hasDefinitions();
  332. }
  333. //What location in the tree should extra be evaluated?
  334. //If it is unconditional, then if first use is unconditional, eval here, else in the root.
  335. //If this expression is conditional and has a single use,
  336. // then evaluate here or globally - if always done globally.
  337. //if conditional, and multiple uses, then find the common evaluation condition of the parents.
  338. ConditionalContextInfo * ConditionalContextTransformer::calcCommonLocation(ConditionalContextInfo * extra)
  339. {
  340. if (extra->calcedCommonLocation)
  341. return extra->commonLocation;
  342. if (!extra->firstParent)
  343. {
  344. dbgassertex(extra == queryBodyExtra(rootExpr));
  345. return extra;
  346. }
  347. ConditionalContextInfo * commonLocation = extra;
  348. if (extra->isUnconditional() && !alwaysEvaluateGuardedTogether)
  349. {
  350. if (!extra->isFirstUseUnconditional())
  351. commonLocation = queryBodyExtra(rootExpr);
  352. }
  353. else
  354. {
  355. commonLocation = calcCommonLocation(extra->firstParent);
  356. if (extra->hasSharedParent)
  357. {
  358. ForEachItemIn(i, extra->extraParents)
  359. {
  360. ConditionalContextInfo * curParent = extra->extraParents.item(i);
  361. ConditionalContextInfo * nextExtra = calcCommonLocation(curParent);
  362. //MORE: What should be done if some expressions can be guarded, and others are marked as unmovable?
  363. commonLocation = findCommonPath(commonLocation, nextExtra);
  364. if (!commonLocation)
  365. break;
  366. }
  367. }
  368. else
  369. {
  370. if (commonLocation == extra->firstParent)
  371. commonLocation = extra;
  372. }
  373. //MORE commonLocation == NULL if you never want to move this - e.g., if parent is a choose/which/rejected/....
  374. }
  375. extra->calcedCommonLocation = true;
  376. extra->commonLocation = commonLocation;
  377. return commonLocation;
  378. }
  379. ConditionalContextInfo * ConditionalContextTransformer::findCandidateLocation(ConditionalContextInfo * extra)
  380. {
  381. ConditionalContextInfo * best = calcCommonLocation(extra);
  382. loop
  383. {
  384. if (!best)
  385. return NULL;
  386. IHqlExpression * bestLocation = best->original;
  387. if (canSurroundWithAlias(bestLocation))
  388. return best;
  389. best = selectParent(best);
  390. }
  391. }
  392. ConditionalContextInfo * ConditionalContextTransformer::selectParent(ConditionalContextInfo * info)
  393. {
  394. if (info->hasSharedParent)
  395. return calcCommonLocation(info);
  396. return info->firstParent;
  397. }
  398. ConditionalContextInfo * ConditionalContextTransformer::findCommonPath(ConditionalContextInfo * left, ConditionalContextInfo * right)
  399. {
  400. loop
  401. {
  402. if (!left || !right)
  403. return NULL;
  404. if (left == right)
  405. return left;
  406. //Ensure if there is a child and a parent that the child's parent is selected
  407. if (left->seq > right->seq)
  408. left = selectParent(left);
  409. else
  410. right = selectParent(right);
  411. }
  412. }
  413. // ---- pass 2 --------
  414. //Recursively walk the tree calculating guards, and gathering a list of guards for child expressions
  415. //Must be called after the common locations have been calculated
  416. void ConditionalContextTransformer::analyseGatherGuards(IHqlExpression * expr)
  417. {
  418. ConditionalContextInfo * extra = queryBodyExtra(expr);
  419. if (!alreadyVisited(extra))
  420. {
  421. unsigned prevGuards = childGuards.ordinality();
  422. doAnalyseExpr(expr);
  423. extra->guards.setown(calcGuard(extra, prevGuards));
  424. extra->calcInheritedGuards();
  425. childGuards.trunc(prevGuards);
  426. }
  427. if (extra->inheritedGuards)
  428. childGuards.append(*extra->inheritedGuards);
  429. }
  430. //Calculate guards for all candidates - even those that will be defined here.
  431. CHqlExprMultiGuard * ConditionalContextTransformer::calcGuard(ConditionalContextInfo * cur, unsigned firstGuard)
  432. {
  433. bool needToAddCandidate = cur->isCandidateThatMoves();
  434. unsigned maxGuard = childGuards.ordinality();
  435. if ((firstGuard == maxGuard) && !needToAddCandidate)
  436. return NULL;
  437. IHqlExpression * original = cur->original;
  438. node_operator op = original->getOperator();
  439. switch (op)
  440. {
  441. case no_if:
  442. return createIfGuard(cur);
  443. case no_case:
  444. case no_map:
  445. return createCaseMapGuard(cur, op);
  446. case no_or:
  447. case no_and:
  448. return createAndOrGuard(cur);
  449. case no_which:
  450. case no_rejected:
  451. case no_choose:
  452. //MORE!!!
  453. break;
  454. }
  455. //Get the union of all guards present for the child expressions
  456. if ((firstGuard == maxGuard-1) && !needToAddCandidate)
  457. return LINK(&childGuards.item(firstGuard));
  458. Owned<CHqlExprMultiGuard> newGuard = new CHqlExprMultiGuard;
  459. for (unsigned i=firstGuard; i < maxGuard; i++)
  460. newGuard->combine(childGuards.item(i));
  461. if (needToAddCandidate)
  462. newGuard->addGuarded(original);
  463. return newGuard.getClear();
  464. }
  465. CHqlExprMultiGuard * ConditionalContextTransformer::createIfGuard(IHqlExpression * ifCond, CHqlExprMultiGuard * condGuard, CHqlExprMultiGuard * trueGuard, CHqlExprMultiGuard * falseGuard)
  466. {
  467. //If you want to common up the conditions between the child query and the parent code you might achieve
  468. //it by forcing the condition into an alias. E.g.,
  469. //queryBodyExtra(ifCond)->createAlias = true;
  470. Owned<CHqlExprMultiGuard> newGuards = new CHqlExprMultiGuard;
  471. HqlExprCopyArray candidates;
  472. gatherCandidates(candidates, trueGuard);
  473. gatherCandidates(candidates, falseGuard);
  474. ForEachItemIn(i, candidates)
  475. {
  476. IHqlExpression * candidate = &candidates.item(i);
  477. IHqlExpression * trueCond = queryGuardCondition(trueGuard, candidate);
  478. IHqlExpression * falseCond = queryGuardCondition(falseGuard, candidate);
  479. OwnedHqlExpr newCond;
  480. if (trueCond != falseCond)
  481. newCond.setown(createValue(no_if, makeBoolType(), LINK(ifCond), LINK(trueCond), LINK(falseCond)));
  482. else
  483. newCond.set(trueCond);
  484. newGuards->addGuarded(newCond, candidate, condGuard != NULL);
  485. }
  486. if (condGuard)
  487. newGuards->combine(*condGuard);
  488. return newGuards.getClear();
  489. }
  490. CHqlExprMultiGuard * ConditionalContextTransformer::createIfGuard(ConditionalContextInfo * cur)
  491. {
  492. IHqlExpression * original = cur->original;
  493. IHqlExpression * ifCond = original->queryChild(0);
  494. CHqlExprMultiGuard * condGuard = queryGuards(ifCond);
  495. CHqlExprMultiGuard * trueGuard = queryGuards(original->queryChild(1));
  496. CHqlExprMultiGuard * falseGuard = queryGuards(original->queryChild(2));
  497. if (!trueGuard && !falseGuard && !cur->isCandidateThatMoves())
  498. return LINK(condGuard);
  499. Owned<CHqlExprMultiGuard> newGuards = createIfGuard(ifCond, condGuard, trueGuard, falseGuard);
  500. if (cur->isCandidateThatMoves())
  501. newGuards->addGuarded(original);
  502. return newGuards.getClear();
  503. }
  504. CHqlExprMultiGuard * ConditionalContextTransformer::createCaseMapGuard(ConditionalContextInfo * cur, node_operator op)
  505. {
  506. IHqlExpression * original = cur->original;
  507. IHqlExpression * testExpr = (op == no_case) ? original->queryChild(0) : NULL;
  508. //MAP(k1=>v1,k2=>v2...,vn) is the same as IF(k1,v1,IF(k2,v2,...vn))
  509. //So walk the MAP operator in reverse applying the guard conditions.
  510. //Use queryLastNonAttribute() instead of max-1 to eventually cope with attributes
  511. IHqlExpression * defaultExpr = queryLastNonAttribute(original);
  512. Linked<CHqlExprMultiGuard> prevGuard = queryGuards(defaultExpr);
  513. bool createdNewGuard = false;
  514. unsigned first = (op == no_case) ? 1 : 0;
  515. unsigned max = original->numChildren();
  516. for (unsigned i= max-1; i-- != first; )
  517. {
  518. IHqlExpression * mapto = original->queryChild(i);
  519. //In the unlikely event there are attributes this ensures only maps are processed
  520. if (mapto->getOperator() != no_mapto)
  521. continue;
  522. IHqlExpression * testValue = mapto->queryChild(0);
  523. CHqlExprMultiGuard * condGuard = queryGuards(testValue);
  524. CHqlExprMultiGuard * trueGuard = queryGuards(mapto->queryChild(1));
  525. if (trueGuard || prevGuard)
  526. {
  527. OwnedHqlExpr ifCond = testExpr ? createBoolExpr(no_eq, LINK(testExpr), LINK(testValue)) : LINK(testValue);
  528. Owned<CHqlExprMultiGuard> newGuards = createIfGuard(ifCond, condGuard, trueGuard, prevGuard);
  529. prevGuard.setown(newGuards.getClear());
  530. createdNewGuard = true;
  531. }
  532. else
  533. {
  534. prevGuard.set(condGuard);
  535. assertex(!createdNewGuard);
  536. }
  537. }
  538. CHqlExprMultiGuard * testGuards = testExpr ? queryGuards(testExpr) : NULL;
  539. if (testGuards || cur->isCandidateThatMoves())
  540. {
  541. if (!createdNewGuard)
  542. prevGuard.setown(new CHqlExprMultiGuard(prevGuard));
  543. if (testGuards)
  544. prevGuard->combine(*testGuards);
  545. if (cur->isCandidateThatMoves())
  546. prevGuard->addGuarded(original);
  547. }
  548. return prevGuard.getClear();
  549. }
  550. CHqlExprMultiGuard * ConditionalContextTransformer::createAndOrGuard(ConditionalContextInfo * cur)
  551. {
  552. IHqlExpression * original = cur->original;
  553. IHqlExpression * left = original->queryChild(0);
  554. CHqlExprMultiGuard * leftGuard = queryGuards(left);
  555. CHqlExprMultiGuard * rightGuard = queryGuards(original->queryChild(1));
  556. if (!rightGuard && !cur->isCandidateThatMoves())
  557. return LINK(leftGuard);
  558. node_operator op = original->getOperator();
  559. Owned<CHqlExprMultiGuard> newGuards = new CHqlExprMultiGuard;
  560. ForEachItemIn(i, rightGuard->guarded)
  561. {
  562. CHqlExprGuard & cur = rightGuard->guarded.item(i);
  563. OwnedHqlExpr cond;
  564. //(a || b): b is evaluated if a is false. => guard'(b,x) = !a && guard(b,x)
  565. //(a && b): b is evaluated if a is true. => guard'(b,x) = a && guard(b,x)
  566. if (op == no_or)
  567. cond.setown(createBoolExpr(no_and, getInverse(left), LINK(cur.guard)));
  568. else
  569. cond.setown(createBoolExpr(no_and, LINK(left), LINK(cur.guard)));
  570. newGuards->addGuarded(cond, cur.original, leftGuard != NULL);
  571. }
  572. //MORE: Is the reversal of the order going to matter? E.g., instead of guard(a,x) || (a && guard(b,x))?
  573. if (leftGuard)
  574. newGuards->combine(*leftGuard);
  575. if (cur->isCandidateThatMoves())
  576. newGuards->addGuarded(cur->original);
  577. return newGuards.getClear();
  578. }
  579. CHqlExprMultiGuard * ConditionalContextTransformer::queryGuards(IHqlExpression * expr)
  580. {
  581. if (!expr)
  582. return NULL;
  583. return queryBodyExtra(expr)->guards;
  584. }
  585. // ---- pass 3 --------
  586. // ---- summary --------
  587. bool ConditionalContextTransformer::hasUnconditionalCandidate() const
  588. {
  589. ForEachItemIn(i, candidates)
  590. {
  591. if (candidates.item(i).isUnconditional())
  592. return true;
  593. }
  594. return false;
  595. }
  596. bool ConditionalContextTransformer::analyseNeedsTransform(const HqlExprArray & exprs)
  597. {
  598. ConditionalContextInfo * rootInfo = queryBodyExtra(rootExpr);
  599. activeParent = rootInfo;
  600. rootInfo->setFirstUnconditional();
  601. // Spot conditional and unconditional expressions
  602. analyseArray(exprs, PassFindConditions);
  603. analyseArray(exprs, PassFindCandidates);
  604. if (candidates.ordinality() == 0)
  605. return false;
  606. // Tag expressions with their parent locations, and gather a list of candidates.
  607. analyseArray(exprs, PassFindParents);
  608. // Work out the locations conditional expressions need to be evaluated in
  609. if (createRootGraph)
  610. associateCandidatesWithRoot();
  611. else if (!alwaysEvaluateGuardedTogether)
  612. findCommonLocations();
  613. else
  614. findSingleCommonLocation();
  615. if (hasConditionalCandidate)
  616. {
  617. // Calculate the guard conditions for each condition
  618. analyseArray(exprs, PassGatherGuards);
  619. rootInfo->guards.setown(calcGuard(rootInfo, 0));
  620. childGuards.kill();
  621. // Any guard conditions that are always true should become unconditional if context is unconditional
  622. analyseArray(exprs, 3);
  623. }
  624. return hasConditionalCandidate || alwaysEvaluateGuardedTogether;
  625. }
  626. // --------------------------------------------------------------------------------------------------------------------
  627. static IHqlExpression * createConditionalExpr(IHqlExpression * condition, IHqlExpression * expr, IHqlExpression * elseExpr)
  628. {
  629. if (matchesBoolean(condition, true))
  630. return LINK(expr);
  631. if (matchesBoolean(condition, false))
  632. return LINK(elseExpr);
  633. //MORE: This code is currently disabled.
  634. //The intent is to ensure that in the condition IF (a || b) that b is only evaluated if a is false.
  635. //However there are complications and problems with nested guard conditions which need independently solving.
  636. if (false)
  637. {
  638. switch (condition->getOperator())
  639. {
  640. case no_and:
  641. if (expr->queryRecord())
  642. {
  643. //Use dataset if operators to ensure that the conditions are sortcircuited.
  644. // if (a && b, c, -) => if (a, if (b, c, -), -)
  645. OwnedHqlExpr second = createConditionalExpr(condition->queryChild(1), expr, elseExpr);
  646. return createConditionalExpr(condition->queryChild(0), second, elseExpr);
  647. }
  648. break;
  649. case no_or:
  650. if (expr->queryRecord())
  651. {
  652. // if (a || b, c, -) => if (a, c, if (b, c, -))
  653. OwnedHqlExpr second = createConditionalExpr(condition->queryChild(1), expr, elseExpr);
  654. return createConditionalExpr(condition->queryChild(0), expr, second);
  655. }
  656. break;
  657. case no_if:
  658. {
  659. OwnedHqlExpr trueExpr = createConditionalExpr(condition->queryChild(1), expr, elseExpr);
  660. OwnedHqlExpr falseExpr = createConditionalExpr(condition->queryChild(2), expr, elseExpr);
  661. return createIf(LINK(condition), trueExpr.getClear(), falseExpr.getClear());
  662. }
  663. }
  664. }
  665. return createIf(LINK(condition), LINK(expr), LINK(elseExpr));
  666. }
  667. IHqlExpression * ConditionalContextTransformer::getGuardCondition(ConditionalContextInfo * extra, IHqlExpression * expr)
  668. {
  669. CHqlExprMultiGuard * guards = extra->guards;
  670. if (guards)
  671. {
  672. if (expr != extra->original)
  673. return foldHqlExpression(guards->queryGuardCondition(expr));
  674. }
  675. return LINK(queryBoolExpr(true));
  676. }
  677. IHqlExpression * ConditionalContextTransformer::createGuardedDefinition(ConditionalContextInfo * extra, IHqlExpression * expr)
  678. {
  679. OwnedHqlExpr guard = getGuardCondition(extra, expr);
  680. assertex(guard);
  681. return createGuardedDefinition(extra, expr, guard);
  682. }
  683. IHqlExpression * ConditionalContextTransformer::createGuardedDefinition(ConditionalContextInfo * extra, IHqlExpression * expr, IHqlExpression * condition)
  684. {
  685. if (matchesBoolean(condition, true))
  686. return LINK(expr);
  687. OwnedHqlExpr guard = transform(condition);
  688. OwnedHqlExpr defaultExpr = createNullExpr(expr);
  689. return createConditionalExpr(guard, expr, defaultExpr);
  690. }
  691. // --------------------------------------------------------------------------------------------------------------------
  692. IHqlExpression * ConditionalContextTransformer::createTransformed(IHqlExpression * expr)
  693. {
  694. OwnedHqlExpr transformed = ConditionalHqlTransformer::createTransformed(expr);
  695. updateOrphanedSelectors(transformed, expr);
  696. switch (transformed->getOperator())
  697. {
  698. case no_compound_indexread:
  699. case no_compound_indexcount:
  700. case no_compound_indexaggregate:
  701. case no_compound_indexgroupaggregate:
  702. case no_compound_indexnormalize:
  703. if (!queryPhysicalRootTable(transformed))
  704. return LINK(transformed->queryChild(0));
  705. break;
  706. }
  707. if (queryBodyExtra(expr)->createAlias)
  708. {
  709. if (expr == expr->queryBody(true))
  710. transformed.setown(createAlias(transformed, NULL));
  711. }
  712. return transformed.getClear();
  713. }
  714. void ConditionalContextTransformer::transformAllCandidates()
  715. {
  716. //Process definitions in the order they were found - since this is a toplogical dependency ordering
  717. ForEachItemIn(i, candidates)
  718. transformCandidate(&candidates.item(i));
  719. }
  720. void ConditionalContextTransformer::transformAll(HqlExprArray & exprs, bool forceRootFirst)
  721. {
  722. transformAllCandidates();
  723. OwnedHqlExpr rootDefinitions = createDefinitions(queryBodyExtra(rootExpr));
  724. HqlExprArray transformed;
  725. if (rootDefinitions && forceRootFirst)
  726. transformed.append(*rootDefinitions.getClear());
  727. ForEachItemIn(i, exprs)
  728. {
  729. IHqlExpression * cur = &exprs.item(i);
  730. OwnedHqlExpr mapped = transformRoot(cur);
  731. //Add the child query etc. before the first expression that changes.
  732. if (rootDefinitions && (mapped != cur))
  733. transformed.append(*rootDefinitions.getClear());
  734. transformed.append(*mapped.getClear());
  735. }
  736. assertex(!rootDefinitions);
  737. exprs.swapWith(transformed);
  738. }