1 /*
2 * Copyright 2006-2008 The FLWOR Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "stdafx.h"
17
18 #include "compiler/api/compilercb.h"
19 #include "compiler/rewriter/rules/ruleset.h"
20 #include "compiler/expression/flwor_expr.h"
21 #include "compiler/expression/script_exprs.h"
22 #include "compiler/expression/expr.h"
23 #include "compiler/rewriter/tools/expr_tools.h"
24 #include "compiler/xqddf/value_index.h"
25 #include "compiler/expression/expr_iter.h"
26
27 #include "functions/function.h"
28 #include "functions/library.h"
29
30 #include "types/typeops.h"
31
32 #include "system/properties.h"
33
34 #include "diagnostics/assert.h"
35
36
37 namespace zorba
38 {
39
40 struct PredicateInfo;
41
42 static bool isIndexJoinPredicate(RewriterContext&, PredicateInfo&);
43
44 static void rewriteJoin(RewriterContext&, PredicateInfo&, bool&);
45
46 static var_expr* findForVar(static_context*, RewriterContext&, const expr*, ulong&);
47
48 static bool checkVarDependency(RewriterContext&, expr*, ulong);
49
50 static bool expandVars(RewriterContext&, expr*, ulong, long&);
51
52 static bool findFlworForVar(RewriterContext&, const var_expr*, flwor_expr*&, ulong&, ulong&);
53
54
55 struct PredicateInfo
56 {
57 flwor_expr * theFlworExpr;
58 expr * thePredicate;
59 expr * theOuterOp;
60 var_expr * theOuterVar;
61 ulong theOuterVarId;
62 expr * theInnerOp;
63 var_expr * theInnerVar;
64 bool theIsGeneral;
65 };
66
67
68 /*******************************************************************************
69 This rule analyzes the where clause of flwor exprs to deterimne whether any
70 predicate in the clause is a join predicate and whether the associated join
71 can be converted into a hashjoin using an index that is built on-th-fly.
72 ********************************************************************************/
apply(RewriterContext & rCtx,expr * node,bool & modified)73 expr* IndexJoinRule::apply(RewriterContext& rCtx, expr* node, bool& modified)
74 {
75 flwor_expr* flworExpr = NULL;
76
77 if (node->get_expr_kind() == gflwor_expr_kind)
78 return node;
79
80 if (node->get_expr_kind() == trycatch_expr_kind)
81 {
82 rCtx.theFlworStack.push_back(node);
83 }
84 else if (node->get_expr_kind() == flwor_expr_kind)
85 {
86 flworExpr = static_cast<flwor_expr *>(node);
87
88 // Push the flwor expr into the stack
89 rCtx.theFlworStack.push_back(flworExpr);
90 rCtx.theInReturnClause.push_back(false);
91
92 expr* whereExpr = flworExpr->get_where();
93
94 if (whereExpr == NULL)
95 goto drilldown;
96
97 PredicateInfo predInfo;
98 predInfo.theFlworExpr = flworExpr;
99 predInfo.thePredicate = whereExpr;
100
101 if (isIndexJoinPredicate(rCtx, predInfo))
102 {
103 rewriteJoin(rCtx, predInfo, modified);
104
105 if (modified)
106 {
107 predInfo.theFlworExpr->remove_where_clause();
108
109 expr* e = rCtx.theFlworStack.back();
110
111 ZORBA_ASSERT(e == node || e->get_expr_kind() == block_expr_kind);
112
113 rCtx.theFlworStack.pop_back();
114 rCtx.theInReturnClause.pop_back();
115 return e;
116 }
117 }
118 else if (whereExpr->get_function_kind() == FunctionConsts::OP_AND_N)
119 {
120 // TODO: consider multi-key indices
121 ExprIterator iter(whereExpr);
122 while (!iter.done())
123 {
124 PredicateInfo predInfo;
125 predInfo.theFlworExpr = flworExpr;
126 predInfo.thePredicate = (**iter);
127
128 if (isIndexJoinPredicate(rCtx, predInfo))
129 {
130 rewriteJoin(rCtx, predInfo, modified);
131
132 if (modified)
133 {
134 expr* trueExpr = rCtx.theEM->create_const_expr(flworExpr->get_sctx(),
135 flworExpr->get_loc(),
136 true);
137 (**iter) = trueExpr;
138
139 expr* e = rCtx.theFlworStack.back();
140
141 ZORBA_ASSERT(e == node || e->get_expr_kind() == block_expr_kind);
142
143 rCtx.theFlworStack.pop_back();
144 rCtx.theInReturnClause.pop_back();
145 return e;
146 }
147 }
148
149 iter.next();
150 }
151 }
152 }
153
154 drilldown:
155
156 ExprIterator iter(node);
157 while (!iter.done())
158 {
159 expr* currChild = **iter;
160
161 if (flworExpr != NULL && currChild == flworExpr->get_return_expr())
162 rCtx.theInReturnClause.back() = true;
163
164 expr* newChild = apply(rCtx, currChild, modified);
165
166 if (currChild != newChild)
167 {
168 assert(modified);
169 **iter = newChild;
170 }
171
172 if (modified)
173 break;
174
175 iter.next();
176 }
177
178 if (node->get_expr_kind() == trycatch_expr_kind)
179 {
180 rCtx.theFlworStack.pop_back();
181 return node;
182 }
183 else if (node->get_expr_kind() == flwor_expr_kind)
184 {
185 expr* e = rCtx.theFlworStack.back();
186
187 ZORBA_ASSERT(e == node || e->get_expr_kind() == block_expr_kind);
188
189 rCtx.theFlworStack.pop_back();
190 rCtx.theInReturnClause.pop_back();
191 return e;
192 }
193 else
194 {
195 return node;
196 }
197 }
198
199
200 /*******************************************************************************
201 Check whether the given predicate is a join predicate that can be converted
202 to a hashjoin.
203 ********************************************************************************/
isIndexJoinPredicate(RewriterContext & rCtx,PredicateInfo & predInfo)204 static bool isIndexJoinPredicate(RewriterContext& rCtx, PredicateInfo& predInfo)
205 {
206 const fo_expr* foExpr = NULL;
207 const function* fn;
208 const expr* predExpr = predInfo.thePredicate;
209
210 // skip fn:boolean() wrapper
211 while (true)
212 {
213 if (predExpr->get_expr_kind() != fo_expr_kind)
214 return false;
215
216 foExpr = static_cast<const fo_expr*>(predExpr);
217 fn = foExpr->get_func();
218
219 if (fn->getKind() == FunctionConsts::FN_BOOLEAN_1)
220 {
221 predExpr = foExpr->get_arg(0);
222 continue;
223 }
224
225 break;
226 }
227
228 CompareConsts::CompareType opKind = fn->comparisonKind();
229
230 if (opKind != CompareConsts::VALUE_EQUAL && opKind != CompareConsts::GENERAL_EQUAL)
231 return false;
232
233 static_context* sctx = predExpr->get_sctx();
234
235 predInfo.theIsGeneral = (opKind == CompareConsts::GENERAL_EQUAL);
236
237 expr* op1 = foExpr->get_arg(0);
238 expr* op2 = foExpr->get_arg(1);
239
240 // Analyze each operand of the eq to see if it depends on a single for
241 // variable. If that is not true, we reject this predicate.
242 ulong var1id;
243 var_expr* var1 = findForVar(sctx, rCtx, op1, var1id);
244 if (var1 == NULL)
245 return false;
246
247 ulong var2id;
248 var_expr* var2 = findForVar(sctx, rCtx, op2, var2id);
249 if (var2 == NULL)
250 return false;
251
252 if (var1 == var2)
253 return false;
254
255 // Determine the outer and inner side of the join
256 ulong outerVarId;
257
258 if (var1id < var2id)
259 {
260 predInfo.theOuterOp = op1;
261 predInfo.theOuterVar = var1;
262 predInfo.theOuterVarId = var1id;
263 predInfo.theInnerOp = op2;
264 predInfo.theInnerVar = var2;
265 outerVarId = var1id;
266 }
267 else
268 {
269 predInfo.theOuterOp = op2;
270 predInfo.theOuterVar = var2;
271 predInfo.theOuterVarId = var2id;
272 predInfo.theInnerOp = op1;
273 predInfo.theInnerVar = var1;
274 outerVarId = var2id;
275 }
276
277 TypeManager* tm = sctx->get_typemanager();
278 RootTypeManager& rtm = GENV_TYPESYSTEM;
279
280 // The domain of the outer var must contain more than 1 item.
281 xqtref_t outerDomainType = predInfo.theOuterVar->get_domain_expr()->get_return_type();
282
283 if (outerDomainType->max_card() < 2)
284 return false;
285
286 // The expr that defines the inner var must not depend on the outer var.
287 expr* innerDomainExpr = predInfo.theInnerVar->get_for_clause()->get_expr();
288 if (checkVarDependency(rCtx, innerDomainExpr, outerVarId))
289 return false;
290
291 // The predicate must be in the same flwor that defines the inner var (this
292 // way we can be sure that the pred acts as a filter over the inner var).
293 if (predInfo.theFlworExpr->defines_variable(predInfo.theInnerVar) < 0)
294 return false;
295
296 // Type checks
297 xqtref_t outerType = predInfo.theOuterOp->get_return_type();
298 xqtref_t innerType = predInfo.theInnerOp->get_return_type();
299 xqtref_t primeOuterType = TypeOps::prime_type(tm, *outerType);
300 xqtref_t primeInnerType = TypeOps::prime_type(tm, *innerType);
301 TypeConstants::quantifier_t outerQuant = outerType->get_quantifier();
302 TypeConstants::quantifier_t innerQuant = innerType->get_quantifier();
303 const QueryLoc& innerLoc = predInfo.theInnerOp->get_loc();
304 const QueryLoc& outerLoc = predInfo.theOuterOp->get_loc();
305
306 if (!predInfo.theIsGeneral)
307 {
308 // Normally, other rewrite rules should have added the necessary casting
309 // to the eq operands so that their static types have quantifiers ONE
310 // or QUESTION and the associated prime types are not xs:untypedAtomic.
311 // But just in case those rules have been disabled, we check again here
312 // and reject the hashjoin rewrite if these condition are violated.
313
314 if (innerQuant != TypeConstants::QUANT_ONE &&
315 innerQuant != TypeConstants::QUANT_QUESTION)
316 return false;
317
318 if (outerQuant != TypeConstants::QUANT_ONE &&
319 outerQuant != TypeConstants::QUANT_QUESTION)
320 return false;
321
322 // The type of the outer/inner operands in the join predicate must not be
323 // xs:untypedAtomic or xs:anyAtomic.
324 if (TypeOps::is_equal(tm, *primeOuterType, *rtm.UNTYPED_ATOMIC_TYPE_ONE, outerLoc) ||
325 TypeOps::is_equal(tm, *primeInnerType, *rtm.UNTYPED_ATOMIC_TYPE_ONE, innerLoc))
326 return false;
327
328 if (TypeOps::is_equal(tm, *primeOuterType, *rtm.ANY_ATOMIC_TYPE_ONE, outerLoc) ||
329 TypeOps::is_equal(tm, *primeInnerType, *rtm.ANY_ATOMIC_TYPE_ONE, innerLoc))
330 return false;
331
332 // The prime type of the outer operand in the join predicate must be a
333 // subtype of the prime type of the inner operand.
334 if (!TypeOps::is_subtype(tm, *primeOuterType, *primeInnerType, outerLoc))
335 return false;
336 }
337 else
338 {
339 // TODO: allow domain exprs that return atomic items?
340 if (! TypeOps::is_subtype(tm,
341 *innerDomainExpr->get_return_type(),
342 *rtm.ANY_NODE_TYPE_STAR,
343 innerDomainExpr->get_loc()))
344 return false;
345
346 if (innerDomainExpr->getProducesDistinctNodes() != ANNOTATION_TRUE &&
347 innerDomainExpr->getProducesDistinctNodes() != ANNOTATION_TRUE_FIXED)
348 return false;
349 }
350
351 return true;
352 }
353
354
355 /*******************************************************************************
356 Check if "curExpr" references a single var and that var is a FOR var. If so,
357 return that FOR var and its prefix id; otherwise return NULL.
358 ********************************************************************************/
findForVar(static_context * sctx,RewriterContext & rCtx,const expr * curExpr,ulong & varid)359 static var_expr* findForVar(
360 static_context* sctx,
361 RewriterContext& rCtx,
362 const expr* curExpr,
363 ulong& varid)
364 {
365 var_expr* var = NULL;
366
367 while (true)
368 {
369 std::vector<ulong> varidSet;
370
371 const DynamicBitset& bitset = (*rCtx.theExprVarsMap)[curExpr];
372
373 bitset.getSet(varidSet);
374
375 if (varidSet.size() != 1)
376 return NULL;
377
378 varid = varidSet[0];
379 var = (*rCtx.theIdVarMap)[varid];
380
381 if (var->get_kind() == var_expr::for_var)
382 {
383 curExpr = var->get_domain_expr();
384
385 if (curExpr->is_sequential())
386 return NULL;
387
388 xqtref_t domainType = var->get_domain_expr()->get_return_type();
389
390 if (domainType->get_quantifier() != TypeConstants::QUANT_ONE)
391 {
392 // found a real FOR var, so we return it.
393 return var;
394 }
395 }
396 else if (var->get_kind() == var_expr::let_var)
397 {
398 curExpr = var->get_domain_expr();
399
400 if (curExpr->is_sequential())
401 return NULL;
402 }
403 else
404 {
405 return NULL;
406 }
407 }
408
409 return var;
410 }
411
412
413 /*******************************************************************************
414 Check if "curExpr" depends on the var V with the given prefix id. The method
415 returns true if "curExpr" references V or references another var whose domain
416 expr depends on V.
417 ********************************************************************************/
checkVarDependency(RewriterContext & rCtx,expr * curExpr,ulong searchVarId)418 static bool checkVarDependency(
419 RewriterContext& rCtx,
420 expr* curExpr,
421 ulong searchVarId)
422 {
423 const DynamicBitset& bitset = (*rCtx.theExprVarsMap)[curExpr];
424
425 if (bitset.get(searchVarId))
426 return true;
427
428 std::vector<ulong> varidSet;
429 bitset.getSet(varidSet);
430
431 ulong numVars = (ulong)varidSet.size();
432 for (ulong i = 0; i < numVars; ++i)
433 {
434 const var_expr* var = (*rCtx.theIdVarMap)[varidSet[i]];
435 curExpr = var->get_forletwin_clause()->get_expr();
436
437 if (checkVarDependency(rCtx, curExpr, searchVarId))
438 return true;
439 }
440
441 return false;
442 }
443
444
445 /*******************************************************************************
446
447 ********************************************************************************/
rewriteJoin(RewriterContext & rCtx,PredicateInfo & predInfo,bool & modified)448 static void rewriteJoin(
449 RewriterContext& rCtx,
450 PredicateInfo& predInfo,
451 bool& modified)
452 {
453 // std::cout << "!!!!! Found Join Index Predicate !!!!!" << std::endl << std::endl;
454
455 const QueryLoc& loc = predInfo.thePredicate->get_loc();
456 static_context* sctx = predInfo.thePredicate->get_sctx();
457
458 for_clause* fc = predInfo.theInnerVar->get_for_clause();
459
460 long maxInnerVarId = -1;
461
462 // The index domain expr is the expr that defines the inner var, expanded, if
463 // possible, so that it does not reference any variables defined after the outer
464 // var (because the index must be built ouside the loop of the outer FOR var).
465 // Note: must clone fc->get_expr() because expandVars modifies its input, but
466 // fc->get_expr should not be modified, because we may discover later that the
467 // rewrite is not possible after all,
468 expr::substitution_t subst;
469 expr* domainExpr = fc->get_expr()->clone(subst);
470
471 if (!expandVars(rCtx, domainExpr, predInfo.theOuterVarId, maxInnerVarId))
472 return;
473
474 //
475 // Create the index name and the "create-index()" expr
476 //
477 std::ostringstream os;
478 os << "tempIndex" << rCtx.theCCB->theTempIndexCounter++;
479
480 store::Item_t qname;
481 GENV_ITEMFACTORY->createQName(qname, "", "", os.str().c_str());
482
483 expr* qnameExpr(rCtx.theEM->create_const_expr(sctx, loc, qname));
484 expr* buildExpr = NULL;
485
486 fo_expr* createExpr = rCtx.theEM->create_fo_expr(sctx,
487 loc,
488 GET_BUILTIN_FUNCTION(OP_CREATE_INTERNAL_INDEX_2),
489 qnameExpr,
490 buildExpr);
491
492 //
493 // Find where to place the create-index expr
494 //
495 if (maxInnerVarId >= 0)
496 {
497 // The domain expr depends on some flwor var that is defined before the outer
498 // var. In this case, we find the flwor expr defining the inner-most var V
499 // referenced by the domain expr of the index. Let F be this flwor expr. If
500 // F does not define the outer var as well, then we create the index in the
501 // return expr of F. Otherwise, we first break up F by creating a sub-flwor
502 // expr (subF) and moving all clauses of F that appear after V's defining
503 // clause into subF, making the return expr of f be the return expr of subF,
504 // and setting subF as the return expr of F. Then, we create the index in
505 // the return expr of F.
506
507 const var_expr* mostInnerVar = (*rCtx.theIdVarMap)[maxInnerVarId];
508 flwor_clause* mostInnerVarClause = mostInnerVar->get_flwor_clause();
509
510 flwor_expr* innerFlwor = NULL;
511 ulong innerPosInStack = 0;
512 ulong mostInnerVarPos = 0;
513
514 if (!findFlworForVar(rCtx,
515 mostInnerVar,
516 innerFlwor,
517 mostInnerVarPos,
518 innerPosInStack))
519 return;
520
521 csize numClauses = innerFlwor->num_clauses();
522
523 if (innerFlwor->defines_variable(predInfo.theOuterVar) >= 0 ||
524 mostInnerVarPos < numClauses-1)
525 {
526 ZORBA_ASSERT(mostInnerVarPos < numClauses-1);
527
528 const QueryLoc& nestedLoc = mostInnerVarClause->get_loc();
529
530 flwor_expr* nestedFlwor = rCtx.theEM->create_flwor_expr(sctx, nestedLoc, false);
531
532 for (csize i = mostInnerVarPos+1; i < numClauses; ++i)
533 {
534 nestedFlwor->add_clause(innerFlwor->get_clause(i));
535 }
536
537 for (csize i = numClauses - 1; i > mostInnerVarPos; --i)
538 {
539 innerFlwor->remove_clause(i);
540 }
541
542 nestedFlwor->set_return_expr(innerFlwor->get_return_expr());
543
544 std::vector<expr*> args(2);
545 args[0] = createExpr;
546 args[1] = nestedFlwor;
547
548 block_expr* seqExpr = rCtx.theEM->create_block_expr(sctx, loc, false, args, NULL);
549
550 innerFlwor->set_return_expr(seqExpr);
551
552 if (predInfo.theFlworExpr == innerFlwor)
553 {
554 // The where clause has moved to the nested flwor.
555 predInfo.theFlworExpr = nestedFlwor;
556 }
557 }
558 else
559 {
560 expr* returnExpr = innerFlwor->get_return_expr();
561
562 if (returnExpr->get_expr_kind() == block_expr_kind)
563 {
564 block_expr* seqExpr = static_cast<block_expr*>(returnExpr);
565
566 csize numArgs = seqExpr->size();
567 csize arg;
568 for (arg = 0; arg < numArgs; ++arg)
569 {
570 if ((*seqExpr)[arg]->get_function_kind() !=
571 FunctionConsts::OP_CREATE_INTERNAL_INDEX_2)
572 {
573 break;
574 }
575 }
576
577 seqExpr->add_at(arg, createExpr);
578 }
579 else
580 {
581 std::vector<expr*> args(2);
582 args[0] = createExpr;
583 args[1] = returnExpr;
584
585 block_expr* seqExpr = rCtx.theEM->create_block_expr(sctx, loc, false, args, NULL);
586
587 innerFlwor->set_return_expr(seqExpr);
588 }
589 }
590 }
591 else
592 {
593 // The inner domain expr does not reference any flwor vars. In this case,
594 // find the flwor expr defining the outer var and create the index just
595 // before this flwor.
596 flwor_expr* outerFlworExpr = NULL;
597 ulong outerPosInStack = 0;
598 ulong dummy = 0;
599
600 if (!findFlworForVar(rCtx,
601 predInfo.theOuterVar,
602 outerFlworExpr,
603 dummy,
604 outerPosInStack))
605 return;
606
607 // Build outer sequential expr
608 std::vector<expr*> args(2);
609 args[0] = createExpr;
610 args[1] = outerFlworExpr;
611
612 block_expr* seqExpr = rCtx.theEM->create_block_expr(sctx, loc, false, args, NULL);
613
614 rCtx.theFlworStack[outerPosInStack] = seqExpr;
615 }
616
617 modified = true;
618
619 //
620 // Replace the expr defining the inner var with an index probe.
621 //
622 fo_expr* probeExpr = NULL;
623
624 if (predInfo.theIsGeneral)
625 {
626 probeExpr =
627 rCtx.theEM->create_fo_expr(sctx,
628 loc,
629 GET_BUILTIN_FUNCTION(FN_ZORBA_XQDDF_PROBE_INDEX_POINT_GENERAL_N),
630 qnameExpr,
631 const_cast<expr*>(predInfo.theOuterOp));
632
633 probeExpr =
634 rCtx.theEM->create_fo_expr(sctx,
635 loc,
636 GET_BUILTIN_FUNCTION(OP_SORT_DISTINCT_NODES_ASC_1),
637 probeExpr);
638 }
639 else
640 {
641 probeExpr =
642 rCtx.theEM->create_fo_expr(sctx,
643 loc,
644 GET_BUILTIN_FUNCTION(FN_ZORBA_XQDDF_PROBE_INDEX_POINT_VALUE_N),
645 qnameExpr,
646 const_cast<expr*>(predInfo.theOuterOp));
647 }
648
649 fc->set_expr(probeExpr);
650
651 //
652 // Create the IndexDecl obj
653 //
654 IndexDecl_t idx = new IndexDecl(sctx, rCtx.theCCB, loc, qname);
655
656 if (predInfo.theIsGeneral)
657 idx->setGeneral(true);
658
659 idx->setTemp(true);
660
661 idx->setDomainExpr(domainExpr);
662
663 idx->setDomainVariable(rCtx.createTempVar(sctx, loc, var_expr::for_var));
664
665 idx->setDomainPositionVariable(rCtx.createTempVar(sctx, loc, var_expr::pos_var));
666
667 std::vector<expr*> columnExprs(1);
668 std::vector<xqtref_t> columnTypes(1);
669 std::vector<OrderModifier> modifiers(1);
670
671 columnExprs[0] = predInfo.theInnerOp;
672
673 columnTypes[0] = (predInfo.theIsGeneral ?
674 NULL :
675 predInfo.theInnerOp->get_return_type());
676
677 modifiers[0].theAscending = true;
678 modifiers[0].theEmptyLeast = true;
679 modifiers[0].theCollation = sctx->get_default_collation(QueryLoc::null);
680
681 expr_tools::replace_var(columnExprs[0], predInfo.theInnerVar, idx->getDomainVariable());
682
683 idx->setKeyExpressions(columnExprs);
684
685 idx->setKeyTypes(columnTypes);
686
687 idx->setOrderModifiers(modifiers);
688
689 sctx->bind_index(idx, loc);
690
691 buildExpr = idx->getBuildExpr(rCtx.getCompilerCB(), loc);
692
693 createExpr->set_arg(1, buildExpr);
694
695 if (Properties::instance()->printIntermediateOpt())
696 {
697 std::cout << std::endl << idx->toString() << std::endl;
698 }
699
700 //idx->setDomainExpr(NULL);
701 //idx->setDomainVariable(NULL);
702
703 #if 0
704 if (predInfo.theIsGeneral)
705 std::cout << "!!!!! Applied General Hash Join !!!!!" << std::endl << std::endl;
706 else
707 std::cout << "!!!!! Applied Value Hash Join !!!!!" << std::endl << std::endl;
708 #endif
709 }
710
711
712
713 /*******************************************************************************
714
715 ********************************************************************************/
expandVars(RewriterContext & rCtx,expr * subExpr,ulong outerVarId,long & maxVarId)716 static bool expandVars(
717 RewriterContext& rCtx,
718 expr* subExpr,
719 ulong outerVarId,
720 long& maxVarId)
721 {
722 if (subExpr->get_expr_kind() == wrapper_expr_kind)
723 {
724 wrapper_expr* wrapper = reinterpret_cast<wrapper_expr*>(subExpr);
725
726 if (wrapper->get_expr()->get_expr_kind() == var_expr_kind)
727 {
728 var_expr* var = reinterpret_cast<var_expr*>(wrapper->get_expr());
729 long varid = -1;
730
731 if (rCtx.theVarIdMap->find(var) != rCtx.theVarIdMap->end())
732 {
733 varid = (*rCtx.theVarIdMap)[var];
734 }
735 else if (var->get_kind() == var_expr::local_var)
736 {
737 // TODO: allow index domain exprs to reference local vars
738 return false;
739 }
740
741 // If it is a variable that is defined after the outer var
742 if (varid > (long)outerVarId)
743 {
744 if (var->get_kind() == var_expr::let_var)
745 {
746 wrapper->set_expr(var->get_forletwin_clause()->get_expr());
747
748 return expandVars(rCtx, wrapper, outerVarId, maxVarId);
749 }
750 else if (var->get_kind() == var_expr::for_var)
751 {
752 #if 1
753 return false;
754 #else
755 // TODO: to expand a FOR var, we must make sure that the expr is a
756 // map w.r.t. that var.
757 wrapper->set_expr(var->get_forletwin_clause()->get_expr());
758
759 return expandVars(rCtx, wrapper, outerVarId, maxVarId);
760 #endif
761 }
762 else
763 {
764 return false;
765 }
766 }
767 else
768 {
769 if (varid > maxVarId)
770 maxVarId = varid;
771
772 return true;
773 }
774 }
775 }
776 else if (subExpr->get_expr_kind() == var_expr_kind)
777 {
778 ZORBA_ASSERT(false);
779 }
780
781 ExprIterator iter(subExpr);
782 while (!iter.done())
783 {
784 if (!expandVars(rCtx, **iter, outerVarId, maxVarId))
785 return false;
786
787 iter.next();
788 }
789
790 return true;
791 }
792
793
794 /*******************************************************************************
795 Find the flwor expr defining the given var.
796 ********************************************************************************/
findFlworForVar(RewriterContext & rCtx,const var_expr * var,flwor_expr * & flworExpr,ulong & varPos,ulong & posInStack)797 static bool findFlworForVar(
798 RewriterContext& rCtx,
799 const var_expr* var,
800 flwor_expr*& flworExpr,
801 ulong& varPos,
802 ulong& posInStack)
803 {
804 flworExpr = NULL;
805
806 long numFlwors = (long)rCtx.theFlworStack.size();
807
808 for (long i = numFlwors - 1; i >= 0; --i)
809 {
810 if (rCtx.theFlworStack[i]->get_expr_kind() == trycatch_expr_kind)
811 return false;
812
813 flworExpr = dynamic_cast<flwor_expr*>(rCtx.theFlworStack[i]);
814
815 assert(flworExpr != NULL);
816
817 if (i < numFlwors - 1 &&
818 rCtx.theInReturnClause[i] == true &&
819 flworExpr->is_sequential())
820 return false;
821
822 // This condition is rather conservative and can be relaxed. TODO
823 if (flworExpr->has_sequential_clauses())
824 return false;
825
826 long pos;
827 if ((pos = flworExpr->defines_variable(var)) >= 0)
828 {
829 varPos = pos;
830 posInStack = i;
831 break;
832 }
833
834 flworExpr = NULL;
835 }
836
837 assert(flworExpr != NULL);
838 return true;
839 }
840
841
842 }
843 /* vim:set et sw=2 ts=2: */
844