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