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 <algorithm>
19 
20 #include "types/typeops.h"
21 
22 #include "functions/function.h"
23 #include "functions/library.h"
24 
25 #include "compiler/xqddf/value_index.h"
26 #include "compiler/api/compilercb.h"
27 #include "compiler/expression/flwor_expr.h"
28 #include "compiler/expression/fo_expr.h"
29 #include "compiler/expression/script_exprs.h"
30 #include "compiler/expression/expr.h"
31 #include "compiler/expression/expr_iter.h"
32 #include "compiler/codegen/plan_visitor.h"
33 
34 #include "runtime/base/plan_iterator.h"
35 #include "runtime/indexing/doc_indexer.h"
36 
37 #include "store/api/item_factory.h"
38 
39 #include "diagnostics/util_macros.h"
40 
41 
42 namespace zorba
43 {
44 
45 SERIALIZABLE_CLASS_VERSIONS(OrderModifier)
46 
SERIALIZABLE_CLASS_VERSIONS(IndexDecl)47 SERIALIZABLE_CLASS_VERSIONS(IndexDecl)
48 
49 
50 /*******************************************************************************
51 
52 ********************************************************************************/
53 IndexDecl::IndexDecl(
54     static_context* sctx,
55     CompilerCB* ccb,
56     const QueryLoc& loc,
57     const store::Item_t& name)
58   :
59   theCCB(ccb),
60   theLocation(loc),
61   theSctx(sctx),
62   theName(name),
63   theIsGeneral(false),
64   theIsUnique(false),
65   theIsTemp(false),
66   theMaintenanceMode(MANUAL),
67   theContainerKind(HASH),
68   theDomainClause(NULL),
69   theDomainExpr(NULL),
70   theDomainVar(NULL),
71   theDomainPosVar(NULL),
72   theBuildExpr(NULL),
73   theDocIndexerExpr(NULL)
74 {
75 }
76 
77 
78 /*******************************************************************************
79 
80 ********************************************************************************/
IndexDecl(::zorba::serialization::Archiver & ar)81 IndexDecl::IndexDecl(::zorba::serialization::Archiver& ar)
82   :
83   SimpleRCObject(ar)
84 {
85 }
86 
87 
88 /*******************************************************************************
89 
90 ********************************************************************************/
serialize(::zorba::serialization::Archiver & ar)91 void IndexDecl::serialize(::zorba::serialization::Archiver& ar)
92 {
93   ar & theSctx;
94   ar & theName;
95   ar & theIsGeneral;
96   ar & theIsUnique;
97   ar & theIsTemp;
98   SERIALIZE_ENUM(MaintenanceMode, theMaintenanceMode);
99   SERIALIZE_ENUM(ContainerKind, theContainerKind);
100   //ar & theDomainClause;
101   //ar & theKeyExprs;
102   ar & theNumKeyExprs;
103   ar & theKeyTypes;
104   ar & theOrderModifiers;
105 
106   ar & theSourceNames;
107   //ar & theDomainSourceExprs;
108 
109   ar & theBuildPlan;
110   ar & theDocIndexerPlan;
111 }
112 
113 
114 /*******************************************************************************
115 
116 ********************************************************************************/
~IndexDecl()117 IndexDecl::~IndexDecl()
118 {
119 }
120 
121 
122 /*******************************************************************************
123 
124 ********************************************************************************/
getName() const125 store::Item* IndexDecl::getName() const
126 {
127   return theName.getp();
128 }
129 
130 
131 /*******************************************************************************
132 
133 ********************************************************************************/
getDomainExpr() const134 expr* IndexDecl::getDomainExpr() const
135 {
136   return theDomainExpr;
137 }
138 
139 
140 /*******************************************************************************
141 
142 ********************************************************************************/
setDomainExpr(expr * domainExpr)143 void IndexDecl::setDomainExpr(expr* domainExpr)
144 {
145   theDomainExpr = domainExpr;
146 
147   if (theDomainClause == NULL)
148     theDomainClause = theCCB->theEM->create_for_clause(domainExpr->get_sctx(),
149                                      domainExpr->get_loc(),
150                                      NULL,
151                                      NULL);
152 
153   theDomainClause->set_expr(domainExpr);
154 }
155 
156 
157 /*******************************************************************************
158 
159 ********************************************************************************/
getDomainVariable() const160 var_expr* IndexDecl::getDomainVariable() const
161 {
162   return theDomainVar;
163 }
164 
165 
166 /*******************************************************************************
167 
168 ********************************************************************************/
setDomainVariable(var_expr * domainVar)169 void IndexDecl::setDomainVariable(var_expr* domainVar)
170 {
171   theDomainVar = domainVar;
172 
173   if (theDomainClause == NULL)
174     theDomainClause = theCCB->theEM->create_for_clause(domainVar->get_sctx(),
175                                      domainVar->get_loc(),
176                                      NULL,
177                                      NULL);
178 
179   theDomainClause->set_var(domainVar);
180 }
181 
182 
183 /*******************************************************************************
184 
185 ********************************************************************************/
getDomainPositionVariable() const186 var_expr* IndexDecl::getDomainPositionVariable() const
187 {
188   return theDomainPosVar;
189 }
190 
191 
192 /*******************************************************************************
193 
194 ********************************************************************************/
setDomainPositionVariable(var_expr * domainPosVar)195 void IndexDecl::setDomainPositionVariable(var_expr* domainPosVar)
196 {
197   theDomainPosVar = domainPosVar;
198 
199   theDomainClause->set_pos_var(domainPosVar);
200 }
201 
202 
203 /*******************************************************************************
204 
205 ********************************************************************************/
setKeyExpressions(const std::vector<expr * > & keyExprs)206 void IndexDecl::setKeyExpressions(const std::vector<expr*>& keyExprs)
207 {
208   theKeyExprs = keyExprs;
209   theNumKeyExprs = theKeyExprs.size();
210 }
211 
212 
213 /*******************************************************************************
214 
215 ********************************************************************************/
getKeyTypes() const216 const std::vector<xqtref_t>& IndexDecl::getKeyTypes() const
217 {
218   return theKeyTypes;
219 }
220 
221 
222 /*******************************************************************************
223 
224 ********************************************************************************/
setKeyTypes(const std::vector<xqtref_t> & keyTypes)225 void IndexDecl::setKeyTypes(const std::vector<xqtref_t>& keyTypes)
226 {
227   assert(!keyTypes.empty());
228 
229   theKeyTypes = keyTypes;
230 
231   if (theIsGeneral && theIsUnique)
232   {
233     TypeManager* tm = theSctx->get_typemanager();
234 
235     xqtref_t type = theKeyTypes[0];
236     xqtref_t ptype;
237 
238     if (type != NULL)
239       ptype = TypeOps::prime_type(tm, *type);
240 
241     if (ptype == NULL ||
242         TypeOps::is_equal(tm, *ptype, *GENV_TYPESYSTEM.ANY_ATOMIC_TYPE_ONE) ||
243         TypeOps::is_subtype(tm, *ptype, *GENV_TYPESYSTEM.UNTYPED_ATOMIC_TYPE_ONE))
244     {
245       RAISE_ERROR(zerr::ZDST0025_INDEX_BAD_UNIQUE_PROPERTY, theLocation,
246       ERROR_PARAMS(theName->getStringValue()));
247     }
248   }
249 }
250 
251 
252 /*******************************************************************************
253 
254 ********************************************************************************/
getOrderModifiers() const255 const std::vector<OrderModifier>& IndexDecl::getOrderModifiers() const
256 {
257   return theOrderModifiers;
258 }
259 
260 
261 /*******************************************************************************
262 
263 ********************************************************************************/
setOrderModifiers(const std::vector<OrderModifier> & modifiers)264 void IndexDecl::setOrderModifiers(const std::vector<OrderModifier>& modifiers)
265 {
266   theOrderModifiers = modifiers;
267 }
268 
269 
270 /******************************************************************************
271   Check that the domain and key exprs satisfy the constraints specified by the
272   XQDDF spec. This method is called from the translator, after the domain and
273   key exprs have been translated and optimized.
274 *******************************************************************************/
analyze(CompilerCB * ccb)275 void IndexDecl::analyze(CompilerCB* ccb)
276 {
277   store::Item_t dotQName;
278   GENV_ITEMFACTORY->createQName(dotQName, "", "", "$$dot");
279   expr* dotVar = NULL;
280 
281   // Get the var_expr representing the context item, if it is defined
282   VarInfo* var = theSctx->lookup_var(dotQName);
283 
284   if (var)
285     dotVar = var->getVar();
286 
287   std::vector<var_expr*> varExprs;
288 
289   // Check constraints on the domain expr
290   analyzeExprInternal(getDomainExpr(),
291                       theSourceNames,
292                       theDomainSourceExprs,
293                       varExprs,
294                       dotVar);
295 
296   varExprs.clear();
297 
298   std::vector<expr*> keySources;
299 
300   csize numKeys = theKeyExprs.size();
301 
302   if (theIsGeneral && numKeys > 1)
303   {
304     RAISE_ERROR(zerr::ZDST0035_INDEX_GENERAL_MULTIKEY, theKeyExprs[1]->get_loc(),
305     ERROR_PARAMS(theName->getStringValue()));
306   }
307 
308   // Check constraints on the key exprs
309   for (csize i = 0; i < numKeys; ++i)
310   {
311     analyzeExprInternal(theKeyExprs[i],
312                         theSourceNames,
313                         keySources,
314                         varExprs,
315                         dotVar);
316   }
317 
318   // If the index is declared as "automatically maintained", check whether
319   // automatic maintence can be done efficiently, and if so, set the appropriate
320   // maintenance mode.
321   if (keySources.empty() &&
322       theDomainSourceExprs.size() == 1 &&
323       theMaintenanceMode != MANUAL)
324   {
325     if (getDomainExpr()->is_map(theDomainSourceExprs[0], theSctx))
326       theMaintenanceMode = DOC_MAP;
327   }
328 
329   if (theMaintenanceMode == REBUILD)
330   {
331     // If the index is declared as "automatically maintained", then
332     // theMaintenanceMode is initially set to REBUILD. If theMaintenanceMode
333     // is not changed above (to DOC_MAP), then we throw an error because we
334     // don't want to automatically rebuild the full index with every update.
335     RAISE_ERROR(zerr::ZDST0034_INDEX_CANNOT_DO_AUTOMATIC_MAINTENANCE,
336     getDomainExpr()->get_loc(),
337     ERROR_PARAMS(theName->getStringValue()));
338   }
339   else if (theMaintenanceMode == DOC_MAP)
340   {
341     // Have to do this here (rather than during runtime) so that we don't have to
342     // serialize the index exprs.
343     (void)getDocIndexer(ccb, theLocation);
344   }
345 
346   // Have to do this here (rather than during runtime) so that we don't have to
347   // serialize the index exprs.
348   (void)getBuildPlan(ccb, theLocation);
349 }
350 
351 
352 /*******************************************************************************
353   Check that the given expr
354   (a) is deterministic,
355   (b) does not reference any variables other than those in varExprs
356   (c) does not reference the given dotVar
357   (c) does not reference any input functions other than xqddf:collection(qname)
358   (d) the arg to each xqddf:collection is a const qname
359 
360   If the above conditions are met, the method will return the qnames of all the
361   accessed collections and the fo exprs representing the xqddf:collection
362   invocations.
363 ********************************************************************************/
analyzeExprInternal(expr * e,std::vector<store::Item * > & sourceNames,std::vector<expr * > & sourceExprs,std::vector<var_expr * > & varExprs,expr * dotVar)364 void IndexDecl::analyzeExprInternal(
365     expr* e,
366     std::vector<store::Item*>& sourceNames,
367     std::vector<expr*>& sourceExprs,
368     std::vector<var_expr*>& varExprs,
369     expr* dotVar)
370 {
371   if (e->get_expr_kind() == fo_expr_kind)
372   {
373     fo_expr* foExpr = static_cast<fo_expr*>(e);
374     const function* func = foExpr->get_func();
375 
376     if (!func->isDeterministic())
377     {
378       RAISE_ERROR(zerr::ZDST0028_INDEX_NOT_DETERMINISTIC, e->get_loc(),
379       ERROR_PARAMS(theName->getStringValue()));
380     }
381 
382     if (func->isSource())
383     {
384       if (func->getKind() == FunctionConsts::STATIC_COLLECTIONS_DML_COLLECTION_1)
385       {
386         const expr* argExpr = foExpr->get_arg(0);
387 
388         const store::Item* qname = argExpr->getQName(theSctx);
389 
390         if (qname != NULL)
391         {
392           sourceNames.push_back((store::Item*)qname);
393           sourceExprs.push_back(foExpr);
394         }
395         else
396         {
397           RAISE_ERROR(zerr::ZDST0030_INDEX_NON_CONST_DATA_SOURCE, e->get_loc(),
398           ERROR_PARAMS(theName->getStringValue()));
399         }
400       }
401       else
402       {
403         RAISE_ERROR(zerr::ZDST0029_INDEX_INVALID_DATA_SOURCE, e->get_loc(),
404         ERROR_PARAMS(theName->getStringValue()));
405       }
406     }
407   }
408   else if (e->get_expr_kind() == var_decl_expr_kind)
409   {
410     var_expr* varExpr = static_cast<var_decl_expr*>(e)->get_var_expr();
411 
412     ZORBA_ASSERT(varExpr->get_kind() == var_expr::local_var);
413 
414     varExprs.push_back(varExpr);
415   }
416   else if (e->get_expr_kind() == flwor_expr_kind ||
417            e->get_expr_kind() == gflwor_expr_kind)
418   {
419     static_cast<const flwor_expr*>(e)->get_vars_defined(varExprs);
420   }
421   else if (e->get_expr_kind() == var_expr_kind)
422   {
423     if (e == dotVar)
424     {
425       RAISE_ERROR(zerr::ZDST0032_INDEX_REFERENCES_CTX_ITEM, e->get_loc(),
426       ERROR_PARAMS(theName->getStringValue()));
427     }
428 
429     if (e != getDomainVariable() &&
430         std::find(varExprs.begin(), varExprs.end(), e) == varExprs.end())
431     {
432       RAISE_ERROR(zerr::ZDST0031_INDEX_HAS_FREE_VARS,  e->get_loc(),
433       ERROR_PARAMS(theName->getStringValue()));
434     }
435   }
436 
437   ExprIterator iter(e);
438   while (!iter.done())
439   {
440     analyzeExprInternal((**iter), sourceNames, sourceExprs, varExprs, dotVar);
441     iter.next();
442   }
443 }
444 
445 
446 /******************************************************************************
447   Create the expression that "builds" the index, if not done already. The expr
448   to build is the following, for value and general indexes, respectively:
449 
450   for $newdot at $newpos in cloned_domain_expr
451   return value-index-entry-builder($$newdot, cloned_key1_expr, ..., cloned_keyN_expr)
452 
453   for $$newdot at $$newpos in cloned_domain_expr
454   return general-index-entry-builder($$newdot, cloned_key_expr);
455 
456   The runtime plan corresponding to this expr is given to the store, which
457   then populates the index by creating entries out of the items returned by
458   this expr.
459 *******************************************************************************/
getBuildExpr(CompilerCB * ccb,const QueryLoc & loc)460 expr* IndexDecl::getBuildExpr(CompilerCB* ccb, const QueryLoc& loc)
461 {
462   if (theBuildExpr != NULL)
463     return theBuildExpr;
464 
465   theDomainClause = NULL;
466 
467   expr* domainExpr = getDomainExpr();
468   var_expr* dot = getDomainVariable();
469   var_expr* pos = getDomainPositionVariable();
470   static_context* sctx = domainExpr->get_sctx();
471   const QueryLoc& dotloc = dot->get_loc();
472 
473   csize numKeys = theKeyExprs.size();
474   std::vector<expr*> clonedExprs(numKeys + 1);
475 
476   //
477   // Clone the domain expr.
478   //
479   expr::substitution_t subst;
480   expr* newdom = domainExpr->clone(subst);
481 
482   //
483   // Clone the domain variable and the domain pos variable. These vars are
484   // referenced by the key exprs.
485   //
486   var_expr* newdot = theCCB->theEM->
487   create_var_expr(sctx, dotloc, dot->get_kind(), dot->get_name());
488 
489   var_expr* newpos = theCCB->theEM->
490   create_var_expr(sctx, dotloc, pos->get_kind(), pos->get_name());
491 
492   //
493   // Create for clause (this has to be done here so that the cloned dot var gets
494   // associated with the cloned domain expr; this is needed before cloning the
495   // key expr) :
496   //
497   // for $newdot at $newpos in new_domain_expr
498   //
499   for_clause* fc =
500     theCCB->theEM->create_for_clause(sctx, dotloc, newdot, newdom, newpos);
501 
502   //
503   // Clone the key exprs, replacing their references to the 2 domain variables
504   // with the clones of these variables.
505   //
506   for (csize i = 0; i < numKeys; ++i)
507   {
508     subst.clear();
509     subst[dot] = newdot;
510     subst[pos] = newpos;
511 
512     clonedExprs[i+1] = theKeyExprs[i]->clone(subst);
513   }
514 
515   //
516   // Create flwor expr:
517   //
518   // for $newdot at $newpos in new_domain_expr
519   // return index-entry-builder($$newdot, new_key1_expr, ..., new_keyN_expr)
520   //
521 
522   expr* domainVarExpr(theCCB->theEM->create_wrapper_expr(sctx, loc, newdot));
523 
524   clonedExprs[0] = domainVarExpr;
525 
526   function* f = NULL;
527 
528   if (theIsGeneral)
529     f = GET_BUILTIN_FUNCTION(OP_GENERAL_INDEX_ENTRY_BUILDER_N);
530   else
531     f = GET_BUILTIN_FUNCTION(OP_VALUE_INDEX_ENTRY_BUILDER_N);
532 
533   ZORBA_ASSERT(f != NULL);
534 
535   fo_expr* returnExpr =  theCCB->theEM->create_fo_expr(sctx, loc, f, clonedExprs);
536 
537   flwor_expr* flworExpr = theCCB->theEM->create_flwor_expr(sctx, loc, false);
538   flworExpr->set_return_expr(returnExpr);
539   flworExpr->add_clause(fc);
540 
541   theBuildExpr = flworExpr;
542 
543   if (ccb->theConfig.optimize_cb != NULL)
544   {
545     std::string msg = "build expr for index " + theName->getStringValue().str();
546 
547     ccb->theConfig.optimize_cb(theBuildExpr, msg);
548   }
549 
550   return theBuildExpr;
551 }
552 
553 
554 /*******************************************************************************
555 
556 ********************************************************************************/
getBuildPlan(CompilerCB * ccb,const QueryLoc & loc)557 PlanIterator* IndexDecl::getBuildPlan(CompilerCB* ccb, const QueryLoc& loc)
558 {
559   if (theBuildPlan != NULL)
560     return theBuildPlan.getp();
561 
562   expr* buildExpr = getBuildExpr(ccb, loc);
563 
564   ulong nextVarId = 1;
565   theBuildPlan = codegen("index", buildExpr, ccb, nextVarId);
566 
567   return theBuildPlan.getp();
568 }
569 
570 
571 /*******************************************************************************
572   Called from ApplyIterator::nextImpl before it actually starts applying the
573   updates.
574 ********************************************************************************/
getDocIndexer(CompilerCB * ccb,const QueryLoc & loc)575 DocIndexer* IndexDecl::getDocIndexer(
576     CompilerCB* ccb,
577     const QueryLoc& loc)
578 {
579   if (theDocIndexer != NULL)
580     return theDocIndexer.getp();
581 
582   if (theMaintenanceMode != DOC_MAP)
583     return NULL;
584 
585   std::stringstream ss;
586   ss << "$$idx_doc_var_" << this;
587   std::string varname = ss.str();
588   store::Item_t docVarName;
589   GENV_ITEMFACTORY->createQName(docVarName, "", "", varname.c_str());
590 
591   csize numKeys = theNumKeyExprs;
592 
593   if (theDocIndexerPlan != NULL)
594   {
595     theDocIndexer = new DocIndexer(isGeneral(), numKeys, theDocIndexerPlan, docVarName);
596 
597     return theDocIndexer.getp();
598   }
599 
600   expr* domainExpr = getDomainExpr();
601   var_expr* dot = getDomainVariable();
602   var_expr* pos = getDomainPositionVariable();
603 
604   static_context* sctx = domainExpr->get_sctx();
605 
606   const QueryLoc& dotloc = dot->get_loc();
607 
608   std::vector<expr*> clonedExprs(numKeys + 1);
609 
610   //
611   // Clone the domain expr and replace the reference to the collection with a
612   // reference to a new prolog variable that will be bound to a set of docs
613   // during the apply-updates.
614   //
615 
616   var_expr* docVar = theCCB->theEM->create_var_expr(sctx,
617                                                     dot->get_loc(),
618                                                     var_expr::prolog_var,
619                                                     docVarName);
620   docVar->set_unique_id(1);
621   ulong nextVarId = 2;
622 
623   expr* wrapperExpr = theCCB->theEM->create_wrapper_expr(sctx, dot->get_loc(), docVar);
624 
625   docVar->set_type(domainExpr->get_return_type());
626 
627   expr::substitution_t subst;
628 
629   subst[theDomainSourceExprs[0]] = wrapperExpr;
630 
631   expr* newdom = domainExpr->clone(subst);
632 
633   //
634   // Clone the domain variable and the domain pos variable. These vars are
635   // referenced by the key exprs.
636   //
637   var_expr* newdot = theCCB->theEM->
638   create_var_expr(sctx, dotloc, dot->get_kind(), dot->get_name());
639 
640   var_expr* newpos = theCCB->theEM->
641   create_var_expr(sctx, dotloc, pos->get_kind(), pos->get_name());
642 
643   //
644   // Create for clause (this has to be done here so that the cloned dot var gets
645   // associated with the cloned domain expr; this is needed before cloning the
646   // key expr) :
647   //
648   // for $newdot at $newpos in new_domain_expr
649   //
650   for_clause* fc =
651     theCCB->theEM->create_for_clause(sctx, dotloc, newdot, newdom, newpos);
652 
653   //
654   // Clone the key exprs, replacing their references to the 2 domain variables
655   // with the clones of these variables.
656   //
657   for (csize i = 0; i < numKeys; ++i)
658   {
659     subst.clear();
660     subst[dot] = newdot;
661     subst[pos] = newpos;
662 
663     clonedExprs[i+1] = theKeyExprs[i]->clone(subst);
664   }
665 
666   //
667   // Create flwor expr:
668   //
669   // for $newdot at $newpos in new_domain_expr
670   // return index-entry-builder($$newdot, new_key1_expr, ..., new_keyN_expr)
671   //
672 
673   expr* domainVarExpr = theCCB->theEM->create_wrapper_expr(sctx, loc, newdot);
674 
675   clonedExprs[0] = domainVarExpr;
676 
677   function* f = NULL;
678 
679   if (theIsGeneral)
680     f = GET_BUILTIN_FUNCTION(OP_GENERAL_INDEX_ENTRY_BUILDER_N);
681   else
682     f = GET_BUILTIN_FUNCTION(OP_VALUE_INDEX_ENTRY_BUILDER_N);
683 
684   ZORBA_ASSERT(f != NULL);
685 
686   fo_expr* returnExpr =  theCCB->theEM->create_fo_expr(sctx, loc, f, clonedExprs);
687 
688   flwor_expr* flworExpr = theCCB->theEM->create_flwor_expr(sctx, loc, false);
689   flworExpr->set_return_expr(returnExpr);
690   flworExpr->add_clause(fc);
691 
692   if (ccb->theConfig.optimize_cb != NULL)
693   {
694     std::string msg = "entry-creator expr for index " + theName->getStringValue().str();
695 
696     ccb->theConfig.optimize_cb(flworExpr, msg);
697   }
698 
699   theDocIndexerExpr = flworExpr;
700 
701   //
702   // Generate the runtime plan for theDocIndexerExpr
703   //
704   theDocIndexerPlan = codegen("doc indexer", flworExpr, ccb, nextVarId);
705 
706   //
707   // Create theDocIndexer obj
708   //
709   theDocIndexer = new DocIndexer(isGeneral(), numKeys, theDocIndexerPlan, docVarName);
710 
711   return theDocIndexer.getp();
712 }
713 
714 
715 /*******************************************************************************
716 
717 ********************************************************************************/
toString()718 std::string IndexDecl::toString()
719 {
720   std::ostringstream os;
721 
722   os << "Index : " << theName->getStringValue() << std::endl;
723 
724   os << "Domain Expr : " << std::endl;
725 
726   getDomainExpr()->put(os) << std::endl;
727 
728   os << "Domain Variable : ";
729   getDomainVariable()->put(os);
730 
731   csize numColumns = theKeyExprs.size();
732 
733   for (csize i = 0; i < numColumns; ++i)
734   {
735     os << std::endl << "Key Expr " << i << " : " << std::endl;
736     theKeyExprs[i]->put(os);
737   }
738 
739   return os.str();
740 }
741 
742 
743 } // namespace zorba
744 /* vim:set et sw=2 ts=2: */
745