/xbird-open/main/src/java/xbird/xquery/optim/FLWORArranger.java

http://xbird.googlecode.com/ · Java · 1010 lines · 695 code · 40 blank · 275 comment · 215 complexity · f4f2ad18d7c094d618aa8c66ce68b7b5 MD5 · raw file

  1. /*
  2. * @(#)$Id: FLWORArranger.java 3619 2008-03-26 07:23:03Z yui $
  3. *
  4. * Copyright 2006-2008 Makoto YUI
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. * Contributors:
  19. * Makoto YUI - initial implementation
  20. */
  21. package xbird.xquery.optim;
  22. import java.util.*;
  23. import xbird.xquery.XQueryException;
  24. import xbird.xquery.expr.XQExpression;
  25. import xbird.xquery.expr.comp.ComparisonOp;
  26. import xbird.xquery.expr.comp.GeneralComp;
  27. import xbird.xquery.expr.cond.IfExpr;
  28. import xbird.xquery.expr.dyna.ContextItemExpr;
  29. import xbird.xquery.expr.flwr.*;
  30. import xbird.xquery.expr.func.DirectFunctionCall;
  31. import xbird.xquery.expr.func.RecursiveCall;
  32. import xbird.xquery.expr.logical.AndExpr;
  33. import xbird.xquery.expr.opt.PathVariable;
  34. import xbird.xquery.expr.opt.TypePromotedExpr;
  35. import xbird.xquery.expr.path.*;
  36. import xbird.xquery.expr.path.PathExpr.CompositePath;
  37. import xbird.xquery.expr.path.axis.SelfStep;
  38. import xbird.xquery.expr.var.*;
  39. import xbird.xquery.expr.var.BindingVariable.*;
  40. import xbird.xquery.func.Function;
  41. import xbird.xquery.func.FunctionSignature;
  42. import xbird.xquery.func.context.Position;
  43. import xbird.xquery.meta.XQueryContext;
  44. import xbird.xquery.misc.TypeUtil;
  45. import xbird.xquery.parser.visitor.AbstractXQueryParserVisitor;
  46. import xbird.xquery.type.Type;
  47. import xbird.xquery.type.Type.Occurrence;
  48. import xbird.xquery.type.xs.*;
  49. /**
  50. *
  51. * <DIV lang="en"></DIV>
  52. * <DIV lang="ja"></DIV>
  53. *
  54. * @author Makoto YUI (yuin405+xbird@gmail.com)
  55. */
  56. public final class FLWORArranger {
  57. private FLWORArranger() {}
  58. public static Map<Binding, XQExpression> getDependentInWhereExpr(final FLWRExpr flwr, final List<XQExpression> nodeps)
  59. throws XQueryException {
  60. final XQExpression whereExpr = flwr.getWhereExpr();
  61. if(whereExpr == null) {
  62. return Collections.<Binding, XQExpression> emptyMap();
  63. }
  64. final List<Binding> clauses = flwr.getClauses();
  65. // try to split 'and' expr
  66. final Map<Binding, XQExpression> dependancies = new IdentityHashMap<Binding, XQExpression>(4);
  67. final DependancyChecker dependancyChecker = new DependancyChecker(clauses);
  68. if(whereExpr instanceof AndExpr) {
  69. final List<XQExpression> andExprs = ((AndExpr) whereExpr).getExpressions();
  70. for(final XQExpression andExpr : andExprs) {
  71. andExpr.visit(dependancyChecker, null);
  72. if(dependancyChecker.hasDependancy()) {
  73. final Binding dep = dependancyChecker.getDependent();
  74. if(dependancies.containsKey(dep)) {
  75. final AndExpr e = new AndExpr(dependancies.get(dep), andExpr);
  76. e.copyLocations(andExpr);
  77. dependancies.put(dep, e);
  78. } else {
  79. dependancies.put(dep, andExpr);
  80. }
  81. } else {
  82. nodeps.add(andExpr);
  83. }
  84. }
  85. } else {
  86. whereExpr.visit(dependancyChecker, null);
  87. if(dependancyChecker.hasDependancy()) {
  88. dependancies.put(dependancyChecker.getDependent(), whereExpr);
  89. } else {
  90. nodeps.add(whereExpr);
  91. }
  92. }
  93. return dependancies;
  94. }
  95. /**
  96. * Apply 'for' bindings normalization.
  97. * <pre>
  98. * <b>[For unnesting]</b>
  99. * Before normalization:
  100. * for $a in /aaa, $b in /bbb, $c in /ccc
  101. * return ($a, $b, $c)
  102. *
  103. * After normalization:
  104. * for $a in /aaa
  105. * return (
  106. * for $b in /bbb
  107. * return (
  108. * for $c in /ccc
  109. * return ($a, $b, $c)
  110. * )
  111. * )
  112. *
  113. * - eg#1 has dependancies
  114. * Before normalization
  115. * for $a in /aaa, $b in /bbb, $c in /ccc
  116. * where $b = $c
  117. * return ($a, $b, $c)
  118. *
  119. * After normalization
  120. * for $a in /aaa
  121. * return (
  122. * for $b in /bbb
  123. * return (
  124. * for $c in /ccc[. = $b]
  125. * return ($a, $b, $c)
  126. * )
  127. * )
  128. *
  129. * - eg#2 no dependancies
  130. * Before normalization
  131. * let $d := $doc/ddd
  132. * for $a in /aaa, $b in /bbb, $c in /ccc
  133. * where $d = 3
  134. * return ($a, $b, $c)
  135. *
  136. * After normalization
  137. * if ($d = 3) then -- pull up to cond
  138. * let $d := $doc/ddd
  139. * for $a in /aaa
  140. * return (
  141. * for $b in /bbb
  142. * return (
  143. * for $c in /ccc
  144. * return ($a, $b, $c)
  145. * )
  146. * )
  147. * else ()
  148. *
  149. * - eg#3 has multiple(in this case 2) dependancies
  150. * Before normalization
  151. * let $d := $doc/ddd
  152. * for $a in /aaa, $b in /bbb, $c in /ccc
  153. * where $d = 3 and $c = $b -- `and` expression is optimized (or is not).
  154. * return ($a, $b, $c)
  155. *
  156. * After normalization
  157. * if ($d = 3) then -- pull up to cond
  158. * let $d := $doc/ddd
  159. * for $a in /aaa
  160. * return (
  161. * for $b in /bbb
  162. * return (
  163. * for $c in /ccc
  164. * where $c = $b -- put to right position
  165. * return ($a, $b, $c)
  166. * )
  167. * )
  168. * else ()
  169. *
  170. * -- eg#4 for + where case(no dep)
  171. * Before normalization
  172. * for $d in $doc/ddd
  173. * where $e = 3
  174. * return $d
  175. *
  176. * After normalization
  177. * if ($e = 3) then
  178. * for $d in $doc/ddd
  179. * return $d
  180. * else ()
  181. * </pre>
  182. */
  183. public static XQExpression applyForNormalization(final FLWRExpr flwr, final Map<Binding, XQExpression> dependancies)
  184. throws XQueryException {
  185. assert (flwr != null);
  186. final List<Binding> clauses = flwr.getClauses();
  187. // apply for unnesting
  188. int size;
  189. for(int i = 0; i < ((size = clauses.size()) - 1); i++) {
  190. final Binding first = clauses.get(i);
  191. if(first.getExpressionType() == Binding.FOR_CLAUSE) {
  192. while((i + 1) < size) { // find next 'for'
  193. final Binding second = clauses.get(i + 1);
  194. if(second.getExpressionType() == Binding.FOR_CLAUSE) { // found nesting
  195. final FLWRExpr parent = new FLWRExpr();
  196. if(dependancies.containsKey(second)) {
  197. final XQExpression old = flwr.getWhereExpr();
  198. if(old != null) {
  199. dependancies.put(first, old);
  200. }
  201. flwr.setWhereExpr(dependancies.remove(second));
  202. } else {
  203. flwr.setWhereExpr(null);
  204. }
  205. final List<OrderSpec> specs = flwr.getOrderSpecs();
  206. if(!specs.isEmpty()) {
  207. parent.setOrderSpecs(specs);
  208. flwr.setOrderSpecs(Collections.<OrderSpec> emptyList());
  209. }
  210. for(int j = 0; j <= i; j++) {
  211. final Binding b = clauses.remove(0);
  212. if(dependancies.containsKey(b)) { // copy depended where clauses
  213. parent.addWhereExpr(dependancies.remove(b));
  214. }
  215. parent.addClause(b); // copy for, let.. to be parent
  216. }
  217. if(size > (i + 2)) {
  218. final XQExpression forUnnested = applyForNormalization(flwr, dependancies);
  219. parent.setReturnExpr(forUnnested);
  220. } else { // inner most
  221. final XQExpression transformed = transform(flwr, dependancies);
  222. parent.setReturnExpr(transformed);
  223. }
  224. return transform(parent, dependancies);
  225. } else {
  226. i++;
  227. }
  228. }
  229. break;
  230. } else {
  231. final FLWRExpr letUnnested = applyLetWhereToIfCondConversion(flwr, dependancies);
  232. if(letUnnested != flwr) {
  233. return transform(letUnnested, dependancies);
  234. }
  235. }
  236. }
  237. if(!dependancies.isEmpty()) {
  238. for(XQExpression expr : dependancies.values()) {
  239. flwr.addWhereExpr(expr);
  240. }
  241. dependancies.clear();
  242. }
  243. return transform(flwr, dependancies);
  244. }
  245. /**
  246. * Convert where clause with If Conditional expression.
  247. * <pre>
  248. * -- eg#1 has relation to binding variable
  249. * Before:
  250. * for $i in /aaa
  251. * where $i/bbb > 0
  252. * return $r
  253. *
  254. * After:
  255. * for $i in /aaa
  256. * return
  257. * if ($i/bbb > 0) then $r
  258. * else ()
  259. * </pre>
  260. */
  261. private static void applyWhereToIfCondConversion(final FLWRExpr flwr) {
  262. if(flwr._filteredReturnExpr == null) {
  263. if(flwr.getWhereExpr() == null) {
  264. flwr._filteredReturnExpr = flwr.getReturnExpr();
  265. } else {
  266. flwr._filteredReturnExpr = new IfExpr(flwr.getWhereExpr(), flwr.getReturnExpr());
  267. flwr.setWhereExpr(null);
  268. }
  269. flwr.setReturnExpr(null);
  270. }
  271. }
  272. /**
  273. * Pull up IF conditional expr that does not have dependancies.
  274. * <pre>
  275. * -- eg#1 no decpendancies
  276. * Before:
  277. * for $i in /aaa/bbb
  278. * return
  279. * if $j = 3 then $r
  280. * else ()
  281. *
  282. * After:
  283. * if ($j = 3) then
  284. * for $i in /aaa/bbb
  285. * return $r
  286. * else ()
  287. * </pre>
  288. * @throws XQueryException
  289. * @depends {@link #applyWhereToIfCondConversion(FLWRExpr)}
  290. */
  291. private static XQExpression applyIfCondPullup(final FLWRExpr flwr) throws XQueryException {
  292. final XQExpression retExpr = flwr.getFilteredReturnExpr();
  293. if(retExpr instanceof IfExpr) {
  294. final DependancyChecker dependancyChecker = new DependancyChecker(flwr.getClauses());
  295. retExpr.visit(dependancyChecker, null);
  296. if(!dependancyChecker.hasDependancy()) {
  297. final IfExpr ifExpr = (IfExpr) retExpr;
  298. flwr._filteredReturnExpr = ifExpr.getThenExpr();
  299. flwr.setReturnExpr(null);
  300. ifExpr.setThenExpr(flwr);
  301. return ifExpr;
  302. }
  303. }
  304. return flwr;
  305. }
  306. /**
  307. * <pre>
  308. * -- eg#1
  309. * Before:
  310. * for $i in /aaa/bbb return $i/ccc
  311. * After:
  312. * /aaa/bbb/ccc
  313. *
  314. * -- eg#2
  315. * Before:
  316. * let $i := /aaa/bbb
  317. * return $i/ccc
  318. * After:
  319. * /aaa/bbb/ccc
  320. *
  321. * -- eg#3
  322. * Before
  323. * for $i in /aaa/bbb return $i
  324. * After
  325. * /aaa/bbb
  326. *
  327. * -- eg#3
  328. * Before
  329. * for $b in a/b/c return $b/d[text()=$b/name]
  330. *
  331. * After
  332. * -- if $b's reference count is greater than 1, could not cut flwr.
  333. * for $b in a/b/c return $b/d[text()=$b/name]
  334. * </pre>
  335. * @throws XQueryException
  336. */
  337. private static XQExpression applyFLWRCutting(final FLWRExpr flwr) throws XQueryException {
  338. if(flwr.getWhereExpr() == null) {
  339. final XQExpression retExpr = flwr.getFilteredReturnExpr();
  340. if(retExpr instanceof DirectFunctionCall && !(retExpr instanceof RecursiveCall)) {
  341. final DirectFunctionCall funcall = (DirectFunctionCall) retExpr;
  342. final List<XQExpression> params = funcall.getParams();
  343. if(params.size() == 1) {
  344. FunctionSignature sig = funcall.getFunction().getFunctionSignature(1);
  345. Type type = sig.getArgumentType(0);
  346. Occurrence occ = type.quantifier();
  347. if(!occ.accepts(Occurrence.OCC_ZERO_OR_MORE.getAlignment())) {
  348. return flwr; //TODO REVIEWME
  349. }
  350. final XQExpression firstArg = params.get(0);
  351. final XQExpression cutted = recApplyFLWRCuttingInternal(flwr, firstArg);
  352. if(cutted != flwr) {
  353. return retExpr;
  354. }
  355. }
  356. } else {
  357. return recApplyFLWRCuttingInternal(flwr, retExpr);
  358. }
  359. }
  360. return flwr;
  361. }
  362. private static XQExpression recApplyFLWRCuttingInternal(final FLWRExpr flwr, final XQExpression retExpr) {
  363. if(retExpr instanceof RelativePath) {//find the front item
  364. final RelativePath pathExpr = ((RelativePath) retExpr);
  365. final XQExpression firstStep = pathExpr.getSteps().get(0);
  366. if(firstStep instanceof VarRef) {
  367. final Variable referent = ((VarRef) firstStep).getValue();
  368. if(referent instanceof BindingVariable) {
  369. final int refcnt = ((BindingVariable) referent).getReferenceCount();
  370. if(refcnt == 1) {
  371. final int csize = flwr.getClauses().size();
  372. if(csize > 0) {
  373. final Binding clause = flwr.getClauses().get(0);
  374. final BindingVariable bindingVar = clause.getVariable();
  375. if(bindingVar == referent) {
  376. final XQExpression bindingExpr = bindingVar.getValue();
  377. pathExpr.setStep(0, bindingExpr);
  378. return pathExpr;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. } else if(retExpr instanceof VarRef) {
  385. final Variable referent = ((VarRef) retExpr).getValue();
  386. if(referent instanceof BindingVariable) {
  387. final int refcnt = ((BindingVariable) referent).getReferenceCount();
  388. if(refcnt == 1) {
  389. for(Binding clause : flwr.getClauses()) {
  390. final BindingVariable bindingVar = clause.getVariable();
  391. if(bindingVar == referent) {
  392. XQExpression bindingExpr = bindingVar.getValue();
  393. Type type = bindingVar.getType();
  394. if(type != Untyped.UNTYPED) {
  395. return new TypePromotedExpr(bindingExpr, type);
  396. } else {
  397. return bindingExpr;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. return flwr;
  405. }
  406. private static XQExpression applyRemoveUnnessaryLetClause(final FLWRExpr flwr) {
  407. assert ((flwr.getReturnExpr() == null) && (flwr.getWhereExpr() == null));
  408. assert (flwr._filteredReturnExpr != null);
  409. final List<Binding> clauses = flwr.getClauses();
  410. int csize = clauses.size();
  411. for(int i = 0; i < csize; i++) {
  412. final Binding binding = clauses.get(i);
  413. if(binding.getExpressionType() == Binding.LET_CLAUSE) {
  414. BindingVariable bv = binding.getVariable();
  415. final int refcnt = bv.getReferenceCount();
  416. if((refcnt == 0) || (refcnt == 1)) {
  417. clauses.remove(i--);
  418. --csize;
  419. }
  420. }
  421. }
  422. final int cleanAfter = clauses.size();
  423. if(cleanAfter == 0) {
  424. return flwr.getFilteredReturnExpr();
  425. }
  426. return flwr;
  427. }
  428. private static XQExpression applyFLWRFlatting(final FLWRExpr flwr) {
  429. if(!flwr.getOrderSpecs().isEmpty()) {
  430. return flwr;
  431. }
  432. final XQExpression ret = flwr.getFilteredReturnExpr();
  433. if(ret == null) {
  434. throw new IllegalStateException();
  435. }
  436. final List<Binding> clauses = flwr.getClauses();
  437. final int clen = clauses.size();
  438. if(clen == 0) {
  439. return ret;
  440. } else if(clen == 1) {
  441. final Binding firstBinding = clauses.get(0);
  442. if(ret instanceof VarRef) {
  443. return applyFLWRFlattingInternal((VarRef) ret, firstBinding, flwr);
  444. } else if(ret instanceof RelativePath) {
  445. final RelativePath path = (RelativePath) ret;
  446. final List<XQExpression> steps = path.getSteps();
  447. final XQExpression firstStep = steps.get(0);
  448. if(firstStep instanceof VarRef) {
  449. final XQExpression cutted = applyFLWRFlattingInternal((VarRef) firstStep, firstBinding, flwr);
  450. if(cutted != flwr) {
  451. steps.set(0, cutted);
  452. return path;
  453. }
  454. }
  455. }
  456. }
  457. return flwr;
  458. }
  459. private static final XQExpression applyFLWRFlattingInternal(final VarRef ret, final Binding firstBinding, final FLWRExpr flwr) {
  460. final Variable referent = ret.getValue();
  461. final BindingVariable bindingVar = firstBinding.getVariable();
  462. if(referent == bindingVar) {
  463. return bindingVar.getValue();
  464. }
  465. final XQExpression bindingExpr = firstBinding.getExpression();
  466. if(referent == bindingExpr) {
  467. return bindingExpr;
  468. }
  469. return flwr;
  470. }
  471. /**
  472. * <pre>
  473. * -- eg#1
  474. * Before
  475. * let $p = /aaa/bbb
  476. * for $i in /ccc
  477. * return
  478. * for $j in /ddd
  479. * return $p
  480. *
  481. * After
  482. * -- $p will be rewritten.
  483. * for $i in /ccc
  484. * return
  485. * for $j in /ddd
  486. * return /aaa/bbb
  487. * </pre>
  488. */
  489. private static void applyConstantLetVariableFolding(final FLWRExpr flwr) {
  490. final List<Binding> clauses = flwr.getClauses();
  491. final int clen = clauses.size();
  492. if(clen > 0) {
  493. final Binding clause = clauses.get(0);
  494. final XQExpression bindingExpr = clause.getVariable().getValue();
  495. if(bindingExpr instanceof RelativePath) {
  496. final RelativePath pathExpr = ((RelativePath) bindingExpr);
  497. final XQExpression firstStep = pathExpr.getSteps().get(0);
  498. if(firstStep instanceof VarRef) {
  499. final Variable referent = ((VarRef) firstStep).getValue();
  500. if(referent instanceof LetVariable) {
  501. final int refcnt = ((LetVariable) referent).getReferenceCount();
  502. if(refcnt == 1) { // varref variable seems to be a constant.
  503. final XQExpression refExpr = referent.getValue();
  504. pathExpr.setStep(0, refExpr);
  505. }
  506. }
  507. }
  508. } else if(bindingExpr instanceof VarRef) {
  509. final Variable referent = ((VarRef) bindingExpr).getValue();
  510. if(referent instanceof LetVariable) {
  511. final int refcnt = ((LetVariable) referent).getReferenceCount();
  512. if(refcnt == 1) {
  513. final XQExpression refExpr = referent.getValue();
  514. final BindingVariable bv = clause.getVariable();
  515. bv.setValue(refExpr);
  516. }
  517. }
  518. }
  519. }
  520. }
  521. /**
  522. * <pre>
  523. * -- eg#1
  524. * Before:
  525. * for $c in /ccc
  526. * where $b = $c
  527. * return ($a, $b, $c)
  528. * After:
  529. * for $c in /ccc[. = $b]
  530. * return ($a, $b, $c)
  531. *
  532. * -- eg#2
  533. * Before:
  534. * let $c := /ccc
  535. * where $b = $c
  536. * return ($a, $b, $c)
  537. * After:
  538. * let $c in /ccc[. = $b]
  539. * return ($a, $b, $c)
  540. *
  541. * -- eg#3
  542. * Before:
  543. * for $x in /aaa/bbb
  544. * where $x/ccc eq "zzz"
  545. * return true
  546. * After:
  547. * for $x in /aaa/bbb[ccc eq "zzz"]
  548. * return true
  549. *
  550. * -- eg#4
  551. * Before normalization
  552. * for $a in /aaa at $pos where $pos = 2 return $a,
  553. * for $a in /aaa where fn:position() = 2 return $a
  554. *
  555. * After normalization
  556. * for $a in /aaa[2] return $a
  557. *
  558. * -- eg#5
  559. * Before:
  560. * for $c in /aaa
  561. * where $d = 3 and $c = $b
  562. * return $a
  563. *
  564. * After:
  565. * if ($d = 3) then
  566. * for $c in /aaa[. = $b]
  567. * return $a
  568. * else ()
  569. * </pre>
  570. * @param dependancies
  571. */
  572. private static void applyWhereToPredicateConversion(final FLWRExpr flwr, final Map<Binding, XQExpression> dependancies)
  573. throws XQueryException {
  574. final XQExpression whereExpr = flwr.getWhereExpr();
  575. if(whereExpr == null) {
  576. return;
  577. }
  578. // first binding entries
  579. final List<Binding> clauses = flwr.getClauses();
  580. final Binding firstBinding = clauses.get(0);
  581. final int firstBindingType = firstBinding.getExpressionType();
  582. final BindingVariable firstBindingVar = firstBinding.getVariable();
  583. XQExpression firstBindingExpr = firstBinding.getExpression();
  584. final List<XQExpression> whereExprs;
  585. if(whereExpr instanceof AndExpr) {
  586. whereExprs = ((AndExpr) whereExpr).flatten();
  587. } else {
  588. whereExprs = new ArrayList<XQExpression>(1);
  589. whereExprs.add(whereExpr);
  590. }
  591. int nWhereSize = whereExprs.size();
  592. for(int oi = 0; oi < nWhereSize; oi++) {
  593. final XQExpression eachExpr = whereExprs.get(oi);
  594. boolean innerModified = false;
  595. if(eachExpr instanceof ComparisonOp) {
  596. final ComparisonOp cmpOpr = (ComparisonOp) eachExpr;
  597. XQExpression replaceVarRef = null;
  598. inner: for(int ii = 0; ii < 2; ii++) {
  599. final XQExpression left = cmpOpr.getLeftOperand();
  600. final XQExpression right = cmpOpr.getRightOperand();
  601. if(left instanceof VarRef) {
  602. final Variable referent = ((VarRef) left).getValue();
  603. if(firstBindingExpr instanceof PathExpr) {
  604. final PathExpr bindingPathExpr = (PathExpr) firstBindingExpr;
  605. if(firstBindingVar == referent) {
  606. cmpOpr.setLeftOperand(new ContextItemExpr(bindingPathExpr.getType()));
  607. bindingPathExpr.addPredicate(cmpOpr);
  608. innerModified = true;
  609. replaceVarRef = firstBindingVar;
  610. break inner;
  611. } else if((firstBindingType == Binding.FOR_CLAUSE)
  612. && (cmpOpr instanceof GeneralComp)) {
  613. final PositionalVariable posVar = ((ForClause) firstBinding).getPositionVariable();
  614. if(posVar == referent) {
  615. bindingPathExpr.addPredicate(right);
  616. innerModified = true;
  617. replaceVarRef = posVar;
  618. break inner;
  619. }
  620. }
  621. }
  622. } else if(left instanceof RelativePath) {
  623. final RelativePath leftPathExpr = ((RelativePath) left);
  624. final List<XQExpression> steps = leftPathExpr.getSteps();
  625. final XQExpression firstStep = steps.get(0);
  626. if(firstStep instanceof VarRef) {
  627. final Variable referent = ((VarRef) firstStep).getValue();
  628. if(firstBindingVar == referent) {
  629. if(firstBindingExpr instanceof VarRef) {
  630. final Variable ref = ((VarRef) firstBindingExpr).getValue();
  631. final XQExpression refval = ref.getValue();
  632. if(refval instanceof PathExpr) {
  633. firstBindingExpr = refval;
  634. }
  635. }
  636. if(firstBindingExpr instanceof PathExpr) {
  637. final PathExpr bindingPathExpr = (PathExpr) firstBindingExpr;
  638. steps.remove(0);
  639. assert (!steps.isEmpty());
  640. bindingPathExpr.addPredicate(cmpOpr);
  641. innerModified = true;
  642. replaceVarRef = firstBindingVar;
  643. break inner;
  644. }
  645. } else if(ii == 1) {// delete an unnessesary where clause
  646. innerModified = true;
  647. break inner;
  648. }
  649. }
  650. } else if(left instanceof DirectFunctionCall) {
  651. final Function func = ((DirectFunctionCall) left).getFunction();
  652. if(func instanceof Position) {
  653. if(firstBindingExpr instanceof PathExpr) {
  654. final PathExpr bindingPathExpr = (PathExpr) firstBindingExpr;
  655. if(TypeUtil.subtypeOf(right.getType(), NumericType.getInstance())) {
  656. bindingPathExpr.addPredicate(right);
  657. } else {
  658. TypePromotedExpr typePromoted = new TypePromotedExpr(right, IntegerType.INTEGER);
  659. bindingPathExpr.addPredicate(typePromoted);
  660. }
  661. innerModified = true;
  662. break inner;
  663. }
  664. }
  665. } else {
  666. if(firstBindingExpr instanceof PathExpr) {
  667. final PathExpr bindingPathExpr = (PathExpr) firstBindingExpr;
  668. final VarRefDetector detector1 = new VarRefDetector(firstBindingVar, true);
  669. detector1.visit(left, null);
  670. if(detector1.isDetected()) {
  671. final VarRefDetector detector2 = new VarRefDetector(firstBindingVar, false);
  672. detector2.visit(right, null);
  673. if(detector2.isJoinDisabled() || detector2.isDetected()) {
  674. break inner;
  675. } else {
  676. bindingPathExpr.addPredicate(cmpOpr);
  677. innerModified = true;
  678. break inner;
  679. }
  680. }
  681. }
  682. }
  683. cmpOpr.switchOperand();
  684. }//inner
  685. if(replaceVarRef != null) {
  686. final PredicateReplacer replacer = new PredicateReplacer(replaceVarRef);
  687. replacer.visit(cmpOpr, null);
  688. }
  689. }//outer
  690. if(innerModified) {
  691. whereExprs.remove(oi--);
  692. --nWhereSize;
  693. }
  694. }
  695. if(nWhereSize == 0) {
  696. flwr.setWhereExpr(null);
  697. } else if(nWhereSize == 1) {
  698. flwr.setWhereExpr(whereExprs.get(0));
  699. } else {
  700. flwr.setWhereExpr(new AndExpr(whereExprs));
  701. }
  702. }
  703. private static XQExpression transform(final FLWRExpr flwr, final Map<Binding, XQExpression> dependancies)
  704. throws XQueryException {
  705. applyWhereToPredicateConversion(flwr, dependancies);
  706. applyConstantLetVariableFolding(flwr);
  707. applyWhereToIfCondConversion(flwr);
  708. XQExpression normedRet = applyIfCondPullup(flwr);
  709. if(normedRet instanceof FLWRExpr) {
  710. normedRet = applyFLWRCutting((FLWRExpr) normedRet);
  711. }
  712. if(normedRet instanceof FLWRExpr) {
  713. normedRet = applyRemoveUnnessaryLetClause((FLWRExpr) normedRet);
  714. }
  715. if(normedRet instanceof FLWRExpr) {
  716. normedRet = applyFLWRFlatting((FLWRExpr) normedRet);
  717. }
  718. return normedRet;
  719. }
  720. /**
  721. * <pre>
  722. * -- eg#1
  723. * Before:
  724. * let $i := 3
  725. * for $j in /aaa/ccc
  726. * where $i > 2
  727. * return $j
  728. *
  729. * After:
  730. * let $i := 3
  731. * return
  732. * if $i > 2 then
  733. * for $j in /aaa/ccc
  734. * return $j
  735. * else ()
  736. * </pre>
  737. */
  738. private static FLWRExpr applyLetWhereToIfCondConversion(final FLWRExpr flwr, final Map<Binding, XQExpression> dependancies)
  739. throws XQueryException {
  740. final List<Binding> clauses = flwr.getClauses();
  741. final int size = clauses.size();
  742. if(size > 0) {
  743. final Binding b = clauses.get(0);
  744. if(b.getExpressionType() == Binding.LET_CLAUSE) {
  745. if(dependancies.containsKey(b)) {
  746. clauses.remove(0);
  747. final FLWRExpr parent = new FLWRExpr();
  748. final List<OrderSpec> specs = flwr.getOrderSpecs();
  749. if(!specs.isEmpty()) {
  750. parent.setOrderSpecs(specs);
  751. flwr.setOrderSpecs(Collections.<OrderSpec> emptyList());
  752. }
  753. parent.addClause(b);
  754. final XQExpression cond = dependancies.remove(b);
  755. final XQExpression then = applyForNormalization(flwr, dependancies);
  756. final IfExpr ifExpr = new IfExpr(cond, then);
  757. parent.setReturnExpr(ifExpr);
  758. return parent;
  759. } else {
  760. final LetVariable lv = ((LetClause) b).getVariable();
  761. final int refcnt = lv.getReferenceCount();
  762. if((refcnt == 0) || (refcnt == 1)) {
  763. clauses.remove(0);
  764. }
  765. }
  766. }
  767. }
  768. return flwr;
  769. }
  770. private static final class VarRefDetector extends AbstractXQueryParserVisitor {
  771. private final BindingVariable _targetVar;
  772. private final int _birthId;
  773. private final boolean _doReplace;
  774. private boolean _detected = false;
  775. private boolean _disableJoin = false;
  776. VarRefDetector(final BindingVariable var, final boolean doReplace) {
  777. super();
  778. this._targetVar = var;
  779. this._birthId = var.getBirthId();
  780. this._doReplace = doReplace;
  781. }
  782. boolean isDetected() {
  783. return _detected;
  784. }
  785. boolean isJoinDisabled() {
  786. return _disableJoin;
  787. }
  788. @Override
  789. public XQExpression visit(final PathExpr path, final XQueryContext ctxt)
  790. throws XQueryException {
  791. if(path instanceof RelativePath) {
  792. final List<XQExpression> steps = path.getSteps();
  793. final int steplen = steps.size();
  794. for(int i = 0; i < steplen; i++) {
  795. final XQExpression step = steps.get(i);
  796. if(step instanceof VarRef) {
  797. final VarRef ref = (VarRef) step;
  798. final Variable referent = ref.getValue();
  799. if(_targetVar == referent) {
  800. if(_doReplace) {
  801. steps.remove(i);
  802. }
  803. _detected = true;
  804. return path;
  805. }
  806. }
  807. step.visit(this, ctxt);
  808. }
  809. }
  810. super.visit(path, ctxt);
  811. return path;
  812. }
  813. @Override
  814. public XQExpression visit(BindingVariable variable, XQueryContext ctxt)
  815. throws XQueryException {
  816. final int targetBirthId = variable.getBirthId();
  817. if(targetBirthId > _birthId) {
  818. this._disableJoin = true;
  819. return variable;
  820. }
  821. return super.visit(variable, ctxt);
  822. }
  823. }
  824. private static final class PredicateReplacer extends AbstractXQueryParserVisitor {
  825. private final XQExpression _target;
  826. PredicateReplacer(final XQExpression target) {
  827. super();
  828. assert (target != null);
  829. _target = target;
  830. }
  831. @Override
  832. public XQExpression visit(final PathVariable variable, final XQueryContext ctxt)
  833. throws XQueryException {
  834. final XQExpression pathExpr = variable.getValue();
  835. if(pathExpr instanceof CompositePath) {
  836. final CompositePath cp = (CompositePath) pathExpr;
  837. final XQExpression srcExpr = cp.getSourceExpr();
  838. assert (srcExpr != null);
  839. final XQExpression filterExpr = cp.getFilterExpr();
  840. assert (filterExpr != null);
  841. srcExpr.visit(this, ctxt);
  842. if(srcExpr == _target) {
  843. variable.setValue(filterExpr);
  844. }
  845. filterExpr.visit(this, ctxt);
  846. }
  847. return variable;
  848. }
  849. @Override
  850. public XQExpression visit(final PathExpr path, final XQueryContext ctxt)
  851. throws XQueryException {
  852. final List<XQExpression> steps = path.getSteps();
  853. int steplen = steps.size();
  854. for(int i = 0; i < steplen; i++) {
  855. final XQExpression s = steps.get(i);
  856. if(s instanceof VarRef) {
  857. final Variable var = ((VarRef) s).getValue();
  858. if(var == _target) {
  859. if(steplen > 1) {
  860. final XQExpression next = steps.get(i + 1);
  861. if(next instanceof StepExpr) {
  862. steps.remove(i);
  863. --steplen;
  864. continue;
  865. }
  866. }
  867. steps.set(i, new SelfStep(NodeTest.ANYNODE));
  868. }
  869. s.visit(this, ctxt);
  870. }
  871. }
  872. return path;
  873. }
  874. }
  875. private static final class DependancyChecker extends AbstractXQueryParserVisitor {
  876. private final List<Binding> _clauses;
  877. private Binding _dependent = null;
  878. private boolean _found = false;
  879. DependancyChecker(final List<Binding> clauses) {
  880. super();
  881. _clauses = clauses;
  882. }
  883. Binding getDependent() {
  884. return _dependent;
  885. }
  886. boolean hasDependancy() {
  887. return _found;
  888. }
  889. @Override
  890. public XQExpression visit(final FLWRExpr expr, final XQueryContext ctxt)
  891. throws XQueryException {
  892. for(final Binding b : expr.getClauses()) {
  893. b.visit(this, ctxt);
  894. }
  895. return expr;
  896. }
  897. @Override
  898. public XQExpression visit(final VarRef ref, final XQueryContext ctxt)
  899. throws XQueryException {
  900. XQExpression referent = ref.getValue();
  901. if(referent instanceof ParametricVariable) {
  902. XQExpression argv = ((ParametricVariable) referent).getValue();
  903. if(argv instanceof TypePromotedExpr) {
  904. argv = ((TypePromotedExpr) argv).getPromotedExpr();
  905. if(argv instanceof VarRef) {
  906. referent = ((VarRef) argv).getValue();
  907. }
  908. }
  909. }
  910. ForClause lastForClause = null;
  911. for(final Binding target : _clauses) {
  912. final int type = target.getExpressionType();
  913. if(type == Binding.FOR_CLAUSE) {
  914. lastForClause = (ForClause) target;
  915. final Variable bvar = lastForClause.getVariable();
  916. final PositionalVariable posVar = lastForClause.getPositionVariable();
  917. if((bvar == referent) || (posVar == referent)) {
  918. if(_found) {
  919. final int posTrgDep = _clauses.indexOf(target);
  920. final int posLastDep = _clauses.indexOf(_dependent); // return -1 with null arg
  921. if(posLastDep > posTrgDep) {
  922. // Considering following case.
  923. // | for $a in /aaa, $b in /bbb, $c in /ccc
  924. // | where $c = $b ..
  925. // | .. ~~~~~~~
  926. continue;
  927. }
  928. }
  929. _found = true;
  930. _dependent = target;
  931. }
  932. } else {
  933. LetClause letClause = (LetClause) target;
  934. final LetVariable lv = letClause.getVariable();
  935. if(lv == referent) {
  936. if(_found) {
  937. final int posTrgDep = _clauses.indexOf(target);
  938. final int posLastDep = _clauses.indexOf(_dependent);
  939. if(posLastDep > posTrgDep) {
  940. continue;
  941. }
  942. }
  943. _found = true;
  944. _dependent = target;
  945. }
  946. }
  947. }
  948. return ref;
  949. }
  950. @Override
  951. public XQExpression visit(final DirectFunctionCall call, final XQueryContext ctxt)
  952. throws XQueryException {
  953. final Function func = (call).getFunction();
  954. if(func instanceof Position) {
  955. for(final Binding target : _clauses) {
  956. final int type = target.getExpressionType();
  957. if(type == Binding.FOR_CLAUSE) {
  958. _dependent = target;
  959. break;
  960. }
  961. }
  962. _found = true;
  963. assert (_dependent != null);
  964. } else {
  965. super.visit(call, ctxt);
  966. }
  967. return call;
  968. }
  969. }
  970. }