hqlhoist.cpp 28 KB

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