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