Exemplo n.º 1
0
// 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();
}
Exemplo n.º 2
0
// 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());
          }
        }
      }
    }
  }
}
Exemplo n.º 3
0
////////////////////////////////////////////////////////////////////////////
// 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
}
Exemplo n.º 4
0
// 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 
}
Exemplo n.º 6
0
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;
}