void RelSequence::addCancelExpr(CollHeap *wHeap) { ItemExpr *cPred = NULL; if (this->partition().entries() > 0) { return; } if(cancelExpr().entries() > 0) { return; } for(ValueId valId = selectionPred().init(); selectionPred().next(valId); selectionPred().advance(valId)) { ItemExpr *pred = valId.getItemExpr(); // Look for preds that select a prefix of the sequence. // Rank() < const; Rank <= const; const > Rank; const >= Rank ItemExpr *op1 = NULL; ItemExpr *op2 = NULL; if(pred->getOperatorType() == ITM_LESS || pred->getOperatorType() == ITM_LESS_EQ) { op1 = pred->child(0); op2 = pred->child(1); } else if (pred->getOperatorType() == ITM_GREATER || pred->getOperatorType() == ITM_GREATER_EQ) { op1 = pred->child(1); op2 = pred->child(0); } NABoolean negate; if (op1 && op2 && (op2->getOperatorType() == ITM_CONSTANT || op2->getOperatorType() == ITM_DYN_PARAM) && (op1->getOperatorType() == ITM_OLAP_RANK || op1->getOperatorType() == ITM_OLAP_DRANK || (op1->getOperatorType() == ITM_OLAP_COUNT && op1->child(0)->getOperatorType() == ITM_CONSTANT && !op1->child(0)->castToConstValue(negate)->isNull()))) { cPred = new(wHeap) UnLogic(ITM_NOT, pred); //break at first occurence break; } } if(cPred) { cPred->synthTypeAndValueId(TRUE); cancelExpr().insert(cPred->getValueId()); } }
short PhysSequence::codeGen(Generator *generator) { // Get a local handle on some of the generator objects. // CollHeap *wHeap = generator->wHeap(); Space *space = generator->getSpace(); ExpGenerator *expGen = generator->getExpGenerator(); MapTable *mapTable = generator->getMapTable(); // Allocate a new map table for this node. This must be done // before generating the code for my child so that this local // map table will be sandwiched between the map tables already // generated and the map tables generated by my offspring. // // Only the items available as output from this node will // be put in the local map table. Before exiting this function, all of // my offsprings map tables will be removed. Thus, none of the outputs // from nodes below this node will be visible to nodes above it except // those placed in the local map table and those that already exist in // my ancestors map tables. This is the standard mechanism used in the // generator for managing the access to item expressions. // MapTable *localMapTable = generator->appendAtEnd(); // Since this operation doesn't modify the row on the way down the tree, // go ahead and generate the child subtree. Capture the given composite row // descriptor and the child's returned TDB and composite row descriptor. // ex_cri_desc * givenCriDesc = generator->getCriDesc(Generator::DOWN); child(0)->codeGen(generator); ComTdb *childTdb = (ComTdb*)generator->getGenObj(); ex_cri_desc * childCriDesc = generator->getCriDesc(Generator::UP); ExplainTuple *childExplainTuple = generator->getExplainTuple(); // Make all of my child's outputs map to ATP 1. The child row is only // accessed in the project expression and it will be the second ATP // (ATP 1) passed to this expression. // localMapTable->setAllAtp(1); // My returned composite row has an additional tupp. // Int32 numberTuples = givenCriDesc->noTuples() + 1; ex_cri_desc * returnCriDesc #pragma nowarn(1506) // warning elimination = new (space) ex_cri_desc(numberTuples, space); #pragma warn(1506) // warning elimination // For now, the history buffer row looks just the return row. Later, // it may be useful to add an additional tupp for sequence function // itermediates that are not needed above this node -- thus, this // ATP is kept separate from the returned ATP. // const Int32 historyAtp = 0; const Int32 historyAtpIndex = numberTuples-1; #pragma nowarn(1506) // warning elimination ex_cri_desc *historyCriDesc = new (space) ex_cri_desc(numberTuples, space); #pragma warn(1506) // warning elimination ExpTupleDesc *historyDesc = 0; //seperate the read and retur expressions seperateReadAndReturnItems(wHeap); // The history buffer consists of items projected directly from the // child, the root sequence functions, the value arguments of the // offset functions, and running sequence functions. These elements must // be materialized in the history buffer in order to be able to compute // the outputs of this node -- the items projected directly from the child // (projectValues) and the root sequence functions (sequenceFunctions). // // Compute the set of sequence function items that must be materialized // int the history buffer. -- sequenceItems // // Compute the set of items in the history buffer: the union of the // projected values and the value arguments. -- historyIds // // Compute the set of items in the history buffer that are computed: // the difference between all the elements in the history buffer // and the projected items. -- computedHistoryIds // // KB---will need to return atp with 3 tups only 0,1 and 2 // 2 -->values from history buffer after ther are moved to it addCheckPartitionChangeExpr(generator, TRUE); ValueIdSet historyIds; historyIds += movePartIdsExpr(); historyIds += sequencedColumns(); ValueIdSet outputFromChild = child(0)->getGroupAttr()->getCharacteristicOutputs(); getHistoryAttributes(readSeqFunctions(),outputFromChild, historyIds, TRUE, wHeap); // Add in the top level sequence functions. historyIds += readSeqFunctions(); getHistoryAttributes(returnSeqFunctions(),outputFromChild, historyIds, TRUE, wHeap); // Add in the top level functions. historyIds += returnSeqFunctions(); // Layout the work tuple format which consists of the projected // columns and the computed sequence functions. First, compute // the number of attributes in the tuple. // ULng32 numberAttributes = ((NOT historyIds.isEmpty()) ? historyIds.entries() : 0); // Allocate an attribute pointer vector from the working heap. // Attributes **attrs = new(wHeap) Attributes*[numberAttributes]; // Fill in the attributes vector for the history buffer including // adding the entries to the map table. Also, compute the value ID // set for the elements to project from the child row. // //??????????re-visit this function?? computeHistoryAttributes(generator, localMapTable, attrs, historyIds); // Create the tuple descriptor for the history buffer row and // assign the offsets to the attributes. For now, this layout is // identical to the returned row. Set the tuple descriptors for // the return and history rows. // ULng32 historyRecLen; expGen->processAttributes(numberAttributes, attrs, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, historyRecLen, historyAtp, historyAtpIndex, &historyDesc, ExpTupleDesc::SHORT_FORMAT); NADELETEBASIC(attrs, wHeap); #pragma nowarn(1506) // warning elimination returnCriDesc->setTupleDescriptor(historyAtpIndex, historyDesc); #pragma warn(1506) // warning elimination #pragma nowarn(1506) // warning elimination historyCriDesc->setTupleDescriptor(historyAtpIndex, historyDesc); #pragma warn(1506) // warning elimination // If there are any sequence function items, generate the sequence // function expressions. // ex_expr * readSeqExpr = NULL; if(NOT readSeqFunctions().isEmpty()) { ValueIdSet seqVals = readSeqFunctions(); seqVals += sequencedColumns(); seqVals += movePartIdsExpr(); expGen->generateSequenceExpression(seqVals, readSeqExpr); } ex_expr *checkPartChangeExpr = NULL; if (!checkPartitionChangeExpr().isEmpty()) { ItemExpr * newCheckPartitionChangeTree= checkPartitionChangeExpr().rebuildExprTree(ITM_AND,TRUE,TRUE); expGen->generateExpr(newCheckPartitionChangeTree->getValueId(), ex_expr::exp_SCAN_PRED, &checkPartChangeExpr); } //unsigned long rowLength; ex_expr * returnExpr = NULL; if(NOT returnSeqFunctions().isEmpty()) { expGen->generateSequenceExpression(returnSeqFunctions(), returnExpr); } // Generate expression to evaluate predicate on the output // ex_expr *postPred = 0; if (! selectionPred().isEmpty()) { ItemExpr * newPredTree = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); expGen->generateExpr(newPredTree->getValueId(), ex_expr::exp_SCAN_PRED, &postPred); } // Reset ATP's to zero for parent. // localMapTable->setAllAtp(0); // Generate expression to evaluate the cancel expression // ex_expr *cancelExpression = 0; if (! cancelExpr().isEmpty()) { ItemExpr * newCancelExprTree = cancelExpr().rebuildExprTree(ITM_AND,TRUE,TRUE); expGen->generateExpr(newCancelExprTree->getValueId(), ex_expr::exp_SCAN_PRED, &cancelExpression); } // // For overflow // // ( The following are meaningless if ! unlimitedHistoryRows() ) NABoolean noOverflow = CmpCommon::getDefault(EXE_BMO_DISABLE_OVERFLOW) == DF_ON ; NABoolean logDiagnostics = CmpCommon::getDefault(EXE_DIAGNOSTIC_EVENTS) == DF_ON ; NABoolean possibleMultipleCalls = generator->getRightSideOfFlow() ; short scratchTresholdPct = (short) CmpCommon::getDefaultLong(SCRATCH_FREESPACE_THRESHOLD_PERCENT); // determione the memory usage (amount of memory as percentage from total // physical memory used to initialize data structures) unsigned short memUsagePercent = (unsigned short) getDefault(BMO_MEMORY_USAGE_PERCENT); short memPressurePct = (short)getDefault(GEN_MEM_PRESSURE_THRESHOLD); historyRecLen = ROUND8(historyRecLen); Lng32 maxNumberOfOLAPBuffers; Lng32 maxRowsInOLAPBuffer; Lng32 minNumberOfOLAPBuffers; Lng32 numberOfWinOLAPBuffers; Lng32 olapBufferSize; computeHistoryParams(historyRecLen, maxRowsInOLAPBuffer, minNumberOfOLAPBuffers, numberOfWinOLAPBuffers, maxNumberOfOLAPBuffers, olapBufferSize); ComTdbSequence *sequenceTdb = new(space) ComTdbSequence(readSeqExpr, returnExpr, postPred, cancelExpression, getMinFollowingRows(), #pragma nowarn(1506) // warning elimination historyRecLen, historyAtpIndex, childTdb, givenCriDesc, returnCriDesc, (queue_index)getDefault(GEN_SEQFUNC_SIZE_DOWN), (queue_index)getDefault(GEN_SEQFUNC_SIZE_UP), getDefault(GEN_SEQFUNC_NUM_BUFFERS), getDefault(GEN_SEQFUNC_BUFFER_SIZE), olapBufferSize, maxNumberOfOLAPBuffers, numHistoryRows(), getUnboundedFollowing(), logDiagnostics, possibleMultipleCalls, scratchTresholdPct, memUsagePercent, memPressurePct, maxRowsInOLAPBuffer, minNumberOfOLAPBuffers, numberOfWinOLAPBuffers, noOverflow, checkPartChangeExpr); #pragma warn(1506) // warning elimination generator->initTdbFields(sequenceTdb); // update the estimated value of HistoryRowLength with actual value //setEstHistoryRowLength(historyIds.getRowLength()); double sequenceMemEst = getEstimatedRunTimeMemoryUsage(sequenceTdb); generator->addToTotalEstimatedMemory(sequenceMemEst); if(!generator->explainDisabled()) { Lng32 seqMemEstInKBPerCPU = (Lng32)(sequenceMemEst / 1024) ; seqMemEstInKBPerCPU = seqMemEstInKBPerCPU/ (MAXOF(generator->compilerStatsInfo().dop(),1)); generator->setOperEstimatedMemory(seqMemEstInKBPerCPU); generator-> setExplainTuple(addExplainInfo(sequenceTdb, childExplainTuple, 0, generator)); generator->setOperEstimatedMemory(0); } sequenceTdb->setScratchIOVectorSize((Int16)getDefault(SCRATCH_IO_VECTOR_SIZE_HASH)); sequenceTdb->setOverflowMode(generator->getOverflowMode()); sequenceTdb->setBmoMinMemBeforePressureCheck((Int16)getDefault(EXE_BMO_MIN_SIZE_BEFORE_PRESSURE_CHECK_IN_MB)); if(generator->getOverflowMode() == ComTdb::OFM_SSD ) sequenceTdb->setBMOMaxMemThresholdMB((UInt16)(ActiveSchemaDB()-> getDefaults()). getAsLong(SSD_BMO_MAX_MEM_THRESHOLD_IN_MB)); else sequenceTdb->setBMOMaxMemThresholdMB((UInt16)(ActiveSchemaDB()-> getDefaults()). getAsLong(EXE_MEMORY_AVAILABLE_IN_MB)); // The CQD EXE_MEM_LIMIT_PER_BMO_IN_MB has precedence over the mem quota sys NADefaults &defs = ActiveSchemaDB()->getDefaults(); UInt16 mmu = (UInt16)(defs.getAsDouble(EXE_MEM_LIMIT_PER_BMO_IN_MB)); UInt16 numBMOsInFrag = (UInt16)generator->getFragmentDir()->getNumBMOs(); if (mmu != 0) sequenceTdb->setMemoryQuotaMB(mmu); else { // Apply quota system if either one the following two is true: // 1. the memory limit feature is turned off and more than one BMOs // 2. the memory limit feature is turned on NABoolean mlimitPerCPU = defs.getAsDouble(EXE_MEMORY_LIMIT_PER_CPU) > 0; if ( mlimitPerCPU || numBMOsInFrag > 1 ) { double memQuota = computeMemoryQuota(generator->getEspLevel() == 0, mlimitPerCPU, generator->getBMOsMemoryLimitPerCPU().value(), generator->getTotalNumBMOsPerCPU(), generator->getTotalBMOsMemoryPerCPU().value(), numBMOsInFrag, generator->getFragmentDir()->getBMOsMemoryUsage() ); sequenceTdb->setMemoryQuotaMB( UInt16(memQuota) ); } } generator->setCriDesc(givenCriDesc, Generator::DOWN); generator->setCriDesc(returnCriDesc, Generator::UP); generator->setGenObj(this, sequenceTdb); return 0; }
// PhysSequence::preCodeGen() ------------------------------------------- // Perform local query rewrites such as for the creation and // population of intermediate tables, for accessing partitioned // data. Rewrite the value expressions after minimizing the dataflow // using the transitive closure of equality predicates. // // PhysSequence::preCodeGen() - is basically the same as the RelExpr:: // preCodeGen() except that here we replace the VEG references in the // sortKey() list, as well as the selectionPred(). // // Parameters: // // Generator *generator // IN/OUT : A pointer to the generator object which contains the state, // and tools (e.g. expression generator) to generate code for // this node. // // ValueIdSet &externalInputs // IN : The set of external Inputs available to this node. // // RelExpr * PhysSequence::preCodeGen(Generator * generator, const ValueIdSet & externalInputs, ValueIdSet &pulledNewInputs) { if (nodeIsPreCodeGenned()) return this; // Resolve the VEGReferences and VEGPredicates, if any, that appear // in the Characteristic Inputs, in terms of the externalInputs. // getGroupAttr()->resolveCharacteristicInputs(externalInputs); // My Characteristic Inputs become the external inputs for my children. // ValueIdSet childPulledInputs; child(0) = child(0)->preCodeGen(generator, externalInputs, pulledNewInputs); if (! child(0).getPtr()) return NULL; // process additional input value ids the child wants getGroupAttr()->addCharacteristicInputs(childPulledInputs); pulledNewInputs += childPulledInputs; // The VEG expressions in the selection predicates and the characteristic // outputs can reference any expression that is either a potential output // or a characteristic input for this RelExpr. Supply these values for // rewriting the VEG expressions. // ValueIdSet availableValues; getInputValuesFromParentAndChildren(availableValues); // The sequenceFunctions() have access to only the Input // Values. These can come from the parent or be the outputs of the // child. // sequenceFunctions(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); sequencedColumns(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); requiredOrder(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); cancelExpr(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); partition(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); //checkPartitionChangeExpr(). // replaceVEGExpressions(availableValues, // getGroupAttr()->getCharacteristicInputs()); // The selectionPred has access to only the output values generated by // Sequence and input values from the parent. // getInputAndPotentialOutputValues(availableValues); // Rewrite the selection predicates. // NABoolean replicatePredicates = TRUE; selectionPred().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs(), FALSE, // no key predicates here 0 /* no need for idempotence here */, replicatePredicates ); // Replace VEG references in the outputs and remove redundant // outputs. // getGroupAttr()->resolveCharacteristicOutputs (availableValues, getGroupAttr()->getCharacteristicInputs()); addCancelExpr(generator->wHeap()); ///addCheckPartitionChangeExpr(generator->wHeap()); transformOlapFunctions(generator->wHeap()); if ( getUnboundedFollowing() ) { // Count this Seq as a BMO and add its needed memory to the total needed generator->incrNumBMOs(); if ((ActiveSchemaDB()->getDefaults()). getAsDouble(EXE_MEMORY_LIMIT_PER_CPU) > 0) generator->incrBMOsMemory(getEstimatedRunTimeMemoryUsage(TRUE)); } else generator->incrNBMOsMemoryPerCPU(getEstimatedRunTimeMemoryUsage(TRUE)); markAsPreCodeGenned(); return this; } // PhysSequence::preCodeGen
// work - doit... // // short ExSequenceTcb::work() { // If there are no parent requests on the queue, then there cannot // be anything to do here. // if (qparent_.down->isEmpty()) return WORK_OK; ex_queue_entry * pentry_down; ExSequencePrivateState * pstate; ex_queue::down_request request; // Take any new parent requests and pass them on to the child as long // as the child's queue is not full. processedInputs_ maintains the // Queue index of the last request that was passed on. // for(queue_index tail = qparent_.down->getTailIndex(); (processedInputs_ != tail) && (!qchild_.down->isFull()); processedInputs_++ ) { pentry_down = qparent_.down->getQueueEntry(processedInputs_); pstate = (ExSequencePrivateState*) pentry_down->pstate; request = pentry_down->downState.request; // If the request has already been cancelled don't pass it to the // child. Instead, just mark the request as done. This will trigger // a EOD reply when this request gets worked on. // if (request == ex_queue::GET_NOMORE) { // LCOV_EXCL_START pstate->step_ = ExSeq_DONE; // LCOV_EXCL_STOP } else { pstate->step_ = ExSeq_WORKING_READ; // Pass the request to the child // ex_queue_entry * centry = qchild_.down->getTailEntry(); centry->downState.request = ex_queue::GET_ALL; centry->downState.requestValue = 11; centry->downState.parentIndex = processedInputs_; centry->passAtp(pentry_down); qchild_.down->insert(); } } // end for processedInputs_ pentry_down = qparent_.down->getHeadEntry(); pstate = (ExSequencePrivateState*) pentry_down->pstate; request = pentry_down->downState.request; // Take any child replies and process them. Return the processed // rows as long the parent queue has room. // while (1) { // If we have satisfied the parent request (or it was cancelled), // then stop processing rows, cancel any outstanding child // requests, and set this request to the CANCELLED state. // if ((pstate->step_ == ExSeq_WORKING_READ) || (pstate->step_ == ExSeq_WORKING_RETURN)) { if ((request == ex_queue::GET_NOMORE) || ((request == ex_queue::GET_N) && (pentry_down->downState.requestValue <= (Lng32)pstate->matchCount_))) { qchild_.down->cancelRequestWithParentIndex (qparent_.down->getHeadIndex()); pstate->step_ = ExSeq_CANCELLED; } } switch (pstate->step_) { // ExSeq_CANCELLED // // Transition to this state from ... // 1. ExSeq_Error - After the error has been processed. // 2. ExSeq_Working - If enough rows have been returned. // 3. ExSeq_Working - If the request was cancelled. // // Remain in this state until .. // 1. All rows from the child including EOD are consumed // // Transition from this state to ... // 1. ExSeq_DONE - In all cases. // case ExSeq_CANCELLED: { // There are no extra rows to process from the child yet, // so try again later. // if (qchild_.up->isEmpty()) { return WORK_OK; } ex_queue_entry * centry = qchild_.up->getHeadEntry(); ex_queue::up_status child_status = centry->upState.status; // If this is the EOD, transition to the ExSeq_DONE state. // if (child_status == ex_queue::Q_NO_DATA) pstate->step_ = ExSeq_DONE; // Discard the child row. qchild_.up->removeHead(); break; } // ExSeq_ERROR // // Transition to this state from ... // 1. ExSeq_WORKING_READ - a child reply with the type SQLERROR. // 2. ExSeq_WORKING_RETURN // 3. ExSeq_OVERFLOW_READ // 4. ExSeq_OVERFLOW_WRITE // Remain in this state until .. // 1. The error row has been returned to the parent. // // Transition from this state to ... // 1. ExSeq_CANCELLED - In all cases. // case ExSeq_ERROR: { // If there is no room in the parent queue for the reply, // try again later. // if (qparent_.up->isFull()) // LCOV_EXCL_START return WORK_OK; // LCOV_EXCL_STOP ex_queue_entry *pentry_up = qparent_.up->getTailEntry(); // Cancel the child request - there must be a child request in // progress to get to the ExSeq_ERROR state. // qchild_.down->cancelRequestWithParentIndex (qparent_.down->getHeadIndex()); // Construct and return the error row. // if (workAtp_->getDiagsArea()) { ComDiagsArea * da = workAtp_->getDiagsArea(); pentry_up->setDiagsArea(da); da->incrRefCount(); workAtp_->setDiagsArea(0); } pentry_up->upState.status = ex_queue::Q_SQLERROR; pentry_up->upState.parentIndex = pentry_down->downState.parentIndex; pentry_up->upState.downIndex = qparent_.down->getHeadIndex(); pentry_up->upState.setMatchNo(pstate->matchCount_); qparent_.up->insert(); // Transition to the ExSeq_CANCELLED state. // pstate->step_ = ExSeq_CANCELLED; break; } // ExSeq_WORKING_READ // // Transition to this state from ... // 1. ExSeq_EMPTY - If a request is started. // 2. ExSeq_WORKING_RETURN - // 3. ExSeq_OVERFLOW_WRITE - // Remain in this state until ... // 1. All child replies including EOD have been processed. // 2. A SQLERROR row is received. // 3. Enough rows have been returned. // 4. The request is cancelled. // 5. End of partition is reached // Transition from this state to ... // 2. ExSeq_ERROR - If an SQLERROR rows is received. // 3. ExSeq_CANCELLED - If the request is cancelled. // 4. ExSeq_WORKING_RETURN // 5. ExSeq_OVERFLOW_WRITE - case ExSeq_WORKING_READ: { if(!isUnboundedFollowing() && isHistoryFull()) { pstate->step_ = ExSeq_WORKING_RETURN; break; } // If there are no replies, try again later. // if (qchild_.up->isEmpty()) return WORK_OK; ex_queue_entry * centry = qchild_.up->getHeadEntry(); switch (centry->upState.status) { // A data row from the child. // case ex_queue::Q_OK_MMORE: { tupp_descriptor histTupp; workAtp_->copyAtp(pentry_down->getAtp()); workAtp_->getTupp(myTdb().tuppIndex_) = &histTupp; if ( checkPartitionChangeExpr() && currentHistRowPtr_) { workAtp_->getTupp (myTdb().tuppIndex_).setDataPointer(currentHistRowPtr_); // Check whether the partition changed ex_expr::exp_return_type retCode = checkPartitionChangeExpr()->eval(workAtp_, centry->getAtp()); if (retCode == ex_expr::EXPR_ERROR) { // LCOV_EXCL_START updateDiagsArea(centry); pstate->step_ = ExSeq_ERROR; break; // LCOV_EXCL_STOP } if ( retCode == ex_expr::EXPR_FALSE) { setPartitionEnd(TRUE); pstate->step_ = ExSeq_END_OF_PARTITION; break; } } if (isUnboundedFollowing() ) { if (OLAPBuffersFlushed_) { OLAPBuffersFlushed_ = FALSE;// current row is the first one in first buffer already } else { NABoolean noMemory = advanceHistoryRow( TRUE /* checkMemoryPressure */); if (noMemory) { pstate->step_ = ExSeq_OVERFLOW_WRITE; cluster_->nextBufferToFlush_ = firstOLAPBuffer_; cluster_->afterLastBufferToFlush_ = NULL;//flush them all // If it is the first overflow, for this partition if ( ! memoryPressureDetected_ ) { memoryPressureDetected_ = TRUE; } // memory pressure detected break; } } } else { advanceHistoryRow(); } workAtp_->getTupp (myTdb().tuppIndex_).setDataPointer(currentHistRowPtr_); ex_expr::exp_return_type retCode = ex_expr::EXPR_OK; // Apply the read phase sequence function expression to compute // the values of the sequence functions. if (sequenceExpr()) { retCode = sequenceExpr()->eval(workAtp_, centry->getAtp()); if (retCode == ex_expr::EXPR_ERROR) { updateDiagsArea(centry); pstate->step_ = ExSeq_ERROR; break; } } // merge the child's diags area into the work atp updateDiagsArea(centry); qchild_.up->removeHead(); break; } // The EOD from the child. Transition to ExSeq_DONE. // case ex_queue::Q_NO_DATA: { setPartitionEnd(TRUE); if (isHistoryEmpty()) { pstate->step_ = ExSeq_DONE; qchild_.up->removeHead(); } else { pstate->step_ = ExSeq_END_OF_PARTITION; } } break; // An SQLERROR from the child. Transition to ExSeq_ERROR. // case ex_queue::Q_SQLERROR: updateDiagsArea(centry); pstate->step_ = ExSeq_ERROR; break; } } break; // ExSeq_WORKING_RETURN // // Transition to this state from ... // 1. ExSeq_WORKING_READ - // 2. ExSeq_OVERFLOW_READ - // 3. ExSeq_END_OF_PARTITION - // Remain in this state until ... // 1. All rows are returned. // 2. A SQLERROR row is received. // 3. Enough rows have been returned. // // Transition from this state to ... // 1. ExSeq_DONE - If all the child rows including EOD have // been processed. // 2. ExSeq_ERROR - If an SQLERROR rows is received. // 3. ExSeq_CANCELLED - If enough rows have been returned. // 4. ExSeq_CANCELLED - If the request is cancelled. // 5. ExSeq_WORKING_RETURN // 6. ExSeq_DONE // 7. ExSeq_OVERFLOW_READ case ExSeq_WORKING_RETURN: { // If there is not room in the parent Queue for the reply, // try again later. // if (qparent_.up->isFull()) return WORK_OK; if(isHistoryEmpty()) { ex_queue_entry * centry = NULL; if(!qchild_.up->isEmpty()) { centry = qchild_.up->getHeadEntry(); } if(centry && (centry->upState.status == ex_queue::Q_NO_DATA)) { pstate->step_ = ExSeq_DONE; qchild_.up->removeHead(); } else { pstate->step_ = ExSeq_WORKING_READ; if (getPartitionEnd()) { initializeHistory(); } } break; } if(!canReturnRows() && !getPartitionEnd() && !isUnboundedFollowing() && !isOverflowStarted()) // redundant? because not unbounded ... { pstate->step_ = ExSeq_WORKING_READ; break; } ex_queue_entry * pentry_up = qparent_.up->getTailEntry(); pentry_up->copyAtp(pentry_down); // Try to allocate a tupp. // if (pool_->get_free_tuple(pentry_up->getTupp(myTdb().tuppIndex_), recLen())) // LCOV_EXCL_START return WORK_POOL_BLOCKED; // LCOV_EXCL_STOP char *tuppData = pentry_up->getTupp (myTdb().tuppIndex_).getDataPointer(); advanceReturnHistoryRow(); char *histData = currentRetHistRowPtr_; pentry_up->getTupp (myTdb().tuppIndex_).setDataPointer(histData); ex_expr::exp_return_type retCode = ex_expr::EXPR_OK; // Apply the return phase expression if(returnExpr()) { retCode = returnExpr()->eval(pentry_up->getAtp(),workAtp_); if (retCode == ex_expr::EXPR_ERROR) { // LCOV_EXCL_START pstate->step_ = ExSeq_ERROR; break; // LCOV_EXCL_STOP } } retCode = ex_expr::EXPR_OK; //Apply post predicate expression if (postPred()) { retCode = postPred()->eval(pentry_up->getAtp(),pentry_up->getAtp()); if (retCode == ex_expr::EXPR_ERROR) { // LCOV_EXCL_START pstate->step_ = ExSeq_ERROR; break; // LCOV_EXCL_STOP } } //pentry_up->getAtp()->display("return eval result", myTdb().getCriDescUp()); // // Case-10-030724-7963: we are done pointing the tupp at the // history buffer, so point it back to the SQL buffer. // pentry_up->getTupp (myTdb().tuppIndex_).setDataPointer(tuppData); switch(retCode) { case ex_expr::EXPR_OK: case ex_expr::EXPR_TRUE: case ex_expr::EXPR_NULL: // Copy the row that was computed in the history buffer, // to the space previously allocated in the SQL buffer. str_cpy_all(tuppData, histData, recLen()); // Return the processed row. // // Finalize the queue entry, then insert it // pentry_up->upState.status = ex_queue::Q_OK_MMORE; pentry_up->upState.parentIndex = pentry_down->downState.parentIndex; pentry_up->upState.downIndex = qparent_.down->getHeadIndex(); pstate->matchCount_++; pentry_up->upState.setMatchNo(pstate->matchCount_); qparent_.up->insert(); break; // If the selection predicate returns FALSE, // do not return the child row. // case ex_expr::EXPR_FALSE: break; // If the selection predicate returns an ERROR, // go to the error processing state. // case ex_expr::EXPR_ERROR: // LCOV_EXCL_START pstate->step_ = ExSeq_ERROR; // LCOV_EXCL_STOP break; } // MV -- // Now, if there are no errors so far, evaluate the // cancel expression if ((pstate->step_ != ExSeq_ERROR) && cancelExpr()) { // Temporarily point the tupp to the tail of the // history buffer for evaluating the // expressions. // pentry_up->getTupp (myTdb().tuppIndex_).setDataPointer(histData); retCode = cancelExpr()->eval(pentry_up->getAtp(),pentry_up->getAtp()); // We are done pointing the tupp at the history // buffer, so point it back to the SQL buffer. // pentry_up->getTupp (myTdb().tuppIndex_).setDataPointer(tuppData); if (retCode == ex_expr::EXPR_TRUE) { qchild_.down->cancelRequestWithParentIndex (qparent_.down->getHeadIndex()); pstate->step_ = ExSeq_CANCELLED; } } updateHistRowsToReturn(); if ( isOverflowStarted() ) { numberOfRowsReturnedBeforeReadOF_ ++; if (numberOfRowsReturnedBeforeReadOF_ == maxNumberOfRowsReturnedBeforeReadOF_) { firstOLAPBufferFromOF_ = currentRetOLAPBuffer_->getNext(); if (firstOLAPBufferFromOF_ == NULL) { firstOLAPBufferFromOF_ = firstOLAPBuffer_; } for( Int32 i = 0; i < numberOfWinOLAPBuffers_; i++) { firstOLAPBufferFromOF_ = firstOLAPBufferFromOF_->getNext(); if (firstOLAPBufferFromOF_ == NULL) { firstOLAPBufferFromOF_ = firstOLAPBuffer_; } } numberOfOLAPBuffersFromOF_ = numberOfOLAPBuffers_ - numberOfWinOLAPBuffers_; cluster_->nextBufferToRead_ = firstOLAPBufferFromOF_; HashBuffer * afterLast = firstOLAPBufferFromOF_; // last buffer to read into is the current buffer - maybe ? for ( Lng32 bufcount = numberOfOLAPBuffersFromOF_ ; bufcount ; bufcount-- ) { afterLast = afterLast->getNext() ; // Don't cycle back if bufcount == 1 because the logic in // Cluster::read relies on the NULL ptr to stop reading if ( bufcount > 1 && ! afterLast ) afterLast = firstOLAPBuffer_; // cycle back } // The last buffer to read to is always the current buffer // ex_assert ( afterLast == currentRetOLAPBuffer_->getNext(), // "Miscalculated the last buffer to read into"); cluster_->afterLastBufferToRead_ = afterLast; pstate->step_ = ExSeq_OVERFLOW_READ; } } } break; // ExSeq_END_OF_PARTITION // // Transition to this state from ... // 1. ExSeq_WORKING_READ - // Transition from this state to ... // 1. ExSeq_OVERFLOW_WRITE // 2. ExSeq_WORKING_RETURN case ExSeq_END_OF_PARTITION: { setPartitionEnd(TRUE); if (lastRow_ && isUnboundedFollowing()) { ex_assert(currentHistRowPtr_ != NULL, "ExSequenceTcb::work() - currentHistRowPtr_ is a NULL pointer"); str_cpy_all(lastRow_, currentHistRowPtr_, recLen()); } if ( isOverflowStarted() ) // we are overflowing { cluster_->nextBufferToFlush_ = firstOLAPBuffer_; // do not flush beyond the current buffer cluster_->afterLastBufferToFlush_ = currentOLAPBuffer_->getNext(); pstate->step_ = ExSeq_OVERFLOW_WRITE; } else { pstate->step_ = ExSeq_WORKING_RETURN; } } break; // ExSeq_OVERFLOW_WRITE // // Transition to this state from ... // 1. ExSeq_WORKING_READ - // 2. ExSeq_END_OF_PARTITION - // Remain in this state until ... // 1. OLAPbuffers are written to oveflow space. // 2. An error occurs // // Transition from this state to ... // 1. ExSeq_OVERFLOW_READ // 2. ExSeq_ERROR - If an error occurs case ExSeq_OVERFLOW_WRITE: { if (!overflowEnabled_) { // LCOV_EXCL_START // used for debugging when CmpCommon::getDefault(EXE_BMO_DISABLE_OVERFLOW)is set to off ; updateDiagsArea(EXE_OLAP_OVERFLOW_NOT_SUPPORTED); pstate->step_ = ExSeq_ERROR; break; // LCOV_EXCL_STOP } ex_assert(isUnboundedFollowing(),""); if ( ! cluster_->flush(&rc_) ) { // flush the buffers // LCOV_EXCL_START // if no errors this code path is not visited if ( rc_ ) { // some error updateDiagsArea( rc_); pstate->step_ = ExSeq_ERROR; break; } // LCOV_EXCL_STOP // not all the buffers are completely flushed. An I/O is pending // LCOV_EXCL_START // maybe we cane remove in the future return WORK_OK; // LCOV_EXCL_STOP } // At this point -- all the buffers were completely flushed OLAPBuffersFlushed_ = TRUE; if (getPartitionEnd()) { firstOLAPBufferFromOF_ = firstOLAPBuffer_; numberOfOLAPBuffersFromOF_ = numberOfOLAPBuffers_; cluster_->nextBufferToRead_ = firstOLAPBufferFromOF_; // First time we read and fill all the buffers cluster_->afterLastBufferToRead_ = NULL; pstate->step_ = ExSeq_OVERFLOW_READ; } else { pstate->step_ = ExSeq_WORKING_READ; } } break; // ExSeq_OVERFLOW_READ // // Transition to this state from ... // 1. ExSeq_OVERFLOW_WRITE // 2. ExSeq_WORKING_RETURN // Remain in this state until ... // 1. OLAPbuffers are read from oveflow space. // 2. An error occurs // // Transition from this state to ... // 1. ExSeq_WORKING_RETURN // 2. ExSeq_ERROR - If an error occurs case ExSeq_OVERFLOW_READ: { assert(firstOLAPBufferFromOF_ && isUnboundedFollowing() ); if ( ! cluster_->read(&rc_) ) { // LCOV_EXCL_START if ( rc_ ) { // some error updateDiagsArea( rc_); pstate->step_ = ExSeq_ERROR; break; } // LCOV_EXCL_STOP // not all the buffers are completely read. An I/O is pending // LCOV_EXCL_START return WORK_OK; // LCOV_EXCL_STOP } numberOfRowsReturnedBeforeReadOF_ = 0; pstate->step_ = ExSeq_WORKING_RETURN; } break; // ExSeq_DONE // // Transition to the state from ... // 1. ExSeq_WORKING_RETURN - if all child rows have been processed. // 2. ExSeq_CANCELLED - if all child rows have been consumed. // 3. ExSeq_EMPTY - if the request was DOA. // // Remain in this state until ... // 1. The EOD is returned to the parent. // // Transition from this state to ... // 1. ExSeq_EMPTY - In all cases. // case ExSeq_DONE: { // If there is not any room in the parent's queue, // try again later. // if (qparent_.up->isFull()) // LCOV_EXCL_START return WORK_OK; // LCOV_EXCL_STOP ex_queue_entry * pentry_up = qparent_.up->getTailEntry(); pentry_up->upState.status = ex_queue::Q_NO_DATA; pentry_up->upState.parentIndex = pentry_down->downState.parentIndex; pentry_up->upState.downIndex = qparent_.down->getHeadIndex(); pentry_up->upState.setMatchNo(pstate->matchCount_); qparent_.down->removeHead(); qparent_.up->insert(); // Re-initialize pstate // pstate->step_ = ExSeq_EMPTY; pstate->matchCount_ = 0; workAtp_->release(); // Initialize the history buffer in preparation for the // next request. // initializeHistory(); // If there are no more requests, simply return. // if (qparent_.down->isEmpty()) return WORK_OK; // LCOV_EXCL_START // If we haven't given to our child the new head // index return and ask to be called again. // if (qparent_.down->getHeadIndex() == processedInputs_) return WORK_CALL_AGAIN; // Position at the new head of the request queue. // pentry_down = qparent_.down->getHeadEntry(); pstate = (ExSequencePrivateState*) pentry_down->pstate; request = pentry_down->downState.request; // LCOV_EXCL_STOP } break; } // switch pstate->step_ } // while }
// ExSequenceTcb constructor // // 1. Allocate buffer pool. // 2. Allocate parent queues and initialize private state. // 3. Fixup expressions. // ExSequenceTcb::ExSequenceTcb (const ExSequenceTdb & myTdb, const ex_tcb & child_tcb, ex_globals * glob) : ex_tcb(myTdb, 1, glob), lastRow_(NULL), clusterDb_(NULL), cluster_(NULL), ioEventHandler_(NULL), OLAPBuffersFlushed_(FALSE), firstOLAPBuffer_(NULL), lastOLAPBuffer_(NULL), rc_(EXE_OK), olapBufferSize_(0), maxNumberOfOLAPBuffers_(0), numberOfOLAPBuffers_(0), minNumberOfOLAPBuffers_(0), memoryPressureDetected_(FALSE) { Space * space = (glob ? glob->getSpace() : 0); CollHeap * heap = (glob ? glob->getDefaultHeap() : 0); heap_ = heap; childTcb_ = &child_tcb; // Allocate the buffer pool #pragma nowarn(1506) // warning elimination pool_ = new(space) sql_buffer_pool(myTdb.numBuffers_, myTdb.bufferSize_, space); allocRowLength_ = ROUND8(myTdb.recLen_); #pragma warn(1506) // warning elimination // Initialize the machinery for maintaining the row history for // computing sequence functions. // maxNumberHistoryRows_ = myTdb.maxHistoryRows_; minFollowing_ = myTdb.minFollowing_; unboundedFollowing_ = myTdb.isUnboundedFollowing(); maxNumberOfOLAPBuffers_ = myTdb.maxNumberOfOLAPBuffers_;//testing olapBufferSize_ = myTdb.OLAPBufferSize_ ; maxRowsInOLAPBuffer_ = myTdb.maxRowsInOLAPBuffer_; minNumberOfOLAPBuffers_ = myTdb.minNumberOfOLAPBuffers_; numberOfWinOLAPBuffers_ = myTdb.numberOfWinOLAPBuffers_; overflowEnabled_ = ! myTdb.isNoOverflow(); ex_assert( maxNumberOfOLAPBuffers_ >= minNumberOfOLAPBuffers_ , "maxNumberOfOLAPBuffers is too small"); // Initialize history parameters // For unbounded following -- also create/initialize clusterDb, cluster initializeHistory(); // get the queue that child use to communicate with me qchild_ = child_tcb.getParentQueue(); // Allocate the queue to communicate with parent qparent_.down = new(space) ex_queue(ex_queue::DOWN_QUEUE, myTdb.initialQueueSizeDown_, myTdb.criDescDown_, space); // Allocate the private state in each entry of the down queue ExSequencePrivateState *p = new(space) ExSequencePrivateState(this); qparent_.down->allocatePstate(p, this); delete p; qparent_.up = new(space) ex_queue(ex_queue::UP_QUEUE, myTdb.initialQueueSizeUp_, myTdb.criDescUp_, space); // Intialized processedInputs_ to the next request to process processedInputs_ = qparent_.down->getTailIndex(); workAtp_ = allocateAtp(myTdb.criDescUp_, space); // Fixup the sequence function expression. This requires the standard // expression fixup plus initializing the GetRow method for the sequence // clauses. // if (sequenceExpr()) { ((ExpSequenceExpression*)sequenceExpr())->seqFixup ((void*)this, GetHistoryRow, GetHistoryRowOLAP); sequenceExpr()->fixup(0, getExpressionMode(), this, space, heap_, FALSE, glob); } if (returnExpr()) { ((ExpSequenceExpression*)returnExpr())->seqFixup ((void*)this, GetHistoryRow, GetHistoryRowFollowingOLAP); returnExpr()->fixup(0, getExpressionMode(), this, space, heap_, FALSE, glob); } if (postPred()) postPred()->fixup(0, getExpressionMode(), this, space, heap_, FALSE, glob); if (cancelExpr()) cancelExpr()->fixup(0, getExpressionMode(), this, space, heap_, FALSE, glob); if (checkPartitionChangeExpr()) { ((ExpSequenceExpression*)checkPartitionChangeExpr())->seqFixup ((void*)this, GetHistoryRow, GetHistoryRowOLAP); checkPartitionChangeExpr()->fixup(0, getExpressionMode(), this, space, heap_, FALSE, glob); } }