hqlhoist.cpp 29 KB

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