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 #pragma once
17 #ifndef ZORBA_RUNTIME_PLAN_ITERATOR
18 #define ZORBA_RUNTIME_PLAN_ITERATOR
19 
20 #include <stack>
21 
22 #include "common/shared_types.h"
23 
24 #include "diagnostics/assert.h"
25 
26 #include "runtime/util/flowctl_exception.h"
27 
28 #include "compiler/parser/query_loc.h"
29 
30 #include "zorbaserialization/class_serializer.h"
31 #include "zorbaserialization/serialize_template_types.h"
32 #include "zorbaserialization/serialize_zorba_types.h"
33 
34 
35 #if ZORBA_BATCHING_TYPE == 1
36 #include "store/api/item.h"
37 #endif
38 
39 
40 // Info: Forcing inlining a function in g++:
41 // store::Item_t next() __attribute__((always_inline)) {...}
42 
43 /*******************************************************************************
44 
45   Macros to automate Duff's Device and separation of code and state.
46 
47   These macros are used in the nextImpl() method of each iterator. In general,
48   the nextImpl() method will be called multiple times, and during each invocation,
49   its behaviour depends on the current state of the iterator (which includes
50   info about what the previous invocation(s) did). The Duff's device is a
51   techique by which the point where each invocation exited from is remembered,
52   and during the next invocation, execution of the method resumes at that point.
53   In general, this simplifies the implementation of the method.
54 
55   DEFUALT_STACK_INIT : Gets a pointer to the iterator's state in the state block
56                        and "starts" the duff's device.
57   STACK_PUSH         : Causes the nextImpl() method to return with the given
58                        status and saves the current line in the source file
59                        into the duffs line so that when the nextImpl method
60                        is called again, execution will start right after the
61                        STACK_PUSH macro.
62   STACK_END          : Causes the nextImpl() method to return with false,
63                        indicating that there are no more results to produce.
64                        It also saves the current line in the source file into
65                        the duffs line so that if the nextImpl method is called
66                        again, an exception will be raised.
67 ********************************************************************************/
68 
69 #define DEFAULT_STACK_INIT(stateType, stateObject, planState)           \
70   stateObject = StateTraitsImpl<stateType>::getState(planState, this->theStateOffset); \
71   switch (stateObject->getDuffsLine())                                  \
72   {                                                                     \
73   case PlanIteratorState::DUFFS_ALLOCATE_RESOURCES:
74 
75 #define STACK_PUSH(status, stateObject)                                 \
76   do                                                                    \
77   {                                                                     \
78     stateObject->setDuffsLine(__LINE__);                                \
79     return status;                                                      \
80   case __LINE__: ;                                                      \
81    } while (0)
82 
83 #define STACK_END( stateObject)                                         \
84   do {                                                                  \
85     stateObject->setDuffsLine(__LINE__);                                \
86     return false;                                                       \
87   case __LINE__:                                                        \
88     stateObject->setDuffsLine(__LINE__ + 1);                            \
89   case __LINE__ + 1:                                                    \
90     ZORBA_ASSERT (false && "nextImpl() called past iterator end");      \
91     return false;                                                       \
92   default:                                                              \
93     return false;                                                       \
94   } while (0);                                                          \
95   }
96 
97 
98 namespace zorba
99 {
100 
101 class RuntimeCB;
102 class PlanIterVisitor;
103 class dynamic_context;
104 class DebuggerCommons;
105 class XQueryImpl;
106 
107 
108 /*******************************************************************************
109   Class to represent state that is shared by all plan iterators.
110 
111   theBlock        : Pointer to the memory block that stores the local state of
112                     each individual plan iterator.
113   theBlockSize    : Size (in bytes) of the block.
114   theHasToQuit    : Boolean that indicates if the query execution has to quit.
115                     Checking this value is done in each consumeNext call,
116                     i.e. between every two iterator next calls. This value is
117                     set by the StateWrapper class (see runtime/util/timeout.h)
118                     after a user-defined timeout value is exceeded.
119 ********************************************************************************/
120 class PlanState
121 {
122 public:
123   int8_t                  * theBlock;
124 
125   uint32_t                  theBlockSize;
126 
127   uint32_t                  theStackDepth;
128 
129   uint32_t                  theMaxStackDepth;
130 
131   // TODO this guy should become const because nothing can change anymore during
132   // runtime. We need to make all accessor in the control block and static context
133   // (see also shortcut below) const before doing that
134   CompilerCB              * theCompilerCB;
135 
136   XQueryImpl              * theQuery;
137 
138   dynamic_context         * theGlobalDynCtx;
139 
140   dynamic_context         * theLocalDynCtx;
141 
142   std::stack<store::Item*>  theNodeConstuctionPath;
143 
144   DebuggerCommons         * theDebuggerCommons;
145 
146   bool                      theHasToQuit;
147 
148 public:
149   PlanState(
150       dynamic_context* globalDctx,
151       dynamic_context* localDctx,
152       uint32_t blockSize,
153       uint32_t aStackDepth = 0,
154       uint32_t aMaxStackDepth = 1024);
155 
156   ~PlanState();
157 
158   void checkDepth(const QueryLoc& loc);
159 };
160 
161 
162 /*******************************************************************************
163   Base class for all iterator state objects.
164 ********************************************************************************/
165 class PlanIteratorState
166 {
167 public:
168   static const uint32_t DUFFS_ALLOCATE_RESOURCES = 0;
169 
170 private:
171   uint32_t         theDuffsLine;
172 
173 public:
174 #if ZORBA_BATCHING_TYPE == 1
175 public:
176   uint32_t        theCurrItem;
177   store::Item_t   theBatch[ZORBA_BATCHING_BATCHSIZE];
178 #endif
179 #ifndef NDEBUG
180   bool            theIsOpened;
181 #endif
182 
183 public:
PlanIteratorState()184   PlanIteratorState()
185     :
186     theDuffsLine(DUFFS_ALLOCATE_RESOURCES)
187 #if ZORBA_BATCHING_TYPE == 1
188     , theCurrItem(ZORBA_BATCHING_BATCHSIZE)
189 #endif
190 #ifndef NDEBUG
191     , theIsOpened(false)
192 #endif
193   {}
194 
~PlanIteratorState()195   ~PlanIteratorState() {}
196 
setDuffsLine(uint32_t aVal)197   void setDuffsLine(uint32_t aVal) { theDuffsLine = aVal; }
198 
getDuffsLine()199   uint32_t getDuffsLine() const { return theDuffsLine; }
200 
201   /**
202    * Initialize the current state object.
203    *
204    * This method is invoked be the openImpl() method of the associated iterator
205    * to initialize the state info needed by the iterator. All initialization of
206    * such info should be done in this function. If resources are acquired during
207    * initialization, they must be released in the destructor.
208    *
209    * Classes that inherit from PlanIteratorState must reimplement this method.
210    * Each subclass implementation of this method must call the init() method of
211    * their parent class explicitly in order to guarantee proper initialization.
212    */
init(PlanState &)213   void init(PlanState&)
214   {
215     theDuffsLine = DUFFS_ALLOCATE_RESOURCES;
216 #if ZORBA_BATCHING_TYPE == 1
217     theCurrItem = ZORBA_BATCHING_BATCHSIZE;
218 #endif
219   }
220 
221   /**
222    * Reset the current state object.
223    *
224    * This method is invoked by the resetImpl() method of the associated iterator.
225    * It resets the state info so that when the nextImpl() method of the iterator
226    * is called again after a resetImpl(), it will behave as if it is called for
227    * the first time.
228    *
229    * Classes that inherit from PlanIteratorState must reimplement this method.
230    * Each subclass implementation of this method must call the reset() method
231    * of their parent class explicitly in order to guarantee proper reset.
232    */
reset(PlanState &)233   void reset(PlanState&)
234   {
235     theDuffsLine = DUFFS_ALLOCATE_RESOURCES;
236 #if ZORBA_BATCHING_TYPE == 1
237     theCurrItem = ZORBA_BATCHING_BATCHSIZE;
238     // seeting the first item to NULL only is sufficient so
239     // that produceNext knows that it's not finished yet
240     theBatch[0] = NULL;
241 #endif
242   }
243 };
244 
245 
246 /*******************************************************************************
247 
248 ********************************************************************************/
249 template <class T>
250 class StateTraitsImpl
251 {
252 private:
StateTraitsImpl()253   StateTraitsImpl() {} // prevent instantiation
254 
255 public:
getStateSize()256   static uint32_t getStateSize()
257   {
258     return sizeof(T);
259   }
260 
getState(PlanState & planState,uint32_t stateOffset)261   static T* getState(PlanState& planState, uint32_t stateOffset)
262   {
263     return reinterpret_cast<T*>(planState.theBlock + stateOffset);
264   }
265 
createState(PlanState & planState,uint32_t & stateOffset,uint32_t & offset)266   static void createState(PlanState& planState, uint32_t& stateOffset, uint32_t& offset)
267   {
268     stateOffset = offset;
269     offset += StateTraitsImpl<T>::getStateSize();
270     new (planState.theBlock + stateOffset)T();
271   }
272 
initState(PlanState & planState,uint32_t & stateOffset)273   static void initState(PlanState& planState, uint32_t& stateOffset)
274   {
275     getState(planState, stateOffset)->init(planState);
276   }
277 
reset(PlanState & planState,uint32_t stateOffset)278   static void reset(PlanState& planState, uint32_t stateOffset)
279   {
280     (reinterpret_cast<T*>(planState.theBlock+ stateOffset))->reset(planState);
281   }
282 
destroyState(PlanState & planState,uint32_t stateOffset)283   static void destroyState(PlanState& planState, uint32_t stateOffset)
284   {
285     (reinterpret_cast<T*>(planState.theBlock + stateOffset))->~T();
286   }
287 };
288 
289 #if ZORBA_BATCHING_TYPE == 1
290 
291 #error "Batching not implemented with the new iterator contract."
292 
293 #endif
294 
295 
296 /*******************************************************************************
297   Base class of all plan iterators.
298 ********************************************************************************/
299 class PlanIterator : public SimpleRCObject
300 {
301   friend class PlanIterWrapper;
302 
303 protected:
304   uint32_t           theStateOffset;
305 
306 public:
307   QueryLoc           loc;
308   static_context   * theSctx;
309 
310 public:
311   SERIALIZABLE_ABSTRACT_CLASS(PlanIterator);
312 
PlanIterator(zorba::serialization::Archiver & ar)313   PlanIterator(zorba::serialization::Archiver& ar)
314     :
315     SimpleRCObject(ar),
316     theStateOffset(0),
317     theSctx(NULL)
318   {
319   }
320 
321   void serialize(::zorba::serialization::Archiver& ar);
322 
323 public:
PlanIterator(static_context * aContext,const QueryLoc & aLoc)324   PlanIterator(static_context* aContext, const QueryLoc& aLoc)
325     :
326     theStateOffset(0),
327     loc(aLoc),
328     theSctx(aContext)
329   {
330   }
331 
PlanIterator(const PlanIterator & it)332   PlanIterator(const PlanIterator& it)
333     :
334     SimpleRCObject(it),
335     theStateOffset(0),
336     loc(it.loc),
337     theSctx(it.theSctx)
338   {
339   }
340 
~PlanIterator()341   virtual ~PlanIterator() {}
342 
setLocation(const QueryLoc & loc_)343   void setLocation(const QueryLoc& loc_) { loc = loc_; }
344 
getLocation()345   const QueryLoc& getLocation() const { return loc; }
346 
getStateOffset()347   uint32_t getStateOffset() const { return theStateOffset; }
348 
getStaticContext()349   static_context* getStaticContext() const { return theSctx; }
350 
351   TypeManager* getTypeManager() const;
352 
isConstructor()353   virtual bool isConstructor() const { return false; }
354 
355   /**
356    * Accept method for the PlanIterator-Tree-Visitor
357    *
358    * @param PlanIterVisitor
359    */
360   virtual void accept(PlanIterVisitor&) const = 0;
361 
362   /**
363    * Returns the size of the state which must be saved for the current iterator
364    * on the state block
365    */
366   virtual uint32_t getStateSize() const = 0;
367 
368   /**
369    * Returns the size of the state for this iterator and all its sub-iterators.
370    */
371   virtual uint32_t getStateSizeOfSubtree() const = 0;
372 
373   /**
374    * Begin the execution of the iterator. Initializes information required for
375    * the plan state and constructs the state object.
376    */
377   virtual void open(PlanState& planState, uint32_t& offset) = 0;
378 
379   /**
380    * Restarts the iterator so that the next 'produceNext' call will start
381    * again from the beginning (should not release any resources).
382    *
383    * @param planState
384    */
385   virtual void reset(PlanState& planState) const = 0;
386 
387   /**
388    * Finish the execution of the iterator. Releases all resources and destroys
389    * the according plan state* objects. Make sure that no exception is throw
390    * when destroying the states. Otherwise we will have a lot of memory leaks.
391    *
392    * @param planState
393    */
394   virtual void close(PlanState& planState) = 0;
395 
396 
397 #if ZORBA_BATCHING_TYPE == 1
398 
399   /**
400    * Produce the next batch of items and place them in the batch buffer.
401    * Implicitly, the first call  of producNext() initializes the iterator and
402    * allocates resources (main memory, file descriptors, etc.).
403    *
404    * @param stateBLock
405    */
406   virtual void produceNext(PlanState& planState) const = 0;
407 
408   /**
409    * Static Method: Retreives the next item from the batch buffer of the given
410    * iterator and returns it to the caller. If all the items from the iterator's
411    * batch buffer have been consumed already, then it makes the iterator produce
412    * its next batch of results and retrieves the 1st item from this new batch.
413    */
consumeNext(store::Item_t & result,const PlanIterator * iter,PlanState & planState)414   static store::Item_t consumeNext(
415         store::Item_t& result,
416         const PlanIterator* iter,
417         PlanState& planState)
418   {
419     try
420     {
421       // use the given iterator's planstate to access it's batch
422       PlanIteratorState* lState =
423       StateTraitsImpl<PlanIteratorState>::getState(planState, iter->getStateOffset());
424 
425       if ( lState->theCurrItem == ZORBA_BATCHING_BATCHSIZE )
426       {
427         iter->produceNext(planState);
428         lState->theCurrItem = 0;
429       }
430     }
431     catch(ZorbaException& e)
432     {
433       if(loc != NULL)
434       {
435         set_source(e, loc);
436         throw;
437       }
438     }
439 
440     return lState->theBatch[lState->theCurrItem++];
441   }
442 
443 
444 #else // ZORBA_BATCHING_TYPE
445 
446   /**
447    * Produce the next item and return it to the caller. Implicitly, the first
448    * call of 'producNext' initializes the iterator and allocates resources
449    * (main memory, file descriptors, etc.).
450    *
451    * @param stateBLock
452    */
453   virtual bool produceNext(store::Item_t& handle, PlanState& planState) const = 0;
454 
455   /**
456    * Static Method: Makes the given iterator produce its next result and returns
457    * that result to the caller.
458    */
459 #ifndef NDEBUG
460   static bool consumeNext(
461         store::Item_t& result,
462         const PlanIterator* iter,
463         PlanState& planState);
464 #else
consumeNext(store::Item_t & result,const PlanIterator * iter,PlanState & planState)465   static bool consumeNext(
466         store::Item_t& result,
467         const PlanIterator* iter,
468         PlanState& planState)
469   {
470     if (planState.theHasToQuit)
471     {
472       // Quit the execution
473       throw FlowCtlException(FlowCtlException::INTERRUPT);
474     }
475 
476     return iter->produceNext(result, planState);
477   }
478 #endif
479 
480 #endif // ZORBA_BATCHING_TYPE
481 };
482 
483 
484 /*******************************************************************************
485   Class to implement batching
486 ********************************************************************************/
487 template <class IterType>
488 class Batcher: public PlanIterator
489 {
490 public:
SERIALIZABLE_TEMPLATE_ABSTRACT_CLASS(Batcher)491   SERIALIZABLE_TEMPLATE_ABSTRACT_CLASS(Batcher)
492   SERIALIZABLE_CLASS_CONSTRUCTOR2(Batcher, PlanIterator)
493   void serialize(::zorba::serialization::Archiver& ar)
494   {
495     serialize_baseclass(ar, (PlanIterator*)this);
496   }
497 
498 public:
Batcher(const Batcher<IterType> & b)499   Batcher(const Batcher<IterType>& b)  : PlanIterator(b) {}
500 
Batcher(static_context * sctx,const QueryLoc & loc)501   Batcher(static_context* sctx, const QueryLoc& loc) : PlanIterator(sctx, loc) {}
502 
~Batcher()503   ~Batcher() {}
504 
505 
506 #if ZORBA_BATCHING_TYPE == 1
507 
produceNext(PlanState & planState)508   void produceNext(PlanState& planState) const
509   {
510     PlanIteratorState* lState =
511     StateTraitsImpl<PlanIteratorState>::getState(planState, stateOffset);
512 #ifndef NDEBUG
513     ZORBA_ASSERT(lState->theIsOpened);
514 #endif
515     bool more;
516     uint32_t i = 0;
517     do
518     {
519       // In general, to compute this iterator's next result, nextImpl() will
520       // call consumeNext() on one or more of this iterator's child iterators.
521       more = static_cast<const IterType*>(this)->nextImpl(lState->theBatch[i], planState);
522     }
523     while ( more && ++i < ZORBA_BATCHING_BATCHSIZE );
524   }
525 
526 #else
527 
produceNext(store::Item_t & result,PlanState & planState)528   bool produceNext(store::Item_t& result, PlanState& planState) const
529   {
530 #ifndef NDEBUG
531     PlanIteratorState* lState =
532     StateTraitsImpl<PlanIteratorState>::getState(planState, theStateOffset);
533     ZORBA_ASSERT(lState->theIsOpened);
534 #endif
535     return static_cast<const IterType*>(this)->nextImpl(result, planState);
536   }
537 
538 #endif // ZORBA_BATCHING_TYPE
539 
open(PlanState & planState,uint32_t & offset)540   void open(PlanState& planState, uint32_t& offset)
541   {
542     static_cast<IterType*>(this)->openImpl(planState, offset);
543 #ifndef NDEBUG
544     // do this after openImpl because the state is created there
545     PlanIteratorState* lState =
546     StateTraitsImpl<PlanIteratorState>::getState(planState, theStateOffset);
547     ZORBA_ASSERT( ! lState->theIsOpened ); // don't call open twice
548     lState->theIsOpened = true;
549 #endif
550   }
551 
reset(PlanState & planState)552   void reset(PlanState& planState) const
553   {
554 #ifndef NDEBUG
555     PlanIteratorState* lState =
556     StateTraitsImpl<PlanIteratorState>::getState(planState, theStateOffset);
557     ZORBA_ASSERT( lState->theIsOpened );
558 #endif
559     static_cast<const IterType*>(this)->resetImpl(planState);
560   }
561 
close(PlanState & planState)562   void close(PlanState& planState)
563   {
564 #ifndef NDEBUG
565     PlanIteratorState* lState =
566     StateTraitsImpl<PlanIteratorState>::getState(planState, theStateOffset);
567     static_cast<IterType*>(this)->closeImpl(planState);
568     lState->theIsOpened = false;
569 #else
570     static_cast<IterType*>(this)->closeImpl(planState);
571 #endif
572   }
573 
574   inline bool nextImpl(store::Item_t& result, PlanState& planState) const;
575 
576   inline void openImpl(PlanState& planState, uint32_t& offset) const;
577 
578   inline void resetImpl(PlanState& planState) const;
579 
580   inline void closeImpl(PlanState& planState);
581 };
582 
583 
584 } /* namespace zorba */
585 
586 #endif  /* ZORBA_ITERATOR_H */
587 
588 /*
589  * Local variables:
590  * mode: c++
591  * End:
592  */
593 /* vim:set et sw=2 ts=2: */
594