// getHistoryAttributes
//
// Helper function that traverses the set of root sequence functions
// supplied by the compiler and constructs the set of all of the
// attributes that must be materialized in the history row.
// 
void PhysSequence::getHistoryAttributes(const ValueIdSet &sequenceFunctions,
                                        const ValueIdSet &outputFromChild,
                                        ValueIdSet &historyAttributes,
                                        NABoolean addConvNodes,
                                        CollHeap *wHeap,
                                        ValueIdMap *origAttributes) const
{
  if(addConvNodes && !origAttributes) {
    origAttributes = new (wHeap) ValueIdMap();
  }

  ValueIdSet children;
  for(ValueId valId = sequenceFunctions.init();
      sequenceFunctions.next(valId);
      sequenceFunctions.advance(valId)) {

    if(valId.getItemExpr()->isASequenceFunction()) {
      ItemExpr *itmExpr = valId.getItemExpr();

      switch(itmExpr->getOperatorType())
        {
          // The child needs to be in the history row.
          //
        case ITM_OFFSET:
        case ITM_ROWS_SINCE:
        case ITM_THIS:
        case ITM_NOT_THIS:

          // If the child needs to be in the history buffer, then
          // add a Convert node to force the value to be moved to the
          // history buffer.
          if (addConvNodes)
            {
              itmExpr->child(0) = 
                addConvNode(itmExpr->child(0), origAttributes, wHeap);
            }
          historyAttributes += itmExpr->child(0)->getValueId();
          break;

          // The sequence function needs to be in the history row.
          //
        case ITM_RUNNING_SUM:
        case ITM_RUNNING_COUNT:
        case ITM_RUNNING_MIN:
        case ITM_RUNNING_MAX:
        case ITM_LAST_NOT_NULL:
          historyAttributes += itmExpr->getValueId();
          break;
/*
        // after PhysSequence precode gen OLAP sum and count are already transform,ed into running
        // this is used during optimization phase-- 
        case ITM_OLAP_SUM:
        case ITM_OLAP_COUNT:
        case ITM_OLAP_RANK:
        case ITM_OLAP_DRANK:
          if (addConvNodes)
            {
              itmExpr->child(0) = 
                addConvNode(itmExpr->child(0), origAttributes, wHeap);
            }

          historyAttributes += itmExpr->child(0)->getValueId();
          //historyAttributes += itmExpr->getValueId();	  
          break;
*/
          // The child and sequence function need to be in the history row.
          //
        case ITM_OLAP_MIN:
        case ITM_OLAP_MAX:
        case ITM_MOVING_MIN:
        case ITM_MOVING_MAX:

          // If the child needs to be in the history buffer, then
          // add a Convert node to force the value to be moved to the
          // history buffer.
          if (addConvNodes)
            {
              itmExpr->child(0) = 
                addConvNode(itmExpr->child(0), origAttributes, wHeap);
            }

          historyAttributes += itmExpr->child(0)->getValueId();
          historyAttributes += itmExpr->getValueId();	  
          break;

        case ITM_RUNNING_CHANGE:
          if (itmExpr->child(0)->getOperatorType() == ITM_ITEM_LIST)
            {
              // child is a multi-valued expression
              // 
              ExprValueId treePtr = itmExpr->child(0);

              ItemExprTreeAsList changeValues(&treePtr,
                                              ITM_ITEM_LIST,
                                              RIGHT_LINEAR_TREE);

              CollIndex nc = changeValues.entries();
              
              ItemExpr *newChild = NULL;
              if(addConvNodes) {
                newChild = addConvNode(changeValues[nc-1], origAttributes, wHeap);
                historyAttributes += newChild->getValueId();
              } else {
                historyAttributes += changeValues[nc-1]->getValueId();
              }

              // add each item in the list
              // 
              for (CollIndex i = nc; i > 0; i--)
                {
                  if(addConvNodes) {
                    ItemExpr *conv
                      = addConvNode(changeValues[i-1], origAttributes, wHeap);

                    newChild = new(wHeap) ItemList(conv, newChild);
                    newChild->synthTypeAndValueId(TRUE);
                    historyAttributes += conv->getValueId();
                  } else {
                    historyAttributes += changeValues[i-1]->getValueId();
                  }
                }

              if(addConvNodes) {
                itmExpr->child(0) = newChild;
              }
            }
          else
            {

              // If the child needs to be in the history buffer, then
              // add a Convert node to force the value to be moved to the
              // history buffer.
              if (addConvNodes)
                {
                  itmExpr->child(0) = 
                    addConvNode(itmExpr->child(0), origAttributes, wHeap);
                }

              historyAttributes += itmExpr->child(0)->getValueId();
            }

          historyAttributes += itmExpr->getValueId();  
          break;

        default:
          CMPASSERT(0);
        }
    }

    // Gather all the children, and if not empty, recurse down to the
    // next level of the tree.
    //
    for(Lng32 i = 0; i < valId.getItemExpr()->getArity(); i++) 
    {
      if (!outputFromChild.contains(valId.getItemExpr()->child(i)->getValueId()))
        //!valId.getItemExpr()->child(i)->nodeIsPreCodeGenned()) 
      {
        children += valId.getItemExpr()->child(i)->getValueId();
      }
    }
  }
  
  if (NOT children.isEmpty())
  {
    getHistoryAttributes( children,
                          outputFromChild,
                          historyAttributes, 
                          addConvNodes, 
                          wHeap, 
                          origAttributes);
  }

} // PhysSequence::getHistoryAttributes
// ItmSeqRunningFunction::preCodeGen
//
// Transforms the running sequence functions into scalar expressions
// that use offset to reference the previous value of the function.
//
ItemExpr *ItmSeqRunningFunction::preCodeGen(Generator *generator)
{
  if (nodeIsPreCodeGenned())
    return this;
  markAsPreCodeGenned();

  // Get some local handles...
  //
  CollHeap *wHeap = generator->wHeap();
  ItemExpr *itmChild = child(0)->castToItemExpr();

  // Allocate a HostVar for referencing the result before it is computed.
  //
  ItemExpr *itmResult 
    = new(wHeap) HostVar("_sys_Result",
			 getValueId().getType().newCopy(wHeap),
			 TRUE);

  // Create an item expression to reference the previous
  // value of this running sequence function.
  //
  ItemExpr *offExpr = new(wHeap) ItmSeqOffset(itmResult, 1);
  ((ItmSeqOffset *)offExpr)->setIsOLAP(isOLAP());
  // Add the sequence function specific computation.
  //
  ItemExpr *itmNewSeqFunc = 0;
  switch(getOperatorType())
    {
    case ITM_RUNNING_COUNT:
      {
	// By this point ITM_RUNNING_COUNT is count(column). The count
	// is one more than the previous count if the current column is
	// not null, otherwise, it is the previous count.
	//

        // Create the increment value.  For non-nullable values, this
        // is always 1, essentially runningcount(*).
        //
        ItemExpr *incr;
        if(itmChild->getValueId().getType().supportsSQLnullLogical()) {
          incr = generator->getExpGenerator()->createExprTree
            ("CASE WHEN @A1 IS NULL THEN @A3 ELSE @A2 END", 
             0, 3, 
             itmChild,
             new(wHeap) ConstValue(1),
             new(wHeap) ConstValue(0));
        } else {
          incr = new(wHeap) ConstValue(1);
        }

        ((ItmSeqOffset *)offExpr)->setNullRowIsZero(TRUE);
        ItemExpr *src = offExpr;

        // Do the increment.
        //
        itmNewSeqFunc = new(wHeap)
              BiArith(ITM_PLUS, src, incr);
      }
    break;
    
    case ITM_RUNNING_SUM:
      {
	// SUM(sum from previous row, child)
	//
	itmNewSeqFunc = new(wHeap) BiArithSum(ITM_PLUS, offExpr, itmChild);
      }
    break;
    
    case ITM_RUNNING_MIN:
      {
	// MIN(min from previous rows, child)
	//
	itmNewSeqFunc 
	  = new(wHeap) ItmScalarMinMax(ITM_SCALAR_MIN,
				       offExpr,
				       itmChild);
      }
    break;
    
    case ITM_RUNNING_MAX:
      {
	// MAX(max from previous row, child)
	//
	itmNewSeqFunc 
	  = new(wHeap) ItmScalarMinMax(ITM_SCALAR_MAX,
				       offExpr,
				       itmChild);
      }
    break;
    
    case ITM_LAST_NOT_NULL:
      {
	// If the current value is null then use the previous value
	// of last not null.
	//
	itmNewSeqFunc = generator->getExpGenerator()->createExprTree
	  ("CASE WHEN @A2 IS NOT NULL THEN @A2 ELSE @A1 END", 
	   0, 2, offExpr, itmChild);
      }
    break;

    case ITM_RUNNING_CHANGE:
      {
        // The running change (or 'rows since changed') can have a
        // composite child (a list of values)
        // Convert the list of values to a list of offset of values.
        //
        ItemExpr *offChild = itmChild;
      
        if (itmChild->getOperatorType() == ITM_ITEM_LIST)
          {
            // child is a multi-valued expression, transform into multiple
            // 
            ExprValueId treePtr = itmChild;

            ItemExprTreeAsList changeValues(&treePtr,
                                            ITM_ITEM_LIST,
                                            RIGHT_LINEAR_TREE);

            offChild = new(wHeap) ItmSeqOffset( changeValues[0], 1);
	    ((ItmSeqOffset *)offChild)->setIsOLAP(isOLAP());
            // add Offset expressions for all the items of the list
            // 
            CollIndex nc = changeValues.entries();
            for (CollIndex i = 1; i < nc; i++)
              {
                ItemExpr *off = new(generator->wHeap()) ItmSeqOffset( changeValues[i], 1);
		((ItmSeqOffset *)off)->setIsOLAP(isOLAP());
                offChild = new(generator->wHeap()) ItemList(offChild, off);
              }
          } else {
            offChild = new(wHeap) ItmSeqOffset( offChild, 1);
	    ((ItmSeqOffset *)offChild)->setIsOLAP(isOLAP());
          }
        
 
        ((ItmSeqOffset *)offExpr)->setNullRowIsZero(TRUE);
        ItemExpr *prevValue = offExpr;

        // Compare the value(s) to the previous value(s).  Use special
        // NULLs flags to treat NULLs as values.  Two NULL values are
        // considered equal here.
        //
        ItemExpr *pred = new (wHeap) BiRelat(ITM_EQUAL,
                                             itmChild,
                                             offChild,
                                             TRUE); // Special NULLs
        // running change = 
        //      (value(s) == prev(value(s))) ? prev(running change)+1 : 1
        //
        // Compute base value.
        //
        itmNewSeqFunc = new (wHeap) 
                IfThenElse(pred, prevValue, new (wHeap) SystemLiteral(0));
        
        itmNewSeqFunc = new (wHeap) Case(NULL, itmNewSeqFunc);

        // Force the evaluation of the offset expression so that the
        // result can be reused by subsequent references.
        //
        itmNewSeqFunc = new(wHeap) ItmBlockFunction(offChild, itmNewSeqFunc);

        // Increment the base value.
        //
        itmNewSeqFunc = new (wHeap) BiArith(ITM_PLUS, 
                                            itmNewSeqFunc,
                                            new(wHeap) SystemLiteral(1));

      }
      break;
      
    }
  
  // Get value Ids and types for all of the items. Must do this typing before
  // replacing this value Id's item expression -- otherwise, the typing
  // will give a result different than the type already computed for this
  // sequence function.
  //
  GenAssert(itmNewSeqFunc, "ItmSeqRunningFunction::preCodeGen -- Unexpected Operator Type!");
  itmNewSeqFunc->synthTypeAndValueId(TRUE);

  // Replace the original value ID with the new expression.
  //
  getValueId().replaceItemExpr(itmNewSeqFunc);
  
  // Map the reference to the result to the actual result in the map table.
  //
  Attributes *attr =  generator->getMapInfo
    (itmNewSeqFunc->getValueId())->getAttr();
  MapInfo *mapInfo = generator->addMapInfo(itmResult->getValueId(), attr);
  itmResult->markAsPreCodeGenned();
  mapInfo->codeGenerated();

  // Return the preCodeGen of the new expression.
  //
  return itmNewSeqFunc->preCodeGen(generator);
}