hqlalias.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*##############################################################################
  2. HPCC SYSTEMS software 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 "jset.hpp"
  16. #include "hql.hpp"
  17. #include "hqlutil.hpp"
  18. #include "hqltrans.ipp"
  19. #include "hqlalias.hpp"
  20. //---------------------------------------------------------------------------------------------------------------------
  21. static bool isSubsetOf(IHqlExpression * search, IHqlExpression * expr)
  22. {
  23. for (;;)
  24. {
  25. if (search->queryBody() == expr->queryBody())
  26. return true;
  27. if (expr->getOperator() != no_and)
  28. return false;
  29. //Check in order most likely to no recurse too deeply.
  30. if (isSubsetOf(search, expr->queryChild(1)))
  31. return true;
  32. expr = expr->queryChild(0);
  33. }
  34. }
  35. ConditionItem::ConditionItem(IHqlExpression * _expr, ConditionSet * _parent)
  36. : expr(_expr), parent(_parent)
  37. {
  38. }
  39. bool ConditionItem::isAlwaysConditionalOn(IHqlExpression * search) const
  40. {
  41. if (isSubsetOf(search, expr))
  42. return true;
  43. if (!parent)
  44. return false;
  45. return parent->isAlwaysConditionalOn(search);
  46. }
  47. //---------------------------------------------------------------------------------------------------------------------
  48. bool ConditionSet::addOrCondition(IHqlExpression * expr, ConditionSet * parent)
  49. {
  50. if (unconditional)
  51. return false;
  52. if (!expr)
  53. {
  54. setUnconditional();
  55. return true;
  56. }
  57. ForEachItemIn(iMerge, conditions)
  58. {
  59. ConditionItem & cur = conditions.item(iMerge);
  60. if (cur.equals(expr, parent))
  61. return false;
  62. }
  63. conditions.append(*new ConditionItem(expr, parent));
  64. return true;
  65. }
  66. void ConditionSet::setUnconditional()
  67. {
  68. unconditional = true;
  69. conditions.kill();
  70. }
  71. /*
  72. IHqlExpression * ConditionSet::getGuardCondition() const
  73. {
  74. if (unconditional)
  75. return NULL;
  76. HqlExprArray values;
  77. ForEachItemIn(i, conditions)
  78. values.append(*conditions.item(i).getCondition());
  79. OwnedITypeInfo boolType = makeBoolType();
  80. return createBalanced(no_or, boolType, values);
  81. }
  82. */
  83. bool ConditionSet::isAlwaysConditionalOn(IHqlExpression * expr)
  84. {
  85. if (!expr)
  86. return true;
  87. if (unconditional)
  88. return false;
  89. //Cache the previous search value to avoid a potentially exponential search of the caller tree.
  90. if (expr == isAlwaysCache.search)
  91. return isAlwaysCache.value;
  92. bool matches = true;
  93. ForEachItemIn(i, conditions)
  94. {
  95. ConditionItem & cur = conditions.item(i);
  96. if (!cur.isAlwaysConditionalOn(expr))
  97. {
  98. matches = false;
  99. break;
  100. }
  101. }
  102. isAlwaysCache.search.set(expr);
  103. isAlwaysCache.value = matches;
  104. return matches;
  105. }
  106. //---------------------------------------------------------------------------------------------------------------------
  107. void ConditionTracker::pushCondition(IHqlExpression * expr, ConditionSet * parent)
  108. {
  109. assertex(expr);
  110. conditionStack.append(expr);
  111. parentStack.append(parent);
  112. }
  113. void ConditionTracker::popCondition()
  114. {
  115. conditionStack.pop();
  116. parentStack.pop();
  117. }
  118. bool ConditionTracker::addActiveCondition(ConditionSet & conditions)
  119. {
  120. if (conditionStack.ordinality() == 0)
  121. return conditions.addOrCondition(NULL, NULL);
  122. return conditions.addOrCondition(conditionStack.tos(), parentStack.tos());
  123. }
  124. //---------------------------------------------------------------------------------------------------------------------
  125. class NestedIfInfo : public NewTransformInfo
  126. {
  127. public:
  128. NestedIfInfo(IHqlExpression * _original) : NewTransformInfo(_original)
  129. {
  130. isShared = false;
  131. containsIf = false;
  132. conditions = NULL;
  133. }
  134. ~NestedIfInfo() { delete conditions; }
  135. ConditionSet * queryConditions()
  136. {
  137. if (!conditions)
  138. conditions = new ConditionSet;
  139. return conditions;
  140. }
  141. public:
  142. ConditionSet * conditions;
  143. bool isShared;
  144. bool containsIf;
  145. };
  146. //MORE: Could remove dependancy on insideCompound if it was ok to have compound operators scattered through the
  147. // contents of a compound item. Probably would cause few problems, and would make life simpler
  148. class NestedIfTransformer : public NewHqlTransformer
  149. {
  150. public:
  151. NestedIfTransformer();
  152. IHqlExpression * process(IHqlExpression * expr);
  153. bool process(const HqlExprArray & exprs, HqlExprArray & transformed);
  154. protected:
  155. void analyseGatherIfs(IHqlExpression * expr);
  156. void analyseNoteConditions(IHqlExpression * expr);
  157. virtual void analyseExpr(IHqlExpression * expr);
  158. virtual IHqlExpression * createTransformed(IHqlExpression * expr);
  159. virtual ANewTransformInfo * createTransformInfo(IHqlExpression * expr)
  160. {
  161. return new NestedIfInfo(expr);
  162. }
  163. inline NestedIfInfo * queryBodyExtra(IHqlExpression * expr) { return static_cast<NestedIfInfo *>(queryTransformExtra(expr->queryBody())); }
  164. protected:
  165. unsigned numIfs;
  166. ConditionTracker tracker;
  167. };
  168. static HqlTransformerInfo nestedIfTransformerInfo("NestedIfTransformer");
  169. NestedIfTransformer::NestedIfTransformer() : NewHqlTransformer(nestedIfTransformerInfo)
  170. {
  171. numIfs = 0;
  172. }
  173. void NestedIfTransformer::analyseExpr(IHqlExpression * expr)
  174. {
  175. IHqlExpression * body = expr->queryBody();
  176. switch (pass)
  177. {
  178. case 0:
  179. analyseGatherIfs(body);
  180. break;
  181. case 1:
  182. analyseNoteConditions(body);
  183. break;
  184. }
  185. }
  186. void NestedIfTransformer::analyseGatherIfs(IHqlExpression * expr)
  187. {
  188. if (expr->getOperator() == no_if)
  189. numIfs++;
  190. NestedIfInfo * extra = queryBodyExtra(expr);
  191. if (alreadyVisited(expr))
  192. {
  193. extra->isShared = true;
  194. if (extra->containsIf)
  195. numIfs++;
  196. return;
  197. }
  198. unsigned prevIfCount = numIfs;
  199. NewHqlTransformer::analyseExpr(expr);
  200. if (prevIfCount != numIfs)
  201. extra->containsIf = true;
  202. }
  203. void NestedIfTransformer::analyseNoteConditions(IHqlExpression * expr)
  204. {
  205. node_operator op = expr->getOperator();
  206. NestedIfInfo * extra = queryBodyExtra(expr);
  207. if (extra->isShared || (op == no_if))
  208. {
  209. if (!tracker.addActiveCondition(*extra->queryConditions()))
  210. return;
  211. }
  212. if (!extra->containsIf)
  213. return;
  214. if (op == no_if)
  215. {
  216. IHqlExpression * cond = expr->queryChild(0);
  217. OwnedHqlExpr normalCond = getNormalizedCondition(cond);
  218. analyseExpr(cond);
  219. tracker.pushCondition(normalCond, extra->queryConditions());
  220. analyseExpr(expr->queryChild(1));
  221. tracker.popCondition();
  222. IHqlExpression * falseExpr = queryRealChild(expr, 2);
  223. if (falseExpr)
  224. {
  225. OwnedHqlExpr inverseCond = getInverse(normalCond);
  226. tracker.pushCondition(inverseCond, extra->queryConditions());
  227. analyseExpr(falseExpr);
  228. tracker.popCondition();
  229. }
  230. }
  231. else
  232. {
  233. NewHqlTransformer::analyseExpr(expr);
  234. }
  235. }
  236. IHqlExpression * NestedIfTransformer::createTransformed(IHqlExpression * expr)
  237. {
  238. if (expr->getOperator() == no_if)
  239. {
  240. IHqlExpression * cond = expr->queryChild(0);
  241. IHqlExpression * falseExpr = queryRealChild(expr, 2);
  242. OwnedHqlExpr normalCond = getNormalizedCondition(cond);
  243. NestedIfInfo * extra = queryBodyExtra(expr);
  244. IHqlExpression * selected = NULL;
  245. if (extra->queryConditions()->isAlwaysConditionalOn(normalCond))
  246. {
  247. selected = expr->queryChild(1);
  248. }
  249. else if (falseExpr)
  250. {
  251. OwnedHqlExpr inverseCond = getInverse(normalCond);
  252. if (extra->queryConditions()->isAlwaysConditionalOn(inverseCond))
  253. selected = falseExpr;
  254. }
  255. if (selected)
  256. {
  257. const char * branch = (selected == falseExpr) ? "false" : "true";
  258. StringBuffer exprText, locationText;
  259. appendLocation(locationText, queryLocation(expr), ": ");
  260. DBGLOG("%s%s replaced with %s branch since condition always %s", locationText.str(), queryChildNodeTraceText(exprText, expr), branch, branch);
  261. OwnedHqlExpr ret = transform(selected);
  262. return cloneMissingAnnotations(expr, ret);
  263. }
  264. }
  265. return NewHqlTransformer::createTransformed(expr);
  266. }
  267. IHqlExpression * NestedIfTransformer::process(IHqlExpression * expr)
  268. {
  269. analyse(expr, 0);
  270. if (numIfs < 2)
  271. return LINK(expr);
  272. analyse(expr, 1);
  273. return transformRoot(expr);
  274. }
  275. bool NestedIfTransformer::process(const HqlExprArray & exprs, HqlExprArray & transformed)
  276. {
  277. ForEachItemIn(i1, exprs)
  278. analyse(&exprs.item(i1), 0);
  279. if (numIfs < 2)
  280. return false;
  281. ForEachItemIn(i2, exprs)
  282. analyse(&exprs.item(i2), 1);
  283. transformRoot(exprs, transformed);
  284. return true;
  285. }
  286. IHqlExpression * optimizeNestedConditional(IHqlExpression * expr)
  287. {
  288. NestedIfTransformer transformer;
  289. return transformer.process(expr);
  290. }
  291. void optimizeNestedConditional(HqlExprArray & exprs)
  292. {
  293. NestedIfTransformer transformer;
  294. HqlExprArray transformed;
  295. if (transformer.process(exprs, transformed))
  296. exprs.swapWith(transformed);
  297. }