// ExTransposeTcb::stop() ---------------------------------------------- // All child rows have been returned. // Now return an EOD indication to parent. // void ExTransposeTcb::stop() { // Remove the head entries of the parent down queue and the // child up queue. All checks are done by the caller. // ex_queue_entry *pEntryDown = qParent_.down->getHeadEntry(); ExTransposePrivateState &pState = *((ExTransposePrivateState*) pEntryDown->pstate); ex_queue_entry *pEntry = qParent_.up->getTailEntry(); pEntry->upState.status = ex_queue::Q_NO_DATA; pEntry->upState.parentIndex = pEntryDown->downState.parentIndex; if (getStatsEntry()) { getStatsEntry()->setActualRowsReturned(pState.matchCount_); } // insert EOD into parent // qParent_.up->insert(); // consume the child row // childQueue_.up->removeHead(); // Reinitialize the state of this processed request. // Pstate will be initialized for when this entry gets used again. // pState.init(); // this parent request has been processed. // qParent_.down->removeHead(); }
// this method find the first set of children in the child tree // that have a valid stats area and sets their parent id to the // input tdb id. void ex_tcb::propagateTdbIdForStats(Lng32 tdbId) { Int32 nc = numChildren(); ExOperStats *stat, *currentStat; currentStat = getStatsEntry(); for (Int32 i=0; i < nc; i++) { if (getChild(i)) { stat = ((ex_tcb*)getChild(i))->getStatsEntry(); if (stat != NULL) { // If the parent tdb id is already set in the by the doAllocateStatsEntry at the time // of building the tcb, donot set parentTdbId, leftChildId and rightChildId // tdb ::build methods need to set all these Ids correctly // If the rightChildTdb is set to -2 then this function just resets the // right child to -1 even there is a right child (conceptually) like in ex_split_top if (stat->getParentTdbId() == -1) { stat->setParentTdbId(tdbId); } if (currentStat && currentStat->getParentTdbId() == -1) { if (i == 0) currentStat->setLeftChildTdbId(getChild(i)->getTdb()->getTdbId()); if (i == 1) { if (currentStat->getRightChildTdbId() == -2) currentStat->setRightChildTdbId(-1); else currentStat->setRightChildTdbId(getChild(i)->getTdb()->getTdbId()); } } } } } }
//////////////////////////////////////////////////////////////////////////// // This is where the action is. //////////////////////////////////////////////////////////////////////////// short ex_sort_grby_tcb::work() { // if no parent request, return if (qparent_.down->isEmpty()) return WORK_OK; ex_queue_entry * pentry_down; ex_sort_grby_private_state * pstate; ex_queue::down_request request; queue_index tail = qparent_.down->getTailIndex(); for( ; (processedInputs_ != tail) && (!qchild_.down->isFull()); processedInputs_++ ) { pentry_down = qparent_.down->getQueueEntry(processedInputs_); pstate = (ex_sort_grby_private_state*) pentry_down->pstate; request = pentry_down->downState.request; ex_assert(pstate->step_ == ex_sort_grby_tcb::SORT_GRBY_EMPTY, "Invalid initial state in ex_sort_grby_tcb::work()"); // if request has been cancelled don't bother child if (request == ex_queue::GET_NOMORE) { pstate->step_ = SORT_GRBY_NEVER_STARTED; } else { ex_queue_entry * centry = qchild_.down->getTailEntry(); if ((sort_grby_tdb().firstNRows() >= 0) && ((pentry_down->downState.request != ex_queue::GET_N) || (pentry_down->downState.requestValue == sort_grby_tdb().firstNRows()))) { centry->downState.request = ex_queue::GET_N; centry->downState.requestValue = sort_grby_tdb().firstNRows(); } else { centry->downState.request = ex_queue::GET_ALL; centry->downState.requestValue = 11; } centry->downState.parentIndex = processedInputs_; pstate->oneRowAggr_ = FALSE; // set the child's input atp centry->passAtp(pentry_down->getAtp()); qchild_.down->insert(); pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_NEW_GROUP; } } // end for processedInputs_ pentry_down = qparent_.down->getHeadEntry(); pstate = (ex_sort_grby_private_state*) pentry_down->pstate; request = pentry_down->downState.request; while (1) // exit via return { // if we have already given to the parent all the rows needed cancel the // parent's request. Also cancel it if the parent cancelled if ((pstate->step_ != ex_sort_grby_tcb::SORT_GRBY_CANCELLED) && (pstate->step_ != ex_sort_grby_tcb::SORT_GRBY_DONE) && (pstate->step_ != ex_sort_grby_tcb::SORT_GRBY_NEVER_STARTED) && ((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_ = ex_sort_grby_tcb::SORT_GRBY_CANCELLED; } switch (pstate->step_) { case ex_sort_grby_tcb::SORT_GRBY_CANCELLED: { // request was cancelled. Child was sent a cancel // request. Ignore all up rows from child. // Wait for EOF. if (qchild_.up->isEmpty()) { // nothing returned from child. Get outta here. return WORK_OK; } ex_queue_entry * centry = qchild_.up->getHeadEntry(); ex_assert(centry->upState.parentIndex == qparent_.down->getHeadIndex(), "ex_sort_grby_tcb::work() child's reply out of sync"); ex_queue::up_status child_status = centry->upState.status; switch(child_status) { case ex_queue::Q_OK_MMORE: case ex_queue::Q_SQLERROR: { // just consume the child row qchild_.up->removeHead(); } break; case ex_queue::Q_NO_DATA: { // return EOF to parent. pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_DONE; } break; case ex_queue::Q_INVALID: ex_assert(0,"ex_sort_grby_tcb::work() Invalid state returned by child"); break; }; // end of switch on status of child queue } // request was cancelled break; case ex_sort_grby_tcb::SORT_GRBY_NEW_GROUP: { // Start of a new group. // // If grouping is being done and a row is returned from child, // create space for the grouped/aggregated row and move the // grouping column value to it. // // If grouping is not being done, create space for the // aggregated row and initialize it. // if (qchild_.up->isEmpty()) { // nothing returned from child. Get outta here. return WORK_OK; } ex_queue_entry * centry = qchild_.up->getHeadEntry(); ex_assert(centry->upState.parentIndex == qparent_.down->getHeadIndex(), "ex_sort_grby_tcb::work() child's reply out of sync"); if (centry->upState.status == ex_queue::Q_SQLERROR) { pstate->step_ = SORT_GRBY_CHILD_ERROR; break; } if ((!grbyExpr()) || ((grbyExpr()) && (centry->upState.status == ex_queue::Q_OK_MMORE))) { // copy the down atp to the work atp for the current request workAtp_->copyAtp(pentry_down->getAtp()); // get space to hold the grouped/aggregated row. if (pool_->getFreeTuple(workAtp_->getTupp( ((ex_sort_grby_tdb &)tdb).tuppIndex_))) return WORK_POOL_BLOCKED; // couldn't allocate, try again. // move the group by value to this new row, if grouping // is being done. if (grbyExpr()) { if (moveExpr()->eval(centry->getAtp(), workAtp_) == ex_expr::EXPR_ERROR) { pstate->step_ = SORT_GRBY_LOCAL_ERROR; break; } } // initialize the aggregate if (aggrExpr()) { if (((AggrExpr *)aggrExpr())->initializeAggr(workAtp_) == ex_expr::EXPR_ERROR) { pstate->step_ = SORT_GRBY_LOCAL_ERROR; break; } // a flag in the expression is set at compilation time // if this is a single row aggregate pstate->oneRowAggr_ = ((AggrExpr *)(aggrExpr()))->isOneRowAggr(); } // if group by not being done(scalar aggrs) and no rows were // found, move in null value for those aggrs that return // null on an empty set (like, min, sum...). if ((!grbyExpr()) && (centry->upState.status == ex_queue::Q_NO_DATA)) { if (((AggrExpr *)aggrExpr())->finalizeNullAggr(workAtp_) == ex_expr::EXPR_ERROR) { pstate->step_ = SORT_GRBY_LOCAL_ERROR; break; } } pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_STARTED; } else { // no rows returned for a group by. Nothing to be returned(except, // of course, Q_NO_DATA). pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_DONE; } } // start of a new group break; case ex_sort_grby_tcb::SORT_GRBY_STARTED: { if (qchild_.up->isEmpty()) { // nothing returned from child. Get outta here. return WORK_OK; } ex_queue_entry * centry = qchild_.up->getHeadEntry(); ex_assert(centry->upState.parentIndex == qparent_.down->getHeadIndex(), "ex_sort_grby_tcb::work() child's reply out of sync"); ex_queue::up_status child_status = centry->upState.status; switch(child_status) { case ex_queue::Q_OK_MMORE: { // row returned from child. Aggregate it. // If the new child row belongs // to this group, then aggregate it. ex_expr::exp_return_type grbyExprRetCode; if(grbyExpr()) { grbyExprRetCode = grbyExpr()->eval(centry->getAtp(), workAtp_); } if (grbyExpr() && ((grbyExprRetCode == ex_expr::EXPR_FALSE) || (grbyExprRetCode == ex_expr::EXPR_ERROR))) { if (grbyExprRetCode == ex_expr::EXPR_FALSE) pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_FINALIZE; else { pstate->step_ = SORT_GRBY_LOCAL_ERROR; } } else { // aggregate the row. if (aggrExpr()) { ex_expr::exp_return_type retcode; retcode = aggrExpr()->eval(centry->getAtp(), workAtp_); if ( retcode == ex_expr::EXPR_OK && pstate->oneRowAggr_ && moveExpr() && !grbyExpr() ) { retcode = moveExpr()->eval( centry->getAtp(), workAtp_ ); } // Only want to short circuit if we have a ScalarAgg. if ((retcode == ex_expr::EXPR_TRUE) && (!grbyExpr())) { // don't do any further processing. Short // circuit and return. This is the case // of ANY_TRUE, ONE_TRUE or short circuit // aggregate evaluation (like, count(*) > 5) // Send GET_NOMORE request to child. qchild_.down->cancelRequestWithParentIndex(qparent_.down->getHeadIndex()); pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_FINALIZE_CANCEL; break; } else if (retcode == ex_expr::EXPR_ERROR) { pstate->step_ = SORT_GRBY_LOCAL_ERROR; break; } } // consume the child row only if the next // step - pstate->step_ is not the following // - SORT_GRBY_LOCAL_ERROR; // - SORT_GRBY_CHILD_ERROR; // - SORT_GRBY_FINALIZE_CANCEL; // - SORT_GRBY_FINALIZE; // The qchild_.up->removeHead() is done as part of // that step. // Here I check for only SORT_GRBY_FINALIZE_CANCEL // because // the logic is such that when we get here, the // state cannot be SORT_GRBY_LOCAL_ERROR, // SORT_GRBY_CHILD_ERROR or SORT_GRBY_FINALIZE. if (pstate->step_ != ex_sort_grby_tcb::SORT_GRBY_FINALIZE_CANCEL) qchild_.up->removeHead(); } } break; case ex_queue::Q_NO_DATA: { // return current group to parent pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_FINALIZE; } break; case ex_queue::Q_SQLERROR: { pstate->step_ = SORT_GRBY_CHILD_ERROR; } break; case ex_queue::Q_INVALID: ex_assert(0,"ex_sort_grby_tcb::work() Invalid state returned by child"); break; }; // end of switch on status of child queue } break; case SORT_GRBY_CHILD_ERROR: case SORT_GRBY_LOCAL_ERROR: { qchild_.down->cancelRequestWithParentIndex( qparent_.down->getHeadIndex()); // check if we've got room in the up queue if (qparent_.up->isFull()) return WORK_OK; // parent queue is full. Just return ex_queue_entry *pentry_up = qparent_.up->getTailEntry(); ex_queue_entry * centry = qchild_.up->getHeadEntry(); pentry_up->copyAtp(centry); pentry_up->upState.parentIndex = pentry_down->downState.parentIndex; pentry_up->upState.downIndex = qparent_.down->getHeadIndex(); pentry_up->upState.setMatchNo(pstate->matchCount_); if ((sort_grby_tdb().isNonFatalErrorTolerated()) && (SORT_GRBY_LOCAL_ERROR == pstate->step_)) pentry_up->upState.status = ex_queue::Q_OK_MMORE; else pentry_up->upState.status = ex_queue::Q_SQLERROR; qparent_.up->insert(); pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_CANCELLED; break; } case ex_sort_grby_tcb::SORT_GRBY_FINALIZE: case ex_sort_grby_tcb::SORT_GRBY_FINALIZE_CANCEL: { // check if we've got room in the up queue if (qparent_.up->isFull()) return WORK_OK; // parent queue is full. Just return ex_expr::exp_return_type evalRetCode = ex_expr::EXPR_OK; if (havingExpr()) { evalRetCode = havingExpr()->eval(workAtp_, 0); } if ((!havingExpr()) || ((havingExpr()) && (evalRetCode == ex_expr::EXPR_TRUE))) { // if stats are to be collected, collect them. if (getStatsEntry()) { getStatsEntry()->incActualRowsReturned(); } // return to parent ex_queue_entry * pentry_up = qparent_.up->getTailEntry(); pentry_up->copyAtp(workAtp_); 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_); // insert into parent up queue qparent_.up->insert(); } else if (evalRetCode == ex_expr::EXPR_ERROR) { // The SORT_GRBY_LOCAL_ERROR state expects the // diags area to be in the ATP of the head entry of // the child's queue. It is currently in the workAtp_, // so copy it to the childs head ATP. // SORT_GRBY_LOCAL_ERROR will copy it from the // child's queue entry to the parents up queue. // ex_queue_entry * centry = qchild_.up->getHeadEntry(); centry->copyAtp(workAtp_); pstate->step_ = SORT_GRBY_LOCAL_ERROR; break; } // start a new group, if more rows in child's up queue if (pstate->step_ == SORT_GRBY_FINALIZE_CANCEL) pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_CANCELLED; else if (qchild_.up->getHeadEntry()->upState.status != ex_queue::Q_OK_MMORE) pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_DONE; else pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_NEW_GROUP; workAtp_->getTupp(sort_grby_tdb().tuppIndex_).release(); break; } case ex_sort_grby_tcb::SORT_GRBY_DONE: case ex_sort_grby_tcb::SORT_GRBY_NEVER_STARTED: { // check if we've got room in the up queue if (qparent_.up->isFull()) return WORK_OK; // parent queue is full. Just return workAtp_->release(); 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_); if (pstate->step_ != SORT_GRBY_NEVER_STARTED) { ex_queue_entry * centry = qchild_.up->getHeadEntry(); ex_assert(centry->upState.status == ex_queue::Q_NO_DATA, "ex_sort_grby_tcb::work() expecting Q_NO_DATA"); ex_assert(centry->upState.parentIndex == qparent_.down->getHeadIndex(), "ex_sort_grby_tcb::work() child's reply out of sync"); qchild_.up->removeHead(); } // remove the down entry qparent_.down->removeHead(); // insert into parent up queue qparent_.up->insert(); // re-initialize pstate pstate->step_ = ex_sort_grby_tcb::SORT_GRBY_EMPTY; pstate->matchCount_ = 0; workAtp_->release(); if (qparent_.down->isEmpty()) return WORK_OK; // 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; // postion at the new head pentry_down = qparent_.down->getHeadEntry(); pstate = (ex_sort_grby_private_state*) pentry_down->pstate; request = pentry_down->downState.request; ex_assert(pstate->step_ != ex_sort_grby_tcb::SORT_GRBY_EMPTY, "ex_sort_grby_tcb::work() invalid step"); } break; default: ex_assert(0, "Invalid pstate->step_ in ex_sort_grby_tcb::work()"); break; } // switch pstate->step_ } // while }
// work - doit... // // short ExSampleTcb::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; ExSamplePrivateState * 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 = (ExSamplePrivateState*) 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) { pstate->step_ = ExSamp_DONE; } else { pstate->step_ = ExSamp_PREWORK; // 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(); // Copy the input atp to the work atp for this request // pstate->workAtp_->copyAtp(pentry_down->getAtp()); } } // end for processedInputs_ pentry_down = qparent_.down->getHeadEntry(); pstate = (ExSamplePrivateState*) pentry_down->pstate; request = pentry_down->downState.request; // If the request has not been worked on yet, then // initialize the presistent expression variable data and // switch to the WORKING state. // if(pstate->step_ == ExSamp_PREWORK) { pstate->step_ = ExSamp_WORKING; if(balanceExpr()) balanceExpr()->initializePersistentData(); } ExOperStats *statsEntry = getStatsEntry(); // 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_ == ExSamp_WORKING) || (pstate->step_ == ExSamp_RETURNINGROWS)) { 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_ = ExSamp_CANCELLED; } } switch (pstate->step_) { // ExSamp_CANCELLED // // Transition to this state from ... // 1. ExSamp_Error - After the error has been processed. // 2. ExSamp_Working - If enough rows have been returned. // 3. ExSamp_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. ExSamp_DONE - In all cases. // case ExSamp_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 ExSamp_DONE state. // if (child_status == ex_queue::Q_NO_DATA) pstate->step_ = ExSamp_DONE; // Discard the child row. qchild_.up->removeHead(); break; } // ExSamp_ERROR // // Transition to this state from ... // 1. ExSamp_WORKING - a child reply with the type SQLERROR. // // Remain in this state until .. // 1. The error row has been returned to the parent. // // Transition from this state to ... // 1. ExSamp_CANCELLED - In all cases. // case ExSamp_ERROR: { // If there is no room in the parent queue for the reply, // try again later. // if (qparent_.up->isFull()) return WORK_OK; ex_queue_entry *pentry_up = qparent_.up->getTailEntry(); ex_queue_entry * centry = qchild_.up->getHeadEntry(); // Cancel the child request - there must be a child request in // progress to get to the ExSamp_ERROR state. // qchild_.down->cancelRequestWithParentIndex (qparent_.down->getHeadIndex()); // Construct and return the error row. // pentry_up->copyAtp(centry); 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 ExSamp_CANCELLED state. // pstate->step_ = ExSamp_CANCELLED; break; } // ExSamp_WORKING // // Transition to this state from ... // 1. ExSamp_EMPTY - If a request is started. // // 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. // // Transition from this state to ... // 1. ExSamp_DONE - If all the child rows including EOD have // been processed. // 2. ExSamp_ERROR - If an SQLERROR rows is received. // 3. ExSamp_CANCELLED - If enough rows have been returned. // 3. ExSamp_CANCELLED - If the request is cancelled. // case ExSamp_WORKING: { // If there is not room in the parent Queue for the reply, // try again later. // if (qparent_.up->isFull()) return WORK_OK; // 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: { // Apply the sampling predicate if it exists and extract // the sampling factor. // ex_expr::exp_return_type retCode = ex_expr::EXPR_TRUE; Int32 samplingFactor = 1; if(balanceExpr()) { retCode = balanceExpr()->eval (centry->getAtp(), centry->getAtp()); if(retCode == ex_expr::EXPR_OK) { samplingFactor = *(Lng32*)balanceExpr()->getPersistentData (returnFactorOffset()); // If the sampling factor is less than 0, then // we are done with this request. Mark the // request as get-no-more. Forces the child to be // cancelled // if(samplingFactor < 0) { // Cancel the rest of this // request. pentry_down->downState.request = ex_queue::GET_NOMORE; request = ex_queue::GET_NOMORE; // Return no rows retCode = ex_expr::EXPR_FALSE; } } } // If the row passed the sampling predicate, apply the // selection predicate if it exists. // if ((samplingFactor > 0) && (retCode == ex_expr::EXPR_OK) && postPred()) retCode = postPred()->eval (centry->getAtp(), centry->getAtp()); // Act on the result of the selection predicate. // switch(retCode) { // If the selection predicate returns TRUE, // return the row to the parent the number // of times indicated by the sampling factor as long // as there is room in the parent queue. // case ex_expr::EXPR_TRUE: case ex_expr::EXPR_OK: while(!qparent_.up->isFull() && (samplingFactor > 0)) { // Copy the child ATP to the parent ATP -- the // row images are exactly the same. // ex_queue_entry * pentry_up = qparent_.up->getTailEntry(); pentry_up->copyAtp(centry); // Fixup the up state. // 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_); // Commit the entry. // qparent_.up->insert(); if (statsEntry) statsEntry->incActualRowsReturned(); samplingFactor--; // If we have satisfied a GET_N request, then // break out of here so we can stop processing. // if((request == ex_queue::GET_N) && (pentry_down->downState.requestValue <= (Lng32)pstate->matchCount_)) { samplingFactor = 0; break; } } // If all of the rows are returned, then we are done // with this entry and can proceed. // if(samplingFactor == 0) { qchild_.up->removeHead(); } // Otherwise, the queue must have become full so we // have to switch to the RETURNINGROWS state to finish // the job. else { pstate->rowsToReturn_ = samplingFactor; pstate->step_ = ExSamp_RETURNINGROWS; return WORK_OK; } break; // If the selection predicate returns FALSE, // do not return the child row. // case ex_expr::EXPR_FALSE: qchild_.up->removeHead(); break; // If the selection predicate returns an ERROR, // go to the error processing state. // case ex_expr::EXPR_ERROR: pstate->step_ = ExSamp_ERROR; break; } } break; // The EOD from the child. Transition to ExSamp_DONE. // case ex_queue::Q_NO_DATA: pstate->step_ = ExSamp_DONE; qchild_.up->removeHead(); break; // An SQLERROR from the child. Transition to ExSamp_ERROR. // case ex_queue::Q_SQLERROR: pstate->step_ = ExSamp_ERROR; break; } } break; // ExSamp_RETURNINGROWS // // Transistion to this state from ... // 1. ExSamp_WORKING - if up queue becomes full when returning // multiple rows from oversampling. // // Remain in this state until ... // 1. the multiple oversampled rows have been returned. // case ExSamp_RETURNINGROWS: { ex_queue_entry * centry = qchild_.up->getHeadEntry(); while(!qparent_.up->isFull() && (pstate->rowsToReturn_ > 0)) { // Copy the child ATP to the parent ATP -- the row // images are exactly the same. // ex_queue_entry * pentry_up = qparent_.up->getTailEntry(); pentry_up->copyAtp(centry); // Fixup the up state. // 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_); // Commit the entry. // qparent_.up->insert(); if (statsEntry) statsEntry->incActualRowsReturned(); pstate->rowsToReturn_--; // If we have satisfied a GET_N request, then // break out of here so we can stop processing. // if((request == ex_queue::GET_N) && (pentry_down->downState.requestValue <= (Lng32)pstate->matchCount_)) { pstate->rowsToReturn_ = 0; break; } } // If all of the rows were returned, remove the child's reply // and go back to the WORKING state. // if(pstate->rowsToReturn_ == 0) { qchild_.up->removeHead(); pstate->step_ = ExSamp_WORKING; } // Otherwise, we need to come back later to finish up. // else { return WORK_OK; } } break; // ExSamp_DONE // // Transition to the state from ... // 1. ExSamp_WORKING - if all child rows have been processed. // 2. ExSamp_CANCELLED - if all child rows have been consumed. // 3. ExSamp_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. ExSamp_EMPTY - In all cases. // case ExSamp_DONE: { // If there is not any room in the parent's queue, // try again later. // if (qparent_.up->isFull()) return WORK_OK; 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_ = ExSamp_EMPTY; pstate->matchCount_ = 0; pstate->rowsToReturn_ = 0; pstate->workAtp_->release(); // If there are no more requests, simply return. // if (qparent_.down->isEmpty()) return WORK_OK; // 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; // Postion at the new head of the request queue. // pentry_down = qparent_.down->getHeadEntry(); pstate = (ExSamplePrivateState*) pentry_down->pstate; request = pentry_down->downState.request; // If the request has not been worked on yet, then initialize the // presistent expression variable data and switch to the // WORKING state. // if(pstate->step_ == ExSamp_PREWORK) { pstate->step_ = ExSamp_WORKING; if(balanceExpr()) balanceExpr()->initializePersistentData(); } } break; } // switch pstate->step_ } // while }
short ExTupleFlowTcb::work() { // This is some sort of a hack to fix the problems with the number of rows // inserted returned to the user for packed tables. For these tables, rows // are packed (by the Pack node which is the left child of this tuple flow) // before they are sent off to DP2. DP2 has no idea that it's actually // inserting multiple logical rows (as perceived by the user). However, // there is actually a hidden count of logical rows stored as the first 4 // bytes of the packed row. This counter is supposed to keep track of a sum // of this number in each packed row it gets from the left. When all // insertions are done, this sum is used to override what's stored by the // PA node in the executor global area the number of rows inserted. This is // not a very good place to have this fix, but since this is a low-priority // problem at this time, here we are. // // // NB: The code introduced for this fix // could be safely removed if desired. Also, all changes are within // this file. // if (qParent_.down->isEmpty()) return WORK_OK; ex_queue_entry * pentry_down = qParent_.down->getHeadEntry(); ExTupleFlowPrivateState & pstate = *((ExTupleFlowPrivateState*) pentry_down->pstate); if ((tflowTdb().userSidetreeInsert()) && (pentry_down->downState.request == ex_queue::GET_EOD) && (NOT pstate.parentEOD_)) { pstate.step_ = MOVE_EOD_TO_TGT_; } else if ((pstate.step_ != DONE_) && (pstate.step_ != CANCELLED_) && (pentry_down->downState.request == ex_queue::GET_NOMORE)) { if (pstate.step_ == EMPTY_) pstate.step_ = DONE_; else pstate.step_ = CANCELLED_; } while (1) { switch (pstate.step_) { case EMPTY_: { if (qSrc_.down->isFull()) return WORK_OK; ex_queue_entry * src_entry = qSrc_.down->getTailEntry(); src_entry->downState.request = pentry_down->downState.request; src_entry->downState.requestValue = pentry_down->downState.requestValue; if ((tflowTdb().firstNRows() >= 0) && (pentry_down->downState.request != ex_queue::GET_N)) { src_entry->downState.request = ex_queue::GET_N; src_entry->downState.requestValue = tflowTdb().firstNRows(); } src_entry->downState.parentIndex = qParent_.down->getHeadIndex(); src_entry->passAtp(pentry_down); qSrc_.down->insert(); // just checking to make sure we got a diags area from the CLI if we are // executing a non-tomic insert. This is done now so that we don't have to do it in multiple // places later. if (tflowTdb().isNonFatalErrorTolerated()) { ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); ex_assert(cliDiagsArea, "In Tupleflow : Non-Atomic insert received no diags area from the CLI"); } pstate.parentEOD_ = FALSE; pstate.srcEOD_ = FALSE; pstate.matchCount_ = 0; pstate.tgtRequests_ = 0; pstate.tgtRowsSent_ = FALSE; pstate.noOfUnPackedRows_ = 0; pstate.srcRequestCount_ = -1; pstate.nonFatalErrorSeen_ = FALSE; // Set startRightIndex_ so that CancelReques doesn't do anything. pstate.startRightIndex_ = pstate.srcRequestCount_; pstate.step_ = MOVE_SRC_TO_TGT_; } break; case MOVE_SRC_TO_TGT_: { // if there are some rows in source up queue, move them to target. while ((! qSrc_.up->isEmpty()) && (! qTgt_.down->isFull()) && (pstate.step_ != HANDLE_ERROR_)) { ex_queue_entry * src_entry = qSrc_.up->getHeadEntry(); ex_queue_entry * tgt_entry = qTgt_.down->getTailEntry(); switch (src_entry->upState.status) { case ex_queue::Q_OK_MMORE: { // move this source row to target. // LCOV_EXCL_START // BEGIN: - Read note at beginning of work(). // if (tcbSrc_->getNodeType() == ComTdb::ex_PACKROWS) { char* packTuppPtr = src_entry->getTupp(src_entry->numTuples()-1) .getDataPointer(); Int32 noOfRows = *((Int32 *)packTuppPtr); pstate.noOfUnPackedRows_ += (noOfRows - 1); } // // END:- Read note at beginning of work(). // LCOV_EXCL_STOP pstate.srcRequestCount_++; tgt_entry->downState.request = pentry_down->downState.request; tgt_entry->downState.requestValue = pentry_down->downState.requestValue; tgt_entry->downState.parentIndex = (Lng32) pstate.srcRequestCount_; tgt_entry->copyAtp(src_entry); qTgt_.down->insert(); pstate.tgtRequests_++; pstate.tgtRowsSent_ = TRUE; qSrc_.up->removeHead(); } break; case ex_queue::Q_NO_DATA: { if ((tflowTdb().vsbbInsertOn()) && (pstate.tgtRowsSent_ == TRUE)) { if (tflowTdb().userSidetreeInsert()) { tgt_entry->downState.request = ex_queue::GET_EOD_NO_ST_COMMIT; } else { tgt_entry->downState.request = ex_queue::GET_EOD; } tgt_entry->downState.requestValue = pentry_down->downState.requestValue; tgt_entry->downState.parentIndex = (Lng32) pstate.srcRequestCount_; tgt_entry->copyAtp(src_entry); qTgt_.down->insert(); pstate.tgtRequests_++; } // LCOV_EXCL_START if ((pstate.tgtRowsSent_ == FALSE) && (src_entry->getDiagsArea())) { // a warning is returned with EOD and // nothing else was returned from source. // Move warning to parent's up queue. if (qParent_.up->isFull()) return WORK_OK; ex_queue_entry * up_entry = qParent_.up->getTailEntry(); up_entry->setDiagsArea(src_entry->getDiagsArea()); } // LCOV_EXCL_STOP qSrc_.up->removeHead(); pstate.srcEOD_ = TRUE; // LCOV_EXCL_START if (tflowTdb().sendEODtoTgt()) pstate.step_ = MOVE_EOD_TO_TGT_; // LCOV_EXCL_STOP } break; case ex_queue::Q_SQLERROR: { if (qParent_.up->isFull()) return WORK_OK; ex_queue_entry * pentry = qParent_.up->getTailEntry(); ComDiagsArea * da = src_entry->getDiagsArea(); ex_assert(da, "We have a Q_SQLERROR in Tupleflow but no diags area"); if (tflowTdb().isNonFatalErrorTolerated() && (da->getNextRowNumber(ComCondition::NONFATAL_ERROR) == ComCondition::NONFATAL_ERROR)) { pstate.nonFatalErrorSeen_ = TRUE; } else { pstate.step_ = HANDLE_ERROR_; pstate.nonFatalErrorSeen_ = FALSE; } pstate.srcRequestCount_++; if(tflowTdb().isRowsetIterator()) da->setAllRowNumber((Lng32) pstate.srcRequestCount_); ComDiagsArea *accumulatedDiagsArea = pentry->getDiagsArea(); if (accumulatedDiagsArea) { accumulatedDiagsArea->mergeAfter(*da); if (!(accumulatedDiagsArea->canAcceptMoreErrors()) && tflowTdb().isNonFatalErrorTolerated()) { pstate.nonFatalErrorSeen_ = FALSE; pstate.step_ = HANDLE_ERROR_; } } else { pentry->setDiagsArea(da); da->incrRefCount(); accumulatedDiagsArea = da ; if (tflowTdb().isNonFatalErrorTolerated()) { ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); da->setLengthLimit(cliDiagsArea->getLengthLimit()); } } // For Non-Fatal errors we will remove this Q_SQLERROR reply from the // left child right below as we will continue to stay in this state (MOVE_SRC_TO_TGT_). // For fatal errors this Q_SQLERROR reply is removed in HANDLE_ERROR step to which // we will transition immediately. if (pstate.nonFatalErrorSeen_ == TRUE) qSrc_.up->removeHead(); } break; case ex_queue::Q_REC_SKIPPED: { pstate.srcRequestCount_++; ComDiagsArea * da = src_entry->getDiagsArea(); if (da) pstate.nonFatalErrorSeen_ = TRUE; qSrc_.up->removeHead(); } break; default: { ex_assert(0, "ExTupleFlowTcb::work() Error returned from src"); // LCOV_EXCL_LINE } break; } // switch } // while // if the child reply is not an Q_SQLERROR, then process target if ((pstate.step_ != HANDLE_ERROR_) && (pstate.step_ != MOVE_EOD_TO_TGT_)) pstate.step_ = PROCESS_TGT_; } // MOVE_SRC_TO_TGT break; case MOVE_EOD_TO_TGT_: { pstate.parentEOD_ = TRUE; if (qTgt_.down->isFull()) return WORK_OK; ex_queue_entry * tgt_entry = qTgt_.down->getTailEntry(); tgt_entry->downState.request = ex_queue::GET_EOD; tgt_entry->downState.requestValue = pentry_down->downState.requestValue; tgt_entry->downState.parentIndex = qParent_.down->getHeadIndex(); //tgt_entry->passAtp(pentry_down); qTgt_.down->insert(); pstate.tgtRequests_++; // LCOV_EXCL_START if (tflowTdb().sendEODtoTgt()) pstate.srcEOD_ = TRUE; // LCOV_EXCL_STOP else pstate.srcEOD_ = FALSE; pstate.step_ = PROCESS_TGT_; } break; case PROCESS_TGT_: { while (! qTgt_.up->isEmpty() && pstate.step_ != HANDLE_ERROR_) { ex_queue_entry * tgt_entry = qTgt_.up->getHeadEntry(); switch (tgt_entry->upState.status) { case ex_queue::Q_OK_MMORE: { if (!tflowTdb().isNonFatalErrorTolerated()) { // ex_assert(0, "ExTupleFlowTcb::work() OK_MMORE from tgt"); if (qParent_.up->isFull()) return WORK_OK; ex_queue_entry * pentry = qParent_.up->getTailEntry(); pentry->upState.status = ex_queue::Q_OK_MMORE; pentry->upState.downIndex = qParent_.down->getHeadIndex(); pentry->upState.parentIndex = pentry_down->downState.parentIndex; pentry->upState.setMatchNo(pstate.matchCount_); // copy input tupps from parent request pentry->copyAtp(pentry_down); // copy child's atp to // the output atp (to parent's up queue) pentry->copyAtp(tgt_entry); // insert into parent up queue qParent_.up->insert(); } else { ComDiagsArea * da = tgt_entry->getDiagsArea(); ex_assert(da, "We have a Q_OK_MMORE in Tupleflow but no diags area"); if (da->mainSQLCODE() != 0) { // Non-atomic Rowsets sends OK_MMORE with non-empty diags from child // empty diags (mainsqlcode == 0) implies OK_MMORE sent by ignoreDupKey code // when NAR is on, for -8102 error. Just consume the OK_MMORE. if(tflowTdb().isRowsetIterator()) { da->setAllRowNumber(Lng32 (tgt_entry->upState.parentIndex)); } pstate.nonFatalErrorSeen_ = TRUE; ex_queue_entry * pentry = qParent_.up->getTailEntry(); ComDiagsArea *accumulatedDiagsArea = pentry->getDiagsArea(); if (accumulatedDiagsArea) { accumulatedDiagsArea->mergeAfter(*da); if (!(accumulatedDiagsArea->canAcceptMoreErrors()) && tflowTdb().isNonFatalErrorTolerated()) { pstate.nonFatalErrorSeen_ = FALSE; pstate.step_ = HANDLE_ERROR_; } } else { pentry->setDiagsArea(da); da->incrRefCount(); if (tflowTdb().isNonFatalErrorTolerated()) { ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); da->setLengthLimit(cliDiagsArea->getLengthLimit()); } } } } qTgt_.up->removeHead(); } break; case ex_queue::Q_NO_DATA: { ComDiagsArea * da = tgt_entry->getDiagsArea(); if (da) { ex_queue_entry * pentry = qParent_.up->getTailEntry(); ComDiagsArea *accumulatedDiagsArea = pentry->getDiagsArea(); if (accumulatedDiagsArea) accumulatedDiagsArea->mergeAfter(*da); else { pentry->setDiagsArea(da); da->incrRefCount(); if (tflowTdb().isNonFatalErrorTolerated()) { ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); da->setLengthLimit(cliDiagsArea->getLengthLimit()); } } } pstate.matchCount_ += tgt_entry->upState.getMatchNo(); qTgt_.up->removeHead(); pstate.tgtRequests_--; pstate.startRightIndex_++; } break; case ex_queue::Q_SQLERROR: { if (qParent_.up->isFull()) return WORK_OK; ex_queue_entry * pentry = qParent_.up->getTailEntry(); pentry->copyAtp(tgt_entry); pstate.nonFatalErrorSeen_ = FALSE; pstate.step_ = HANDLE_ERROR_; if(tflowTdb().isRowsetIterator()) { ex_queue_entry * pentry = qParent_.up->getTailEntry(); ComDiagsArea *da = pentry->getDiagsArea(); ex_assert(da, "To set RowNumber, an error condition must be present in the diags area"); da->setAllRowNumber(Lng32 (tgt_entry->upState.parentIndex)); } } break; default: { ex_assert(0, "ExTupleFlowTcb::work() Error returned from tgt"); // LCOV_EXCL_LINE } break; } // switch } // while if (pstate.step_ == HANDLE_ERROR_) break; // if source has returned EOD, // and there are no pending requests in target's down // queue, then we are done with this parent request. if (((pstate.srcEOD_ == TRUE) || (pstate.parentEOD_ == TRUE)) && (qTgt_.down->isEmpty())) pstate.step_ = DONE_; else { if (NOT pstate.parentEOD_) pstate.step_ = MOVE_SRC_TO_TGT_; if (qSrc_.up->isEmpty() || qTgt_.down->isFull()) return WORK_OK; else return WORK_CALL_AGAIN; } } break; case HANDLE_ERROR_: { ex_queue_entry * pentry = qParent_.up->getTailEntry(); pentry->upState.status = ex_queue::Q_SQLERROR; pentry->upState.downIndex = qParent_.down->getHeadIndex(); pentry->upState.parentIndex = pentry_down->downState.parentIndex; pentry->upState.setMatchNo(pstate.matchCount_); ComDiagsArea *da = pentry->getDiagsArea(); if (tflowTdb().isNonFatalErrorTolerated() && !(da->canAcceptMoreErrors())) { ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); da->removeLastErrorCondition(); *da << DgSqlCode(-EXE_NONATOMIC_FAILURE_LIMIT_EXCEEDED) << DgInt0(cliDiagsArea->getLengthLimit()); } // insert into parent up queue qParent_.up->insert(); pstate.step_ = CANCELLED_; } break; case CANCELLED_: { qSrc_.down->cancelRequestWithParentIndex(qParent_.down->getHeadIndex()); // Cancel all the outstanding requests that have been sent to the target. // Cancel all requests within given range (inclusive) qTgt_.down->cancelRequestWithParentIndexRange((queue_index)pstate.startRightIndex_+1, (queue_index)pstate.srcRequestCount_); pstate.startRightIndex_ = pstate.srcRequestCount_; //ignore all rows from source child, till Q_NO_DATA is reached while ((pstate.srcEOD_ != TRUE) && (!qSrc_.up->isEmpty())) { ex_queue_entry * src_entry = qSrc_.up->getHeadEntry(); switch(src_entry->upState.status) { case ex_queue::Q_OK_MMORE: case ex_queue::Q_SQLERROR: case ex_queue::Q_REC_SKIPPED: { qSrc_.up->removeHead(); } break; case ex_queue::Q_NO_DATA: { pstate.srcEOD_ = TRUE; qSrc_.up->removeHead(); } break; default: { ex_assert(0, "ExTupleFlowTcb::work() Error returned from src"); // LCOV_EXCL_LINE } break; } } //ignore all rows from target child, till Q_NO_DATA is reached while (pstate.tgtRequests_ && !qTgt_.up->isEmpty()) { ex_queue_entry * tgt_entry = qTgt_.up->getHeadEntry(); switch(tgt_entry->upState.status) { case ex_queue::Q_OK_MMORE: case ex_queue::Q_SQLERROR: { qTgt_.up->removeHead(); } break; case ex_queue::Q_NO_DATA: { qTgt_.up->removeHead(); pstate.tgtRequests_--; } break; default: { ex_assert(0, "ExTupleFlowTcb::work() Error returned from tgt"); // LCOV_EXCL_LINE } break; } } // if both source and target returned all the rows, // insert Q_SQLERROR into the parent up queue if ((pstate.srcEOD_ == TRUE) && !pstate.tgtRequests_) { pstate.step_ = DONE_; } else return WORK_OK; } break; case DONE_: { if (qParent_.up->isFull()) return WORK_OK; ex_queue_entry * pentry = qParent_.up->getTailEntry(); if (pstate.nonFatalErrorSeen_) { ComDiagsArea *da = pentry->getDiagsArea(); ComDiagsArea *cliDiagsArea = pentry_down->getDiagsArea(); ex_assert((da || cliDiagsArea), "We have non-fatal errors in Tupleflow but no diags area"); if (cliDiagsArea) { if (da) da->mergeAfter(*cliDiagsArea); else { pentry->setDiagsArea(cliDiagsArea); cliDiagsArea->incrRefCount(); } } if (cliDiagsArea->canAcceptMoreErrors()) { ComDiagsArea *mergedDiagsArea = pentry->getDiagsArea(); // used to make mainSQLCODE() return 30022 or 30035. mergedDiagsArea->setNonFatalErrorSeen(TRUE); NABoolean anyRowsAffected = FALSE; // This tupleflow should be in the master for // non-atomic rowsets. ExMasterStmtGlobals *g = getGlobals()-> castToExExeStmtGlobals()->castToExMasterStmtGlobals(); ex_assert(g, "Rowset insert has a flow node that is not in the master executor"); if (g->getRowsAffected() > 0) anyRowsAffected = TRUE; if (anyRowsAffected) *mergedDiagsArea << DgSqlCode(EXE_NONFATAL_ERROR_SEEN); else *mergedDiagsArea << DgSqlCode(EXE_NONFATAL_ERROR_ON_ALL_ROWS); } // we exceeded the Nonfatal error limit when merging with the CLI diags area else { pstate.step_ = HANDLE_ERROR_; // will prevent us from merging the diags areas again pstate.nonFatalErrorSeen_ = FALSE ; break ; } } pentry->upState.status = ex_queue::Q_NO_DATA; pentry->upState.downIndex = qParent_.down->getHeadIndex(); pentry->upState.parentIndex = pentry_down->downState.parentIndex; pentry->upState.setMatchNo(pstate.matchCount_); // LCOV_EXCL_START // BEGIN: Read note at beginning of work(). // if(pstate.noOfUnPackedRows_ != 0) { ComDiagsArea *da = pentry->getDiagsArea(); if (da == NULL) { da = ComDiagsArea::allocate(getGlobals()->getDefaultHeap()); pentry->setDiagsArea(da); } da->addRowCount(pstate.noOfUnPackedRows_); pstate.noOfUnPackedRows_ = 0; } // // END: - Read note at beginning of work(). // LCOV_EXCL_STOP // if stats are to be collected, collect them. if (getStatsEntry()) { // nothing yet returned from right child or returned // to parent. getStatsEntry()->setActualRowsReturned(0); } // insert into parent up queue qParent_.up->insert(); pstate.step_ = EMPTY_; qParent_.down->removeHead(); return WORK_CALL_AGAIN; // check for more down requests } break; } // switch pstate.step_ } // while #pragma nowarn(203) // warning elimination return 0; #pragma warn(203) // warning elimination }
void ExProbeCacheTcb::makeReplyToParentUp(ex_queue_entry *pentry_down, ExProbeCachePrivateState &pstate, ex_queue::up_status reply_status) { ExOperStats *stats; ex_queue_entry * up_entry = qparent_.up->getTailEntry(); Int32 rowQualifies = 1; // Copy the pointers to the input data from parent up_entry->copyAtp(pentry_down); if ((reply_status == ex_queue::Q_OK_MMORE) && (pstate.pcEntry_->innerRowTupp_.isAllocated())) { // Use the pcEntry_ to set the returned tuple from // the pool. up_entry->getAtp()->getTupp(probeCacheTdb().tuppIndex_) = pstate.pcEntry_->innerRowTupp_; if (selectPred()) { ex_expr::exp_return_type evalRetCode = selectPred()->eval(up_entry->getAtp(), 0); if (evalRetCode == ex_expr::EXPR_FALSE) rowQualifies = 0; else if (evalRetCode != ex_expr::EXPR_TRUE) { ex_assert(evalRetCode == ex_expr::EXPR_ERROR, "invalid return code from expr eval"); // diags area should have been generated reply_status = ex_queue::Q_SQLERROR; } } } if (rowQualifies) { // Initialize the upState. up_entry->upState.parentIndex = pentry_down->downState.parentIndex; up_entry->upState.downIndex = qparent_.down->getHeadIndex(); up_entry->upState.setMatchNo(pstate.matchCount_); up_entry->upState.status = reply_status; // Give the reply any diagsArea. Test pcEntry_ before using it // because a request that went from CANCELED_NOT_STARTED to // DONE will never be hooked up with a pcEntry_. if (pstate.pcEntry_ && pstate.pcEntry_->diagsArea_) { ComDiagsArea *accumulatedDiagsArea = up_entry->getDiagsArea(); if (accumulatedDiagsArea) accumulatedDiagsArea->mergeAfter(*pstate.pcEntry_->diagsArea_); else { up_entry->setDiagsArea(pstate.pcEntry_->diagsArea_); pstate.pcEntry_->diagsArea_->incrRefCount(); } } // Insert the reply. qparent_.up->insert(); if ((stats = getStatsEntry()) != NULL && reply_status == ex_queue::Q_OK_MMORE) stats->incActualRowsReturned(); } return; }