@@ -105,9 +105,13 @@ short GroupByAgg::genAggrGrbyExpr(Generator * generator,
   ValueId valId;
 
   // find the number of aggregate and groupby entries
-  ULng32 numAttrs
-    = ((NOT aggregateExpr.isEmpty() ) ? aggregateExpr.entries() : 0)
-    + ((NOT groupExpr.isEmpty() ) ? groupExpr.entries() : 0);
+  ULng32 numAttrs = 0;
+  if (NOT aggregateExpr.isEmpty())
+    numAttrs += aggregateExpr.entries();
+  if (isRollup() && (NOT rollupGroupExprList.isEmpty()))
+    numAttrs += rollupGroupExprList.entries();
+  else if (NOT groupExpr.isEmpty())
+    numAttrs += groupExpr.entries();
 
   NABoolean isAggrOneRow_ = FALSE;
 
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
******************************************************************************
*
* File:         GenRelGrby.C
* Description:  Aggregate and grouping operators
*               
*               
* Created:      5/17/94
* Language:     C++
*
*
*
*
******************************************************************************
*/
#include "ComOptIncludes.h"
#include "GroupAttr.h"
#include "RelGrby.h"
#include "Generator.h"
#include "GenExpGenerator.h"
//#include "ex_stdh.h"
#include "ExpCriDesc.h"
#include "ComTdb.h"
//#include "ex_tcb.h"
#include "HashRow.h"
#include "hash_table.h" // for HashTableHeader
#include "ComTdbHashGrby.h"
#include "ComTdbSortGrby.h"
#include "DefaultConstants.h"
#include "ItmBitMuxFunction.h"
#include "ComUnits.h"
//#include "ExStats.h"

/////////////////////////////////////////////////////////////////////
//
// Contents:
//    
//   GroupByAgg::genAggrGrbyExpr()
//   HashGroupBy::codeGen()
//   GroupByAgg::codeGen()
//
//////////////////////////////////////////////////////////////////////

// Called by GroupByAgg::codeGen()
short GroupByAgg::genAggrGrbyExpr(Generator * generator,
				  ValueIdSet &aggregateExpr,
				  ValueIdSet &groupExpr,
				  ValueIdList &rollupGroupExprList,
				  ValueIdSet &selectionPred,
				  Int32 workAtp,
				  Int32 workAtpIndex,
				  short returnedAtpIndex, 
				  ex_expr ** aggrExpr,
				  ex_expr ** grbyExpr,
				  ex_expr ** moveExpr,
				  ex_expr ** havingExpr,
				  ComTdb ** childTdb,
				  ExpTupleDesc ** tupleDesc) {
  ExpGenerator * expGen = generator->getExpGenerator();
  Space * space = generator->getSpace();
  
  // remember the end of the current MT
  MapTable * lastMapTable = generator->getLastMapTable();

  // create extra MT to manipultate the childs MT
  MapTable * childMapTable = generator->appendAtEnd();

  // generate code for child tree
  child(0)->codeGen(generator);
  *childTdb = (ComTdb *)(generator->getGenObj());

  // "unlink" the childs MT
  generator->unlinkNext(lastMapTable);

  ////////////////////////////////////////////////////////////
  // Before generating any expression for this node, set the
  // the expression generation flag not to generate float
  // validation PCode. This is to speed up PCode evaluation
  ////////////////////////////////////////////////////////////
  generator->setGenNoFloatValidatePCode(TRUE);

  ULng32 recLen = 0;
  ValueId valId;

  // find the number of aggregate and groupby entries
  ULng32 numAttrs
    = ((NOT aggregateExpr.isEmpty() ) ? aggregateExpr.entries() : 0)
    + ((NOT groupExpr.isEmpty() ) ? groupExpr.entries() : 0);

  NABoolean isAggrOneRow_ = FALSE;

  // ITM_ONE_ROW can occur in a scalar groupby or in a groupBy with 
  // grouping columns. The second possibility occurs only when the 
  // subquery that contained the ITM_ONE_ROW predicate has been 
  // processed by subquery unnesting.
  if (aggregateExpr.entries() == 1)
  {
    ValueId exprId;
    aggregateExpr.getFirst(exprId);
    
    if ( exprId.getItemExpr()->getOperatorType() == ITM_ONE_ROW )
    {
      isAggrOneRow_ = TRUE;
    }
  }

  // add the elements in the aggr list to the map table
  Int32 i = 0;
  Attributes ** attrs = NULL;

  if ( NOT isAggrOneRow_ )
    {
    attrs = new(generator->wHeap()) Attributes * [numAttrs];
    if (NOT aggregateExpr.isEmpty()) {
      for (valId = aggregateExpr.init();
	   aggregateExpr.next(valId);
	   aggregateExpr.advance(valId), i++) {
        attrs[i] = (generator->addMapInfo(valId, 0))->getAttr();
      }
    }
  }

  // -----------------------------------------------------------------------
  // The values of this moveSet has to be copied from the child's outputs
  // into our buffer at run-time. The moveExpr is going to do this and it
  // will be code-generated from this moveSet later in this method.
  // -----------------------------------------------------------------------
  ValueIdSet moveSet;

  if ( isAggrOneRow_ )
  {
    // ---------------------------------------------------------------------
    // First, collect all the inst-null values under ITM_ONE_ROW. We should
    // have the same number of values as the child's outputs.
    // ---------------------------------------------------------------------
    GenAssert(NOT aggregateExpr.isEmpty(),
              "both groupby and aggregate are empty");
    ValueIdSet inulls;
    for (ValueId vid = aggregateExpr.init();
                       aggregateExpr.next(vid);
                       aggregateExpr.advance(vid))
    {
      GenAssert(vid.getItemExpr()->getOperatorType() == ITM_ONE_ROW,
                "aggregateExpr of one-row aggr must be rooted by ITM_ONE_ROW");

      // The child of ITM_ONE_ROW might be an ITEM_LIST backbone. This code
      // assumes that the LIST backbone is a left-linear tree.
      //
      ValueId listBackboneVid = vid.getItemExpr()->child(0)->getValueId();
      NABoolean moreToGo = TRUE;
      do {
        ValueId listItemVid;
        if (listBackboneVid.getItemExpr()->getOperatorType() == ITM_ITEM_LIST)
          listItemVid = listBackboneVid.getItemExpr()->child(1)->getValueId();
        else
        {
          // reach the end of the backbone. handle the remaining one item and
          // exit.
          listItemVid = listBackboneVid;
          moreToGo = FALSE;
        }
 
        if (listItemVid.getItemExpr()->getOperatorType() == ITM_CAST)
        {
          // The code above might have inserted a ITM_CAST on top of INST_NULL.
          listItemVid = listItemVid.getItemExpr()->child(0)->getValueId();
        }
 
        GenAssert(
          listItemVid.getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL,
          "ITM_ONE_ROW's logical leaf nodes can only be inst-null values");
 
        inulls.insert(listItemVid); // collect the value id.
 
        // Go to the next item. Here is where we assume the tree is left linear.
        if (moreToGo)
        listBackboneVid = listBackboneVid.getItemExpr()->child(0)->getValueId();
      } while (moreToGo);
    }

    /* this might not hold due to veg rewrite. for example inst-null(1) can
       be a result of rewriting inst-null(veg{x.a,1}). child will no longer
       produce x.a if x.a = 1 has been evaluated at it. In that case, child's
       output set can be empty while we still find inull(1) under the aggr.

    GenAssert(inulls.entries() ==
              child(0)->getGroupAttr()->getCharacteristicOutputs().entries(),
             "inst-null values under ITM_ONE_ROW not same as child's outputs");
    */

    moveSet += inulls;

    // ---------------------------------------------------------------------
    // Also add my own outputs.
    // ---------------------------------------------------------------------
    //
    // sol 10-070112-1749 (wangth)
    // since isAggrOneRow_ is true we know the child node of sort_grby will
    // either return only one row or no row at all. at runtime the executor
    // does following (see ex_sort_grby_tcb::work in ex_sort_grby.cpp):
    //
    //   - if one row is returned from child node, evaluate it with
    //     aggrExpr.moveExpr_ and return the results upstream.
    //   - if no row is returned from child node, don't do any evaluation
    //     but simply return NULL upstream. this can cause problem if
    //     the select object is not of column type. for example, if select
    //     contains expression such as ISNULL(), the evaluation of the ISNULL
    //     expression with no row may result in a not-NULL value.
    //
    // to solve above problem we have two options:
    //
    // 1. generate two moveExpr_ expressions, one used for one row scenario,
    //    and the other used for no row scenario.
    // 2. if select contains expressions other than column, do not include
    //    those expressions in moveExpr_. instead, let the parent node of
    //    sort_grby do code generation for those expressions. this won't
    //    cause any performance setback because the parent node will evaluate
    //    the expressions (such as ISNULL) only once.
    //
    // we choose the second option. the first option is a lot more complex
    // and risky.
    //
    const ValueIdSet & charOutputs = getGroupAttr()->getCharacteristicOutputs();
    for (valId = charOutputs.init();
         charOutputs.next(valId);
         charOutputs.advance(valId))
      {
        OperatorTypeEnum opType = valId.getValueDesc()->getItemExpr()->getOperatorType();
        if (opType == ITM_BASECOLUMN || opType == ITM_INDEXCOLUMN ||
            opType == ITM_REFERENCE || opType == ITM_BASECOL)
          // only do code gen for columns (but not other expressions)

	  // add only those value ids that are not already in the groupExpr
	  if (NOT groupExpr.contains(valId))
	      moveSet.insert(valId);
      }

    // ----------------------------------------------------------------------
    // Set up the destinations of this moveSet (if any). It's going to reside
    // in the workAtp together with the aggregates.
    // ----------------------------------------------------------------------
    attrs = new(generator->wHeap()) Attributes * [numAttrs+moveSet.entries()];

    for (valId = aggregateExpr.init();
                 aggregateExpr.next(valId);
                 aggregateExpr.advance(valId), i++)
    {
      attrs[i] = (generator->addMapInfo(valId, 0))->getAttr();
    }

    for (valId = moveSet.init();
                 moveSet.next(valId);
                 moveSet.advance(valId), i++)
    {
      attrs[i] = (generator->addMapInfo(valId, 0))->getAttr();
    }
  } // aggregate is ITM_ONE_ROW

  // create a copy of the group by set. This set is used
  // to move the incoming values to current group buffer.
  ValueIdList moveValIdList;
  ValueIdList gbyValIdList;
  ValueIdSet searchValIdSet;
  
  if (isRollup() && (NOT rollupGroupExprList.isEmpty())) {
    for (CollIndex j = 0; j < rollupGroupExprList.entries(); j++) {
      valId = rollupGroupExprList[j];

      ItemExpr * itemExpr = valId.getItemExpr();
      
      // add this converted value to the map table.
      ItemExpr * convNode = NULL;
      convNode = new(generator->wHeap()) Convert (itemExpr);

      // bind/type propagate the new node
      convNode->bindNode(generator->getBindWA());    
      
      attrs[i++] = 
        (generator->addMapInfo(convNode->getValueId(), 0))->getAttr();
      moveValIdList.insert(convNode->getValueId());
      gbyValIdList.insert(valId);
      
      // add the search condition
      BiRelat * biRelat = new(generator->wHeap())
        BiRelat(ITM_EQUAL, itemExpr, convNode);
      biRelat->setSpecialNulls(-1);
      biRelat->bindNode(generator->getBindWA());
      
      biRelat->rollupColumnNum() = j+1;
      
      searchValIdSet.insert(biRelat->getValueId());
    }
  }
  else if (NOT groupExpr.isEmpty()) {
    for (valId = groupExpr.init();
         groupExpr.next(valId);
         groupExpr.advance(valId), i++) {
      
      ItemExpr * itemExpr = valId.getItemExpr();
      
      // add this converted value to the map table.
      Convert * convNode = new(generator->wHeap()) Convert (itemExpr);
      
      // bind/type propagate the new node
      convNode->bindNode(generator->getBindWA());    
      
      attrs[i] = 
	(generator->addMapInfo(convNode->getValueId(), 0))->getAttr();
      moveValIdList.insert(convNode->getValueId());
      gbyValIdList.insert(valId);
      
      // add the search condition
      BiRelat * biRelat = new(generator->wHeap())
	BiRelat(ITM_EQUAL, itemExpr, convNode);
      biRelat->setSpecialNulls(-1);
      biRelat->bindNode(generator->getBindWA());
      
      searchValIdSet.insert(biRelat->getValueId());
    }
  }
  
  numAttrs = ( isAggrOneRow_ ?  numAttrs + moveSet.entries() : numAttrs );

  // Create the descriptor describing the aggr row and assign offset to attrs. 
  expGen->processAttributes(numAttrs,
			    attrs,
			    ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
			    recLen,
			    workAtp,
			    workAtpIndex,
			    tupleDesc,
			    (isRollup() ? ExpTupleDesc::LONG_FORMAT : ExpTupleDesc::SHORT_FORMAT));
 
  NADELETEBASIC(attrs, generator->wHeap());

  // add the childs MT again, we need it to generate the following expressions
  generator->appendAtEnd(childMapTable);

  if (NOT aggregateExpr.isEmpty())
  {
    if (isAggrOneRow_)
    {
      // --------------------------------------------------------------------
      // generate aggregate expression now. The function will also generate
      // an initializeExpr to initialize all the values in moveSet to null.
      // --------------------------------------------------------------------
      expGen->generateAggrExpr(aggregateExpr, ex_expr::exp_AGGR,
			       aggrExpr, 0, 
                               (NOT groupExpr.isEmpty()), &moveSet);

      ((AggrExpr *)*(aggrExpr))->setOneRowAggr();
    }
    else
    {
      // generate aggregate expression 
      expGen->generateAggrExpr(aggregateExpr, ex_expr::exp_AGGR, aggrExpr, 
                               0, (NOT groupExpr.isEmpty()));
    }
  }

  MapTable *returnedMapTable_ = NULL;

  if (NOT groupExpr.isEmpty()) {
    // generate the move expression. This is used to move the incoming
    // grouping values to the aggr buffer.
    expGen->generateListExpr(moveValIdList,
			     ex_expr::exp_ARITH_EXPR,
			     moveExpr);

    // generate the search expression. This expression is used
    // to look for a matching value in the hash table.
    ItemExpr * newPredTree =
      searchValIdSet.rebuildExprTree(ITM_AND, TRUE, TRUE);
      
    expGen->generateExpr(newPredTree->getValueId(),
			 ex_expr::exp_SCAN_PRED,
			 grbyExpr);
  }
  
   if ( isAggrOneRow_ && (NOT moveSet.isEmpty()) )
   {
     // --------------------------------------------------------------------
     // Generate the moveExpr which moves values from the child's outputs
     // (atp0) to the workAtp (atp1).
     // --------------------------------------------------------------------
     expGen->generateSetExpr(moveSet,
                             ex_expr::exp_ARITH_EXPR,
                             moveExpr);
   }

  // Change the atp of aggregate values to 0.
  // All references to these values from this point on
  // will be at atp = 0, atp_index = last entry in returned desc.
  // Offset will be the same as in the workAtp.
  if (NOT aggregateExpr.isEmpty()) {
    for (valId = aggregateExpr.init();
	 aggregateExpr.next(valId);
	 aggregateExpr.advance(valId)) {
      Attributes * attr = generator->getMapInfo(valId)->getAttr();
      attr->setAtp(0);
      attr->setAtpIndex(returnedAtpIndex);
    }
  }
  
  // remove the child's map table. Nothing from child's context
  // should be visible from here on upwards.
  generator->removeAll(lastMapTable);

  if ( returnedMapTable_ )
   generator->appendAtEnd( returnedMapTable_ );

  if ( isAggrOneRow_ )
  {
    // -------------------------------------------------------------------
    // Both the selection predicates and the parent expects outputs in
    // atp0. Set the values in moveSet to reference atp0, so that the
    // selection predicates and parent could be generated properly.
    // -------------------------------------------------------------------
    for (valId = moveSet.init(); 
	         moveSet.next(valId); 
	         moveSet.advance(valId)) 
    {
 
      MapInfo    * mapInfo = generator->getMapInfo(valId);
      Attributes * colAttr = mapInfo->getAttr();
      colAttr->setAtp(0);

      // This code assumes that returnedAtpIndex == workAtpIndex.
      colAttr->setAtpIndex(returnedAtpIndex);

      // code has been generated for valId and a value is available.
      // Mark it so. The valId should have already been marked "generated"
      // while we were generating the moveExpr.
      //
      mapInfo->codeGenerated();
    }
  } // if ITM_ONE_ROW and outputs exist

  // the moveValIdList (set of convert nodes) was used to move the incoming
  // group by values to the aggregate buffer. The child of the convert node
  // is the original grouping column. From this point on, the grouping value
  // is available in the aggregate buffer. Change the buffer attributes of
  // the grouping values in the map table to point to this new location.
  // Also, the atp value of the grouping columns is now 0.
  // This change of attributes is not done if the grouping column
  // is a 'constant'.
  if (NOT groupExpr.isEmpty()) {
    ValueId convValId;

    for (CollIndex i = 0; i < moveValIdList.entries(); i++) {
      convValId = moveValIdList[i];
      Attributes * newGroupColAttr =
	(generator->getMapInfo(convValId))->getAttr();
	
      ValueId valId = gbyValIdList[i];

      // change the location of the grouping columns, unless they
      // are input to this node. Input values already have location
      // assigned to them.
      if ((NOT getGroupAttr()->getCharacteristicInputs().contains(valId)) &&
           (valId.getItemExpr()->getOperatorType() != ITM_CONSTANT)) 
        {
          MapInfo * mapInfo = generator->addMapInfo(valId, 0);
          Attributes * oldGroupColAttr = mapInfo->getAttr();
	  
          oldGroupColAttr->copyLocationAttrs(newGroupColAttr);
          oldGroupColAttr->setAtp(0);
          oldGroupColAttr->setAtpIndex(returnedAtpIndex);
	  
          // code has been generated for valId and a value is available.
          // Mark it so.
          mapInfo->codeGenerated();
        }
    }
  }
  
  // generate having expression, if present
  if (NOT selectionPred.isEmpty()) {
    ItemExpr * newPredTree =
      selectionPred.rebuildExprTree(ITM_AND,TRUE,TRUE);
      expGen->generateExpr(newPredTree->getValueId(),
			    ex_expr::exp_SCAN_PRED,
			    havingExpr);
  }
  
  // reset the expression generation flag to generate float validation pcode
  generator->setGenNoFloatValidatePCode(FALSE);

  return 0;
} // GroupByAgg::genAggrGrbyExpr()

/////////////////////////////////////////////////////////
//
// HashGroupBy::codeGen()
//
/////////////////////////////////////////////////////////
short HashGroupBy::codeGen(Generator * generator) {
  Space * space = generator->getSpace();

  // create a map tables for returned group by and aggregate values
  MapTable * myMapTable1 = generator->appendAtEnd();
  MapTable * myMapTable2 = generator->appendAtEnd();

  ////////////////////////////////////////////////////////////////////////////
  //
  // Layout at this node:
  //
  // |-------------------------------------------------|
  // | input data  |  Grouped data | child's data      |
  // | ( I tupps ) |  ( 1 tupp )   | ( C tupps )       |
  // |-------------------------------------------------|
  // <-- returned row to parent --->
  // <------------ returned row from child ------------>
  //
  // input data:        the atp input to this node by its parent. 
  // grouped data:      tupp where the aggr/grouped result is moved
  // child data:        tupps appended by the left child
  //
  // Input to child:    I + 1 tupps
  //
  // Tupps returned from child are only used to create the
  // grouped data. They are not returned to parent.
  //
  ////////////////////////////////////////////////////////////////////////////

  ex_cri_desc * givenDesc
    = generator->getCriDesc(Generator::DOWN);

  ex_cri_desc * returnedDesc 
#pragma nowarn(1506)   // warning elimination 
    = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); 
#pragma warn(1506)  // warning elimination 
  
  generator->setCriDesc(returnedDesc, Generator::DOWN);

  short returnedAtpIndex = (short) (returnedDesc->noTuples() - 1);

  ///////////////////////////////////////////////////////////////////////////
  // in all the computation below, the grouped/aggregated rows is available
  // in a temporary work atp. They are used from there to aggregate / hash /
  // group values.
  // At runtime, the work atp is passed as the second atp to the
  // expression evaluation procedures. So, the atp value for all group /
  // aggregate values ids  are set to 1 (which corresponds to the second
  // atp -- it is zero based).
  // Before returning from this proc, these value ids are moved to the
  // map table that is being returned.
  // The work atp contains 6 entries:
  // index    what
  // -------------------------------------------------
  //   0      constants
  //   1      temps
  //   2      grouped row in the hash buffer
  //   3      another grouped row in the hash buffer, this time
  //          interpreted as an overflow buffer
  //   4      the calculated hash value
  //   5      grouped row in the result buffer
  //////////////////////////////////////////////////////////////////////////
  short workAtpPos = 1; // second atp 
  short hbRowAtpIndex = 2;
  short ofRowAtpIndex = 3;
  short hashValueAtpIndex = 4;
  short resultRowAtpIndex = 5;
  short bitMuxAtpIndex = 6;

  ex_cri_desc * workCriDesc = new(space) ex_cri_desc(7, space);

  ComTdb * childTdb = 0;

  // The hashExpr takes a child row and calculates the hash value. The hash
  // value is stored in the hashValueTupp_ of the hash_grby_tcb.
  ex_expr * hashExpr = 0;

  // The bitMuxExpr takes a child row and computes the entry in the 
  // bitMuxTable of the row's group. The entry is stored in the 
  // hashValueTupp_ of the hash_grby_tcb (like hashExpr above).
  //
  ex_expr * bitMuxExpr = 0;

  // The bitMuxAggrExpr is equivalent to hbAggrExpr except that since
  // the BitMux table automatically computes some aggregates (i.e. count),
  // the bitMuxAggrExpr does not recompute these aggregates.
  //
  ex_expr * bitMuxAggrExpr = 0;

  // The hbMoveInExpr is used to move the grouping columns from the child
  // row into the hash buffer. This is done if a new group is started.
  ex_expr * hbMoveInExpr = 0;

  // The ofMoveInExpr is used to move the grouping columns from the overflow
  // buffer into the hash buffer. This is done if a new group is started.
  // In case of a partial hash group this expression is not generated.
  // Note that the overflow buffer and the hash buffer contain rows with
  // identical format.
  ex_expr * ofMoveInExpr = 0;

  // The resMoveInExpr is used to move the grouping columns from the child
  // row into the result buffer. This is done in case of partial hash groups
  // if the current row doesn't belong to any group in the hash buffer. In
  // case the row estabilshes a new (partial) group in the result buffer. The
  // parent takes care of merging partial groups. Partial groups are used to
  // avoid overflow handling (overflow handling can't be used in case the
  // hash grouping is executed in DP2) or in case of parallel execution.
  // This expression is also generated in the case of no aggregates, where 
  // the root node (non partial) also needs to copy rows from child input
  // to the result buffer (that row/group is returned up immediately.)
  ex_expr * resMoveInExpr = 0;

  // The hbAggrExpr is used to aggregate a child row into an existing group
  // in the hash buffer.
  ex_expr * hbAggrExpr = 0;

  // The ofAggrExpr is used to aggregate a row from the overflow buffer into
  // an existing group in the hash buffer.
  // In case of a partial hash group this expression is not generated.
  ex_expr * ofAggrExpr = 0;

  // The resAggrExpr is used to aggregate a child row into a partial group
  // in the result buffer.
  // This expression is only generated in case of a partial hash group.
  ex_expr * resAggrExpr = 0;

  // The havingExpr is used to decide whether a a group in the hash buffer
  // qualifies and is therefore moved into the result buffer.
  ex_expr * havingExpr = 0;

  // The moveOutExpr is used to move a qualifiying group from the hash
  // buffer to the result buffer
  ex_expr * moveOutExpr = 0;

  // The hbSearchExpr is used to serach the hash table for the group to which
  // the child row belongs.
  ex_expr * hbSearchExpr = 0;

  // The ofSearchExpr is used to search the hash table for the group to which
  // a row in a overflow buffer belongs
  ex_expr * ofSearchExpr = 0;
 
  ExpTupleDesc * tupleDesc = 0;

  ExpGenerator * expGen = generator->getExpGenerator();

  // if the hash grouping is executed in DP2, we don't do overflow
  // handling. This also means, that it is a partial group by
  // Do not do overflow handling for any partial groupby.
  //
  NABoolean isPartialGroupBy = (isAPartialGroupByNonLeaf() ||
                                isAPartialGroupByLeaf());

  // The old way, only groupbys in DP2 are considered partial
  //
  if (CmpCommon::getDefault(COMP_BOOL_152) == DF_ON) {
    isPartialGroupBy = executeInDP2();
  }

  // set flag to enable pcode for indirect varchar
  NABoolean vcflag = expGen->handleIndirectVC();
  if (CmpCommon::getDefault(VARCHAR_PCODE) == DF_ON) {
    expGen->setHandleIndirectVC( TRUE );
  }

  // If executing in DP2, then it should be a partial
  //
  GenAssert(!executeInDP2() || isPartialGroupBy, "Invalid Groupby in DP2");

  // Get the value from the defaults table that specifies
  // whether to use BitMux or not.
  NABoolean useBitMux = (CmpCommon::getDefault(HGB_BITMUX) == DF_ON);

  useBitMux = FALSE;

  // split the aggregate into a lower and an upper aggregate function.
  // Impotant assumption for this code: The lower and the upper aggregate
  // have identical format. 
  ValueId valId;
  ValueIdList originalAggrValIds(aggregateExpr());

  GenAssert(aggregateEvaluationCanBeStaged(),
	    "illegal aggregate function for hash grouping");

  ValueIdList lowerAggrValIdList;
  ValueIdList upperAggrValIdList;
  for (valId = aggregateExpr().init();
       aggregateExpr().next(valId);
       aggregateExpr().advance(valId)) {
    Aggregate *a = (Aggregate *) valId.getItemExpr();
    ItemExpr * newResult =
      a->rewriteForStagedEvaluation(lowerAggrValIdList,
				    upperAggrValIdList, TRUE);
  };

  ValueIdSet lowerAggrValIds(lowerAggrValIdList);
  ValueIdSet upperAggrValIds(upperAggrValIdList);

  // the value Ids of the grouping expression. 
  ValueIdSet &groupValIds = groupExpr();

  // the size of the grouped row in the hash buffer (not including the 
  // HashRow header).
  ULng32 groupedRowLength = 0;

  // find the number of aggregate and groupby entries
  ULng32 numAttrs
    = ((NOT lowerAggrValIds.isEmpty() ) ? lowerAggrValIds.entries() : 0)
    + ((NOT groupValIds.isEmpty() ) ? groupValIds.entries() : 0);
  
  Attributes ** attrs = new(generator->wHeap()) Attributes * [numAttrs];
					  
  // add the elements in the aggr list to the map table
  Int32 i = 0;
  if (NOT lowerAggrValIds.isEmpty()) {
    for (valId = lowerAggrValIds.init();
	 lowerAggrValIds.next(valId);
	 lowerAggrValIds.advance(valId), i++) {
      attrs[i] = (generator->addMapInfo(valId, 0))->getAttr();

      // For any varchar aggregates to be treated as fixed values in
      // Aligned format (CIF)
      attrs[i]->setForceFixed();
    }
  };

  // create a copy of the group by set. This set is used
  // to move the incoming values to the hash buffer. It is also
  // used to generate the search expression to decide if an incoming
  // row belongs to an existing group
  ValueIdList hbMoveValIds;
  ValueIdList gbyValIdList;
  ValueIdSet hbSearchValIds;

  // remember index of first group attr
  Int32 attrIdx = i;
  
  if (NOT groupValIds.isEmpty()) {
    for (valId = groupValIds.init();
	 groupValIds.next(valId);
	 groupValIds.advance(valId), i++) {
      
      ItemExpr * itemExpr = valId.getItemExpr();
	  
      // add this converted value to the map table.
      Convert * convNode = new(generator->wHeap()) Convert (itemExpr);
	  
      // bind/type propagate the new node
      convNode->bindNode(generator->getBindWA());    
	  
      attrs[i] = 
	(generator->addMapInfo(convNode->getValueId(), 0))->getAttr();
      hbMoveValIds.insert(convNode->getValueId());
      gbyValIdList.insert(valId);

      // add the search condition
      BiRelat * biRelat = new(generator->wHeap())
	BiRelat(ITM_EQUAL, itemExpr, convNode);
      biRelat->setSpecialNulls(-1);
      biRelat->bindNode(generator->getBindWA());
      hbSearchValIds.insert(biRelat->getValueId());
    }
  };

  // Incoming records will be divided equally (sans data skew) among the ESPs
  // so each HGB instance will handle only its share (i.e. divide by #esps)
  Lng32 saveNumEsps = generator->getNumESPs();

  // generate code for child tree
  child(0)->codeGen(generator);
  childTdb = (ComTdb *)(generator->getGenObj());
  
  // This value was originally set inside generator by my parent exchange node
  // as a global variable. Now need to reset the saveNumEsps value back into
  // generator since codegen of my children exchange nodes may have changed it.
  generator->setNumESPs(saveNumEsps);

  ////////////////////////////////////////////////////////////
  // Before generating any expression for this node, set the
  // the expression generation flag not to generate float
  // validation PCode. This is to speed up PCode evaluation
  ////////////////////////////////////////////////////////////
  generator->setGenNoFloatValidatePCode(TRUE);

  ExpTupleDesc::TupleDataFormat tupleFormat = generator->getInternalFormat();

  //determine the tuple format and whether we want to resize rows or not
  NABoolean bmo_affinity = (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO_AFFINITY) == DF_ON);
  NABoolean resizeCifRecord = FALSE;
  NABoolean considerBufferDefrag = FALSE;

  if (! bmo_affinity &&
      getCachedTupleFormat() != ExpTupleDesc::UNINITIALIZED_FORMAT &&
      CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM &&
      CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_SYSTEM)
  {
    resizeCifRecord = getCachedResizeCIFRecord();
    tupleFormat = getCachedTupleFormat();
    considerBufferDefrag = getCachedDefrag() && resizeCifRecord;
  }
  else
  {
     tupleFormat = determineInternalFormat(hbMoveValIds,
                                          this,
                                          resizeCifRecord,
                                          generator,
                                          bmo_affinity,
                                          considerBufferDefrag);
     considerBufferDefrag = considerBufferDefrag && resizeCifRecord;
  }

  // If generating a contiguous move expression where the target tuple data
  // format is a disk format - Packed or Aligned - then header information
  // must be gathered during processing the attributes. This allows a new 
  // header clause to be generated during endExprGen()
  ExpHdrInfo *hdrInfo = NULL;
  if ( ExpTupleDesc::isDiskFormat(tupleFormat) )
    hdrInfo = new( generator->wHeap() )ExpHdrInfo();

  // generate resMoveInExpr and resAggrExpr required in case of
  // partial group by
  if ( isPartialGroupBy || upperAggrValIds.isEmpty() ) {
    // first add a maptable, so that we can get rid of all the
    // stuff added to the map table by the following statements
    MapTable * tempMapTable  = generator->appendAtEnd();
    MapTable * tempMapTable1 = generator->appendAtEnd();
    expGen->processAttributes(numAttrs,
			      attrs,
			      tupleFormat,
			      groupedRowLength,
			      workAtpPos,
			      resultRowAtpIndex,
			      0,
			      ExpTupleDesc::SHORT_FORMAT,
                              0,
                              hdrInfo);

    // generate resAggrExpr 
    if (NOT lowerAggrValIds.isEmpty()) {
      expGen->generateAggrExpr(lowerAggrValIds, ex_expr::exp_AGGR, 
               &resAggrExpr, 0, (NOT groupValIds.isEmpty()));
    }

    // create a copy of the group by set. This set is used
    // to move the incoming values to the result buffer.
    ValueIdSet resMoveValIds;

    if (NOT groupValIds.isEmpty()) {
      for (valId = groupValIds.init();
	   groupValIds.next(valId);
	   groupValIds.advance(valId), attrIdx++) {
      
	ItemExpr * itemExpr = valId.getItemExpr();
	  
	// add this converted value to the map table.
	Convert * convNode = new(generator->wHeap()) Convert (itemExpr);
	  
	// bind/type propagate the new node
	convNode->bindNode(generator->getBindWA());    
	  
	generator->addMapInfo(convNode->getValueId(), attrs[attrIdx]);
	resMoveValIds.insert(convNode->getValueId());

      }

      // generate the move expression. This is used to move the incoming
      // grouping values to the result buffer.
      expGen->generateSetExpr(resMoveValIds,
			      ex_expr::exp_ARITH_EXPR,
			      &resMoveInExpr,
                              -1,
                              hdrInfo);
    };

    // the extra extressions are generated. Lets get rid of the additional
    // map table entries
    generator->removeAll(tempMapTable);
  };

  // change attributes and offsets to represent the rows in the
  // hash buffer
  // Offsets are based on the row starting after the HashRow structure.
  expGen->processAttributes(numAttrs,
			    attrs,
			    tupleFormat,
			    groupedRowLength,
			    workAtpPos,
			    hbRowAtpIndex,
			    0,
			    ExpTupleDesc::SHORT_FORMAT,
                            0,
                            hdrInfo);

  // the size of the grouped row in the hash buffer
  ULng32 extGroupRowLength = groupedRowLength + sizeof(HashRow);

  NADELETEBASIC(attrs, generator->wHeap());

  // generate the hash computation function
  ItemExpr * leftExpr = 0;
  for (valId = groupValIds.init();
       groupValIds.next(valId);     
       groupValIds.advance(valId)) {

    ItemExpr * itemExpr = valId.getItemExpr();

    BuiltinFunction * hashFunction = new(generator->wHeap()) Hash(itemExpr);

    if (leftExpr)
      leftExpr = new(generator->wHeap()) HashComb(leftExpr, hashFunction);
    else
      leftExpr = hashFunction;
  }

  leftExpr = new (generator->wHeap()) Cast(leftExpr, new (generator->wHeap())
					   SQLInt(FALSE, FALSE));
  leftExpr->setConstFoldingDisabled(TRUE);      

  // bind/type propagate the hash evaluation tree
  leftExpr->bindNode(generator->getBindWA());
      
  // add the root value id to the map table. This is the hash value.
  Attributes * mapAttr 
    = (generator->addMapInfo(leftExpr->getValueId(), 0))->getAttr();
  mapAttr->setAtp(workAtpPos);
  mapAttr->setAtpIndex(hashValueAtpIndex);
  ULng32 len;
  ExpTupleDesc::computeOffsets(mapAttr, 
                               ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
                               len);

  // generate code to evaluate the hash expression
  expGen->generateArithExpr(leftExpr->getValueId(),
                            ex_expr::exp_ARITH_EXPR,
                            &hashExpr);

  // Construct the BitMux expression. The BitMux expression computes the 
  // binary encoded key for the grouping operation. The key is stored in 
  // the bitMuxTupp_ of the workAtp.
  //
  // First, construct a list of the item expressions representing the
  // grouping attributes.
  //
  LIST(ItemExpr*) bitMuxAttrList(generator->wHeap());
  for(valId = groupValIds.init();
      groupValIds.next(valId);
      groupValIds.advance(valId)) {

    ItemExpr * ie = valId.getItemExpr();
    const NAType &valType = valId.getType();
    
    if(valType.getTypeQualifier() == NA_CHARACTER_TYPE) {
      
      const CharType &chType = (CharType&)valType;
      
      if ((chType.isCaseinsensitive()) &&
	  (NOT chType.isUpshifted())) {
	ie = new (generator->wHeap()) Upper(ie);
	ie = ie->bindNode(generator->getBindWA());
      }
      // At least for now, don't use BitMux if CZECH collation is involved.
      if (chType.getCollation() == CharInfo::CZECH_COLLATION )
         useBitMux = FALSE;
    }
    
    bitMuxAttrList.insert(ie);
  }

  // Allocate and bind the bit muxing item expression
  //
  ItemExpr *bitMuxItemExpr = new(generator->wHeap())
    ItmBitMuxFunction(bitMuxAttrList);
  bitMuxItemExpr->setConstFoldingDisabled(TRUE);      

  bitMuxItemExpr->bindNode(generator->getBindWA());
  
  // Alter the map table so that the result of the bitMux expression
  // side effects the bitMuxAtpIndex of the workAtp.
  //
  ULng32 keyLength;
  mapAttr = generator->addMapInfo(bitMuxItemExpr->getValueId(), 0)->getAttr();
  mapAttr->setAtp(workAtpPos);
  mapAttr->setAtpIndex(bitMuxAtpIndex);
  ExpTupleDesc::computeOffsets(mapAttr,
			       ExpTupleDesc::SQLARK_EXPLODED_FORMAT,
			       keyLength);

  // a simple heuristic for now until BitMux limitations are better understood
  if (keyLength > 12 ||

      // BitMux becomes a burden once its table gets full, as it adds unneeded
      // checks for every newly incoming group. Don't use BitMux when the
      // groups held in memory are expected to exceed the bitmux table size!

      1000 * 1024 <   // bitmux table size (taken from ex_hash_grby.cpp)
      (Cardinality) getEstRowsUsed().getValue()  *   // expected #groups
      // Approximate memory used per each group in the BitMux table
      // (Detail in ExBitMapTable.cpp; "+3+7" is a cheap ROUND4/8 substitute)
      ( extGroupRowLength + keyLength + 2*sizeof(char *) + 3 + 7 )

      )
    useBitMux = FALSE;

  // There is an arbitrary limit in the standard expression evaluation
  // scheme of MAX_OPERANDS arguments (including the result) for any
  // operation. Thus, the number of items in the BitMux attribute list
  // must be less than MAX_OPERANDS.
  //
  if(bitMuxAttrList.entries() < ex_clause::MAX_OPERANDS && useBitMux) {
    // Finally, generate the clauses for the bit muxing expression.
    //
    expGen->generateArithExpr(bitMuxItemExpr->getValueId(),
			      ex_expr::exp_ARITH_EXPR,
			      &bitMuxExpr);
  }

  // If the BitMux expression is successfully generated.
  //
  // countOffset is the offset within the BitMux buffer to store the count
  // for the row. By default, this is at offset zero which is where the
  // hash value is normally stored for hash buffers. If there is a count(*)
  // aggregate is the lower aggregates, then the countOffset will point
  // to that location instead.
  //
  short bitMuxCountOffset = 0;

  if (NOT lowerAggrValIds.isEmpty()) {
    // generate the hash buffer aggregate expression 
    expGen->generateAggrExpr(lowerAggrValIds, ex_expr::exp_AGGR,
			     &hbAggrExpr, 0, 
                             (NOT groupValIds.isEmpty()));

    if (bitMuxExpr) {
      // Generate the BitMux table aggregate expression. For now the only
      // difference is that the BitMux table already has the count available
      // so the BitMux aggregate expression does not need to recompute the
      // count.
      //
      // First, duplicate the aggregate value id set except for any
      // count(*) aggregates.
      //
      ValueIdSet bitMuxAggrValIds;
      for (valId = lowerAggrValIds.init();
	   lowerAggrValIds.next(valId);
	   lowerAggrValIds.advance(valId), i++) {
	Aggregate *aggr = (Aggregate*)valId.getItemExpr();

	if(aggr->getOperatorType() == ITM_COUNT) {
	  Attributes * attr = generator->getMapInfo(valId)->getAttr();
	  if(bitMuxCountOffset == 0) {
	    bitMuxCountOffset = (short)attr->getOffset();
	  } else {
	    attr->setOffset(bitMuxCountOffset);
	  }
	  continue;
	}

	bitMuxAggrValIds += valId;
      }

      // If there are still aggregates, generate the BitMux aggregate 
      // expression.
      //
      if(NOT bitMuxAggrValIds.isEmpty())
	expGen->generateAggrExpr(bitMuxAggrValIds, ex_expr::exp_AGGR,
				 &bitMuxAggrExpr, 0, 
				 (NOT groupValIds.isEmpty()));
    }
  }

  
  if (NOT groupValIds.isEmpty()) {

    // generate the move expression. This is used to move the incoming
    // grouping values to the hash buffer.
    expGen->generateListExpr(hbMoveValIds,
			     ex_expr::exp_ARITH_EXPR,
			     &hbMoveInExpr,
                             -1, -1,
                             hdrInfo
                             );
    // generate the search expression. This expression is used
    // to look for a matching value in the hash table.
    ItemExpr * newPredTree =
      hbSearchValIds.rebuildExprTree(ITM_AND, TRUE, TRUE);
      
    expGen->generateExpr(newPredTree->getValueId(),
			 ex_expr::exp_SCAN_PRED,
			 &hbSearchExpr);

  };

  // remove all the map tables generated by the child. This leaves us
  // with the map table describing the grouped row in the hash buffer
  generator->removeAll(myMapTable2);

  // generate the expression which moves a result row to the result buffer
  ValueIdList resultValIds;
  ULng32 resultRowLength;
  MapTable * resultValMapTable = NULL;
  ValueIdList moveOutValIdList;
  ValueIdList gendAggrValIdList;

  // add the valIds of the aggregate expressions
  if (NOT lowerAggrValIds.isEmpty()) {

    for (CollIndex i = 0; i < lowerAggrValIdList.entries(); i++)
      {
	valId = lowerAggrValIdList[i];
	ValueId origValId = originalAggrValIds[i];

	// convert lowerAggrValIds to original aggr type before
	// adding to the result row, if the two types are not the same.
	ValueId moveOutValId;
	if (! (valId.getType() == origValId.getType()))
	  {
	    ItemExpr * item_expr = new(generator->wHeap()) Cast(valId.getItemExpr(),
						     &(origValId.getType()));
	    item_expr->bindNode(generator->getBindWA());
	    moveOutValId = item_expr->getValueId();
	  }
	else
	  moveOutValId = valId;

	resultValIds.insert(valId);
	moveOutValIdList.insert(moveOutValId);
	gendAggrValIdList.insert(moveOutValId);
      }
  };
  
  // add the valIds for grouping
  if (NOT hbMoveValIds.isEmpty()) {
    for (CollIndex i = 0; i < hbMoveValIds.entries(); i++)
      {
	valId = hbMoveValIds[i];
	moveOutValIdList.insert(valId);
	resultValIds.insert(valId);
      }
  };
    
  expGen->generateContiguousMoveExpr(moveOutValIdList, //resultValIds,
				     -1, // add convert nodes
				     workAtpPos,
				     resultRowAtpIndex,
				     tupleFormat,
				     resultRowLength,
				     &moveOutExpr, 
				     &tupleDesc,
				     ExpTupleDesc::SHORT_FORMAT,
				     &resultValMapTable);

  // if we do overflow handling, we have to generate ofMoveInExpr,
  // ofSearchExpr, and ofAggrExpr
  if (!isPartialGroupBy) {
    // the map table describes a row in the hash buffer. This is the
    // row we read in from the temporary file. Thus, we change the
    // atp index accordingly. This guarantees, that the row in the
    // hash buffer always has atpindex hbRowAtpIndex.
    // The row from the overflow buffer is always the first parameter
    // for expressions. Thus, the atp is 0.
    for (CollIndex ix = 0; ix < resultValIds.entries(); ix++) {
      valId = resultValIds[ix];
      Attributes * attr = generator->getMapInfo(valId)->getAttr();
      attr->setAtpIndex(ofRowAtpIndex);
      attr->setAtp(0);
    };

    // add the elements of the upper aggregate to the map table
    attrs = new(generator->wHeap()) Attributes * [numAttrs];
    Int32 i = 0;
    if (NOT upperAggrValIds.isEmpty()) {
      for (valId = upperAggrValIds.init();
	   upperAggrValIds.next(valId);
	   upperAggrValIds.advance(valId), i++) {
	attrs[i] = (generator->addMapInfo(valId, 0))->getAttr();

        // For any varchar aggregates to be treated as fixed values in
        // Aligned format (CIF)
        attrs[i]->setForceFixed();
      }
    };

    // create a copy of the group by set. This set is used
    // to move the incoming values from the overflow buffer to the
    // hash buffer. It is also used to generate the search expression
    // to decide if a row from the overflow buffer belongs to an
    // existing group
    ValueIdSet ofMoveValIds;
    ValueIdSet ofSearchValIds;
  
    if (NOT hbMoveValIds.isEmpty()) {
      for (CollIndex j = 0; j < hbMoveValIds.entries(); j++,i++) {
	valId = hbMoveValIds[j];

	ItemExpr * itemExpr = valId.getItemExpr();
	Attributes * hbAttr = (generator->getMapInfo(valId))->getAttr();

	// copy this value and add it to the map table.
	Convert * newItemExpr = new(generator->wHeap()) Convert (itemExpr);
	  
	// bind/type propagate the new node
	newItemExpr->bindNode(generator->getBindWA());    
	  
	attrs[i] = 
	  (generator->addMapInfo(newItemExpr->getValueId(), 0))->getAttr();
	ofMoveValIds.insert(newItemExpr->getValueId());

	// add the search condition
	BiRelat * biRelat = new(generator->wHeap())
	  BiRelat(ITM_EQUAL, itemExpr, newItemExpr);
	biRelat->setSpecialNulls(-1);
	biRelat->bindNode(generator->getBindWA());
	ofSearchValIds.insert(biRelat->getValueId());
      }
    };

    
    // set the attributes and offsets to represent the rows in the
    // hash buffer
    // Offsets are based on the row starting after the HashRow structure.
    expGen->processAttributes(numAttrs,
                              attrs,
                              tupleFormat,
                              groupedRowLength,
                              workAtpPos,
                              hbRowAtpIndex,
                              0,
                              ExpTupleDesc::SHORT_FORMAT,
                              0,
                              hdrInfo);

    NADELETEBASIC(attrs, generator->wHeap());

    // generate the overflow buffer aggregate expression which aggregates
    // rows from the overflow buffer into groups in the hash buffer
    if (NOT upperAggrValIds.isEmpty())
      expGen->generateAggrExpr(upperAggrValIds, ex_expr::exp_AGGR,
			       &ofAggrExpr, 0, 
                               (NOT hbMoveValIds.isEmpty()));

    if (NOT hbMoveValIds.isEmpty()) {
      // generate the move expression. This is used to move the
      // grouping values from the overflow buffer to the hash buffer.
      expGen->generateSetExpr(ofMoveValIds,
			      ex_expr::exp_ARITH_EXPR,
			      &ofMoveInExpr);

      // generate the search expression. This expression is used
      // to look for a matching value in the hash table.
      ItemExpr * newPredTree =
	ofSearchValIds.rebuildExprTree(ITM_AND, TRUE, TRUE);
      
      expGen->generateExpr(newPredTree->getValueId(),
			   ex_expr::exp_SCAN_PRED,
			   &ofSearchExpr );
    };
  };

  if (hdrInfo)
    NADELETEBASIC( hdrInfo, generator->wHeap() );

  // remove all the map tables generated by this node so far
  generator->removeAll(myMapTable1);

  // add the resultMapTable
  generator->appendAtEnd(resultValMapTable);

  // insert the original valueIds into the map table and
  // change the atp of aggregate values to 0.
  // All references to these values from this point on
  // will be at atp = 0, atp_index = resultRowAtpIndex.
  for (CollIndex ix = 0; ix <  lowerAggrValIdList.entries(); ix++) {
    //    valId = lowerAggrValIdList[ix];
    valId = gendAggrValIdList[ix];
      
    // get the "new" attributes
    Attributes * newAggrAttr =
      (generator->getMapInfo(valId))->getAttr();
    
    // insert the original valIds into the map table
    ValueId originalValId = originalAggrValIds[ix];
    MapInfo * originalMapInfo = generator->addMapInfo(originalValId, 0);
    Attributes * originalAggrAttr = originalMapInfo->getAttr();
    
    // set the attributes of the original valIds
    originalAggrAttr->copyLocationAttrs(newAggrAttr);
    originalAggrAttr->setAtp(0);
    originalAggrAttr->setAtpIndex(returnedAtpIndex);
    
    // code has been generated for originalValId and a
    // value is available. Mark it so.
    originalMapInfo->codeGenerated();
  };

  // the hbMoveValIds (set of convert nodes) was used to move the incoming
  // group by values to the aggregate buffer. The child of the convert node
  // is the original grouping column. From this point on, the grouping value
  // is available in the result buffer. Change the buffer attributes of
  // the grouping values in the map table to point to this new location.
  // Also, the atp value of the grouping columns is now 0.
  // This change of attributes is not done if the grouping column
  // is a 'constant'.
  if (NOT hbMoveValIds.isEmpty()) {
    ValueId convValId;

    for (CollIndex i = 0; i < hbMoveValIds.entries(); i++) {
      convValId = hbMoveValIds[i];
      Attributes * newGroupColAttr =
	(generator->getMapInfo(convValId))->getAttr();
	
      ValueId valId = gbyValIdList[i];

      // change the location of the grouping columns, unless they
      // are input to this node. Input values already have location
      // assigned to them.
      if ((NOT getGroupAttr()->getCharacteristicInputs().contains(valId)) &&
           (valId.getItemExpr()->getOperatorType() != ITM_CONSTANT)) {
	MapInfo * mapInfo = generator->addMapInfo(valId, 0);
	Attributes * oldGroupColAttr = mapInfo->getAttr();
	  
	oldGroupColAttr->copyLocationAttrs(newGroupColAttr);
	oldGroupColAttr->setAtp(0);
	oldGroupColAttr->setAtpIndex(returnedAtpIndex);
	  
	// code has been generated for valId and a value is available.
	// Mark it so.
	mapInfo->codeGenerated();
      }
    }
  };

  // generate having expression, if present
  if (NOT selectionPred().isEmpty()) {
    ItemExpr * newPredTree =
      selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE);
    expGen->generateExpr(newPredTree->getValueId(),
	             ex_expr::exp_SCAN_PRED, &havingExpr);

  };

  ExplainTuple *childExplainTuple = generator->getExplainTuple();

  returnedDesc->
#pragma nowarn(1506)   // warning elimination 
    setTupleDescriptor(returnedDesc->noTuples() - 1, tupleDesc);
#pragma warn(1506)  // warning elimination 

  // This estimate on the number of groups should work correctly even when
  // under the right side of a NestedLoopJoin (i.e., multiple probes are
  // sent to this HGB, but the estimate is "per a single probe".)
  // This estimate is only used to determine the number of clusters; the
  // hash-table would adapt dynamically to the number of groups.
  Cardinality expectedRows = (Cardinality) getEstRowsUsed().getValue() ;

  // If this HGB is performed within ESPs, then number of records
  // processed by each ESP is a subset of total records.
  if ( saveNumEsps > 0 )
    expectedRows /= (Cardinality) saveNumEsps ;

  // also, make sure that we don't run into overflow problems.
  // estimatedRowCount_ is an estimate. Therfore it is ok to limit it
  
  // the c89 doesn't handle the direct comparison of float and
  // UINT_MAX correctly. For now we use 4294967295.0!!!!!!!
  if (expectedRows > 4294967295.0)
    expectedRows  = (float)4294967295.0;

  // determine the size of the HGB buffers. This buffer size is used for
  // 1 - store the rows in the hash table (HashTableBuffer)
  // 2 - store the result rows (sql_buffer)
  // first determine the minimum size for the hash table buffers.
  // a buffer has to store at least one (extended) group/row plus tupp_desc

  // get the default value for the buffer size
  ULng32 bufferSize = (ULng32) getDefault(GEN_HGBY_BUFFER_SIZE);

  ULng32 hashBufferSize = extGroupRowLength + sizeof(tupp_descriptor);
  bufferSize = MAXOF( hashBufferSize, bufferSize );

  // minimum result buffer size
  ULng32 resBufferSize = resultRowLength + sizeof(tupp_descriptor);
  bufferSize = MAXOF( resBufferSize, bufferSize );

  short scrthreshold = 
    (short) CmpCommon::getDefaultLong(SCRATCH_FREESPACE_THRESHOLD_PERCENT);
  short hgbGrowthPercent = 
    RelExpr::bmoGrowthPercent(getEstRowsUsed(), getMaxCardEst());

  ComTdbHashGrby * hashGrbyTdb = new(space)
    ComTdbHashGrby(childTdb,
		   givenDesc,
		   returnedDesc,
		   hashExpr,
		   bitMuxExpr,
		   bitMuxAggrExpr,
		   hbMoveInExpr,
		   ofMoveInExpr,
		   resMoveInExpr,
		   hbAggrExpr,
		   ofAggrExpr,
		   resAggrExpr,
		   havingExpr,
		   moveOutExpr,
		   hbSearchExpr,
		   ofSearchExpr,
		   keyLength,
		   resultRowLength,
		   extGroupRowLength,
		   workCriDesc,
		   hbRowAtpIndex,
		   ofRowAtpIndex,
		   hashValueAtpIndex,
		   bitMuxAtpIndex,
		   bitMuxCountOffset,
		   resultRowAtpIndex,
		   returnedAtpIndex,
		   (unsigned short)getDefault(BMO_MEMORY_USAGE_PERCENT),
		   (short)getDefault(GEN_MEM_PRESSURE_THRESHOLD),
                   scrthreshold,
		   (queue_index)getDefault(GEN_HGBY_SIZE_DOWN),
		   (queue_index)getDefault(GEN_HGBY_SIZE_UP),
		   isPartialGroupBy,
		   expectedRows,
		   (Lng32)getDefault(GEN_HGBY_NUM_BUFFERS),
		   bufferSize,
		   getDefault(GEN_HGBY_PARTIAL_GROUP_FLUSH_THRESHOLD),
		   getDefault(GEN_HGBY_PARTIAL_GROUP_ROWS_PER_CLUSTER),
		   (ULng32)getDefault(EXE_HGB_INITIAL_HT_SIZE),
		   // To get the min number of buffers per a flushed cluster
		   // before is can be flushed again
		   (unsigned short)getDefault(EXE_NUM_CONCURRENT_SCRATCH_IOS)
		   + (short)getDefault(COMP_INT_66), // for testing
		   (ULng32) getDefault(COMP_INT_67), // numInBatch
                   hgbGrowthPercent
		   );

  generator->initTdbFields(hashGrbyTdb);
  hashGrbyTdb->setOverflowMode(generator->getOverflowMode());
  if (CmpCommon::getDefault(EXE_DIAGNOSTIC_EVENTS) == DF_ON)
    hashGrbyTdb->setLogDiagnostics(TRUE);
  hashGrbyTdb->setBmoMinMemBeforePressureCheck((Int16)getDefault(EXE_BMO_MIN_SIZE_BEFORE_PRESSURE_CHECK_IN_MB));

  if(generator->getOverflowMode() == ComTdb::OFM_SSD )
    hashGrbyTdb->setBMOMaxMemThresholdMB((UInt16)(ActiveSchemaDB()->
				   getDefaults()).
			  getAsLong(SSD_BMO_MAX_MEM_THRESHOLD_IN_MB));
  else
    hashGrbyTdb->setBMOMaxMemThresholdMB((UInt16)(ActiveSchemaDB()->
				   getDefaults()).
			  getAsLong(EXE_MEMORY_AVAILABLE_IN_MB));

  hashGrbyTdb->setScratchIOVectorSize((Int16)getDefault(SCRATCH_IO_VECTOR_SIZE_HASH));

  double memQuota = 0;

  if(isPartialGroupBy) {
    // The Quota system does not apply to Partial GroupBy
    UInt16 partialMem = 
      (UInt16)(ActiveSchemaDB()->getDefaults()).
      getAsULong(EXE_MEMORY_FOR_PARTIALHGB_IN_MB);
    hashGrbyTdb->setPartialGrbyMemoryMB(partialMem);

    // Test the PASS Thru mode.
    if (CmpCommon::getDefault(COMP_BOOL_159) == DF_ON)
      hashGrbyTdb->setPassPartialRows(TRUE);

  } else {

    // The CQD EXE_MEM_LIMIT_PER_BMO_IN_MB has precedence over the mem quota sys

    NADefaults &defs            = ActiveSchemaDB()->getDefaults();

    UInt16 mmu = UInt16(defs.getAsDouble(EXE_MEM_LIMIT_PER_BMO_IN_MB));

    UInt16 numBMOsInFrag = (UInt16)generator->getFragmentDir()->getNumBMOs();

    if (mmu != 0) {
      hashGrbyTdb->setMemoryQuotaMB(mmu);
      memQuota = mmu;
    } else { 

      // Apply quota system if either one the following two is true:
      //   1. the memory limit feature is turned off and more than one BMOs
      //   2. the memory limit feature is turned on
      NABoolean mlimitPerCPU = defs.getAsDouble(EXE_MEMORY_LIMIT_PER_CPU) > 0;

      if ( mlimitPerCPU || numBMOsInFrag > 1 ||
           (numBMOsInFrag == 1 && CmpCommon::getDefault(EXE_SINGLE_BMO_QUOTA) == DF_ON)) {
        memQuota =
           computeMemoryQuota(generator->getEspLevel() == 0,
                              mlimitPerCPU,
                              generator->getBMOsMemoryLimitPerCPU().value(),
                              generator->getTotalNumBMOsPerCPU(),
                              generator->getTotalBMOsMemoryPerCPU().value(),
                              numBMOsInFrag, 
                              generator->getFragmentDir()->getBMOsMemoryUsage()
                             );

        Lng32 hjGyMemoryLowbound = 
            defs.getAsLong(EXE_MEMORY_LIMIT_LOWER_BOUND_HASHGROUPBY);

        if ( memQuota < hjGyMemoryLowbound )
           memQuota = hjGyMemoryLowbound;

        hashGrbyTdb->setMemoryQuotaMB( UInt16(memQuota) );
      }
    }

    generator->addToTotalOverflowMemory(
          getEstimatedRunTimeOverflowSize(memQuota)
                                    );
  }

  generator->addToTotalOverflowMemory(
           getEstimatedRunTimeOverflowSize(memQuota)
                                     );

  // For debugging overflow only (default is zero == not used).
  hashGrbyTdb->
    setForceOverflowEvery((UInt16)(ActiveSchemaDB()->getDefaults()).
			  getAsULong(EXE_TEST_HASH_FORCE_OVERFLOW_EVERY));

  double hashGBMemEst = getEstimatedRunTimeMemoryUsage(hashGrbyTdb);
  generator->addToTotalEstimatedMemory(hashGBMemEst);

  if ( generator->getRightSideOfFlow() ) 
    hashGrbyTdb->setPossibleMultipleCalls(TRUE);

  Lng32 hgbMemEstInKBPerCPU = (Lng32)(hashGBMemEst / 1024) ;
  hgbMemEstInKBPerCPU = hgbMemEstInKBPerCPU/
    (MAXOF(generator->compilerStatsInfo().dop(),1));
  hashGrbyTdb->setHgbMemEstInMbPerCpu
    ( Float32(MAXOF(hgbMemEstInKBPerCPU/1024,1)) );

  // For now use variable size records whenever Aligned format is
  // used.
  if (resizeCifRecord) {//tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT) {
    hashGrbyTdb->setUseVariableLength();
    if (considerBufferDefrag)
    {
      hashGrbyTdb->setConsiderBufferDefrag();
    }
  }

  hashGrbyTdb->setCIFON((tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT));
  if(!generator->explainDisabled()) {
    generator->setOperEstimatedMemory(hgbMemEstInKBPerCPU);

    generator->setExplainTuple(
       addExplainInfo(hashGrbyTdb, childExplainTuple, 0, generator));

    generator->setOperEstimatedMemory(0);
  }



  // set the new up cri desc.
  generator->setCriDesc(returnedDesc, Generator::UP);

  // restore the original down cri desc since this node changed it.
  generator->setCriDesc(givenDesc, Generator::DOWN);

  generator->setGenObj(this, hashGrbyTdb);

  // reset the expression generation flag to generate float validation pcode
  generator->setGenNoFloatValidatePCode(FALSE);

  // reset the handleIndirectVC flag to its initial value
  expGen->setHandleIndirectVC( vcflag );

  return 0;
} // HashGroupBy::codeGen()


ExpTupleDesc::TupleDataFormat HashGroupBy::determineInternalFormat( const ValueIdList & valIdList,
                                                                   RelExpr * relExpr,
                                                                   NABoolean & resizeCifRecord,
                                                                   Generator * generator,
                                                                   NABoolean bmo_affinity,
                                                                   NABoolean & considerBufferDefrag)
{

  RelExpr::CifUseOptions bmo_cif = RelExpr::CIF_SYSTEM;


  if (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_OFF)
  {
    bmo_cif = RelExpr::CIF_OFF;
  }
  else
  if (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_ON)
  {
    bmo_cif = RelExpr::CIF_ON;
  }

  //CIF_SYSTEM

  return generator->determineInternalFormat(valIdList, 
                                            relExpr, 
                                            resizeCifRecord, 
                                            bmo_cif,
                                            bmo_affinity,
                                            considerBufferDefrag);

}

CostScalar HashGroupBy::getEstimatedRunTimeMemoryUsage(NABoolean perCPU)
{
  GroupAttributes * childGroupAttr = child(0).getGroupAttr();
  const CostScalar childRecordSize = childGroupAttr->getCharacteristicOutputs().getRowLength();
  const CostScalar childRowCount = getEstRowsUsed(); // the number of 
                                                     // distinct rows groupped
  // Each record also uses a header (HashRow) in memory (8 bytes for 32bit).
  // Hash tables also take memory -- they are about %50 longer than the 
  // number of entries.
  const ULng32 
    memOverheadPerRecord = sizeof(HashRow) + sizeof(HashTableHeader) * 3 / 2 ;

  // totalHashTableMemory is for all CPUs at this point of time.
  CostScalar totalHashTableMemory = 
    childRowCount * (childRecordSize + memOverheadPerRecord);

  if ( perCPU == TRUE ) {
     const PhysicalProperty* const phyProp = getPhysicalProperty();
     if (phyProp)
     {
       PartitioningFunction * partFunc = phyProp -> getPartitioningFunction() ;

      // totalHashTableMemory is per CPU at this point of time.
       totalHashTableMemory /= partFunc->getCountOfPartitions();
     }
  }
  return totalHashTableMemory;
}

double HashGroupBy::getEstimatedRunTimeMemoryUsage(ComTdb * tdb)
{
  CostScalar totalHashTableMemory = getEstimatedRunTimeMemoryUsage(FALSE);

  double memoryLimitPerCpu;
  ULng32 memoryQuotaInMB = ((ComTdbHashGrby *)tdb)->memoryQuotaMB();
  if (memoryQuotaInMB)
    memoryLimitPerCpu = memoryQuotaInMB * 1024 * 1024 ;
  else if ( isNotAPartialGroupBy() == FALSE)
  {
    memoryLimitPerCpu = 
      ActiveSchemaDB()->getDefaults().getAsLong(EXE_MEMORY_FOR_PARTIALHGB_IN_MB) * 1024 * 1024;
  }
  else
  {
    memoryLimitPerCpu = 
        ActiveSchemaDB()->getDefaults().getAsLong(EXE_MEMORY_AVAILABLE_IN_MB) * 1024 * 1024 ;
  }

  const PhysicalProperty* const phyProp = getPhysicalProperty();
  Lng32 numOfStreams = 1;
  if (phyProp)
  {
    PartitioningFunction * partFunc = phyProp -> getPartitioningFunction() ;
    numOfStreams = partFunc->getCountOfPartitions();
  }

  CostScalar memoryPerCpu = totalHashTableMemory/numOfStreams ;
  if ( memoryPerCpu > memoryLimitPerCpu ) 
  {
      memoryPerCpu = memoryLimitPerCpu;
  }
  totalHashTableMemory = memoryPerCpu * numOfStreams ;
  return totalHashTableMemory.value();
}

double HashGroupBy::getEstimatedRunTimeOverflowSize(double memoryQuotaMB)
{

  if ( memoryQuotaMB > 0 ) {

     CostScalar memoryUsage =
        getEstimatedRunTimeMemoryUsage(TRUE /*per CPU*/);

     double delta = memoryUsage.getValue() - memoryQuotaMB * COM_ONE_MEG ;

     if ( delta > 0 ) {
        const PhysicalProperty* const phyProp = getPhysicalProperty();
        Lng32 pipelines = 1;
   
        if (phyProp)
        {
          PartitioningFunction * partFunc = 
                   phyProp -> getPartitioningFunction() ;
   
          if ( partFunc )
             pipelines = partFunc -> getCountOfPartitions();
        }
   
   
        return delta * pipelines;
     } 
  } 

  return 0;

}

/////////////////////////////////////////////////////////
//
// GroupByAgg::codeGen()
//
//  Used by SortGroupBy and ShortCutGroupBy 
/////////////////////////////////////////////////////////

short GroupByAgg::codeGen(Generator * generator) {

  Space * space = generator->getSpace();
  
  // create a map table for returned group by and aggregate values
  MapTable * myMapTable = generator->appendAtEnd();

  ////////////////////////////////////////////////////////////////////////////
  //
  // Layout at this node:
  //
  // |-------------------------------------------------|
  // | input data  |  Grouped data | child's data      |
  // | ( I tupps ) |  ( 1 tupp )   | ( C tupps )       |
  // |-------------------------------------------------|
  // <-- returned row to parent --->
  // <------------ returned row from child ------------>
  //
  // input data:        the atp input to this node by its parent. 
  // grouped data:      tupp where the aggr/grouped result is moved
  // child data:        tupps appended by the left child
  //
  // Input to child:    I + 1 tupps
  //
  // Tupps returned from child are only used to create the
  // grouped data. They are not returned to parent.
  //
  ////////////////////////////////////////////////////////////////////////////

  ex_cri_desc * givenDesc
    = generator->getCriDesc(Generator::DOWN);

  ex_cri_desc * returnedDesc 
#pragma nowarn(1506)   // warning elimination 
    = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); 
#pragma warn(1506)  // warning elimination 
  
  generator->setCriDesc(returnedDesc, Generator::DOWN);

  ComTdb * childTdb = 0;

  ex_expr * aggrExpr = 0;
  ex_expr * havingExpr = 0;
  ex_expr * grbyExpr = 0;
  ex_expr * moveExpr = 0;

  ExpTupleDesc * tupleDesc = 0;
  
  genAggrGrbyExpr(generator,
		  aggregateExpr(),
                  groupExpr(),
		  rollupGroupExprList(),
		  selectionPred(),
		  1,
		  returnedDesc->noTuples() - 1, 
		  (short) (returnedDesc->noTuples() - 1),
		  &aggrExpr,
		  &grbyExpr,
		  &moveExpr,
		  &havingExpr,
		  &childTdb,
		  &tupleDesc);


  ExplainTuple *childExplainTuple = generator->getExplainTuple();

  returnedDesc->
#pragma nowarn(1506)   // warning elimination 
    setTupleDescriptor(returnedDesc->noTuples() - 1, tupleDesc); 
 
  ComTdbSortGrby * sortGrbyTdb 
    = new(space) ComTdbSortGrby(aggrExpr,
				grbyExpr,
				moveExpr,
				havingExpr,
				tupleDesc->tupleDataLength(),
				returnedDesc->noTuples()-1,
				childTdb,
				givenDesc,
				returnedDesc,
				(queue_index)getDefault(GEN_SGBY_SIZE_DOWN),
				(queue_index)getDefault(GEN_SGBY_SIZE_UP),
				(Cardinality) getGroupAttr()->
				getOutputLogPropList()[0]->
				getResultCardinality().value(),
				getDefault(GEN_SGBY_NUM_BUFFERS),
				getDefault(GEN_SGBY_BUFFER_SIZE),
				generator->getTolerateNonFatalError());
#pragma warn(1506)  // warning elimination 
  generator->initTdbFields(sortGrbyTdb);

  if (isRollup())
    {
      sortGrbyTdb->setIsRollup(TRUE);

      sortGrbyTdb->setNumRollupGroups(rollupGroupExprList().entries());
    }

  if(!generator->explainDisabled()) {
    generator->setExplainTuple(
         addExplainInfo(sortGrbyTdb, childExplainTuple, 0, generator));
  }
 
  // set the new up cri desc.
  generator->setCriDesc(returnedDesc, Generator::UP);

  // restore the original down cri desc since this node changed it.
  generator->setCriDesc(givenDesc, Generator::DOWN);

  generator->setGenObj(this, sortGrbyTdb);

  return 0;
} // GroupByAgg::codeGen()



I take it that we cannot stick a GROUP BY clause after a ROLLUP?