@@ -7880,11 +7880,17 @@ NABoolean GroupByAgg::tryToPullUpPredicatesInPreCodeGen(
       else
         pulledPredicates += tempPulledPreds;
 
+      // just remove pulled up predicates from char. input
+      ValueIdSet newInputs(getGroupAttr()->getCharacteristicInputs());
+      myLocalExpr += selectionPred();
+      myLocalExpr -= tempPulledPreds;
+      myLocalExpr.weedOutUnreferenced(newInputs);
+      
       // adjust char. inputs - this is not exactly
       // good style, just overwriting the char. inputs, but
       // hopefully we'll get away with it at this stage in
       // the processing
-      getGroupAttr()->setCharacteristicInputs(myNewInputs);
+      getGroupAttr()->setCharacteristicInputs(newInputs);
     }
 
   // note that we removed these predicates from our node, it's the
/***********************************************************************
// @@@ 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:         RelExpr.C
* Description:  Relational expressions (both physical and logical operators)
* Created:      5/17/94
* Language:     C++
*
*
******************************************************************************
*/

#define   SQLPARSERGLOBALS_FLAGS	// must precede all #include's
#define   SQLPARSERGLOBALS_NADEFAULTS

#include "Debug.h"
#include "Sqlcomp.h"
#include "AllRelExpr.h"
#include "AllItemExpr.h"
#include "GroupAttr.h"
#include "opt.h"
#include "PhyProp.h"
#include "mdam.h"
#include "ControlDB.h"
#include "disjuncts.h"
#include "ScanOptimizer.h"
#include "CmpContext.h"
#include "StmtDDLCreateTrigger.h"
#include "ExpError.h"
#include "ComTransInfo.h"
#include "BindWA.h"
#include "Refresh.h"
#include "CmpMain.h"
#include "ControlDB.h"
#include "ElemDDLColDef.h"
#include "Analyzer.h"
#include "OptHints.h"
#include "ComTdbSendTop.h"
#include "DatetimeType.h"
#include "SequenceGeneratorAttributes.h"
#include "SqlParserGlobals.h"	
#include "AppliedStatMan.h"
#include "Generator.h"
#include "CmpStatement.h"

#define TEXT_DISPLAY_LENGTH 1001

// ----------------------------------------------------------------------
// forward declarations
// ----------------------------------------------------------------------

// -----------------------------------------------------------------------
// methods for class ExprGroupId
// -----------------------------------------------------------------------

ExprGroupId::ExprGroupId()
{
  groupIdMode_ = STANDALONE;
  node_        = NULL;
  groupId_     = INVALID_GROUP_ID;
}

ExprGroupId::ExprGroupId(const ExprGroupId & other)
{
  groupIdMode_ = other.groupIdMode_;
  node_        = other.node_;
  groupId_     = other.groupId_;
}

ExprGroupId::ExprGroupId(RelExpr *node)
{
  groupIdMode_ = STANDALONE;
  node_        = node;
  groupId_     = INVALID_GROUP_ID;
}

ExprGroupId::ExprGroupId(CascadesGroupId groupId)
{
  groupIdMode_ = MEMOIZED;
  node_        = NULL;
  groupId_     = groupId;
}

ExprGroupId & ExprGroupId::operator = (const ExprGroupId & other)
{
  groupIdMode_ = other.groupIdMode_;
  node_        = other.node_;
  groupId_     = other.groupId_;
  return *this;
}

ExprGroupId & ExprGroupId::operator = (RelExpr * other)
{
  if (groupIdMode_ == MEMOIZED)
    {
      // Trying to assign an actual pointer to an ExprGroupId that
      // is in CascadesMemo. This is materialization of a binding.
      groupIdMode_ = BINDING;
    }
  else if (groupIdMode_ == BINDING)
    // sanity check, can't directly overwrite another binding
    ABORT("Didn't call BINDING::release_expr()");

  node_ = other;

  return *this;
}

ExprGroupId & ExprGroupId::operator = (CascadesGroupId other)
{
  // The expression is now (again) in CascadesMemo without participating in
  // a binding. This may happen when an expression is copied into CascadesMemo
  // (groupIdMode_ was STANDALONE) or when a binding is released (groupIdMode_
  // was BINDING). The node_ member is no longer a valid pointer.

  if (groupIdMode_ == BINDING && groupId_ != other)
    ABORT("can't change group of an expression during release of binding");

  groupIdMode_ = MEMOIZED;
  groupId_ = other;
  node_ = NULL;

  return *this;
}

NABoolean ExprGroupId::operator == (const ExprGroupId &other) const
{
  // if the two operands have mode ...	 	then do this:
  // ----------------------------------------   ------------------
  // STANDALONE-STANDALONE:			ptrs must match
  // STANDALONE-MEMOIZED:	(x)		return FALSE
  // STANDALONE-BINDING:			ptrs must match
  // MEMOIZED-MEMOIZED:		(x)		groups must match
  // MEMOIZED-BINDING:		(x)		groups must match
  // BINDING-BINDING:				ptrs must match

  if (node_ == NULL OR other.getPtr() == NULL)
    return (groupId_ == other.getGroupId());    // cases with (x)
  else
    return (node_ == other.getPtr());           // ptrs must match
}

NABoolean ExprGroupId::operator == (const RelExpr *other) const
{
  CMPASSERT(groupIdMode_ != MEMOIZED);
  return node_ == other;
}

CascadesGroupId ExprGroupId::getGroupId() const
 { return ((groupIdMode_ != STANDALONE)? groupId_ : INVALID_GROUP_ID); }

void ExprGroupId::releaseBinding()
{
  if (groupIdMode_ != BINDING)
    ABORT("binding to release was not established");

  groupIdMode_ = MEMOIZED;
  node_ = NULL;
}

void ExprGroupId::convertBindingToStandalone()
{
  groupIdMode_ = STANDALONE;
}

void ExprGroupId::setGroupAttr(GroupAttributes *gaPtr)
{
  // If the expression is either in the standalone mode or is
  // a part of a binding, then store the Group Attributes
  // in the node. Group attributes in Cascades can not be set through
  // an individual expression and an attempt to do this results in an abort.

  CMPASSERT(groupIdMode_ == STANDALONE);
  node_->setGroupAttr(gaPtr);
} // ExprGroupId::setGroupAttr()

GroupAttributes * ExprGroupId::getGroupAttr() const
{
  CMPASSERT(node_ != NULL OR groupIdMode_ == MEMOIZED);

  // If the expression is either in the standalone mode or is
  // a part of a binding, then use the Group Attributes that
  // are stored in the node.
  if (node_ != NULL)
    return node_->getGroupAttr();
  else
    // otherwise, use the Cascades group's group attributes
    return  (*CURRSTMT_OPTGLOBALS->memo)[groupId_]->getGroupAttr();
} // ExprGroupId::getGroupAttr()

// shortcut to get the output estimated log props out of the group
// attributes
EstLogPropSharedPtr ExprGroupId::outputLogProp(const EstLogPropSharedPtr& inputLogProp)
{
  return getGroupAttr()->outputLogProp(inputLogProp);
}

// a shortcut to get the bound expression, if it exists ...
// or the first logical expression inserted in the Cascades Group or NULL.
// Note:  The last log expr in the list is the first one inserted.
RelExpr * ExprGroupId::getLogExpr() const
{
  if (node_ != NULL)
    return node_;
  else if (groupId_ != INVALID_GROUP_ID)
    return ((*CURRSTMT_OPTGLOBALS->memo)[groupId_]->getLastLogExpr());
  return 0;
}

RelExpr * ExprGroupId::getFirstLogExpr() const
{
  if (node_ != NULL)
    return node_;
  else if (groupId_ != INVALID_GROUP_ID)
    return ((*CURRSTMT_OPTGLOBALS->memo)[groupId_]->getFirstLogExpr());
  return 0;
}

// -----------------------------------------------------------------------
// member functions for class RelExpr
// -----------------------------------------------------------------------

THREAD_P ObjectCounter (*RelExpr::counter_)(0);

RelExpr::RelExpr(OperatorTypeEnum otype,
		 RelExpr  *leftChild,
		 RelExpr  *rightChild,
		 CollHeap *outHeap)
     : ExprNode(otype)
  ,selection_(NULL)
  ,RETDesc_(NULL)
  ,groupAttr_(NULL)
  ,groupId_(INVALID_GROUP_ID)
  ,groupNext_(NULL)
  ,bucketNext_(NULL)
  ,operatorCost_(NULL)
  ,rollUpCost_(NULL)
  ,physProp_(NULL)
  ,estRowsUsed_((Cardinality)-1)
  ,inputCardinality_((Cardinality)-1)
  ,maxCardEst_((Cardinality)-1)
  ,contextInsensRules_(outHeap)
  ,contextSensRules_(outHeap)
  ,accessSet0_(NULL) // Triggers --
  ,accessSet1_(NULL)
  ,uniqueColumnsTree_(NULL) //++MV
  ,cardConstraint_(NULL) //++MV
  ,isinBlockStmt_(FALSE)
  ,firstNRows_(-1)
  ,flags_(0)
  ,rowsetIterator_(FALSE)
  ,tolerateNonFatalError_(UNSPECIFIED_)
  ,hint_(NULL)
  ,markedForElimination_(FALSE)
  ,isExtraHub_(FALSE)
  ,potential_(-1)
  ,seenIUD_(FALSE)
  ,parentTaskId_(0)
  ,stride_(0)
  ,birthId_(0)
  ,memoExprId_(0)
  ,sourceMemoExprId_(0)
  ,sourceGroupId_(0)
  ,costLimit_(-1)
  ,cachedTupleFormat_(ExpTupleDesc::UNINITIALIZED_FORMAT)
  ,cachedResizeCIFRecord_(FALSE)
  ,dopReduced_(FALSE)
  ,originalExpr_(NULL)
  ,operKey_(outHeap)
{

  child_[0] = leftChild;
  child_[1] = rightChild;
  (*counter_).incrementCounter();
  //  QSTUFF
  setGroupAttr(new (outHeap) GroupAttributes);
  // QSTUFF
}

RelExpr::~RelExpr()
{
  // the group attributes maintain a reference count
  if (groupAttr_ != NULL)
    groupAttr_->decrementReferenceCount();

  // these data structures are always owned by the tree
  delete selection_;

  // delete all children, if this is a standalone query
  // (NOTE: can't use the virtual function getArity() in a destructor!!!)
  for (Lng32 i = 0; i < MAX_REL_ARITY; i++)
    {
      if (child(i).getMode() == ExprGroupId::STANDALONE)
	{
	  // the input was not obtained from CascadesMemo, so delete it
	  if (child(i).getPtr() != NULL)
	    delete child(i).getPtr();
	}
    }
  (*counter_).decrementCounter();

  delete cardConstraint_; //++MV

  if (hint_) delete hint_;
} // RelExpr::~RelExpr()


Int32 RelExpr::getArity() const
{
  switch (getOperatorType())
    {
    case REL_SCAN:
      return 0;

    case REL_EXCHANGE:
      return 1;

    case REL_JOIN:
    case REL_TSJ:
    case REL_ROUTINE_JOIN:
    case REL_SEMIJOIN:
    case REL_SEMITSJ:
    case REL_ANTI_SEMIJOIN:
    case REL_ANTI_SEMITSJ:
    case REL_LEFT_JOIN:
    case REL_FULL_JOIN:
    case REL_LEFT_TSJ:
    case REL_NESTED_JOIN:
    case REL_MERGE_JOIN:
    case REL_INTERSECT:
    case REL_EXCEPT:
      return 2;

    default:
      ABORT("RelExpr with unknown arity encountered");
      return 0;
    }
}

void RelExpr::deleteInstance()
{
  Int32 nc = getArity();
  // avoid deleting the children by resetting all child pointers first
  for (Lng32 i = 0; i < nc; i++)
    {
      child(i) = (RelExpr *) NULL;
    }
  delete this;
} // RelExpr::deleteInstance()

TableMappingUDF *RelExpr::castToTableMappingUDF()
{
  return NULL;
}

ExprNode * RelExpr::getChild(Lng32 index)
{
  return child(index);
} // RelExpr::getChild()

void RelExpr::setChild(Lng32 index, ExprNode * newChild)
{
  if (newChild)
    {
      CMPASSERT(newChild->castToRelExpr());
      child(index) = newChild->castToRelExpr();
    }
  else
    child(index) = (RelExpr *)NULL;
} // RelExpr::setChild()

// get TableDesc from the expression. It could be directly
// attached to the expression, as in Scan, or could be a
// part of GroupAnalysis, as in cut-opp. For expressions
// which do not have a tableDesc attached to them, like Join
// it would be NULL

TableDesc*
RelExpr::getTableDescForExpr()
{
  TableDesc * tableDesc = NULL;

  if (getOperatorType() == REL_SCAN)
  {
	tableDesc = ((Scan *)this)->getTableDesc();
  }
  else
  {
	if(getGroupAttr()->getGroupAnalysis() &&
	   getGroupAttr()->getGroupAnalysis()->getNodeAnalysis() )
	{
	  TableAnalysis * tableAnalysis = getGroupAttr()->getGroupAnalysis()->getNodeAnalysis()->getTableAnalysis();
	  if (tableAnalysis)
		tableDesc = tableAnalysis->getTableDesc();
	}
  }

  return tableDesc;
}

// This method clears all logical expressions uptill the leaf node
// for multi-join. The methid should be called only before optimization phases
// Reason for that is (1)it is very expensive to synthLogProp and should be avoided
// (2) we are resetting number of joined tables, which should not be done once it is
// set during optimization phases

void RelExpr::clearLogExprForSynthDuringAnalysis()
{
	Int32 numChildren = getArity();

	if (numChildren >= 1) 
	{
		GroupAttributes * grp = getGroupAttr();
		grp->setLogExprForSynthesis(NULL); 
		grp->resetNumJoinedTables(1);
	}

	// clear the log expr for all children
	for (Lng32 i = 0; i < numChildren; i++)
	{
		// only if the child is not a CascadesGroup or NULL
		if (child(i).getPtr() != NULL)
		{
			child(i)->clearLogExprForSynthDuringAnalysis();
		}
	}           
}

void RelExpr::releaseBindingTree(NABoolean memoIsMoribund)
{
  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
    {
      if (memoIsMoribund || child(i).getMode() == ExprGroupId::BINDING)
	{
	  // recursively release the bindings of the children's children
	  if (child(i).getPtr() != NULL)
	    child(i)->releaseBindingTree(memoIsMoribund);

	  // release the bindings to the children
	  child(i).convertBindingToStandalone();
	}
    } // for each child

  // indicate that this expression is no longer part of CascadesMemo,
  // (although its groupNext_ and bucketNext_ pointers are still valid)
  groupId_ = INVALID_GROUP_ID;

  if (memoIsMoribund)
    {
      groupAttr_ = NULL;
      groupNext_ = bucketNext_ = NULL;
    }

}

void RelExpr::addSelPredTree(ItemExpr *selpred)
{
  ExprValueId sel = selection_;
  ItemExprTreeAsList(&sel, ITM_AND).insert(selpred);
  selection_ = sel.getPtr();
} // RelExpr::addSelPredTree()

ItemExpr * RelExpr::removeSelPredTree()
{
  ItemExpr * result = selection_;
  selection_ = NULL;
  return result;
} // RelExpr::removeSelPredTree()

//++ MV -
void RelExpr::addUniqueColumnsTree(ItemExpr *uniqueColumnsTree)
{
  ExprValueId t = uniqueColumnsTree_;

  ItemExprTreeAsList(&t, ITM_ITEM_LIST).insert(uniqueColumnsTree);
  uniqueColumnsTree_ = t.getPtr();
}

ItemExpr *RelExpr::removeUniqueColumnsTree()
{
  ItemExpr *result = uniqueColumnsTree_;
  uniqueColumnsTree_ = NULL;

  return result;
}
// MV--

void RelExpr::setGroupAttr(GroupAttributes *gaPtr)
{
  // the new group attributes are now used in one more place
  if (gaPtr != NULL)
    gaPtr->incrementReferenceCount();

  // the old group attributes are now used in one place less than before
  // NOTE: old and new group attribute pointers may be the same
  if (groupAttr_ != NULL)
    groupAttr_->decrementReferenceCount();

  // now assign the new group attribute pointer to the local data member
  groupAttr_ = gaPtr;
}

NABoolean RelExpr::reconcileGroupAttr(GroupAttributes *newGroupAttr)
{
  // make sure the new group attributes have all the information needed
  // and are not inconsistent
  newGroupAttr->reconcile(*groupAttr_);

  // unlink from the current group attributes and adopt the new (compatible)
  // ones
  setGroupAttr(newGroupAttr);

  return FALSE; // no re-optimization for now
}

RelExpr * RelExpr::castToRelExpr()
{
  return this;
}

const RelExpr * RelExpr::castToRelExpr() const
{
  return this;
}

NABoolean RelExpr::isLogical() const   { return TRUE;  }

NABoolean RelExpr::isPhysical() const  { return FALSE; }

NABoolean RelExpr::isCutOp() const     { return FALSE; }

NABoolean RelExpr::isSubtreeOp() const { return FALSE; }

NABoolean RelExpr::isWildcard() const  { return FALSE; }

ItemExpr * RelExpr::selectList()
{
  // RelRoot redefines this virtual method (see BindRelExpr.cpp);
  // Tuple and Union use this standard method.
  RETDesc *rd = getRETDesc();
  if (rd) {
    ValueIdList vids;
    const ColumnDescList &cols = *rd->getColumnList();
    for (CollIndex i = 0; i < cols.entries(); i++)
      vids.insert(cols[i]->getValueId());
    return vids.rebuildExprTree(ITM_ITEM_LIST);
  }
  return NULL;
}

SimpleHashValue RelExpr::hash()
{
  // this method is just defined to have a hash method in ExprNode
  // without referencing class HashValue (which is too complicated
  // for the common code directory)
  return treeHash().getValue();
}

HashValue RelExpr::topHash()
{
  HashValue result = (Int32) getOperatorType();

  // hash the required input and output values from the GroupAttributes
  if (groupAttr_ != NULL)
    result ^= groupAttr_->hash();

  // hash the ValueIdSet of the selection predicates
  result ^= predicates_;

  // the other data members are not significant for the hash function
  CMPASSERT(selection_ == NULL); // this method doesn't work in the parser

  return result;
}

// this method is not virtual, since combining the hash values of the
// top node and its children should be independent of the actual node
HashValue RelExpr::treeHash()
{
  HashValue result = topHash();
  Int32 maxc = getArity();

  for (Lng32 i = 0; i < maxc; i++)
    {
      if (child(i).getMode() == ExprGroupId::MEMOIZED)
	// use the numbers of the input CascadesGroup
	result ^= child(i).getGroupId();
      else
	// call this method recursively for the children
	result ^= child(i)->treeHash();
    }

  return result;
}

NABoolean RelExpr::patternMatch(const RelExpr & other) const
{
  return getOperator().match(other.getOperator());
}

// Checks if the selection preds at this join node are of the
// form FKtable.col1 = UKTable.col1 and FKtable.col1 = UKTable.col1
// and ..., where FKTable.col1 is the FK column that points to
// UKTable.col1.
// The third arguments matchingPreds is an output parameter.
// It is used to send the a list of FKtable.col1 = UKTable.col1
// type predicates back to the caller, so that it can be used
// to adjust the selection preds and equiJoinPreds in the join
// node.
NABoolean Join::hasRIMatchingPredicates(const ValueIdList& fkCols,
					const ValueIdList& ucCols,
					const TableDesc * compRefTabId,
					ValueIdSet & matchingPreds) const
{
  // if the size of the fkCols does not match with ucCols then something is wrong.
  // We also assume below that corresponding cols have identical positions
  // in the two valueidlists and that all entries here are in terms of VEG.
  CMPASSERT(fkCols.entries() == ucCols.entries());
  // There is not possibility of finding a full match
  // if number ofselection preds is smaller than the fkCols.
  // number of selection preds can be larger than fkCols.entries,
  // for example there may be a predicate on the fktable and some
  // other table which is being joined up above. Since the fktable
  // is below this join, this join will have that predicate.
  if ((getSelectionPredicates().entries() < fkCols.entries()))
      return FALSE;

  ValueIdList localFKCols(fkCols);
  ValueIdList localUCCols(ucCols);

  ValueIdSet compRefTabNonUCCols(compRefTabId->getColumnVEGList());
  ValueIdSet localUCColsSet(localUCCols);
  compRefTabNonUCCols -= localUCColsSet;

  NABoolean matchFound = FALSE;
  const ValueIdSet& selPreds = getSelectionPredicates();
  matchingPreds.clear();

  for (ValueId x = selPreds.init();
	    selPreds.next(x);
	    selPreds.advance(x))
  {
    ItemExpr *ie = x.getItemExpr();
    matchFound = FALSE;
    if (ie->getOperatorType() == ITM_VEG_PREDICATE)
    {
      ValueId vegRef = ((VEGPredicate *)ie)->getVEG()->getVEGReference()->getValueId();
      CollIndex fkidx = localFKCols.index(vegRef);
      if ((fkidx != NULL_COLL_INDEX)&&(localUCCols[fkidx] == vegRef))
      {
	localFKCols.removeAt(fkidx);
	localUCCols.removeAt(fkidx);
	matchingPreds.insert(x);
      }
      if (compRefTabNonUCCols.contains(vegRef))
      {
	// return false on a predicate
	// of the form fktable.x = uniquetable.x where x  is a nonkey column.
	  matchingPreds.clear();
	  return FALSE;
      }
    }
    else if ((ie->getOperatorType() == ITM_EQUAL)&&
	    (ie->child(0)->getOperatorType() == ITM_VEG_REFERENCE)&&
	    (ie->child(1)->getOperatorType() == ITM_VEG_REFERENCE))
    {
      ValueId vegRef0 = ((VEGReference *)ie->child(0).getPtr())->getValueId();
      ValueId vegRef1 = ((VEGReference *)ie->child(1).getPtr())->getValueId();
      ValueId ukVid = NULL_COLL_INDEX;
      CollIndex fkidx = localFKCols.index(vegRef0);
      if (fkidx == NULL_COLL_INDEX)
      {
	CollIndex fkidx = localFKCols.index(vegRef1);
	if (fkidx != NULL_COLL_INDEX)
	  ukVid = vegRef0;
      }
      else
	ukVid = vegRef1;
      if ((fkidx != NULL_COLL_INDEX)&&(localUCCols[fkidx] == ukVid))
      {
	localFKCols.removeAt(fkidx);
	localUCCols.removeAt(fkidx);
	matchingPreds.insert(x);
      }
    }
    else
    {
      matchingPreds.clear();
      return FALSE; // not a VEG Pred (revisit for char-varchar)
    }
  }
  if (localFKCols.isEmpty())
    return TRUE ; // all preds have a match with a FK-UC column pair.
  else
  {
    matchingPreds.clear();
    return FALSE;
  }
}


// Special method added to check for ordered cross product called by
// RequiredPhysicalProperty::satisfied() to ensure that if a CQS has
// requested an ordered cross product, then one is being produced.
NABoolean HashJoin::patternMatch(const RelExpr &other) const
{
  if (other.getOperator() == REL_FORCE_ORDERED_CROSS_PRODUCT)
    return ((HashJoin *) this)->isOrderedCrossProduct();



  else
    return RelExpr::patternMatch(other);
}

// Two trees match, if their top nodes and their children are duplicates
// (are the same logical or physical expression). This method provides
// the generic part for determining a match. It can be called by
// redefined virtual methods of derived classes.
NABoolean RelExpr::duplicateMatch(const RelExpr & other) const
{
  if (getOperatorType() != other.getOperatorType())
    return FALSE;

  CMPASSERT(selection_ == NULL); // this method doesn't work in the parser

  if (predicates_ != other.predicates_)
    return FALSE;

  if (rowsetIterator_ != other.rowsetIterator_)
    return FALSE;

  if (tolerateNonFatalError_ != other.tolerateNonFatalError_)
    return FALSE;

  Int32 maxc = getArity();

  // determine whether the children match
  for (Lng32 i = 0; i < maxc; i++)
    {
      // different situations, depending on whether the child
      // and the other node's child is in state MEMOIZED,
      // BINDING, or STANDALONE. See ExprGroupId::operator ==
      // for an explanation for each of the cases

      if (child(i).getMode() == ExprGroupId::MEMOIZED OR
	  other.child(i).getMode() == ExprGroupId::MEMOIZED)
	{
	  // cases marked (x) in ExprGroupId::operator ==
	  // (groups must match)
	  if (NOT (child(i) == other.child(i)))
	    return FALSE;
	}
      else
	{
	  // outside of CascadesMemo or in a CascadesBinding, then
	  //  call this method recursively for the children
	  if (NOT child(i)->duplicateMatch(*other.child(i).getPtr()))
	    return FALSE;
	}
    }

  return TRUE;
}
const CorrName RelExpr::invalid = CorrName("~X~invalid");

RelExpr * RelExpr::copyTopNode(RelExpr *derivedNode,CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) RelExpr(getOperatorType(),
				   NULL,
				   NULL,
				   outHeap);
  else
    result = derivedNode;

  // don't copy pointers to required input/output values, since we don't
  // allow duplicate expressions the new node is likely to get new group
  // attributes

  // copy selection predicates
  result->predicates_ = predicates_;

  // copy pointer to the selection expression tree (Parser only)
  if (selection_ != NULL)
    result->selection_ = selection_->copyTree(outHeap)->castToItemExpr();

  // -- Triggers
  // Copy the inlining information and access sets.
  result->getInliningInfo().merge(&getInliningInfo());
  result->setAccessSet0(getAccessSet0());
  result->setAccessSet0(getAccessSet0());

  //++MV -

  result->setUniqueColumns(getUniqueColumns());

  if (uniqueColumnsTree_ != NULL)
    result->uniqueColumnsTree_ =
      uniqueColumnsTree_->copyTree(outHeap)->castToItemExpr();
  //--MV -

  // leave any physical properties or CascadesMemo-related data
  // off the returned result ??? (see RelExpr::save)

  result->setBlockStmt(isinBlockStmt());

  result->setFirstNRows(getFirstNRows());

  result->oltOptInfo() = oltOptInfo();
  result->setHint(getHint());

  result->setRowsetIterator(isRowsetIterator());

  result->setTolerateNonFatalError(getTolerateNonFatalError());

  result->setIsExtraHub(isExtraHub());
  result->setMarkedForElimination(markedForElimination());

  result->seenIUD_ = seenIUD_;

  // set the expression's potential
  result->potential_ = potential_;
  
  // copy cascades trace info
  result->parentTaskId_ = parentTaskId_; 
  result->stride_ = stride_; 
  result->birthId_ = birthId_;
  result->memoExprId_ = memoExprId_;
  result->sourceMemoExprId_ = sourceMemoExprId_;
  result->sourceGroupId_ = sourceGroupId_;
  result->costLimit_ = costLimit_;
  result->originalExpr_ = this;

  return result;
}

// this method is not virtual, since combining the copies of the
// top node and its children should be independent of the actual node
RelExpr * RelExpr::copyTree(CollHeap* outHeap)
{
  RelExpr * result = copyTopNode(0,outHeap);
  Int32 arity = getArity();

  for (Lng32 i = 0; i < arity; i++)
    result->child(i) = child(i)->copyTree(outHeap);

  return result;
}

// this method is also not virtual, It does same thing as copyTree
// except that it copies the RETDesc and groupAttr pointers too
// this is method is used to get a copy of the original tree before
// inserting it to Cascades.
RelExpr * RelExpr::copyRelExprTree(CollHeap* outHeap)
{
  RelExpr * result = copyTopNode(0,outHeap);
  result->setGroupAttr(new (outHeap) GroupAttributes(*(getGroupAttr())));
  result->setRETDesc(getRETDesc());
  result->getGroupAttr()->setLogExprForSynthesis(result);

  Int32 arity = getArity();

  for (Lng32 i = 0; i < arity; i++)
    result->child(i) = child(i)->copyRelExprTree(outHeap);

  return result;
}

const RelExpr * RelExpr::getOriginalExpr(NABoolean transitive) const
{
  if (originalExpr_ == NULL)
    return this;

  RelExpr *result = originalExpr_;

  while (result->originalExpr_ && transitive)
    result = result->originalExpr_;

  return result;
}

RelExpr * RelExpr::getOriginalExpr(NABoolean transitive)
{
  if (originalExpr_ == NULL)
    return this;

  RelExpr *result = originalExpr_;

  while (result->originalExpr_ && transitive)
    result = result->originalExpr_;

  return result;
}

void RelExpr::setBlockStmtRecursively(NABoolean x)
{
  setBlockStmt(x);
  Int32 arity = getArity();

  for (Lng32 i = 0; i < arity; i++)
    child(i)->setBlockStmtRecursively(x);
}

// -----------------------------------------------------------------------
// create or share an optimization goal for a child group
// -----------------------------------------------------------------------
Context * RelExpr::shareContext(Lng32 childIndex,
				const ReqdPhysicalProperty* const reqdPhys,
				const InputPhysicalProperty* const inputPhys,
				CostLimit* costLimit,
				Context * parentContext,
				const EstLogPropSharedPtr& inputLogProp,
				RelExpr *explicitlyRequiredShape) const
{
  // no need to do the job if costLimit id already negative
  if ( costLimit AND
       CURRSTMT_OPTDEFAULTS->OPHpruneWhenCLExceeded() AND
       costLimit->getValue(reqdPhys) < 0 )
    return NULL;

  const ReqdPhysicalProperty* searchForRPP;

  // if the required physical properties are empty, don't use them
  if (reqdPhys != NULL AND reqdPhys->isEmpty())
    searchForRPP = NULL;
  else
    searchForRPP = reqdPhys;

  // handle force plan directives: if the parent node must match a
  // certain tree, make sure the child node gets the appropriate
  // requirement to match a child node of the mustMatch pattern
  RelExpr *childMustMatch = explicitlyRequiredShape;

  if (parentContext->getReqdPhysicalProperty() != NULL AND
      parentContext->getReqdPhysicalProperty()->getMustMatch() != NULL AND
      explicitlyRequiredShape == NULL)
    {
      const RelExpr *parentMustMatch =
	parentContext->getReqdPhysicalProperty()->getMustMatch();

      // Reuse the parent's pattern if this node is a map value ids
      // node and the required pattern isn't. This is because a map value
      // ids node, PACK node and UNPACK node is essentially a no-op and
      // does not need to be specified in CONTROL QUERY SHAPE.
      // Sorry for putting this DBI code into
      // places where particular operator types shouldn't be known.
      // It's the summer of 1997 and we have a deadline for FCS.
      // Its (almost) summer of 2003 and I am adding the same thingy
      // for FIRST_N operator.
      if (((getOperatorType() == REL_MAP_VALUEIDS) &&
	   (parentMustMatch->getOperatorType() != REL_MAP_VALUEIDS)) ||
	   ((getOperatorType() == REL_PACK) AND
		(parentMustMatch->getOperatorType() != REL_PACK)) ||
	   ((getOperatorType() == REL_UNPACKROWS) AND
		(parentMustMatch->getOperatorType() != REL_UNPACKROWS)) ||
	  ((getOperatorType() == REL_FIRST_N) AND
	   (parentMustMatch->getOperatorType() != REL_FIRST_N)) ||
	  (CURRSTMT_OPTDEFAULTS->ignoreExchangesInCQS() AND
	   (getOperatorType() == REL_EXCHANGE) AND
	   (parentMustMatch->getOperatorType() != REL_FORCE_EXCHANGE)) ||
	  (CURRSTMT_OPTDEFAULTS->ignoreSortsInCQS() AND
	   (getOperatorType() == REL_SORT) AND
	   (parentMustMatch->getOperatorType() != REL_SORT)))
	{
	  childMustMatch = (RelExpr *) parentMustMatch;
	}
      else
	{
	  // If the "must match" pattern specifies something other than
	  // a cut op for child "childIndex" then this is our new "must match".
	  if (childIndex < parentMustMatch->getArity() AND
	      NOT parentMustMatch->child(childIndex)->isCutOp())
	    childMustMatch = parentMustMatch->child(childIndex);
	}
    }

  if (childMustMatch != NULL OR
      searchForRPP AND searchForRPP->getMustMatch() != NULL)
    {
      // we have to change the "must match" attribute of searchForRPP
      // add the "mustMatch" requirement
      if (searchForRPP != NULL)
	{
	  searchForRPP = new (CmpCommon::statementHeap())
	                     ReqdPhysicalProperty(*searchForRPP,
				                  childMustMatch);
	}
      else
	searchForRPP = new (CmpCommon::statementHeap())
	                   ReqdPhysicalProperty(childMustMatch);
    }

  return (*CURRSTMT_OPTGLOBALS->memo)[child(childIndex).getGroupId()]->shareContext(searchForRPP,
                                                               inputPhys,
							       costLimit,
							       parentContext,
							       inputLogProp);
} // RelExpr::shareContext()

ULng32 RelExpr::getDefault(DefaultConstants id)
{
  return ActiveSchemaDB()->getDefaults().getAsULong(id);
}

void RelExpr::addLocalExpr(LIST(ExprNode *) &xlist,
			   LIST(NAString) &llist) const
{
  if (selection_ != NULL OR
      NOT predicates_.isEmpty())
    {
      if (predicates_.isEmpty())
	xlist.insert(selection_);
      else
	xlist.insert(predicates_.rebuildExprTree());
      llist.insert("selection_predicates");
    }
  if(NOT uniqueColumns_.isEmpty())
  {
    xlist.insert(uniqueColumns_.rebuildExprTree());
    llist.insert("uniqueColumns_");
  }
}

//QSTUFF
// we must pushdown the outputs of a genericupdate root to its
// descendants to ensure that only those required output values are
// tested against indexes when selecting an index for a stream scan
// followed by an embedded update. Since we may allow for unions and
// for inner updates we just follow the isEmbeddedUpdate() thread once
// we reach a generic update root.

void RelExpr::pushDownGenericUpdateRootOutputs( const ValueIdSet &outputs)
{

  ValueIdSet rootOutputs =
    getGroupAttr()->isGenericUpdateRoot() ?
    getGroupAttr()->getCharacteristicOutputs() : outputs;


  for (Int32 i=0; i < getArity(); i++) {
    if (child(i)->castToRelExpr()->getGroupAttr()->isEmbeddedUpdateOrDelete()){
      child(i)->castToRelExpr()->
        pushDownGenericUpdateRootOutputs(rootOutputs);
    }
  }

  if (NOT rootOutputs.isEmpty()){
    getGroupAttr()->setGenericUpdateRootOutputs(rootOutputs);
  }
}

//QSTUFF

void RelExpr::needSortedNRows(NABoolean val)
{ 
  // The operators listed below can create OR propogate a GET_N
  // request. Other operatots will turn a GET_N request into GET_ALL
  // There are a few exceptions like right side of NJ for semi join etc.
  // but these are not relevant for FirstN sort
  // This method should only in the generator since we are using 
  // physical node types.
  OperatorTypeEnum operatorType = getOperatorType();
  if ((operatorType != REL_FIRST_N) &&
      (operatorType != REL_EXCHANGE) &&
      (operatorType != REL_MERGE_UNION) &&
      (operatorType != REL_PROBE_CACHE) &&
      (operatorType != REL_ROOT) && 
      (operatorType != REL_LEFT_NESTED_JOIN) &&
      (operatorType != REL_LEFT_TSJ) &&
      (operatorType != REL_MAP_VALUEIDS))
    return ;
      
  if ((operatorType == REL_LEFT_NESTED_JOIN) || 
      (operatorType == REL_LEFT_TSJ)) {
    // left side of left tsj propagates a GET_N request if afterPred is empty.
    if (getSelectionPred().isEmpty())
      child(0)->castToRelExpr()->needSortedNRows(val);

    return ;
  }
 
  for (Int32 i=0; i < getArity(); i++) {
    if (child(i))
      child(i)->castToRelExpr()->needSortedNRows(val);
  }
}

// -----------------------------------------------------------------------
// computeValuesReqdForPredicates()
//
// There has been some problems with this function (as to how it should
// behave). The issue has been whether we should allow an operator to
// have the boolean value of a predicate as its output. That is to say,
// whether (SCAN T1), for example, could evaluate a predicate such as
// (T1.a > 3) and output a value of true or false to its parent.
//
// In most cases, this wouldn't be an issue since the predicate is used
// to filter out all the non-qualified rows. However, such is not the
// case when a CASE statement is involved. Part of the CASE statement,
// (e.g. the WHEN clause) can be evaluated at the SCAN, while the rest
// of the statement could reference some other tables and therefore must
// be evaluated at an ancestor node of the tree.
//
// A complete example is SELECT CASE WHEN T1.A > 3 THEN T2.A ELSE 0 END
// FROM T1 JOIN T2 ON T1.C = T2.C. In this case, if we allow a boolean
// value to be our output, the (T1.A > 3) could be evaluated at SCAN T1,
// and the CASE statement itself at the JOIN. The alternative would be
// for SCAN T1 to output T1.A and the CASE statement evaluated wholly at
// the JOIN.
//
// Now, how do all these relate to this function? The purpose of this
// function is to turn a predicate into values required to evaluate the
// predicate. Thus, the question is: should we allow the boolean value
// of (T1.A > 3) be the value required to evaluate the predicate (T1.A >
// 3). Or, should the values be T1.A and 3 instead? More generally,
// should we go for the leaf values of a non-VEG predicate (there is a
// separate story for VEG predicates, see later) or just the bool value
// of that predicate?
//
// This function has been implemented to gather the leaf values. However,
// there is no reason why we could
// not just require the boolean value. The logic of predicate pushdown
// mandates that if the child of the operator is unable to produce that
// boolean value, it will figure out what sub-expressions it could produce
// in its outputs in order for the boolean value to be evaluated at its
// parent.
//
// On the other hand, since this function has not been changed for quite
// a while, we are worried the change might trigger problematic spots in
// other places which rely on this function behaving the way it has been.
// Through extensive testing, we didn't seem to identify any problems and
// therefore, we decided to commit this fix.
//
// Now for VEGPred's. A VEGPred is considered "evaluable" at an operator
// if any *one* of its VEG members is "evaluable". For example, VEGPred(
// VEG{T1.a,T2.a}) in the query SELECT T2.B FROM (T1 JOIN T2 ON T1.A =
// T2.A) is "evaluable" at SCAN T1 and will be pushed down. Clearly, only
// evaluating the predicate there is not enough. We have to keep the
// VEGPred at the JOIN as well. The logic in Join::pushdownCoveredExpr()
// correctly handle that now. That is, it keeps the predicate even if
// it has been pushed down. However, doing so means that in the example
// given, SCAN T1 has to return T1.A as an output rather than just the
// boolean value of VEGPred(VEG{T1.A,T2.A}). That boolean value is sort
// of only local to SCAN T1. This function, therefore, declares that the
// value required to evaluate a VEGPred is not the boolean value of the
// VEGPred itself but the VEGRef of its VEG. In our example, the required
// value is VEGRef(VEG{T1.A,T2.A}). The semantics of this is that SCAN T1
// is asked to return as an output one of the VEG members available to
// it. The pre-code generator will thus change this VEGRef into T1.A.
//
//                                                              8/14/1998
//
// -----------------------------------------------------------------------
void RelExpr::computeValuesReqdForPredicates(const ValueIdSet& setOfExpr,
					     ValueIdSet& reqdValues,
                                             NABoolean addInstNull)
{
  for (ValueId exprId = setOfExpr.init();
       setOfExpr.next(exprId);
       setOfExpr.advance(exprId))
  {
    if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE)
    {
      VEG * vegPtr = ((VEGPredicate *)(exprId.getItemExpr()))->getVEG();
      reqdValues += vegPtr->getVEGReference()->getValueId();

      // If the VEG for this VEGPredicate contains a member that is
      // another VEGReference, add it to reqdValues in order to ensure
      // that it gets retrieved.
      //
      for (ValueId x = vegPtr->getAllValues().init();
           vegPtr->getAllValues().next(x);
           vegPtr->getAllValues().advance(x))
      {
        OperatorTypeEnum optype = x.getItemExpr()->getOperatorType();
        if ( optype == ITM_VEG_REFERENCE )
             // **********************************************************
             // Note: this "if" used to have the following cases as well.
             //       We feel that they might not be necessary any more.
             // || optype == ITM_INSTANTIATE_NULL ||
             // optype == ITM_UNPACKCOL )
             // **********************************************************
          reqdValues += x;
        else if ( addInstNull && optype == ITM_INSTANTIATE_NULL ) 
          { // part of fix to soln 10-090618-2434: a full outer join
            //   select ... from t1 inner join t2 on ... 
            //   full outer join t3 on ... where t2.RGN = 'EMEA'
            // whose selection predicate "t2.RGN = <constant>" must have
            // its null-instantiated "t.RGN" column added to reqdValues.
            reqdValues += x;
          }
      } // end inner for
    } // endif is a VEGPredicate
    else
    {
      // Not a VEGPred (either a "normal" pred or a "real" value). In
      // any case, just add the value to the required values set. (For
      // a "normal" pred, it means the boolean value for the predicate
      // is required.
      //
      reqdValues += exprId;
    }
  } // end outer for
} // computeValuesReqdForPredicates()

void RelExpr::computeValuesReqdForOutput(const ValueIdSet& setOfExpr,
                                         const ValueIdSet& newExternalInputs,
                                         ValueIdSet& reqdValues)
{
  // if VEGPreds are in the output, get the underlying VEGRefs
  computeValuesReqdForPredicates(setOfExpr, reqdValues);

  const GroupAttributes emptyGA;
  for (ValueId exprId = setOfExpr.init();
       setOfExpr.next(exprId);
       setOfExpr.advance(exprId))
  {
    if  ((exprId.getType().getTypeQualifier() == NA_CHARACTER_TYPE) &&
         (exprId.getType().getNominalSize() > CONST_32K))
    {
        exprId.getItemExpr()->getLeafValuesForCoverTest(reqdValues, 
                                                        emptyGA, 
                                                        newExternalInputs);
    }
  }
}

// -----------------------------------------------------------------------
// RelExpr::pushdownCoveredExpr()
// -----------------------------------------------------------------------
void RelExpr::pushdownCoveredExpr(const ValueIdSet & outputExpr,
                                  const ValueIdSet & newExternalInputs,
                                  ValueIdSet & predicatesOnParent,
				  const ValueIdSet * setOfValuesReqdByParent,
				  Lng32 childIndex
		                 )
{
  ValueIdSet exprToEvalOnParent, outputSet, extraHubNonEssOutputs;
  Int32 firstChild, lastChild; // loop bounds
  Int32 iter;                  // loop index variable
  NABoolean optimizeOutputs;


  if (getArity() == 0 ) return; // we don't do anything for leaf nodes..

  if ((getOperator().match(REL_ANY_TSJ) ) ||
      (getOperator().match(REL_ANY_GEN_UPDATE) ) )
    optimizeOutputs = FALSE;
  else
    optimizeOutputs = TRUE;


  if (getOperator().match(REL_ANY_JOIN) &&
      isExtraHub())
    extraHubNonEssOutputs = ((Join *)this)->getExtraHubNonEssentialOutputs();
 
    
  // -----------------------------------------------------------------
  // Should the pushdown be attempted on a specific child?
  // -----------------------------------------------------------------
  if ( (childIndex >= 0) AND (childIndex < getArity()) )
    {                             // yes, a child index is given
      firstChild = (Int32)childIndex;
      lastChild = firstChild + 1;
    }
  else                            // no, perform pushdown on all
    {
      firstChild = 0;
      lastChild  = getArity();
    }

  // ---------------------------------------------------------------------
  // Examine the set of values required by the parent. Replace each
  // VEGPredicate with a VEGReferences for its VEG; if its VEG
  // contains other VEGReferences, add them to exprToEvalOnParent.
  // ---------------------------------------------------------------------
  if (setOfValuesReqdByParent)
    computeValuesReqdForPredicates(*setOfValuesReqdByParent,
                                 exprToEvalOnParent);

  computeValuesReqdForOutput(outputExpr,newExternalInputs,outputSet);


  // ---------------------------------------------------------------------
  // Are there any predicates that can be pushed down?
  // ---------------------------------------------------------------------
  if ( (getArity() > 0) AND (NOT predicatesOnParent.isEmpty()) )
    {
      // -----------------------------------------------------------------
      // 1) Figure out which predicates could be push to which child.
      //    Try to give all predicates to all children.
      // 2) Modify predOnParent to be those predicates that no could
      //    could take.
      // 3) Add to the selectionPred() of each child those predicates
      //    it could take (if it is not a cut operator)
      // 4) Add to exprToEvalOnParent the predicates that could not
      //    be push down to any child (predOnParent)
      // 5) Recompute the input and outputs for each child given this
      //    set of exprOnParent.
      // -----------------------------------------------------------------
      // Allocate an array to contain the ValueIds of external inputs
      // that are referenced in the given expressions.
      // -----------------------------------------------------------------
      ValueIdSet referencedInputs[MAX_REL_ARITY];
      // -----------------------------------------------------------------
      // Allocate a ValueIdSet to contain the ValueIds of the roots of
      // sub-expressions that are covered by
      // a) the Group Attributes of a child and
      // b) the new external inputs.
      // Note that the containing expression is not covered for each
      // such sub-expression.
      // -----------------------------------------------------------------
      ValueIdSet coveredSubExprNotUsed;
      // -----------------------------------------------------------------
      // Allocate an array to contain the ValueIds of predicates that
      // can be pushed down to a specific child.
      // -----------------------------------------------------------------
      ValueIdSet predPushSet[MAX_REL_ARITY];
      // -----------------------------------------------------------------
      // Check which predicate factors are fully covered by a certain
      // child. Gather their ValueIds in predPushSet.
      // -----------------------------------------------------------------
      const ValueIdSet emptySet;
      // -----------------------------------------------------------------
      // Join predicates can be pushed below a GU root as the comment a
      // few lines below does applies only to selection predicates
      // and not join predicates. The comment below indicates that in
      // some cases we do not wish to push a user provided predicate on
      // select below the GU root. These user provided predicates are
      // stored as selection predicates.
      // For MTS deletes, an anti-semi-join is used to glue the
      // inlined tree. For such joins all predicates that are pulled
      // are stored as join predicates. The change below facilitates
      // a push down of those predicates. The firstChild condition below
      // ensures that we are considering join predicates here (see
      // Join::pushDownCoveredExpr)
      // -----------------------------------------------------------------
      NABoolean pushPredicateBelowGURoot = FALSE;

      if ((getGroupAttr()->isGenericUpdateRoot() AND
	  getOperator() == REL_ANTI_SEMITSJ AND
	  firstChild == 1 ) OR
	  (NOT (getGroupAttr()->isGenericUpdateRoot())))
      {
	pushPredicateBelowGURoot = TRUE;
      }

      for (iter = firstChild; iter < lastChild; iter++)
	{
        if (NOT child(iter).getPtr()->isCutOp()){

          // QSTUFF
          // we don't push predicates beyond the root of a generic
          // update tree. This is done by pretending that those
          // predicates are not covered by any child. This is
          // required to allows us to distinguish between the
          // following two types of expressions:
          // select * from (delete from x) y where y.x > 3;
          // select * from (delete from x where x.x > 3) y;

          if (pushPredicateBelowGURoot ) {
            // QSTUFF
            child(iter).getGroupAttr()->coverTest(predicatesOnParent,
              newExternalInputs,
              predPushSet[iter],
              referencedInputs[iter],
              &coveredSubExprNotUsed);
            // QSTUFF
          }
          // QSTUFF
        }
          else
            // ----------------------------------------------------------
            // If this is a cutop these predicates were already pushed
            // down to the child during predicate pushdown. Compute
            // which predicates were pushable so that we can remove them
            // from predOnParent and avoid creating a new group that will
            // later be merged
            // ----------------------------------------------------------

            // QSTUFF
            // for more explanation please see comment above

          if ( pushPredicateBelowGURoot ) {
              // QSTUFF
              child(iter).getGroupAttr()->coverTest(predicatesOnParent,
                emptySet,
                predPushSet[iter],
                referencedInputs[iter],
                &coveredSubExprNotUsed);
              // QSTUFF
            }
            // QSTUFF
      } // for loop to perform coverTest()
      // -----------------------------------------------------------------
      // From the original set of predicates, delete all those predicates
      // that will be pushed down. The remaining predicates will be
      // evaluated on the parent (this node).
      // -----------------------------------------------------------------
      for (iter = firstChild; iter < lastChild; iter++)
		predicatesOnParent -= predPushSet[iter];

      // -----------------------------------------------------------------
      // Add the predicates that could not be pushed to any child to the
      // set of expressions to evaluate on the parent.
      // -----------------------------------------------------------------
      computeValuesReqdForPredicates(predicatesOnParent,
                                     exprToEvalOnParent);

      // -----------------------------------------------------------------
      // Perform predicate pushdown
      // -----------------------------------------------------------------
      for (iter = firstChild; iter < lastChild; iter++)
	{
          if (NOT child(iter).getPtr()->isCutOp())
            {
	      // ---------------------------------------------------------
	      // Reassign predicate factors to the appropriate children
	      // ---------------------------------------------------------
	      child(iter).getPtr()->selectionPred().insert(predPushSet[iter]);
	      // ---------------------------------------------------------
	      // Add the input values that are referenced by the predicates
	      // that were pushed down in the above step, to the Group
	      // Attributes of the child.
              // We need to call coverTest again to figure out which inputs
              // are needed for the predicates that will be pushdown.
	      // ---------------------------------------------------------
              ValueIdSet inputsNeededByPredicates;
  	      child(iter).getGroupAttr()->coverTest(predPushSet[iter],
  						    referencedInputs[iter],
  						    predPushSet[iter],
  						    inputsNeededByPredicates,
  						    &coveredSubExprNotUsed);
	      child(iter).getPtr()->getGroupAttr()->addCharacteristicInputs
	                                            (inputsNeededByPredicates);
	      ValueIdSet essChildOutputs;
	      child(iter).getPtr()->getEssentialOutputsFromChildren
	       (essChildOutputs);
              // ----------------------------------------------------------
              // Have the child compute what output it can provide for
              // the expressions that remain on the parent
              // ----------------------------------------------------------

	     // TBD: Fix the hack described in 
	     // GroupAttributes::resolveCharacteristicOutputs()
	     if(iter==1 AND getOperator().match(REL_ANY_LEFT_JOIN))
	          child(iter).getPtr()->getGroupAttr()->computeCharacteristicIO
	                                              (newExternalInputs,
						       exprToEvalOnParent,
						       outputSet,
						       essChildOutputs,
						       &(getSelectionPred()),
						       TRUE,
						       optimizeOutputs,
                                                       &extraHubNonEssOutputs
						       );
	     else
		 child(iter).getPtr()->getGroupAttr()->computeCharacteristicIO
	                                              (newExternalInputs,
						       exprToEvalOnParent,
						       outputSet,
						       essChildOutputs,
						       NULL,
						       FALSE,
						       optimizeOutputs,
                                                       &extraHubNonEssOutputs
                                                       );

            };
	} // for loop to pushdown predicates
    } // endif (NOT predicatesOnParent.isEmpty())
  else
    {
      // ---------------------------------------------------------------------
      // Compute the characteristic inputs and outputs of each child
      // ---------------------------------------------------------------------
      for (iter = firstChild; iter < lastChild; iter++)
	{
	  // -----------------------------------------------------------------
	  // Ignore CutOps because they exist simply to facilitate
	  // pattern matching. Their Group Attributes are actually those
	  // of the CascadesGroup. So, don't mess with them!
	  // -----------------------------------------------------------------
	  if (NOT child(iter).getPtr()->isCutOp())
	  {
	    ValueIdSet essChildOutputs;
	    child(iter).getPtr()->getEssentialOutputsFromChildren
	       (essChildOutputs);

	    // TBD: Fix the hack described in 
	    // GroupAttributes::resolveCharacteristicOutputs()
	    child(iter).getPtr()->getGroupAttr()->computeCharacteristicIO
	                                            (newExternalInputs,
                                                     exprToEvalOnParent,
						     outputSet,
						     essChildOutputs,
						     NULL,
						     FALSE,
						     optimizeOutputs,
                                                     &extraHubNonEssOutputs
						     );
	  }
	} // for loop to compute characteristic inputs and outputs
    } // endelse predicatesOnParent is empty

} // RelExpr::pushdownCoveredExpr()

// -----------------------------------------------------------------------
// A virtual method for computing output values that an operator can
// produce potentially.
// -----------------------------------------------------------------------
void RelExpr::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  Int32 nc = getArity();
  // For operators that are not leaves, clear the potential outputs
  // and rebuild them.
  if (nc > 0)
    for (Lng32 i = 0; i < nc; i++)
      outputValues += child(i).getGroupAttr()->getCharacteristicOutputs();
  else
    outputValues += getGroupAttr()->getCharacteristicOutputs();

} // RelExpr::getPotentialOutputValues()

void RelExpr::getPotentialOutputValuesAsVEGs(ValueIdSet& outputs) const
{
  getPotentialOutputValues(outputs);
}


// -----------------------------------------------------------------------
// primeGroupAttributes()
// Initialize the Characteristic Inputs And Outputs of this operator.
// -----------------------------------------------------------------------
void RelExpr::primeGroupAttributes()
{
  // Ignore CutOps because they exist simply to facilitate
  // pattern matching. Their Group Attributes are actually those
  // of the CascadesGroup. So, don't mess with them.
  if (isCutOp())
    return;

  // The method sets the characteristic outputs of a node to its
  // potential outputs and sets the required input to the values
  // it needs. It does this by calling two virtual functions
  // on RelExpr.

  ValueIdSet outputValues;
  getPotentialOutputValues(outputValues);
  getGroupAttr()->setCharacteristicOutputs(outputValues);

  recomputeOuterReferences();
} // RelExpr::primeGroupAttributes()

// -----------------------------------------------------------------------
// allocateAndPrimeGroupAttributes()
// This method is for allocating new Group Attributes for the children
// of this operator that were introduced in the dataflow by a rule-
// based transformation. Each new child, or set of children, intervene
// between this operator and another operator that was originally a
// direct child of the latter. The Group Attributes of each newly
// introduced child are recursively primed with the Characteristic
// Inputs and Outputs of the operators of which it is the parent.
// -----------------------------------------------------------------------
void RelExpr::allocateAndPrimeGroupAttributes()
{
  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
    {
      CMPASSERT(child(i).getMode() == ExprGroupId::STANDALONE);
      // Terminate the recursive descent upon reaching a CutOp.
      // Ignore CutOps because they exist simply to facilitate
      // pattern matching. Their Group Attributes are actually
      // those for the CascadesGroup that they belong to and
      // must not change.
      if (NOT child(i)->isCutOp())
	{
	  if (child(i).getGroupAttr() == NULL)
	    {
	      // A CutOp must have Group Attributes.
	      child(i)->setGroupAttr(new (CmpCommon::statementHeap())
				         GroupAttributes());
	    }
	  // Assign my Characteristic Inputs to my child.
	  // This is done in order to ensure that they are propagated
	  // recursively to all my children who are not CutOps.
	  child(i).getPtr()->getGroupAttr()
             ->addCharacteristicInputs
                 (getGroupAttr()->getCharacteristicInputs());
	  // Recompute the potential inputs/outputs for each real child
	  // recursively.
          // Terminate the recursive descent upon encountering an
	  // operator whose arity == 0
	  child(i).getPtr()->allocateAndPrimeGroupAttributes();
	  // Prime the Group Attributes of the child.
	  // The following call primes the child's Characteristic Outputs.
	  // It ensures that the inputs are minimal and outputs are maximal.
	  child(i).getPtr()->primeGroupAttributes();
      // Now compute the GroupAnalysis fields
      child(i).getPtr()->primeGroupAnalysis();
	} // endif child is not a CutOp

    } // for loop

} // RelExpr::allocateAndPrimeGroupAttributes()

void RelExpr::getEssentialOutputsFromChildren(ValueIdSet & essOutputs)
{
  Int32 nc = getArity();
  for (Lng32 i = 0; i < nc; i++)
  {
    essOutputs += child(i).getGroupAttr()->
      getEssentialCharacteristicOutputs();
  }
}

void RelExpr::fixEssentialCharacteristicOutputs()
{
  ValueIdSet essChildOutputs,nonEssOutputs;
  getEssentialOutputsFromChildren(essChildOutputs);
  getGroupAttr()->getNonEssentialCharacteristicOutputs(nonEssOutputs);
  nonEssOutputs.intersectSet(essChildOutputs);
  getGroupAttr()->addEssentialCharacteristicOutputs(nonEssOutputs);
}

// do some analysis on the initial plan
// this is called at the end of the analysis phase
void RelExpr::analyzeInitialPlan()
{
  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
  {
    child(i)->analyzeInitialPlan();
  }

}

double RelExpr::calculateNoOfLogPlans(Lng32& numOfMergedExprs)
{
  double result = 1;
  Int32 nc = getArity();
  CascadesGroup* group;

  for (Lng32 i = 0; i < nc; i++)
  {
    if (getGroupId() == child(i).getGroupId())
    {
      // This is  a recursive reference of an expression to itself
      // due to a group merge. We cannot call this method on the
      // child, as we would end up calling this method on ourselves
      // again! So, we skip the recursive call on our child and
      // instead return an indication to our caller
      // (CascadesGroup::calculateNoOfLogPlans) that we encountered
      // a merged expression. Our caller will then know what to do
      // to calculate the correct number of logical expressions.
      numOfMergedExprs++;
    }
    else
    {
      group = (*CURRSTMT_OPTGLOBALS->memo)[child(i).getGroupId()];
      result *= group->calculateNoOfLogPlans();
    }
  } // for each child

  return result;
}

// This function is called before any optimization starts
// i.e. applied to the normalizer output (reorderJoinTree OK)
double RelExpr::calculateSubTreeComplexity
                 (NABoolean& enableJoinToTSJRuleOnPass1)
{

  double result = 0;
  Int32 freeLeaves = 1; // # of subtree legs that can permutate
  RelExpr* expr = this;

  while (expr)
  {
    if (expr->getGroupAttr()->isEmbeddedUpdateOrDelete() OR
	expr->getGroupAttr()->isStream())
    {
      enableJoinToTSJRuleOnPass1 = TRUE;
    }
    Int32 nc = expr->getArity();

    // The multi-join case
    if (expr->getOperatorType() == REL_MULTI_JOIN)
    {
      for (Int32 i = 0; i < nc; i++)
      {
        CascadesGroup* groupi = (*CURRSTMT_OPTGLOBALS->memo)[expr->child(i).getGroupId()];

	RelExpr * expri = groupi->getFirstLogExpr();

        result += expri->
          calculateSubTreeComplexity(enableJoinToTSJRuleOnPass1);

      }
      freeLeaves = nc;
      // end the while loop
      expr = NULL;
    }
    // Not multi-join, and not leaf
    else if (nc > 0)
    {
      if (nc == 1)
      {
        // no permutation can take place across groupbys
        if (expr->getOperator().match(REL_ANY_GROUP))
        {
          if (freeLeaves > 1)
          {
            // compute the last permuatation set contribution
            // to the complexity and start a new one
            result += freeLeaves * pow(2,freeLeaves-1);
            freeLeaves = 1;  // start again
          }
        }
      }
      if (nc == 2)
      {
        double child1Complexity;
        CascadesGroup* group1 = (*CURRSTMT_OPTGLOBALS->memo)[expr->child(1).getGroupId()];
        if (group1->getGroupAttr()->getNumBaseTables() > 1)
        {
          // Only one log expr exist in the group at this point
          RelExpr * expr1 = group1->getFirstLogExpr();
          child1Complexity =
            expr1->calculateSubTreeComplexity(enableJoinToTSJRuleOnPass1);

		  // adding this comp_bool guard in case this fix causes regressions
		  // and we need to disable this fix. Should be taken out in a subsequent
		  // release. (say 2.2)
		 if (CmpCommon::getDefault(COMP_BOOL_123) == DF_OFF)
		 {
			  // The factor 2 accounts for the fact that the join could be a
			  // join or a TSJ i.e. two possible logical choices.
			  if (expr->getOperator().match(REL_ANY_NON_TSJ_JOIN))
				child1Complexity = 2*child1Complexity ;
		 }

	  // add the right child subtree contribution to complexity
          result += child1Complexity;
        }
        // only REL_ANY_NON_TSJ_JOINs can permutate
        if (expr->getOperator().match(REL_ANY_NON_TSJ_JOIN))
          freeLeaves++;  // still in same permutation set
        else
        {
          // compute the last permuatation set contribution
          // to the complexity and start a new one
          result += freeLeaves * pow(2,freeLeaves-1);
          freeLeaves = 1;  // start again
        }
      }
      // we do not handle VPJoin yet (nc==3)
      CascadesGroup* group0 = (*CURRSTMT_OPTGLOBALS->memo)[expr->child(0).getGroupId()];
      // Only one log expr exist in the group at this point
      expr = group0->getFirstLogExpr();
    }
    // leaf operators
    else
      expr = NULL;
  }
  // add last permutation set contribution
  result += freeLeaves * pow(2,freeLeaves-1);
  return result;

}

// calculate a query's MJ complexity,
// shoud be called after MJ rewrite
double RelExpr::calculateQueryMJComplexity(double &n,double &n2,double &n3,double &n4)
{
  double result = 0;
  Int32 nc = getArity();
  Int32 freeLeaves = nc; // # of subtree legs that can permutate
 
  RelExpr * expr = this;

  if (getOperatorType() == REL_MULTI_JOIN)
  {
    for (Int32 i = 0; i < nc; i++)
    {
      RelExpr * expri = expr->child(i);

      NABoolean childIsFullOuterJoinOrTSJ =
        child(i)->getGroupAnalysis()->getNodeAnalysis()->
          getJBBC()->isFullOuterJoinOrTSJJBBC();
          

      if (childIsFullOuterJoinOrTSJ)
      {
        NABoolean childIsOuterMost =
          !(child(i)->getGroupAnalysis()->getNodeAnalysis()->
              getJBBC()->getOriginalParentJoin());
        
        if(childIsOuterMost)
          freeLeaves--;
      }

      result += expri->
        calculateQueryMJComplexity(n, n2, n3, n4);
    }
    //only do this for multijoins since only the children
    //of the multijoin will be permuted.
    //Note: This assumes the query tree to be the multijoinized
    //tree produced after multijoin rewrite in the Analyzer
    n += freeLeaves;
    n2 += pow(freeLeaves,2);
    n3 += pow(freeLeaves,3);
    n4 += pow(freeLeaves,4);
    result += freeLeaves * pow(2,freeLeaves-1);
  }
  else if(nc > 0)
  {
    if (nc == 1)
    {
      RelExpr * expr0 = expr->child(0);

      result += expr0->
        calculateQueryMJComplexity(n, n2, n3, n4);      
    }
    else if (nc == 2)
    {
      // only for joins, not for union
      // these will only be TSJ or Full Outer Joins
      // other joins become part of JBB
      if (expr->getOperator().match(REL_ANY_JOIN))
      {
        RelExpr * expr0 = expr->child(0);
        result += expr0->calculateQueryMJComplexity(n, n2, n3, n4);

        RelExpr * expr1 = expr->child(1);
        result += expr1->calculateQueryMJComplexity(n, n2, n3, n4);
      }
    }
  }

  return result;
}


// -----------------------------------------------------------------------
//  the following method is used to created a list of all scan operators
//  in order by size.
// -----------------------------------------------------------------------
void
RelExpr::makeListBySize(LIST(CostScalar) & orderedList, // order list of size
                        NABoolean  recompute)           // recompute memory
                                                        // limit -not used
{
  Int32 nc = getArity();
  RelExpr * expr       = this;
  CostScalar size = 0;

  if (recompute)
  {
    // this needs to be filled in if this ever is redriven by costing
    CMPASSERT(NOT recompute);
  }
  else
  {
    if (expr->getOperatorType() == REL_SCAN  OR
        expr->getOperatorType() == REL_GROUPBY)
    {
      //++MV, use the global empty input logical properties instead of
      //initializing a new one
      size =
       expr->getGroupAttr()->outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->getResultCardinality()
                    * expr->getGroupAttr()->getRecordLength() / 1024;
    }
  }

  if (size > 1)  // don't include anything 1KB or less
  {
    CollIndex idx = 0;

    for (idx = 0; idx < orderedList.entries(); idx++)
    {
      // list should be ordered by increasing estimated rowcount.
      if (orderedList[idx] >= size)
      {
        orderedList.insertAt (idx, size);
        break;
      }
    }

    // insert at end of list
    if (idx >= orderedList.entries())
    {
      orderedList.insertAt (orderedList.entries(), size);
    }

  }

  for (Lng32 i = 0; i < nc; i++)
  {
    CascadesGroup* group1 = (*CURRSTMT_OPTGLOBALS->memo)[expr->child(i).getGroupId()];
    // Only one log expr exist in the group at this point
    // if onlyMemoryOps is ever set true, we will have to traverse
    // the tree differently
    RelExpr * expr1 = group1->getFirstLogExpr();
    expr1->makeListBySize(orderedList, recompute);
  }
}

// Default implementation every RelExpr returns normal priority
PlanPriority RelExpr::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  PlanPriority result; // This will create normal plan priority
  return result;
}

// -----------------------------------------------------------------------
// Method for debugging
// -----------------------------------------------------------------------
void RelExpr::print(FILE * f,
		    const char * prefix,
		    const char * suffix) const
{
#ifndef NDEBUG
  ExprNode::print(f,prefix,suffix);

  fprintf(f,"%sRelational Expression:\n",prefix);

  if (selection_ != NULL)
    selection_->print(f,prefix,suffix);
  else
    predicates_.print(f,prefix,suffix);

  // print children or input equivalence classes
  Int32 nc = getArity();
  for (Lng32 i = 0; i < nc; i++)
    {
      fprintf(f,"%sExpression input %d:\n",prefix,i);
      if (child(i).getMode() == ExprGroupId::MEMOIZED)
	{
	  fprintf(f,
		  "%s    input eq. class #%d\n",
		  prefix,
		  child(i).getGroupId());
	}
      else
	{
	  if (child(i).getPtr() != NULL)
	    child(i)->print(f,CONCAT(prefix,"    "));
	  else
	    fprintf(f,"%snonexistent child\n",prefix);
	}
    }
#endif
}

Int32 RelExpr::nodeCount() const
{
  Int32 result = 1; // start from me.
  Int32 nc = getArity();
  for (Lng32 i = 0; i < nc; i++)
	  if (child(i).getPtr() != NULL)
		result += child(i)->nodeCount();

  return result;
}

NABoolean RelExpr::containsNode(OperatorTypeEnum nodeType)
{
  if (getOperatorType() == nodeType)
    return TRUE;

  Int32 nc = getArity();
  for (Int32 i = 0; i < nc; i++)
    {
    
      if (child(i).getPtr() != NULL &&
          child(i)->containsNode(nodeType))
        return TRUE;
    }
    
  return FALSE;
}

double RelExpr::computeMemoryQuota(NABoolean inMaster,
                                   NABoolean perNode,
                                   double BMOsMemoryLimit, // in MB 
                                   UInt16 totalNumBMOs, // per query 
                                   double totalBMOsMemoryUsage, // for all BMOs per node in bytes 
                                   UInt16 numBMOsPerFragment, // per fragment
                                   double bmoMemoryUsage, // for the current BMO/Operator per node in bytes
                                   Lng32 numStreams,
                                   double &bmoQuotaRatio
                                   ) 
{
   if ( perNode == TRUE ) {
      Lng32 exeMem = Lng32(BMOsMemoryLimit/(1024*1024));

     // the quota is allocated in 2 parts
     // The constant part divided equally across all bmo operators
     // The variable part allocated in proportion of the given BMO operator
     // estimated memory usage to the total estimated memory usage of all BMOs
   
     // The ratio can be capped by the CQD
     double equalQuotaShareRatio = 0;
     equalQuotaShareRatio = ActiveSchemaDB()->getDefaults().getAsDouble(BMO_MEMORY_EQUAL_QUOTA_SHARE_RATIO);
     double constMemQuota = 0;
     double variableMemLimit = exeMem;
     if (equalQuotaShareRatio > 0 && totalNumBMOs > 1) {
        constMemQuota = (exeMem * equalQuotaShareRatio )/ totalNumBMOs;
        variableMemLimit = (1-equalQuotaShareRatio) * exeMem;
     }
     double bmoMemoryRatio = bmoMemoryUsage / totalBMOsMemoryUsage;
     bmoQuotaRatio = bmoMemoryRatio;
     double bmoMemoryQuotaPerNode = constMemQuota + (variableMemLimit * bmoMemoryRatio);
     double numInstancesPerNode = numStreams / MINOF(MAXOF(((NAClusterInfoLinux*)gpClusterInfo)->getTotalNumberOfCPUs(), 1), numStreams);
     double bmoMemoryQuotaPerInstance =  bmoMemoryQuotaPerNode / numInstancesPerNode;
     return bmoMemoryQuotaPerInstance;
  } else {
     // the old way to compute quota 
     Lng32 exeMem = getExeMemoryAvailable(inMaster);
     bmoQuotaRatio = BMOQuotaRatio::NO_RATIO;
     return exeMem / numBMOsPerFragment; 
  }
}

Lng32 RelExpr::getExeMemoryAvailable(NABoolean inMaster) const
{
   Lng32 exeMemAvailMB = 
      ActiveSchemaDB()->getDefaults().getAsLong(EXE_MEMORY_AVAILABLE_IN_MB);
   return exeMemAvailMB;
}

// -----------------------------------------------------------------------
// methods for class RelExprList
// -----------------------------------------------------------------------
void RelExprList::insertOrderByRowcount (RelExpr * expr)
{
  Int32 i = 0;
  NABoolean done = FALSE;


  // QSTUFF
  // insert stream expression as the left most expression
  // by articially forcing it to have lowest cost
  // assumes that only one stream and one embedded update clause
  // is in the statement

  if (expr->getGroupAttr()->isStream() ||
    expr->getGroupAttr()->isEmbeddedUpdateOrDelete())
  {
    insertAt(0,expr);
    done = TRUE;
  }
  // QSTUFF

  while (!done && i < (Int32)entries())
  {
    CostScalar thisCard = (*this)[i]->
                            getGroupAttr()->
                              getResultCardinalityForEmptyInput();
    CostScalar exprCard = expr->
                            getGroupAttr()->
                              getResultCardinalityForEmptyInput();

    NABoolean increasing =
       ((ActiveSchemaDB()->getDefaults()).getAsULong(COMP_INT_90)==1);

    // list should be ordered by increasing estimated rowcount.
    if (((thisCard >= exprCard ) && increasing) ||
        ((thisCard < exprCard ) && !increasing))
    {
      // QSTUFF
      // stream and nested updates or deletes expressions should always be
      // left most, i.e of lowest cost

      if (
        (*this)[i]->getGroupAttr()->isStream() ||
        (*this)[i]->getGroupAttr()->isEmbeddedUpdateOrDelete())
        i++;
      // QSTUFF

      insertAt (i, expr);
      done = TRUE;
    }
    else
      i++;
  }

  // insert at end of list
  if (!done) insertAt (entries(), expr);
}

NABoolean RelExprList::operator== (const RelExprList &other) const
{
  if (entries() != other.entries())
    return FALSE;

  for (Lng32 i = 0; i < (Lng32)entries(); i++)
  {
    if ((*this)[i] != other[i])
      return FALSE;
  }
  return TRUE;
}

NABoolean RelExprList::operator!= (const RelExprList &other) const
{
  if ((*this) == other)
    return FALSE;
  else
    return TRUE;
}

// -----------------------------------------------------------------------
// methods for class CutOp
// -----------------------------------------------------------------------

CutOp::~CutOp() {}

void CutOp::print(FILE * f,
		  const char * prefix,
		  const char *) const
{
#ifndef NDEBUG
  if (getGroupId() == INVALID_GROUP_ID)
    fprintf(f, "%sLeaf (%d)\n", prefix, index_);
  else
    fprintf(f, "%sLeaf (%d, bound to group #%d)\n",
	    prefix, index_, getGroupId());

  return;
#endif
}

Int32 CutOp::getArity () const { return 0; }

NABoolean CutOp::isCutOp() const { return TRUE; }

const NAString CutOp::getText() const
{
  char theText[TEXT_DISPLAY_LENGTH];

  if (getGroupId() == INVALID_GROUP_ID)
    sprintf(theText, "Cut (%d)", index_);
  else
    if (index_ < 99)
      sprintf(theText, "Cut (%d, #%d)", index_, getGroupId());
    else
      // don't display funny indexes (>= 99)
      sprintf(theText, "Cut (#%d)", getGroupId());

  return NAString(theText);
}

RelExpr * CutOp::copyTopNode(RelExpr * derivedNode, CollHeap* outHeap)
{
  if (getGroupId() == INVALID_GROUP_ID)
    {
      // this is a standalone cut operator (e.g. in the tree of a
      // CONTROL QUERY SHAPE directive), return a copy of it
      CMPASSERT(derivedNode == NULL);
      CutOp* result = new (outHeap)CutOp(index_, outHeap);
      return RelExpr::copyTopNode(result,outHeap);
    }
  else
    {
      // CutOps are shared among the pattern and the substitute of
      // a rule. Often the substitute is produced by calling the copyTree()
      // method on the "before" expression or a part of it. This implementation
      // of copyTopNode() makes it possible to do that.
      return this;
    }
}

void CutOp::setGroupIdAndAttr(CascadesGroupId groupId)
{
  setGroupId(groupId);

  // set the group attributes of the leaf node to match the group
  if (groupId == INVALID_GROUP_ID)
    setGroupAttr(NULL);
  else
    setGroupAttr((*CURRSTMT_OPTGLOBALS->memo)[groupId]->getGroupAttr());
}

void CutOp::setExpr(RelExpr *e)
{
  expr_ = e;
  if (expr_ == NULL)
    {
      setGroupIdAndAttr(INVALID_GROUP_ID);
    }
  else
    {
      setGroupAttr(expr_->getGroupAttr());	  // ##shouldn't this line..
      // setGroupIdAndAttr(expr_->getGroupId());  // ##..be replaced by this?
    }
}

// -----------------------------------------------------------------------
// methods for class SubtreeOp
// -----------------------------------------------------------------------

SubtreeOp::~SubtreeOp() {}

Int32 SubtreeOp::getArity() const { return 0; }

NABoolean SubtreeOp::isSubtreeOp() const { return TRUE; }

const NAString SubtreeOp::getText() const { return NAString("Tree Op"); }

RelExpr * SubtreeOp::copyTopNode(RelExpr *, CollHeap*) { return this; }

// -----------------------------------------------------------------------
// methods for class WildCardOp
// -----------------------------------------------------------------------

WildCardOp::~WildCardOp() {}

Int32 WildCardOp::getArity() const
{
  switch (getOperatorType())
    {
    case REL_ANY_LEAF_OP:
    case REL_FORCE_ANY_SCAN:
    case REL_ANY_ROUTINE:
    case REL_FORCE_ANY_SCALAR_UDF:
    case REL_ANY_SCALAR_UDF_ROUTINE:
    case REL_ANY_LEAF_GEN_UPDATE:
    case REL_ANY_LEAF_TABLE_MAPPING_UDF:
      return 0;

    case REL_ANY_UNARY_GEN_UPDATE:
    case REL_ANY_UNARY_OP:
    case REL_ANY_GROUP:
    case REL_FORCE_EXCHANGE:
    case REL_ANY_UNARY_TABLE_MAPPING_UDF:
    case REL_ANY_EXTRACT:
      return 1;

    case REL_ANY_BINARY_OP:
    case REL_ANY_JOIN:
    case REL_ANY_TSJ:
    case REL_ANY_SEMIJOIN:
    case REL_ANY_SEMITSJ:
    case REL_ANY_ANTI_SEMIJOIN:
    case REL_ANY_ANTI_SEMITSJ:
    case REL_ANY_INNER_JOIN:
    case REL_ANY_NON_TS_INNER_JOIN:
    case REL_ANY_NON_TSJ_JOIN:
    case REL_ANY_LEFT_JOIN:
    case REL_ANY_LEFT_TSJ:
    case REL_ANY_NESTED_JOIN:
    case REL_ANY_HASH_JOIN:
    case REL_ANY_MERGE_JOIN:
    case REL_FORCE_JOIN:
    case REL_FORCE_NESTED_JOIN:
    case REL_FORCE_HASH_JOIN:
    case REL_FORCE_ORDERED_HASH_JOIN:
    case REL_FORCE_HYBRID_HASH_JOIN:
    case REL_FORCE_MERGE_JOIN:
    case REL_FORCE_ORDERED_CROSS_PRODUCT:
    case REL_ANY_BINARY_TABLE_MAPPING_UDF:
      return 2;

    default:
      ABORT("WildCardOp with unknown arity encountered");
      return 0;
    }
}

NABoolean WildCardOp::isWildcard() const { return TRUE; }

const NAString WildCardOp::getText() const
{
  switch (getOperatorType())
    {
    case ANY_REL_OR_ITM_OP:
      return "ANY_REL_OR_ITM_OP";
    case REL_ANY_LEAF_OP:
      return "REL_ANY_LEAF_OP";
    case REL_ANY_UNARY_OP:
      return "REL_ANY_UNARY_OP";
    case REL_ANY_ROUTINE:
      return "REL_ANY_ROUTINE";
    case REL_ANY_GEN_UPDATE:
      return "REL_ANY_GEN_UPDATE";
    case REL_ANY_UNARY_GEN_UPDATE:
      return "REL_ANY_UNARY_GEN_UPDATE";
    case REL_ANY_LEAF_GEN_UPDATE:
      return "REL_ANY_LEAF_GEN_UPDATE";
    case REL_ANY_GROUP:
      return "REL_ANY_GROUP";
    case REL_ANY_BINARY_OP:
      return "REL_ANY_BINARY_OP";
    case REL_ANY_JOIN:
      return "REL_ANY_JOIN";
    case REL_ANY_TSJ:
      return "REL_ANY_TSJ";
    case REL_ANY_SEMIJOIN:
      return "REL_ANY_SEMIJOIN";
    case REL_ANY_SEMITSJ:
      return "REL_ANY_SEMITSJ";
    case REL_ANY_INNER_JOIN:
      return "REL_ANY_INNER_JOIN";
    case REL_ANY_LEFT_JOIN:
      return "REL_ANY_LEFT_JOIN";
    case REL_ANY_LEFT_TSJ:
      return "REL_ANY_LEFT_TSJ";
    case REL_ANY_NESTED_JOIN:
      return "REL_ANY_NESTED_JOIN";
    case REL_ANY_HASH_JOIN:
      return "REL_ANY_HASH_JOIN";
    case REL_ANY_MERGE_JOIN:
      return "REL_ANY_MERGE_JOIN";
    case REL_ANY_EXTRACT:
      return "REL_ANY_EXTRACT";
    case REL_FORCE_ANY_SCAN:
      return "REL_FORCE_ANY_SCAN";
    case REL_FORCE_EXCHANGE:
      return "REL_FORCE_EXCHANGE";
    case REL_FORCE_JOIN:
      return "REL_FORCE_JOIN";
    case REL_FORCE_NESTED_JOIN:
      return "REL_FORCE_NESTED_JOIN";
    case REL_FORCE_HASH_JOIN:
      return "REL_FORCE_HASH_JOIN";
    case REL_FORCE_HYBRID_HASH_JOIN:
      return "REL_FORCE_HYBRID_HASH_JOIN";
    case REL_FORCE_ORDERED_HASH_JOIN:
      return "REL_FORCE_ORDERED_HASH_JOIN";
    case REL_FORCE_MERGE_JOIN:
      return "REL_FORCE_MERGE_JOIN";
    default:
      return "unknown??";
    }
}

RelExpr * WildCardOp::copyTopNode(RelExpr * derivedNode,
				  CollHeap* outHeap)
{
  if (corrNode_ != NULL)
    return corrNode_->copyTopNode(0, outHeap);
  else
    {
      if (derivedNode != NULL)
	return derivedNode;
      else
	{
	  WildCardOp* result;

	  result = new (outHeap) WildCardOp(getOperatorType(),
					    0,
					    NULL,
					    NULL,
					    outHeap);
	  return RelExpr::copyTopNode(result,outHeap);
	}
    }

  return NULL; // shouldn't really reach here
}

// -----------------------------------------------------------------------
// member functions for class ScanForceWildCard
// -----------------------------------------------------------------------

ScanForceWildCard::ScanForceWildCard(CollHeap * outHeap) :
  WildCardOp(REL_FORCE_ANY_SCAN),
  exposedName_(outHeap),
  indexName_(outHeap)
{initializeScanOptions();}

ScanForceWildCard::ScanForceWildCard(const NAString& exposedName,
				     CollHeap *outHeap) :
  WildCardOp(REL_FORCE_ANY_SCAN,0,NULL,NULL,outHeap),
  exposedName_(exposedName, outHeap),
  indexName_(outHeap)
{initializeScanOptions();}

ScanForceWildCard::ScanForceWildCard(const NAString& exposedName,
				     const NAString& indexName,
				     CollHeap *outHeap) :
  WildCardOp(REL_FORCE_ANY_SCAN,0,NULL,NULL,outHeap),
  exposedName_(exposedName, outHeap),
  indexName_(indexName, outHeap)
{initializeScanOptions();}

ScanForceWildCard::~ScanForceWildCard()
{
  collHeap()->deallocateMemory((void*)enumAlgorithms_);
  // delete enumAlgorithms_ from the same heap were the
  // ScanForceWildCard object belong
}

//----------------------------------------------------------
// initialize class members
//----------------------------------------------------------
void ScanForceWildCard::initializeScanOptions()
{
  mdamStatus_ = UNDEFINED;
  direction_ = UNDEFINED;
  indexStatus_ = UNDEFINED;
  numMdamColumns_ = 0;
  mdamColumnsStatus_ = UNDEFINED;
  enumAlgorithms_ = NULL;
  numberOfBlocksToReadPerAccess_ = -1;
}

//----------------------------------------------------------
// get the enumeration algorithm (density) for column
// if beyound specified columns return COLUMN_SYSTEM
//----------------------------------------------------------
ScanForceWildCard::scanOptionEnum
  ScanForceWildCard::getEnumAlgorithmForColumn(CollIndex column) const
{
  if (column >= numMdamColumns_)
    return ScanForceWildCard::COLUMN_SYSTEM;
  else
    return enumAlgorithms_[column];
}

//----------------------------------------------------------
// set the following scan option. return FALSE only if
// such option does not exist.
//----------------------------------------------------------
NABoolean ScanForceWildCard::setScanOptions(ScanForceWildCard::scanOptionEnum option)
{
  if (option == INDEX_SYSTEM)
    {
      indexStatus_ = INDEX_SYSTEM;
      return TRUE;
    }
  else if (option == MDAM_SYSTEM)
    {
      mdamStatus_ = MDAM_SYSTEM;
      return TRUE;
    }
  else if (option == MDAM_OFF)
    {
      mdamStatus_ = MDAM_OFF;
      return TRUE;
    }
  else if (option == MDAM_FORCED)
    {
      mdamStatus_ = MDAM_FORCED;
      return TRUE;
    }
  else if (option == DIRECTION_FORWARD)
    {
      direction_ = DIRECTION_FORWARD;
      return TRUE;
    }
  else if (option == DIRECTION_REVERSED)
    {
      direction_ = DIRECTION_REVERSED;
      return TRUE;
    }
  else if (option == DIRECTION_SYSTEM)
    {
      direction_ = DIRECTION_SYSTEM;
      return TRUE;
    }
  else return FALSE;
}


NABoolean ScanForceWildCard::setIndexName(const NAString& value)
{
  if (value != "")
    {
      indexName_ = value;
      return TRUE;
    }
  else
    return FALSE;		// Error should be nonempty string
}

//----------------------------------------------------------
// set the columns options based on passed values
//----------------------------------------------------------
NABoolean ScanForceWildCard::
  setColumnOptions(CollIndex numColumns,
		   ScanForceWildCard::scanOptionEnum* columnAlgorithms,
		   ScanForceWildCard::scanOptionEnum mdamColumnsStatus)
{
  mdamStatus_ = MDAM_FORCED;
  mdamColumnsStatus_ = mdamColumnsStatus;
  numMdamColumns_ = numColumns;
  // delete enumAlgorithms_ from the same heap were the
  // ScanForceWildCard object belong
  collHeap()->deallocateMemory((void*)enumAlgorithms_);
  // allocate enumAlgorithms_[numMdamColumns] in the same heap
  // were the ScanForceWildCard object belong
  enumAlgorithms_ = (ScanForceWildCard::scanOptionEnum*)
    collHeap()->allocateMemory(sizeof(ScanForceWildCard::scanOptionEnum)*numMdamColumns_);
  for (CollIndex i=0; i<numMdamColumns_; i++)
    enumAlgorithms_[i] = columnAlgorithms[i];
  return TRUE;
}

//----------------------------------------------------------
// set the columns options based on passed values
// here options for particular columns are not passed
// and hence COLUMN_SYSTEM is assigned
//----------------------------------------------------------
NABoolean ScanForceWildCard::
  setColumnOptions(CollIndex numColumns,
		   ScanForceWildCard::scanOptionEnum mdamColumnsStatus)
{
  mdamStatus_ = MDAM_FORCED;
  mdamColumnsStatus_ = mdamColumnsStatus;
  numMdamColumns_ = numColumns;
  // delete enumAlgorithms_ from the same heap were the
  // ScanForceWildCard object belong
  collHeap()->deallocateMemory((void*)enumAlgorithms_);
  // allocate enumAlgorithms_[numMdamColumns] in the same heap
  // were the ScanForceWildCard object belong
  enumAlgorithms_ = (ScanForceWildCard::scanOptionEnum*)
    collHeap()->allocateMemory(sizeof(ScanForceWildCard::scanOptionEnum)*numMdamColumns_);
  //enumAlgorithms_ = new scanOptionEnum[numMdamColumns_];
  for (CollIndex i=0; i<numMdamColumns_; i++)
    enumAlgorithms_[i] = COLUMN_SYSTEM;
  return TRUE;
}

//----------------------------------------------------------
// check if the forced scan options conflict with the Mdam
// Master switch status
//----------------------------------------------------------
NABoolean ScanForceWildCard::doesThisCoflictMasterSwitch() const
{
  char* globalMdamStatus = getenv("MDAM");
  if (globalMdamStatus != NULL)
    {
      if (strcmp(globalMdamStatus,"OFF")==0 )
	{
	  if ((mdamStatus_ == MDAM_FORCED)||(mdamStatus_ == MDAM_SYSTEM))
	    return TRUE;
	}
    }
  return FALSE;
}

//----------------------------------------------------------
// merge with another ScanForceWildCard object.
// return FALSE if a conflict between the options of
// the two objects exists.
//----------------------------------------------------------
NABoolean ScanForceWildCard::mergeScanOptions(const ScanForceWildCard &other)
{
  if ((other.exposedName_ != "")
      &&(other.exposedName_ != exposedName_))
    {
      if (exposedName_ == "")
	{
	  exposedName_ = other.exposedName_;
	}
      else
	return FALSE; // conflict
    }

  if ((other.indexName_ != "")
      &&(other.indexName_ != indexName_))
    {
      if (indexName_ == "")
	{
	  indexName_ = other.indexName_;
	}
      else
	return FALSE; // conflict
    }

  if (other.indexStatus_ == INDEX_SYSTEM)
    {
      indexStatus_ = INDEX_SYSTEM;
    }

  if (indexStatus_ == INDEX_SYSTEM)
    {
      if (indexName_ != "")
	return FALSE;  // conflict
    }

  if ((other.mdamStatus_ == MDAM_OFF)
      &&(mdamStatus_ != MDAM_OFF))
    {
      if (mdamStatus_ == UNDEFINED)
	{
	  mdamStatus_ = other.mdamStatus_;
	}
      else
	return FALSE; // conflict
    }

  if ((other.mdamStatus_ == MDAM_SYSTEM)
      &&(mdamStatus_ != MDAM_SYSTEM))
    {
      if (mdamStatus_ == UNDEFINED)
	{
	  mdamStatus_ = other.mdamStatus_;
	}
      else
	return FALSE; // conflict
    }

  if ((other.mdamStatus_ == MDAM_FORCED)
      &&(mdamStatus_ != MDAM_FORCED))
    {
      if (mdamStatus_ == UNDEFINED)
	{
	  mdamStatus_ = other.mdamStatus_;
	}
      else
	return FALSE; // conflict
    }

  if (other.numMdamColumns_ > 0)
    {
      if ((mdamStatus_ == UNDEFINED)||(mdamStatus_ == MDAM_FORCED))
	{
	  if (numMdamColumns_ == other.numMdamColumns_)
	    {
	      for (CollIndex i=0; i<numMdamColumns_; i++)
		{
		  if (enumAlgorithms_[i] != other.enumAlgorithms_[i])
		    return FALSE;	// conflict
		}
	      if (other.mdamColumnsStatus_ != mdamColumnsStatus_)
		return FALSE;		// conflict
	    }
	  else if (numMdamColumns_ == 0) // i.e. enumAlgorithm is NULL
	    {
	      numMdamColumns_ = other.numMdamColumns_;
	      collHeap()->deallocateMemory((void*)enumAlgorithms_);
	      //delete enumAlgorithms_;
	      enumAlgorithms_ = (ScanForceWildCard::scanOptionEnum*)
		collHeap()->allocateMemory(sizeof(ScanForceWildCard::scanOptionEnum)*numMdamColumns_);
	      //enumAlgorithms_ = new scanOptionEnum[numMdamColumns_];
	      for (CollIndex i=0; i<numMdamColumns_; i++)
		{
		  enumAlgorithms_[i] = other.enumAlgorithms_[i];
		}
	    }
	  else
	    return FALSE;	// coflict
	}
      else
	return FALSE; // conflict
    }

  if (other.mdamColumnsStatus_ != UNDEFINED)
    {
      if (mdamColumnsStatus_ == UNDEFINED)
	{
	  mdamColumnsStatus_ = other.mdamColumnsStatus_;
	}
      if (mdamColumnsStatus_ != other.mdamColumnsStatus_)
	{
	  return FALSE; // conflict
	}
    }

  if ((other.direction_ == DIRECTION_FORWARD)
      &&(direction_ != DIRECTION_FORWARD))
    {
      if (direction_ == UNDEFINED)
	{
	  direction_ = other.direction_;
	}
      else
	return FALSE; // conflict
    }

  if ((other.direction_ == DIRECTION_REVERSED)
      &&(direction_ != DIRECTION_REVERSED))
    {
      if (direction_ == UNDEFINED)
	{
	  direction_ = other.direction_;
	}
      else
	return FALSE; // conflict
    }

  if ((other.direction_ == DIRECTION_SYSTEM)
      &&(direction_ != DIRECTION_SYSTEM))
    {
      if (direction_ == UNDEFINED)
	{
	  direction_ = other.direction_;
	}
      else
	return FALSE; // conflict
    }

  if (other.numberOfBlocksToReadPerAccess_ > 0)
    {
      numberOfBlocksToReadPerAccess_ = other.numberOfBlocksToReadPerAccess_;
    }

  return TRUE;
}

//----------------------------------------------------------
// if access path is not given then the default is ANY i.e
// system choice unless MDAM is forced then the default is
// the base table.
//----------------------------------------------------------
void ScanForceWildCard::prepare()
{
  if (mdamStatus_ != ScanForceWildCard::MDAM_FORCED)
    {
      if (indexName_ == "")
	{
	  indexStatus_ = ScanForceWildCard::INDEX_SYSTEM;
	}
    }
  else // mdam is forced
    {
      if ((indexName_ == "") &&
	  (indexStatus_ != ScanForceWildCard::INDEX_SYSTEM))
        {
  	  indexName_ = exposedName_;
	}
    }
  if (mdamColumnsStatus_ == ScanForceWildCard::UNDEFINED)
   {
     mdamColumnsStatus_ = ScanForceWildCard::MDAM_COLUMNS_REST_BY_SYSTEM;
   }

  return;
}

RelExpr * ScanForceWildCard::copyTopNode(RelExpr *derivedNode,
					 CollHeap* outHeap)
{
  ScanForceWildCard *temp;
  if (derivedNode != NULL)
    ABORT("No support for classes derived from ScanForceWildcard");

  temp = new (outHeap) ScanForceWildCard(exposedName_,indexName_,outHeap);
  temp->direction_ = direction_;
  temp->indexStatus_ = indexStatus_;
  temp->mdamStatus_ = mdamStatus_;
  temp->mdamColumnsStatus_ = mdamColumnsStatus_;
  temp->numMdamColumns_ = numMdamColumns_;
  temp->numberOfBlocksToReadPerAccess_ = numberOfBlocksToReadPerAccess_;
  temp->enumAlgorithms_ = new (outHeap) scanOptionEnum[numMdamColumns_];
  for (CollIndex i=0; i<numMdamColumns_; i++)
    {
      temp->enumAlgorithms_[i]=enumAlgorithms_[i];
    }
  RelExpr *result = temp;
  WildCardOp::copyTopNode(result,outHeap);

  return result;
}

const NAString ScanForceWildCard::getText() const
{
  NAString result("forced scan", CmpCommon::statementHeap());

  if (exposedName_ != "")
    {
      result += "(";
      result += exposedName_;

      if (indexName_ != "")
	{
	  result += ", index ";
	  result += indexName_;
	}
      result += ")";
    }

  return result;
}

// -----------------------------------------------------------------------
// member functions for class JoinForceWildCard
// -----------------------------------------------------------------------

JoinForceWildCard::JoinForceWildCard(OperatorTypeEnum type,
				     RelExpr *child0,
				     RelExpr *child1,
				     forcedPlanEnum plan,
				     Int32 numOfEsps,
				     CollHeap *outHeap) :
     WildCardOp(type, 0, child0, child1,outHeap)
{
  plan_ = plan;
  numOfEsps_ = numOfEsps;
}

JoinForceWildCard::~JoinForceWildCard() {}

RelExpr * JoinForceWildCard::copyTopNode(RelExpr *derivedNode,
					 CollHeap* outHeap)
{
  RelExpr *result;
  if (derivedNode != NULL)
    ABORT("No support for classes derived from JoinForceWildcard");

  result = new(outHeap) JoinForceWildCard(getOperatorType(),
					  NULL,
					  NULL,
					  plan_,
					  numOfEsps_,
					  outHeap);
  WildCardOp::copyTopNode(result,outHeap);

  return result;
}

const NAString JoinForceWildCard::getText() const
{
  NAString result("forced", CmpCommon::statementHeap());

  if (plan_ == FORCED_PLAN0)
    result += " plan0";
  else if (plan_ == FORCED_PLAN1)
    result += " plan1";
  else if (plan_ == FORCED_PLAN2)
    result += " plan2";
  else if (plan_ == FORCED_TYPE1)
    result += " type1";
  else if (plan_ == FORCED_TYPE2)
    result += " type2";
  else if (plan_ == FORCED_INDEXJOIN)
    result += " indexjoin";

  switch (getOperatorType())
    {
    case REL_FORCE_NESTED_JOIN:
      result += " nested join";
      break;
    case REL_FORCE_MERGE_JOIN:
      result += " merge join";
      break;
    case REL_FORCE_HASH_JOIN:
      result += " hash join";
      break;
    case REL_FORCE_HYBRID_HASH_JOIN:
      result += " hybrid hash join";
      break;
    case REL_FORCE_ORDERED_HASH_JOIN:
      result += " ordered hash join";
      break;
    default:
      result += " join";
      break;
    }

  return result;
}

// -----------------------------------------------------------------------
// member functions for class ExchangeForceWildCard
// -----------------------------------------------------------------------

ExchangeForceWildCard::ExchangeForceWildCard(RelExpr *child0,
					     forcedExchEnum which,
					     forcedLogPartEnum whatLogPart,
					     Lng32 numBottomEsps,
					     CollHeap *outHeap) :
     WildCardOp(REL_FORCE_EXCHANGE, 0, child0, NULL, outHeap),
     which_(which),
     whatLogPart_(whatLogPart),
     howMany_(numBottomEsps)
{
}

ExchangeForceWildCard::~ExchangeForceWildCard() {}

RelExpr * ExchangeForceWildCard::copyTopNode(RelExpr *derivedNode,
					 CollHeap* outHeap)
{
  RelExpr *result;
  if (derivedNode != NULL)
    ABORT("No support for classes derived from ExchangeForceWildcard");

  result = new(outHeap) ExchangeForceWildCard(NULL,
					      which_,
					      whatLogPart_,
					      howMany_,
					      outHeap);
  WildCardOp::copyTopNode(result,outHeap);

  return result;
}

const NAString ExchangeForceWildCard::getText() const
{
  NAString result("forced",CmpCommon::statementHeap());

  if (which_ == FORCED_PA)
    result += " PA";
  else if (which_ == FORCED_PAPA)
    result += " PAPA";
  else if (which_ == FORCED_ESP_EXCHANGE)
    result += " ESP";

  result += " exchange";

  return result;
}

// -----------------------------------------------------------------------
// member functions for class UDFForceWildCard
// -----------------------------------------------------------------------

UDFForceWildCard::UDFForceWildCard(OperatorTypeEnum op, CollHeap *outHeap) :
  WildCardOp(op, 0, NULL, NULL, outHeap),
  functionName_(outHeap),
  actionName_(outHeap)
{}

UDFForceWildCard::UDFForceWildCard(const NAString& functionName, 
                                   const NAString& actionName, 
                                   CollHeap *outHeap) :
  WildCardOp(REL_FORCE_ANY_SCALAR_UDF, 0, NULL, NULL, outHeap),
  functionName_(functionName, outHeap),
  actionName_(actionName, outHeap)
{}

UDFForceWildCard::~UDFForceWildCard()
{
}

//----------------------------------------------------------
// merge with another UDFForceWildCard object.
// return FALSE if a conflict between the options of
// the two objects exists.
//----------------------------------------------------------
NABoolean UDFForceWildCard::mergeUDFOptions(const UDFForceWildCard &other)
{
  if ((other.functionName_ != "")
      &&(other.functionName_ != functionName_))
    {
      if (functionName_ == "")
	{
	  functionName_ = other.functionName_;
	}
      else
	return FALSE; // conflict
    }
  if ((other.actionName_ != "")
      &&(other.actionName_ != actionName_))
    {
      if (actionName_ == "")
	{
	  actionName_ = other.actionName_;
	}
      else
	return FALSE; // conflict
    }

  return TRUE;
}

RelExpr * UDFForceWildCard::copyTopNode(RelExpr *derivedNode,
					 CollHeap* outHeap)
{
  UDFForceWildCard *temp;
  if (derivedNode != NULL)
    ABORT("No support for classes derived from UDFForceWildCard");

  temp = new (outHeap) UDFForceWildCard(functionName_,actionName_,outHeap);
  RelExpr *result = temp;
  WildCardOp::copyTopNode(result,outHeap);

  return result;
}

const NAString UDFForceWildCard::getText() const
{
  NAString result("forced UDF", CmpCommon::statementHeap());
  if (functionName_ != "")
    {
      result += "(";
      result += functionName_;
      if (actionName_ != "")
        {
          result += ", ";
          result += actionName_;
        }
      result += ")";
    }
  return result;
}

// -----------------------------------------------------------------------
// member functions for Control* base class
// -----------------------------------------------------------------------

ControlAbstractClass::~ControlAbstractClass() {}

NABoolean ControlAbstractClass::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  ControlAbstractClass &o = (ControlAbstractClass &) other;

  // We do NOT need to compare sqlText's here
  return (token_ == o.token_     AND value_ == o.value_ AND
  	  dynamic_ == o.dynamic_ AND reset_ == o.reset_);
}

RelExpr *ControlAbstractClass::copyTopNode(RelExpr *derivedNode, CollHeap *h)
{
  CMPASSERT(derivedNode);
  ((ControlAbstractClass *)derivedNode)->reset_ = reset_;
  return derivedNode;
}

NABoolean ControlAbstractClass::alterArkcmpEnvNow() const
{ 
  return NOT (dynamic() || CmpCommon::context()->GetMode() == STMT_DYNAMIC); 
}

StaticOnly ControlAbstractClass::isAStaticOnlyStatement() const
{
  if (dynamic_) return NOT_STATIC_ONLY;
  return (getOperatorType() == REL_CONTROL_QUERY_DEFAULT) ?
    STATIC_ONLY_WITH_WORK_FOR_PREPROCESSOR : STATIC_ONLY_VANILLA;
}

static void removeLeadingSpace(NAString &sqlText)
{
  if (!sqlText.isNull() && isspace((unsigned char)sqlText[size_t(0)])) sqlText.remove(0, 1);  // For VS2003
}

static NABoolean beginsWithKeyword(NAString &sqlText, const char *kwd,
				   NABoolean thenRemoveIt = TRUE)
{
  // Assumes Prettify has been called, so only one space (at most) btw tokens.
  // If this is called more than once, the second time in the text might begin
  // with a delimiting space.
  removeLeadingSpace(sqlText);

  size_t len = strlen(kwd) + 1;		// +1 for delimiter (space)
  if (sqlText.length() > len) {
  NAString tmp(sqlText,CmpCommon::statementHeap());
    tmp.remove(len);
    char c = tmp[--len];		// delimiter
    if (!isalnum(c) && c != '_') {
      tmp.remove(len);			// remove the delimiter 'c' from tmp
      if (tmp == kwd) {
	if (thenRemoveIt)
	  sqlText.remove(0, len);	// leave delimiter, now at beginning
	return TRUE;
      }
    }
  }
  return FALSE;
}

// Convert "PROCEDURE xyz () CONTROL..." to just the "CONTROL..." part.
// Convert a dynamic stmt's text of  "SET SCHEMA 'x.y';"
// into the static "CONTROL QUERY DEFAULT SCHEMA 'x.y';"
// for its round-trip to executor and back here as a SQLTEXT_STATIC_COMPILE.
void ControlAbstractClass::rewriteControlText(
					NAString &sqlText, CharInfo::CharSet sqlTextCharSet,
					ControlAbstractClass *ctrl)
{
  PrettifySqlText(sqlText);	// trim, upcase where okay, cvt tabs to spaces

  if (beginsWithKeyword(sqlText, "PROCEDURE")) {
    size_t rp = sqlText.index(')');
    CMPASSERT(rp != NA_NPOS);
    sqlText.remove(0, ++rp);
    removeLeadingSpace(sqlText);
  }

  // if SHOWSHAPE or SHOWPLAN, remove them from the beginning.
  // beginsWithKeyword will remove the keyword, if found.
  if ((beginsWithKeyword(sqlText, "SHOWSHAPE")) ||
      (beginsWithKeyword(sqlText, "SHOWPLAN"))) {
    removeLeadingSpace(sqlText);
  }

  if (ctrl->dynamic()) {
    if ((beginsWithKeyword(sqlText, "SET")) &&
	(ctrl->getOperatorType() != REL_SET_SESSION_DEFAULT))
      sqlText.prepend(getControlTextPrefix(ctrl));
  }
  else {
    if (beginsWithKeyword(sqlText, "DECLARE"))
      sqlText.prepend(getControlTextPrefix(ctrl));
  }

  //## We'll have to fix SqlParser.y, I think, if this stmt appears in
  //## a compound stmt (IF ... THEN SET SCHEMA 'x' ... ELSE SET SCHEMA 'y' ...)
  if (ctrl->getOperatorType() != REL_SET_SESSION_DEFAULT)
    {
      if ((NOT beginsWithKeyword(sqlText, "CONTROL", FALSE)) &&
	  (NOT beginsWithKeyword(sqlText, "CQD", FALSE)))
	CMPASSERT(0);
    }
}

NAString ControlAbstractClass::getControlTextPrefix(
     const ControlAbstractClass *ctrl)
{
  switch (ctrl->getOperatorType()) {
    case REL_CONTROL_QUERY_SHAPE:	return "CONTROL QUERY SHAPE";
    case REL_CONTROL_QUERY_DEFAULT:	return "CONTROL QUERY DEFAULT";
    case REL_CONTROL_TABLE:		return "CONTROL TABLE";
    case REL_CONTROL_SESSION:		return "CONTROL SESSION";
    case REL_SET_SESSION_DEFAULT:	return "SET SESSION DEFAULT";
    default:				return "CONTROL ??";
  }
}

// -----------------------------------------------------------------------
// member functions for class ControlQueryShape
// -----------------------------------------------------------------------

RelExpr * ControlQueryShape::copyTopNode(RelExpr *derivedNode,
					 CollHeap *h)
{
  ControlQueryShape *result;

  if (derivedNode == NULL)
    result = new (h) ControlQueryShape(NULL, getSqlText(), getSqlTextCharSet(), holdShape_,
				       dynamic_, ignoreExchange_,
				       ignoreSort_, h);
  else
    result = (ControlQueryShape *) derivedNode;

  return ControlAbstractClass::copyTopNode(result,h);
}

const NAString ControlQueryShape::getText() const
{
  NAString result(getControlTextPrefix(this));

  if (ignoreExchange_)
    {
      if (ignoreSort_)
	result += " WITHOUT ENFORCERS";
      else
	result += " WITHOUT EXCHANGE";
    }
  else if (ignoreSort_)
    result += " WITHOUT SORT";

  return result;
}

// -----------------------------------------------------------------------
// member functions for class ControlQueryDefault
// -----------------------------------------------------------------------

ControlQueryDefault::ControlQueryDefault(
     const NAString &sqlText, CharInfo::CharSet sqlTextCharSet,
     const NAString &token,
     const NAString &value,
     NABoolean dyn,
     Lng32 holdOrRestoreCQD,
     CollHeap *h,
     Int32 reset):
   ControlAbstractClass(REL_CONTROL_QUERY_DEFAULT, sqlText, sqlTextCharSet, token, value,
                        dyn, h, reset),
   holdOrRestoreCQD_(holdOrRestoreCQD),
   attrEnum_(__INVALID_DEFAULT_ATTRIBUTE)
{}

RelExpr * ControlQueryDefault::copyTopNode(RelExpr *derivedNode,
					   CollHeap *h)
{
  RelExpr *result;

  if (derivedNode == NULL) {
    result = new (h) ControlQueryDefault(sqlText_, sqlTextCharSet_, token_, value_, dynamic_, holdOrRestoreCQD_, h);
    ((ControlQueryDefault *)result)->attrEnum_ = attrEnum_;
  }
  else
    result = derivedNode;

  return ControlAbstractClass::copyTopNode(result,h);
}

const NAString ControlQueryDefault::getText() const
{
  return getControlTextPrefix(this);
}

// -----------------------------------------------------------------------
// member functions for class ControlTable
// -----------------------------------------------------------------------

ControlTable::ControlTable(
     CorrName *tableName,
     const NAString &sqlText, CharInfo::CharSet sqlTextCharSet,
     const NAString &token,
     const NAString &value,
     NABoolean dyn,
     CollHeap *h):
   ControlAbstractClass(REL_CONTROL_TABLE, sqlText, sqlTextCharSet, token, value, dyn, h),
   tableName_(tableName)
{}

RelExpr * ControlTable::copyTopNode(RelExpr *derivedNode,
				    CollHeap *h)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (h) ControlTable(tableName_, sqlText_, sqlTextCharSet_, token_, value_, dynamic_, h);
  else
    result = derivedNode;

  return ControlAbstractClass::copyTopNode(result,h);
}

const NAString ControlTable::getText() const
{
  return getControlTextPrefix(this);
}

// -----------------------------------------------------------------------
// member functions for class ControlSession
// -----------------------------------------------------------------------

ControlSession::ControlSession(
     const NAString &sqlText, CharInfo::CharSet sqlTextCharSet,
     const NAString &token,
     const NAString &value,
     NABoolean dyn,
     CollHeap *h):
   ControlAbstractClass(REL_CONTROL_SESSION, sqlText, sqlTextCharSet, token, value, dyn, h)
{}

RelExpr * ControlSession::copyTopNode(RelExpr *derivedNode,
				      CollHeap *h)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (h) ControlSession(sqlText_, sqlTextCharSet_, token_, value_, dynamic_, h);
  else
    result = derivedNode;

  return ControlAbstractClass::copyTopNode(result,h);
}

const NAString ControlSession::getText() const
{
  return getControlTextPrefix(this);
}

// -----------------------------------------------------------------------
// member functions for class SetSessionDefault
// -----------------------------------------------------------------------

SetSessionDefault::SetSessionDefault(
     const NAString &sqlText, CharInfo::CharSet sqlTextCharSet,
     const NAString &token,
     const NAString &value,
     CollHeap *h):
     ControlAbstractClass(REL_SET_SESSION_DEFAULT, sqlText, sqlTextCharSet, token, value, TRUE, h)
{}

RelExpr * SetSessionDefault::copyTopNode(RelExpr *derivedNode,
					 CollHeap *h)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (h) SetSessionDefault(sqlText_, sqlTextCharSet_, token_, value_, h);
  else
    result = derivedNode;

  return ControlAbstractClass::copyTopNode(result,h);
}

const NAString SetSessionDefault::getText() const
{
  return getControlTextPrefix(this);
}

// -----------------------------------------------------------------------
// member functions for class OSIMControl
// -----------------------------------------------------------------------
OSIMControl::OSIMControl(OptimizerSimulator::osimMode mode,
                                       NAString & localDir,
                                       NABoolean force,
                                       CollHeap * oHeap)
                                       //the real work is done in OSIMControl::bindNode() to control OSIM.
                                       //We set operator type to REL_SET_SESSION_DEFAULT,
                                       //so as not to define dummy OSIMControl::codeGen() and OSIMControl::work(),
                                       //which will do nothing there,  
                       : ControlAbstractClass(REL_SET_SESSION_DEFAULT, NAString("DUMMYSQLTEXT", oHeap), 
                                                           CharInfo::ISO88591, NAString("OSIM", oHeap), 
                                                           NAString("DUMMYVALUE", oHeap), TRUE, oHeap)
                       , targetMode_(mode), osimLocalDir_(localDir, oHeap), forceLoad_(force)
{}


RelExpr * OSIMControl::copyTopNode(RelExpr *derivedNode, CollHeap *h )
{
      RelExpr *result;

      if (derivedNode == NULL)
          result = new (h) OSIMControl(targetMode_, osimLocalDir_, forceLoad_, h);
      else
          result = derivedNode;
          
      return ControlAbstractClass::copyTopNode(result,h);
}

// -----------------------------------------------------------------------
// member functions for class Sort
// -----------------------------------------------------------------------

Sort::~Sort()
{
}

Int32 Sort::getArity() const { return 1;}

HashValue Sort::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= sortKey_;
  result ^= arrangedCols_;

  return result;
}

NABoolean Sort::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  Sort &o = (Sort &) other;

  if (NOT (sortKey_ == o.sortKey_) OR
      NOT(arrangedCols_ == o.arrangedCols_))
    return FALSE;

  return TRUE;
}

RelExpr * Sort::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Sort *result;

  if (derivedNode == NULL)
    result = new (outHeap) Sort(NULL, sortKey_, outHeap);
  else
    result = (Sort *) derivedNode;

  // copy arranged columns
  result->arrangedCols_ = arrangedCols_;

  return RelExpr::copyTopNode(result, outHeap);
}

NABoolean Sort::isLogical() const  { return FALSE; }

NABoolean Sort::isPhysical() const { return TRUE;  }

const NAString Sort::getText() const
{
  return "sort";
}

PlanPriority Sort::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  double val = 1;
  if (degreeOfParallelism <= 1)
    {
      // serial plans are risky. exact an insurance premium from serial plans.
      val = CURRSTMT_OPTDEFAULTS->riskPremiumSerial();
    }
  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  if (QueryAnalysis::Instance() AND
      QueryAnalysis::Instance()->optimizeForFirstNRows())
    result.incrementLevels(SORT_FIRST_N_PRIORITY,0);

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR ( maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

  return result;
}

void Sort::addLocalExpr(LIST(ExprNode *) &xlist,
			LIST(NAString) &llist) const
{
  if (sortKey_.entries() > 0)
    {
      xlist.insert(sortKey_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("sort_key");
    }

  if (PartialSortKeyFromChild_.entries() > 0)
    {
      xlist.insert(PartialSortKeyFromChild_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("PartialSort_FromChild");
    }

//  if (NOT arrangedCols_.isEmpty())
//    {
//      xlist.insert(arrangedCols_.rebuildExprTree(ITM_ITEM_LIST));
//      llist.insert("arranged_cols");
//    }

  RelExpr::addLocalExpr(xlist,llist);
}

void Sort::needSortedNRows(NABoolean val)
{
  sortNRows_ = val;
  // Sort changes a GET_N to GET_ALL, so it does not propagate a 
  // Get_N request. It can simply act on one.
}

// -----------------------------------------------------------------------
// member functions for class SortFromTop
// -----------------------------------------------------------------------
RelExpr * SortFromTop::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  SortFromTop *result;

  if (derivedNode == NULL)
    result = new (outHeap) SortFromTop(NULL, outHeap);
  else
    result = (SortFromTop *) derivedNode;

  result->getSortRecExpr() = getSortRecExpr();

  return Sort::copyTopNode(result, outHeap);
}

const NAString SortFromTop::getText() const
{
  return "sort_from_top";
}

// -----------------------------------------------------------------------
// member functions for class Exchange
// -----------------------------------------------------------------------

Exchange::~Exchange()
{
}

Int32 Exchange::getArity() const { return 1;}

RelExpr * Exchange::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Exchange *result;

  if (derivedNode == NULL)
    {
      result = new (outHeap) Exchange(NULL, outHeap);
    }
  else
    result = (Exchange *) derivedNode;

  result->upMessageBufferLength_=upMessageBufferLength_;
  result->downMessageBufferLength_=downMessageBufferLength_;
  result->hash2RepartitioningWithSameKey_ = hash2RepartitioningWithSameKey_;
  if (halloweenSortIsMyChild_) 
    result->markHalloweenSortIsMyChild();

  return RelExpr::copyTopNode(result, outHeap);
}

NABoolean Exchange::isLogical() const  { return FALSE; }

NABoolean Exchange::isPhysical() const { return TRUE;  }

NABoolean Exchange::isAPA() const
{
  if (NOT isDP2Exchange() OR bottomPartFunc_ == NULL)
    return FALSE;

  const LogPhysPartitioningFunction *lpf = bottomPartFunc_->
    castToLogPhysPartitioningFunction();

  return (lpf == NULL OR NOT lpf->getUsePapa());
}

NABoolean Exchange::isAPAPA() const
{
  return (isDP2Exchange() AND bottomPartFunc_ AND NOT isAPA());
}

const NAString Exchange::getText() const
{
  NAString result("exchange",CmpCommon::statementHeap());

  if (isAPA())
    result = "pa_exchange";
  else if (isAPAPA())
    result = "split_top";
  else if (isAnESPAccess())
    result = "esp_access";
  else if (isEspExchange())
    result = "esp_exchange";

  const PartitioningFunction *topPartFunc =
    getTopPartitioningFunction();
  const PartitioningFunction *bottomPartFunc =
    getBottomPartitioningFunction();

  Lng32 topNumParts = ANY_NUMBER_OF_PARTITIONS;
  Lng32 bottomNumParts = ANY_NUMBER_OF_PARTITIONS;

  if (topPartFunc)
    topNumParts = topPartFunc->getCountOfPartitions();
  if (bottomPartFunc)
    bottomNumParts = bottomPartFunc->getCountOfPartitions();

  if (topNumParts != ANY_NUMBER_OF_PARTITIONS OR
      bottomNumParts != ANY_NUMBER_OF_PARTITIONS)
    {
      char str[TEXT_DISPLAY_LENGTH];
      sprintf(str," %d:%d",topNumParts,bottomNumParts);
      result += str;
    }

  if (bottomPartFunc AND isDP2Exchange())
    {
      const LogPhysPartitioningFunction *lpf =
          bottomPartFunc->castToLogPhysPartitioningFunction();
      if (lpf)
	{
	  if (lpf->getUsePapa())
	    {
	      char str[TEXT_DISPLAY_LENGTH];
	      sprintf(str," with %d PA(s)",lpf->getNumOfClients());
	      result += str;
	    }
	  switch (lpf->getLogPartType())
	    {
	    case LogPhysPartitioningFunction::LOGICAL_SUBPARTITIONING:
	      result += ", log. subpart.";
	      break;
	    case LogPhysPartitioningFunction::HORIZONTAL_PARTITION_SLICING:
	      result += ", slicing";
	      break;
	    case LogPhysPartitioningFunction::PA_GROUPED_REPARTITIONING:
	      result += ", repartitioned";
	      break;
	    default:
	      break;
	    }
	}
    }
  return result;
}

PlanPriority Exchange::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  PlanPriority result;

  OperatorTypeEnum parOperType = context->getCurrentAncestor()->getPlan()->
                                   getPhysicalExpr()->getOperatorType();

  Lng32 cqdValue = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if ((cqdValue == -2) AND
      ((parOperType == REL_ROOT) OR
       (parOperType == REL_FIRST_N)))
    result.incrementLevels(10,0);

  return result;
}

void Exchange::addLocalExpr(LIST(ExprNode *) &xlist,
			    LIST(NAString) &llist) const
{
  const PartitioningFunction *topPartFunc =
    getTopPartitioningFunction();

  if (topPartFunc AND topPartFunc->getCountOfPartitions() > 1 AND
      topPartFunc->getPartitioningExpression())
    {
      xlist.insert(topPartFunc->getPartitioningExpression());
      llist.insert("partitioning_expression");
    }

  if (NOT sortKeyForMyOutput_.isEmpty())
    {
      xlist.insert(sortKeyForMyOutput_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("merged_order");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

// -----------------------------------------------------------------------
// member functions for class Join
// -----------------------------------------------------------------------
Int32 Join::getArity() const { return 2;}

// helper function used in 
//   HashJoinRule::topMatch() and 
//   JoinToTSJRule::topMatch()
// to make sure that only one of them is turned off.
// Otherwise, error 2235 "pass one skipped, but cannot produce a plan 
// in pass two, originated from file ../optimizer/opt.cpp"
NABoolean Join::allowHashJoin()
{
  // HJ is only implementation for full outer join
  NABoolean fullOuterJoin = 
    (CmpCommon::getDefault(COMP_BOOL_196) != DF_ON) AND isFullOuterJoin();

  if (fullOuterJoin) return TRUE;

  // HJ is only join implementation allowed when avoiding halloween update
  if (avoidHalloweenR2()) return TRUE;

  // favor NJ when only one row from outer for sure
  Cardinality minrows, outerLimit; 
  GroupAttributes *oGrpAttr = child(0).getGroupAttr();
  NABoolean hasConstraint = oGrpAttr->hasCardConstraint(minrows, outerLimit);
  // outerLimit was set to INFINITE_CARDINALITY if no constraint
  CostScalar outerRows(outerLimit); 
  // if it has no constraint and outer is 1 table
  if (!hasConstraint && oGrpAttr->getNumBaseTables() == 1) {
    // use max cardinality estimate
    outerRows = oGrpAttr->getResultMaxCardinalityForEmptyInput();
  }
  NABoolean favorNJ = 
    CURRSTMT_OPTDEFAULTS->isNestedJoinConsidered() AND
    (outerRows <= 1) AND
    GlobalRuleSet->getCurrentPassNumber() >
    GlobalRuleSet->getFirstPassNumber();

  if (favorNJ) return FALSE; // disallow HJ

  return TRUE; // allow HJ
}

OperatorTypeEnum Join::getTSJJoinOpType()
{
  switch (getOperatorType())
    {
    case REL_JOIN:
    case REL_ROUTINE_JOIN:
      return REL_TSJ;
    case REL_LEFT_JOIN:
      return REL_LEFT_TSJ;
    case REL_SEMIJOIN:
      return REL_SEMITSJ;
    case REL_ANTI_SEMIJOIN:
      return REL_ANTI_SEMITSJ;
    default:
      ABORT("Unsupported join type in Join::getTSJJoinOpType()");
      return REL_NESTED_JOIN;    // Return makes MSVC happy.
    } // switch
} // Join::getNestedJoinOpType()


OperatorTypeEnum Join::getNestedJoinOpType()
{
  switch (getOperatorType())
    {
    case REL_JOIN:
    case REL_ROUTINE_JOIN:
    case REL_TSJ:
      return REL_NESTED_JOIN;
    case REL_LEFT_JOIN:
    case REL_LEFT_TSJ:
      return REL_LEFT_NESTED_JOIN;
    case REL_SEMIJOIN:
    case REL_SEMITSJ:
      return REL_NESTED_SEMIJOIN;
    case REL_ANTI_SEMIJOIN:
    case REL_ANTI_SEMITSJ:
      return REL_NESTED_ANTI_SEMIJOIN;
    default:
      ABORT("Unsupported join type in Join::getNestedJoinOpType()");
      return REL_NESTED_JOIN;    // Return makes MSVC happy.
    } // switch
} // Join::getNestedJoinOpType()

OperatorTypeEnum Join::getHashJoinOpType(NABoolean isNoOverflow)
{
  if(isNoOverflow)
  {
      switch (getOperatorType())
    {
    case REL_JOIN:
      return REL_ORDERED_HASH_JOIN;
    case REL_LEFT_JOIN:
      return REL_LEFT_ORDERED_HASH_JOIN;
    case REL_SEMIJOIN:
      return REL_ORDERED_HASH_SEMIJOIN;
    case REL_ANTI_SEMIJOIN:
      return REL_ORDERED_HASH_ANTI_SEMIJOIN;
    default:
      ABORT("Unsupported join type in Join::getHashJoinOpType()");
      return REL_ORDERED_HASH_JOIN;     // return makes MSVC happy.
      } // switch
  }
  else
  {

    switch (getOperatorType())
      {
      case REL_JOIN:
	return REL_HYBRID_HASH_JOIN;
      case REL_LEFT_JOIN:
	return REL_LEFT_HYBRID_HASH_JOIN;
      case REL_FULL_JOIN:
	return REL_FULL_HYBRID_HASH_JOIN;
      case REL_SEMIJOIN:
	return REL_HYBRID_HASH_SEMIJOIN;
      case REL_ANTI_SEMIJOIN:
	return REL_HYBRID_HASH_ANTI_SEMIJOIN;
      default:
	ABORT("Unsupported join type in Join::getHashJoinOpType()");
	return REL_HYBRID_HASH_JOIN;     // return makes MSVC happy.
      } // switch
  }//else

} // Join::getHashJoinOpType()

NABoolean Join::isCrossProduct() const
{
  // only for our beloved inner non semi joins (for now)
  if (NOT isInnerNonSemiJoin()) return FALSE;

  ValueIdSet VEGEqPreds;
  ValueIdSet newJoinPreds = getSelectionPred();
  newJoinPreds += getJoinPred();

  // -----------------------------------------------------------------
  // Find all the VEGPreds in the newJoinPreds.
  // Remove all the ones that are not true join predicates
  //   (i.e. they are covered by the inputs, or one of the
  //         children cannot produce it)
  // -----------------------------------------------------------------
  newJoinPreds.lookForVEGPredicates(VEGEqPreds);

  // remove those VEGPredicates that are covered by the input values
  VEGEqPreds.removeCoveredExprs(getGroupAttr()->
				getCharacteristicInputs());
  VEGEqPreds.removeUnCoveredExprs(child(0).getGroupAttr()->
                                  getCharacteristicOutputs());
  VEGEqPreds.removeUnCoveredExprs(child(1).getGroupAttr()->
                                  getCharacteristicOutputs());
  if (VEGEqPreds.isEmpty())
    return TRUE;    // is a cross product
  else
    return FALSE;
}

NABoolean Join::isOuterJoin() const
{
  if(isLeftJoin() || isRightJoin() || isFullOuterJoin())
    return TRUE;

  return FALSE;
}

NABoolean RelExpr::isAnyJoin() const
{
   switch (getOperatorType())
   {
    case REL_JOIN:
    case REL_ROUTINE_JOIN:
    case REL_LEFT_JOIN:
    case REL_ANY_JOIN:
    case REL_ANY_TSJ:
    case REL_ANY_INNER_JOIN:
    case REL_ANY_NON_TS_INNER_JOIN:
    case REL_ANY_NON_TSJ_JOIN:
    case REL_ANY_LEFT_JOIN:
    case REL_ANY_LEFT_TSJ:
    case REL_ANY_NESTED_JOIN:
    case REL_ANY_HASH_JOIN:
    case REL_ANY_MERGE_JOIN:
    case REL_FORCE_JOIN:
    case REL_FORCE_NESTED_JOIN:
    case REL_FORCE_HASH_JOIN:
    case REL_FORCE_ORDERED_HASH_JOIN:
    case REL_FORCE_HYBRID_HASH_JOIN:
    case REL_FORCE_MERGE_JOIN:
    case REL_INTERSECT:
    case REL_EXCEPT:
      return TRUE;

    default:
      return FALSE;
   }
}

NABoolean Join::isInnerNonSemiJoinWithNoPredicates() const
{
  if (isInnerNonSemiJoin() AND
      getSelectionPred().isEmpty() AND
      getJoinPred().isEmpty())
      return TRUE;
  else
    return FALSE;
}


NABoolean Join:: isInnerJoin() const
{
  return getOperator().match(REL_ANY_INNER_JOIN);
}

NABoolean Join::isInnerNonSemiJoin() const
{
  return getOperator().match(REL_ANY_INNER_JOIN) AND
    NOT getOperator().match(REL_ANY_SEMIJOIN);
}

NABoolean Join::isInnerNonSemiNonTSJJoin() const
{
  return (getOperator().match(REL_ANY_INNER_JOIN) AND
    NOT getOperator().match(REL_ANY_SEMIJOIN)) AND
    NOT getOperator().match(REL_ANY_TSJ);
}
NABoolean Join::isLeftJoin() const
{
  return getOperator().match(REL_ANY_LEFT_JOIN);
}

NABoolean Join::isRightJoin() const
{
  return getOperator().match(REL_ANY_RIGHT_JOIN);
}

NABoolean Join::isFullOuterJoin() const
{
  return (getOperator().match(REL_ANY_FULL_JOIN));
}

NABoolean Join::isSemiJoin() const
{
  return getOperator().match(REL_ANY_SEMIJOIN);
}

NABoolean Join::isAntiSemiJoin() const
{
  return getOperator().match(REL_ANY_ANTI_SEMIJOIN);
}

NABoolean Join::isTSJ() const
{
  return getOperator().match(REL_ANY_TSJ);
}

NABoolean Join::isRoutineJoin() const
{
  return (getOperator() == REL_ROUTINE_JOIN);
}

NABoolean Join::isNonRoutineTSJ() const
{
  return ( getOperator().match(REL_ANY_TSJ) && 
          ( getOperator() != REL_ROUTINE_JOIN));
}

NABoolean Join::isNestedJoin() const
{
  return getOperator().match(REL_ANY_NESTED_JOIN);
}

NABoolean Join::isHashJoin() const
{
  return getOperator().match(REL_ANY_HASH_JOIN);
}

OperatorTypeEnum Join::getBaseHashType() const
{
  switch (getOperatorType())
    {
    case REL_HASH_JOIN:
    case REL_LEFT_HASH_JOIN:
    case REL_HASH_SEMIJOIN:
    case REL_HASH_ANTI_SEMIJOIN:
    case REL_ANY_HASH_JOIN:
      return REL_HASH_JOIN;

    case REL_HYBRID_HASH_JOIN:
    case REL_LEFT_HYBRID_HASH_JOIN:
    case REL_FULL_HYBRID_HASH_JOIN:
    case REL_HYBRID_HASH_SEMIJOIN:
    case REL_HYBRID_HASH_ANTI_SEMIJOIN:
      return REL_HYBRID_HASH_JOIN;

    case REL_ORDERED_HASH_JOIN:
    case REL_LEFT_ORDERED_HASH_JOIN:
    case REL_ORDERED_HASH_SEMIJOIN:
    case REL_ORDERED_HASH_ANTI_SEMIJOIN:
      return REL_ORDERED_HASH_JOIN;

    default:
      return INVALID_OPERATOR_TYPE;
    } // switch
} // Join::getBaseHashType

NABoolean Join::isMergeJoin() const
{
  return getOperator().match(REL_ANY_MERGE_JOIN);
}

OperatorTypeEnum Join::getMergeJoinOpType()
{
  switch (getOperatorType())
    {
    case REL_JOIN:
      return REL_MERGE_JOIN;
    case REL_LEFT_JOIN:
      return REL_LEFT_MERGE_JOIN;
    case REL_SEMIJOIN:
      return REL_MERGE_SEMIJOIN;
    case REL_ANTI_SEMIJOIN:
      return REL_MERGE_ANTI_SEMIJOIN;
    default:
      ABORT("Unsupported join type in Join::getMergeJoinOpType()");
      return REL_MERGE_JOIN;  // return makes MSVC happy.
    } // switch
} // Join::getMergeJoinOpType()

void Join::pushdownCoveredExpr(const ValueIdSet & outputExpr,
                               const ValueIdSet & newExternalInputs,
                               ValueIdSet & predicatesOnParent,
			       const ValueIdSet * setOfValuesReqdByParent,
			       Lng32 childIndex
		              )
{
  #ifdef NDEBUG
  NAString pushdownDebugStr(CmpCommon::statementHeap());
    #define PUSHDOWN_DEBUG_SAVE(str)
  #else
    Int32 PUSHDOWN_DEBUG = !!getenv("PUSHDOWN_DEBUG");
    NAString pushdownDebugStr, pushdownDebugTmp;
    #define PUSHDOWN_DEBUG_SAVE(str)					      \
      { if (PUSHDOWN_DEBUG)						      \
	{ pushdownDebugTmp = "";					      \
	  predicatesOnParent.unparse(pushdownDebugTmp);			      \
	  pushdownDebugStr += NAString("\n") + str + ": " + pushdownDebugTmp; \
	  if (PUSHDOWN_DEBUG == 99) cerr << pushdownDebugStr << endl;	      \
	}								      \
      }
    PUSHDOWN_DEBUG_SAVE("J1");
  #endif

  // ----------------------------------------------------------------------
  // Note: Normally, predicatesOnParent is the set of predicates to be
  // considered for pushing down. For most of the other nodes, this set
  // is usually just the set (or maybe a subset) of selection predicates
  // the node has. However, a Join node distinguishes between selection
  // predicates (which are specified in the WHERE clause of a SQL query)
  // and join predicates (which are specified in the ON clause). The two
  // types of predicates have different criteria (as we will see later)
  // to satisfy in order to be pushed down. The predicatesOnParent supplied
  // are treated as selection predicates for the purpose of consideration
  // for pushing down. Note also that this procedure also considers the
  // pushing down of join predicates (as in joinPred_ of Join).
  // ----------------------------------------------------------------------

  // This method only supports pushing predicates down to both children,
  // but not only to one specified.
  //
  CMPASSERT(childIndex < 0);

  NABoolean isATSJFlag = isTSJ();
  ValueIdSet exprOnParent ;
  if (setOfValuesReqdByParent)
    exprOnParent = *setOfValuesReqdByParent;

  if (isFullOuterJoin())
    {
      // For Full Outer Join, we cannot push down the selctionPred()
      // or the  joinPred() to either child0 or child1.
      // Note that for FOJ, predicates are not pulled up.

      // ---------------------------------------------------------------------
      // STEP 1: We cannot pushdown join predicates to either child,
      //         so compute values required to evaluate the joinPred()
      //         here at the parent (the Join) and add
      //         it to exprOnParent.
      //
      // ---------------------------------------------------------------------
      computeValuesReqdForPredicates(joinPred(),
				     exprOnParent, TRUE);

      // ---------------------------------------------------------------------
      // STEP 2: We cannot pushdown selectionPred()  to either child,
      //         so compute values required to evaluate the selectionPred()
      //         here at the parent (the Join) and add
      //         it to exprOnParent.
      //
      // ---------------------------------------------------------------------
      computeValuesReqdForPredicates(selectionPred(),
				     exprOnParent, TRUE);

      // ---------------------------------------------------------------------
      // STEP 3: Calling pushdownCoveredExpr on an empty set, so that the child
      // inputs and outputs are set properly.
      // ---------------------------------------------------------------------
      ValueIdSet emptySet;

       RelExpr::pushdownCoveredExpr(outputExpr,
				   newExternalInputs,
				   emptySet,
				   &exprOnParent,
				   0);

      // ---------------------------------------------------------------------
      // STEP 4: Calling pushdownCoveredExpr on an empty set, so that the child
      // inputs and outputs are set properly.
      // ---------------------------------------------------------------------
      RelExpr::pushdownCoveredExpr(outputExpr,
				   newExternalInputs,
				   emptySet,
				   &exprOnParent,
				   1);

    } // if (isFullOuterJoin())
  // -----------------------------------------------------------------------
  // It might not be obvious, but it turns out that pushing down of
  // predicates in a Left Join is quite similar to that in an Anti-Semi
  // Join. One striking similarity is that the join predicates cannot be
  // pushed down to the left child. In both cases, pushing down join preds
  // filters out rows from the left child which shouldn't be filtered out.
  // In the case of Left Join, those rows should be null-instantiated while
  // in the case of Anti-Semi Join, they are exactly the set of rows which
  // we *should* return. (An Anti-Semi Join returns rows from the left
  // which do *not* join with rows from the right).
  // -----------------------------------------------------------------------
  else if ((isLeftJoin() || isAntiSemiJoin()) && (!isFullOuterJoin()))
    {
      // ---------------------------------------------------------------------
      // STEP 1: Try to push down the given predicatesOnParent to first child.
      // ---------------------------------------------------------------------

    // ---------------------------------------------------------------------
    // STEP 1A: Gather all values the left child must still produce even if
    //          predicates are pushed down.
    //
    // Selection predicates can only be pushed to the first child of an
    // outer join. Join predicates can only be pushed to the second. Make
    // sure the first child produces what we need for the join predicates.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(joinPred(),
                                   exprOnParent);

    // ---------------------------------------------------------------------
    // If this is a TSJ, the left child should also produce those values
    // that the parent needs to give as inputs to the second child.
    // ---------------------------------------------------------------------
    if (isATSJFlag)
         exprOnParent += child(1).getGroupAttr()->getCharacteristicInputs();

    // ---------------------------------------------------------------------
    // This seems to be the only difference between the case of a Left Join
    // and the case of a Anti-Semi Join. For an Anti-Semi Join, selection
    // predicates should be in terms of columns on the left child only (by
    // definition). Therefore, we don't have to worry about retaining things
    // like VEGPred(VEG{T1.a,T2.a}).
    // ---------------------------------------------------------------------
    ValueIdSet VEGEqPreds1;
    if (isLeftJoin())
    {
    // ---------------------------------------------------------------------
    // Find all the VEGPreds in predicatesOnParent. VEGPred(VEG{T1.a,T2.a})
    // will be pushed down to Scan T1 even if T2.a is not available there.
    // Therefore, we still need to keep a copy of this type of predicates
    // here at this Join node where both T1.a and T2.a will be available.
    // ---------------------------------------------------------------------
    predicatesOnParent.lookForVEGPredicates(VEGEqPreds1);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds that are covered by the input values, since
    // VEGPred(VEG{T1.a,3}) needn't be retained at this Join node after it's
    // pushed down to Scan T1.
    // ---------------------------------------------------------------------
    VEGEqPreds1.removeCoveredExprs(newExternalInputs);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds which are not covered at second child. For
    // example VEGPred(VEG{T1.a,T2.a}) in JOIN2 of ((T1 JOIN1 T2) JOIN2 T3)
    // is not covered at the second child. The predicate should be pushed
    // down to the first child without being retained at JOIN2. Note that
    // since predicatesOnParent are selection predicates evaluated after
    // a Left Join, they are in terms of the null-instantiated outputs from
    // the Join rather than direct outputs from the second child.
    // ---------------------------------------------------------------------
    VEGEqPreds1.removeUnCoveredExprs(nullInstantiatedOutput());
    PUSHDOWN_DEBUG_SAVE("J2");

    // ---------------------------------------------------------------------
    // ??? First child not needed ??? since we are trying to push down to
    // child0, if it's uncovered, it wouldn't be pushed down anyway.
    //VEGEqPreds1.removeUnCoveredExprs(
    //                 child(0).getGroupAttr()->getCharacteristicOutputs());
    // ---------------------------------------------------------------------

    // ---------------------------------------------------------------------
    // Since these VEGEqPreds1 will be added back to predicatesOnParent
    // after the attempt to push down to first child, make sure the first
    // child produces the required values to evaluate them.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(VEGEqPreds1,
                                   exprOnParent);
    } // endif (isLeftJoin())

    // ---------------------------------------------------------------------
    // STEP 1B: Perform pushdown to the first child, and add VEGEqPreds
    // back to predicatesOnParent after the push down.
    // ---------------------------------------------------------------------
    RelExpr::pushdownCoveredExpr(outputExpr,
                                 newExternalInputs,
                                 predicatesOnParent,
				 &exprOnParent,
                                 0);

    // ---------------------------------------------------------------------
    // All selection predicates could be pushed to the first child for an
    // Anti-Semi Join should those predicates should involve columns from
    // the second child by definition.
    // ---------------------------------------------------------------------
    if (isAntiSemiJoin())
    {
      CMPASSERT(predicatesOnParent.isEmpty()
		// QSTUFF
		OR getGroupAttr()->isGenericUpdateRoot()
		// QSTUFF
	  );
    }
    else
      predicatesOnParent += VEGEqPreds1;
    PUSHDOWN_DEBUG_SAVE("J3");

    // ---------------------------------------------------------------------
    // STEP 2: Try to push down the join predicates to second child.
    // ---------------------------------------------------------------------

    // ---------------------------------------------------------------------
    // STEP 2A: Gather all values the second child must still produce even
    //          if predicates are pushed down. Start with all the required
    //          values specified by the caller of this method.
    // ---------------------------------------------------------------------
    if (setOfValuesReqdByParent)
      exprOnParent = *setOfValuesReqdByParent;
    else exprOnParent.clear();

    // ---------------------------------------------------------------------
    // Since the remaining predicatesOnParent could not be pushed down to
    // the second child and must be evaluated on the Left Join, values reqd
    // for their evaluation must be included to make sure the second child
    // produces them. For Anti-Semi Join, predicatesOnParent is empty.
    // ---------------------------------------------------------------------
    ValueIdSet inputs = newExternalInputs;
    ValueIdSet inputsTakenOut;
    if (isLeftJoin())
    {
      computeValuesReqdForPredicates(predicatesOnParent,
                                     exprOnParent);

      // -------------------------------------------------------------------
      // Special case: If this left join is the right child of a Nested
      // Join, it could happen that the inputs we get from above already
      // contains a value which our push down logic considers to have
      // covered the predicatesOnParent which we don't attempt to push
      // down. This is a very peculiar failure of our cover logic, where
      // the predicates should have been pushed down but held back cos of
      // the semantics of an operator (case in point, the left join). To
      // deal with this, we remove from the available inputs to my child
      // those values so that the child will produce this as an output.
      // -------------------------------------------------------------------
      // inputTakenOut = inputs;
      // predicatesOnParent.weedOutUnreferenced(inputsTakenOut);
      // inputs -= inputsTakenOut;
    }

    // ---------------------------------------------------------------------
    // Also, if this is NOT a TSJ, there are some join predicates which need
    // to be retained even if they are pushed down to the second child. All
    // Join predicates are pushable to the second child of a TSJ without
    // being retained at the TSJ. (See later for an exception)
    // ---------------------------------------------------------------------
    ValueIdSet VEGEqPreds2;
    ValueIdSet availableInputs = inputs;
    if (isATSJFlag)
    {
      availableInputs += child(0).getGroupAttr()->getCharacteristicOutputs();
    }
    else
    {
      // -------------------------------------------------------------------
      // First, find all the VEGPreds in join predicates. This is similar to
      // what we did above with predicatesOnParent. VEGPred(VEG{T1.a,T2.a})
      // will be pushed down to Scan T2 even if T1.a is not available there.
      // Therefore, we still need to keep a copy of this type of predicates
      // here at this Join node where both T1.a and T2.a will be available.
      // -------------------------------------------------------------------
      joinPred().lookForVEGPredicates(VEGEqPreds2);

      // -------------------------------------------------------------------
      // Remove those VEGPreds that are covered by the input values, since
      // VEGPred(VEG{T2.a,3}) needn't be retained at this Join node after
      // pushed down to Scan T2. (There is an exception to this. See later.)
      // -------------------------------------------------------------------
      VEGEqPreds2.removeCoveredExprs(availableInputs); //newExternalInputs

      // -------------------------------------------------------------------
      // Remove those VEGPreds which are not covered at first child. For
      // example VEGPred(VEG{T2.a,T3.a}) in JOIN1 of (T1 JOIN1 (T2 JOIN2 T3))
      // is not covered at the first child. The predicate could be pushed
      // down to the second child without being retained at JOIN2.
      // -------------------------------------------------------------------
      VEGEqPreds2.removeUnCoveredExprs(
                       child(0).getGroupAttr()->getCharacteristicOutputs());

      // -------------------------------------------------------------------
      // Since these predicates will be added back to the join predicates
      // after the attempt to push down to second child, make sure the second
      // child produces the required values to evaluate them.
      // -------------------------------------------------------------------
      computeValuesReqdForPredicates(VEGEqPreds2,
                                     exprOnParent);
    }

    // ---------------------------------------------------------------------
    // Now, there are additional join predicates that must be retained
    // even if they are pushable to the second child. An example would be
    // VEGPred(VEG{T1.a,T2.a,10}). For an inner join, this predicate can
    // be pushed to Scan T1 and Scan T2 and evaluated as (T1.a=10) and
    // (T2.a=10) respectively. However, for a Left Join or Anti-Semi Join,
    // this predicate (if it's a join predicate) cannot be pushed down to
    // the first child. The (T1.a=10) part must then be retained at this
    // Join node. These types of VEGPreds are those covered by T1 and the
    // external inputs.
    // ---------------------------------------------------------------------
    ValueIdSet joinPredsThatStay;
    joinPredsThatStay = joinPred();
    ValueIdSet availableValues = availableInputs; //newExternalInputs
    availableValues += child(0).getGroupAttr()->getCharacteristicOutputs();
    joinPredsThatStay.removeUnCoveredExprs(availableValues);

    // ---------------------------------------------------------------------
    // However, we don't want VEGPred like VEGPred(VEG{T2.a,10}) which
    // actually does not reference an output of T1.
    // ---------------------------------------------------------------------
    joinPredsThatStay.removeUnReferencedVEGPreds(
                       child(0).getGroupAttr()->getCharacteristicOutputs());

    // ---------------------------------------------------------------------
    // Also, if some inputs have been taken out deliberately, we want to
    // make sure other predicates which references the inputs taken out
    // are going to stay. Otherwise, we will have the issue that not
    // sufficient values are available at the child to ensure correctness
    // in evaluating the predicates pushed down to it. The same predicate
    // must be re-evaluated at this JOIN node.
    // ---------------------------------------------------------------------
    if (NOT inputsTakenOut.isEmpty())
    {
      ValueIdSet moreJoinPredsThatStay;
      joinPred().lookForVEGPredicates(moreJoinPredsThatStay);
      moreJoinPredsThatStay.removeUnReferencedVEGPreds(inputsTakenOut);
      joinPredsThatStay += moreJoinPredsThatStay;
    }

    // ---------------------------------------------------------------------
    // Since these predicates will be added back to the join predicates
    // after the attempt to push down to second child, make sure the second
    // child produces the required values to evaluate them.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(joinPredsThatStay,
                                   exprOnParent);

    //----------------------------------------------------------------------
    // Solution 10-030728-8252: check if the second child could produce
    // expressions of type Instnull(CAST(aggregate)).
    // See if the CAST could be pushed
    // up. The Groupby node does not manufacture expressions of the type
    // cast(aggregate) as outputs in the generator. So do not ask for them
    //----------------------------------------------------------------------
    exprOnParent.replaceInstnullCastAggregateWithAggregateInLeftJoins(this);

    // ---------------------------------------------------------------------
    // STEP 2B: Perform pushdown to the second child, and add reqd preds
    // back to the join predicates after the push down.
    // ---------------------------------------------------------------------
    RelExpr::pushdownCoveredExpr(outputExpr,
                                 availableInputs,
                                 joinPred(),
				 &exprOnParent,
                                 1);


    // ---------------------------------------------------------------------
    // Add back those predicates which must stay with the JOIN even after
    // they are pushed to the second child.
    // ---------------------------------------------------------------------
    joinPred() += VEGEqPreds2;
    joinPred() += joinPredsThatStay;
    PUSHDOWN_DEBUG_SAVE("J4");

  }
  else
  // -----------------------------------------------------------------------
  // For other types of Join's: Semi and Inner (either TSJ or non-TSJ),
  // processing is quite similar. Inner Joins has no join prediciates. For
  // Semi-Join, although we distinguish between join predicates and
  // selection predicates, both types of predicates are equally pushable.
  // The only thing is "true join VEGPreds" like VEGPred(VEG{T1.a,T2.a})
  // should be retained as join predicates in a Semi-Join but as selection
  // predicates in an Inner Join after being pushed down.
  // -----------------------------------------------------------------------
  {
    ValueIdSet predicates1 = predicatesOnParent;
    ValueIdSet predicates2 = predicatesOnParent;
    if (isSemiJoin())
    {
      // Join predicates in a Semi-Join are "as pushable as" its selection
      // predicates.
      //
      predicates1 += joinPred();
      predicates2 += joinPred();
    }
    else
    {
      // Inner Join should have no join predicates.
      CMPASSERT(joinPred().isEmpty()
		// QSTUFF
		OR getGroupAttr()->isGenericUpdateRoot()
		// QSTUFF
	  );
    }

    // ---------------------------------------------------------------------
    // STEP 1: Gather all values the children must still produce even if
    //         predicates are pushed down.
    //
    // Find all the "true join VEGPreds" in predicates. E.g, VEGPred(VEG{
    // T1.a,T2.a}) will be pushed down to Scan T1 and to Scan T2 even if
    // not both values are availble at either node. Therefore, we still
    // need to keep a copy of this type of predicates here at this Join node
    // where both T1.a and T2.a will be available. That means the children
    // need to provide these values to the Join node. The only exception is
    // when we are doing a TSJ. The predicates are then all pushed to the
    // right child, and the right child could then *not* provide the value
    // to the Join node if it's not a required output from the Join.
    // ---------------------------------------------------------------------
    ValueIdSet VEGEqPreds;
    predicates1.lookForVEGPredicates(VEGEqPreds);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds that are covered by the input values, since
    // VEGPred(VEG{T1.a,3}) needn't be retained at this Join node after
    // it's pushed down to Scan T1.
    // ---------------------------------------------------------------------
    VEGEqPreds.removeCoveredExprs(newExternalInputs);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds which are not covered at first child. For
    // example VEGPred(VEG{T2.a,T3.a}) in JOIN1 of (T1 JOIN1 (T2 JOIN2 T3))
    // is not covered at the first child. The predicate could be pushed
    // down to the second child without being retained at JOIN2.
    // ---------------------------------------------------------------------
    VEGEqPreds.removeUnCoveredExprs(
		       child(0).getGroupAttr()->getCharacteristicOutputs());

    // ---------------------------------------------------------------------
    // Remove those VEGPreds which are not covered at second child. For
    // example VEGPred(VEG{T1.a,T2.a}) in JOIN2 of ((T1 JOIN1 T2) JOIN2 T3)
    // is not covered at the second child. The predicate could be pushed
    // down to the first child without being retained at JOIN2.
    // ---------------------------------------------------------------------
    VEGEqPreds.removeUnCoveredExprs(
                       child(1).getGroupAttr()->getCharacteristicOutputs());

    // ---------------------------------------------------------------------
    // Since these predicates will be retained at the Join (or pushed down
    // to the second child in the case of a TSJ), make sure the first
    // child produces the required values to evaluate them.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(VEGEqPreds,
                                   exprOnParent);

    // ---------------------------------------------------------------------
    // First child of a TSJ should produce inputs required from the second
    // child as well.
    // ---------------------------------------------------------------------
    if (isATSJFlag)
       exprOnParent += child(1).getGroupAttr()->getCharacteristicInputs();

    // ---------------------------------------------------------------------
    // STEP 2: Try pushing down to the first child.
    // ---------------------------------------------------------------------
    RelExpr::pushdownCoveredExpr(outputExpr,
                                 newExternalInputs,
                                 predicates1,
				 &exprOnParent,
                                 0);
    PUSHDOWN_DEBUG_SAVE("J5");

    // ---------------------------------------------------------------------
    // Find subset of predicatesOnParent which have *not* been pushed down
    // to first child.
    // ---------------------------------------------------------------------
    predicatesOnParent.intersectSet(predicates1);
    PUSHDOWN_DEBUG_SAVE("J6");

    // ---------------------------------------------------------------------
    // For a Semi-Join, all selection predicates (which should not involve
    // columns from the second child) should be pushed down to the first
    // child by now. Also get rid of the join predicates which have been
    // pushed down.
    // ---------------------------------------------------------------------
    if (isSemiJoin())
    {
      joinPred().intersectSet(predicates1);
      CMPASSERT(predicatesOnParent.isEmpty()
		// QSTUFF
		OR getGroupAttr()->isGenericUpdateRoot()
		// QSTUFF
	  );
    }

    // ---------------------------------------------------------------------
    // If this is a TSJ, we do not even need to retain VEGEqPreds at the
    // Join. Everything remaining should be pushable to the right child.
    // Therefore, we don't need the right child to output values required
    // for evaluating VEGEqPreds, unless it's an required output from the
    // 
    // We do not want to push the predicate down to the right child now for
    // the RoutineJoin. That will happen later when the RoutineJoin gets 
    // transfered back to a TSJ/nested join by the optimizer impl rules.
    // 
    // The reason we don't want it pushed here is so that the analyzer does
    // not have to differentiate what imputs are required for predicates and
    // which is required for a UDF. By knowing what inputs are required for
    // the UDF, the analyzer can determine if there is a different join order
    // that might be cheaper. We will attempt to push the predicate during
    // the optimizer phase..
    // ---------------------------------------------------------------------
    if (!isRoutineJoin())
    {
       ValueIdSet availableInputs = newExternalInputs;
       if (isATSJFlag)
       {
         if (setOfValuesReqdByParent)
	   exprOnParent = *setOfValuesReqdByParent;
         else exprOnParent.clear();
         availableInputs += child(0).getGroupAttr()->getCharacteristicOutputs();
       }

       // ---------------------------------------------------------------------
       // STEP 3: Try pushing to second child now.
       // ---------------------------------------------------------------------
       RelExpr::pushdownCoveredExpr(outputExpr,
                                 availableInputs,
                                 predicates2,
				 &exprOnParent,
                                 1);
    }

    // ---------------------------------------------------------------------
    // Find subset of predicatesOnParent which have *not* been pushed down
    // to second child.
    // ---------------------------------------------------------------------
    predicatesOnParent.intersectSet(predicates2);
    PUSHDOWN_DEBUG_SAVE("J7");

    if (isSemiJoin())
    {
      // -------------------------------------------------------------------
      // set joinPred to have those predicates that were not pushed down to
      // the second child.
      // -------------------------------------------------------------------
      joinPred().intersectSet(predicates2);

      // -------------------------------------------------------------------
      // If this is a semi-join that is not a TSJ we need to add all the
      // true join VEGPreds back to joinPred().
      // -------------------------------------------------------------------
      if (NOT isATSJFlag)
        joinPred() += VEGEqPreds;
      else
        // -----------------------------------------------------------------
        // If it is a TSJ all join predicates should be pushable, no preds
        // should be remaining in joinPred().
        // -----------------------------------------------------------------
        CMPASSERT(joinPred().isEmpty()
		  // QSTUFF
		  OR getGroupAttr()->isGenericUpdateRoot()
		  // QSTUFF
		);
    }
    else
    {
      // -------------------------------------------------------------------
      // If this is a inner-join that is not a TSJ we need to add all the
      // true join VEGPreds back to selection predicates.
      // -------------------------------------------------------------------
      if (NOT isATSJFlag OR isRoutineJoin())
      {
        predicatesOnParent += VEGEqPreds;
        PUSHDOWN_DEBUG_SAVE("J9");
      }
      else
        // -----------------------------------------------------------------
        // If it is a TSJ all selection predicates should be pushable, no
        // preds should remain.
        // -----------------------------------------------------------------
        CMPASSERT(predicatesOnParent.isEmpty()
		  // QSTUFF
		  OR getGroupAttr()->isGenericUpdateRoot()
		  // QSTUFF
		);
    }
  }
} // Join::pushdownCoveredExpr

// --------------------------------------------------------------------------
// Join::pushdownCoveredExprSQO
// Rules for pushdown from Join during the SemanticQueryOptimize(SQO)
// subphase are different in two ways from the usual. 
// 1) If left child does not cover any part of a 
// VEGPred it will still be retained in the Join, so that it can be pulled
// further up the query tree as we apply this transformation at other levels
// In the usual rules, the VEGPred will be pushed down to the right child
// without being retained at the Join. This behaviour is controlled by the 
// boolean input parameter keepPredsNotCoveredByChild0. Similarly preds not 
// covered by the right child can also be retained at the Join. This is 
// controlled by keepPredsNotCoveredByChild1.
// 2) If left child is a semiJoin or a TSJ we do not push any predicates 
// down that side as those selection predicates are supposed to be empty 
// at this phase of compilation.
// ---------------------------------------------------------------------------
void Join::pushdownCoveredExprSQO(const ValueIdSet & outputExpr,
                               const ValueIdSet & newExternalInputs,
                               ValueIdSet & predicatesOnParent,
			       ValueIdSet & setOfValuesReqdByParent,
			       NABoolean keepPredsNotCoveredByChild0,
			       NABoolean keepPredsNotCoveredByChild1
		              )
{

  ValueIdSet exprOnParent1 = setOfValuesReqdByParent;
  ValueIdSet exprOnParent  = setOfValuesReqdByParent;
  ValueIdSet exprOnParent2 = setOfValuesReqdByParent;

 
  ValueIdSet predicates1 = predicatesOnParent;
  ValueIdSet predicates2 = predicatesOnParent;
  
  if (isLeftJoin())
    {
      // ---------------------------------------------------------------------
      // STEP 1: Try to push down the given predicatesOnParent to first child.
      // ---------------------------------------------------------------------

    // ---------------------------------------------------------------------
    // STEP 1A: Gather all values the left child must still produce even if
    //          predicates are pushed down.
    //
    // Selection predicates can only be pushed to the first child of an
    // outer join. Join predicates can only be pushed to the second. Make
    // sure the first child produces what we need for the join predicates.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(joinPred(),
                                   exprOnParent);


    ValueIdSet VEGEqPreds1;

    // ---------------------------------------------------------------------
    // Find all the VEGPreds in predicatesOnParent. VEGPred(VEG{T1.a,T2.a})
    // will be pushed down to Scan T1 even if T2.a is not available there.
    // Therefore, we still need to keep a copy of this type of predicates
    // here at this Join node where both T1.a and T2.a will be available.
    // ---------------------------------------------------------------------
    predicatesOnParent.lookForVEGPredicates(VEGEqPreds1);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds that are covered by the input values, since
    // VEGPred(VEG{T1.a,3}) needn't be retained at this Join node after it's
    // pushed down to Scan T1.
    // ---------------------------------------------------------------------
    VEGEqPreds1.removeCoveredExprs(newExternalInputs);

    // ---------------------------------------------------------------------
    // Remove those VEGPreds which are not covered at second child. For
    // example VEGPred(VEG{T1.a,T2.a}) in JOIN2 of ((T1 JOIN1 T2) JOIN2 T3)
    // is not covered at the second child. The predicate should be pushed
    // down to the first child without being retained at JOIN2. Note that
    // since predicatesOnParent are selection predicates evaluated after
    // a Left Join, they are in terms of the null-instantiated outputs from
    // the Join rather than direct outputs from the second child.
    // ---------------------------------------------------------------------
    if (NOT keepPredsNotCoveredByChild1)
      VEGEqPreds1.removeUnCoveredExprs(nullInstantiatedOutput());

    // ---------------------------------------------------------------------
    // Since these VEGEqPreds1 will be added back to predicatesOnParent
    // after the attempt to push down to first child, make sure the first
    // child produces the required values to evaluate them.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(VEGEqPreds1,
                                   exprOnParent);

    // ---------------------------------------------------------------------
    // STEP 1B: Perform pushdown to the first child, and add VEGEqPreds
    // back to predicatesOnParent after the push down.
    // ---------------------------------------------------------------------
    RelExpr::pushdownCoveredExpr(outputExpr,
				  newExternalInputs,
				  predicatesOnParent,
				  &exprOnParent,
				  0);


    // ---------------------------------------------------------------------
    // All selection predicates could be pushed to the first child for an
    // Anti-Semi Join should those predicates should involve columns from
    // the second child by definition.
    // ---------------------------------------------------------------------

    predicatesOnParent += VEGEqPreds1;

    // ---------------------------------------------------------------------
    // STEP 2: Try to push down the join predicates to second child.
    // ---------------------------------------------------------------------

    // ---------------------------------------------------------------------
    // STEP 2A: Gather all values the second child must still produce even
    //          if predicates are pushed down. Start with all the required
    //          values specified by the caller of this method.
    // ---------------------------------------------------------------------
    exprOnParent = outputExpr;

    // ---------------------------------------------------------------------
    // Since the remaining predicatesOnParent could not be pushed down to
    // the second child and must be evaluated on the Left Join, values reqd
    // for their evaluation must be included to make sure the second child
    // produces them. For Anti-Semi Join, predicatesOnParent is empty.
    // ---------------------------------------------------------------------
    ValueIdSet inputs = newExternalInputs;
    ValueIdSet inputsTakenOut;
    computeValuesReqdForPredicates(predicatesOnParent,
                                     exprOnParent);


    // ---------------------------------------------------------------------
    // Also, if this is NOT a TSJ, there are some join predicates which need
    // to be retained even if they are pushed down to the second child. All
    // Join predicates are pushable to the second child of a TSJ without
    // being retained at the TSJ. (See later for an exception)
    // ---------------------------------------------------------------------
    ValueIdSet VEGEqPreds2;
    ValueIdSet availableInputs = inputs;
      // -------------------------------------------------------------------
      // First, find all the VEGPreds in join predicates. This is similar to
      // what we did above with predicatesOnParent. VEGPred(VEG{T1.a,T2.a})
      // will be pushed down to Scan T2 even if T1.a is not available there.
      // Therefore, we still need to keep a copy of this type of predicates
      // here at this Join node where both T1.a and T2.a will be available.
      // -------------------------------------------------------------------
      joinPred().lookForVEGPredicates(VEGEqPreds2);

      // -------------------------------------------------------------------
      // Remove those VEGPreds that are covered by the input values, since
      // VEGPred(VEG{T2.a,3}) needn't be retained at this Join node after
      // pushed down to Scan T2. (There is an exception to this. See later.)
      // -------------------------------------------------------------------
      VEGEqPreds2.removeCoveredExprs(availableInputs); //newExternalInputs

      // -------------------------------------------------------------------
      // Remove those VEGPreds which are not covered at first child. For
      // example VEGPred(VEG{T2.a,T3.a}) in JOIN1 of (T1 JOIN1 (T2 JOIN2 T3))
      // is not covered at the first child. The predicate could be pushed
      // down to the second child without being retained at JOIN2.
      // -------------------------------------------------------------------
      if (NOT keepPredsNotCoveredByChild0)
        VEGEqPreds2.removeUnCoveredExprs(
                       child(0).getGroupAttr()->getCharacteristicOutputs());

      // -------------------------------------------------------------------
      // Since these predicates will be added back to the join predicates
      // after the attempt to push down to second child, make sure the second
      // child produces the required values to evaluate them.
      // -------------------------------------------------------------------
      computeValuesReqdForPredicates(VEGEqPreds2,
                                     exprOnParent);
    

    // ---------------------------------------------------------------------
    // Now, there are additional join predicates that must be retained
    // even if they are pushable to the second child. An example would be
    // VEGPred(VEG{T1.a,T2.a,10}). For an inner join, this predicate can
    // be pushed to Scan T1 and Scan T2 and evaluated as (T1.a=10) and
    // (T2.a=10) respectively. However, for a Left Join or Anti-Semi Join,
    // this predicate (if it's a join predicate) cannot be pushed down to
    // the first child. The (T1.a=10) part must then be retained at this
    // Join node. These types of VEGPreds are those covered by T1 and the
    // external inputs.
    // ---------------------------------------------------------------------
    ValueIdSet joinPredsThatStay;
    joinPredsThatStay = joinPred();
    ValueIdSet availableValues = availableInputs; //newExternalInputs
    availableValues += child(0).getGroupAttr()->getCharacteristicOutputs();
    joinPredsThatStay.removeUnCoveredExprs(availableValues);

    // ---------------------------------------------------------------------
    // However, we don't want VEGPred like VEGPred(VEG{T2.a,10}) which
    // actually does not reference an output of T1.
    // ---------------------------------------------------------------------
    if (NOT keepPredsNotCoveredByChild0)
      joinPredsThatStay.removeUnReferencedVEGPreds(
                       child(0).getGroupAttr()->getCharacteristicOutputs());

    // ---------------------------------------------------------------------
    // Also, if some inputs have been taken out deliberately, we want to
    // make sure other predicates which references the inputs taken out
    // are going to stay. Otherwise, we will have the issue that not
    // sufficient values are available at the child to ensure correctness
    // in evaluating the predicates pushed down to it. The same predicate
    // must be re-evaluated at this JOIN node.
    // ---------------------------------------------------------------------
    if (NOT inputsTakenOut.isEmpty())
    {
      ValueIdSet moreJoinPredsThatStay;
      joinPred().lookForVEGPredicates(moreJoinPredsThatStay);
      moreJoinPredsThatStay.removeUnReferencedVEGPreds(inputsTakenOut);
      joinPredsThatStay += moreJoinPredsThatStay;
    }

    // ---------------------------------------------------------------------
    // Since these predicates will be added back to the join predicates
    // after the attempt to push down to second child, make sure the second
    // child produces the required values to evaluate them.
    // ---------------------------------------------------------------------
    computeValuesReqdForPredicates(joinPredsThatStay,
                                   exprOnParent);

    //----------------------------------------------------------------------
    // Solution 10-030728-8252: check if the second child could produce
    // expressions of type Instnull(CAST(aggregate)).
    // See if the CAST could be pushed
    // up. The Groupby node does not manufacture expressions of the type
    // cast(aggregate) as outputs in the generator. So do not ask for them
    //----------------------------------------------------------------------
    exprOnParent.replaceInstnullCastAggregateWithAggregateInLeftJoins(this);

    // ---------------------------------------------------------------------
    // STEP 2B: Perform pushdown to the second child, and add reqd preds
    // back to the join predicates after the push down.
    // ---------------------------------------------------------------------
     RelExpr::pushdownCoveredExpr(outputExpr,
				  availableInputs,
				  joinPred(),
				  &exprOnParent,
				  1);
    // ---------------------------------------------------------------------
    // Add back those predicates which must stay with the JOIN even after
    // they are pushed to the second child.
    // ---------------------------------------------------------------------
    joinPred() += VEGEqPreds2;
    joinPred() += joinPredsThatStay;

  } 
  else 
  {
  // STEP 1: Gather all values the children must still produce even if
  //         predicates are pushed down.
  //
  // Find all the "true join VEGPreds" in predicates. E.g, VEGPred(VEG{
  // T1.a,T2.a}) will be pushed down to Scan T1 and to Scan T2 even if
  // not both values are availble at either node. Therefore, we still
  // need to keep a copy of this type of predicates here at this Join node
  // where both T1.a and T2.a will be available. That means the children
  // need to provide these values to the Join node. The only exception is
  // when we are doing a TSJ. The predicates are then all pushed to the
  // right child, and the right child could then *not* provide the value
  // to the Join node if it's not a required output from the Join.
  // ---------------------------------------------------------------------
  ValueIdSet VEGEqPreds;
  predicates1.lookForVEGPredicates(VEGEqPreds);

  // ---------------------------------------------------------------------
  // Remove those VEGPreds that are covered by the input values, since
  // VEGPred(VEG{T1.a,3}) needn't be retained at this Join node after
  // it's pushed down to Scan T1.
  // ---------------------------------------------------------------------
  VEGEqPreds.removeCoveredExprs(newExternalInputs);

  // ---------------------------------------------------------------------
  // Remove those VEGPreds which are not covered at first child. For
  // example VEGPred(VEG{T2.a,T3.a}) in JOIN1 of (T1 JOIN1 (T2 JOIN2 T3))
  // is not covered at the first child. The predicate could be pushed
  // down to the second child without being retained at JOIN2.
  // ---------------------------------------------------------------------
  if (NOT keepPredsNotCoveredByChild0)
    VEGEqPreds.removeUnCoveredExprs(
		       child(0).getGroupAttr()->getCharacteristicOutputs());

  // ---------------------------------------------------------------------
  // Remove those VEGPreds which are not covered at second child. For
  // example VEGPred(VEG{T1.a,T2.a}) in JOIN2 of ((T1 JOIN1 T2) JOIN2 T3)
  // is not covered at the second child. The predicate could be pushed
  // down to the first child without being retained at JOIN2.
  // ---------------------------------------------------------------------
  if (NOT keepPredsNotCoveredByChild1)
    VEGEqPreds.removeUnCoveredExprs(
                     child(1).getGroupAttr()->getCharacteristicOutputs());

  // ---------------------------------------------------------------------
  // Since these predicates will be retained at the Join (or pushed down
  // to the second child in the case of a TSJ), make sure the first
  // child produces the required values to evaluate them.
  // ---------------------------------------------------------------------

  computeValuesReqdForPredicates(VEGEqPreds,
                                 exprOnParent);

  // ---------------------------------------------------------------------
  // STEP 2: Try pushing down to the first child.
  // ---------------------------------------------------------------------

   
    if (child(0).getPtr()->getOperator().match(REL_ANY_SEMIJOIN) ||
	child(0).getPtr()->getOperator().match(REL_ANY_TSJ))
    {
      computeValuesReqdForPredicates(predicates1,
				     exprOnParent1);
      ValueIdSet emptySet;
      RelExpr::pushdownCoveredExpr(outputExpr,
				   newExternalInputs,
				   emptySet,
				   &exprOnParent1,
				   0);
    }
    else
    {
      RelExpr::pushdownCoveredExpr(outputExpr,
				   newExternalInputs,
				   predicates1,
				   &exprOnParent,
				   0);
    }


  // ---------------------------------------------------------------------
  // Find subset of predicatesOnParent which have *not* been pushed down
  // to first child.
  // ---------------------------------------------------------------------
  predicatesOnParent.intersectSet(predicates1);


  // ---------------------------------------------------------------------
  // STEP 3: Try pushing to second child now.
  // ---------------------------------------------------------------------

    if (child(1).getPtr()->getOperator().match(REL_ANY_SEMIJOIN) ||
        (child(1).getPtr()->getOperator().match(REL_ANY_TSJ) &&
        (child(1).getPtr()->getOperator() != REL_ROUTINE_JOIN)))
    {
      computeValuesReqdForPredicates(predicates2,
				     exprOnParent2);
      ValueIdSet emptySet;
      RelExpr::pushdownCoveredExpr(outputExpr,
				   newExternalInputs,
				   emptySet,
				   &exprOnParent2,
				   1);
    }
    else
    {
      // We do not want to push predicates to the right child of a 
      // routineJoin.
      if (!isRoutineJoin())
      {
        RelExpr::pushdownCoveredExpr(outputExpr,
                                     newExternalInputs,
                                     predicates2,
                                     &exprOnParent,
                                     1);
      }
    }

  // ---------------------------------------------------------------------
  // Find subset of predicatesOnParent which have *not* been pushed down
  // to second child.
  // ---------------------------------------------------------------------
  predicatesOnParent.intersectSet(predicates2);

  
  // -------------------------------------------------------------------
  // If this is a inner-join that is not a TSJ we need to add all the
  // true join VEGPreds back to selection predicates.
  // -------------------------------------------------------------------
  
  predicatesOnParent += VEGEqPreds;
  }

} // Join::pushdownCoveredExprSQO


void Join::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  switch (getOperatorType())
    {
    case REL_JOIN:
    case REL_MERGE_JOIN:
    case REL_NESTED_JOIN:
    case REL_HASH_JOIN:
    case REL_HYBRID_HASH_JOIN:
    case REL_ORDERED_HASH_JOIN:
    case REL_INDEX_JOIN:
    case REL_ROUTINE_JOIN:
    case REL_TSJ:
      {
	// Potentially, all the values that are produced by
	// my left child as well as my right child.
	outputValues += child(0).getGroupAttr()->getCharacteristicOutputs();
	outputValues += child(1).getGroupAttr()->getCharacteristicOutputs();
	break;
      }
    case REL_LEFT_JOIN:
    case REL_LEFT_NESTED_JOIN:
    case REL_LEFT_MERGE_JOIN:
    case REL_LEFT_ORDERED_HASH_JOIN:
    case REL_LEFT_HYBRID_HASH_JOIN:
    case REL_LEFT_TSJ:
      {
	// Potentially, all the values that are produced by
	// my left child and all null instantiated values from
	// my right child.
	outputValues += child(0).getGroupAttr()->getCharacteristicOutputs();
	outputValues.insertList(nullInstantiatedOutput());
 	break;
      }
    case REL_FULL_JOIN:
    case REL_UNION_JOIN:
    case REL_FULL_HYBRID_HASH_JOIN:
      {
	// Potentially, all the values that are produced by
	// my left child and the right child. Since it's a FULL_OUTER_JOIN
	// all null instantiated values from my right and left child.

	outputValues.insertList(nullInstantiatedOutput());
	outputValues.insertList(nullInstantiatedForRightJoinOutput());
	break;
      }
    case REL_SEMIJOIN:
    case REL_ANTI_SEMIJOIN:
    case REL_SEMITSJ:
    case REL_ANTI_SEMITSJ:
    case REL_HASH_SEMIJOIN:
    case REL_HASH_ANTI_SEMIJOIN:
    case REL_MERGE_SEMIJOIN:
    case REL_MERGE_ANTI_SEMIJOIN:
    case REL_HYBRID_HASH_SEMIJOIN:
    case REL_HYBRID_HASH_ANTI_SEMIJOIN:
    case REL_ORDERED_HASH_SEMIJOIN:
    case REL_ORDERED_HASH_ANTI_SEMIJOIN:
    case REL_NESTED_SEMIJOIN:
    case REL_NESTED_ANTI_SEMIJOIN:
      {
	// No value from my right child can appear in my output.
	outputValues += child(0).getGroupAttr()->getCharacteristicOutputs();
	break;
      }
    case REL_TSJ_FLOW:
    case REL_NESTED_JOIN_FLOW:
      {
	// No value from my left child can appear in my output.
	outputValues += child(1).getGroupAttr()->getCharacteristicOutputs();
	break;
      }
    default:
      {
	ABORT("Unsupported join type in Join::getPotentialOutputValues()");
	break;
      }
    } // switch
} // Join::getPotentialOutputValues()

CostScalar Join::computeMinEstRCForGroup()
{
  CostScalar minCard =  csOne;

  GroupAttributes * ga = getGroupAttr();

  RelExpr * logExpr = ga->getLogExprForSynthesis();
  if (logExpr != NULL)
  {
    logExpr->finishSynthEstLogProp();
    minCard = ga->getMinChildEstRowCount();
  }

  return minCard;
}

// get the highest reduction from local predicates for cols of this join
CostScalar
Join::highestReductionForCols(ValueIdSet colSet) 
{
  // if the child is anything other than scan, then we assume the reduction to be 1
  // but before that we still need to see if the column set that we are looking for 
  // belongs to this child or not.
  // since we don't know to which child tableOne belongs, we shall look at both left 
  // and right histograms for the columns. Start with the left child
  ColStatDescList completeList = child(0).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->colStats();
  ColStatDescList rightColStatList = child(1).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->colStats();

  // form a complete list of histograms from both sides
  completeList.makeDeepCopy(rightColStatList);
  // Compute reduction for this column set
  CostScalar highestUecRedByLocalPreds = highestUecRedByLocalPreds = completeList.getHighestUecReductionByLocalPreds(colSet);
  return highestUecRedByLocalPreds;
}

const NAString Join::getText() const
{
  NAString result;

  switch (getOperatorType())
    {
    case REL_JOIN:
      result += "join";
      break;
    case REL_LEFT_JOIN:
      result += "left_join";
      break;
    case REL_RIGHT_JOIN:
      result += "right_join";
      break;
    case REL_FULL_JOIN:
      result += "full_join";
      break;
    case REL_UNION_JOIN:
      result += "union_join";
      break;
    case REL_ROUTINE_JOIN:
      result += "routine_join";
      break;
    case REL_TSJ:
      result += "tsj";
      break;
    case REL_TSJ_FLOW:
      result += "tsj_flow";
      break;
    case REL_LEFT_TSJ:
      result += "left_tsj";
      break;
    case REL_SEMIJOIN:
      result += "semi_join";
      break;
    case REL_ANTI_SEMIJOIN:
      result += "anti_semi_join";
      break;
    case REL_SEMITSJ:
      result += "semi_tsj";
      break;
    case REL_ANTI_SEMITSJ:
      result += "anti_semi_tsj";
      break;
    case REL_INDEX_JOIN:
      result += "index_join";
      break;

    default:
      result += "UNKNOWN??";
      break;
    } // switch

    if(CmpCommon::getDefault(COMP_BOOL_183) == DF_ON)
    {
      Int32 potential = getPotential();
      if(potential < 0)
      {
        result += "_-"+ istring(-1*potential);
      }
      else
        result += "_" + istring(potential);
    }

    return result;
} // Join::getText()

HashValue Join::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= joinPred_;

  return result;
}

NABoolean Join::duplicateMatch(const RelExpr & other) const
{
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  Join &o = (Join &) other;

  if (joinPred_ != o.joinPred_)
    return FALSE;

  // Temp member to seperate joins PTRule from others in cascades memo
  if (joinFromPTRule_ != o.joinFromPTRule_)
    return FALSE;

  if (joinForZigZag_ != o.joinForZigZag_)
    return FALSE;

  if (avoidHalloweenR2_ != o.avoidHalloweenR2_)
    return FALSE;

  if (halloweenForceSort_ != o.halloweenForceSort_)
    return FALSE;

  return TRUE;
}

 
RelExpr * Intersect::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    {
      result = new (outHeap) Intersect(NULL,
                                        NULL
                                        );
    }
  else
    result = derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

RelExpr * Except::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    {
      result = new (outHeap) Except(NULL,
                                        NULL
                                        );
    }
  else
    result = derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

RelExpr * Join::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Join *result;

  if (derivedNode == NULL)
    result = new (outHeap) Join(NULL,
			        NULL,
			        getOperatorType(),
				NULL,
				FALSE,
				FALSE,
				outHeap);
  else
    result = (Join *) derivedNode;

  // copy join predicate parse tree (parser only)
  if (joinPredTree_ != NULL)
    result->joinPredTree_ = joinPredTree_->copyTree(outHeap)->castToItemExpr();

  result->joinPred_ = joinPred_;

  // Copy the uniqueness flags
  result->leftHasUniqueMatches_ = leftHasUniqueMatches_;
  result->rightHasUniqueMatches_ = rightHasUniqueMatches_;

  // Copy the equijoin predicates
  result->equiJoinPredicates_ = equiJoinPredicates_;
  result->equiJoinExpressions_ = equiJoinExpressions_;

  result->nullInstantiatedOutput() =  nullInstantiatedOutput();

  result->nullInstantiatedForRightJoinOutput() =
    nullInstantiatedForRightJoinOutput();

  result->transformComplete_ =  transformComplete_;

  // Copy the required order, if any, that originated from an insert node
  result->reqdOrder_ = reqdOrder_;

  // copy flag that marks a mandatory TSJ which could not be unnested
  result->tsjAfterSQO_ = tsjAfterSQO_;
  
  // Copy the flag that indicates if this is a TSJ for a write operation
  result->tsjForWrite_ = tsjForWrite_;
  result->tsjForUndo_ = tsjForUndo_;
  result->tsjForSetNFError_ = tsjForSetNFError_;
  result->tsjForMerge_ = tsjForMerge_;
  result->tsjForMergeWithInsert_ = tsjForMergeWithInsert_;
  result->tsjForMergeUpsert_ = tsjForMergeUpsert_;
  result->tsjForSideTreeInsert_ = tsjForSideTreeInsert_;
  result->enableTransformToSTI_ = enableTransformToSTI_;

  result->forcePhysicalJoinType_ = forcePhysicalJoinType_;
  result->derivedFromRoutineJoin_ = derivedFromRoutineJoin_;

  // Temp member to seperate joins PTRule from others in cascades memo
  result->joinFromPTRule_ = joinFromPTRule_;

  result->joinForZigZag_ = joinForZigZag_;

  result->sourceType_ = sourceType_;


  result->rowsetRowCountArraySize_ = rowsetRowCountArraySize_;

  result->avoidHalloweenR2_ = avoidHalloweenR2_;

  result->halloweenForceSort_ = halloweenForceSort_;

  result->candidateForSubqueryUnnest_ = candidateForSubqueryUnnest_;

  result->candidateForSubqueryLeftJoinConversion_ = candidateForSubqueryLeftJoinConversion_;

  result->candidateForSemiJoinTransform_ = candidateForSemiJoinTransform_;

  result->predicatesToBeRemoved_ = predicatesToBeRemoved_;

//++MV
  result->rightChildMapForLeftJoin_ = rightChildMapForLeftJoin_;
//--MV

  result->isIndexJoin_ = isIndexJoin_;

  if(!result->isInnerNonSemiJoin())
    result->floatingJoin_ = floatingJoin_;

  result->allowPushDown_ = allowPushDown_;

  result->extraHubNonEssentialOutputs_ = extraHubNonEssentialOutputs_;

  result->isForTrafLoadPrep_ = isForTrafLoadPrep_;

  result->beforeJoinPredOnOuterOnly_ = beforeJoinPredOnOuterOnly_;

  return RelExpr::copyTopNode(result, outHeap);
}

void Join::addJoinPredTree(ItemExpr *joinPred)
{
  ExprValueId j = joinPredTree_;

  ItemExprTreeAsList(&j, ITM_AND).insert(joinPred);
  joinPredTree_ = j.getPtr();
}

ItemExpr * Join::removeJoinPredTree()
{
  ItemExpr * result = joinPredTree_;

  joinPredTree_ = NULL;

  return result;
}

void Join::addLocalExpr(LIST(ExprNode *) &xlist,
			LIST(NAString) &llist) const
{
  if (joinPredTree_ != NULL OR
      NOT joinPred_.isEmpty())
    {
      if (joinPred_.isEmpty())
	xlist.insert(joinPredTree_);
      else
	xlist.insert(joinPred_.rebuildExprTree());
      llist.insert("other_join_predicates");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

void Join::convertToTsj()
{
  switch (getOperatorType())
    {
    case REL_JOIN:
      setOperatorType(REL_TSJ);
      break;
    case REL_LEFT_JOIN:
      setOperatorType(REL_LEFT_TSJ);
      break;
    case REL_SEMIJOIN:
      setOperatorType(REL_SEMITSJ);
      break;
    case REL_ANTI_SEMIJOIN:
      setOperatorType(REL_ANTI_SEMITSJ);
      break;
    default:
      ABORT("Internal error: Join::convertTsj()");
      break;
    }
} // Join::convertToTsj()

void Join::convertToNotTsj()
{
  switch (getOperatorType())
    {
    case REL_TSJ:
    case REL_TSJ_FLOW:
    case REL_ROUTINE_JOIN:
      setOperatorType(REL_JOIN);
      break;
    case REL_LEFT_TSJ:
      setOperatorType(REL_LEFT_JOIN);
      break;
    case REL_SEMITSJ:
      setOperatorType(REL_SEMIJOIN);
      break;
    case REL_ANTI_SEMITSJ:
      setOperatorType(REL_ANTI_SEMIJOIN);
      break;
    default:
      ABORT("Internal error: Join::convertTsj()");
      break;
    }
} // Join::convertToNotTsj()

void Join::convertToNotOuterJoin()
{
  switch (getOperatorType())
    {
    case REL_LEFT_JOIN:
      setOperatorType(REL_JOIN);
      break;
    case REL_LEFT_TSJ:
      setOperatorType(REL_TSJ);
      break;
    default:
      ABORT("Internal error: Join::convertOuterJoin()");
      break;
    } // end switch
} // Join::convertToNotOuterJoin()

// ----------------------------------------------------------------------------
// This procedure gets called when synthesising logical properties.
// It finds all the equijoin predicates and saves them in equiJoinPredicates_
// But leaves them in the originating selectionPred()/joinPred()
// ---------------------------------------------------------------------------
void Join::findEquiJoinPredicates()
{
  ValueIdSet  allJoinPredicates;
  ValueId     leftExprId, rightExprId;
  NABoolean   predicateIsOrderPreserving;
  ItemExpr*   expr;

  equiJoinPredicates_.clear();
  equiJoinExpressions_.clear();

  // If this is a TSJ there is nothing to analyze. All join predicates
  // have been pushed down to the second child.
  if(isTSJ())
    return;

  if (isInnerNonSemiJoin())
    {
      allJoinPredicates = selectionPred();
      CMPASSERT(joinPred().isEmpty());
    }
  else
    {
      // for an outer or semi join, the ON clause is stored in "joinPred"
      // while the WHERE clause is stored in "selectionPred".
      allJoinPredicates = joinPred();
    }

  // remove any predicates covered by the inputs
  allJoinPredicates.removeCoveredExprs(getGroupAttr()->
				          getCharacteristicInputs());

  for (ValueId exprId = allJoinPredicates.init();
       allJoinPredicates.next(exprId);
       allJoinPredicates.advance(exprId))
    {
      expr = exprId.getItemExpr();

      if (expr->isAnEquiJoinPredicate(child(0).getGroupAttr(),
				      child(1).getGroupAttr(),
                                      getGroupAttr(),
				      leftExprId, rightExprId,
				      predicateIsOrderPreserving))
	{
	  equiJoinPredicates_ += exprId;
	  equiJoinExpressions_.addMapEntry(leftExprId, rightExprId);
	}
    }

} // Join::findEquiJoinPredicates()

// ---------------------------------------------------------------------------
// separateEquiAndNonEquiJoinPredicates is called from the Join
// implementation rules to weed out of equiJoinPredicates_ all
// the predicates that can be used by the join. The equiJoin
// predicates for the physical operator will be remove from
// the selectioPred() or joinPred() where they came from.
// ---------------------------------------------------------------------------

void Join::separateEquiAndNonEquiJoinPredicates
               (const NABoolean joinStrategyIsOrderSensitive)
{
  ValueId     leftExprId, rightExprId;
  NABoolean   predicateIsOrderPreserving;
  ItemExpr*   expr;

  // equiJoinPredicates_ has all the equijoin predicates found
  // when synthesing logical properties. It is a subset of
  // either selectionPred() or joinPred()

  ValueIdSet  foundEquiJoinPredicates = equiJoinPredicates_;
  equiJoinPredicates_.clear();
  equiJoinExpressions_.clear();

  // remove any predicates covered by the inputs
  foundEquiJoinPredicates.removeCoveredExprs(getGroupAttr()->
				          getCharacteristicInputs());

  for (ValueId exprId = foundEquiJoinPredicates.init();
       foundEquiJoinPredicates.next(exprId);
       foundEquiJoinPredicates.advance(exprId))
    {
      expr = exprId.getItemExpr();

      if (expr->isAnEquiJoinPredicate(child(0).getGroupAttr(),
				      child(1).getGroupAttr(),
                                      getGroupAttr(),
				      leftExprId, rightExprId,
				      predicateIsOrderPreserving))
	{
	  if ( (NOT joinStrategyIsOrderSensitive) OR
               (joinStrategyIsOrderSensitive AND predicateIsOrderPreserving) )
	    {
	      equiJoinPredicates_ += exprId;
	      equiJoinExpressions_.addMapEntry(leftExprId, rightExprId);
	    }
	}
     else
        {
          CMPASSERT(0); // We knew it was an equijoin predicate already
        }
    }

  if (isInnerNonSemiJoin())
    {
      selectionPred() -= equiJoinPredicates_;
      CMPASSERT(joinPred().isEmpty());
    }
  else
    {
      // for an outer or semi join, the ON clause is stored in "joinPred"
      // while the WHERE clause is stored in "selectionPred".
      joinPred() -= equiJoinPredicates_;
    }

  // Since we have changed the set of equijoin predicates we will consider
  // we should resyhtnesize the left/rightHasUnqiueMatches_ flags
  synthConstraints(NULL);

} // Join::separateEquiAndNonEquiJoinPredicates()

void Join::flipChildren()
{
  NABoolean flipUnique;
  flipUnique             = leftHasUniqueMatches_;
  leftHasUniqueMatches_  = rightHasUniqueMatches_;
  rightHasUniqueMatches_ = flipUnique;

  equiJoinExpressions_.flipSides();

} // Join::flipChildren()

// ---------------------------------------------------------------------------
// get the parallel join type and return additional info (optional)
//
// 0: serial join
// 1: TYPE1 join (matching partitions on both sides, including SkewBuster)
// 2: TYPE2 join (join one partition on one side with the
//                entire table on the other side)
// ---------------------------------------------------------------------------

Int32 Join::getParallelJoinType(ParallelJoinTypeDetail *optionalDetail) const
{
  Int32 result = 0;
  ParallelJoinTypeDetail detailedType = Join::PAR_NONE;
  const PartitioningFunction *mpf = NULL;
  const PartitioningFunction *cpf = NULL;

  if (getPhysicalProperty())
    mpf = getPhysicalProperty()->getPartitioningFunction();

  if (mpf == NULL OR mpf->getCountOfPartitions() <= 1)
    {
      // no parallelism or unknown parallelism, not a parallel join
      if (optionalDetail)
        *optionalDetail = detailedType;

      return 0;
    }

  if (child(1)->getPhysicalProperty())
    cpf = child(1)->getPhysicalProperty()->getPartitioningFunction();

  CMPASSERT( cpf );
  if (cpf->castToLogPhysPartitioningFunction())
  {
    // only the child of a join in DP2 can have a logphys part func
    DCMPASSERT(getPhysicalProperty()->executeInDP2());

    cpf = cpf->castToLogPhysPartitioningFunction()->
      getPhysPartitioningFunction();
  }

  if (cpf->isAReplicateViaBroadcastPartitioningFunction() OR
      cpf->isAReplicateNoBroadcastPartitioningFunction())
  {
    // Right child replicates, now check my own partitioning
    // function to see whether this node just passes on the
    // replication function.

    if (mpf->castToLogPhysPartitioningFunction())
    {
      // only a join in DP2 can have a logphys part func
      DCMPASSERT(getPhysicalProperty()->executeInDP2());

      // check the physical part. func of the join in DP2
      mpf = mpf->castToLogPhysPartitioningFunction()->
        getPhysPartitioningFunction();
    }


    if (NOT mpf->isAReplicateViaBroadcastPartitioningFunction() AND
        NOT mpf->isAReplicateNoBroadcastPartitioningFunction())
    {
      // See if the right child REALLY replicates data. If this is
      // a nested join and the chosen plan was a "preferred probing
      // order" plan, then this is really a type 1 join, because a
      // ppo plan always demands the two tables be logically
      // partitioned the same way.
      if (isNestedJoin() && ((NestedJoin*)this)->probesInOrder())
        {
          result = 1;
          detailedType = PAR_OCR;
        }
      else
        {
          // right child replicates data, and the node itself doesn't,
          // this is a type 2 join
          result = 2;
          if (isNestedJoin())
            detailedType = PAR_N2J;
        }
    }
    else
    {
      // Both the right child and the parent replicate data.
      // This is not a parallel join, it is a join that simply
      // passes its replication requirement down to both of its
      // children. The join will be executed in multiple ESPs,
      // but it will not employ one of the two parallel algorithms
      // (TYPE1 or TYPE2).
      result = 0;
    }
  }
  else
  {
    // right child is partitioned, but does not replicate, parallel type 1 join or SkewBuster or OCB
    PartitioningFunction *opf = NULL;

    if (child(0)->getPhysicalProperty())
      opf = child(0)->getPhysicalProperty()->getPartitioningFunction();

    if (opf->isAReplicateViaBroadcastPartitioningFunction())
      {
        // this is an OCB join, which is considered type2
        result = 2;
        detailedType = PAR_OCB;
      }
    else
      {
        // the regular TYPE1 join (including SkewBuster)
        result = 1;

        if (opf->isASkewedDataPartitioningFunction())
          detailedType = PAR_SB;

      }
  }

  if (optionalDetail)
    *optionalDetail = detailedType;

  return result;
}

// ---------------------------------------------------------------------
// Method to split the order req between the two join children.
// return FALSE if not possible
// ---------------------------------------------------------------------
NABoolean Join::splitOrderReq(
                  const ValueIdList& myOrderReq, /*IN*/
                        ValueIdList& orderReqOfChild0, /*OUT*/
                        ValueIdList& orderReqOfChild1  /*OUT*/) const
{
  NABoolean partOfChild0List = TRUE;
  ValueId exprId;

  GroupAttributes* child0GA = child(0).getGroupAttr();
  GroupAttributes* child1GA = child(1).getGroupAttr();

  orderReqOfChild0.clear();
  orderReqOfChild1.clear();

  for (CollIndex ix = 0; ix < myOrderReq.entries(); ix++)
  {
    exprId = myOrderReq.at(ix);

    // dummy variables for the cover test
    ValueIdSet newInputs,referencedInputs,
               coveredSubExpr,uncoveredExpr;
    NABoolean coveredByChild0 =
      child0GA->covers(exprId,
                       newInputs,
                       referencedInputs,
                       &coveredSubExpr,
                       &uncoveredExpr);

    if (NOT coveredByChild0)
      partOfChild0List = FALSE;

    if (partOfChild0List)
      orderReqOfChild0.insertAt(orderReqOfChild0.entries(),exprId);
    else // i.e. NOT partOfChild0List
    {
      //++MV
      // For left join we need to translate the required sort key to
      // the right child outputs because there is an InstantiateNull function
      // on all of the right child outputs. The InstantiateNull function will
      // cause the cover test to fail and therefore the optimization that merge
      // the left child sort key with the right child sort key will fail
      // For more information see NestedJoin::synthPhysicalProperty()
      if (isLeftJoin())
      {
	const ValueIdMap &map = rightChildMapForLeftJoin();
	ValueId tempExprId = exprId;
	map.mapValueIdDown(tempExprId, exprId);
      }
      //--MV

      coveredSubExpr.clear();
      uncoveredExpr.clear();
      NABoolean coveredByChild1 =
      child1GA->covers(exprId,
                       newInputs,
                       referencedInputs,
                       &coveredSubExpr,
                       &uncoveredExpr);

      if (coveredByChild1)
      {
        orderReqOfChild1.insertAt(orderReqOfChild1.entries(),exprId);
      }
      else // i.e NOT (partOfChild0List || coveredByChild1)
      {
        orderReqOfChild0.clear();
        orderReqOfChild1.clear();
        return FALSE;
      }
    }
  } // end for all expressions in the required order

  // Check to see if it is possible to split the order
  if (child0GA->isUnique(orderReqOfChild0) OR
      (child0GA->getMaxNumOfRows() <= 1) OR
      (orderReqOfChild1.entries() == 0))
  {
    return TRUE;
  }
  else
  {
    orderReqOfChild0.clear();
    orderReqOfChild1.clear();
    return FALSE;
  }
} // end splitOrderReq()

// ---------------------------------------------------------------------
// method to split the arrangement req between the two join childs.
// return FALSE if not possible
// ---------------------------------------------------------------------
NABoolean Join::splitArrangementReq(
                  const ValueIdSet& myArrangReq, /*IN*/
                        ValueIdSet& ArrangReqOfChild0, /*OUT*/
                        ValueIdSet& ArrangReqOfChild1  /*OUT*/) const
{
  ArrangReqOfChild0.clear();
  ArrangReqOfChild1.clear();

  ValueId exprId;

  GroupAttributes* child0GA = child(0).getGroupAttr();
  GroupAttributes* child1GA = child(1).getGroupAttr();

  for (exprId = myArrangReq.init();
       myArrangReq.next(exprId);
       myArrangReq.advance(exprId))
  {
    // dummy variables for the cover test
    ValueIdSet newInputs,referencedInputs,
               coveredSubExpr,uncoveredExpr;

    // First we see if this element is covered by child 0
    if (child0GA->covers(exprId,
                         newInputs,
                         referencedInputs,
                         &coveredSubExpr,
                         &uncoveredExpr))
    {
      ArrangReqOfChild0.insert(exprId);
    }
    // Only if an element is not covered by Child0 then we check
    // Child1. i.e. if it is covered by both we bill it to Child0.
    else
    {
      coveredSubExpr.clear();
      uncoveredExpr.clear();
      if (child1GA->covers(exprId,
                           newInputs,
                           referencedInputs,
                           &coveredSubExpr,
                           &uncoveredExpr))
      {
        ArrangReqOfChild1.insert(exprId);
      }
      else
      {
        // If the expression was not covered soley by one of the children, then
        // we must give up. For example, "T1.a * T2.a" needs both children.
        ArrangReqOfChild0.clear();
        ArrangReqOfChild1.clear();
        return FALSE;
      }
    } // end if not covered by child0
  } // end for all expressions in the required arrangement

  // Check to see if it is possible to split the arrangement
  if (child0GA->isUnique(ArrangReqOfChild0) OR
      (child0GA->getMaxNumOfRows() <= 1) OR
      (ArrangReqOfChild1.entries() == 0))
  {
    return TRUE;
  }
  else
  {
    ArrangReqOfChild0.clear();
    ArrangReqOfChild1.clear();
    return FALSE;
  }
} // end splitArrangementReq()

NABoolean Join::ownsVEGRegions() const 
{
  return isLeftJoin() OR isAntiSemiJoin() OR isFullOuterJoin();
}

PlanPriority NestedJoin::computeOperatorPriority
    (const Context* context,
     PlanWorkSpace *pws,
     Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  NABoolean applySerialPremium = TRUE;

  double val;
  Cardinality minrows, maxrows;

  CostScalar expectedrows = 
    child(0).getGroupAttr()->getResultCardinalityForEmptyInput();

  if (child(0).getGroupAttr()->hasCardConstraint(minrows, maxrows) && 
     (maxrows <= ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_99)
       OR CostScalar(maxrows) < CostScalar(1.2) * expectedrows))
    {
      // a nested join with at most N outer rows is NOT risky
      val = 1.0;

      // In this case premium for serial plan can be waived because cost of
      // starting ESPs over weighs any benefit we get from parallel plan.
      // Fix is controlled by COMP_BOOL_75, default value is ON.
      if (CmpCommon::getDefault(COMP_BOOL_75) == DF_ON)
        applySerialPremium = FALSE;
    }
  else if (context->getInputLogProp() && 
           context->getInputLogProp()->getResultCardinality().value() > 1)
    {
      // temporary workaround until we cost HJ under NJ correctly
      val = 1.0;
    }
  else
    {
      // a nested join with more than N outer rows is considered risky
      val = CURRSTMT_OPTDEFAULTS->riskPremiumNJ();
      // nested join cache should have a lower risk premium
      GroupAttributes &rightGA = *child(1).getGroupAttr();
      NABoolean probeIsUnique = rightGA.isUnique(rightGA.getCharacteristicInputs());
      NABoolean isTypeOfSemiJoin = isSemiJoin() || isAntiSemiJoin();

      if ((probeIsUnique  || isTypeOfSemiJoin)              &&
          (rowsFromRightHaveUniqueMatch() == FALSE)         &&
          (getOperatorType() != REL_NESTED_JOIN_FLOW)       &&
          (isTSJForWrite() == FALSE )                       &&  
          (getGroupAttr()->
           isEmbeddedUpdateOrDelete() == FALSE )       &&
          (!spp->executeInDP2())                       &&
          (CmpCommon::getDefault(NESTED_JOIN_CACHE) != DF_OFF))
        {
          double red=ActiveSchemaDB()->getDefaults().getAsDouble(COMP_INT_89);
          if (red > 1) 
            {
              // reduce risk premium because it's a nested join cache operator
              val = 1 + (val - 1) / red;
            }
        }
    }

  if (degreeOfParallelism <= 1 && applySerialPremium)
    {
    // serial plans are risky. exact an insurance premium from serial plans.
    val *= CURRSTMT_OPTDEFAULTS->riskPremiumSerial();
  }
  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  // esp parallelism priority logic below does not apply to operators in dp2
  if(spp->executeInDP2())
    return result;

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR (maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

    // fix for SAP case 10-100602-2913, soln 10-100602-0803
  // long-running select for DSO activation, query plan for empty table

  // if nested join has
  // 1) a tuple list (something with 0 base tables) on left, and
  // 2) a table on right, and
  // 3) prefer_key_nested_join is set, and
  // 4) table's predicate (including pushed join pred) forms begin/end
  //    key on table, and
  // 5) tuple list is of reasonable size (<= tuplelist_size_threshold), 
  //    and
  // 6) table is small or has no stats
  // then give nested join plan higher priority
  //   push it by 1 if it has a key range predicate
  //   push it by 2 if it has a unique key predicate

  // is prefer_key_nested_join active?
  NABoolean prefer_key_nested_join = 
    (CmpCommon::getDefault(SAP_PREFER_KEY_NESTED_JOIN) == DF_ON);
  if (prefer_key_nested_join) {
    GroupAttributes *grpAttr0 = child(0).getGroupAttr();
    GroupAttributes *grpAttr1 = child(1).getGroupAttr();
    GroupAnalysis *grpA0 = grpAttr0->getGroupAnalysis();
    GroupAnalysis *grpA1 = grpAttr1->getGroupAnalysis();

    // is left child guaranteed small?
    NABoolean leftIsSmall = FALSE;
    Cardinality minLeft, maxLeft;
    if (grpAttr0->hasCardConstraint(minLeft,maxLeft) AND
        maxLeft <= ActiveSchemaDB()->getDefaults().getAsLong
        (SAP_TUPLELIST_SIZE_THRESHOLD)) {
      leftIsSmall = TRUE;
    }

    // is right a single table?
    FileScan *rScan = NULL;
    NABoolean rightIsTable = pws->getScanLeaf(1, planNumber, rScan);

    // is right table small?
    NABoolean isSmallTable = 
      grpAttr1->getResultCardinalityForEmptyInput() <= 
      ActiveSchemaDB()->getDefaults().getAsLong
      (SAP_KEY_NJ_TABLE_SIZE_THRESHOLD);

    // prefer this nested_join iff all above conditions are met
    if (leftIsSmall && rightIsTable && isSmallTable && rScan) {

      // is predicate on unique key or prefix key?
      NABoolean hasUniqKeyPred = FALSE;
      NABoolean hasPrefixKeyPred = FALSE;
      const SearchKey *sKey = rScan->getSearchKey();
      if (sKey) {
        hasUniqKeyPred = sKey->isUnique();
        // TBD: check if key prefix selects few or many rows
        hasPrefixKeyPred = sKey->getKeyPredicates().entries() > 0;
      }
      // TBD: take care of MDAM case

      // push priority by 2 if it has a unique key predicate
      if (hasUniqKeyPred)
        result.incrementLevels(2,0);

      // push priority by 1 if it has a prefix key predicate
      else if (hasPrefixKeyPred)
        result.incrementLevels(1,0);
    }
  }

  return result;
}

// -----------------------------------------------------------------------
// member functions for class NestedJoinFlow
// -----------------------------------------------------------------------
RelExpr * NestedJoinFlow::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  NestedJoinFlow *result;

  if (derivedNode == NULL)
    {
      result = new (outHeap) NestedJoinFlow(NULL,
					    NULL,
					    NULL,
					    NULL,
					    outHeap);
    }
  else
    result = (NestedJoinFlow*)derivedNode;

  result->sendEODtoTgt_ = sendEODtoTgt_;

  return NestedJoin::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class NestedJoin
// -----------------------------------------------------------------------

NABoolean NestedJoin::isLogical()  const {return FALSE;}

NABoolean NestedJoin::isPhysical() const {return TRUE;}

const NAString NestedJoin::getText() const
{
  switch (getOperatorType())
    {
    case REL_NESTED_JOIN:
      return "nested_join";
    case REL_LEFT_NESTED_JOIN:
      return "left_nested_join";
    case REL_NESTED_SEMIJOIN:
      return "nested_semi_join";
    case REL_NESTED_ANTI_SEMIJOIN:
      return "nested_anti_semi_join";
    case REL_NESTED_JOIN_FLOW:
      return "tuple_flow";
    default:
      return "UNKNOWN??";
    } // switch
} // NestedJoin::getText()

RelExpr * NestedJoin::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    {
      result = new (outHeap) NestedJoin(NULL,
				        NULL,
				        getOperatorType(),
					outHeap);
    }
  else
    result = derivedNode;

  return Join::copyTopNode(result, outHeap);
}

NABoolean NestedJoin::allPartitionsProbed()
{
  return TRUE;//all partitions probed
}

// Conditions to check before applying the nested join probing cache:
//   1. The right child has a cardinality constraint of at most one row,
//      or else the join is a semi-join or anti-semi-join.
//   2. The right child's characteristic inputs are not unique for every
//      request (with exceptions, see below).
//   3. The nested join is not a NestedJoinFlow.
//   4. The right child does not contain any IUD operations.
//   5. The nested join's GroupAttributes do not include embedded IUD.
//   6. The execution location is not in DP2.
//   7. The nested join cache feature is not suppressed by a default.
//   8. The right child does not contain non-deterministic UDRs
NABoolean NestedJoin::isProbeCacheApplicable(PlanExecutionEnum loc) const
{
  NABoolean result = FALSE;
  GroupAttributes &rightGA = *child(1).getGroupAttr();
  NABoolean probeIsUnique = rightGA.isUnique(rightGA.getCharacteristicInputs());

  if ( !probeIsUnique ) {
     // dig deep into the right child to see if the searchKey associated with the
     // only Scan node is unique. If it is unique, we also declare the probe is
     // unique (i.e., for each probe, there is at most one row returned). The
     // probe uniqueness property check is for the current implementation in executor
     // where only one entry per probe in the hash table in probe cache is allocated.

     RelExpr *childExpr = child(1);

     // skip over Exchange nodes
     while (childExpr && (childExpr->getOperator() == REL_EXCHANGE))
        childExpr = childExpr->child(0);

     if (childExpr)
        {
           OperatorTypeEnum x = childExpr->getOperator();
           if (x == REL_HBASE_ACCESS || x == REL_HBASE_COPROC_AGGR)
           {
             HbaseAccess *hbscan = (HbaseAccess*)childExpr;
             const SearchKey *skey = hbscan->getSearchKey();
             if (skey && skey->isUnique())
               probeIsUnique = TRUE;
           }
        }
  }

  NABoolean isTypeOfSemiJoin = isSemiJoin() || isAntiSemiJoin();

  if
    ((probeIsUnique  || isTypeOfSemiJoin)                  &&
     (getOperatorType() != REL_NESTED_JOIN_FLOW)           &&
     (isTSJForWrite() == FALSE )                           &&
     (getGroupAttr()->
        isEmbeddedUpdateOrDelete() == FALSE )              &&
     loc != EXECUTE_IN_DP2                                 &&
     (CmpCommon::getDefault(NESTED_JOIN_CACHE) != DF_OFF)  &&
     (rightGA.getHasNonDeterministicUDRs() == FALSE))
    {
      if (! rowsFromRightHaveUniqueMatch())
        {
          // big if passed and we have a chance of duplicate probes from the left
          result = TRUE;
        }
      else
        {
          // If left probes are unique, there isn't a reason for a probe
          // cache. However, we might be able to pull up some predicate from
          // the right into the ProbeCache, which might give us non-unique
          // probes. The code below targets a specific case (ALM 4783):
          //
          //   NestedJoin
          //   /       \
          //          Aggregate (one equi-join pred is a HAVING pred)
          //
          // We can't detect this in the optimizer (where the nested join
          // may point to a filter or a MEMO group), but that's fine, since
          // we don't really want to give this unusual case a cost advantage.
          RelExpr *childExpr = child(1);
          
          // skip over Exchange and MapValueIds nodes
          while (childExpr &&
                 (childExpr->getOperator() == REL_EXCHANGE ||
                  childExpr->getOperator() == REL_MAP_VALUEIDS))
            childExpr = childExpr->child(0);
          
          if (childExpr && 
              childExpr->getOperator().match(REL_ANY_GROUP) &&
              CmpCommon::getDefault(NESTED_JOIN_CACHE_PREDS) != DF_OFF)
            {
              GroupByAgg *childGB = (GroupByAgg *) childExpr;

              if (childGB->groupExpr().isEmpty() &&
                  ! childGB->selectionPred().isEmpty())
                // This is a scalar aggregate with a HAVING predicate,
                // at least we know that there is a reasonable chance that
                // we can pull up a HAVING predicate into the probe cache
                // in method GroupByAgg::tryToPullUpPredicatesInPreCodeGen()
                result = TRUE;
            }
        }
    }
  return result;
}

// -----------------------------------------------------------------------
// member functions for class MergeJoin
// -----------------------------------------------------------------------

NABoolean MergeJoin::isLogical()  const {return FALSE;}

NABoolean MergeJoin::isPhysical() const {return TRUE;}

const NAString MergeJoin::getText() const
{
  switch (getOperatorType())
    {
    case REL_MERGE_JOIN:
      return "merge_join";
    case REL_LEFT_MERGE_JOIN:
      return "left_merge_join";
    case REL_MERGE_SEMIJOIN:
      return "merge_semi_join";
    case REL_MERGE_ANTI_SEMIJOIN:
      return "merge_anti_semi_join";
    default:
      return "UNKNOWN merge join??";
    } // switch
} // MergeJoin::getText()

RelExpr * MergeJoin::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) MergeJoin(NULL,
				     NULL,
				     getOperatorType(),
				     NULL,
				     outHeap);
  else
    result = derivedNode;

  return Join::copyTopNode(result, outHeap);
}

void MergeJoin::addLocalExpr(LIST(ExprNode *) &xlist,
			     LIST(NAString) &llist) const
{
  xlist.insert(orderedMJPreds_.rebuildExprTree());
  llist.insert("merge_join_predicate");

  Join::addLocalExpr(xlist,llist);
}

PlanPriority MergeJoin::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  double val = CURRSTMT_OPTDEFAULTS->riskPremiumMJ();
  if (degreeOfParallelism <= 1)
    {
      // serial plans are risky. exact an insurance premium from serial plans.
      val *= CURRSTMT_OPTDEFAULTS->riskPremiumSerial();
    }
  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR (maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

  return result;
}

// -----------------------------------------------------------------------
// member functions for class HashJoin
// -----------------------------------------------------------------------

NABoolean HashJoin::isLogical()  const { return FALSE; }

NABoolean HashJoin::isPhysical() const { return TRUE; }

const NAString HashJoin::getText() const
{
  switch (getOperatorType())
    {
    case REL_HASH_JOIN:
      return "hash_join";
    case REL_LEFT_HASH_JOIN:
      return "left_hash_join";
    case REL_HASH_SEMIJOIN:
      return "semi_join";
    case REL_HASH_ANTI_SEMIJOIN:
      return "hash_anti_semi_join";

    case REL_HYBRID_HASH_JOIN:
     {
        if(((HashJoin *)this)->isOrderedCrossProduct())
          return "ordered_cross_product";
        else
          return "hybrid_hash_join";
      }
    case REL_LEFT_HYBRID_HASH_JOIN:
      return "left_hybrid_hash_join";
    case REL_FULL_HYBRID_HASH_JOIN:
      return "full_hybrid_hash_join";
    case REL_HYBRID_HASH_SEMIJOIN:
      return "hybrid_hash_semi_join";
    case REL_HYBRID_HASH_ANTI_SEMIJOIN:
      return "hybrid_hash_anti_semi_join";

    case REL_ORDERED_HASH_JOIN:
      {
	if (getEquiJoinPredicates().isEmpty())
          return "ordered_cross_product";
        else
	  return "ordered_hash_join";
      }
    case REL_LEFT_ORDERED_HASH_JOIN:
      return "left_ordered_hash_join";
    case REL_ORDERED_HASH_SEMIJOIN:
      return "ordered_hash_semi_join";
    case REL_ORDERED_HASH_ANTI_SEMIJOIN:
      return "ordered_hash_anti_semi_join";
    default:
      return "UNKNOWN hash join??";
    } // switch
} // HashJoin::getText()




RelExpr * HashJoin::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL) {
    result = new (outHeap) HashJoin(NULL,
				    NULL,
				    getOperatorType(),
				    NULL,
				    outHeap);
    ((HashJoin *)result)->setIsOrderedCrossProduct(isOrderedCrossProduct());
    ((HashJoin *)result)->setReuse(isReuse());
    ((HashJoin *)result)->setNoOverflow(isNoOverflow());
  }
  else
    result = derivedNode;

  ((HashJoin*)result)->isNotInSubqTransform_ = isNotInSubqTransform_;
  ((HashJoin*)result)->requireOneBroadcast_  = requireOneBroadcast_;

  ((HashJoin*)result)->innerAccessOnePartition_ = innerAccessOnePartition_;

  return Join::copyTopNode(result, outHeap);
}

PlanPriority HashJoin::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  double val = 1;
  if (degreeOfParallelism <= 1 && getInnerAccessOnePartition() == FALSE ) 
    {
      // serial plans are risky. exact an insurance premium from serial plans.
      // The exception is when only one partition is accessed.
      val = CURRSTMT_OPTDEFAULTS->riskPremiumSerial();
    }
  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  if (QueryAnalysis::Instance() AND
      QueryAnalysis::Instance()->optimizeForFirstNRows())
    result.incrementLevels(HASH_JOIN_FIRST_N_PRIORITY,0);

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR (maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

  return result;
}

void HashJoin::addLocalExpr(LIST(ExprNode *) &xlist,
			    LIST(NAString) &llist) const
{
  if (NOT getEquiJoinPredicates().isEmpty())
  {
  xlist.insert(getEquiJoinPredicates().rebuildExprTree());
    llist.insert("hash_join_predicates");
  }

  if (NOT valuesGivenToChild_.isEmpty())
  {
    xlist.insert(valuesGivenToChild_.rebuildExprTree(ITM_ITEM_LIST));
    llist.insert("reuse_comparison_values");
  }

  if (NOT checkInnerNullExpr_.isEmpty())
  {
    xlist.insert(checkInnerNullExpr_.rebuildExprTree());
    llist.insert("check_inner_null_expr");
  }
  
  if (NOT checkOuterNullExpr_.isEmpty())
  {
    xlist.insert(checkOuterNullExpr_.rebuildExprTree());
    llist.insert("check_outer_null_expr");
  }

  Join::addLocalExpr(xlist,llist);
}
void HashJoin::resolveSingleColNotInPredicate()
{

  if (!isAntiSemiJoin() ||
      !isHashJoin())
  {
    return;
  }

  ValueIdSet jPred = joinPred();
  short notinCount=0;
  
  for (	ValueId valId = jPred.init(); 
	jPred.next(valId); 
	jPred.advance(valId))
  {
    ItemExpr * itmExpr = valId.getItemExpr();
    
    if (itmExpr->getOperatorType() == ITM_NOT_IN)
    {
      if (((NotIn*)itmExpr)->getEquivEquiPredicate() == NULL_VALUE_ID)
      {
        ((NotIn*)itmExpr)->cacheEquivEquiPredicate();

      }

      // use cached value ids
      equiJoinPredicates() += ((NotIn*)itmExpr)->getEquivEquiPredicate();
        
      joinPred() -=valId;
      joinPred() += ((NotIn*)itmExpr)->getEquivEquiPredicate();

      notinCount++;

      setIsNotInSubqTransform(TRUE); 
      setRequireOneBroadcast(((NotIn*)itmExpr)->getIsOneInnerBroadcastRequired());
    }
  }


  DCMPASSERT(notinCount <=1);
}//void HashJoin::resolveSingleColNotInPredicate()


void Join::resolveSingleColNotInPredicate()
{
  // applies only to anti_semi_joins
  if (!isAntiSemiJoin()) {
    return; 
  }

  short notinCount = 0;

  ValueIdSet jPred = joinPred();

  for (	ValueId valId = jPred.init(); 
	jPred.next(valId); 
	jPred.advance(valId))
  {
    ItemExpr * itmExpr = valId.getItemExpr();

    if (itmExpr->getOperatorType() == ITM_NOT_IN)
    {
      if (((NotIn*)itmExpr)->getEquivNonEquiPredicate() == NULL_VALUE_ID )
      {
        ((NotIn*)itmExpr)->cacheEquivNonEquiPredicate();
      }
      
      //use cached valueids
      joinPred() -= valId;
      joinPred() += ((NotIn*)itmExpr)->getEquivNonEquiPredicate();

      notinCount++;
    }
  }


  DCMPASSERT(notinCount <=1);

}//void Join::resolveSingleColNotInPredicate()


// Join::rewriteNotInPredicate() 
// is method is called right after the predicates are pushed down and
// the goal is to make sure that only the NotIn predicate is present.
// in any other preduicate exist besides the NotIn Predicate than we can not
// optimize the hash anti semi join when the outer column is nullable and may 
// have null values
void Join::rewriteNotInPredicate()
{
  // applies only to anti_semi_joins
  if (!isAntiSemiJoin()) 
  {
    return; 
  }

  ValueIdSet jPred = joinPred();

  ItemExpr * notinExpr=NULL;

  NABoolean otherPredicatesExist = FALSE;
  for (	ValueId valId = jPred.init(); 
	jPred.next(valId); 
	jPred.advance(valId))
  {
    ItemExpr * itmExpr = valId.getItemExpr();
    if (itmExpr->getOperatorType() != ITM_NOT_IN)
    {
      otherPredicatesExist = TRUE;
    }
    else
    {
      // assert if we already encoutered a not in
      DCMPASSERT(notinExpr == NULL);
      notinExpr = itmExpr;
    }
  }

  if (notinExpr)
  {
    //single column
    DCMPASSERT (notinExpr->child(0)->getOperatorType() != ITM_ITEM_LIST);
    const NAType &outerType = notinExpr->child(0)->getValueId().getType();

    GroupAttributes * leftChildGrpAttr = child(0).getGroupAttr();
    GroupAttributes * rightChildGrpAttr = child(1).getGroupAttr();
    const ValueIdSet &inputs = getGroupAttr()->getCharacteristicInputs();
    ValueIdSet refs;

    ValueId valId = notinExpr->getValueId();

    if ((outerType.supportsSQLnull() && 
        !((NotIn*)notinExpr)->getOuterNullFilteringDetected() &&
        otherPredicatesExist) ||
        //fix for solution Id:10-100331-9194 
        //select count(*) from h2_data_1k_37 where col_lar2 <> all (select col_id from
        //h2_data_1k_37 where col_id=100) ;
        //NotIn(VEGRef(col_lar2),VEGRef(col_id=100)) is in the join prdicate and in t he characteristc
        //outputs of the child. changing it to euipredicate may lead to wrong results
        //the below code will change the NotIn(a,b) predicate to NOT(a<>B is true) when the predicate is covered 
        //by one of of children
        leftChildGrpAttr->covers(valId, inputs, refs) || 
        rightChildGrpAttr->covers(valId, inputs, refs))
    {
      ValueId tmpId = ((NotIn *)notinExpr)->createEquivNonEquiPredicate();
      ItemExpr * tmpItemExpr = tmpId.getItemExpr();
      valId.replaceItemExpr(tmpItemExpr);
    }
  }
  
}//Join::rewriteNotInPredicate()

// Join::rewriteNotInPredicate( ValueIdSet & origVidSet, ValueIdSet & newVidSet)
// if both the outer and the inner columns are not nullable or are nullable but
// have no NULL values then the NotIn Predicate is changed to an equi-predicate.
// otherwise the NotIn predicate is not changed and the optimizer will decide what
// to do with it
// this method is called right after the pull up of the predicates in join::transformNode()
// in the case of anti semi join the inner predicates are pulled and added to join pred
// and the outer predicates are pulled and added to selectionPredicates
// When we look for outer NUll filetering predicates we look in the selection predocates 
// and when we look inner NULL filtering predicates we look in the join predicates
void Join::rewriteNotInPredicate( ValueIdSet & origVidSet, ValueIdSet & newVidSet)
{
  // applies only to anti_semi_joins
  if (!isAntiSemiJoin()) 
  {
    return; 
  }

  ValueIdSet jPred = joinPred();
  ValueIdSet selPred = selectionPred();

  short notinCount = 0;

  for (	ValueId valId = joinPred().init(); 
	joinPred().next(valId); 
	joinPred().advance(valId))
  {
    ItemExpr * itmExpr = valId.getItemExpr();
 
    if (itmExpr->getOperatorType() == ITM_NOT_IN)
    {
      //single column
      if (itmExpr->child(0)->getOperatorType() != ITM_ITEM_LIST)
      {
        const NAType &innerType = itmExpr->child(1)->getValueId().getType();
        const NAType &outerType = itmExpr->child(0)->getValueId().getType();

        selPred -= valId;
        jPred -= valId;

        NABoolean child0IsNotNullable = selPred.isNotNullable(itmExpr->child(0)) ;
        NABoolean child1IsNotNullable = jPred.isNotNullable(itmExpr->child(1)) ;
        
        if ((!innerType.supportsSQLnull() || child1IsNotNullable) &&
              (!outerType.supportsSQLnull() || child0IsNotNullable) )
        {
          origVidSet += valId;
          // we can change the not in predicate to an equi-predicate in this case
          newVidSet  += ((NotIn *)itmExpr)->createEquivEquiPredicate();
        }
        else
        { 
          // outer refrences case are not handled by optimization
          ValueIdSet rightSideofPred;
          ValueIdSet tempSet;

          rightSideofPred.insert(itmExpr->child(1)->getValueId());
          
          rightSideofPred.getReferencedPredicates(child(0)->getGroupAttr()->getCharacteristicOutputs(), tempSet) ;

          if (!tempSet.isEmpty())
          {
             origVidSet += valId;
             // we can change the not in predicate to an equi-predicate in this case
             newVidSet  += ((NotIn *)itmExpr)->createEquivNonEquiPredicate();
          }
          else
          {
            if (CmpCommon::getDefault(NOT_IN_OUTER_OPTIMIZATION) == DF_OFF)
            {
              //NOT_IN_OUTER_OPTIMIZATION == OFF ==> if outer is nullable and may have NULL values
              //			       change to Non equi-predicate here
              if ( outerType.supportsSQLnull() &&  
                 !child0IsNotNullable) 
              {
                origVidSet += valId;
                // we can change the not in predicate to an equi-predicate in this case
                newVidSet  += ((NotIn *)itmExpr)->createEquivNonEquiPredicate();
              }
            }
            else
            {
              // case where outer or inner columns (or both) is nullable and may have NULL values
              // optimizer will decide depending on the type of join
              // hash join      ==> equi-predicate with cancel expression when inner is nullbale
              //		    ==> filter to filter out NULL values coming from outer side
              //		    ==> when inner is not empty
              //		    ==> NUILL values coming from outer side are not filtered out when 
              //		    ==> inner is empty
              // non hash join  ==> Non equi-predicate
              if (child0IsNotNullable)
              {
                ((NotIn*)itmExpr)->setOuterNullFilteringDetected(TRUE);
              }
              if (child1IsNotNullable)
              {
                ((NotIn*)itmExpr)->setInnerNullFilteringDetected(TRUE);
              }
            }
          }
        }
      }
      else
      {
        ValueIdSet predSet ;
        //ValueIdSet jPreds;
        //ValueIdSet selPred;

        //jPreds = joinPred();
        //selPred = selectionPred();

        predSet = NotIn::rewriteMultiColNotInPredicate( valId, 
                                                        joinPred(),
                                                        selectionPred());
        
        DCMPASSERT(predSet.entries() >0);
        
        origVidSet += valId;
        
        newVidSet += predSet;

      }
      
      notinCount++;

    }//if (itmExpr->getOperatorType() == ITM_NOT_IN)
 
  }
  DCMPASSERT(notinCount <=1);

}//void Join::rewriteNotInPredicate()

// -----------------------------------------------------------------------
// member functions for class Intersect
// -----------------------------------------------------------------------

Intersect::Intersect(RelExpr *leftChild,
	     RelExpr *rightChild)
: RelExpr(REL_INTERSECT, leftChild, rightChild)
{ }

Intersect::~Intersect() {}

Int32 Intersect::getArity() const { return 2; }

const NAString Intersect::getText() const
{
  return "intersect";
}

// -----------------------------------------------------------------------
// // member functions for class Except
// // -----------------------------------------------------------------------
Except::Except(RelExpr *leftChild,
             RelExpr *rightChild)
: RelExpr(REL_EXCEPT, leftChild, rightChild)
{ }

Except::~Except() {}

Int32 Except::getArity() const { return 2; }

const NAString Except::getText() const
{
  return "except";
}

// -----------------------------------------------------------------------
// member functions for class Union
// -----------------------------------------------------------------------
Union::Union(RelExpr *leftChild,
	     RelExpr *rightChild,
	     UnionMap *unionMap,
	     ItemExpr *condExpr,
	     OperatorTypeEnum otype,
         CollHeap *oHeap,
         NABoolean sysGenerated,
         NABoolean mayBeCacheable
         )
: RelExpr(otype, leftChild, rightChild, oHeap),
  condExprTree_(condExpr)
  ,trigExceptExprTree_(NULL)
  ,previousIF_(NULL)
  ,flags_(0)
  ,leftList_(NULL)
  ,rightList_(NULL)
  ,currentChild_(-1)
  ,alternateRightChildOrderExprTree_(NULL) //++MV
  ,isSystemGenerated_(sysGenerated)
  ,isSerialUnion_(FALSE)
  ,variablesSet_(oHeap)
  
{
  if ( NOT mayBeCacheable )
    setNonCacheable();

  if (unionMap != NULL)
    {
      unionMap_ = unionMap;
      unionMap_->count_++;
    }
   else
      unionMap_ = new (oHeap) UnionMap;

   condExpr_.clear();
   trigExceptExpr_.clear();
   alternateRightChildOrderExpr_.clear(); //++MV
   variablesSet_.clear();
   controlFlags_ = 0; //++ Triggers -
}

Union::~Union() { if (unionMap_->count_ == 0) delete unionMap_;}

Int32 Union::getArity() const { return 2; }

void Union::rewriteUnionExpr(const ValueIdSet &unionExpr,
                             ValueIdSet &leftExpr,
                             ValueIdSet &rightExpr) const
{
  // walk the original selection predicates and rewrite them in terms
  // of the mapped value ids of the union's inputs
  for (ValueId x = unionExpr.init(); unionExpr.next(x); unionExpr.advance(x))
    {
      ValueId newLeftExpr =
	x.getItemExpr()->mapAndRewrite(getLeftMap(),TRUE);
      ValueId newRightExpr =
	x.getItemExpr()->mapAndRewrite(getRightMap(),TRUE);

      leftExpr += newLeftExpr;
      rightExpr += newRightExpr;
    }
} // Union::rewriteExprs()

void Union::pushdownCoveredExpr(const ValueIdSet & outputExpr,
                                const ValueIdSet & newExternalInputs,
                                ValueIdSet & predicatesOnParent,
				const ValueIdSet * setOfValuesReqdByParent,
			        Lng32 // childIndex ignored
		               )
{
  ValueIdSet resultSet = outputExpr;
  if (setOfValuesReqdByParent)
    resultSet += *setOfValuesReqdByParent;
  resultSet += getGroupAttr()->getCharacteristicInputs();

  // alternateRightChildOrderExpr expressions should not be pushed down
  resultSet.insertList(alternateRightChildOrderExpr()); // ++MV
  // ---------------------------------------------------------------------
  // Not all the output columns from the union may be needed.
  // Map the required input list to the corresponding left
  // and right required outputs list
  // ---------------------------------------------------------------------
  ValueIdSet valuesRequiredFromLeft, valuesRequiredFromRight;
  rewriteUnionExpr(resultSet,
               valuesRequiredFromLeft,
               valuesRequiredFromRight);

  // ---------------------------------------------------------------------
  // Rewrite selectionPred()
  // ---------------------------------------------------------------------
  ValueIdSet leftPred, rightPred, emptySet;
  rewriteUnionExpr(predicatesOnParent, leftPred, rightPred);

  // push the left predicates to the left subtree
  // empty set for the first argument indicates that there are no
  // non-essential outputs, (in other words, outputs that are
  // simply passed through)
  RelExpr::pushdownCoveredExpr(emptySet,
                               newExternalInputs,
                               leftPred,
			       &valuesRequiredFromLeft,
                               0
                               );

  // push the right predicates to the right subtree
  RelExpr::pushdownCoveredExpr(emptySet,
                               newExternalInputs,
                               rightPred,
			       &valuesRequiredFromRight,
                               1
                               );

  // Verify that all the predicates were pushed
  leftPred -= child(0)->selectionPred();
  CMPASSERT( leftPred.isEmpty() );

  rightPred -= child(1)->selectionPred();
  CMPASSERT( rightPred.isEmpty() );

  // All the predicates have been pushed down to the children.
  predicatesOnParent.clear();

} // Union::pushdownCoveredExpr

void Union::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // The output of the union is defined by the ValueIdUnion
  // expressions that are maintained in the colMapTable_.
  //
  Lng32 ne = unionMap_->colMapTable_.entries();
  for (Lng32 index = 0; index < ne; index++)
    {
      // Accumulate the ValueIds of the result of the union
      // in the set provided by the caller.
      outputValues += ((ValueIdUnion *) (unionMap_->colMapTable_[index].getItemExpr()))->getResult();
    }
} // Union::getPotentialOutputValues()


HashValue Union::topHash()
{
  HashValue result = RelExpr::topHash();

  // result ^= colMapTable_;

  return result;
}

NABoolean Union::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  Union &o = (Union &) other;

  if (NOT ((unionMap_ == o.unionMap_) AND
	   (condExpr_ == o.condExpr_) AND
	   (trigExceptExpr_ == o.trigExceptExpr_) AND
	   (alternateRightChildOrderExpr_ == o.alternateRightChildOrderExpr_))) //++MV
    return FALSE;

  return TRUE;
}

RelExpr * Union::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Union *result;

  if (derivedNode == NULL)
    result = new (outHeap) Union(NULL,
				 NULL,
				 unionMap_,
				 NULL,
				 getOperatorType(),
                 outHeap);
  else
    result = (Union *) derivedNode;

  if (condExprTree_ != NULL)
    result->condExprTree_ = condExprTree_->copyTree(outHeap)->castToItemExpr();

  if (trigExceptExprTree_ != NULL)
    result->trigExceptExprTree_ = trigExceptExprTree_->copyTree(outHeap)->castToItemExpr();

  //++MV -
  if (alternateRightChildOrderExprTree_ != NULL)
    result->alternateRightChildOrderExprTree_ =
      alternateRightChildOrderExprTree_->copyTree(outHeap)->castToItemExpr();
  //--MV -

  result->condExpr_ = condExpr_;
  result->trigExceptExpr_ = trigExceptExpr_;
  result->alternateRightChildOrderExpr_ = alternateRightChildOrderExpr_;
  result->setUnionFlags(getUnionFlags());
  //++Triggers -
  result->controlFlags_ = controlFlags_;

  result->isSystemGenerated_ = isSystemGenerated_;

  if (getSerialUnion())
  {
    result->setSerialUnion();
  }
  return RelExpr::copyTopNode(result, outHeap);
}

void Union::addValueIdUnion(ValueId vidUnion, CollHeap* heap)
{
  ValueIdUnion *xvid = (ValueIdUnion *) vidUnion.getItemExpr();

  CMPASSERT(vidUnion.getItemExpr()->getOperatorType() == ITM_VALUEIDUNION);

  // This method is only called by the binder when it is first
  // building the unionMap
  if(unionMap_->count_ > 1)
    {
    unionMap_->count_--;
    unionMap_ = new (heap) UnionMap;
    }

  CMPASSERT(unionMap_->count_ == 1);


  // add the value id to the list of value ids for ValueIdUnion expressions
  // and also add entries to the two maps that describe the same information
  unionMap_->colMapTable_.insert(vidUnion);
  unionMap_->leftColMap_.addMapEntry(vidUnion,xvid->getLeftSource());
  unionMap_->rightColMap_.addMapEntry(vidUnion,xvid->getRightSource());
}

//++ Triggers -
void Union::setNoOutputs()
{
  CMPASSERT(flags_ == UNION_BLOCKED || flags_ == UNION_ORDERED);

  controlFlags_ |= NO_OUTPUTS;
}

void Union::addLocalExpr(LIST(ExprNode *) &xlist,
			 LIST(NAString) &llist) const
{
  if (condExprTree_ != NULL)
  {
    xlist.insert(condExprTree_);
    llist.insert("condExprTree");
  }

  if (NOT condExpr_.isEmpty())
  {
    xlist.insert(condExpr_.rebuildExprTree());
    llist.insert("condExpr");
  }

  if (trigExceptExprTree_ != NULL)
  {
    xlist.insert(trigExceptExprTree_);
    llist.insert("trigExceptExprTree");
  }

  if (NOT trigExceptExpr_.isEmpty())
  {
    xlist.insert(trigExceptExpr_.rebuildExprTree());
    llist.insert("trigExceptExpr");
  }

  if (alternateRightChildOrderExprTree_ != NULL)
  {
    xlist.insert(alternateRightChildOrderExprTree_);
    llist.insert("alternateRightChildOrderExprTree");
  }

  if (NOT alternateRightChildOrderExpr_.isEmpty())
  {
    xlist.insert(alternateRightChildOrderExpr_.rebuildExprTree());
    llist.insert("alternateRightChildOrderExpr");
  }

  RelExpr::addLocalExpr(xlist,llist);
}

const NAString Union::getText() const
{
  NAString text;
  switch (getUnionFlags())
  {
	case UNION_ORDERED    : text += "ordered_union"; break;
	case UNION_BLOCKED    : text += "blocked_union"; break;
	case UNION_COND_UNARY : text += "unary_union"; break;
	default               : text += "merge_union"; break;
  }

  if (getOperatorType() == REL_MERGE_UNION)
	  text += " (phys.)";

  return text;
}

ItemExpr *Union::getCondExprTree()
{
  return condExprTree_;
}

void Union::addCondExprTree(ItemExpr *condExpr)
{
  ExprValueId t = condExprTree_;

  ItemExprTreeAsList(&t, ITM_ITEM_LIST).insert(condExpr);
  condExprTree_ = t.getPtr();
}

ItemExpr *Union::removeCondExprTree()
{
  ItemExpr *result = condExprTree_;
  condExprTree_ = NULL;

  return result;
}

ItemExpr *Union::getTrigExceptExprTree()
{
  return trigExceptExprTree_;
}

void Union::addTrigExceptExprTree(ItemExpr *trigExceptExpr)
{
  ExprValueId t = trigExceptExprTree_;

  ItemExprTreeAsList(&t, ITM_ITEM_LIST).insert(trigExceptExpr);
  trigExceptExprTree_ = t.getPtr();
}

ItemExpr *Union::removeTrigExceptExprTree()
{
  ItemExpr *result = trigExceptExprTree_;
  trigExceptExprTree_ = NULL;

  return result;
}

// If this Union node is an IF node of a compound statement, this function
// returns either the left or right list of value ids associated with the node.
// It returns the left one if we are currently visiting the left child.
// Otherwise we return the right one.
AssignmentStHostVars *Union::getCurrentList(BindWA *bindWA)
{
  if (currentChild_ == 0) {
    if (!leftList_) {
      leftList_ = new (bindWA->wHeap()) AssignmentStHostVars(bindWA);
    }
    return leftList_;
  }
  else {
   if (!rightList_) {
     rightList_ = new (bindWA->wHeap()) AssignmentStHostVars(bindWA);
   }
   return rightList_;
  }
}


// When we are in a CompoundStatement and we have IF statements in it,
// we must create a RETDesc for this Union node (which is
// actually an IF node). In this function, we create a list of
// ValueIdUnion nodes. We figure out which valueids
// of the left child must be matched with those of the right child
// (for instance, if SET :a appears in both children) and which must
// be matched with previously existing valueids (for instance, if
// SET :a = ... only appears in one branch, then the ValueIdUnion associated
// with that SET statement must reference the value id of :a that existed before
// this IF statement).
RETDesc * Union::createReturnTable(AssignmentStArea *assignArea, BindWA *bindWA)
{
  AssignmentStHostVars * leftList =  leftList_;
  AssignmentStHostVars * rightList = rightList_;
  NABoolean foundAMatch = FALSE;
  AssignmentStHostVars *globalList = assignArea->getAssignmentStHostVars();
  NAString const *nameOfLeftVar;

  RETDesc *resultTable = new (bindWA->wHeap()) RETDesc(bindWA);
  ColRefName *refName =  new (bindWA->wHeap()) ColRefName();

  AssignmentStHostVars *listOfPreviousIF = NULL;

  // We find the list of variables of the previous IF node. We will
  // need to update it since some of its variables may get new value ids
  // within the IF statement

  if (previousIF_) {

    short currentChild = previousIF_->currentChild();

    if (currentChild == 0) {
      listOfPreviousIF = previousIF_->leftList();
    }
    else {
      listOfPreviousIF = previousIF_->rightList();
    }
  }

  // Scan the left list and look for matches in the right List
  while (leftList && (leftList->var())) {

    foundAMatch = FALSE;

    nameOfLeftVar = &(leftList->var()->getName());

    rightList = rightList_;

    while (rightList && rightList->var()) {

       NAString const *nameOfRightVar = &(rightList->var()->getName());

       if (*nameOfLeftVar == *nameOfRightVar) {
	 foundAMatch = TRUE;
	 break;
       }

       rightList = rightList->next();
    }

    AssignmentStHostVars *ptrLeftVar = globalList->findVar(*nameOfLeftVar);
    CMPASSERT(ptrLeftVar);

    // If we found a match, we create a ValueIdUnion node of the paired match; otherwise
    // we pair the current value id of the variable in question with the value id it
    // had before the IF statement. If the variable does not have a value id, we bind it.
    ValueId value ;
    if (foundAMatch) {
      value = rightList->currentValueId();
    }
    else {
      ValueIdList list = ptrLeftVar->valueIds();
      if (list.entries() > 0) {
        value = ptrLeftVar->currentValueId();
      }
      else {
	// Get a value id for this variable.
	ItemExpr *expr = ptrLeftVar->var()->bindNode(bindWA);
	if (bindWA->errStatus()) {
          return NULL;
	}

	value = expr->getValueId();
      }
    }

    ValueIdUnion *vidUnion = new (bindWA->wHeap())
        ValueIdUnion(leftList->currentValueId(),
                     value,
	             NULL_VALUE_ID);
    vidUnion->bindNode(bindWA);
    if (bindWA->errStatus()) {
      delete vidUnion;
      return NULL;
    }

    ValueId valId = vidUnion->getValueId();
    addValueIdUnion(valId,bindWA->wHeap());

    resultTable->addColumn(bindWA, *refName, valId);

    // The variable inside the IF gets the value id of the ValueIdUnion just
    // generated.
    ptrLeftVar->setCurrentValueId(valId);

    // Also update the variable list in the previous IF node
    if (listOfPreviousIF) {
      listOfPreviousIF->addToListInIF(leftList->var(), valId);
    }

    leftList = leftList->next();
  } // while

  // We now search the right list and do a similar processing for the variables on
  // the right side that are not on the left
  rightList = rightList_;

  while (rightList && (rightList->var())) {

   foundAMatch = FALSE;

   NAString const *nameOfRightVar = &(rightList->var()->getName());

   AssignmentStHostVars *ptrRightVar = globalList->findVar(*nameOfRightVar);
   CMPASSERT(ptrRightVar);

   leftList = leftList_;

   while (leftList && (leftList->var())) {

     nameOfLeftVar = &(leftList->var()->getName());

     if (*nameOfLeftVar == *nameOfRightVar) {
       foundAMatch = TRUE;
       break;
     }

     leftList = leftList->next();
   }

    // Create the ValueIdUnion of the two value ids
    if (!foundAMatch) {
      ValueId value;
      ValueIdList list = ptrRightVar->valueIds();
      if (list.entries() > 0) {
        value = ptrRightVar->currentValueId();
      }
      else {
	// Get a value id for this variable.
	ItemExpr *expr = ptrRightVar->var()->bindNode(bindWA);
	value = expr->getValueId();
      }

      ValueIdUnion *vidUnion = new (bindWA->wHeap())
        ValueIdUnion(value, rightList->currentValueId(),
	             NULL_VALUE_ID);
      vidUnion->bindNode(bindWA);
      if (bindWA->errStatus()) {
        delete vidUnion;
        return NULL;
      }

      ValueId valId = vidUnion->getValueId();
      addValueIdUnion(valId, bindWA->wHeap());

      resultTable->addColumn(bindWA, *refName, valId);

      // The variable inside the IF gets the value id of the ValueIdUnion just
      // generated.
      ptrRightVar->setCurrentValueId(valId);

      // Also update the variable list in the previous IF node
      if (listOfPreviousIF) {
        listOfPreviousIF->addToListInIF(rightList->var(), valId);
      }

    }  // if (!foundAMatch)

    rightList = rightList->next();

  } // while

  return resultTable;
}

//++ MV -
void Union::addAlternateRightChildOrderExprTree(ItemExpr *alternateRightChildOrderExprTree)
{
  ExprValueId t = alternateRightChildOrderExprTree_;

  ItemExprTreeAsList(&t, ITM_ITEM_LIST).insert(alternateRightChildOrderExprTree);
  alternateRightChildOrderExprTree_ = t.getPtr();
}

ItemExpr *Union::removeAlternateRightChildOrderExprTree()
{
  ItemExpr *result = alternateRightChildOrderExprTree_;
  alternateRightChildOrderExprTree_ = NULL;

  return result;
}
// MV--

// -----------------------------------------------------------------------
// member functions for class MergeUnion
// -----------------------------------------------------------------------

MergeUnion::~MergeUnion() {}

NABoolean MergeUnion::isLogical()  const { return FALSE; }

NABoolean MergeUnion::isPhysical() const { return TRUE; }

HashValue MergeUnion::topHash()
{
  HashValue result = Union::topHash();

  // result ^= mergeExpr_;

  return result;
}

NABoolean MergeUnion::duplicateMatch(const RelExpr & other) const
{
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  MergeUnion &o = (MergeUnion &) other;

  // if (mergeExpr_ != o.mergeExpr_)

  ABORT("duplicateMatch shouldn't be called for physical nodes");
  return FALSE;

}

RelExpr * MergeUnion::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  MergeUnion *result;

  if (derivedNode == NULL)
    result = new (outHeap) MergeUnion(NULL,
				      NULL,
				      new (outHeap)UnionMap(*getUnionMap()),
				      getOperatorType(),
				      outHeap);
  else
    result = (MergeUnion *) derivedNode;

  result->mergeExpr_ = mergeExpr_;

  return Union::copyTopNode(result, outHeap);
}

void MergeUnion::setSortOrder(const ValueIdList &newSortOrder)
{
  sortOrder_ = newSortOrder;
  buildMergeExpr();
}

void MergeUnion::addLocalExpr(LIST(ExprNode *) &xlist,
			      LIST(NAString) &llist) const
{
  if (sortOrder_.entries() > 0)
    {
      xlist.insert(sortOrder_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("sort_order");
    }

  if (mergeExpr_ != NULL)
    {
      xlist.insert(mergeExpr_);
      llist.insert("merge_expr");
    }

  Union::addLocalExpr(xlist,llist);
}

void MergeUnion::buildMergeExpr()
{
  // ---------------------------------------------------------------------
  // build the merge expression (an expression that tells which of the
  // two input rows, left or right, should be returned next) by creating
  // an expression "left <= right" from the sort order.
  // ---------------------------------------------------------------------
  ItemExpr *leftList = NULL;
  ItemExpr *rightList = NULL;
  BiRelat *result = NULL;

  if (sortOrder_.entries() > 0)
    {
      for (Lng32 i = 0; i < (Lng32)sortOrder_.entries(); i++)
	{
	  ItemExpr *leftItem;
	  ItemExpr *rightItem;

	  leftItem = sortOrder_[i].getItemExpr()->
	    mapAndRewrite(getLeftMap(),TRUE).getItemExpr();
	  rightItem = sortOrder_[i].getItemExpr()->
	    mapAndRewrite(getRightMap(),TRUE).getItemExpr();

          // swap left and right if DESC is specified.
          if(leftItem->getOperatorType() == ITM_INVERSE)
          {
            // both streams must be sorted according to the same order.
            CMPASSERT(rightItem->getOperatorType() == ITM_INVERSE);
            ItemExpr *temp = leftItem;
            leftItem = rightItem;
            rightItem = temp;
          }

	  // add the newly formed fields of the sort key to the
	  // left and right lists of sort keys
	  if (leftList != NULL)
	    {
	      leftList = new (CmpCommon::statementHeap())
		             ItemList(leftList,leftItem);
	      rightList = new (CmpCommon::statementHeap())
		              ItemList(rightList,rightItem);
	    }
	  else
	    {
	      // both left and right list must be NULL
	      leftList = leftItem;
	      rightList = rightItem;
	    }
	}

      result = new (CmpCommon::statementHeap())
	           BiRelat(ITM_LESS_EQ,leftList,rightList);
      // make the comparison such that NULLs compare greater than instead
      // of making the expression result NULL
      result->setSpecialNulls(TRUE);

      result->synthTypeAndValueId();
    }

  // store the result in the merge expression
  mergeExpr_ = result;
}


// -----------------------------------------------------------------------
// member functions for class GroupByAgg
// -----------------------------------------------------------------------

GroupByAgg::~GroupByAgg() {}

Int32 GroupByAgg::getArity() const { return 1; }

void GroupByAgg::pushdownCoveredExpr(const ValueIdSet & outputExpr,
                                  const ValueIdSet & newExternalInputs,
                                  ValueIdSet & predicatesOnParent,
				  const ValueIdSet * setOfValuesReqdByParent,
				  Lng32 childIndex
		                 )
{
  // ---------------------------------------------------------------------
  // predicates can only be pushed down if the group by did contain
  // a group by clause or if this is a scalar groupby for a subquery that
  // contains null rejecting predicates. If the subquery contains null-rej.
  // preds then it does not need to do null instantiation for the empty
  // result set and therefore we do not create a separate VEGRegion for this
  // subquery. This means that preds can be freely pushed down in this case.
  // See GroupByAgg::pullUpPreds for a symmetric condition.
  // ---------------------------------------------------------------------
  ValueIdSet pushablePredicates;
  ValueIdSet exprOnParent;
  ValueIdSet emptySet;

  if (NOT groupExpr().isEmpty() || containsNullRejectingPredicates())
    pushablePredicates = predicatesOnParent;
#if 0
  else
    computeValuesReqdForPredicates(predicatesOnParent,
                                   exprOnParent);
#endif

  // ---------------------------------------------------------------------
  // Cause the retrieval of all those values that are needed for
  // computing the aggregate functions and the group by list.
  // ---------------------------------------------------------------------
  getValuesRequiredForEvaluatingAggregate(exprOnParent);
  exprOnParent += groupExpr();

  // ---------------------------------------------------------------------
  RelExpr::pushdownCoveredExpr(emptySet,
                               newExternalInputs,
                               pushablePredicates,
			       &exprOnParent,
                               childIndex
                               );

  // ---------------------------------------------------------------------
  // Set the value of predicatesOnParent appropriately.
  // ---------------------------------------------------------------------
  if (NOT groupExpr().isEmpty() || containsNullRejectingPredicates())
      predicatesOnParent.intersectSet(pushablePredicates);

} // GroupByAgg::pushdownCoveredExpr

void GroupByAgg::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();

  // Assign the grouping expressions and the aggregate functions
  // that are computed here as the outputs.
  //
  outputValues += groupExpr();
  outputValues += aggregateExpr();

  // If we're enforcing an ITM_ONE_ROW on (x,y), then we can produce not
  // merely the ITM_ONE_ROW, but also x and y, so add them to our outputs.
  // For example, if the aggregate is, say,
  //	ITM_ONE_ROW(VEGRef_10(T.A,ixT.A), VEGRef_15(T.B,ixT.B))
  //	{ example query: select * from S where (select A,B from T) < (100,200) }
  // then add value ids 10 and 11 to our characteristic outputs.
  //
  for (ValueId aggid = aggregateExpr().init();
       aggregateExpr().next(aggid);
       aggregateExpr().advance(aggid))
    {
      ItemExpr *aggie = aggid.getItemExpr();
      if (aggie->getOperatorType() == ITM_ONE_ROW)
        {
	  ValueIdSet moreAvailableOutputs;
	  aggie->child(0)->convertToValueIdSet(moreAvailableOutputs,
					       NULL, ITM_ITEM_LIST, FALSE);
	  outputValues += moreAvailableOutputs;
	}
    }
} // GroupByAgg::getPotentialOutputValues()

const NAString GroupByAgg::getText() const
{
  if (NOT groupExpr().isEmpty())
    {
      if (isNotAPartialGroupBy())
	return "groupby";
      else if (isAPartialGroupByRoot())
	return "partial_groupby_root";
      else if (isAPartialGroupByNonLeaf())
	return "partial_groupby_non_leaf";
      else
	return "partial_groupby_leaf";
    }
 else
   {
      if (isNotAPartialGroupBy())
	return "scalar_aggr";
      else if (isAPartialGroupByRoot())
	return "partial_aggr_root";
      else if (isAPartialGroupByNonLeaf())
	return "partial_aggr_non_leaf";
      else
	return "partial_aggr_leaf";
   }

} // GroupByAgg::getText()

HashValue GroupByAgg::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= groupExpr_;
  result ^= aggregateExpr_;
  result ^= (Int32) formEnum_;        // MSVC requires cast.

  return result;
}

NABoolean GroupByAgg::duplicateMatch(const RelExpr & other) const
{
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  GroupByAgg &o = (GroupByAgg &) other;

  if (groupExpr_     != o.groupExpr_ OR
      aggregateExpr_ != o.aggregateExpr_ OR
      formEnum_ != o.formEnum_ )

    return FALSE;

  return TRUE;
}

RelExpr * GroupByAgg::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  GroupByAgg *result;

  if (derivedNode == NULL)
    result = new (outHeap) GroupByAgg(NULL,
		                      getOperatorType(),
				      NULL,
				      NULL,
				      outHeap);
  else
    result = (GroupByAgg *) derivedNode;

  // copy parse tree nodes (parser only)
  if (groupExprTree_ != NULL)
    result->groupExprTree_ = groupExprTree_->copyTree(outHeap);
  if (aggregateExprTree_ != NULL)
    result->aggregateExprTree_ = aggregateExprTree_->copyTree(outHeap);

  result->groupExpr_     = groupExpr_;
  result->rollupGroupExprList_ = rollupGroupExprList_;
  result->aggregateExpr_ = aggregateExpr_;
  result->formEnum_      = formEnum_;
  result->gbAggPushedBelowTSJ_ = gbAggPushedBelowTSJ_;
  result->gbAnalysis_ = gbAnalysis_;
  result->requiresMoveUp_ = requiresMoveUp_ ;
  result->leftUniqueExpr_ = leftUniqueExpr_;
  result->containsNullRejectingPredicates_ = containsNullRejectingPredicates_ ;
  result->parentRootSelectList_ = parentRootSelectList_;
  result->isMarkedForElimination_ = isMarkedForElimination_;

  result->selIndexInHaving_ = selIndexInHaving_;
  result->aggrExprsToBeDeleted_ = aggrExprsToBeDeleted_;

  result->isRollup_ = isRollup_;
  result->extraGrpOrderby_= extraGrpOrderby_;
  result->extraOrderExpr_= extraOrderExpr_;

  return RelExpr::copyTopNode(result, outHeap);
}

void GroupByAgg::addGroupExprTree(ItemExpr *groupExpr)
{
  ExprValueId g = groupExprTree_;

  ItemExprTreeAsList(&g, ITM_ITEM_LIST).insert(groupExpr);
  groupExprTree_ = g.getPtr();
}

ItemExpr * GroupByAgg::removeGroupExprTree()
{
  ItemExpr * result = groupExprTree_;

  groupExprTree_ = NULL;

  return result;
}

void GroupByAgg::addAggregateExprTree(ItemExpr *aggrExpr)
{
  ExprValueId g = groupExprTree_;

  ItemExprTreeAsList(&g, ITM_ITEM_LIST).insert(aggrExpr);
  groupExprTree_ = g.getPtr();
}

ItemExpr * GroupByAgg::removeAggregateExprTree()
{
  ItemExpr * result = aggregateExprTree_;

  aggregateExprTree_ = NULL;

  return result;
}

void GroupByAgg::getValuesRequiredForEvaluatingAggregate(ValueIdSet& relevantValues)
{
  // Find the values that are needed to evaluate aggregate functions.
  // NOTE: this should normally just be the direct children of the
  // aggregate functions. However, some aggregate functions such as
  // anyTrue sometimes refer to values further down the tree (and
  // if it's only by using such values as required sort orders).
  // Handle this special case here (or maybe we should have changed
  // the anyTrue aggregate function such that it takes separate arguments:
  // anyTrueGreater(a,b), anyTrueLess(a,b), anyTrueGreaterEq(a,b), ...

  // for each aggregate expression in the groupby node
  for (ValueId x = aggregateExpr_.init();
       aggregateExpr_.next(x);
       aggregateExpr_.advance(x))
    {
      Aggregate *agg = (Aggregate *) x.getItemExpr();
      Lng32 nc = agg->getArity();

      // handle special cases for special aggregate functions
      switch (agg->getOperatorType())
	{
	case ITM_ANY_TRUE:
	case ITM_ANY_TRUE_MAX:
	  {
	    ItemExpr *boolInput = agg->child(0);

	    // if the child is a binary comparison operator, then
	    // require both of the children instead of the comparison op.
	    switch (boolInput->getOperatorType())
	      {
	      case ITM_EQUAL:
	      case ITM_NOT_EQUAL:
	      case ITM_LESS:
	      case ITM_LESS_EQ:
	      case ITM_GREATER:
	      case ITM_GREATER_EQ:
		relevantValues += boolInput->child(0)->getValueId();
		relevantValues += boolInput->child(1)->getValueId();
		break;
              case ITM_VEG_PREDICATE:
                {
                  VEG * vegPtr = ((VEGPredicate *)boolInput)->getVEG();
                  relevantValues += vegPtr->getVEGReference()->getValueId();
		}
                break;
	      default:

		// might not happen right now: an anyTrue with something
		// other than a binary comparison operator
		relevantValues += boolInput->getValueId();
		break;
	      }
	  }

	  break;

        case ITM_ONE_ROW:
         {
           // collect leaf values into relevant Values
           ValueIdSet AvailableOutputs_;
           agg->child(0)->convertToValueIdSet(AvailableOutputs_,
                          NULL, ITM_ITEM_LIST, FALSE);
           relevantValues += AvailableOutputs_;
           break;
         }
	default:
	  {
	    // all other aggregate functions are handled here
            //
            // If we are doing a distinct aggregate we need the
            // distinct value id. E.g. sum(distinct x*x) with distinct
            //                         valueId x, means we eliminate
            //                         distinct x's first, then compute sum(x*x)
            if(agg->isDistinct())
              relevantValues += agg->getDistinctValueId();
            else
              relevantValues += agg->child(0)->getValueId();

	    // for each child of this particular aggregate expression
	    for (Lng32 i = 1; i < nc; i++)
	      {
		// add the value id of that child to "relevantValues"
		relevantValues += agg->child(i)->getValueId();
	      }
	  }
	  break;
	}
    }
}

void GroupByAgg::addLocalExpr(LIST(ExprNode *) &xlist,
			      LIST(NAString) &llist) const
{
  if (groupExprTree_ != NULL OR
      NOT groupExpr_.isEmpty())
    {
      if (groupExpr_.isEmpty())
	xlist.insert(groupExprTree_);
      else if (isRollup() && (NOT rollupGroupExprList_.isEmpty()))
 	xlist.insert(rollupGroupExprList_.rebuildExprTree(ITM_ITEM_LIST));
      else
	xlist.insert(groupExpr_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("grouping_columns");
    }

  if (aggregateExprTree_ != NULL OR
      NOT aggregateExpr_.isEmpty())
    {
      if (aggregateExpr_.isEmpty())
	xlist.insert(aggregateExprTree_);
      else
	xlist.insert(aggregateExpr_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("aggregates");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

// -----------------------------------------------------------------------
// Examine the aggregate functions. If any one of them cannot be
// evaluated in stages, for example, a partial aggregation followed
// by finalization, then do not split this GroupByAgg.
// -----------------------------------------------------------------------
 NABoolean GroupByAgg::aggregateEvaluationCanBeStaged() const
{
  for (ValueId aggrId = aggregateExpr().init();
       aggregateExpr().next(aggrId);
       aggregateExpr().advance(aggrId))
    {
      CMPASSERT(aggrId.getItemExpr()->isAnAggregate());
      if (groupExpr().isEmpty() && 
	  (aggrId.getItemExpr()->getOperatorType() == ITM_ONEROW))
	    return FALSE;

      if (NOT ((Aggregate *)aggrId.getItemExpr())->evaluationCanBeStaged())
	return FALSE;
    }
  return TRUE;
} // GroupByAgg::aggregateEvaluationCanBeStaged()

NABoolean GroupByAgg::executeInDP2() const
{
  CMPASSERT(getPhysicalProperty());
  return getPhysicalProperty()->executeInDP2();
}

// Try to pull up predicates in preCodeGen, to reduce char. inputs of the
// child. Don't actually do this unless "modify" parameter is set to TRUE,
// (we want to test this condition in the optimizer for costing).
// Return TRUE if we could move some predicates.
// Could make this a virtual method on RelExpr if we want to support
// this for other operators as well.
NABoolean GroupByAgg::tryToPullUpPredicatesInPreCodeGen(
     const ValueIdSet &valuesAvailableInParent,    // pull preds that are covered by these
     ValueIdSet       &pulledPredicates,           // return the pulled-up preds
     ValueIdMap       *optionalMap)                // optional map to rewrite preds
{
  // other item expressions needed by the child (excluding
  // selection preds), this is where we make use of the knowledge
  // that we are dealing with a groupby. 
  ValueIdSet myLocalExpr;
  ValueIdSet myNewInputs(getGroupAttr()->getCharacteristicInputs());
  ValueIdSet mappedValuesAvailableInParent;
  ValueIdSet tempPulledPreds(selectionPred()); // be optimistic

  myLocalExpr += child(0).getGroupAttr()->getCharacteristicInputs();
  myLocalExpr += groupExpr();
  myLocalExpr += aggregateExpr();
  // make sure we can still produce our characteristic outputs too
  myLocalExpr += getGroupAttr()->getCharacteristicOutputs();
          
  // consider only preds that we can evaluate in the parent
  if (optionalMap)
    optionalMap->mapValueIdSetDown(valuesAvailableInParent,
                                   mappedValuesAvailableInParent);
  else
    mappedValuesAvailableInParent = valuesAvailableInParent;
  tempPulledPreds.removeUnCoveredExprs(mappedValuesAvailableInParent);

  // add the rest to myLocalExpr
  myLocalExpr += selectionPred();
  myLocalExpr -= tempPulledPreds;
              
  // see which of the char. inputs are needed by my local expressions
  myLocalExpr.weedOutUnreferenced(myNewInputs);
          
  // pull up predicates only if that reduces my char. inputs
  if (NOT (myNewInputs == getGroupAttr()->getCharacteristicInputs()))
    {
      ValueIdSet selPredOnlyInputs(getGroupAttr()->getCharacteristicInputs());
          
      // inputs only used by selection predicates
      selPredOnlyInputs -= myNewInputs;

      // loop through the selection predicates and pull
      // those up that reference myNewInputs
      for (ValueId x=tempPulledPreds.init();
           tempPulledPreds.next(x);
           tempPulledPreds.advance(x))
        {
          if (x.getItemExpr()->referencesOneValueFrom(selPredOnlyInputs))
            {
              // keep this predicate in tempPulledPreds and
              // remove it from the selection predicates
              selectionPred() -= x;
            }
          else
            {
              // this predicate stays on the local node,
              // remove it from tempPulledPreds
              tempPulledPreds -= x;
            }
        }
    }
  else
    {
      // no predicates get pulled up
      tempPulledPreds.clear();
    }

  if (!tempPulledPreds.isEmpty())
    {
      // return pulled predicates
      if (optionalMap)
        {
          ValueIdSet rewrittenPulledPreds;

          optionalMap->rewriteValueIdSetUp(rewrittenPulledPreds, tempPulledPreds);
          pulledPredicates += rewrittenPulledPreds;
        }
      else
        pulledPredicates += tempPulledPreds;

      // adjust char. inputs - this is not exactly
      // good style, just overwriting the char. inputs, but
      // hopefully we'll get away with it at this stage in
      // the processing
      getGroupAttr()->setCharacteristicInputs(myNewInputs);
    }

  // note that we removed these predicates from our node, it's the
  // caller's responsibility to take them
  return (NOT tempPulledPreds.isEmpty());
}

// -----------------------------------------------------------------------
// member functions for class SortGroupBy
// -----------------------------------------------------------------------

SortGroupBy::~SortGroupBy() {}

NABoolean SortGroupBy::isLogical()  const {return FALSE;}

NABoolean SortGroupBy::isPhysical() const {return TRUE;}

const NAString SortGroupBy::getText() const
{
  if (isRollup())
    return "sort_" + GroupByAgg::getText() + "_rollup";
  else
    return "sort_" + GroupByAgg::getText();
}

RelExpr * SortGroupBy::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) SortGroupBy(NULL,
				       getOperatorType(),
				       NULL,
				       NULL,
				       outHeap);
  else
    result = derivedNode;

  return GroupByAgg::copyTopNode(result, outHeap);
}

PlanPriority ShortCutGroupBy::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  // For min(X) or max(X) where X is the first clustering key column,
  // allow shortcutgroupby plan to compete with other plans based on cost.
  // Specifically, match the priority of the wave fix so that a
  // shortcutgroupby plan can cost compete with the parallel 
  // partialgroupby plan.
  PlanPriority result;
  if (QueryAnalysis::Instance() &&
      QueryAnalysis::Instance()->dontSurfTheWave()) {
   // do this only if the wave fix is a competing plan
    result.incrementLevels(10, 0);
  }
  return result;
}

PlanPriority SortGroupBy::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  double val = 1;
  if (degreeOfParallelism <= 1)
    {
      // serial plans are risky. exact an insurance premium from serial plans.
      val = CURRSTMT_OPTDEFAULTS->riskPremiumSerial();

      // when dontSurfTheWave is ON, 
      // consider serial sort_partial_aggr_nonleaf risky 
      if (QueryAnalysis::Instance() &&
          QueryAnalysis::Instance()->dontSurfTheWave() &&
          isAPartialGroupByNonLeaf() && val <= 1)
        {
          val = 1.1; 
        }
    }
  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  // WaveFix Begin
  // This is part of the fix for the count(*) wave
  // if there is a scalar aggregate query on a single partitioned table,
  // something like Select count(*) from fact;
  // In such a case we would like to get a layer of esps,
  // doing so causes the plan to fixup in parallel avoiding the serial
  // fixup if the plan is just the master executor on top of dp2. The
  // serial fixup causes the query to execute in wave pattern, since
  // each dp2 is fixed up and then starts execution. Due to serial
  // fixup a dp2 is fixed up, and then we move to the next dp2 causing
  // the wave pattern.
  if (QueryAnalysis::Instance() &&
      QueryAnalysis::Instance()->dontSurfTheWave())
  {
    if (isAPartialGroupByLeaf2() && spp->executeInDP2())
      result.incrementLevels(10, 0);
    else if (isAPartialGroupByLeaf1() &&
            (degreeOfParallelism>1) &&
            (!spp->executeInDP2()))
      result.incrementLevels(5, 0);
  }
  // WaveFix End

  // The remaining part of the code in this function relates to parallelism
  // priority and not applicable to scalar aggregates
  if (groupExpr().isEmpty())
    return result;

  if(spp->executeInDP2())
    return result;

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR (maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

  return result;
}

// -----------------------------------------------------------------------
// member functions for class ShortCutGroupBy
// -----------------------------------------------------------------------

const NAString ShortCutGroupBy::getText() const
{
  return "shortcut_" + GroupByAgg::getText();
}

RelExpr * ShortCutGroupBy::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  ShortCutGroupBy *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Create an empty ShortCutGroupBy node.
    //
    result = new (outHeap) ShortCutGroupBy(NULL,
					  getOperatorType(),
					  NULL,
					  NULL,
					  outHeap);
  else
    // A node has already been constructed as a derived class.
    //
    result = (ShortCutGroupBy *) derivedNode;

  // Copy the relevant fields.

  result->opt_for_max_ = opt_for_max_;
  result->opt_for_min_ = opt_for_min_;
  result->isnullable_ = isnullable_;
  result->lhs_anytrue_ = lhs_anytrue_;
  result->rhs_anytrue_ = rhs_anytrue_;

  // Copy any data members from the classes lower in the derivation chain.
  //
  return GroupByAgg::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class PhysShortCutGroupBy
// -----------------------------------------------------------------------

RelExpr * PhysShortCutGroupBy::copyTopNode(RelExpr *derivedNode,
                                           CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Create an empty ShortCutGroupBy node.
    //
    result = new (outHeap) PhysShortCutGroupBy(NULL,
					       getOperatorType(),
					       NULL,
					       NULL,
					       outHeap);
  else
    // A node has already been constructed as a derived class.
    //
    result = (PhysShortCutGroupBy *) derivedNode;

  // PhysShortCutGroupBy has no data members.

  // Copy any data members from the classes lower in the derivation chain.
  //
  return ShortCutGroupBy::copyTopNode(result, outHeap);
}


// -----------------------------------------------------------------------
// member functions for class HashGroupBy
// -----------------------------------------------------------------------

HashGroupBy::~HashGroupBy() {}

NABoolean HashGroupBy::isLogical()  const {return FALSE;}

NABoolean HashGroupBy::isPhysical() const {return TRUE;}

const NAString HashGroupBy::getText() const
{
  return "hash_" + GroupByAgg::getText();
}

RelExpr * HashGroupBy::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) HashGroupBy(NULL,
				       getOperatorType(),
				       NULL,
				       NULL,
				       outHeap);
  else
    result = derivedNode;

  return GroupByAgg::copyTopNode(result, outHeap);
}


PlanPriority HashGroupBy::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
  Lng32 degreeOfParallelism = spp->getCountOfPartitions();
  double val = 1;
  if (degreeOfParallelism <= 1)
    {

    // Don't command premium for serial hash partial groupby plan if :
    // 1. Operator is partial group by root
    // 2. process < 5K rows
    // This is to prevent optimizer choosing parallel plans for small queries.
    // The idea is either premium has been already applied for groupby leaf level
    // or leaf is running in parallel, we don't need to run root also in parallel

       if ( isAPartialGroupByRoot() && 
            CostScalar((ActiveSchemaDB()->getDefaults()).
              getAsULong(GROUP_BY_PARTIAL_ROOT_THRESHOLD)) >=
                  this->getChild0Cardinality(context) )
          val = 1;
       else
          // serial plans are risky. extract an insurance premium from serial plans.

          val = CURRSTMT_OPTDEFAULTS->riskPremiumSerial();
    }

  CostScalar premium(val);
  PlanPriority result(0, 0, premium);

  if (QueryAnalysis::Instance() AND
      QueryAnalysis::Instance()->optimizeForFirstNRows())
    result.incrementLevels(HASH_GROUP_BY_FIRST_N_PRIORITY,0);

  // The remaining part of the code in this funtion relates to parallelism
  // priority and not applicable to scalar aggregates
  if (groupExpr().isEmpty())
    return result;

  // esp parallelism priority logic does not apply to operators in dp2
  if(spp->executeInDP2())
    return result;

  // For the option of Max Degree of Parallelism we can either use the
  // value set in comp_int_9 (if positive) or we use the number of CPUs
  // if the CQD is set to -1, or feature is disabled if CQD is 0 (default).
  Lng32 maxDegree = ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_9);
  if (CURRSTMT_OPTDEFAULTS->maxParallelismIsFeasible() OR (maxDegree == -1) )
  {
    // if CQD is set to -1 this mean use the number of CPUs
    maxDegree = spp->getCurrentCountOfCPUs();
  }
  if (maxDegree > 1) // CQD set to 0 means feature is OFF
  {
    if (degreeOfParallelism < maxDegree)
      result.incrementLevels(0,-10); // need to replace with constant
  }

  //cout<<maxDegree<<"-------"<<spp->getCountOfPartitions()<<endl;

  return result;
}

// -----------------------------------------------------------------------
// member functions for class Scan
// -----------------------------------------------------------------------

void Scan::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // Assign the set of columns that belong to the table to be scanned
  // as the output values that can be produced by this scan.
  //
  if (potentialOutputs_.isEmpty())
    {
      outputValues.insertList( getTableDesc()->getColumnList() );
      outputValues.insertList( getTableDesc()->hbaseTSList() );
      outputValues.insertList( getTableDesc()->hbaseVersionList() );
    }
  else
    outputValues = potentialOutputs_;

  outputValues += getExtraOutputColumns();
} // Scan::getPotentialOutputValues()

void Scan::getPotentialOutputValuesAsVEGs(ValueIdSet& outputs) const
{
  outputs.clear();
  ValueIdSet tempSet ;

  getPotentialOutputValues(tempSet);
  getTableDesc()->getEquivVEGCols(tempSet, outputs); 
}



Int32 Scan::getArity() const { return 0;}

NABoolean Scan::isHiveTable() const 
{
  return (getTableDesc() && getTableDesc()->getNATable() ?
	  getTableDesc()->getNATable()->isHiveTable() :
	  FALSE);
}

NABoolean Scan::isHbaseTable() const 
{
  return (getTableDesc() && getTableDesc()->getNATable() ?
	  getTableDesc()->getNATable()->isHbaseTable() :
	  FALSE);
}

NABoolean Scan::isSeabaseTable() const 
{
  return (getTableDesc() && getTableDesc()->getNATable() ?
	  getTableDesc()->getNATable()->isSeabaseTable() :
	  FALSE);
}

const NAString Scan::getText() const
{
  NAString op(CmpCommon::statementHeap());

  if (isSampleScan() == TRUE)
    op = "sample_scan ";
  else
    op = "scan ";
  return op + userTableName_.getTextWithSpecialType();
}

HashValue Scan::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= getTableDesc();
  result ^= potentialOutputs_;
  result ^= numIndexJoins_;

  return result;
}

NABoolean Scan::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  Scan &o = (Scan &) other;

  if (NOT (userTableName_ == o.userTableName_) OR
      NOT (getTableDesc() == o.getTableDesc()) OR
      NOT (potentialOutputs_ == o.potentialOutputs_) OR
      ((forcedIndexInfo_ OR o.forcedIndexInfo_) AND (
      //just comparing the entries is probably not enough????
      NOT (indexOnlyIndexes_.entries() == o.indexOnlyIndexes_.entries()) OR
      NOT (possibleIndexJoins_ == o.possibleIndexJoins_) OR
      NOT (numIndexJoins_ == o.numIndexJoins_)))
      OR
      NOT (suppressHints_ == o.suppressHints_) OR
      NOT (isSingleVPScan_ == o.isSingleVPScan_) OR
      NOT (getExtraOutputColumns() == o.getExtraOutputColumns()) OR
      NOT (samplePercent() == o.samplePercent()) OR
      NOT (clusterSize() == o.clusterSize()))
    return FALSE;

  return TRUE;
}

RelExpr * Scan::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Scan *result;

  if (derivedNode == NULL)
    result = new (outHeap)
                 Scan(userTableName_, getTableDesc(), REL_SCAN, outHeap);
  else
    result = (Scan *) derivedNode;

  result->baseCardinality_  = baseCardinality_;
  result->potentialOutputs_ = potentialOutputs_;
  result->numIndexJoins_    = numIndexJoins_;
  result->accessOptions_    = accessOptions_;
  result->pkeyHvarList_     = pkeyHvarList_;
  result->setOptStoi(stoi_);
  result->samplePercent(samplePercent());
  result->suppressHints_    = suppressHints_;
  result->clusterSize(clusterSize());
  result->scanFlags_ = scanFlags_;
  result->setExtraOutputColumns(getExtraOutputColumns());
  result->isRewrittenMV_ = isRewrittenMV_;
  result->matchingMVs_ = matchingMVs_;
  result->hbaseAccessOptions_ = hbaseAccessOptions_;
  result->commonSubExpr_ = commonSubExpr_;

  // don't copy values that can be calculated by addIndexInfo()
  // (some callers will change selection preds, which requires recomputation)

  return RelExpr::copyTopNode(result, outHeap);
}

void Scan::copyIndexInfo(RelExpr *derivedNode)
{
  CMPASSERT (derivedNode != NULL AND
      derivedNode->getOperatorType() == REL_SCAN);

  Scan * scan = (Scan *)derivedNode;
  forcedIndexInfo_ = scan->forcedIndexInfo_;
  if (NOT scan->getIndexOnlyIndexes().isEmpty() OR
      scan->getPossibleIndexJoins().entries() > 0)
  {
    // first copy the possible index join info
    const LIST(ScanIndexInfo *) & ixJoins = scan->getPossibleIndexJoins();
    for (CollIndex i = 0; i < ixJoins.entries(); i++)
    {
      ScanIndexInfo * ix = new (CmpCommon::statementHeap())
	ScanIndexInfo(*(ixJoins[i]));
      possibleIndexJoins_.insert(ix);
    }

    // now, copy the index descriptors
    const SET(IndexProperty *) & ixDescs = scan->getIndexOnlyIndexes();

    for (CollIndex j = 0; j <ixDescs.entries(); j++)
    {
      indexOnlyIndexes_.insert(ixDescs[j]);
    }
  }
  generatedCCPreds_ = scan->generatedCCPreds_;
}

void Scan::removeIndexInfo()
{
	possibleIndexJoins_.clear();
	indexOnlyIndexes_.clear();
        indexJoinScans_.clear();
        generatedCCPreds_.clear();
	forcedIndexInfo_ = FALSE;
}

/*******************************************************
* Generates set of IndexDesc from the set of IndexProperty
********************************************************/

const SET(IndexDesc *) & Scan::deriveIndexOnlyIndexDesc()
{
    indexOnlyScans_.clear();
    CollIndex ixCount = indexOnlyIndexes_.entries();
    for(CollIndex i=0; i< ixCount; i++)
    {
      indexOnlyScans_.insert(indexOnlyIndexes_[i]->getIndexDesc());
    }
    return indexOnlyScans_;

}

/*******************************************************
* Generates set of IndexDesc from the set of ScanIndexInfo
********************************************************/
const SET(IndexDesc *) & Scan::deriveIndexJoinIndexDesc()
{
    indexJoinScans_.clear();
    CollIndex ijCount = possibleIndexJoins_.entries();
    for(CollIndex i=0; i< ijCount; i++)
    {
      ScanIndexInfo *ixi = possibleIndexJoins_[i];
      CollIndex uixCount = ixi->usableIndexes_.entries();
      for(CollIndex j=0; j < uixCount; j++)
      {
        if (ixi->usableIndexes_[j]->getIndexDesc())
          indexJoinScans_.insert(ixi->usableIndexes_[j]->getIndexDesc());
      }
    }
    return indexJoinScans_;
}

void Scan::addIndexInfo()
{
  // don't do this twice, return if already set
  if (NOT indexOnlyIndexes_.isEmpty() OR
      possibleIndexJoins_.entries() > 0)
    return;

  forcedIndexInfo_ = FALSE;
  const TableDesc     * tableDesc = getTableDesc();
  const LIST(IndexDesc *) & ixlist = tableDesc->getIndexes();
  ValueIdSet preds = selectionPred();
  // changing back to old predicate tree:
  if ((CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) &&
      (preds.entries()))
  {
    ValueIdList selectionPredList(preds);
    ItemExpr *inputItemExprTree = selectionPredList.rebuildExprTree(ITM_AND,FALSE,FALSE);
    ItemExpr * resultOld = revertBackToOldTree(CmpCommon::statementHeap(), inputItemExprTree);
    preds.clear();
    resultOld->convertToValueIdSet(preds, NULL, ITM_AND);
    doNotReplaceAnItemExpressionForLikePredicates(resultOld,preds,resultOld);
  }


  if (CmpCommon::getDefault(MTD_GENERATE_CC_PREDS) == DF_ON)
    {
      // compute predicates on computed columns from regular predicates, based
      // on the definition of the computed column. Example: 
      // - regular predicate: a = 99
      // - computed column definition: "_SALT_" = HASH2PARTFUNC(a,2)
      // - computed predicate: "_SALT_" = HASH2PARTFUNC(99,2);
      ValueIdSet clusteringKeyCols(
           getTableDesc()->getClusteringIndex()->getClusteringKeyCols());
      ValueIdSet selectionPreds(preds);

      ScanKey::createComputedColumnPredicates(
           selectionPreds,
           clusteringKeyCols,
           getGroupAttr()->getCharacteristicInputs(),
           generatedCCPreds_);
    }

  // a shortcut for tables with no indexes
  if ((ixlist.entries() == 1)||
      (tableDesc->isPartitionNameSpecified()))
    {
      // that's easy, there is only one index (the base table)
      // and that index better have everything we need
      IndexJoinSelectivityEnum junk;
      MdamFlags flag=ixlist[0]->pruneMdam(preds,TRUE,junk);;
      IndexProperty * ixProp = new(CmpCommon::statementHeap())
			       IndexProperty(ixlist[0],
					     flag);
      indexOnlyIndexes_.insert(ixProp);
      return;
    }

  // all the value ids that are required by the scan and its parents
  ValueIdSet requiredValueIds(getGroupAttr()->getCharacteristicOutputs());


  // VEGPreds can have two forms, an  A IS NOT NULL form and an A=B form
  // when expanded in the generator. If an index does not provide a
  // VEG member that the base table provides, a VEGPredicate could be
  // covered by the index in its IS NOT NULL form (checking a char. input
  // whether it is not null). To avoid this bug, add all the base cols
  // that contribute to VEGPredicates as explicitly required values.
  addBaseColsFromVEGPreds(requiredValueIds);

  // using old predicate tree:
  if ((CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) &&
      (preds.entries()))
  {
    requiredValueIds += preds;
  }
  else
    // selection predicates are also required, add them to requiredValueIds
    requiredValueIds += selectionPred();

  // a list of VEGReferences to the clustering key column(s)
  ValueIdSet clusteringKeyColumns;

  // some helper variables
  ValueIdList clusteringKeyColList;

  // a set of join predicates between indexes (same for all indexes)
  ValueIdSet indexJoinPreds;

  // ---------------------------------------------------------------------
  // find out the subset of values that are always covered
  // ---------------------------------------------------------------------
  // get the clustering key columns and transform them into VEGies
  CMPASSERT(tableDesc);
  tableDesc->getEquivVEGCols(
       tableDesc->getClusteringIndex()->getIndexKey(),
       clusteringKeyColList);
  clusteringKeyColumns = clusteringKeyColList;

  // get the VEGPredicates from the list of VEGReferences; they are
  // the join predicates between indexes (who all contain the clustering key)
  for (ValueId x = clusteringKeyColumns.init();
       clusteringKeyColumns.next(x);
       clusteringKeyColumns.advance(x))
    {
      // clustering key columns must be VEGReferences
      CMPASSERT(x.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE);

      if (((VEGReference *) x.getItemExpr())->getVEG()->getSpecialNulls())
        ((VEGReference *) x.getItemExpr())
               ->getVEG()->getVEGPredicate()->setSpecialNulls(TRUE);

      // find the corresponding VEGPredicate and add it to the join preds
      indexJoinPreds += ((VEGReference *) x.getItemExpr())->
	getVEG()->getVEGPredicate()->getValueId();
    }


  const NABoolean updatingCol = tableDesc->getColUpdated().entries() > 0;
  const NABoolean unlimitedIndexJoinsAllowed =
          ((ActiveControlDB()->getRequiredShape() AND
	    ActiveControlDB()->getRequiredShape()->getShape() AND
	    NOT ActiveControlDB()->getRequiredShape()->getShape()->isCutOp())
           OR
           (getGroupAttr()->isEmbeddedUpdateOrDelete())
           OR
	   (getGroupAttr()->isStream())
          );

		      
  const TableAnalysis * tAnalysis = getTableDesc()->getTableAnalysis();

  // with this CQD value set, try to consider minimum indexes possible
  // if ixProp is no better than any of indexOnlyIndexes_ - don't add
  // it. If ixProp is better than some of this set - remove them.
  NABoolean tryToEliminateIndex = 
             CURRSTMT_OPTDEFAULTS->indexEliminationLevel() == OptDefaults::AGGRESSIVE
             AND NOT unlimitedIndexJoinsAllowed 
             AND tAnalysis;

  CostScalar indexEliminationThreshold = 
                ActiveSchemaDB()->getDefaults().getAsLong(INDEX_ELIMINATION_THRESHOLD); 


  NABoolean printIndexElimination = 
         CmpCommon::getDefault(NSK_DBG_PRINT_INDEX_ELIMINATION) == DF_ON &&
         CmpCommon::getDefault(NSK_DBG) == DF_ON;

  ostream &out = CURRCONTEXT_OPTDEBUG->stream();

  if ( printIndexElimination ) {
    out << endl << "call addIndexInfo()" << endl;
    out << "tryToEliminateIndex=" << (Lng32)tryToEliminateIndex << endl;
  }

  // ---------------------------------------------------------------------
  // For each index, check whether it provides any useful values
  // ---------------------------------------------------------------------
  for (CollIndex indexNo = 0; indexNo < ixlist.entries(); indexNo++)
    {
      IndexDesc *idesc = ixlist[indexNo];

      NABoolean dummy; // halloweenProtection is decided using updateableIndex
      // in GU::normalizeNode. Here this parameter is not used.
      // Determine if this index can be used for a scan during an update.
      if (updatingCol AND NOT updateableIndex(idesc, preds, dummy))
        continue;

      ValueIdSet indexColumns(idesc->getIndexColumns());
      ValueIdSet referencedInputs;
      ValueIdSet coveredSubexpr;
      ValueIdSet unCoveredExpr;
      GroupAttributes indexOnlyGA;
      NABoolean indexOnlyScan;

      // make group attributes for an index scan
      indexOnlyGA.addCharacteristicOutputs(idesc->getIndexColumns());
      indexOnlyGA.addCharacteristicOutputs(extraOutputColumns_);

      // does the index cover all required values, and if not, which
      // ones does it cover and which ones are not covered
      indexOnlyScan = requiredValueIds.isCovered(
	   getGroupAttr()->getCharacteristicInputs(),
	   indexOnlyGA,
	   referencedInputs,
	   coveredSubexpr,
	   unCoveredExpr);

      // if this is a sample scan (currently these are only CLUSTER
      // sampling scans) then do not choose index only scan.  Also,
      // due to an artifact of sampling, the 'isCovered' test above
      // will not return TRUE even for the ClusteringIndex, so force
      // it to be true for the ClusteringIndex.  Note that
      // ClusterIndex means that this is the basetable access path.
      //
      if (isSampleScan())
        {
          if (idesc->isClusteringIndex())
            {
              // Force it to be TRUE for the basetable access path.
              // This overrides the value of 'indexOnlyScan' produced
              // above since for sample scans, the isCovered test will
              // always fail, even for the basetable.
              //
              indexOnlyScan = TRUE;
            }
          else
            {
              indexOnlyScan = FALSE;
            }
        }

      // if the user specified IN EXCLUSIVE MODE option for this select,
      // then do not choose index only scan. This is needed so the base
      // table row could be locked in exclusive mode.
      if ((indexOnlyScan) &&
	  (! idesc->isClusteringIndex()) &&
	  (accessOptions().lockMode() == EXCLUSIVE_))
	indexOnlyScan = FALSE;

      //pruneMdam() returns a flag indicating if the index would have
      //has good enough key access for MDAM access to be viable. For index
      //join indexes it also returns a IndexJoinSelectivityEnum that
      //indicates if the index join is going to exceed the cost of just
      //scanning the base table.
      if (indexOnlyScan)
	{
	  // this index supplies all the info we need, consider
	  // it for an index only scan later
	  IndexJoinSelectivityEnum junk;
	  MdamFlags flag=idesc->pruneMdam(preds,TRUE,junk);
	  IndexProperty * ixProp = new(CmpCommon::statementHeap())
			       IndexProperty(idesc,
					     flag);
          if (tryToEliminateIndex) 
          {
            // with this CQD value set, try to consider minimum indexes possible
            // if ixProp is no better than any of indexOnlyIndexes_ - don't add
            // it. If ixProp is better than some of this set - remove them.
            ixProp->updatePossibleIndexes(indexOnlyIndexes_, this);
          }
          else
	    indexOnlyIndexes_.insert(ixProp);
	}
      else
	{
	  GroupAttributes indexGA;
	  ValueIdSet ijCoveredPredicates;
	  if(numIndexJoins_ < MAX_NUM_INDEX_JOINS AND
             NOT unlimitedIndexJoinsAllowed)
	  {
	    //Is any of the predicates covered by key columns of the
	    //alternate index?
	    ValueIdList userKeyColumns(idesc->getIndexKey());
            CollIndex numSecondaryIndexKey = 
              idesc->getNAFileSet()->getCountOfColumns(
                   TRUE,   // key columns only
                   TRUE,   // user-specified key columns only
                   FALSE,  // don't exclude system columns
                   FALSE); // don't exclude salt/divisioning columns
	    CollIndex numClusteringKey = 
              userKeyColumns.entries() - numSecondaryIndexKey;
	    if(NOT idesc->isUniqueIndex())
	    {
	      CollIndex entry = userKeyColumns.entries() -1;
	      for(CollIndex i=0;i<numClusteringKey;i++)
	      {
		userKeyColumns.removeAt(entry);
		entry--;
	      }
	    }
	    indexGA.addCharacteristicOutputs(userKeyColumns);

	    ValueIdSet ijReferencedInputs;
	    ValueIdSet ijUnCoveredExpr;

            ValueId vid;
            ValueIdSet disjuncts;

            // Multi-Index OR optimization requires that the index information
            // is maintained for disjuncts as well. So here we check if the
            // predicate is of the form A OR B OR C. If it is, then the top
            // operator is the OR operator. The optimization is considered only
            // in this case. So if the predicate has an OR on top of the item
            // expression, then we check if the disjuncts are covered by the
            // index.

            if (preds.entries() == 1)
            {
               preds.getFirst(vid);
               if (vid.getItemExpr()->getOperatorType() ==  ITM_OR)
               {
                  vid.getItemExpr()->convertToValueIdSet(disjuncts,
                                                         NULL,
                                                         ITM_OR,
                                                         FALSE);
               }
               else
                 disjuncts=preds;
            }
            else
               disjuncts=preds;

	    disjuncts.isCovered(
	      getGroupAttr()->getCharacteristicInputs(),
	      indexGA,
	      ijReferencedInputs,
	      ijCoveredPredicates,
	      ijUnCoveredExpr);

	    // we only care about predicates that are entirely covered,
	    // parts of predicates (like constants) that are covered
	    // don't help in this context
	    ijCoveredPredicates.intersectSet(disjuncts);
	  }
	  // This index does not provide all required values.
	  // However, it might be useful to join this index with another
	  // one (most likely the clustering index) to get the rest
	  // of the required values. If this is promising at all, then
	  // add this index to a list of possible index joins.
	  // In that list of possible index joins, group all indexes
	  // that provide the same set of output values (but different
	  // orders) together.
	  if (numIndexJoins_ < MAX_NUM_INDEX_JOINS AND
	      (unlimitedIndexJoinsAllowed OR NOT ijCoveredPredicates.isEmpty()))
	  {
	    // changing back to old predicate tree:
	    ValueIdSet selectionpreds;
	    if((CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) &&
	       (selectionPred().entries()))
	    {
	      ValueIdList selectionPredList(selectionPred());
	      ItemExpr *inputItemExprTree = selectionPredList.rebuildExprTree(ITM_AND,FALSE,FALSE);
	      ItemExpr * resultOld = revertBackToOldTree(CmpCommon::statementHeap(), inputItemExprTree);
	      resultOld->convertToValueIdSet(selectionpreds, NULL, ITM_AND);
	      doNotReplaceAnItemExpressionForLikePredicates(resultOld,selectionpreds,resultOld);
	    }

	      // For now, only consider indexes that covers one of the selection
	      // predicates unless control query shape is in effect.
              // NOTE: we should also consider indexes that potentially
              // could provide an interesting order or partitioning. To do
              // that, we would have to check whether their first key column
              // or any of their partitioning key columns is used.
              // For exclusive mode, any index can be called a usable index of
              // of another, only if it produces the same characteristic
              // outputs as the main index, and also both indexes have the same
              // uncovered expressions. This is because, in exclusive mode the
	      // base (clustering key) index must always be read even if the
	      // alternate index is index only, because the locks on the
	      // base index are required for exclusive mode.
	      // We can test the index only case with exclusive mode by
	      // requiring the uncovered expressions to be the same
	      // (both would be NULL for index only).

	      // we now have the following information ready:
	      // - coveredSubexpr are the values that the index can deliver
	      //   (+ clustering key columns)
	      // - unCoveredExpr are the values that the right child of the
	      //   index join should deliver (+ clustering key values)
	      // - we know the clustering key VEGies, whose VEGPredicates
	      //   serve as join predicates between the indexes
	      // - we can find out the selection predicates covered
	      //   by the index by intersecting them with coveredSubexpr

	      ValueIdSet newOutputsFromIndex(coveredSubexpr);
	      ValueIdSet newIndexPredicates(coveredSubexpr);
	      ValueIdSet newOutputsFromRightScan(unCoveredExpr);

	      newOutputsFromIndex += clusteringKeyColumns;
	      if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON )
	      {
		newOutputsFromIndex -= selectionpreds;
	      }
	      else
		newOutputsFromIndex -= selectionPred();
              newOutputsFromIndex -= getGroupAttr()->
	                               getCharacteristicInputs();
	      if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON )
	      {
		newIndexPredicates.intersectSet(selectionpreds);
		newOutputsFromRightScan -= selectionpreds;
	      }
	      else
	      {
		newIndexPredicates.intersectSet(selectionPred());
		newOutputsFromRightScan -= selectionPred();
	      }
	      newOutputsFromRightScan += clusteringKeyColumns;

              NABoolean idescAbsorbed = FALSE;


              // does another index have the same covered values?
              for (CollIndex i = 0; i < possibleIndexJoins_.entries(); i++)
                {
                  NABoolean isASupersetIndex =
                      possibleIndexJoins_[i]->outputsFromIndex_.contains(newOutputsFromIndex) &&
                      possibleIndexJoins_[i]->indexPredicates_.contains(newIndexPredicates);

                  NABoolean isASubsetIndex =
                      newOutputsFromIndex.contains(possibleIndexJoins_[i]->outputsFromIndex_) &&
                      newIndexPredicates.contains(possibleIndexJoins_[i]->indexPredicates_);

                  NABoolean isASuperOrSubsetIndex = isASupersetIndex || isASubsetIndex;

                  NABoolean produceSameIndexOutputs = isASupersetIndex && isASubsetIndex;

                  if ((possibleIndexJoins_[i]->inputsToIndex_ == referencedInputs)
		      && ((accessOptions().lockMode() != EXCLUSIVE_)
			  || possibleIndexJoins_[i]->outputsFromRightScan_ ==
			  newOutputsFromRightScan))
                    {
                      ScanIndexInfo *ixi = possibleIndexJoins_[i];

		      IndexJoinSelectivityEnum isGoodIndexJoin = INDEX_JOIN_VIABLE;
		      MdamFlags mdamFlag = idesc->pruneMdam(ixi->indexPredicates_,FALSE,
						 isGoodIndexJoin,
						 getGroupAttr(),&(ixi->inputsToIndex_));
		      IndexProperty * ixProp;
		      if(getGroupAttr()->getInputLogPropList().entries() >0)
			 ixProp = new(CmpCommon::statementHeap())
					      IndexProperty(idesc, mdamFlag, isGoodIndexJoin,
					      (getGroupAttr()->getInputLogPropList())[0]);
		      else
                        ixProp = new(CmpCommon::statementHeap())
                           IndexProperty(idesc, mdamFlag, isGoodIndexJoin);

                      if ( !tryToEliminateIndex || idesc->indexHintPriorityDelta() > 0) {

                         if ( produceSameIndexOutputs && ixi->indexPredicates_ == newIndexPredicates )
                         {
                             ixi->usableIndexes_.insert(ixProp);
                             idescAbsorbed = TRUE;
                             break;
                         } 

                      } else {

                         CANodeId tableId = tAnalysis->getNodeAnalysis()->getId();

                         // keep the index that provides the maximal coverage of the
                         // predicate. Do this only when the output from one index is
                         // the super set of the other. For example (a,b) in I1 
                         // (CREATE INDEX T1 on T(a, b)) is a superset of (a) in I2 
                         // (CREATE INDEX T2 on T(a)). 
                         if ( isASuperOrSubsetIndex && !produceSameIndexOutputs ) {
   
                             // Score the index's coverage by computing the remaining length of the 
                             // key columns not covering the index predicates. The one with remaining
                             // length of 0 is the best.

                             ValueIdSet indexCols;
                             newIndexPredicates.findAllReferencedIndexCols(indexCols);

                             Lng32 currentPrefixLen =
                                     idesc->getIndexKey().findPrefixLength(indexCols);
   
                             Lng32 currentSuffixLen = idesc->getIndexKey().entries() - currentPrefixLen;
   
                             Lng32 previousPrefixLen = 
                                  ixi->usableIndexes_[0]->getIndexDesc()
                                     ->getIndexKey().findPrefixLength(indexCols);
   
                             Lng32 previousSuffixLen = ixi->usableIndexes_[0]->getIndexDesc()
                                     ->getIndexKey().entries() - previousPrefixLen;
   
                             if ( currentSuffixLen < previousSuffixLen ) {
   
                               if ( printIndexElimination )
                                  out << "Eliminate index join heuristics 1: remove " 
                                      << ixi->usableIndexes_[0]->getIndexDesc()->getExtIndexName().data() 
                                      << endl;

                               ixi = new (CmpCommon::statementHeap())
                                          ScanIndexInfo(referencedInputs, newOutputsFromIndex,
                                          newIndexPredicates, indexJoinPreds,
                                          newOutputsFromRightScan, idesc->getIndexKey(),
                                          ixProp);
   
                                possibleIndexJoins_[i] = ixi;


                             } else {
                                // do nothing. The current index is less useful.
                                 if ( printIndexElimination )
                                    out << "Eliminate index join heuristics 1: remove " 
                                        << idesc->getExtIndexName().data() 
                                        << endl;
                               }

                             idescAbsorbed = TRUE;

                         } else 
                         // if no index is a prefix of the other and the two do not produce
                         // same output, pick one with high selectivity.
                         if ( !isASuperOrSubsetIndex && !produceSameIndexOutputs ) {

                            // two indexes do not produce the same outputs. Select
                            // one with the most selectivity.

                            CostScalar rowsToScan;

                            CostScalar currentDataAccess = 
                                computeCpuResourceForIndexJoin(tableId, idesc, 
                                                               newIndexPredicates, rowsToScan);

                            if ( rowsToScan > indexEliminationThreshold )
                              break;

                            CostScalar previousDataAccess = 
                                  computeCpuResourceForIndexJoin(tableId, 
                                                                ixi->usableIndexes_[0]->getIndexDesc(),
                                                                ixi->indexPredicates_, rowsToScan);


                            if ( currentDataAccess < previousDataAccess ) {
   
                               if ( printIndexElimination )
                                  out << "Eliminate index join heuristics 2: remove " 
                                      << ixi->usableIndexes_[0]->getIndexDesc()->getExtIndexName().data() 
                                      << endl;

                               ixi = new (CmpCommon::statementHeap()) 
                                       ScanIndexInfo(referencedInputs, newOutputsFromIndex,
                                                     newIndexPredicates, indexJoinPreds, 
                                                     newOutputsFromRightScan, idesc->getIndexKey(),
                                                     ixProp);

                               possibleIndexJoins_[i] = ixi;
                             } else {

                                // do nothing. The current index is less useful.
                                if ( printIndexElimination )
                                  out << "Eliminate index join heuristics 2: remove " 
                                      << idesc->getExtIndexName().data() << endl;
                             }

                             idescAbsorbed = TRUE;

                         } else {
   
                           // must be produceSameIndexOutputs when reach here. 
                           CMPASSERT(produceSameIndexOutputs);

                           // Another index produces the same characteristic
                           // outputs. Combine the two indexes in a single
                           // scan. Add this index to the list of indexes,
                           // everything else should be set already
                           if ( possibleIndexJoins_[i]->indexPredicates_ == newIndexPredicates &&
                                ixProp->compareIndexPromise(ixi->usableIndexes_[0]) == MORE ) 
                           {
                               if ( printIndexElimination )
                                  out << "Eliminate index join heuristics 0: remove " 
                                      << ixi->usableIndexes_[0]->getIndexDesc()->getExtIndexName().data() 
                                      << endl;

                               ixi = new (CmpCommon::statementHeap()) 
                                       ScanIndexInfo(referencedInputs, newOutputsFromIndex,
                                                     newIndexPredicates, indexJoinPreds, 
                                                     newOutputsFromRightScan, idesc->getIndexKey(),
                                                     ixProp);

                               possibleIndexJoins_[i] = ixi;

                               idescAbsorbed = TRUE;
                           }
                         }

                         break;
                      } // try to eliminate this index from consideration
                    } // found another index join with the same covered values
		} // for loop: does another index join have the same covered values?

	      if (!idescAbsorbed)
		{
		  // create a new index info struct and add this into the
		  // possible index joins list
		  IndexJoinSelectivityEnum isGoodIndexJoin = INDEX_JOIN_VIABLE;

		  MdamFlags mdamFlag = idesc->pruneMdam(newIndexPredicates,FALSE,
						 isGoodIndexJoin,
						 getGroupAttr(),&referencedInputs);

		  IndexProperty * ixProp = (getGroupAttr()->getInputLogPropList().entries() >0) ?
			new(CmpCommon::statementHeap())
					      IndexProperty(idesc, mdamFlag, isGoodIndexJoin,
					      (getGroupAttr()->getInputLogPropList())[0])
		        :
			new(CmpCommon::statementHeap())
					      IndexProperty(idesc, mdamFlag, isGoodIndexJoin);

                  ScanIndexInfo *ixi = new (CmpCommon::statementHeap()) 
                                ScanIndexInfo(referencedInputs, newOutputsFromIndex,
                                              newIndexPredicates, indexJoinPreds, 
                                              newOutputsFromRightScan, idesc->getIndexKey(),
                                              ixProp);

		  possibleIndexJoins_.insert(ixi);

		} // !idescAbsorbed 
	    } // index delivers new values
	} // not indexOnly access
    } // for each index

                               
  if ( printIndexElimination ) {
    out << "# of index join scans=" << possibleIndexJoins_.entries() << endl;
    out << "# of index only scans=" << indexOnlyIndexes_.entries() << endl;
    out << "==================" << endl;
  }

  CMPASSERT(indexOnlyIndexes_.entries() > 0);

} // Scan::addIndexInfo

void Scan::setTableAttributes(CANodeId nodeId)
{
  NodeAnalysis * nodeAnalysis = nodeId.getNodeAnalysis();

  if (nodeAnalysis == NULL)
    return;

  TableAnalysis * tableAnalysis = nodeAnalysis->getTableAnalysis();

  if (tableAnalysis == NULL)
    return;

  TableDesc * tableDesc = tableAnalysis->getTableDesc();
  const CorrName& name = tableDesc->getNATable()->getTableName();

  setTableName((CorrName &)name);
  setTableDesc(tableDesc);
  setBaseCardinality(MIN_ONE (tableDesc->getNATable()->getEstRowCount())) ;
}

NABoolean Scan::updateableIndex(IndexDesc *idx, ValueIdSet& preds, 
                                NABoolean & needsHalloweenProtection)
{
  //
  // Returns TRUE if the index (idx) can be used for a scan during an UPDATE.
  // Returns TRUE with needsHalloweenProtection also set to TRUE, if the index
  // needs a blocking sort for Halloween protection
  // Otherwise, returns FALSE to prevent use of this index. 
  // Using the index in this case requires Halloween protection, but is likely 
  // inefficient since there are no useful preds on the index key columns, so
  // we don't add this index to list of candidates.
  //

  // The conditions of when this index returns TRUE can also be expressed as
  // returns true for an index if it is 
  // a) a unique/clustering index or 
  // b) has a unique predicate on its key or 
  // c) has an equals or range predicate on all the index columns that 
  // get updated. If one of the key columns being updated has a range predicate
  // then needsHalloweenProtection is set to TRUE.
  // Note that if a key column is being updated and has no predicate on it then
  // we return FALSE.

  // preds has predicate in non-RangeSpec form, 
  // while pred will be in RangeSpec form if feature is enabled.
  ValueIdSet pred = getSelectionPredicates(), dummySet;

  SearchKey searchKey(idx->getIndexKey(),
	              idx->getOrderOfKeyValues(),
	              getGroupAttr()->getCharacteristicInputs(),
		      TRUE,
		      pred,
                      dummySet, // needed by the interface but not used here
                      idx
                      );

  // Unique index is OK to use.
  if (searchKey.isUnique())
    return TRUE;
  
  const ValueIdList colUpdated = getTableDesc()->getColUpdated();
  const ValueIdList indexKey = idx->getIndexKey();

  // Determine if the columns being updated are key columns. Each key
  // column being updated must have an associated equality clause in
  // the WHERE clause of the UPDATE for it to be used.
  for (CollIndex i = 0; i < colUpdated.entries(); i++)
  {
    ItemExpr *updateCol = colUpdated[i].getItemExpr();
    CMPASSERT(updateCol->getOperatorType() == ITM_BASECOLUMN);
    for (CollIndex j = 0; j < indexKey.entries(); j++)
    {
      ItemExpr *keyCol = indexKey[j].getItemExpr();
      ItemExpr *baseCol = ((IndexColumn*)keyCol)->getDefinition().getItemExpr();
      CMPASSERT(baseCol->getOperatorType() == ITM_BASECOLUMN);
      if (getGroupAttr()->isEmbeddedUpdate()){
        if (((BaseColumn*)updateCol)->getColNumber() ==
            ((BaseColumn*)baseCol)->getColNumber())
          return FALSE;
      }
      if ((NOT(idx->isUniqueIndex() || idx->isClusteringIndex()) && 
           (CmpCommon::getDefault(UPDATE_CLUSTERING_OR_UNIQUE_INDEX_KEY) != DF_AGGRESSIVE))
          ||
	  (CmpCommon::getDefault(UPDATE_CLUSTERING_OR_UNIQUE_INDEX_KEY) == DF_OFF))
      {
        if (((BaseColumn*)updateCol)->getColNumber() ==
            ((BaseColumn*)baseCol)->getColNumber()) 
        {
          if (preds.containsAsEquiLocalPred(baseCol->getValueId()))
            continue;
          else if (preds.containsAsRangeLocalPred(baseCol->getValueId()))
            needsHalloweenProtection = TRUE;
          else {
            needsHalloweenProtection = FALSE;
            return FALSE;
          }
        } // index key col is being updated
      } // not a clustering or unique index
    } // loop over index key cols
  } // loop over cols being updated

  return TRUE;
} // Scan::updateableIndex

NABoolean Scan::requiresHalloweenForUpdateUsingIndexScan()
{ 
  // Returns TRUE if any index that is in the list of indexes that will be 
  // added later in addIndexInfo() to drive the scan for an UPDATE requires 
  // Halloween protection. This is decided by using Scan::updateableIndex(). 
  // If this method returns TRUE we will use a sort node to prevent the 
  // "Halloween Update Problem".

  // preds are in RangeSpec form
  ValueIdSet preds = getSelectionPredicates();
  const ValueIdList & colUpdated = getTableDesc()->getColUpdated();
  const LIST(IndexDesc *) & ixlist = getTableDesc()->getIndexes();
  
  if ((colUpdated.entries() == 0) || (preds.entries() == 0) ||
      (ixlist.entries() == 1) || // this is the clustering index
      (CmpCommon::getDefault(UPDATE_CLUSTERING_OR_UNIQUE_INDEX_KEY) 
       == DF_AGGRESSIVE)) // this setting means no Halloween protection
    return FALSE;
 
  if (CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON)
  {
    ValueIdList selectionPredList(preds);
    ItemExpr *inputItemExprTree = 
      selectionPredList.rebuildExprTree(ITM_AND,FALSE,FALSE);
    ItemExpr * resultOld = revertBackToOldTree(STMTHEAP, 
                                               inputItemExprTree);
    preds.clear();
    resultOld->convertToValueIdSet(preds, NULL, ITM_AND);
    doNotReplaceAnItemExpressionForLikePredicates(resultOld,preds,
                                                  resultOld);
  }
  
  NABoolean needsHalloweenProtection ;
  for (CollIndex indexNo = 0; indexNo < ixlist.entries(); indexNo++)
  {
    IndexDesc *idx = ixlist[indexNo];
    if (idx->isClusteringIndex() || 
        (idx->isUniqueIndex() && 
         (CmpCommon::getDefault(UPDATE_CLUSTERING_OR_UNIQUE_INDEX_KEY) 
          == DF_ON)))
      continue ; // skip this idesc
    
    needsHalloweenProtection = FALSE;
    if(updateableIndex(idx, preds, needsHalloweenProtection) && 
       needsHalloweenProtection)
      return TRUE; // if even one index requires Halloween, then we add the sort
  }
  return FALSE;
}

// how many index descriptors can be used with this scan node?
CollIndex Scan::numUsableIndexes()
{
  // start with the index-only indexes
  CollIndex result = indexOnlyIndexes_.entries();

  // for each index join, count all the indexes that result in equivalent
  // characteristics inputs and outputs
  for (CollIndex i=0; i < possibleIndexJoins_.entries(); i++)
    result += possibleIndexJoins_[i]->usableIndexes_.entries();

  return result;
}

// this method works like an operator[], with values for indexNum
// between 0 and numUsableIndexes()-1. It returns the associated IndexDesc
// and - depending on whether it is an index-only scan or an index join -
// the appropriate information class (optional).
IndexDesc * Scan::getUsableIndex(CollIndex indexNum,
				 IndexProperty **indexOnlyInfo,
				 ScanIndexInfo **indexJoinInfo)
{
  IndexDesc     *result           = NULL;
  IndexProperty *locIndexOnlyInfo = NULL;
  ScanIndexInfo *locIndexJoinInfo = NULL;

  if (indexNum < indexOnlyIndexes_.entries())
    {
      // indexNum corresponds to an index-only scan
      locIndexOnlyInfo = indexOnlyIndexes_[indexNum];
      result = locIndexOnlyInfo->getIndexDesc();
    }
  else
    {
      // search for index desc "indexNum" in the index joins
      // (which is a list of sets of index descs, making this
      // method somewhat complex)
      indexNum -= indexOnlyIndexes_.entries();
      CollIndex ixJoinIx = 0;
      CollIndex numIndexJoins = possibleIndexJoins_.entries();

      if (numIndexJoins > 0)
	{
	  // loop over the list of index joins, counting index descs until
	  // we find the right index join
	  while (ixJoinIx < numIndexJoins)
	    {
	      ScanIndexInfo *si = possibleIndexJoins_[ixJoinIx];

	      if (indexNum >= si->usableIndexes_.entries())
		{
		  // not there yet, go on to the next index join
		  indexNum -= si->usableIndexes_.entries();
		  ixJoinIx++;
		}
	      else
		{
		  // now we have reached the right index join (if
		  // any), select an index
		  locIndexJoinInfo = si;
		  result = si->usableIndexes_[indexNum]->getIndexDesc();
		  break;
		}
	    }
	}
    }

  // return information or NULL for not found
  if (indexOnlyInfo)
    *indexOnlyInfo = locIndexOnlyInfo;
  if (indexJoinInfo)
    *indexJoinInfo = locIndexJoinInfo;
  return result;
}

void Scan::getRequiredVerticalPartitions
           (SET(IndexDesc  *) & requiredVPs,
            SET(ValueIdSet *) & columnsProvidedByVP) const
{
  // We get requiredVPs and columnsProvidedByVP passed to us that have
  // no entries.  We have to populate them with the vertical partitions
  // required to service the query and the columns that each of those
  // VPs will provide.  Each entry in columnsProvidedByVP is related to
  // the corresponding entry in requiredVPs
  CMPASSERT(requiredVPs.entries() == 0 &&
            columnsProvidedByVP.entries() == 0);

  const TableDesc * tableDesc = getTableDesc();
  const LIST(IndexDesc *) & allVPs = tableDesc->getVerticalPartitions();

#ifdef OLD
  // Get all the value ids that are required by the scan and its parents
  ValueIdSet requiredValueIds(getGroupAttr()->getCharacteristicOutputs());

  // VEGPreds can have two forms, an  A IS NOT NULL form and an A=B form
  // when expanded in the generator. If an index does not provide a
  // VEG member that the base table provides, a VEGPredicate could be
  // covered by the index in its IS NOT NULL form (checking a char. input
  // whether it is not null). To avoid this bug, add all the base cols
  // that contribute to VEGPredicates as explicitly required values.
  addBaseColsFromVEGPreds(requiredValueIds);

  // selection predicates are also required, add them to requiredValueIds
  requiredValueIds += getSelectionPred();

  // Remove any VEGPreds from required Values
  ValueIdSet VEGEqPreds;
  getSelectionPred().lookForVEGPredicates(VEGEqPreds);
  requiredValueIds -= VEGEqPreds;

  // The following code gets all the leaf node value ids.  It deletes the
  // characteristic input list of value ids from the leaf value ids.  It
  // does this since predicates are not pushed down to the VPs and the VPs
  // only provide outputs and do not take any inputs.  It reassigns the
  // remaining leaf value ids as those actually required from the VPs.
  // This code e.g. will only keep the b from a predicate such as b > 3
  // and the c from an expression c + 1 in the select list.
  ValueIdSet leafValues, emptySet;
  GroupAttributes emptyGA;
  requiredValueIds.getLeafValuesForCoverTest(leafValues, emptyGA, emptySet);

  leafValues -= getGroupAttr()->getCharacteristicInputs();
  requiredValueIds = leafValues;
#endif

  // -----------------------------------------------------------------
  // Accumulate the ValueIds of all VEGPredicates.
  // -----------------------------------------------------------------
  ValueIdSet VEGEqPreds;
  getSelectionPred().lookForVEGPredicates(VEGEqPreds);

  // -----------------------------------------------------------------
  // Compute the set of expressions that will be evaluated on the
  // parent. Add a VEGReference for every VEGPredicate in this set.
  // -----------------------------------------------------------------
  // remaining expressions on parent
  ValueIdSet requiredValueIdsMembers;
  RelExpr::computeValuesReqdForPredicates(VEGEqPreds,
                                 requiredValueIdsMembers);

  ValueIdSet requiredValueIds;
  requiredValueIds.replaceVEGExpressionsAndCopy(requiredValueIdsMembers);

  // ---------------------------------------------------------------------
  // Examine the set of values required by the parent (VPJoin node).
  // Replace each VEGPredicate with a VEGReferences for its VEG; if its
  // VEG contains other VEGReferences, add them to requiredValueIds.
  // ---------------------------------------------------------------------
  RelExpr::computeValuesReqdForPredicates(getGroupAttr()->getCharacteristicOutputs(),
				 requiredValueIds);

  requiredValueIds += getSelectionPred();
  requiredValueIds -= VEGEqPreds;   // delete all VEGPredicates

  // The following code gets all the leaf node value ids.  It deletes the
  // characteristic input list of value ids from the leaf value ids.  It
  // does this since predicates are not pushed down to the VPs and the VPs
  // only provide outputs and do not take any inputs.  It reassigns the
  // remaining leaf value ids as those actually required from the VPs.
  // This code e.g. will only keep the b from a predicate such as b > 3
  // and the c from an expression c + 1 in the select list.
  ValueIdSet leafValues, emptySet;
  GroupAttributes emptyGA;
  requiredValueIds.getLeafValuesForCoverTest(leafValues, emptyGA, emptySet);

  leafValues -= getGroupAttr()->getCharacteristicInputs();
  requiredValueIds = leafValues;

  // Remove all basecolumns (logical columns)
  //
  for(ValueId expr = requiredValueIds.init();
      requiredValueIds.next(expr);
      requiredValueIds.advance(expr)) {

    ItemExpr *ie = expr.getItemExpr();

    if(ie->getOperatorType() == ITM_BASECOLUMN)
      requiredValueIds -= expr;
  }

  // the values that are covered by every vertical partition (such as
  // clustering key)
  ValueIdSet alwaysCovered;
  // a list of VEGReferences to the clustering key column(s)
  ValueIdSet clusteringKeyColumns;
  // some helper variables
  ValueIdList clusteringKeyColList;
  GroupAttributes alwaysCoveredGA;
  ValueIdSet dummyReferencedInputs;
  ValueIdSet dummyUnCoveredExpr;

  // ---------------------------------------------------------------------
  // find out the subset of values that are always covered
  // ---------------------------------------------------------------------
  // get the clustering key columns and transform them into VEGies
  tableDesc->getEquivVEGCols(tableDesc->getClusteringIndex()->getIndexKey(),
                             clusteringKeyColList);
  clusteringKeyColumns = clusteringKeyColList;

  // make group attributes that get the original scan node's char.
  // inputs and the clustering key columns as outputs (every VP
  // should have the clustering key), to represent the least common
  // denominator of all VP attributes
  alwaysCoveredGA.addCharacteristicOutputs(clusteringKeyColumns);

  requiredValueIds.isCovered(
       getGroupAttr()->getCharacteristicInputs(),
       alwaysCoveredGA,
       dummyReferencedInputs,
       alwaysCovered,
       dummyUnCoveredExpr);

  // alwaysCovered now contains a set of values that should be covered
  // by every vertical partition


  ValueIdSet remainingValueIds = requiredValueIds;

  // ---------------------------------------------------------------------
  // For each vertical partition, check whether it provides any useful
  // values
  // ---------------------------------------------------------------------
  for (CollIndex indexNo = 0; indexNo < allVPs.entries(); indexNo++)
    {
      IndexDesc *idesc = allVPs[indexNo];

      ValueIdSet indexColumns(idesc->getIndexColumns());
      ValueIdSet *coveredSubexpr = new (CmpCommon::statementHeap())
                                       ValueIdSet();
      ValueIdSet noCharacteristicInputs;
      GroupAttributes vpGroupAttributes;
      NABoolean onlyOneVPRequired;

      // make group attributes for a vertical partition scan
      vpGroupAttributes.addCharacteristicOutputs(idesc->getIndexColumns());

      // does the index cover all required values, and if not, which
      // ones does it cover
      onlyOneVPRequired = requiredValueIds.isCovered(noCharacteristicInputs,
                                                     vpGroupAttributes,
                                                     dummyReferencedInputs,
                                                     *coveredSubexpr,
                                                     dummyUnCoveredExpr);

      if (onlyOneVPRequired)
        {
          // This vertical partition supplies all the required values.
          // That means we have all the required vertical partitions
          // and we are done.  In fact, if we had selected other VPs
          // before we need to clear them out along with the columns
          // they provide.
          // There should not be any selection predicates in this list
          // since they should have been eliminated from
          // requiredValueIdsby the leaf value id code earlier.
          requiredVPs.clear();
          columnsProvidedByVP.clear();
          requiredVPs.insert(idesc);
          columnsProvidedByVP.insert(coveredSubexpr);
          return;
        }
      else
        {
          if(remainingValueIds.entries() > 0) {
            coveredSubexpr->clear();

            requiredValueIds.isCovered(noCharacteristicInputs,
                                       vpGroupAttributes,
                                       dummyReferencedInputs,
                                       *coveredSubexpr,
                                       dummyUnCoveredExpr);


            // This vertical partition does not provide all required values.
            // Normally we wouldn't expect it to.  But does it provide a
            // column value other than the clustering key?  If it does, it's in!
            // We should take out the selection predicates since we will
            // not be evaluating any predicates in the VP scan.
            if ( (*coveredSubexpr != alwaysCovered) &&
                 ((*coveredSubexpr).entries() > 0) )
              {
                requiredVPs.insert(idesc);
                *coveredSubexpr -= getSelectionPred();
                columnsProvidedByVP.insert(coveredSubexpr);
              } // VP delivers column values

            remainingValueIds -= *coveredSubexpr;
          }
        } // not onlyOneVPRequired
    } // for each VP

  return;

} // Scan::getRequiredVerticalPartitions

void Scan::addBaseColsFromVEGPreds(ValueIdSet &vs) const
{
  // get all the base columns of the table (no VEGies)
  ValueIdSet baseCols(tabId_->getColumnList());

  for (ValueId x = getSelectionPred().init();
       getSelectionPred().next(x);
       getSelectionPred().advance(x))
    {
      ItemExpr *ie = x.getItemExpr();

      if (ie->getOperatorType() == ITM_VEG_PREDICATE)
	{
	  // get the VEG members
	  ValueIdSet vegMembers(
	       ((VEGPredicate *)ie)->getVEG()->getAllValues());

	  // filter out the base columns of this table that are VEG members
	  // and add them to the output parameter
	  vegMembers.intersectSet(baseCols);
	  vs += vegMembers;
	}
    }
}

void Scan::addLocalExpr(LIST(ExprNode *) &xlist,
			LIST(NAString) &llist) const
{
  RelExpr::addLocalExpr(xlist,llist);
}


NABoolean Scan::reconcileGroupAttr(GroupAttributes *newGroupAttr)
{
  addIndexInfo();
  const SET(IndexDesc *) & indexOnlyScans = deriveIndexOnlyIndexDesc();
  const SET(IndexDesc *) & indexJoinScans = deriveIndexJoinIndexDesc();
  // we add the available indexes on this scan node to the
  // new GroupAttrs availableBtreeIndexes
  newGroupAttr->addToAvailableBtreeIndexes(indexOnlyScans);
  newGroupAttr->addToAvailableBtreeIndexes(indexJoinScans);
  // This one is not actually necessary
  getGroupAttr()->addToAvailableBtreeIndexes(indexOnlyScans);
  getGroupAttr()->addToAvailableBtreeIndexes(indexJoinScans);
  // Now as usual
  return RelExpr::reconcileGroupAttr(newGroupAttr);
}

// --------------------------------------------------------------------
// 10-040128-2749 -begin
// This will compute based on the context,Current Control table setting
// and defaults.
// Input : Context
// --------------------------------------------------------------------
NABoolean Scan::isMdamEnabled(const Context *context)
{
  NABoolean mdamIsEnabled = TRUE;

    // -----------------------------------------------------------------------
    // Check the status of the enabled/disabled flag in
    // the defaults:
    // -----------------------------------------------------------------------
    if (CmpCommon::getDefault(MDAM_SCAN_METHOD) == DF_OFF)
      mdamIsEnabled = FALSE;

    // -----------------------------------------------------------------------
    // Mdam can also be disabled for a particular scan via Control
    // Query Shape. The information is passed by the context.
    // -----------------------------------------------------------------------
    if (mdamIsEnabled)
	  {
        const ReqdPhysicalProperty* propertyPtr =
            context->getReqdPhysicalProperty();
        if ( propertyPtr
             && propertyPtr->getMustMatch()
             && (propertyPtr->getMustMatch()->getOperatorType()
                 == REL_FORCE_ANY_SCAN))
		  {
            ScanForceWildCard* scanForcePtr =
              (ScanForceWildCard*)propertyPtr->getMustMatch();
            if (scanForcePtr->getMdamStatus() == ScanForceWildCard::MDAM_OFF)
              mdamIsEnabled = FALSE;
		  }
	   }

    // -----------------------------------------------------------------------
    // Mdam can also be disabled for a particular table via a Control
    // Table command.
    // -----------------------------------------------------------------------
    if (mdamIsEnabled)
	  {
        const NAString * val =
	    ActiveControlDB()->getControlTableValue(getTableName().getUgivenName(), "MDAM");
        if ((val) && (*val == "OFF")) // CT in effect
		{
	       mdamIsEnabled = FALSE;
		}
	  }
    return mdamIsEnabled;
}
// 10-040128-2749 -end

// -----------------------------------------------------------------------
// methods for class ScanIndexInfo
// -----------------------------------------------------------------------
ScanIndexInfo::ScanIndexInfo(const ScanIndexInfo & other) :
  outputsFromIndex_ (other.outputsFromIndex_),
  indexPredicates_  (other.indexPredicates_),
  joinPredicates_   (other.joinPredicates_),
  outputsFromRightScan_ (other.outputsFromRightScan_),
  transformationDone_   (other.transformationDone_),
  indexColumns_ (other.indexColumns_),
  usableIndexes_        (other.usableIndexes_)
{}

ScanIndexInfo::ScanIndexInfo(
              const ValueIdSet& inputsToIndex,
              const ValueIdSet& outputsFromIndex,
              const ValueIdSet& indexPredicates,
              const ValueIdSet& joinPredicates,
              const ValueIdSet& outputsFromRightScan,
              const ValueIdSet& indexColumns,
              IndexProperty* ixProp
             ) :
   inputsToIndex_(inputsToIndex),
   outputsFromIndex_(outputsFromIndex),
   indexPredicates_(indexPredicates),
   joinPredicates_(joinPredicates),
   outputsFromRightScan_(outputsFromRightScan),
   indexColumns_(indexColumns),
   transformationDone_(FALSE),
   usableIndexes_(CmpCommon::statementHeap())
{
   usableIndexes_.insert(ixProp);
}


// -----------------------------------------------------------------------
// methods for class FileScan
// -----------------------------------------------------------------------

FileScan::FileScan(const CorrName& tableName,
		   TableDesc * tableDescPtr,
		   const IndexDesc *indexDescPtr,
		   const NABoolean isReverseScan,
		   const Cardinality& baseCardinality,
		   StmtLevelAccessOptions& accessOpts,
		   GroupAttributes * groupAttributesPtr,
		   const ValueIdSet& selectionPredicates,
		   const Disjuncts& disjuncts,
                   const ValueIdSet& generatedCCPreds,
                   OperatorTypeEnum otype) :
     Scan (tableName, tableDescPtr, otype),
     indexDesc_(indexDescPtr),
     reverseScan_(isReverseScan),
     executorPredTree_(NULL),
     mdamKeyPtr_(NULL),
     disjunctsPtr_(&disjuncts),
     pathKeys_(NULL),
     partKeys_(NULL),
     hiveSearchKey_(NULL),
     estRowsAccessed_ (0),
     mdamFlag_(UNDECIDED),
     skipRowsToPreventHalloween_(FALSE),
     doUseSearchKey_(TRUE),
     computedNumOfActivePartitions_(-1)
{
  // Set the filescan properties:


  // Set the base cardinality to that for the logical scan
  setBaseCardinality(baseCardinality);

  // move the statement level access options
  accessOptions() = accessOpts;

  // the top node keeps the original group attributes
  setGroupAttr(groupAttributesPtr);

  // Initialize selection predicates:
  // (they are needed to set the executor predicates later in
  // pre-code gen)
  selectionPred().insert(selectionPredicates);

  // Get the predicates on the partitioning key:
  if (getIndexDesc() && getIndexDesc()->isPartitioned())
    {
      ValueIdSet externalInputs = getGroupAttr()->getCharacteristicInputs();
      ValueIdSet dummySet;
      ValueIdSet selPreds(selectionPredicates);

      // Create and set the Searchkey for the partitioning key:
      partKeys_ =  new (CmpCommon::statementHeap())
                   SearchKey(indexDesc_->getPartitioningKey(),
                                     indexDesc_->getOrderOfPartitioningKeyValues(),
                                     externalInputs,
                                     NOT getReverseScan(),
                                     selPreds,
                                     disjuncts,
                                     dummySet, // needed by interface but not used here
                                     indexDesc_
                                   );

   
      if ( indexDesc_->getPartitioningFunction() &&
           indexDesc_->getPartitioningFunction()->castToRangePartitioningFunction() ) 
      {
         const RangePartitioningFunction* rangePartFunc =
              indexDesc_->getPartitioningFunction()->castToRangePartitioningFunction();

         computedNumOfActivePartitions_ = 
             rangePartFunc->computeNumOfActivePartitions(partKeys_, tableDescPtr);
      }
    }
  setComputedPredicates(generatedCCPreds);

} // FileScan()

void FileScan::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // Assign the set of columns that belong to the index to be scanned
  // as the output values that can be produced by this scan.
  //
  outputValues.insertList( getIndexDesc()->getIndexColumns() );

  // MV --
  // Add the CurrentEpoch column as well.
  outputValues.insert(getExtraOutputColumns());
} // FileScan::getPotentialOutputValues()

NABoolean FileScan::patternMatch(const RelExpr & other) const
{
  // handle the special case of a pattern to force a
  // specific table or index
  if (other.getOperatorType() == REL_FORCE_ANY_SCAN)
    {
      ScanForceWildCard &w = (ScanForceWildCard &) other;

      if (w.getExposedName() != "")
        {
          QualifiedName wName(w.getExposedName(), 1 /* minimal 1 part name */);

          if (getTableName().getCorrNameAsString() != "")
             {
                // query uses a correlation name, compare that with the wildcard
                // as a string
                if (wName.getQualifiedNameAsAnsiString() != 
                     ToAnsiIdentifier(getTableName().getCorrNameAsString()))
                  return FALSE;
             }
          else
             {
               // no correlation name used in the query, compare catalog, schema
               // and table parts separately, if they exist in the wildcard
               const NAString& catName = wName.getCatalogName();
               const NAString& schName = wName.getSchemaName();
               const QualifiedName& x  = getTableName().
                             getExtendedQualNameObj().getQualifiedNameObj();
   
               if ((catName.length() > 0 && x.getCatalogName() != catName) ||
                   (schName.length() > 0 && x.getSchemaName()  != schName) ||
                   x.getObjectName() != wName.getObjectName())
                 return FALSE;
             }
      }

      // if an index name was specified in the wildcard, check for it
      if (w.getIndexName() != "")
      {
        NAString forcedIndexName(w.getIndexName(),
                                 CmpCommon::statementHeap());

        // The user can specify the index to be the base table in the
        // Control Query Shape statement by using the table name (object
        // name or correlation) as the index name. Ex: scan('t1','t1',..)
        // since t1 might be a correlation name, its necessary to check
        // for the corresponding object name and not the table correlation
        // name when searching for the index match.
        if (forcedIndexName == w.getExposedName())
          forcedIndexName =  ToAnsiIdentifier(
                  getTableName().getQualifiedNameObj().getObjectName()
                                             );

        // get the three-part name of the index
        const NAString &ixName = indexDesc_->getNAFileSet()->getExtFileSetName();

        // Declare a match if either the index name in w is equal to
        // indexName or if it is equal to the last part of indexName.
        //if (w.getIndexName() != ixName)
        if (forcedIndexName != ixName)
        {
          QualifiedName ixNameQ(ixName, 1);

          if ( ToAnsiIdentifier(ixNameQ.getObjectName()) != forcedIndexName )
            return FALSE;
        }
      }

      return TRUE;
  }
  else
    return RelExpr::patternMatch(other);
}

NABoolean FileScan::duplicateMatch(const RelExpr & other) const
{
  if (!Scan::duplicateMatch(other))
    return FALSE;

  FileScan &o = (FileScan &) other;

  if (//beginKeyPred_      != o.beginKeyPred_ OR
      //endKeyPred_        != o.endKeyPred_ OR
      retrievedCols_     != o.retrievedCols_ OR
      getExecutorPredicates() != o.getExecutorPredicates())
    return FALSE;

  return TRUE;
}

RelExpr * FileScan::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{

  // $$$ Function needs to be updated with new fields
  // added to the filescan.
  // If you need to use this function please update it
  // with those new fields (i.e. SearchKey *, etc's)
  // then remove the following abort
  // CMPABORT;
  FileScan *result;

  if (derivedNode == NULL)
    result = new(outHeap) FileScan(getTableName(),
				   getTableDesc(),
				   getIndexDesc(),
				   REL_FILE_SCAN,
				   outHeap);
  else
    result = (FileScan *) derivedNode;

  result->setBaseCardinality(getBaseCardinality());
  result->setEstRowsAccessed(getEstRowsAccessed());
  result->beginKeyPred_ = beginKeyPred_;
  result->endKeyPred_ = endKeyPred_;
  result->setExecutorPredicates(getExecutorPredicates());
  result->retrievedCols_ = retrievedCols_;

  return Scan::copyTopNode(result, outHeap);
}

NABoolean FileScan::isLogical() const  { return FALSE; }

NABoolean FileScan::isPhysical() const { return TRUE;  }

PlanPriority FileScan::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{

  PlanPriority result;

  // ---------------------------------------------------------------------
  // If under interactive_access mode, then give preference to plans that
  // avoid full table scans
  // Similarly if under firstN optimization mode then give preference to
  // plans that avoid full table scans
  // ---------------------------------------------------------------------
  NABoolean interactiveAccess =
    (CmpCommon::getDefault(INTERACTIVE_ACCESS) == DF_ON) OR
    ( QueryAnalysis::Instance() AND
      QueryAnalysis::Instance()->optimizeForFirstNRows());
  int indexPriorityDelta = getIndexDesc()->indexHintPriorityDelta();

  if (interactiveAccess)
  {
    if(getMdamKeyPtr())
    {
      // We have MDAM. Give this a preference
      result.incrementLevels(INTERACTIVE_ACCESS_MDAM_PRIORITY,0);
    }
    else if(getSearchKeyPtr() AND
            getSearchKeyPtr()->getKeyPredicates().entries())
    {
      // We have direct index access. Give this a preference
      result.incrementLevels(INTERACTIVE_ACCESS_PRIORITY,0);
    }
  }

  if (indexPriorityDelta && !areHintsSuppressed())
    // yes, the index is one of the indexes listed in the hint
    result.incrementLevels(indexPriorityDelta, 0);

  return result;
}

// currently only used by MV query rewrite
PlanPriority PhysicalMapValueIds::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{
  PlanPriority result;

  // is this MVI wraps one of the favorite MVs 
  // (included in the MVQR_REWRITE_CANDIDATES default)
  if (includesFavoriteMV())
  {
     result.incrementLevels(MVQR_FAVORITE_PRIORITY,0);
  }

  return result;
}

const NAString FileScan::getText() const
{
  // ---------------------------------------------------------------------
  // returns:
  //
  // file scan t c			for a primary index scan on table
  // 					t with correlation name c
  // index scan ix(t c)			for an index scan on index ix of
  //					table t
  // rev. index scan ix(t c)		if the scan goes backwards
  // ---------------------------------------------------------------------

  NAString op(CmpCommon::statementHeap());
  NAString tname(getTableName().getText(),CmpCommon::statementHeap());

  if (isSampleScan() == TRUE)
    op = "sample_";
  if (indexDesc_ == NULL OR indexDesc_->isClusteringIndex())
    {
      if (isHiveTable())
	op += "hive_scan ";
      else
	op += "file_scan ";
    }
  else {
    op += "index_scan ";
    tname = indexDesc_->getIndexName().getQualifiedNameAsString() +
	    "(" + tname + ")";
  }

  if (reverseScan_)
    op += NAString("rev ");

  return op + tname;
}

const NAString FileScan::getTypeText() const
{
  NAString descr(CmpCommon::statementHeap());
  NAString tname(getTableName().getText(),CmpCommon::statementHeap());

  if (isSampleScan() == TRUE)
    descr = "sample ";

  if (reverseScan_)
    descr += NAString("reverse ");

  if (isFullScanPresent() && !getMdamKeyPtr())
  {
    descr += "full scan ";
    if (getMdamKeyPtr())
      descr += "limited by mdam ";
  }
  else
  {
    descr += "subset scan ";
    if (getMdamKeyPtr())
      descr += "limited by mdam ";
  }

  descr += "of ";

  if (indexDesc_ == NULL OR indexDesc_->isClusteringIndex())
    descr += "table ";
  else {
    descr += "index ";
    tname = indexDesc_->getIndexName().getQualifiedNameAsString() +
	    "(" + tname + ")";
  }
  descr += tname;

  return descr;
}

void FileScan::addLocalExpr(LIST(ExprNode *) &xlist,
			    LIST(NAString) &llist) const
{
  if (getIndexDesc() != NULL)
    {
      const ValueIdList& keyColumns = getIndexDesc()->getIndexKey();
      xlist.insert(keyColumns.rebuildExprTree());
      llist.insert("key_columns");

    }

  if (executorPredTree_ != NULL OR
      NOT getExecutorPredicates().isEmpty())
    {
      if (getExecutorPredicates().isEmpty())
	xlist.insert(executorPredTree_);
      else
	xlist.insert(getExecutorPredicates().rebuildExprTree());
      llist.insert("executor_predicates");
    }

  // -----------------------------------------------------------------------
  // Display key information
  // -----------------------------------------------------------------------
  if (getMdamKeyPtr() != NULL)
    {
      // Mdam access!
      const CollIndex columns = getIndexDesc()->getIndexKey().entries();
      const CollIndex disjEntries =
        getMdamKeyPtr()->getKeyDisjunctEntries();

      // If we are in the optimizer, obtain key preds from
      // the disjuncts,
      // else obtain them from the column order list array:
      if (NOT nodeIsPreCodeGenned())
        {
          // We are in the optimizer...
          // For every disjunct
          for (CollIndex i=0;
               i < disjEntries;
               i++)
            {
              ColumnOrderList kpbc(getIndexDesc()->getIndexKey());
              getMdamKeyPtr()->getKeyPredicatesByColumn(kpbc,i);

              // gather the key predicates:
              ValueIdSet keyPreds;

              for (CollIndex j=0; j < columns; j++)
                {
                  if (kpbc[j])
                    {
                      keyPreds.insert(*(kpbc[j]));
                    }
                }

              // display this disjunct key preds. into the GUI:
              xlist.insert(keyPreds.rebuildExprTree());
              llist.insert("mdam_disjunct");
            } // for every disjunct
        }
      else
        {
          // we are after the generator...

          const ColumnOrderListPtrArray &columnOrderListPtrArray =
            getMdamKeyPtr()->getColumnOrderListPtrArray();

          // we are in the generator, obtain the key preds
          // from thr column order list:
          ValueIdSet *predsPtr = NULL;
          for (CollIndex n = 0; n < columnOrderListPtrArray.entries(); n++)
            {
              // get the list of key predicates associated with the n disjunct:
              const ColumnOrderList &columnOrderList =
                *columnOrderListPtrArray[n];
              	      // get predicates for column order i:

              // gather the key predicates:
              ValueIdSet keyPreds;

              const ValueIdSet *predsPtr = NULL;
              for (CollIndex i = 0; i < columnOrderList.entries(); i++)
                {
                  predsPtr = columnOrderList[i];
                  if (predsPtr)
                    {
                      keyPreds.insert(*predsPtr);
                    }
                }

              // display this disjunct key preds. into the GUI:
              xlist.insert(keyPreds.rebuildExprTree());
              llist.insert("mdam_disjunct");
            }
        } // mdam after the generator



    } // mdam access
  else if (getSearchKeyPtr() != NULL)   // Is Single subset access?
    {
      // yes!
      // display preds from search key only if begin/end keys are
      // not generated yet (e.g. during optimization)
      if (getBeginKeyPred().isEmpty() AND
          getEndKeyPred().isEmpty() AND
          pathKeys_ AND NOT pathKeys_->getKeyPredicates().isEmpty())
        {
          xlist.insert(pathKeys_->getKeyPredicates().rebuildExprTree());
          if (pathKeys_ == partKeys_)
            llist.insert("key_and_part_key_preds");
          else
            llist.insert("key_predicates");
        }
    }

  // display part key preds only if different from clustering key preds
  if (partKeys_ AND pathKeys_ != partKeys_ AND
      NOT partKeys_->getKeyPredicates().isEmpty())
    {
      xlist.insert(partKeys_->getKeyPredicates().rebuildExprTree());
      llist.insert("part_key_predicates");
    }

  if (NOT getBeginKeyPred().isEmpty())
    {
      xlist.insert(getBeginKeyPred().rebuildExprTree());
      llist.insert("begin_key");
    }
  if (NOT getEndKeyPred().isEmpty())
    {
      xlist.insert(getEndKeyPred().rebuildExprTree());
      llist.insert("end_key");
    }

    // xlist.insert(retrievedCols_.rebuildExprTree(ITM_ITEM_LIST));
    // llist.insert("retrieved_cols");

  RelExpr::addLocalExpr(xlist,llist);
}

const Disjuncts& FileScan::getDisjuncts() const
{
  CMPASSERT(disjunctsPtr_ != NULL);
  return *disjunctsPtr_;
}

// -----------------------------------------------------------------------
// methods for class HbaseAccess
// -----------------------------------------------------------------------

HbaseAccess::HbaseAccess(CorrName &corrName,
			 OperatorTypeEnum otype,
			 CollHeap *oHeap)
  : FileScan(corrName, NULL, NULL, otype, oHeap),
    listOfSearchKeys_(oHeap),
    snpType_(SNP_NONE),
    retHbaseColRefSet_(oHeap),
    opList_(oHeap)
{
  accessType_ = SELECT_;
  uniqueHbaseOper_ = FALSE;
  uniqueRowsetHbaseOper_ = FALSE;
}

HbaseAccess::HbaseAccess(CorrName &corrName,
			 TableDesc *tableDesc,
			 IndexDesc *idx,
                         const NABoolean isReverseScan,
                         const Cardinality& baseCardinality,
                         StmtLevelAccessOptions& accessOptions,
                         GroupAttributes * groupAttributesPtr,
                         const ValueIdSet& selectionPredicates,
                         const Disjuncts& disjuncts,
                         const ValueIdSet& generatedCCPreds,
			 OperatorTypeEnum otype,
                         CollHeap *oHeap)
  : FileScan(corrName, tableDesc, idx, 
             isReverseScan, baseCardinality,
             accessOptions, groupAttributesPtr,
             selectionPredicates, disjuncts,
             generatedCCPreds,
             otype),
    listOfSearchKeys_(oHeap),
    snpType_(SNP_NONE),
    retHbaseColRefSet_(oHeap),
    opList_(oHeap)
{
  accessType_ = SELECT_;
  //setTableDesc(tableDesc);
  uniqueHbaseOper_ = FALSE;
  uniqueRowsetHbaseOper_ = FALSE;
}

HbaseAccess::HbaseAccess(CorrName &corrName,
			 NABoolean isRW, NABoolean isCW,
			 CollHeap *oHeap)
  : FileScan(corrName, NULL, NULL, REL_HBASE_ACCESS, oHeap),
    isRW_(isRW),
    isCW_(isCW),
    listOfSearchKeys_(oHeap),
    snpType_(SNP_NONE),
    retHbaseColRefSet_(oHeap),
    opList_(oHeap)
{
  accessType_ = SELECT_;
  uniqueHbaseOper_ = FALSE;
  uniqueRowsetHbaseOper_ = FALSE;
}

HbaseAccess::HbaseAccess( OperatorTypeEnum otype,
			  CollHeap *oHeap)
  : FileScan(CorrName(), NULL, NULL, otype, oHeap),
    listOfSearchKeys_(oHeap),
    snpType_(SNP_NONE),
    retHbaseColRefSet_(oHeap),
    opList_(oHeap)
{
  accessType_ = SELECT_;
  uniqueHbaseOper_ = FALSE;
  uniqueRowsetHbaseOper_ = FALSE;
}

//! HbaseAccess::~HbaseAccess Destructor 
HbaseAccess::~HbaseAccess()
{
}

//! HbaseAccess::copyTopNode method 
RelExpr * HbaseAccess::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  HbaseAccess *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseAccess(REL_HBASE_ACCESS, outHeap);
  else
    result = (HbaseAccess *) derivedNode;

  //result->corrName_ = corrName_;
  result->accessType_ = accessType_;
  result->isRW_ = isRW_;
  result->isCW_ = isCW_;

  result->setTableDesc(getTableDesc());
  result->setIndexDesc(getIndexDesc());

  //result->setTableDesc(getTableDesc());
  result->listOfSearchKeys_ = listOfSearchKeys_;

  result->retColRefSet_ = retColRefSet_;

  result->uniqueHbaseOper_ = uniqueHbaseOper_;
  result->uniqueRowsetHbaseOper_ = uniqueRowsetHbaseOper_;

  return Scan::copyTopNode(result, outHeap);
  //  return BuiltinTableValuedFunction::copyTopNode(result, outHeap);
}

const NAString HbaseAccess::getText() const
{
  NAString op(CmpCommon::statementHeap());
  NAString tname(getTableName().getText(),CmpCommon::statementHeap());

  NAString sampleOpt(CmpCommon::statementHeap());
  if (isSampleScan())
    sampleOpt = "sample_";

  if (getIndexDesc() == NULL OR getIndexDesc()->isClusteringIndex())
    {
      if (isSeabaseTable())
	{
	  if (uniqueRowsetHbaseOper())
	    (op += "trafodion_vsbb_") += sampleOpt += "scan ";
	  else
	    (op += "trafodion_") += sampleOpt += "scan ";
	}
      else
	(op += "hbase_") += sampleOpt += "scan ";
    }
  else 
    {
      if (isSeabaseTable())
	(op += "trafodion_index_") += sampleOpt += "scan ";
      else
	(op += "hbase_index_") += sampleOpt += "scan ";
 
      tname = getIndexDesc()->getIndexName().getQualifiedNameAsString() +
	"(" + tname + ")";
    }
  
  if (getReverseScan())
    op += NAString("rev ");

  return op + tname;
}

RelExpr *HbaseAccess::bindNode(BindWA *bindWA)
{
  if (nodeIsBound())
    {
      bindWA->getCurrentScope()->setRETDesc(getRETDesc());
      return this;
    }

  //  CorrName &corrName = (CorrName&)getCorrName();
  CorrName &corrName = getTableName();
  NATable * naTable = NULL;

  naTable = bindWA->getSchemaDB()->getNATableDB()->
    get(&corrName.getExtendedQualNameObj());

  if ( !naTable || bindWA->errStatus())
    {
      *CmpCommon::diags()
	<< DgSqlCode(-1388)
	<< DgTableName(corrName.getExposedNameAsAnsiString());
      
      bindWA->setErrStatus();
      return this;
    }
  
  // Allocate a TableDesc and attach it to this.
  //
  TableDesc * td = bindWA->createTableDesc(naTable, corrName);
  if (! td || bindWA->errStatus())
    return this;
  
  setTableDesc(td);
  setIndexDesc(td->getClusteringIndex());
  if (bindWA->errStatus())
    return this;
  
  RelExpr * re = NULL;
  //  re = BuiltinTableValuedFunction::bindNode(bindWA);
  re = Scan::bindNode(bindWA);
  if (bindWA->errStatus())
    return this;

  return re;
}

void HbaseAccess::getPotentialOutputValues(
     ValueIdSet & outputValues) const
{
  outputValues.clear();

  // since this is a physical operator, it only generates the index columns
  outputValues.insertList( getIndexDesc()->getIndexColumns() );
  outputValues.insertList( getTableDesc()->hbaseTSList() );
  outputValues.insertList( getTableDesc()->hbaseVersionList() );
  
} // HbaseAccess::getPotentialOutputValues()

void
HbaseAccess::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
  if (getGroupAttr()->isPropSynthesized(inputEstLogProp))
    return;

  // Create a new Output Log Property with cardinality of 10 for now.
  EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(10));

  getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);

} // HbaseAccess::synthEstLogProp

void
HbaseAccess::synthLogProp(NormWA * normWAPtr)
{
  // Check to see whether this GA has already been associated
  // with a logExpr for synthesis.  If so, no need to resynthesize
  //  for this equivalent log. expression.
  if (getGroupAttr()->existsLogExprForSynthesis()) return;

  RelExpr::synthLogProp(normWAPtr);

} // HbaseAccess::synthLogProp()

// -----------------------------------------------------------------------
// methods for class HBaseAccessCoProcAggr
// -----------------------------------------------------------------------

HbaseAccessCoProcAggr::HbaseAccessCoProcAggr(CorrName &corrName,
					     ValueIdSet &aggregateExpr,
					     TableDesc *tableDesc,
					     IndexDesc *idx,
					     const NABoolean isReverseScan,
					     const Cardinality& baseCardinality,
					     StmtLevelAccessOptions& accessOptions,
					     GroupAttributes * groupAttributesPtr,
					     const ValueIdSet& selectionPredicates,
					     const Disjuncts& disjuncts,
					     CollHeap *oHeap)
  : HbaseAccess(corrName, tableDesc, idx, 
		isReverseScan, baseCardinality,
		accessOptions, groupAttributesPtr,
		selectionPredicates, disjuncts,
                ValueIdSet(),
		REL_HBASE_COPROC_AGGR),
    aggregateExpr_(aggregateExpr)
{
  accessType_ = COPROC_AGGR_;

}

HbaseAccessCoProcAggr::HbaseAccessCoProcAggr(CorrName &corrName,
					     ValueIdSet &aggregateExpr,
					     CollHeap *oHeap)
  : HbaseAccess(corrName, 
		REL_HBASE_COPROC_AGGR,
		oHeap),
    aggregateExpr_(aggregateExpr)
{
  accessType_ = COPROC_AGGR_;
}

HbaseAccessCoProcAggr::HbaseAccessCoProcAggr( CollHeap *oHeap)
  : HbaseAccess(REL_HBASE_COPROC_AGGR, oHeap)
{
  accessType_ = SELECT_;

  uniqueHbaseOper_ = FALSE;
  uniqueRowsetHbaseOper_ = FALSE;
}

//! HbaseAccessCoProcAggr::~HbaseAccessCoProcAggr Destructor 
HbaseAccessCoProcAggr::~HbaseAccessCoProcAggr()
{
}

//! HbaseAccessCoProcAggr::copyTopNode method 
RelExpr * HbaseAccessCoProcAggr::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  HbaseAccessCoProcAggr *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseAccessCoProcAggr(outHeap);
  else
    result = (HbaseAccessCoProcAggr *) derivedNode;

  result->aggregateExpr_ = aggregateExpr_;

  return HbaseAccess::copyTopNode(result, outHeap);
}

const NAString HbaseAccessCoProcAggr::getText() const
{
  NAString op(CmpCommon::statementHeap());
  NAString tname(getTableName().getText(),CmpCommon::statementHeap());

  op += "hbase_coproc_aggr ";

  return op + tname;
}

RelExpr *HbaseAccessCoProcAggr::bindNode(BindWA *bindWA)
{
  if (nodeIsBound())
    {
      bindWA->getCurrentScope()->setRETDesc(getRETDesc());
      return this;
    }

  RelExpr * re = NULL;
  re = HbaseAccess::bindNode(bindWA);
  if (bindWA->errStatus())
    return this;

  return re;
}

void HbaseAccessCoProcAggr::getPotentialOutputValues(
     ValueIdSet & outputValues) const
{
  outputValues.clear();

  outputValues += aggregateExpr();
} // HbaseAccessCoProcAggr::getPotentialOutputValues()

PhysicalProperty*
HbaseAccessCoProcAggr::synthPhysicalProperty(const Context* myContext,
					     const Lng32     planNumber,
                                             PlanWorkSpace  *pws)
{
  
  //----------------------------------------------------------
  // Create a node map with a single, active, wild-card entry.
  //----------------------------------------------------------
  NodeMap* myNodeMap = new(CmpCommon::statementHeap())
    NodeMap(CmpCommon::statementHeap(),
	    1,
	    NodeMapEntry::ACTIVE);
  
  //------------------------------------------------------------
  // Synthesize a partitioning function with a single partition.
  //------------------------------------------------------------
  PartitioningFunction* myPartFunc =
    new(CmpCommon::statementHeap())
    SinglePartitionPartitioningFunction(myNodeMap);
  
  PhysicalProperty * sppForMe =
    new(CmpCommon::statementHeap())
    PhysicalProperty(myPartFunc,
                     EXECUTE_IN_MASTER,
                     SOURCE_VIRTUAL_TABLE);
  
  // remove anything that's not covered by the group attributes
  sppForMe->enforceCoverageByGroupAttributes (getGroupAttr()) ;
  
  return sppForMe;
} //  HbaseAccessCoProcAggr::synthPhysicalProperty()

// -----------------------------------------------------------------------
// methods for class HbaseDelete
// -----------------------------------------------------------------------

HbaseDelete::HbaseDelete(CorrName &corrName,
			 RelExpr *scan,
			 CollHeap *oHeap)
  : Delete(corrName, NULL, REL_HBASE_DELETE, scan, NULL, NULL, NULL, oHeap),
    corrName_(corrName), 
    listOfSearchKeys_(oHeap) 
{ 
   hbaseOper() = TRUE; 
}
 

HbaseDelete::HbaseDelete(CorrName &corrName,
			 TableDesc *tableDesc,
			 CollHeap *oHeap)
  : Delete(corrName, tableDesc, REL_HBASE_DELETE, NULL, NULL, NULL, NULL,oHeap),
    corrName_(corrName),
    listOfSearchKeys_(oHeap) 
{
  hbaseOper() = TRUE;

}

HbaseDelete::HbaseDelete( CollHeap *oHeap)
  : Delete(CorrName(""), NULL, REL_HBASE_DELETE, NULL, NULL, NULL, NULL, oHeap),
    listOfSearchKeys_(oHeap) 
{
  hbaseOper() = TRUE;
}

//! HbaseDelete::~HbaseDelete Destructor 
HbaseDelete::~HbaseDelete()
{
}

//! HbaseDelete::copyTopNode method 
RelExpr * HbaseDelete::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  HbaseDelete *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseDelete(corrName_, getTableDesc(), outHeap);
  else
    result = (HbaseDelete *) derivedNode;

  result->corrName_ = corrName_;
  result->setTableDesc(getTableDesc());

  result->listOfSearchKeys_ = listOfSearchKeys_;

  result->retColRefSet_ = retColRefSet_;

  return Delete::copyTopNode(result, outHeap);
}

RelExpr *HbaseDelete::bindNode(BindWA *bindWA)
{
  if (nodeIsBound())
    {
      bindWA->getCurrentScope()->setRETDesc(getRETDesc());
      return this;
    }

  RelExpr * re = NULL;
  re = Delete::bindNode(bindWA);
  if (bindWA->errStatus())
    return this;

  return re;
}

//! HbaseDelete::getText method 
const NAString HbaseDelete::getText() const
{
  NABoolean isSeabase = 
    (getTableDesc() && getTableDesc()->getNATable() ? 
     getTableDesc()->getNATable()->isSeabaseTable() : FALSE);

  NAString text;

  if (NOT isSeabase)
    text = "hbase_";
  else
    text = "trafodion_";

  if (uniqueRowsetHbaseOper())
    text += "vsbb_";

  text += "delete";

  return text;
}

Int32 HbaseDelete::getArity() const
{
  return 0;
}

void HbaseDelete::getPotentialOutputValues(
     ValueIdSet & outputValues) const
{
  outputValues.clear();
  // since this is a physical operator, it only generates the index columns
  if (getScanIndexDesc())
    outputValues.insertList(getScanIndexDesc()->getIndexColumns());
} // HbaseDelete::getPotentialOutputValues()

// -----------------------------------------------------------------------
// methods for class HbaseUpdate
// -----------------------------------------------------------------------

HbaseUpdate::HbaseUpdate(CorrName &corrName,
			 RelExpr *scan,
			 CollHeap *oHeap)
  : UpdateCursor(corrName, NULL, REL_HBASE_UPDATE, scan, oHeap),
    corrName_(corrName), 
    listOfSearchKeys_(oHeap) 
{ 
   hbaseOper() = TRUE; 
}
 

HbaseUpdate::HbaseUpdate(CorrName &corrName,
			 TableDesc *tableDesc,
			 CollHeap *oHeap)
  : UpdateCursor(corrName, tableDesc, REL_HBASE_UPDATE, NULL, oHeap),
    corrName_(corrName),
    listOfSearchKeys_(oHeap) 
{
  hbaseOper() = TRUE;

}

HbaseUpdate::HbaseUpdate( CollHeap *oHeap)
  : UpdateCursor(CorrName(""), NULL, REL_HBASE_UPDATE, NULL, oHeap),
    listOfSearchKeys_(oHeap) 
{
  hbaseOper() = TRUE;
}

//! HbaseUpdate::~HbaseUpdate Destructor 
HbaseUpdate::~HbaseUpdate()
{
}

//! HbaseUpdate::copyTopNode method 
RelExpr * HbaseUpdate::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  HbaseUpdate *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseUpdate(corrName_, getTableDesc(), outHeap);
  else
    result = (HbaseUpdate *) derivedNode;

  result->corrName_ = corrName_;
  result->setTableDesc(getTableDesc());

  result->listOfSearchKeys_ = listOfSearchKeys_;

  result->retColRefSet_ = retColRefSet_;

  return Update::copyTopNode(result, outHeap);
}

RelExpr *HbaseUpdate::bindNode(BindWA *bindWA)
{
  if (nodeIsBound())
    {
      bindWA->getCurrentScope()->setRETDesc(getRETDesc());
      return this;
    }

  RelExpr * re = NULL;
  re = Update::bindNode(bindWA);
  if (bindWA->errStatus())
    return this;

  return re;
}

//! HbaseUpdate::getText method 
const NAString HbaseUpdate::getText() const
{
  NABoolean isSeabase = 
    (getTableDesc() ? getTableDesc()->getNATable()->isSeabaseTable() : FALSE);

  NAString text;
  if (isMerge())
    {
      text = (isSeabase ? "trafodion_merge" : "hbase_merge");
    }
  else
    {
      if (NOT isSeabase)
	text = "hbase_";
      else
	text = "trafodion_";
      
      if (uniqueRowsetHbaseOper())
	text += "vsbb_";
      
      text += "update";
    }
  
  return text;
}

Int32 HbaseUpdate::getArity() const
{
  return 0;
}

void HbaseUpdate::getPotentialOutputValues(
     ValueIdSet & outputValues) const
{
  outputValues.clear();
  // Include the index columns from the original Scan, if any
  if (getScanIndexDesc())
    outputValues.insertList(getScanIndexDesc()->getIndexColumns());

  // Include the index columns from the updated table, if any
  if (getIndexDesc())
  outputValues.insertList (getIndexDesc()->getIndexColumns());

} // HbaseUpdate::getPotentialOutputValues()


// -----------------------------------------------------------------------
// Member functions for DP2 Scan
// -----------------------------------------------------------------------
DP2Scan::DP2Scan(const CorrName& tableName,
		 TableDesc * tableDescPtr,
		 const IndexDesc *indexDescPtr,
		 const NABoolean isReverseScan,
		 const Cardinality& baseCardinality,
		 StmtLevelAccessOptions& accessOpts,
		 GroupAttributes * groupAttributesPtr,
		 const ValueIdSet& selectionPredicates,
		 const Disjuncts& disjuncts)
       :  FileScan(tableName,
		   tableDescPtr,
		   indexDescPtr,
		   isReverseScan,
		   baseCardinality,
		   accessOpts,
		   groupAttributesPtr,
		   selectionPredicates,
		   disjuncts,
                   ValueIdSet())
{
}

// --------------------------------------------------
// methods for class Describe
// --------------------------------------------------
void Describe::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // Assign the set of columns that belong to the index to be scanned
  // as the output values that can be produced by this scan.
  //
  outputValues.insertList( getTableDesc()->getClusteringIndex()->getIndexColumns() );
} // Describe::getPotentialOutputValues()

// -----------------------------------------------------------------------
// methods for class RelRoot
// -----------------------------------------------------------------------

RelRoot::RelRoot(RelExpr *input,
		 OperatorTypeEnum otype,
		 ItemExpr *compExpr,
		 ItemExpr *orderBy,
		 ItemExpr *updateCol,
		 RelExpr *reqdShape,
		 CollHeap *oHeap)
  : RelExpr(otype, input, NULL, oHeap),
    compExprTree_(compExpr),
    orderByTree_(orderBy),
    updateColTree_(updateCol),
    reqdShape_(reqdShape),
    viewStoiList_(CmpCommon::statementHeap()),
    ddlStoiList_(CmpCommon::statementHeap()),
    stoiUdrList_(CmpCommon::statementHeap()),
    udfList_(CmpCommon::statementHeap()),
    securityKeySet_(CmpCommon::statementHeap()),
    trueRoot_(FALSE),
    subRoot_(FALSE),
    displayTree_(FALSE),
    outputVarCnt_(-1),
    inputVarTree_(NULL),
    outputVarTree_(NULL),
    updatableSelect_(TRUE),
    updateCurrentOf_(FALSE),
    currOfCursorName_(NULL),
    rollbackOnError_(FALSE),
    readOnlyTransIsOK_(FALSE),
    needFirstSortedRows_(FALSE),
    numSimpleVar_(0),
    numHostVar_(0),
    childOperType_(NO_OPERATOR_TYPE),
    hostArraysArea_(NULL),
    assignmentStTree_(NULL),
    assignList_(NULL),
    isRootOfInternalRefresh_(FALSE),
    isQueryNonCacheable_(FALSE),
    pMvBindContextForScope_(NULL),
    parentForRowsetReqdOrder_(NULL),
    isEmptySelectList_(FALSE),
    isDontOpenNewScope_(FALSE),
    triggersList_(NULL),
    spOutParams_(NULL),
    downrevCompileMXV_(COM_VERS_CURR_PLAN),
    numExtractStreams_(0),
    numBMOs_(0),
    BMOsMemoryUsage_(0),
    nBMOsMemoryUsage_(0),
    uninitializedMvList_(NULL),
    allOrderByRefsInGby_(FALSE),
    avoidHalloween_(FALSE),
    containsOnStatementMV_(FALSE),
    containsLRU_(FALSE),
    disableESPParallelism_(FALSE),
    hasOlapFunctions_(FALSE),
    hasTDFunctions_(FALSE),
    isAnalyzeOnly_(FALSE),
    hasMandatoryXP_(FALSE),
    partReqType_(ANY_PARTITIONING),
    partitionByTree_(NULL),
    predExprTree_(NULL),
    firstNRowsParam_(NULL),
    flags_(0)
{
  accessOptions().accessType() = TransMode::ACCESS_TYPE_NOT_SPECIFIED_;
  accessOptions().lockMode() = LOCK_MODE_NOT_SPECIFIED_;
  isCIFOn_ = FALSE;
}

RelRoot::RelRoot(RelExpr *input,
		 TransMode::AccessType at,
		 LockMode lm,
		 OperatorTypeEnum otype,
		 ItemExpr *compExpr,
		 ItemExpr *orderBy,
		 ItemExpr *updateCol,
		 RelExpr *reqdShape,
		 CollHeap *oHeap)
  : RelExpr(otype, input, NULL, oHeap),
    compExprTree_(compExpr),
    orderByTree_(orderBy),
    updateColTree_(updateCol),
    reqdShape_(reqdShape),
    viewStoiList_(CmpCommon::statementHeap()),
    ddlStoiList_(CmpCommon::statementHeap()),
    stoiUdrList_(CmpCommon::statementHeap()),
    udfList_(CmpCommon::statementHeap()),
    securityKeySet_(CmpCommon::statementHeap()),
    trueRoot_(FALSE),
    subRoot_(FALSE),
    displayTree_(FALSE),
    outputVarCnt_(-1),
    inputVarTree_(NULL),
    outputVarTree_(NULL),
    updatableSelect_(TRUE),
    updateCurrentOf_(FALSE),
    currOfCursorName_(NULL),
    rollbackOnError_(FALSE),
    readOnlyTransIsOK_(FALSE),
    needFirstSortedRows_(FALSE),
    numSimpleVar_(0),
    numHostVar_(0),
    childOperType_(NO_OPERATOR_TYPE),
    hostArraysArea_(NULL),
    assignmentStTree_(NULL),
    assignList_(NULL),
    isRootOfInternalRefresh_(FALSE),
    isQueryNonCacheable_(FALSE),
    pMvBindContextForScope_(NULL),
    parentForRowsetReqdOrder_(NULL),
    isEmptySelectList_(FALSE),
    isDontOpenNewScope_(FALSE),
    triggersList_(NULL),
    spOutParams_(NULL),
    downrevCompileMXV_(COM_VERS_CURR_PLAN),
    numExtractStreams_(0),
    numBMOs_(0),
    BMOsMemoryUsage_(0),
    nBMOsMemoryUsage_(0),
    uninitializedMvList_(NULL),
    allOrderByRefsInGby_(FALSE),
    avoidHalloween_(FALSE),
    containsOnStatementMV_(FALSE),
    containsLRU_(FALSE),
    disableESPParallelism_(FALSE),
    hasOlapFunctions_(FALSE),
    hasTDFunctions_(FALSE),
    isAnalyzeOnly_(FALSE),
    hasMandatoryXP_(FALSE),
    partReqType_(ANY_PARTITIONING),
    partitionByTree_(NULL),
    predExprTree_(NULL),
    firstNRowsParam_(NULL),
    flags_(0)
{
  accessOptions().accessType() = at;
  accessOptions().lockMode()   = lm;
  isCIFOn_ = FALSE;
}

// Why not just use the default copy ctor that C++ provides automatically, ##
// rather than having to maintain this??? 			##
// Is it because of the "numXXXVar_(0)" lines below, 		##
// or should those be   "numXXXVar_(other.numXXXVar_)" ?	##
RelRoot::RelRoot(const RelRoot & other)
  : RelExpr(REL_ROOT, other.child(0)),
    compExprTree_(other.compExprTree_),
    orderByTree_(other.orderByTree_),
    updateColTree_(other.updateColTree_),
    reqdShape_(other.reqdShape_),
    viewStoiList_(other.viewStoiList_),
    ddlStoiList_(other.ddlStoiList_),
    stoiUdrList_(other.stoiUdrList_),
    udfList_(other.udfList_),
    securityKeySet_(other.securityKeySet_),
    trueRoot_(other.trueRoot_),
    subRoot_(other.subRoot_),
    displayTree_(other.displayTree_),
    outputVarCnt_(other.outputVarCnt_),
    inputVarTree_(other.inputVarTree_),
    outputVarTree_(other.outputVarTree_),
    updatableSelect_(other.updatableSelect_),
    updateCurrentOf_(other.updateCurrentOf_),
    currOfCursorName_(other.currOfCursorName_),
    rollbackOnError_(other.rollbackOnError_),
    readOnlyTransIsOK_(other.readOnlyTransIsOK_),
    needFirstSortedRows_(other.needFirstSortedRows_),
    isRootOfInternalRefresh_(other.isRootOfInternalRefresh_),
    isQueryNonCacheable_(other.isQueryNonCacheable_),
    pMvBindContextForScope_(other.pMvBindContextForScope_),
    isEmptySelectList_(other.isEmptySelectList_),
    isDontOpenNewScope_(other.isDontOpenNewScope_),
    //    oltOptInfo_(other.oltOptInfo_),
    numSimpleVar_(0),						//## bug?
    numHostVar_(0),						//## bug?
    childOperType_(other.childOperType_),
    hostArraysArea_(other.hostArraysArea_),
    assignmentStTree_(other.assignmentStTree_),
    assignList_(other.assignList_),
    triggersList_(other.triggersList_),
    compExpr_(other.compExpr_),
    reqdOrder_(other.reqdOrder_),
    partArrangement_(other.partArrangement_),
    updateCol_(other.updateCol_),
    inputVars_(other.inputVars_),
    accessOptions_(other.accessOptions_),
    pkeyList_(other.pkeyList_),
    rowsetReqdOrder_(other.rowsetReqdOrder_),
    parentForRowsetReqdOrder_(other.parentForRowsetReqdOrder_),
    spOutParams_ (NULL), // Raj P - 12/2000 - stored procedures (for java)
    downrevCompileMXV_(COM_VERS_CURR_PLAN),
    uninitializedMvList_(other.uninitializedMvList_),
    allOrderByRefsInGby_(other.allOrderByRefsInGby_),
    numExtractStreams_(other.numExtractStreams_),
    numBMOs_(other.numBMOs_),
    BMOsMemoryUsage_(other.BMOsMemoryUsage_),
    nBMOsMemoryUsage_(other.nBMOsMemoryUsage_),
    avoidHalloween_(other.avoidHalloween_),
    disableESPParallelism_(other.disableESPParallelism_),
    containsOnStatementMV_(other.containsOnStatementMV_),
    containsLRU_(other.containsLRU_),
    hasOlapFunctions_(other.hasOlapFunctions_),
    hasTDFunctions_(other.hasTDFunctions_ ),
    isAnalyzeOnly_(FALSE),
    hasMandatoryXP_(other.hasMandatoryXP_),
    partReqType_(other.partReqType_),
    partitionByTree_(other.partitionByTree_),
    isCIFOn_(other.isCIFOn_),
    predExprTree_(other.predExprTree_),
    firstNRowsParam_(other.firstNRowsParam_),
    flags_(other.flags_)
{
  oltOptInfo() = ((RelRoot&)other).oltOptInfo();
  setRETDesc(other.getRETDesc());
}

RelRoot::~RelRoot()
{
  //	Explicitly deleting our members is deliberately not being done --
  //	we should allocate all RelExpr's on the appropriate heap
  //	(as a NABasicObject) and free the (stmt) heap all in one blow.
  // delete compExprTree_;
  // delete orderByTree_;
  // delete reqdShape_;
  // delete inputVarTree_;
}

Int32 RelRoot::getArity() const { return 1; }

void RelRoot::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // Assign the select list as the outputs
  //
  outputValues.insertList(compExpr());
} // RelRoot::getPotentialOutputValues()


void RelRoot::pushdownCoveredExpr(const ValueIdSet & outputExpr,
                                  const ValueIdSet & newExternalInputs,
                                  ValueIdSet & predicatesOnParent,
				  const ValueIdSet * setOfValuesReqdByParent,
				  Lng32   //childIndex ignored
		                 )
{
  //---------------------------------------------------------------------
  // case 10-030708-7671: In the case of a cast, the RelRoot operator
  // needs to ask for original expression it is casting; if it asks for
  // the cast expression, the child may be incapable of producing it. For
  // example, a nested join operator can't produce the cast expression,
  // unless produced by its children
  //---------------------------------------------------------------------
  ValueIdSet originalOutputs = getGroupAttr()->getCharacteristicOutputs();
  ValueIdSet updateOutputs(originalOutputs);

  updateOutputs.replaceCastExprWithOriginal(originalOutputs, this);


  ValueIdSet myCharInput = getGroupAttr()->getCharacteristicInputs();

  // since the orderby list is not a subset of the select list include it.
  ValueIdSet allMyExpr;
  ValueIdList orderByList = reqdOrder();
  allMyExpr.insertList(orderByList);

  // add the primary key columns, if they are to be returned.
  if (updatableSelect() == TRUE)
  {
    allMyExpr.insertList(pkeyList());
  }
  // ---------------------------------------------------------------------
  RelExpr::pushdownCoveredExpr(updateOutputs,
                               myCharInput,
                               predicatesOnParent,
			       &allMyExpr
                               );

  // All expressions should have been pushed
  CMPASSERT(predicatesOnParent.isEmpty());

} // RelRoot::pushdownCoveredExpr

const NAString RelRoot::getText() const
{
	NAString result("root");

#ifdef DEBUG_TRIGGERS
	char totalNodes[20];
	sprintf(totalNodes, "(total %d nodes)", nodeCount());
	result += totalNodes;

	if (isDontOpenNewScope())
		result += "(no_scope)";

	if (isEmptySelectList())
		result += "(empty_select_list)";
#endif

	return result;
}

HashValue RelRoot::topHash()
{
  HashValue result = RelExpr::topHash();

  // it's not (yet) needed to produce a really good hash value for this node
  result ^= compExpr_.entries();
  result ^= inputVars_.entries();
  // result ^= compExpr_;
  // result ^= inputVars_;
  return result;
}

NABoolean RelRoot::duplicateMatch(const RelExpr & other) const
{
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  RelRoot &o = (RelRoot &) other;

  if (NOT (compExpr_ == o.compExpr_) OR
      NOT (inputVars_ == o.inputVars_))
    return FALSE;

  if (avoidHalloween_ != o.avoidHalloween_)
    return FALSE;

  if (disableESPParallelism_ != o.disableESPParallelism_)
    return FALSE;

  if (isAnalyzeOnly_ != o.isAnalyzeOnly_)
    return FALSE;

  return TRUE;
}

RelExpr * RelRoot::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelRoot *result;

  if (derivedNode == NULL)
    result = new (outHeap) RelRoot(NULL,
				   getOperatorType(),
				   NULL,
				   NULL,
				   NULL,
				   NULL,
				   outHeap);
  else
    result = (RelRoot *) derivedNode;

  if (compExprTree_ != NULL)
    result->compExprTree_ = compExprTree_->copyTree(outHeap)->castToItemExpr();

  if (inputVarTree_ != NULL)
    result->inputVarTree_ = inputVarTree_->copyTree(outHeap)->castToItemExpr();

  if (outputVarTree_ != NULL)
    result->outputVarTree_ = outputVarTree_->copyTree(outHeap)->castToItemExpr();

  if (predExprTree_ != NULL)
    result->predExprTree_ = predExprTree_->copyTree(outHeap)->castToItemExpr();

  result->compExpr_ = compExpr_;
  result->inputVars_ = inputVars_;

  result->accessOptions_ = accessOptions_;

  result->updatableSelect_ = updatableSelect_;
  result->updateCurrentOf_ = updateCurrentOf_;
  if (currOfCursorName())
    result->currOfCursorName_ = currOfCursorName()->copyTree(outHeap)->castToItemExpr();

  result->rollbackOnError_ = rollbackOnError_;

  result->isEmptySelectList_ = isEmptySelectList_;
  result->isDontOpenNewScope_ = isDontOpenNewScope_;

  result->oltOptInfo() = oltOptInfo();

  result->childOperType_ = childOperType_;

  result->rowsetReqdOrder_ = rowsetReqdOrder_;
  result->parentForRowsetReqdOrder_ = parentForRowsetReqdOrder_;

  // Raj P - 12/2000 stored procedures (for java)
  if ( spOutParams_ )
  {
    result->spOutParams_ = new ItemExprList (outHeap);
    (*result->spOutParams_) = *spOutParams_;
  }

  if( uninitializedMvList_ )
  {
      result->uninitializedMvList_ = new UninitializedMvNameList (outHeap);
      result->uninitializedMvList_->insert( *uninitializedMvList_ );
  }

  result->setDownrevCompileMXV(getDownrevCompileMXV());
  result->numExtractStreams_ = numExtractStreams_;
  result->allOrderByRefsInGby_ = allOrderByRefsInGby_;

  result->avoidHalloween_ = avoidHalloween_;

  result->disableESPParallelism_ = disableESPParallelism_;

  result->containsOnStatementMV_ = containsOnStatementMV_;

  result->hasOlapFunctions_ = hasOlapFunctions_;

  result->containsLRU_ = containsLRU_;

  result->isAnalyzeOnly_ = isAnalyzeOnly_;

  result->hasMandatoryXP_ = hasMandatoryXP_ ;

  if (partitionByTree_ != NULL)
    result->partitionByTree_ = partitionByTree_->copyTree(outHeap)->castToItemExpr();
  
  result->partReqType_ = partReqType_ ;

  result->isQueryNonCacheable_ = isQueryNonCacheable_; 

  result->firstNRowsParam_ = firstNRowsParam_;

  result->flags_ = flags_;

  return RelExpr::copyTopNode(result, outHeap);
}

void RelRoot::addCompExprTree(ItemExpr *compExpr)
{
  ExprValueId c = compExprTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(compExpr);
  compExprTree_ = c.getPtr();
}

ItemExpr * RelRoot::removeCompExprTree()
{
  ItemExpr * result = compExprTree_;

  compExprTree_ = NULL;

  return result;
}

void RelRoot::addPredExprTree(ItemExpr *predExpr)
{
  ExprValueId c = predExprTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(predExpr);
  predExprTree_ = c.getPtr();
}

ItemExpr * RelRoot::removePredExprTree()
{
  ItemExpr * result = predExprTree_;

  predExprTree_ = NULL;

  return result;
}

void RelRoot::addInputVarTree(ItemExpr *inputVar)
{
  ExprValueId c = inputVarTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(inputVar);
  inputVarTree_ = c.getPtr();
}

void RelRoot::addAtTopOfInputVarTree(ItemExpr *inputVar)
{
  ExprValueId c = inputVarTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insertAtTop(inputVar);
  inputVarTree_ = c.getPtr();
}

ItemExpr * RelRoot::removeInputVarTree()
{
  ItemExpr * result = inputVarTree_;

  inputVarTree_ = NULL;

  return result;
}

void RelRoot::addOutputVarTree(ItemExpr *outputVar)
{
  if (!outputVarTree_) {
    outputVarTree_ = new(CmpCommon::statementHeap()) ItemList(outputVar, NULL);
  }
  else {
    ItemExpr *start = outputVarTree_;
    while (start->child(1)) {
	  start = start->child(1);
    }
    start->child(1) = new(CmpCommon::statementHeap()) ItemList(outputVar, NULL);
  }
}

// Used by Assignment Statement in a Compound Statement. It adds a host variable
// to assignmentStTree_
void RelRoot::addAssignmentStTree(ItemExpr *inputOutputVar)
{
  if (!assignmentStTree_) {
     assignmentStTree_ = new(CmpCommon::statementHeap()) ItemList(inputOutputVar, NULL);
  }
  else {
    ItemExpr *start = assignmentStTree_;
    while (start->child(1)) {
       start = start->child(1);
    }
    start->child(1) = new(CmpCommon::statementHeap()) ItemList(inputOutputVar, NULL);
  }
}

ItemExpr * RelRoot::removeOutputVarTree()
{
  ItemExpr * result = outputVarTree_;

  outputVarTree_ = NULL;

  return result;
}

void RelRoot::addOrderByTree(ItemExpr *orderBy)
{
  ExprValueId c = orderByTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(orderBy);
  orderByTree_ = c.getPtr();
}

ItemExpr * RelRoot::removeOrderByTree()
{
  ItemExpr * result = orderByTree_;

  orderByTree_ = NULL;

  return result;
}

void RelRoot::addPartitionByTree(ItemExpr *partBy)
{
  ExprValueId c = partitionByTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(partBy);
  partitionByTree_ = c.getPtr();
}

ItemExpr * RelRoot::removePartitionByTree()
{
  ItemExpr * result = partitionByTree_;

  partitionByTree_ = NULL;

  return result;
}

void RelRoot::addUpdateColTree(ItemExpr *updateCol)
{
  ExprValueId c = updateColTree_;

  ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(updateCol);
  updateColTree_ = c.getPtr();
}

ItemExpr * RelRoot::removeUpdateColTree()
{
  ItemExpr * result = updateColTree_;

  updateColTree_ = NULL;

  return result;
}

void RelRoot::addLocalExpr(LIST(ExprNode *) &xlist,
			   LIST(NAString) &llist) const
{
  if (compExprTree_ != NULL OR
      compExpr_.entries() == 0)
    xlist.insert(compExprTree_);
  else
    xlist.insert(compExpr_.rebuildExprTree(ITM_ITEM_LIST));
  llist.insert("select_list");

  if (inputVarTree_ != NULL OR
      inputVars_.entries() > 0)
    {
      if (inputVars_.entries() == 0)
	xlist.insert(inputVarTree_);
      else
	xlist.insert(inputVars_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("input_variables");
    }

  if (orderByTree_ != NULL OR
      reqdOrder_.entries() > 0)
    {
      if (reqdOrder_.entries() == 0)
	xlist.insert(orderByTree_);
      else
	xlist.insert(reqdOrder_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("order_by");
    }

    if ((partitionByTree_ != NULL) OR 
        (partArrangement_.entries() > 0))
    {
      if (partArrangement_.entries() == 0)
	xlist.insert(partitionByTree_);
      else
	xlist.insert(partArrangement_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("partition_by");
    }


  if (updateColTree_ != NULL OR
      updateCol_.entries() > 0)
    {
      if (updateCol_.entries() == 0)
	xlist.insert(updateColTree_);
      else
	xlist.insert(updateCol_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("update_col");
    }

  if (reqdShape_ != NULL)
    {
      xlist.insert(reqdShape_);
      llist.insert("must_match");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

//----------------------------------------------------------------------------
//++ MV OZ
NABoolean RelRoot::hasMvBindContext() const
{
  return (NULL != pMvBindContextForScope_) ? TRUE : FALSE;
}

MvBindContext * RelRoot::getMvBindContext() const
{
  return pMvBindContextForScope_;
}

void RelRoot::setMvBindContext(MvBindContext * pMvBindContext)
{
  pMvBindContextForScope_ = pMvBindContext;
}

NABoolean RelRoot::addOneRowAggregates(BindWA* bindWA, NABoolean forceGroupByAgg)
{
  NABoolean groupByAggNodeAdded = FALSE;
  RelExpr * childOfRoot = child(0);
  GroupByAgg *aggNode = NULL;
  // If the One Row Subquery is already enforced by a scalar aggregate
  // then we do not need to add an additional one row aggregate. The exceptions to
  // this rule is if we have count(t1.a) where t1 itself is a one row subquery.
  // Note that the count is needed in order to have a groupby below the root node.
  // See soln. 10-071105-8680 
  // Another exception is when the select list has say max(a) + select a from t1
  // In this case there is a onerowsubquery in the select list but it is a child
  // of BiArith. Due to all these exceptions we are simply going to scan the select 
  // list. As soon as we find something other than an aggregate we take the safe
  // way out and add a one row aggregate.
  // Also if the groupby is non scalar then we need to add a one row aggregate.
  // Also if we have select max(a) + select b from t1 from t2;
  // Still another exception is if there is a [last 0] on top of this node. We
  // need an extra GroupByAgg node with one row aggregates in this case so
  // we can put the FirstN node underneath that.
  if (!forceGroupByAgg &&
      (childOfRoot->getOperatorType() == REL_GROUPBY))
    {
      aggNode = (GroupByAgg *)childOfRoot;

      if (!aggNode->groupExpr().isEmpty())
        aggNode = NULL;

      // Check to see if the compExpr contains a subquery.
      for (CollIndex i=0; i < compExpr().entries(); i++) 
	if (compExpr()[i].getItemExpr()->containsOpType(ITM_ROW_SUBQUERY))
        {
	  aggNode = NULL ;
          break;
        }

    }
  if (aggNode)
    return groupByAggNodeAdded;

  const RETDesc *oldTable = getRETDesc();
  RETDesc *resultTable = new(bindWA->wHeap()) RETDesc(bindWA);
  
  
  // Transform select list such that that each item has a oneRow parent.
  for (CollIndex selectListIndex=0; 
       selectListIndex < compExpr().entries(); 
       selectListIndex++)
  {
  
    ItemExpr *colExpr = compExpr()[selectListIndex].getItemExpr();

    // Build a new OneRow aggregate on top of the existing expression.
    ItemExpr *newColExpr = new(bindWA->wHeap()) 
      Aggregate(ITM_ONEROW, colExpr);

    newColExpr->bindNode(bindWA);

    ColumnNameMap *xcnmEntry = oldTable->findColumn(compExpr()[selectListIndex]);

    if (xcnmEntry)	// ## I don't recall when this case occurs...
      resultTable->addColumn(bindWA,
	  		     xcnmEntry->getColRefNameObj(),
	  		     newColExpr->getValueId(),
			     USER_COLUMN,
			     xcnmEntry->getColumnDesc()->getHeading());
    else
    {
      ColRefName colRefName;
      resultTable->addColumn(bindWA,
	                      colRefName,
			      newColExpr->getValueId());
    }

    // Replace the select list expression with the new one.
    compExpr()[selectListIndex] = newColExpr->getValueId();
  }
 

  ValueIdSet aggregateExpr(compExpr()) ;
  GroupByAgg *newGrby = NULL;
  newGrby = new(bindWA->wHeap())
	                 GroupByAgg(childOfRoot, aggregateExpr);

  newGrby->bindNode(bindWA) ;
  child(0) = newGrby ;
  groupByAggNodeAdded = TRUE;
  // Set the return descriptor
  //
  setRETDesc(resultTable);

  return groupByAggNodeAdded;
}
// -----------------------------------------------------------------------
// member functions for class PhysicalRelRoot
// -----------------------------------------------------------------------

NABoolean PhysicalRelRoot::isLogical()  const { return FALSE; }

NABoolean PhysicalRelRoot::isPhysical() const { return TRUE; }


// -----------------------------------------------------------------------
// methods for class Tuple
// -----------------------------------------------------------------------

THREAD_P Lng32 Tuple::idCounter_(0);

Tuple::Tuple(const Tuple & other) : RelExpr(other.getOperatorType())
{
  selectionPred() = other.getSelectionPred();

  tupleExprTree_ = other.tupleExprTree_;
  tupleExpr_ = other.tupleExpr_;
  rejectPredicates_ = other.rejectPredicates_;
  id_ = other.id_;
}

Tuple::~Tuple() {}

Int32 Tuple::getArity() const { return 0; }

// -----------------------------------------------------------------------
// A virtual method for computing output values that an operator can
// produce potentially.
// -----------------------------------------------------------------------
void Tuple::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  outputValues.insertList( tupleExpr() );
} // Tuple::getPotentialOutputValues()

HashValue Tuple::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= tupleExpr_.entries();

  return result;
}

NABoolean Tuple::duplicateMatch(const RelExpr & other) const
{
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  Tuple &o = (Tuple &) other;

  if (NOT (tupleExpr_ == o.tupleExpr_))
    return FALSE;

  if (NOT (id_ == o.id_))
    return FALSE;

  return TRUE;
}

RelExpr * Tuple::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Tuple *result;

  if (derivedNode == NULL)
    result = new (outHeap) Tuple(NULL,outHeap);
  else
    result = (Tuple *) derivedNode;

  if (tupleExprTree_ != NULL)
    result->tupleExprTree_ = tupleExprTree_->copyTree(outHeap)->castToItemExpr();

  result->tupleExpr_ = tupleExpr_;

  result->id_ = id_;

  return RelExpr::copyTopNode(result, outHeap);
}

void Tuple::addTupleExprTree(ItemExpr *tupleExpr)
{
  ExprValueId t = tupleExprTree_;

  ItemExprTreeAsList(&t, ITM_ITEM_LIST).insert(tupleExpr);
  tupleExprTree_ = t.getPtr();
}

ItemExpr * Tuple::removeTupleExprTree()
{
  ItemExpr * result = tupleExprTree_;

  tupleExprTree_ = NULL;

  return result;
}

void Tuple::addLocalExpr(LIST(ExprNode *) &xlist,
			 LIST(NAString) &llist) const
{
  if (tupleExprTree_ != NULL OR
      NOT tupleExpr_.isEmpty())
    {
      if(tupleExpr_.isEmpty())
	xlist.insert(tupleExprTree_);
      else
	xlist.insert(tupleExpr_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("tuple_expr");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

const NAString Tuple::getText() const
{
  NAString tmp("values ", CmpCommon::statementHeap());
  if (tupleExprTree()) tupleExprTree()->unparse(tmp);
  else                 ((ValueIdList &)tupleExpr()).unparse(tmp);
  return tmp;
}

// -----------------------------------------------------------------------
// methods for class TupleList
// -----------------------------------------------------------------------
TupleList::TupleList(const TupleList & other) : Tuple(other)
{
  castToList_ = other.castToList_;
  createdForInList_ = other.createdForInList_;
}

RelExpr * TupleList::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  TupleList *result;

  if (derivedNode == NULL)
    result = new (outHeap) TupleList(NULL,outHeap);
  else
    result = (TupleList *) derivedNode;

  result->castToList() = castToList();

  return Tuple::copyTopNode(result, outHeap);
}

void TupleList::addLocalExpr(LIST(ExprNode *) &xlist,
			 LIST(NAString) &llist) const
{
  Tuple::addLocalExpr(xlist,llist);
}

const NAString TupleList::getText() const
{
  NAString tmp("TupleList",CmpCommon::statementHeap());

  return tmp;
}

// -----------------------------------------------------------------------
// member functions for class PhysicalTuple
// -----------------------------------------------------------------------

NABoolean PhysicalTuple::isLogical()  const { return FALSE; }

NABoolean PhysicalTuple::isPhysical() const { return TRUE; }

// -----------------------------------------------------------------------
// Member functions for class FirstN
// -----------------------------------------------------------------------

RelExpr * FirstN::copyTopNode(RelExpr *derivedNode,
			      CollHeap* outHeap)
{
  FirstN *result;

  if (derivedNode == NULL) {
    result = new (outHeap) FirstN(NULL, getFirstNRows(), isFirstN(), getFirstNRowsParam(),
                                  outHeap);
    result->setCanExecuteInDp2(canExecuteInDp2());
  }
  else
    result = (FirstN *) derivedNode;

  result->reqdOrder().insert(reqdOrder());

  return RelExpr::copyTopNode(result, outHeap);
}

const NAString FirstN::getText() const
{
  NAString result(CmpCommon::statementHeap());

  result = "FirstN";

  return result;
}

// helper method to determine if we have a child Fisrt N node that can execute in Dp2.
// This method will only search nodes with one child. We do not expect the child First N
// to occur below a join or union type node. This method will unwind as soon as the first
// FirstN child is found.
static NABoolean haveChildFirstNInDp2 (RelExpr * node)
{
  if (node->getArity() != 1) return FALSE;
  if (node->child(0))
  {
    if (node->child(0)->getOperatorType() == REL_FIRST_N)
    {  // child is FirstN
      FirstN * innerFirstN = (FirstN *) node->child(0)->castToRelExpr();
	if (innerFirstN->canExecuteInDp2())
	  return TRUE;
	else
	  return FALSE;
    }
    else
    {  // have child but it is not FirstN
      return (haveChildFirstNInDp2(node->child(0)));
    }
  }
  else
    return FALSE;  // no child even though arity is 1!
}

RelExpr * FirstN::bindNode(BindWA *bindWA)
{
  if (nodeIsBound())
    {
      bindWA->getCurrentScope()->setRETDesc(getRETDesc());
      return this;
    }

  if (bindWA->isEmbeddedIUDStatement() && haveChildFirstNInDp2(this))
    {
      setCanExecuteInDp2(TRUE);
    }

  return RelExpr::bindNode(bindWA);
}

NABoolean FirstN::computeRowsAffected() const
{
    if (child(0))
    {
      return child(0)->castToRelExpr()->computeRowsAffected();
    }
    return FALSE;
}
// member functions for class Filter
// -----------------------------------------------------------------------

Filter::~Filter() {}

Int32 Filter::getArity() const { return 1; }

HashValue Filter::topHash()
{
  HashValue result = RelExpr::topHash();

  return result;
}

NABoolean Filter::duplicateMatch(const RelExpr & other) const
{
  return RelExpr::duplicateMatch(other);
}

RelExpr * Filter::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Filter *result;

  if (derivedNode == NULL)
    result = new (outHeap) Filter(NULL, outHeap);
  else
    result = (Filter *) derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

void Filter::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  outputValues += child(0)->getGroupAttr()->getCharacteristicOutputs();
} // Filter::getPotentialOutputValues()

const NAString Filter::getText() const { return "filter"; }

// -----------------------------------------------------------------------
// member functions for class Rename
// -----------------------------------------------------------------------

Rename::~Rename() {}

Int32 Rename::getArity() const { return 1; }

HashValue Rename::topHash()
{
  ABORT("Hash functions can't be called in the parser");
  return 0x0;
}

NABoolean Rename::duplicateMatch(const RelExpr & /* other */) const
{
  ABORT("Duplicate match doesn't work in the parser");
  return FALSE;
}

// -----------------------------------------------------------------------
// member functions for class RenameTable
// -----------------------------------------------------------------------

RenameTable::~RenameTable() {}

const NAString RenameTable::getText() const
{
  return ("rename_as " + newTableName_.getCorrNameAsString());
}

RelExpr * RenameTable::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RenameTable *result;

  if (derivedNode == NULL)
    result = new (outHeap) RenameTable(TRUE, NULL, newTableName_, NULL, outHeap);
  else
    result = (RenameTable *) derivedNode;

  if (newColNamesTree_ != NULL)
    result->newColNamesTree_ = newColNamesTree_->copyTree(outHeap)->castToItemExpr();

  if (viewNATable_ != NULL)
    result->viewNATable_ = viewNATable_;

  return RelExpr::copyTopNode(result, outHeap);
}

ItemExpr * RenameTable::removeColNameTree()
{
  ItemExpr * result = newColNamesTree_;

  newColNamesTree_ = NULL;

  return result;
}

void RenameTable::addLocalExpr(LIST(ExprNode *) &xlist,
			       LIST(NAString) &llist) const
{
  xlist.insert(newColNamesTree_);
  llist.insert("new_col_names");

  RelExpr::addLocalExpr(xlist,llist);
}

// -----------------------------------------------------------------------
// member functions for class RenameReference
// -----------------------------------------------------------------------

RenameReference::~RenameReference() {}

const NAString RenameReference::getText() const
{
  NAString text("rename_ref");
  for (CollIndex i=0; i<tableReferences_.entries(); i++)
	  text += " " + tableReferences_[i].getRefName();
  return text;
}

RelExpr * RenameReference::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RenameReference *result;

  if (derivedNode == NULL)
  {
    TableRefList *tableRef = new(outHeap) TableRefList(tableReferences_);
    result = new (outHeap) RenameReference(NULL, *tableRef, outHeap);
  }
  else
    result = (RenameReference *) derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

void RenameReference::addLocalExpr(LIST(ExprNode *) &xlist,
			       LIST(NAString) &llist) const
{
  RelExpr::addLocalExpr(xlist,llist);
}

// -----------------------------------------------------------------------
// member functions for class BeforeTrigger
// -----------------------------------------------------------------------

BeforeTrigger::~BeforeTrigger() {}

Int32 BeforeTrigger::getArity() const
{
	if (child(0) == NULL)
		return 0;
	else
		return 1;
}

const NAString BeforeTrigger::getText() const
{
  NAString text("before_trigger: ");

  if (signal_)
	text += "Signal";
  else
	text += "Set";

  return text;
}

RelExpr * BeforeTrigger::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  BeforeTrigger *result;

  if (derivedNode == NULL)
  {
	  TableRefList *tableRef   = new(outHeap) TableRefList(tableReferences_);
	  ItemExpr     *whenClause = NULL;
	  if (whenClause_ != NULL)
		  whenClause = whenClause_->copyTree(outHeap);

	  if (isSignal_)
	  {
		RaiseError *signalClause = (RaiseError *)signal_->copyTree(outHeap);
		result = new (outHeap) BeforeTrigger(*tableRef, whenClause, signalClause, outHeap);
	  }
	  else
	  {
		CMPASSERT(setList_ != NULL);  // Must have either SET or SIGNAL clause.
		ItemExprList *setList = new(outHeap) ItemExprList(setList_->entries(), outHeap);
		for (CollIndex i=0; i<setList_->entries(); i++)
		  setList->insert(setList_->at(i)->copyTree(outHeap));

		result = new (outHeap) BeforeTrigger(*tableRef, whenClause, setList, outHeap);
	  }
  }
  else
    result = (BeforeTrigger *) derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

void BeforeTrigger::addLocalExpr(LIST(ExprNode *) &xlist,
			       LIST(NAString) &llist) const
{
  if (whenClause_ != NULL)
  {
	llist.insert("WHEN clause");
	xlist.insert(whenClause_);
  }

  if (signal_)
  {
	llist.insert("SIGNAL clause");
	xlist.insert(signal_);
  }
  else
  {
	for (CollIndex i=0; i<setList_->entries(); i++)
	{
		llist.insert("SET clause");
		xlist.insert(setList_->at(i));
	}
  }

  RelExpr::addLocalExpr(xlist,llist);
}


// -----------------------------------------------------------------------
// member functions for class MapValueIds
// -----------------------------------------------------------------------

MapValueIds::~MapValueIds()
{
}

Int32 MapValueIds::getArity() const { return 1; }

void MapValueIds::pushdownCoveredExpr(
     const ValueIdSet & outputExpr,
     const ValueIdSet & newExternalInputs,
     ValueIdSet & predicatesOnParent,
     const ValueIdSet * setOfValuesReqdByParent,
     Lng32 childIndex)
{
  // ---------------------------------------------------------------------
  // Since the MapValueIds node rewrites predicates, the characteristic
  // outputs of the node usually make no sense to the child node. For
  // this reason, translate the characteristic outputs of this node before
  // passing them down to the children.
  // ---------------------------------------------------------------------
  ValueIdSet requiredValues;
  if (setOfValuesReqdByParent)
    requiredValues = *setOfValuesReqdByParent;
  ValueIdSet translatedOutputs;
  ValueIdSet predsRewrittenForChild;
  ValueIdSet outputValues(outputExpr);

  // first subtract the outputs from the required values, then add back
  // the translated outputs
  outputValues -= getGroupAttr()->getCharacteristicOutputs();
  getMap().rewriteValueIdSetDown(getGroupAttr()->getCharacteristicOutputs(),
				 translatedOutputs);
  outputValues += translatedOutputs;

  translatedOutputs.clear();
  requiredValues -= getGroupAttr()->getCharacteristicOutputs();
  getMap().rewriteValueIdSetDown(getGroupAttr()->getCharacteristicOutputs(),
				 translatedOutputs);
  requiredValues += translatedOutputs;

  if (cseRef_)
    {
      // If this MapValueIds node represents a common subexpression,
      // then don't try to push predicates again that already have
      // been pushed down before. VEGPredicates may not be pushable
      // at all to the rewritten child, and other predicates might
      // be duplicated with different ValueIds for the internal
      // operators such as "=", "+", ">".
      predicatesOnParent -= cseRef_->getPushedPredicates();

      // Also, don't push down VEGPredicates on columns that are
      // characteristic outputs of the MapValueIds. Those predicates
      // (or their equivalents) should have already been pushed down.
      for (ValueId g=predicatesOnParent.init();
           predicatesOnParent.next(g);
           predicatesOnParent.advance(g))
        if (g.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE)
          {
            VEG *veg =
              static_cast<VEGPredicate *>(g.getItemExpr())->getVEG();
            ValueId vegRef(veg->getVEGReference()->getValueId());

            if (newExternalInputs.contains(vegRef))
              {
                // We are trying to push down a VEGPred and we are at
                // the same time offering the VEGRef as a new
                // characteristic input.  Example: We want to push
                // VEGPred_2(a=b) down and we offer its corresponding
                // VEGRef_1(a,b) as an input. The map in our
                // MapValueIds node maps VEGRef_1(a,b) on the top to
                // VEGRef_99(x,y) on the bottom. Note that either one
                // of the VEGies might contain a constant that the
                // other one doesn't contain. Note also that the
                // MapValueIds node doesn't map characteristic inputs,
                // those are passed unchanged to the child. So, we need
                // to generate a predicate for the child that:
                // a) represents the semantics of VEGPred_2(a,b)
                // b) compares the new characteristic input
                //    VEGRef_1(a,b) with some value in the child
                // c) doesn't change the members of the existing
                //    VEGies.
                // The best solution is probably an equals predicate
                // between the characteristic input and the VEGRef
                // (or other expression) that is the child's equivalent.
                // Example:  VEGRef_1(a,b) = VEGRef_99(x,y)
                ValueId bottomVal;
                ItemExpr *eqPred = NULL;

                map_.mapValueIdDown(vegRef, bottomVal);

                if (vegRef != bottomVal && bottomVal != NULL_VALUE_ID)
                  {
                    eqPred = new(CmpCommon::statementHeap()) BiRelat(
                         ITM_EQUAL,
                         vegRef.getItemExpr(),
                         bottomVal.getItemExpr(),
                         veg->getSpecialNulls());
                    eqPred->synthTypeAndValueId();
                    // replace g with the new equals predicate
                    // when we do the actual rewrite below
                    map_.addMapEntry(g, eqPred->getValueId());
                  }
              }
            else
              {
                // Don't push down VEGPredicates on columns that are
                // characteristic outputs of the MapValueIds. Those
                // predicates (or their equivalents) should have
                // already been pushed down.
                ValueIdSet vegMembers(veg->getAllValues());

                vegMembers += vegRef;
                vegMembers.intersectSet(
                     getGroupAttr()->getCharacteristicOutputs());
                if (!vegMembers.isEmpty())
                  {
                    // a VEGPred on one of my characteristic outputs,

                    // assume that my child tree already has the
                    // associated VEGPreds and remove this predicate
                    // silently
                    predicatesOnParent -= g;
                  }
                // else leave the predicate and let the code below deal
                // with it, this will probably end up in a failed assert
                // below for predsRewrittenForChild.isEmpty()
              }
          }
    }

  // rewrite the predicates so they can be applied in the child node
  getMap().rewriteValueIdSetDown(predicatesOnParent,predsRewrittenForChild);

  // use the standard method with the new required values
  RelExpr::pushdownCoveredExpr(outputValues,
                               newExternalInputs,
                               predsRewrittenForChild,
			       &requiredValues,
                               childIndex);

  // eliminate any VEGPredicates that got duplicated in the parent
  if (NOT child(0)->isCutOp())
    predsRewrittenForChild -= child(0)->selectionPred();

  // all predicates must have been pushed down!!
  CMPASSERT(predsRewrittenForChild.isEmpty());
  predicatesOnParent.clear();

  // Remove entries from the map that are no longer required since
  // they no longer appear in the outputs.
  NABoolean matchWithTopValues = FALSE;
  if (child(0)->isCutOp() || 
      child(0)->getOperatorType() == REL_FILTER ||
      child(0)->getOperatorType() == REL_MAP_VALUEIDS )
  {
    matchWithTopValues = TRUE;
    getMap().removeUnusedEntries(getGroupAttr()->getCharacteristicOutputs(), matchWithTopValues);
  }
  else
  {
    getMap().removeUnusedEntries(child(0)->getGroupAttr()->getCharacteristicOutputs(), matchWithTopValues);
  }

} // MapValueIds::pushdownCoveredExpr

void MapValueIds::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues.clear();
  //
  // The output of the MapValueId is given by the ValueIds
  // contained in the "upper" portion of a two-tiered mapping
  // table.
  //
  outputValues.insertList((getMap2()).getTopValues());
} // MapValueIds::getPotentialOutputValues()

void MapValueIds::addSameMapEntries(const ValueIdSet & newTopBottomValues)
{
  for (ValueId x = newTopBottomValues.init();
	    newTopBottomValues.next(x);
	    newTopBottomValues.advance(x))
    addMapEntry(x,x);
}

void MapValueIds::addLocalExpr(LIST(ExprNode *) &xlist,
			       LIST(NAString) &llist) const
{
  xlist.insert(map_.getTopValues().rebuildExprTree(ITM_ITEM_LIST));
  llist.insert("upper_values");
  xlist.insert(map_.getBottomValues().rebuildExprTree(ITM_ITEM_LIST));
  llist.insert("lower_values");

  RelExpr::addLocalExpr(xlist,llist);
}

HashValue MapValueIds::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= map_.getTopValues();
  result ^= map_.getBottomValues();

  return result;
}

NABoolean MapValueIds::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  MapValueIds &o = (MapValueIds &) other;

  if (includesFavoriteMV_ != o.includesFavoriteMV_)
    return FALSE;

  if (cseRef_ != o.cseRef_)
    return FALSE;

  if (map_ != o.map_)
    return FALSE;

  return TRUE;
}

RelExpr * MapValueIds::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  MapValueIds *result;

  if (derivedNode == NULL)
    result = new (outHeap) MapValueIds(NULL,map_,outHeap);
  else
    result = static_cast<MapValueIds*>(derivedNode);

  result->includesFavoriteMV_ = includesFavoriteMV_;
  result->cseRef_ = cseRef_;

  return RelExpr::copyTopNode(result, outHeap);
}

const NAString MapValueIds::getText() const
{
    return "map_value_ids";
//    return "expr";
}

// -----------------------------------------------------------------------
// Member functions for class PhysicalMapValueIds
// -----------------------------------------------------------------------

NABoolean PhysicalMapValueIds::isLogical() const  { return FALSE; }

NABoolean PhysicalMapValueIds::isPhysical() const { return TRUE; }


// -----------------------------------------------------------------------
// Member functions for class Describe
// -----------------------------------------------------------------------

RelExpr * Describe::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Describe *result;

  if (derivedNode == NULL)
    result = new (outHeap)
                 Describe(originalQuery_, getDescribedTableName(), format_, labelAnsiNameSpace_);
  else
    result = (Describe *) derivedNode;

  return RelExpr::copyTopNode(result, outHeap);
}

const NAString Describe::getText() const
{
  return "describe";
}

// -----------------------------------------------------------------------
// Member functions for class ProbeCache
// -----------------------------------------------------------------------

NABoolean ProbeCache::isLogical()  const { return FALSE; }

NABoolean ProbeCache::isPhysical() const { return TRUE; }

Int32 ProbeCache::getArity() const { return 1; }

RelExpr * ProbeCache::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  ProbeCache *result;

  if (derivedNode == NULL)
    result = new (outHeap)
                 ProbeCache(NULL, numCachedProbes_, outHeap);
  else
    result = (ProbeCache *) derivedNode;

  result->numCachedProbes_ = numCachedProbes_;
  result->numInnerTuples_  = numInnerTuples_;

  return RelExpr::copyTopNode(result, outHeap);
}

const NAString ProbeCache::getText() const
{
  return "probe_cache";
}

// -----------------------------------------------------------------------
// Member functions for class ControlRunningQuery
// -----------------------------------------------------------------------

NABoolean ControlRunningQuery::isLogical()  const { return TRUE; }

NABoolean ControlRunningQuery::isPhysical() const { return TRUE; }

Int32 ControlRunningQuery::getArity() const { return 0; }

RelExpr * ControlRunningQuery::copyTopNode(RelExpr *derivedNode, 
                                           CollHeap* outHeap)
{
  ControlRunningQuery *result = NULL;
  
  if (derivedNode == NULL)
  {
    switch (qs_)
    {
      case ControlNidPid:
        result = new (outHeap) ControlRunningQuery(nid_, pid_, 
                                ControlNidPid, action_, forced_, outHeap);
        break;
      case ControlPname:
        result = new (outHeap) 
              ControlRunningQuery(pname_, ControlPname, action_, forced_, 
                                  outHeap);
        break;
      case ControlQid:
        result = new (outHeap)
              ControlRunningQuery(queryId_, ControlQid, action_, forced_,
                                  outHeap);
        break;
      default:
        CMPASSERT(0);
    }
    result->setComment(comment_);
  }
  else
    result = (ControlRunningQuery *) derivedNode;
  
  return RelExpr::copyTopNode(result, outHeap);
}

const NAString ControlRunningQuery::getText() const
{
  return "control_running_query";
}

void ControlRunningQuery::setComment(NAString &comment)
{
  NAString cancelComment (comment, CmpCommon::statementHeap());
  comment_ = cancelComment;
}

// -----------------------------------------------------------------------
// member functions for class CSEInfo (helper for CommonSubExprRef)
// -----------------------------------------------------------------------

Int32 CSEInfo::getTotalNumRefs(Int32 restrictToSingleConsumer) const
{
  // shortcut for main query
  if (cseId_ == CmpStatement::getCSEIdForMainQuery())
    return 1;

  // Calculate how many times we will evaluate this common subexpression
  // at runtime:
  //  - If the CSE is shared then we evaluate it once
  //  - Otherwise, look at the consumers that are lexical refs
  //    (avoid double-counting refs from multiple copies of a parent)
  //    and add the times they are being executed.
  Int32 result = 0;
  NABoolean sharedConsumers = FALSE;
  LIST(CountedCSEInfo) countsByCSE(CmpCommon::statementHeap());
  CollIndex minc = 0;
  CollIndex maxc = consumers_.entries();

  if (restrictToSingleConsumer >= 0)
    {
      // count only the executions resulting from a single consumer
      minc = restrictToSingleConsumer;
      maxc = minc + 1;
    }

  // loop over all consumers or look at just one
  for (CollIndex c=minc; c<maxc; c++)
    {
      if (isShared(c))
        {
          sharedConsumers = TRUE;
        }
      else
        {
          CommonSubExprRef *consumer = getConsumer(c);
          CSEInfo *parentInfo = CmpCommon::statement()->getCSEInfoById(
               consumer->getParentCSEId());
          NABoolean duplicateDueToSharedConsumer = FALSE;

          // Don't double-count consumers that originate from the
          // same parent CSE, have the same lexical ref number from
          // the parent, and are shared.
          if (parentInfo->isShared(
                   consumer->getParentConsumerId()))
            {
              for (CollIndex d=0; d<countsByCSE.entries(); d++)
                if (countsByCSE[d].getInfo() == parentInfo &&
                    countsByCSE[d].getLexicalCount() ==
                    consumer->getLexicalRefNumFromParent())
                  {
                    duplicateDueToSharedConsumer = TRUE;
                    break;
                  }

              // First consumer from this parent CSE with this lexical
              // ref number, remember that we are going to count
              // it. Note that we are use the lexical ref "count" in
              // CountedCSEInfo as the lexical ref number in this
              // method, so the name "count" means "ref number" in
              // this context.
              if (!duplicateDueToSharedConsumer)
                countsByCSE.insert(
                     CountedCSEInfo(
                          parentInfo,
                          consumer->getLexicalRefNumFromParent()));
            } // parent consumer is shared

          if (!duplicateDueToSharedConsumer)
            {
              // recursively determine number of times the parent of
              // this consumer gets executed
              result += parentInfo->getTotalNumRefs(
                   consumer->getParentConsumerId());
            }
        } // consumer is not shared
    } // loop over consumer(s)

  if (sharedConsumers)
    // all the shared consumers are handled by evaluating the CSE once
    result++;

  return result;
}

CSEInfo::CSEAnalysisOutcome CSEInfo::getAnalysisOutcome(Int32 id) const
{
  if (idOfAnalyzingConsumer_ != id &&
      analysisOutcome_ == CREATE_TEMP)
    // only the analyzing consumer creates and reads the temp, the
    // others only read it
    return TEMP;
  else
    return analysisOutcome_;
}

Int32 CSEInfo::addChildCSE(CSEInfo *childInfo, NABoolean addLexicalRef)
{
  Int32 result = -1;
  CollIndex foundIndex = NULL_COLL_INDEX;

  // look for an existing entry
  for (CollIndex i=0;
       i<childCSEs_.entries() && foundIndex == NULL_COLL_INDEX;
       i++)
    if (childCSEs_[i].getInfo() == childInfo)
      foundIndex = i;

  if (foundIndex == NULL_COLL_INDEX)
    {
      // create a new entry
      foundIndex = childCSEs_.entries();
      childCSEs_.insert(CountedCSEInfo(childInfo));
    }           

  if (addLexicalRef)
    {
      // The return value for a lexical ref is the count of lexical
      // refs for this particular parent/child CSE relationship so far
      // (0 if this is the first one). Note that we can't say anything
      // abount counts for expanded refs at this time, those will be
      // handled later, during the transform phase of the normalizer.
      result = childCSEs_[foundIndex].getLexicalCount();
      childCSEs_[foundIndex].incrementLexicalCount();
    }

  return result;
}

void CSEInfo::addCSERef(CommonSubExprRef *cse)
{
  CMPASSERT(name_ == cse->getName());
  cse->setId(consumers_.entries());
  consumers_.insert(cse);
  if (cse->isALexicalRef())
    numLexicalRefs_++;
}

void CSEInfo::replaceConsumerWithAnAlternative(CommonSubExprRef *c)
{
  Int32 idToReplace = c->getId();

  if (consumers_[idToReplace] != c)
    {
      CollIndex foundPos = alternativeConsumers_.index(c);

      CMPASSERT(foundPos != NULL_COLL_INDEX);
      CMPASSERT(consumers_[idToReplace]->getOriginalRef() ==
                c->getOriginalRef());
      // c moves from the list of copies to the real list and another
      // consumer moves the opposite way
      alternativeConsumers_.removeAt(foundPos);
      alternativeConsumers_.insert(consumers_[idToReplace]);
      consumers_[idToReplace] = c;
    }
}

// -----------------------------------------------------------------------
// member functions for class CommonSubExprRef
// -----------------------------------------------------------------------

NABoolean CommonSubExprRef::isAChildOfTheMainQuery() const
{
  return (parentCSEId_ == CmpStatement::getCSEIdForMainQuery());
}

CommonSubExprRef::~CommonSubExprRef()
{
}

Int32 CommonSubExprRef::getArity() const
{
  // always return 1 for now, that may change in the future
  return 1;
}

void CommonSubExprRef::addToCmpStatement(NABoolean lexicalRef)
{
  NABoolean alreadySeen = TRUE;

  // look up whether a CSE with this name already exists
  CSEInfo *info = CmpCommon::statement()->getCSEInfo(internalName_);

  // sanity check, make sure that the caller knows whether this is a
  // lexical ref (generated with CommonSubExprRef constructor) or an
  // expanded ref (generated with CommonSubExprRef::copyTopNode()
  // before the bind phase)
  CMPASSERT(isALexicalRef() == lexicalRef);

  if (!info)
    {
      // make a new object to hold a list of all references
      // to this CSE (the first one of them will be "this")
      info = new(CmpCommon::statementHeap())
        CSEInfo(internalName_,
                CmpCommon::statementHeap());
      alreadySeen = FALSE;
    }

  info->addCSERef(this);

  if (!alreadySeen)
    CmpCommon::statement()->addCSEInfo(info);
}

void CommonSubExprRef::addParentRef(CommonSubExprRef *parentRef)
{
  // Establish the parent/child relationship between two
  // CommonSubExprRef nodes, or between the main query and a
  // CommonSubExprRef node (parentRef == NULL). Also, record
  // the dependency between parent and child CSEs in the lexical
  // dependency multigraph. Bookkeeping to do:
  //
  // - Add the child's CSE to the list of child CSEs of the
  //   parent CSE
  // - Set the data members in the child ref that point to the
  //   parent CSE and parent ref

  // parent info is for a parent CSE or for the main query
  CSEInfo *parentInfo =
    (parentRef ? CmpCommon::statement()->getCSEInfo(parentRef->getName())
               : CmpCommon::statement()->getCSEInfoForMainQuery());
  CSEInfo *childInfo =
    CmpCommon::statement()->getCSEInfo(getName());

  CMPASSERT(parentInfo && childInfo);

  // Update the lexical CSE multigraph, and also return the
  // count of previously existing edges in the graph.
  // LexicalRefNumFromParent is set to a positive value only for
  // lexical refs, the other refs will be handled later, in the SQO
  // phase. This is since expanded refs may be processed before their
  // lexical ref and we may not know this value yet.
  lexicalRefNumFromParent_ =
    parentInfo->addChildCSE(childInfo, isALexicalRef());

  parentCSEId_ = parentInfo->getCSEId();
  if (parentRef)
    parentRefId_ = parentRef->getId();
  else
    parentRefId_ = -1;  // main query does not have a CommonSubExprRef
}

NABoolean CommonSubExprRef::isFirstReference() const
{
  return (CmpCommon::statement()->getCSEInfo(internalName_) == NULL);
}

void CommonSubExprRef::addLocalExpr(LIST(ExprNode *) &xlist,
                                    LIST(NAString) &llist) const
{
  if (NOT columnList_.isEmpty())
    {
      xlist.insert(columnList_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("column_list");
    }

  if(NOT pushedPredicates_.isEmpty())
  {
    xlist.insert(pushedPredicates_.rebuildExprTree());
    llist.insert("pushed_predicates");
  }
}

HashValue CommonSubExprRef::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= internalName_;
  result ^= id_;
  result ^= columnList_;
  result ^= pushedPredicates_;

  return result;
}

NABoolean CommonSubExprRef::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  const CommonSubExprRef &o = static_cast<const CommonSubExprRef &>(other);

  return (internalName_ == o.internalName_ &&
          id_ == o.id_ &&
          columnList_ == o.columnList_ &&
          pushedPredicates_ == o.pushedPredicates_);
}

RelExpr * CommonSubExprRef::copyTopNode(RelExpr *derivedNode,
                                        CollHeap* outHeap)
{
  CommonSubExprRef *result = NULL;

  if (derivedNode == NULL)
    result = new (outHeap) CommonSubExprRef(NULL,
                                            internalName_.data(),
                                            outHeap);
  else
    result = static_cast<CommonSubExprRef *>(derivedNode);

  // copy fields that are common for bound and unbound nodes
  result->hbAccessOptionsFromCTE_ = hbAccessOptionsFromCTE_;

  if (nodeIsBound())
    {
      // if the node is bound, we assume that the copy is serving the same function
      // as the original, as an alternative, create an "alternative ref"
      result->setId(id_);

      result->parentCSEId_ = parentCSEId_;
      result->parentRefId_ = parentRefId_;
      result->lexicalRefNumFromParent_ = lexicalRefNumFromParent_;
      result->columnList_ = columnList_;
      result->nonVEGColumns_ = nonVEGColumns_;
      result->commonInputs_ = commonInputs_;
      result->pushedPredicates_ = pushedPredicates_;
      result->nonSharedPredicates_ = nonSharedPredicates_;
      result->cseEstLogProps_ = cseEstLogProps_;
      // don't copy the tempScan_

      // Mark this as an alternative ref, not that this does not
      // change the status of lexical vs. expanded ref
      result->isAnExpansionOf_ = isAnExpansionOf_;
      result->isAnAlternativeOf_ =
        ( isAnAlternativeOf_ ? isAnAlternativeOf_ : this);

      CmpCommon::statement()->getCSEInfo(internalName_)->
        registerAnAlternativeConsumer(result);
    }
  else
    {
      // If the node is not bound, we assume that we created an
      // "expanded" reference to a common subexpression in a tree that
      // itself is a reference to a CTE (Common Table Expression).
      // See the comment in RelMisc.h for method isAnExpandedRef()
      // to explain the term "expanded" ref.
      result->isAnExpansionOf_ =
        (isAnExpansionOf_ ? isAnExpansionOf_ : this);
      result->addToCmpStatement(FALSE);
    }

  return result;
}

const NAString CommonSubExprRef::getText() const
{
  NAString result("cse ");
  char buf[20];

  result += ToAnsiIdentifier(internalName_);

  snprintf(buf, sizeof(buf), " %d", id_);
  result += buf;

  return result;
}

Union * CommonSubExprRef::makeUnion(RelExpr *lc,
                                    RelExpr *rc,
                                    NABoolean blocked)
{
  // Make a regular or blocked union with no characteristic outputs
  Union *result;
  ValueIdSet newInputs(lc->getGroupAttr()->getCharacteristicInputs());

  result = new(CmpCommon::statementHeap()) Union(lc, rc);

  newInputs += rc->getGroupAttr()->getCharacteristicInputs();

  result->setGroupAttr(new (CmpCommon::statementHeap()) GroupAttributes());
  result->getGroupAttr()->addCharacteristicInputs(newInputs);

  if(blocked)
    result->setBlockedUnion();

  return result;
}

void CommonSubExprRef::display()
{
  if (isAChildOfTheMainQuery())
    printf("Parent: main query, lexical ref %d\n",
           lexicalRefNumFromParent_);
  else
    printf("Parent: %s(consumer %d, lexical ref %d)\n",
           CmpCommon::statement()->getCSEInfoById(
                parentCSEId_)->getName().data(),
           parentRefId_,
           lexicalRefNumFromParent_);
  printf("Original columns:\n");
  columnList_.display();
  printf("\nCommon inputs:\n");
  commonInputs_.display();
  printf("\nPushed predicates:\n");
  pushedPredicates_.display();
  printf("\nNon shared preds to be applied to scan:\n");
  nonSharedPredicates_.display();
  printf("\nPotential values for VEG rewrite:\n");
  nonVEGColumns_.display();
}

void CommonSubExprRef::displayAll(const char *optionalId)
{
  const LIST(CSEInfo *) *cses = CmpCommon::statement()->getCSEInfoList();

  if (cses)
    for (CollIndex i=0; i<cses->entries(); i++)
      if (!optionalId ||
          strlen(optionalId) == 0 ||
          cses->at(i)->getName() == optionalId)
        {
          CSEInfo *info = cses->at(i);
          CollIndex nc = info->getNumConsumers();
          NABoolean isMainQuery =
            (info->getCSEId() == CmpStatement::getCSEIdForMainQuery());

          if (isMainQuery)
            {
              printf("\n\n==========================\n");
              printf("MainQuery:\n");
            }
          else
            {
              printf("\n\n==========================\n");
              printf("CSE: %s (%d consumers, %d lexical ref(s), %d total execution(s))\n",
                     info->getName().data(),
                     nc,
                     info->getNumLexicalRefs(),
                     info->getTotalNumRefs());
            }

          const LIST(CountedCSEInfo) &children(info->getChildCSEs());

          for (CollIndex j=0; j<children.entries(); j++)
            printf("       references CSE: %s %d times\n",
                   children[j].getInfo()->getName().data(),
                   children[j].getLexicalCount());

          if (info->getIdOfAnalyzingConsumer() >= 0)
            {
              const char *outcome = "?";
              ValueIdList cols;
              CommonSubExprRef *consumer =
                info->getConsumer(info->getIdOfAnalyzingConsumer());
              const ValueIdList &cCols(consumer->getColumnList());

              switch (info->getAnalysisOutcome(0))
                {
                case CSEInfo::UNKNOWN_ANALYSIS:
                  outcome = "UNKNOWN";
                  break;
                case CSEInfo::EXPAND:
                  outcome = "EXPAND";
                  break;
                case CSEInfo::CREATE_TEMP:
                  outcome = "CREATE_TEMP";
                  break;
                case CSEInfo::TEMP:
                  outcome = "TEMP";
                  break;
                case CSEInfo::ERROR:
                  outcome = "ERROR";
                  break;
                default:
                  outcome = "???";
                  break;
                }

              printf("  analyzed by consumer %d, outcome: %s\n",
                     info->getIdOfAnalyzingConsumer(),
                     outcome);

              makeValueIdListFromBitVector(
                   cols,
                   cCols,
                   info->getNeededColumns());
              printf("\n  columns of temp table:\n");
              cols.display();
              printf("\n  commonPredicates:\n");
              info->getCommonPredicates().display();
              if (info->getVEGRefsWithDifferingConstants().entries() > 0)
                {
                  printf("\n  vegRefsWithDifferingConstants:\n");
                  info->getVEGRefsWithDifferingConstants().display();
                }
              if (info->getVEGRefsWithDifferingInputs().entries() > 0)
                {
                  printf("\n  vegRefsWithDifferingInputs:\n");
                  info->getVEGRefsWithDifferingInputs().display();
                }
              if (info->getCSETreeKeyColumns().entries() > 0)
                {
                  ValueIdList keyCols;

                  makeValueIdListFromBitVector(
                       keyCols,
                       cCols,
                       info->getCSETreeKeyColumns());
                  printf("\n  CSE key columns:\n");
                  keyCols.display();
                }
              printf("\n  DDL of temp table:\n%s\n",
                     info->getTempTableDDL().data());
            } // analyzed
          else if (info->getAnalysisOutcome(0) ==
                   CSEInfo::ELIMINATED_IN_BINDER)
            printf("  eliminated in the binder\n");
          else if (!isMainQuery)
            printf("  not yet analyzed\n");

          for (int c=0; c<nc; c++)
            {
              printf("\n\n----- Consumer %d:\n", c);
              info->getConsumer(c)->display();
            }
        } // a CSE we want to display
}

void CommonSubExprRef::makeValueIdListFromBitVector(ValueIdList &tgt,
                                                    const ValueIdList &src,
                                                    const NABitVector &vec)
{
  for (CollIndex b=0; vec.nextUsed(b); b++)
    tgt.insert(src[b]);
}


// -----------------------------------------------------------------------
// member functions for class GenericUpdate
// -----------------------------------------------------------------------


GenericUpdate::~GenericUpdate() {}

Int32 GenericUpdate::getArity() const
{
  if (getOperator().match(REL_ANY_LEAF_GEN_UPDATE))
    return 0;
  else if (getOperator().match(REL_ANY_UNARY_GEN_UPDATE))
    return 1;
  else
    ABORT("Don't know opcode in GenericUpdate::getArity()");

  return 0;    // return makes MSVC happy.
}

void GenericUpdate::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  outputValues = potentialOutputs_;
  if (producedMergeIUDIndicator_ != NULL_VALUE_ID)
    outputValues += producedMergeIUDIndicator_;
}

const NAString GenericUpdate::getUpdTableNameText() const
{
  return updatedTableName_.getTextWithSpecialType();
}

void GenericUpdate::computeUsedCols()
{
  ValueIdSet          requiredValueIds(newRecExpr_);
  ValueIdSet          coveredExprs;

  // ---------------------------------------------------------------------
  // Call the "coverTest" method, offering it all the index columns
  // as additional inputs. "coverTest" will mark those index columns that
  // it actually needs to satisfy the required value ids, and that is
  // what we actually want. The actual cover test should always succeed,
  // otherwise the update node would have been inconsistent.
  // ---------------------------------------------------------------------

  // use the clustering index, unless set otherwise
  if (indexDesc_ == NULL)
    indexDesc_ = getTableDesc()->getClusteringIndex();

  if (isMerge())
    {
      requiredValueIds.insertList(mergeInsertRecExpr_);
    }

  requiredValueIds.insertList(beginKeyPred_);
  requiredValueIds.insertList(endKeyPred_);
  requiredValueIds.insertList(getCheckConstraints());

  // QSTUFF
  requiredValueIds.insertList(newRecBeforeExpr_);
  // QSTUFF

  getGroupAttr()->coverTest(requiredValueIds,
			    indexDesc_->getIndexColumns(), // all index columns
			    coveredExprs,                  // dummy parameter
			    usedColumns_);                 // needed index cols

  // usedColumns_ is now set correctly
} // GenericUpdate::computeUsedCols


const NAString GenericUpdate::getText() const
{
  return ("GenericUpdate " + getUpdTableNameText());
}

HashValue GenericUpdate::topHash()
{
  HashValue result = RelExpr::topHash();

  result ^= newRecExpr_;

  if (isMerge())
    result ^= mergeInsertRecExpr_;

  // result ^= keyExpr_;

  return result;
}

NABoolean GenericUpdate::duplicateMatch(const RelExpr & other) const
{
  if (NOT RelExpr::duplicateMatch(other))
    return FALSE;

  GenericUpdate &o = (GenericUpdate &) other;

  if (newRecExpr_ != o.newRecExpr_ OR
      (isMerge() && 
       ((mergeInsertRecExpr_ != o.mergeInsertRecExpr_) OR
        (mergeUpdatePred_ != o.mergeUpdatePred_))       ) OR
      NOT (beginKeyPred_ == o.beginKeyPred_) OR
      NOT (endKeyPred_   == o.endKeyPred_))
    return FALSE;

  // later, replace this with the getTableDesc() ???
  if (NOT (updatedTableName_ == o.updatedTableName_))
    return FALSE;

  if (mtsStatement_ != o.mtsStatement_)
    return FALSE;

  if (noRollback_ != o.noRollback_)
    return FALSE;

  if (avoidHalloweenR2_ != o.avoidHalloweenR2_)
    return FALSE;

  if (avoidHalloween_ != o.avoidHalloween_)
    return FALSE;

  if (halloweenCannotUseDP2Locks_ != o.halloweenCannotUseDP2Locks_)
    return FALSE;

  return TRUE;
}

RelExpr * GenericUpdate::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  GenericUpdate *result;

  if (derivedNode == NULL)
    result = new (outHeap)
                 GenericUpdate(updatedTableName_,
		               getTableDesc(),
		               getOperatorType(),
			       NULL,
			       NULL, NULL,
			       outHeap);
  else
    result = (GenericUpdate *) derivedNode;

  result->setIndexDesc((IndexDesc *)getIndexDesc());

  if (newRecExprTree_)
    result->newRecExprTree_ = newRecExprTree_->copyTree(outHeap)->castToItemExpr();

  // ## Should usedColumns_ be copied here?  Is it missing deliberately or only by mistake?
  result->updateToSelectMap_	  = updateToSelectMap_;
  result->newRecExpr_   	  = newRecExpr_;
  result->newRecExprArray_ 	  = newRecExprArray_;
  // QSTUFF
  result->newRecBeforeExpr_   	  = newRecBeforeExpr_;
  result->newRecBeforeExprArray_  = newRecBeforeExprArray_;
  // QSTUFF

  result->mergeInsertRecExpr_ 	  = mergeInsertRecExpr_;
  result->mergeInsertRecExprArray_ 	  = mergeInsertRecExprArray_;
  result->mergeUpdatePred_ 	  = mergeUpdatePred_;

  result->beginKeyPred_ 	  = beginKeyPred_;
  result->endKeyPred_   	  = endKeyPred_;
  result->executorPred_ 	  = executorPred_;
  result->potentialOutputs_ 	  = potentialOutputs_;
  result->indexNewRecExprArrays_  = indexNewRecExprArrays_;
  result->indexBeginKeyPredArray_ = indexBeginKeyPredArray_;
  result->indexEndKeyPredArray_   = indexEndKeyPredArray_;
  result->indexNumberArray_ 	  = indexNumberArray_;
  result->scanIndexDesc_          = scanIndexDesc_;
  result->accessOptions_ 	  = accessOptions_;
  result->checkConstraints_ 	  = checkConstraints_;
  result->rowsAffected_ 	  = rowsAffected_;
  result->setOptStoi(stoi_);
  result->setNoFlow(noFlow_);
  result->setMtsStatement(mtsStatement_);
  result->setNoRollbackOperation(noRollback_);
  result->setAvoidHalloweenR2(avoidHalloweenR2_);
  result->avoidHalloween_         = avoidHalloween_;
  result->halloweenCannotUseDP2Locks_ = halloweenCannotUseDP2Locks_;
  result->setIsMergeUpdate(isMergeUpdate_);
  result->setIsMergeDelete(isMergeDelete_);
  result->subqInUpdateAssign_ = subqInUpdateAssign_;
  result->setUpdateCKorUniqueIndexKey(updateCKorUniqueIndexKey_);
  result->hbaseOper() = hbaseOper();
  result->uniqueHbaseOper() = uniqueHbaseOper();
  result->cursorHbaseOper() = cursorHbaseOper();
  result->uniqueRowsetHbaseOper() = uniqueRowsetHbaseOper();
  result->canDoCheckAndUpdel() = canDoCheckAndUpdel();
  result->setNoCheck(noCheck());
  result->noDTMxn() = noDTMxn();
  result->useMVCC() = useMVCC();
  result->useSSCC() = useSSCC();

  if (currOfCursorName())
    result->currOfCursorName_ = currOfCursorName()->copyTree(outHeap)->castToItemExpr();

  if (preconditionTree_)
    result->preconditionTree_ = preconditionTree_->copyTree(outHeap)->castToItemExpr();
  result->setPrecondition(precondition_);
  result->exprsInDerivedClasses_ = exprsInDerivedClasses_;
  result->producedMergeIUDIndicator_ = producedMergeIUDIndicator_;
  result->referencedMergeIUDIndicator_ = referencedMergeIUDIndicator_;

  return RelExpr::copyTopNode(result, outHeap);
}

PlanPriority GenericUpdate::computeOperatorPriority
(const Context* context,
 PlanWorkSpace *pws,
 Lng32 planNumber)
{

  PlanPriority result;

  NABoolean interactiveAccess =
    (CmpCommon::getDefault(INTERACTIVE_ACCESS) == DF_ON) OR
    ( QueryAnalysis::Instance() AND
      QueryAnalysis::Instance()->optimizeForFirstNRows());

  return result;
}

void GenericUpdate::addNewRecExprTree(ItemExpr *expr)
{
  ExprValueId newRec = newRecExprTree_;
  ItemExprTreeAsList(&newRec, ITM_AND).insert(expr);
  newRecExprTree_ = newRec.getPtr();
}


ItemExpr * GenericUpdate::removeNewRecExprTree()
{
  ItemExpr * result = newRecExprTree_;

  newRecExprTree_ = NULL;

  return result;
}

void GenericUpdate::addLocalExpr(LIST(ExprNode *) &xlist,
				 LIST(NAString) &llist) const
{

  if (newRecExprTree_ != NULL OR
      NOT newRecExpr_.isEmpty())
    {
      if (newRecExpr_.isEmpty())
	xlist.insert(newRecExprTree_);
      else
	xlist.insert(newRecExpr_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("new_rec_expr");
    }

  if ((isMerge()) &&
      (NOT mergeInsertRecExpr_.isEmpty()))
    {
      xlist.insert(mergeInsertRecExpr_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("merge_insert_rec_expr");
    }

  if ((isMerge()) &&
      (NOT mergeUpdatePred_.isEmpty()))
    {
      xlist.insert(mergeUpdatePred_.rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("merge_update_where_pred");
    }

  Int32 indexNo = 0;
  for(; indexNo < (Int32)indexNewRecExprArrays_.entries(); indexNo++) {
    ValueIdArray array = indexNewRecExprArrays_[indexNo];
    ValueIdList list;
    for(Int32 i = 0; i < (Int32)array.entries(); i++)
      list.insert(array[i]);
    xlist.insert(list.rebuildExprTree(ITM_ITEM_LIST));
    llist.insert("new idx rec expr");
  }

  if (executorPredTree_ != NULL OR
      NOT executorPred_.isEmpty())
    {
      if (executorPred_.isEmpty())
	xlist.insert(executorPredTree_);
      else
	xlist.insert(executorPred_.rebuildExprTree());
      llist.insert("predicate");
    }

  // display preds from search key only if begin/end keys are
  // not generated yet (e.g. during optimization)
  if (beginKeyPred_.isEmpty() AND endKeyPred_.isEmpty() AND
      pathKeys_ AND NOT pathKeys_->getKeyPredicates().isEmpty())
    {
      xlist.insert(pathKeys_->getKeyPredicates().rebuildExprTree());
      if (pathKeys_ == partKeys_)
	llist.insert("key_and_part_key_preds");
      else
	llist.insert("key_predicates");
    }

  // display part key preds only if different from clustering key preds
  if (partKeys_ AND pathKeys_ != partKeys_ AND
      NOT partKeys_->getKeyPredicates().isEmpty())
    {
      xlist.insert(partKeys_->getKeyPredicates().rebuildExprTree());
      llist.insert("part_key_predicates");
    }

  if (NOT beginKeyPred_.isEmpty())
    {
      xlist.insert(beginKeyPred_.rebuildExprTree(ITM_AND));
      llist.insert("begin_key");
    }

  for(indexNo = 0; indexNo < (Int32)indexBeginKeyPredArray_.entries(); indexNo++){
    if(NOT indexBeginKeyPredArray_[indexNo].isEmpty()) {
      xlist.insert(indexBeginKeyPredArray_[indexNo]
		   .rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("index_begin_key");
    }
  }

  if (NOT endKeyPred_.isEmpty())
    {
      xlist.insert(endKeyPred_.rebuildExprTree(ITM_AND));
      llist.insert("end_key");
    }

  for(indexNo = 0; indexNo < (Int32)indexEndKeyPredArray_.entries(); indexNo++) {
    if(NOT indexEndKeyPredArray_[indexNo].isEmpty()) {
      xlist.insert(indexEndKeyPredArray_[indexNo]
		   .rebuildExprTree(ITM_ITEM_LIST));
      llist.insert("index_end_key");
    }
  }

  if (NOT getCheckConstraints().isEmpty())
    {
      xlist.insert(getCheckConstraints().rebuildExprTree(ITM_AND));
      llist.insert("check_constraint");
    }

  if (preconditionTree_ != NULL OR
      precondition_.entries() > 0)
    {
      if (preconditionTree_ != NULL)
        xlist.insert(preconditionTree_);
      else
        xlist.insert(precondition_.rebuildExprTree(ITM_AND));
      llist.insert("precondition");
    }

  RelExpr::addLocalExpr(xlist,llist);
}

NABoolean GenericUpdate::updateCurrentOf()
{
  return currOfCursorName() != NULL
	   #ifndef NDEBUG
	     || getenv("FORCE_UPD_CURR_OF")
	   #endif
	   ;
}

//++MV - returns the GenericUpdateOutputFunction's that are in the
// potential outputs

NABoolean GenericUpdate::getOutputFunctionsForMV(ValueId &valueId,
                                                 OperatorTypeEnum opType) const
{
  const ValueIdSet& outputs = getGroupAttr()->getCharacteristicOutputs();
  for (ValueId vid= outputs.init();
       outputs.next(vid);
       outputs.advance(vid) )
  {
    ItemExpr *expr = vid.getItemExpr();

    if (expr->getOperatorType() == ITM_CAST)
      expr = expr->child(0);

    if (expr->getOperator().match(opType) && 
        expr->isAGenericUpdateOutputFunction() )
    {
      valueId = vid;
      return TRUE;
    }
  }

  return FALSE;
}

NABoolean GenericUpdate::computeRowsAffected() const
{
  if (rowsAffected_ == GenericUpdate::COMPUTE_ROWSAFFECTED)
    return TRUE;
  else
    return FALSE;
};

void GenericUpdate::configTSJforHalloween( Join* tsj, OperatorTypeEnum opType,
                                           CostScalar inputCardinality)
{
  if (avoidHalloween())
  {
    // If we use DP2's FELOCKSELF (i.e., DP2Locks) method to
    // protect against Halloween, then lock escalation will
    // be disabled in the Generator.  So DP2 wants us to use
    // no more than 25000 locks per volume.
    // Also, notice that
    // by design, we are relying on good cardinality estimates.
    // If the estimates are too low, then there may be
    // runtime errors.

    const PartitioningFunction *partFunc = getTableDesc()->
      getClusteringIndex()->getPartitioningFunction();
    const Lng32 numParts = partFunc ? partFunc->getCountOfPartitions() :
                          1;
    const Lng32 maxLocksAllParts = 25000 * numParts;

    if ((opType == REL_LEAF_INSERT) &&
        (inputCardinality < maxLocksAllParts)
             &&
         ! getHalloweenCannotUseDP2Locks() &&
         (CmpCommon::getDefault(BLOCK_TO_PREVENT_HALLOWEEN) != DF_ON)
       )
      tsj->setHalloweenForceSort(Join::NOT_FORCED);
    else
      tsj->setHalloweenForceSort(Join::FORCED);
  }
}

void GenericUpdate::pushdownCoveredExpr(const ValueIdSet &outputExpr,
				   const ValueIdSet &newExternalInputs,
				   ValueIdSet &predicatesOnParent,
				   const ValueIdSet *setOfValuesReqdByParent,
				    Lng32 childIndex
				  )
{

  // ---------------------------------------------------------------------
  // determine the set of local expressions that need to be evaluated
  // - assign expressions (reference source & target cols)
  // - source cols alone (in case order is required)
  // - characteristic outputs for this node
  // ---------------------------------------------------------------------
  // QSTUFF ?? again need to understand details 
  ValueIdSet localExprs(newRecExpr());

  if (setOfValuesReqdByParent)
    localExprs += *setOfValuesReqdByParent;

  // QSTUFF
  localExprs.insertList(newRecBeforeExpr());
  // QSTUFF

 
  if (isMerge())
    {
      localExprs.insertList(mergeInsertRecExpr());
    }

  localExprs.insertList(beginKeyPred());

  localExprs.insertList(updateToSelectMap().getBottomValues());
  if (setOfValuesReqdByParent)
    localExprs += *setOfValuesReqdByParent ;
  localExprs += exprsInDerivedClasses_;

  // ---------------------------------------------------------------------
  // Check which expressions can be evaluated by my child.
  // Modify the Group Attributes of those children who inherit some of
  // these expressions.
  // Since an GenericUpdate has no predicates, supply an empty set.
  // ---------------------------------------------------------------------
 RelExpr::pushdownCoveredExpr(	outputExpr,
				newExternalInputs,
				predicatesOnParent,
				&localExprs);
}

/*
NABoolean Insert::reconcileGroupAttr(GroupAttributes *newGroupAttr)
{
  SET(IndexDesc *) x;
  const IndexDesc* y = getTableDesc()->getClusteringIndex();
  x.insert((IndexDesc*)y);

  newGroupAttr->addToAvailableBtreeIndexes(x);

  // Now as usual
  return RelExpr::reconcileGroupAttr(newGroupAttr);
}
*/

// -----------------------------------------------------------------------
// member functions for class Insert
// -----------------------------------------------------------------------
Insert::Insert(const CorrName &name,
        TableDesc *tabId,
        OperatorTypeEnum otype,
        RelExpr *child ,
        ItemExpr *insertCols ,
        ItemExpr *orderBy ,
        CollHeap *oHeap ,
        InsertType insertType,
        NABoolean createUstatSample)
 : GenericUpdate(name,tabId,otype,child,NULL,NULL,oHeap),
   insertColTree_(insertCols),
   orderByTree_(orderBy),
   targetUserColPosList_(NULL),
   bufferedInsertsAllowed_(FALSE),
   insertType_(insertType),
   noBeginSTInsert_(FALSE),
   noCommitSTInsert_(FALSE),
   enableTransformToSTI_(FALSE),
   enableAqrWnrEmpty_(FALSE),
   systemGeneratesIdentityValue_(FALSE),
   insertSelectQuery_(FALSE),
   boundView_(NULL),
   overwriteHiveTable_(FALSE),
   isSequenceFile_(FALSE),
   isUpsert_(FALSE),
   isTrafLoadPrep_(FALSE),
   createUstatSample_(createUstatSample),
   xformedEffUpsert_(FALSE),
   baseColRefs_(NULL)
{
  insert_a_tuple_ = FALSE;
  if ( child ) {
    if ( child->getOperatorType() == REL_TUPLE ) {
      insert_a_tuple_ = TRUE;
      if (!name.isLocationNameSpecified()) {
        setCacheableNode(CmpMain::PARSE);
      }
    }
    else if ( child->getOperatorType() == REL_TUPLE_LIST &&
              !name.isLocationNameSpecified() ) {
      setCacheableNode(CmpMain::PARSE);
    }
  }
  else
	// this is a patch to pass regression for maximum parallelism project,
	// if we insert a default values not a real tuple the child is NULL
	// but we'd like to identify is as a tuple insert. March,2006
	if (CmpCommon::getDefault(COMP_BOOL_66) == DF_OFF)
	{
       insert_a_tuple_ = TRUE;
	}
}

Insert::~Insert() {}

void Insert::addInsertColTree(ItemExpr *expr)
{
  ExprValueId newCol = insertColTree_;
  ItemExprTreeAsList(&newCol, ITM_AND).insert(expr);
  insertColTree_ = newCol.getPtr();
}

ItemExpr * Insert::removeInsertColTree()
{
  ItemExpr * result = insertColTree_;

  insertColTree_ = NULL;

  return result;
}

ItemExpr * Insert::getInsertColTree()
{
  return insertColTree_;
}

const NAString Insert::getText() const
{
  NAString text("insert",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * Insert::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Insert *result;

  if (derivedNode == NULL)
    result = new (outHeap) Insert(getTableName(),
				  getTableDesc(),
				  getOperatorType(),
				  NULL,
				  NULL,
				  NULL,
				  outHeap,
				  getInsertType());
  else
    result = (Insert *) derivedNode;

  result->rrKeyExpr() = rrKeyExpr();
  result->partNumInput() = partNumInput();
  result->rowPosInput() = rowPosInput();
  result->totalNumPartsInput() = totalNumPartsInput();
  result->reqdOrder()  = reqdOrder();

  result->noBeginSTInsert_  = noBeginSTInsert_;
  result->noCommitSTInsert_ = noCommitSTInsert_;
  result->enableTransformToSTI() = enableTransformToSTI();
  result->enableAqrWnrEmpty()    = enableAqrWnrEmpty();

  if (insertColTree_ != NULL)
    result->insertColTree_ = insertColTree_->copyTree(outHeap)->castToItemExpr();

  result->insertATuple() = insertATuple();
  result->setInsertSelectQuery(isInsertSelectQuery());
  result->setOverwriteHiveTable(getOverwriteHiveTable());
  result->setSequenceFile(isSequenceFile());
  result->isUpsert_ = isUpsert_;
  result->isTrafLoadPrep_ = isTrafLoadPrep_;
  result->createUstatSample_ = createUstatSample_;
  result->xformedEffUpsert_ = xformedEffUpsert_;
  return GenericUpdate::copyTopNode(result, outHeap);
}

void Insert::setNoBeginCommitSTInsert(NABoolean noBeginSTI, NABoolean noCommitSTI)
{
  noBeginSTInsert_ = noBeginSTI;
  noCommitSTInsert_ = noCommitSTI;
}

// -----------------------------------------------------------------------
// member functions for class Update
// -----------------------------------------------------------------------
Update::Update(const CorrName &name,
               TableDesc *tabId,
               OperatorTypeEnum otype,
               RelExpr *child,
               ItemExpr *newRecExpr,
               ItemExpr *currOfCursorName,
               CollHeap *oHeap)
     : GenericUpdate(name,tabId,otype,child,newRecExpr,currOfCursorName,oHeap),
       estRowsAccessed_(0)
{
  setCacheableNode(CmpMain::BIND);
}

Update::~Update() {}

const NAString Update::getText() const
{
  NAString text("update",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * Update::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Update *result;

  if (derivedNode == NULL)
    result = new (outHeap) Update(getTableName(),
				  getTableDesc(),
				  getOperatorType(),
				  NULL,
				  NULL, NULL,
				  outHeap);
  else
    result = (Update *) derivedNode;

  result->setEstRowsAccessed(getEstRowsAccessed());

  return GenericUpdate::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class MergeUpdate
// -----------------------------------------------------------------------
MergeUpdate::MergeUpdate(const CorrName &name,
			 TableDesc *tabId,
			 OperatorTypeEnum otype,
			 RelExpr *child,
			 ItemExpr *setExpr,
			 ItemExpr *insertCols,
			 ItemExpr *insertValues,
			 CollHeap *oHeap,
			 ItemExpr *where)
     : Update(name,tabId,otype,child,setExpr,NULL,oHeap),
       insertCols_(insertCols), insertValues_(insertValues),
       where_(where), xformedUpsert_(FALSE), needsBindScope_(TRUE)
{
  setCacheableNode(CmpMain::BIND);
  
  setIsMergeUpdate(TRUE);

  // if there is a WHERE NOT MATCHED INSERT action, then the scan
  // has to take place in the merge node at run time, so we have
  // to suppress the TSJ transformation on this node
  if (insertValues)
    setNoFlow(TRUE);
}

MergeUpdate::~MergeUpdate() {}

const NAString MergeUpdate::getText() const
{
  NAString text("merge_update",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * MergeUpdate::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  MergeUpdate *result;

  if (derivedNode == NULL)
    result = new (outHeap) MergeUpdate(getTableName(),
				       getTableDesc(),
				       getOperatorType(),
				       child(0),
				       NULL,
				       insertCols(), insertValues(),
				       outHeap, where_);
  else
    result = (MergeUpdate *) derivedNode;
  if (xformedUpsert())
    result->setXformedUpsert();

  return Update::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class Delete
// -----------------------------------------------------------------------

Delete::Delete(const CorrName &name, TableDesc *tabId, OperatorTypeEnum otype,
               RelExpr *child, ItemExpr *newRecExpr,
               ItemExpr *currOfCursorName, 
	       ConstStringList * csl,
	       CollHeap *oHeap)
  : GenericUpdate(name,tabId,otype,child,newRecExpr,currOfCursorName,oHeap),
    isFastDelete_(FALSE),
    csl_(csl),estRowsAccessed_(0)
{
  setCacheableNode(CmpMain::BIND);
}

Delete::~Delete() {}

const NAString Delete::getText() const
{
  NAString text("delete",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * Delete::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  Delete *result;

  if (derivedNode == NULL)
    result = new (outHeap) Delete(getTableName(),
				  getTableDesc(),
				  getOperatorType(),
				  NULL,
				  NULL, NULL,
				  csl_,
				  outHeap);
  else
    result = (Delete *) derivedNode;

  result->isFastDelete_       = isFastDelete_;
  result->csl() = csl();
  result->setEstRowsAccessed(getEstRowsAccessed());

  return GenericUpdate::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class MergeDelete
// -----------------------------------------------------------------------
MergeDelete::MergeDelete(const CorrName &name,
			 TableDesc *tabId,
			 OperatorTypeEnum otype,
			 RelExpr *child,
			 ItemExpr *insertCols,
			 ItemExpr *insertValues,
			 CollHeap *oHeap)
  : Delete(name,tabId,otype,child,NULL,NULL,NULL,oHeap),
       insertCols_(insertCols), insertValues_(insertValues)
{
  setCacheableNode(CmpMain::BIND);
  
  setIsMergeDelete(TRUE);

  // if there is a WHERE NOT MATCHED INSERT action, then the scan
  // has to take place in the merge node at run time, so we have
  // to suppress the TSJ transformation on this node
  if (insertValues)
    setNoFlow(TRUE);
}

MergeDelete::~MergeDelete() {}

const NAString MergeDelete::getText() const
{
  NAString text("merge_delete",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * MergeDelete::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  MergeDelete *result;

  if (derivedNode == NULL)
    result = new (outHeap) MergeDelete(getTableName(),
				       getTableDesc(),
				       getOperatorType(),
				       child(0),
				       insertCols(), insertValues(),
				       outHeap);
  else
    result = (MergeDelete *) derivedNode;

  return Delete::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class InsertCursor
// -----------------------------------------------------------------------

InsertCursor::~InsertCursor() {}

NABoolean InsertCursor::isLogical() const { return FALSE; }

NABoolean InsertCursor::isPhysical() const { return TRUE; }

const NAString InsertCursor::getText() const
{
  NAString text("insert",  CmpCommon::statementHeap());

  if ((insertType_ == VSBB_INSERT_SYSTEM) ||
      (insertType_ == VSBB_INSERT_USER))
    text = text + "_vsbb";
  else if ((insertType_ == VSBB_LOAD) ||
	   (insertType_ == VSBB_LOAD_APPEND) ||
	   (insertType_ == VSBB_LOAD_NO_DUP_KEY_CHECK) ||
	   (insertType_ == VSBB_LOAD_APPEND_NO_DUP_KEY_CHECK))
    text = text + "_sidetree";
  else if (insertType_ == VSBB_LOAD_AUDITED)
    text = text + "_sidetree_audited";

  text = text + " (physical)";

  return (text + " " + getUpdTableNameText());
}

RelExpr * InsertCursor::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) InsertCursor(getTableName(),
				        getTableDesc(),
				        getOperatorType(),
					NULL,
					outHeap);
  else
    result = derivedNode;

  return Insert::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class HiveInsert
// -----------------------------------------------------------------------
const NAString HiveInsert::getText() const
{
  NAString text("hive_insert",  CmpCommon::statementHeap());

  text += " (physical)";

  return (text + " " + getUpdTableNameText());
}

RelExpr * HiveInsert::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) HiveInsert(getTableName(),
				        getTableDesc(),
				        getOperatorType(),
					NULL,
					outHeap);
  else
    result = derivedNode;

  return Insert::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class HbaseInsert
// -----------------------------------------------------------------------
const NAString HbaseInsert::getText() const
{
  NABoolean isSeabase = 
    (getTableDesc() && getTableDesc()->getNATable() ? 
     getTableDesc()->getNATable()->isSeabaseTable() : FALSE);

  NAString text;

  if (NOT isSeabase)
    text = "hbase_";
  else
    text = "trafodion_";

  if (isUpsert())
    {
      if (getInsertType() == Insert::UPSERT_LOAD)
        {
          if (getIsTrafLoadPrep())
            text += "load_preparation";
          else
            text += "load";
        }
      else if (vsbbInsert())
	text += "vsbb_upsert";
      else
        text += "upsert";
    }
  else
    {
      if (vsbbInsert())
	text += "vsbb_upsert";
      else
        text += "insert";
    }
  
  return (text + " " + getUpdTableNameText());
}

RelExpr * HbaseInsert::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  HbaseInsert *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseInsert(getTableName(),
				        getTableDesc(),
				        getOperatorType(),
					NULL,
					outHeap);
  else
    result = (HbaseInsert *) derivedNode;
  
  result->returnRow_ = returnRow_;

  return Insert::copyTopNode(result, outHeap);
}

RelExpr * HBaseBulkLoadPrep::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) HbaseInsert(getTableName(),
                                        getTableDesc(),
                                        getOperatorType(),
                                        NULL,
                                        outHeap);
  else
    result = derivedNode;

  return Insert::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class UpdateCursor
// -----------------------------------------------------------------------

UpdateCursor::~UpdateCursor() {}

NABoolean UpdateCursor::isLogical() const { return FALSE; }

NABoolean UpdateCursor::isPhysical() const { return TRUE; }

const NAString UpdateCursor::getText() const
{
  NAString text("cursor_update",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}


RelExpr * UpdateCursor::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) UpdateCursor(getTableName(),
				        getTableDesc(),
				        getOperatorType(),
					NULL,
					outHeap);
  else
    result = derivedNode;

  return Update::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// member functions for class DeleteCursor
// -----------------------------------------------------------------------

DeleteCursor::~DeleteCursor() {}

NABoolean DeleteCursor::isLogical() const { return FALSE; }

NABoolean DeleteCursor::isPhysical() const { return TRUE; }

const NAString DeleteCursor::getText() const
{
  NAString text("cursor_delete",CmpCommon::statementHeap());

  return (text + " " + getUpdTableNameText());
}

RelExpr * DeleteCursor::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
  RelExpr *result;

  if (derivedNode == NULL)
    result = new (outHeap) DeleteCursor(getTableName(),
				        getTableDesc(),
				        getOperatorType(),
					NULL,
					outHeap);
  else
    result = derivedNode;

  return Delete::copyTopNode(result, outHeap);
}


/////////////////////////////////////////////////////////////////////

void
RelExpr::unparse(NAString &result,
		 PhaseEnum /* phase */,
		 UnparseFormatEnum /* form */,
		 TableDesc * tabId) const
{
  result += getText();

#ifndef NDEBUG
  if (getenv("UNPARSE_FULL"))
    {
      if (selection_)
	{
	  result += "[";
	  selection_->unparse(result /*, phase, form */);
	  result += "]";
	}
      if (predicates_.entries())
	{
	  result += "{";
	  predicates_.unparse(result /*, phase, form */);
	  result += "}";
	}
    }
#endif

  Int32 maxi = getArity();
  if (maxi)
    {
      result += "(";
      for (Lng32 i = 0; i < maxi; i++)
	{
	  if (i > 0)
	    result += ", ";
          if ( child(i).getPtr() == NULL )
            continue;
	  child(i)->unparse(result);
	}
      result += ")";
    }
}

// -----------------------------------------------------------------------
// methods for class Transpose
// -----------------------------------------------------------------------

// Transpose::~Transpose() -----------------------------------------------
// The destructor
//
Transpose::~Transpose()
{
}

// Transpose::topHash() --------------------------------------------------
// Compute a hash value for a chain of derived RelExpr nodes.
// Used by the Cascade engine as a quick way to determine if
// two nodes are identical.
// Can produce false positives (nodes appear to be identical),
// but should not produce false negatives (nodes are definitely different)
//
// Inputs: none (other than 'this')
//
// Outputs: A HashValue of this node and all nodes in the
// derivation chain below (towards the base class) this node.
//
HashValue Transpose::topHash()
{
  // Compute a hash value of the derivation chain below this node.
  //
  HashValue result = RelExpr::topHash();

  // transUnionVector is the only relevant
  // data members at this point. The other data members do not
  // live past the binder.
  //
  for(CollIndex i = 0; i < transUnionVectorSize(); i++) {
    result ^= transUnionVector()[i];
  }

  return result;
}

// Transpose::duplicateMatch()
// A more thorough method to compare two RelExpr nodes.
// Used by the Cascades engine when the topHash() of two
// nodes returns the same hash values.
//
// Inputs: other - a reference to another node of the same type.
//
// Outputs: NABoolean - TRUE if this node is 'identical' to the
//          'other' node. FALSE otherwise.
//
// In order to match, this node must match all the way down the
// derivation chain to the RelExpr class.
//
// For the Transpose node, the only relevant data member which
// needs to be compared is transUnionVals_. The other data members
// do not exist passed the binder.
//
NABoolean
Transpose::duplicateMatch(const RelExpr & other) const
{
  // Compare this node with 'other' down the derivation chain.
  //
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  // Cast the RelExpr to a Transpose node. (This must be a Transpose node)
  //
  Transpose &o = (Transpose &) other;

  // If the transUnionVectors are the same size and have the same entries,
  // then the nodes are identical
  //
  if(transUnionVectorSize() != o.transUnionVectorSize())
    return FALSE;

  for(CollIndex i = 0; i < transUnionVectorSize(); i++) {
    if (!(transUnionVector()[i] == o.transUnionVector()[i]))
      return FALSE;
  }

  return TRUE;
}

// Transpose::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls RelExpr::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
//         which is derived from this node.  If NULL, then this
//         node is the top of the derivation chain and a node must
//         be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and a transpose
// node must be constructed.
//
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (RelExpr::copyTopNode() in this
// case).
//
RelExpr *
Transpose::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
  Transpose *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Create an empty Transpose node.
    //
    result = new (outHeap) Transpose(NULL,NULL,NULL,outHeap);
  else
    // A node has already been constructed as a derived class.
    //
    result = (Transpose *) derivedNode;

  // Copy the relavant fields.

  result->transUnionVectorSize_ = transUnionVectorSize();

  result->transUnionVector() =
    new (outHeap) ValueIdList[transUnionVectorSize()];

  for(CollIndex i = 0; i < transUnionVectorSize(); i++) {
    result->transUnionVector()[i] = transUnionVector()[i];
  }

  // copy pointer to expressions
  // These are not available after bindNode()
  //
  if (transValsTree_ != NULL)
    result->transValsTree_ = transValsTree_->copyTree(outHeap)->castToItemExpr();

  if (keyCol_ != NULL)
    result->keyCol_ = keyCol_->copyTree(outHeap)->castToItemExpr();


  // Copy any data members from the classes lower in the derivation chain.
  //
  return RelExpr::copyTopNode(result, outHeap);
}


// Transpose::addLocalExpr() -----------------------------------------------
// Insert into a list of expressions all the expressions of this node and
// all nodes below this node in the derivation chain. Insert into a list of
// names, all the names of the expressions of this node and all nodes below
// this node in the derivation chain. This method is used by the GUI tool
// and by the Explain Function to have a common method to get all the
// expressions associated with a node.
//
// Inputs/Outputs: xlist - a list of expressions.
//                 llist - a list of names of expressions.
//
// The xlist contains a list of all the expressions associated with this
// node. The llist contains the names of these expressions. (This lists
// must be kept in the same order).
// Transpose::addLocalExpr potentially adds the transUnionVals_ expression
// ("transpose_union_values"), the transValsTree_ expression
// ("transpose_values"), and the keyCol_ expression ("key_column").
//
// It then calls RelExpr::addLocalExpr() which will add any RelExpr
// expressions to the list.
//
void Transpose::addLocalExpr(LIST(ExprNode *) &xlist,
			     LIST(NAString) &llist) const
{

  for(CollIndex i = 0; i < transUnionVectorSize(); i++) {
    if (NOT transUnionVector()[i].isEmpty()) {
      xlist.insert(transUnionVector()[i].rebuildExprTree());
      llist.insert("transpose_union_vector");
    }
  }

  // This is only available as an ItemExpr tree.  It is never
  // stored as a ValueIdSet.  This is not available after bindNode().
  //
  if(transValsTree_) {
    xlist.insert(transValsTree_);
    llist.insert("transpose_values");
  }

  // This is only available as an ItemExpr tree.  It is never
  // stored as a ValueIdSet.  This is not available after bindNode().
  //
  if(keyCol_) {
    xlist.insert(keyCol_);
    llist.insert("key_column");
  }

  RelExpr::addLocalExpr(xlist,llist);
}

// Transpose::getPotentialOutputValues() ---------------------------------
// Construct a Set of the potential outputs of this node.
//
// Inputs: none (other than 'this')
//
// Outputs: outputValues - a ValueIdSet representing the potential outputs
//          of this node.
//
// The potential outputs for the transpose node are the new columns
// generated by the transpose node, plus the outputs produced by the
// child node.  The new columns generated by transpose are the key
// column and the value colunms (one for each transpose group).
//
void
Transpose::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  // Make sure the ValueIdSet is empty.
  //
  outputValues.clear();

  // Add the values generated by the transpose node.
  //
  for(CollIndex i = 0; i < transUnionVectorSize(); i++) {
    outputValues.insertList( transUnionVector()[i] );
  }

  // Add the values produced by the child.
  //
  outputValues += child(0).getGroupAttr()->getCharacteristicOutputs();

} // Transpose::getPotentialOutputValues()


// Transpose::pushdownCoveredExpr() ------------------------------------
//
// In order to compute the Group Attributes for a relational operator
// an analysis of all the scalar expressions associated with it is
// performed. The purpose of this analysis is to identify the sources
// of the values that each expression requires. As a result of this
// analysis values are categorized as external dataflow inputs or
// those that can be produced completely by a certain child of the
// relational operator.
//
// This method is invoked on each relational operator. It causes
// a) the pushdown of predicates and
// b) the recomputation of the Group Attributes of each child.
//    The recomputation is required either because the child is
//    assigned new predicates or is expected to compute some of the
//    expressions that are required by its parent.
//
// Parameters:
//
// const ValueIdSet &setOfValuesReqdByParent
//    IN:  a read-only reference to a set of expressions that are
//         associated with this operator. Typically, they do not
//         include the predicates.
//
// ValueIdSet & newExternalInputs
//    IN : a reference to a set of new external inputs (ValueIds)
//         that are provided by this operator for evaluating the
//         the above expressions.
//
// ValueIdSet & predicatesOnParent
//    IN : the set of predicates existing on the operator
//    OUT: a subset of the original predicates. Some of the
//         predicate factors may have been pushed down to
//         the operator's children.
//
// long childIndex
//    IN : This is an optional parameter.
//         If supplied, it is a zero-based index for a specific child
//         on which the predicate pushdown should be attempted.
//         If not supplied, or a null pointer is supplied, then
//         the pushdown is attempted on all the children.
//
// ---------------------------------------------------------------------
void Transpose::pushdownCoveredExpr(const ValueIdSet &outputExpr,
			       const ValueIdSet &newExternalInputs,
			       ValueIdSet &predicatesOnParent,
			       const ValueIdSet *setOfValuesReqdByParent,
			       Lng32 childIndex
			       )
{

  ValueIdSet exprOnParent;
  if (setOfValuesReqdByParent)
    exprOnParent = *setOfValuesReqdByParent;

  // Add all the values required for the transpose expressions
  // to the values required by the parent.
  // Don't add the valueIds of the ValueIdUnion nodes, but the
  // valueIds of the contents of the ValueIdUnion nodes.
  //
  for(CollIndex v = 0; v < transUnionVectorSize(); v++) {
    ValueIdList &valIdList = transUnionVector()[v];

    for(CollIndex i = 0; i < valIdList.entries(); i++) {
      ValueIdUnion *valIdu = ((ValueIdUnion *)valIdList[i].
			      getValueDesc()->getItemExpr());

      exprOnParent.insertList(valIdu->getSources());
    }
  }

  ValueIdSet pushablePredicates(predicatesOnParent);

  RelExpr::pushdownCoveredExpr(outputExpr,
                               newExternalInputs,
                               pushablePredicates,
			       &exprOnParent,
                               childIndex);

  predicatesOnParent.intersectSet(pushablePredicates);
} // Transpose::pushdownCoveredExpr

// Transpose::removeTransValsTree() -------------------------------------
// Return the transValsTree_ ItemExpr tree and set to NULL,
//
// Inputs: none (Other than 'this')
//
// Outputs: ItemExpr * - the value of transValsTree_
//
// Side Effects: Sets the value of transValsTree_ to NULL.
//
// Called by Transpose::bindNode(). The value of transValsTree_ is not
// needed after the binder.
//
const ItemExpr *
Transpose::removeTransValsTree()
{
  ItemExpr *result = transValsTree_;
  transValsTree_ = (ItemExpr *)NULL;
  return result;
}

// Transpose::removeKeyCol() -------------------------------------
// Return the keyCol_ ItemExpr tree and set it to NULL,
//
// Inputs: none (Other than 'this')
//
// Outputs: ItemExpr * - the value of keyCol_
//
// Side Effects: Sets the value of keyCol_ to NULL.
//
// Call by Transpose::bindNode(). The value of keyCol_ is not
// needed after the binder.
//
const ItemExpr *
Transpose::removeKeyCol()
{
  ItemExpr *result = keyCol_;
  keyCol_ = (ItemExpr *)NULL;
  return result;
}

// This method is used in case there are expressions in the Transpose column
// list. It traverses through the expression to get the column under them
// If it is a unary expression, it gets the column directly below the expression.
// If the expression has two children, it goes through both the children
// to see which one of them has a higher UEC. It returns the ValueId of that
// column. This column is later used to determine the UEC of the final
// transpose column.

ValueId Transpose::getSourceColFromExprForUec(ValueId sourceValId,
					      const ColStatDescList & childColStatsList)
{

  if (sourceValId.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE)
    return sourceValId;

  ValueIdSet vegCols;
  sourceValId.getItemExpr()->
    findAll(ITM_VEG_REFERENCE, vegCols, TRUE, TRUE);

  // case 1 : expression with a constant, return sourceValId
  // case 2 :(only one expr) concentrates on simple expressions only
  // case 3 :(Multiple exprs) Expression of type EXPR1 OP EXPR2
  // where EXPR1 , EXPR2 is VEGREF or EXPR we will assume the max UEC
  // admist the list of base columns found will be used.
  // This is an approximation but better that the worst case.
  if (vegCols.entries() == 0)
  {
    // case 1
    return sourceValId;
  }
  if(vegCols.entries() == 1)
  {
    // case 2
    // There is only one get that.
    vegCols.getFirst(sourceValId);
  }
  else
  {
    //case 3
    //Initialize for safety.
    vegCols.getFirst(sourceValId);
    //CostScalars are initialized by their constructor to zero.
    CostScalar currentMaxUEC,currentUEC;
    CollIndex index = NULL_COLL_INDEX;

    for(ValueId currentValId = vegCols.init()
	;vegCols.next(currentValId)
	;vegCols.advance(currentValId))
    {
       index = NULL_COLL_INDEX;
       childColStatsList.getColStatDescIndex(index, currentValId);
       if (index == NULL_COLL_INDEX) continue;

       currentUEC = childColStatsList[index]->getColStats()
		       ->getTotalUec();
       //get the UEC and find the max and corresponding valueID
       //and assign it ti sourceValId.
       if(currentUEC > currentMaxUEC)
       {
	   currentMaxUEC = currentUEC;
	   sourceValId = currentValId;
       }
    }// end of for
  }//end of elsif
  return sourceValId;
}

// The destructor
//
PhysTranspose::~PhysTranspose()
{
}

// PhysTranspose::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls Transpose::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
//         which is derived from this node.  If NULL, then this
//         node is the top of the derivation chain and a node must
//         be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and a transpose
// node must be constructed.
//
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (Transpose::copyTopNode() in this
// case).
//
RelExpr *
PhysTranspose::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
  PhysTranspose *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Generate an empty PhysTranspose node.
    //
    result = new (outHeap) PhysTranspose(NULL,outHeap);
  else
    // A node has already been constructed as a derived class.
    //
    result = (PhysTranspose *) derivedNode;

  // PhysTranspose has no data members.

  // Copy any data members from the classes lower in the derivation chain.
  //
  return Transpose::copyTopNode(result, outHeap);
}

// -----------------------------------------------------------------------
// methods for class Pack
// -----------------------------------------------------------------------

// Constructor
Pack::Pack(ULng32 pf,
           RelExpr* child,
           ItemExpr* packingExprTree,
           CollHeap* oHeap)
 : RelExpr(REL_PACK,child,NULL,oHeap),
   packingFactorLong_(pf),
   packingFactorTree_(NULL),
   packingExprTree_(packingExprTree)
{
  setNonCacheable();
  packingFactor().clear();
  packingExpr().clear();
  requiredOrder_.clear();
}

// Destructor.
Pack::~Pack()
{
}

// -----------------------------------------------------------------------
// Pack:: some Accessors/Mutators.
// -----------------------------------------------------------------------

ItemExpr* Pack::removePackingFactorTree()
{
  ItemExpr* pf = packingFactorTree_;
  packingFactorTree_ = NULL;
  return pf;
}

ItemExpr* Pack::removePackingExprTree()
{
  ItemExpr* pe = packingExprTree_;
  packingExprTree_ = NULL;
  return pe;
}

// -----------------------------------------------------------------------
// Pack::getText()
// -----------------------------------------------------------------------
const NAString Pack::getText() const
{
  return "PACK";
}

// -----------------------------------------------------------------------
// Pack::topHash()
// -----------------------------------------------------------------------
HashValue Pack::topHash()
{
  // The base class's topHash deals with inputs/outputs and operator type.
  HashValue result = RelExpr::topHash();

  // Packing factor and packing expression are the differentiating factors.
  result ^= packingFactor();
  result ^= packingExpr();
  result ^= requiredOrder();

  return result;
}

// -----------------------------------------------------------------------
// Pack::duplicateMatch()
// -----------------------------------------------------------------------
NABoolean Pack::duplicateMatch(const RelExpr& other) const
{
  // Assume optimizer already matches inputs/outputs in Group Attributes.

  // Base class checks for operator type, predicates and children.
  if(NOT RelExpr::duplicateMatch(other)) return FALSE;

  // Base class implementation already makes sure other is a Pack node.
  Pack& otherPack = (Pack &) other;

  // If the required order keys are not the same
  // then the nodes are not identical
  //
  if (!(requiredOrder() == otherPack.requiredOrder()))
    return FALSE;

  // Packing factor is the only remaining thing to check.
  return (packingFactor_ == otherPack.packingFactor() AND
                                 packingExpr_ == otherPack.packingExpr());
}

// -----------------------------------------------------------------------
// Pack::copyTopNode()
// -----------------------------------------------------------------------
RelExpr* Pack::copyTopNode(RelExpr* derivedNode, CollHeap* outHeap)
{
  Pack* result;

  // This the real node we want to copy. Construct a new Pack node.
  if(derivedNode == NULL)
  {
    result = new (outHeap) Pack (packingFactorLong(),NULL,NULL,outHeap);
    result->packingFactor() = packingFactor();
    result->packingExpr() = packingExpr();
    //result->setRequiredOrder(requiredOrder());
    result->requiredOrder() = requiredOrder();
    result->setFirstNRows(getFirstNRows());
  }
  else
  // ---------------------------------------------------------------------
  // The real node we want to copy is of a derived class. The duplicate
  // has already been made and store in derived node. All I need to do is
  // to copy the members stored with this base class.
  // ---------------------------------------------------------------------
  {
    result = (Pack *) derivedNode;
    result->packingFactorLong() = packingFactorLong();
    result->packingFactor() = packingFactor();
    result->packingExpr() = packingExpr();

    result->requiredOrder() = requiredOrder();
    result->setFirstNRows(getFirstNRows());
  }

  // Call base class to make copies of its own data members.
  return RelExpr::copyTopNode(result,outHeap);
}

// -----------------------------------------------------------------------
// Pack::getPotentialOutputValues()
// -----------------------------------------------------------------------
void Pack::getPotentialOutputValues(ValueIdSet& outputValues) const
{
  // Just the outputs of the packing expression.
  outputValues.clear();
  outputValues.insertList(packingExpr_);
}

// -----------------------------------------------------------------------
// Pack::getNonPackedExpr() returns the non-packed sub-expressions of the
// packing expression.
// -----------------------------------------------------------------------
void Pack::getNonPackedExpr(ValueIdSet& vidset)
{
  for(CollIndex i = 0; i < packingExpr().entries(); i++)
  {
    ItemExpr* packItem = packingExpr().at(i).getItemExpr();
    vidset.insert(packItem->child(0)->getValueId());
  }
}

// -----------------------------------------------------------------------
// Pack::pushdownCoveredExpr() needs to add the sub-expressions of the
// packing expression to nonPredExprOnOperator and then make use of the
// default implementation. It is expected in the first phase, nothing
// can be pushed down though.
// -----------------------------------------------------------------------
void Pack::pushdownCoveredExpr(const ValueIdSet& outputExpr,
                               const ValueIdSet& newExternalInputs,
                               ValueIdSet& predOnOperator,
			       const ValueIdSet* nonPredExprOnOperator,
			       Lng32 childId)
{
  ValueIdSet exprNeededByOperator;
  getNonPackedExpr(exprNeededByOperator);
  if (nonPredExprOnOperator)
    exprNeededByOperator += *nonPredExprOnOperator;
  exprNeededByOperator.insertList(requiredOrder());

  RelExpr::pushdownCoveredExpr(outputExpr,
                               newExternalInputs,
                               predOnOperator,
			       &exprNeededByOperator,
                               childId);
}

// -----------------------------------------------------------------------
// Pack::addLocalExpr() adds the packing expressions to be displayed by
// the GUI debugger.
// -----------------------------------------------------------------------
void Pack::addLocalExpr(LIST(ExprNode*)& xlist,
                        LIST(NAString)& llist) const
{
  if(packingExprTree_ != NULL)
  {
    xlist.insert(packingExprTree_);
    llist.insert("pack_expr_tree");
  }

  if (requiredOrder().entries() > 0) {
    xlist.insert(requiredOrder().rebuildExprTree(ITM_ITEM_LIST));
    llist.insert("required_order");
  }

  if(NOT packingExpr_.isEmpty())
  {
    xlist.insert(packingExpr_.rebuildExprTree());
    llist.insert("pack_expr");
  }

  RelExpr::addLocalExpr(xlist,llist);
}

// -----------------------------------------------------------------------
// methods for class PhyPack
// -----------------------------------------------------------------------

// Destructor.
PhyPack::~PhyPack()
{
}

// -----------------------------------------------------------------------
// PhyPack::copyTopNode()
// -----------------------------------------------------------------------
RelExpr* PhyPack::copyTopNode(RelExpr* derivedNode, CollHeap* outHeap)
{
  PhyPack* result;

  // This the real node we want to copy. Construct a new PhyPack node.
  if(derivedNode == NULL)
  {
    result = new (outHeap) PhyPack (0,NULL,outHeap);
  }
  else
  // ---------------------------------------------------------------------
  // The real node we want to copy is of a derived class. The duplicate
  // has already been made and store in derived node. All I need to do is
  // to copy the members stored with this base class.
  // ---------------------------------------------------------------------
  {
    result = (PhyPack *) derivedNode;
  }

  // Tell base class to copy its members. PhyPack has no added members.
  return Pack::copyTopNode(result,outHeap);
}


// -----------------------------------------------------------------------
// methods for class Rowset
// -----------------------------------------------------------------------
// Constructor
Rowset::Rowset(ItemExpr *inputHostvars, ItemExpr *indexExpr,
               ItemExpr *sizeExpr, RelExpr * childExpr, CollHeap* oHeap)
 : RelExpr(REL_ROWSET,childExpr,NULL,oHeap),
   inputHostvars_(inputHostvars),
   indexExpr_(indexExpr),
   sizeExpr_(sizeExpr)
{
  setNonCacheable();
} // Rowset::Rowset()

// Destructor.
Rowset::~Rowset()
{
} // Rowset::~Rowset()

RelExpr * Rowset::copyTopNode(RelExpr *derivedNode,
                              CollHeap* oHeap)
{
  Rowset *result;

  if (derivedNode == NULL)
    result = new (oHeap) Rowset(inputHostvars_, indexExpr_, sizeExpr_, NULL, oHeap);
  else {
    result = (Rowset *) derivedNode;
  }

  return RelExpr::copyTopNode(result,oHeap);
} // Rowset::copyTopNode()

Int32 Rowset::getArity() const
{
  return 0; // This is a leaf node
} // Rowset::getArity()


const NAString Rowset::getText() const
{
  NAString result("RowSet",CmpCommon::statementHeap());

  if (sizeExpr_) {
    if (sizeExpr_->getOperatorType() == ITM_CONSTANT) {
      char str[TEXT_DISPLAY_LENGTH];
      sprintf(str, " " PF64,
              ((ConstValue *)sizeExpr_)->getExactNumericValue());
      result += str;
    } else if (sizeExpr_->getOperatorType() == ITM_HOSTVAR)
      result += " " + ((HostVar *)sizeExpr_)->getName();
    else
      result += " ??";
  }

  result += " (";
  for (ItemExpr *hostVarTree = inputHostvars_;
       hostVarTree != NULL;
       hostVarTree = hostVarTree->child(1)) {
    if (inputHostvars_ != hostVarTree)
      result += ", ";

    HostVar *hostVar = (HostVar *)hostVarTree->getChild(0);
    result += hostVar->getName();
  }
  result += ")";

  if (indexExpr_)
    result += ("KEY BY " +
               ((ColReference *)indexExpr_)->getColRefNameObj().getColName());

  return result;
}

// returns the name of the exposed index of the Rowset
const NAString Rowset::getIndexName() const
{
     // A hack to check if the Rowset has an index expression
     NAString result("",CmpCommon::statementHeap());

     if (indexExpr_)
       result += ((ColReference *)indexExpr_)->getColRefNameObj().getColName();
     return(result);
}

// -----------------------------------------------------------------------
// methods for class Rowset
// -----------------------------------------------------------------------
// Constructor
RowsetRowwise::RowsetRowwise(RelExpr * childExpr,
			     CollHeap* oHeap)
 : Rowset(NULL, NULL, NULL, childExpr, oHeap)
{
} // RowsetRowwise::RowsetRowwise()

RelExpr * RowsetRowwise::copyTopNode(RelExpr *derivedNode,
				     CollHeap* oHeap)
{
  Rowset *result;

  if (derivedNode == NULL)
    result = new (oHeap) RowsetRowwise(NULL, oHeap);
  else {
    result = (RowsetRowwise *) derivedNode;
  }

  return Rowset::copyTopNode(result,oHeap);
} // RowsetRowwise::copyTopNode()

const NAString RowsetRowwise::getText() const
{
  NAString result("RowSet Rowwise",CmpCommon::statementHeap());

  return result;
}

Int32 RowsetRowwise::getArity() const
{
  return 1; 
} // Rowset::getArity()

RowsetFor::RowsetFor(RelExpr  *child,
                     ItemExpr *inputSizeExpr,
                     ItemExpr *outputSizeExpr,
                     ItemExpr *indexExpr,
		     ItemExpr *maxSizeExpr,
		     ItemExpr *maxInputRowlen,
		     ItemExpr *rwrsBuffer,
		     ItemExpr *partnNum,
                     CollHeap *oHeap)
  : RelExpr(REL_ROWSETFOR,child,NULL,oHeap),
    inputSizeExpr_(inputSizeExpr),
    outputSizeExpr_(outputSizeExpr),
    indexExpr_(indexExpr),
    maxSizeExpr_(maxSizeExpr),
    maxInputRowlen_(maxInputRowlen),
    rwrsBuffer_(rwrsBuffer),
    partnNum_(partnNum),
    rowwiseRowset_(FALSE),
    packedFormat_(FALSE),
    compressed_(FALSE),
    dcompressInMaster_(FALSE),
    compressInMaster_(FALSE),
    partnNumInBuffer_(FALSE)
{
  setNonCacheable(); 
}

// Destructor.
RowsetFor::~RowsetFor()
{
}

RelExpr * RowsetFor::copyTopNode(RelExpr *derivedNode,
                                 CollHeap* oHeap)
{
  RowsetFor *result;

  if (derivedNode == NULL)
    result = new (oHeap) RowsetFor(NULL, inputSizeExpr_, outputSizeExpr_,
                                   indexExpr_, 
				   maxSizeExpr_, maxInputRowlen_,
				   rwrsBuffer_, partnNum_, oHeap);
  else
    result = (RowsetFor *) derivedNode;

  result->rowwiseRowset_ = rowwiseRowset_;
  result->setBufferAttributes(packedFormat_,
			      compressed_, dcompressInMaster_,
			      compressInMaster_, partnNumInBuffer_);

  return RelExpr::copyTopNode(result,oHeap);
}

Int32 RowsetFor::getArity() const
{
  return 1;
} // RowsetFor::getArity()

const NAString RowsetFor::getText() const
{
  NAString result("RowSetFor ", CmpCommon::statementHeap());

  if (inputSizeExpr_) {
    if (inputSizeExpr_->getOperatorType() == ITM_CONSTANT) {
      char str[TEXT_DISPLAY_LENGTH];
      sprintf(str, PF64,
              ((ConstValue *)inputSizeExpr_)->getExactNumericValue());
      result += "INPUT SIZE ";
      result += str;
    } else if (inputSizeExpr_->getOperatorType() == ITM_HOSTVAR)
      result += "INPUT SIZE " + ((HostVar *)inputSizeExpr_)->getName();
    else
      result += "INPUT SIZE ??";

    if (outputSizeExpr_ || indexExpr_)
      result += ",";
  }

  if (outputSizeExpr_) {
    if (outputSizeExpr_->getOperatorType() == ITM_CONSTANT) {
      char str[TEXT_DISPLAY_LENGTH];
      sprintf(str, PF64,
              ((ConstValue *)outputSizeExpr_)->getExactNumericValue());
      result += "OUTPUT SIZE ";
      result += str;
    } else if (outputSizeExpr_->getOperatorType() == ITM_HOSTVAR)
      result += "OUTPUT SIZE " + ((HostVar *)outputSizeExpr_)->getName();
    else
      result += "OUTPUT SIZE ??";

    if (indexExpr_)
      result += ",";

  }

  if (indexExpr_)
    result += ("KEY BY " +
               ((ColReference *)indexExpr_)->getColRefNameObj().getColName());

  return result;
}

// -----------------------------------------------------------------------
// methods for class RowsetInto
// -----------------------------------------------------------------------
// Constructor
RowsetInto::RowsetInto(RelExpr *child, ItemExpr *outputHostvars,
                       ItemExpr *sizeExpr, CollHeap* oHeap)
 : RelExpr(REL_ROWSET_INTO,child,NULL,oHeap),
   outputHostvars_(outputHostvars),
   sizeExpr_(sizeExpr),
   requiredOrderTree_(NULL)
{
  setNonCacheable();
   requiredOrder_.clear();
} // RowsetInto::RowsetInto()

// Destructor.
RowsetInto::~RowsetInto()
{
} // RowsetInto::~RowsetInto()

RelExpr * RowsetInto::copyTopNode(RelExpr *derivedNode,
                                  CollHeap* oHeap)
{
  RowsetInto *result;

  if (derivedNode == NULL)
    result = new (oHeap) RowsetInto(NULL, outputHostvars_, sizeExpr_, oHeap);
  else
    result = (RowsetInto *) derivedNode;

  return RelExpr::copyTopNode(result,oHeap);
} // RowsetInto::copyTopNode()

Int32 RowsetInto::getArity() const
{
  return 1; // This select-list root node
} // RowsetInto::getArity()


const NAString RowsetInto::getText() const
{
  NAString result("RowsetINTO",CmpCommon::statementHeap());

  if (sizeExpr_) {
    if (sizeExpr_->getOperatorType() == ITM_CONSTANT) {
      char str[TEXT_DISPLAY_LENGTH];
      sprintf(str, " " PF64 ,
              ((ConstValue *)sizeExpr_)->getExactNumericValue());
      result += str;
    } else if (sizeExpr_->getOperatorType() == ITM_HOSTVAR)
      result += " " + ((HostVar *)sizeExpr_)->getName();
    else
      result += " ??";
  }

  result += " (";
  for (ItemExpr *hostVarTree = outputHostvars_;
       hostVarTree != NULL;
       hostVarTree = hostVarTree->child(1)) {
    if (outputHostvars_ != hostVarTree)
      result += ", ";

    HostVar *hostVar = (HostVar *)hostVarTree->getChild(0);
    result += hostVar->getName();
  }
  result += ")";

  return result;
}


NABoolean RelExpr::treeContainsEspExchange()
{
  Lng32 nc = getArity();
  if (nc > 0)
  {
    if ((getOperatorType() == REL_EXCHANGE) &&
        (child(0)->castToRelExpr()->getPhysicalProperty()->getPlanExecutionLocation()
            != EXECUTE_IN_DP2))
    {
      return TRUE;
    }

    for (Lng32 i = 0; i < nc; i++)
    {
      if (child(i)->treeContainsEspExchange())
        return TRUE;
    }
  }
  return FALSE;
}

NABoolean Exchange::areProbesHashed(const ValueIdSet pkey)
{
  return getGroupAttr()->getCharacteristicInputs().contains(pkey);
}


void Exchange::computeBufferLength(const Context *myContext,
                                   const CostScalar &numConsumers,
                                   const CostScalar &numProducers,
                                   CostScalar &upMessageBufferLength,
                                   CostScalar &downMessageBufferLength)
{

  CostScalar numDownBuffers = (Int32) ActiveSchemaDB()->getDefaults().getAsULong
                              (GEN_SNDT_NUM_BUFFERS);
  CostScalar numUpBuffers = (Int32) ActiveSchemaDB()->getDefaults().getAsULong
                                 (GEN_SNDB_NUM_BUFFERS);
  CostScalar maxOutDegree = MAXOF(numConsumers, numProducers);

  CostScalar upSizeOverride = ActiveSchemaDB()->getDefaults().getAsLong
                             (GEN_SNDT_BUFFER_SIZE_UP);

  // The adjustment is a fudge factor to improve scalability by 
  // reducing the buffer size
  // "penalty" when the number of connections is high due 
  //to a high degree of parallelism.
  // The net result is to increase the memory "floor" and "ceiling"
   // (that are base d on the
   // number of connections) by up to fourfold. 
   //Too high a ceiling can cause memory pressure,
   // a high level of paging activity, etc., 
   //while too low a ceiling can cause a large
   // number of IPC messages and dispatches, and a 
   // resultant increase in path lengt h.
   // The adjustment attempts to strike a balance between 
   // the two opposing clusters  of performance factors.

  CostScalar adjMaxNumConnections =
    maxOutDegree < 32 || upSizeOverride == 1 || upSizeOverride > 2 ? maxOutDegree :
    maxOutDegree < 64 ? 32 :
    maxOutDegree < 128 ? 40 :
    maxOutDegree < 256 ? 50 : 
    maxOutDegree < 512 ? 64 : 70;

  CostScalar overhead = CostScalar(50);

  // compute numProbes, probeSize, cardinality, outputSize
  CostScalar downRecordLength = getGroupAttr()->
                   getCharacteristicInputs().getRowLength();

  CostScalar upRecordLength = getGroupAttr()->
             getCharacteristicOutputs().getRowLength();

  const CostScalar & numOfProbes =
  ( myContext->getInputLogProp()->getResultCardinality() ).minCsOne();

  // use no more than 50 KB and try to send all rows down in a single message
  CostScalar reasonableBufferSpace1 =
    CostScalar(50000) / (maxOutDegree * numDownBuffers);

  reasonableBufferSpace1 =
    MINOF(reasonableBufferSpace1,
           (downRecordLength + overhead) * numOfProbes);

  const EstLogPropSharedPtr inputLP    = myContext->getInputLogProp();
  CostScalar numRowsUp = child(0).outputLogProp(inputLP)->
                         getResultCardinality();

  const PartitioningFunction* const parentPartFunc =
    myContext->getPlan()->getPhysicalProperty()->getPartitioningFunction();

  if (parentPartFunc->isAReplicateViaBroadcastPartitioningFunction())
     numRowsUp = numRowsUp * numConsumers;

  // check for an overriding define for the buffer size
  CostScalar downSizeOverride = ActiveSchemaDB()->getDefaults().getAsLong
                                 (GEN_SNDT_BUFFER_SIZE_DOWN);

  if (downSizeOverride.isGreaterThanZero())
    reasonableBufferSpace1 = downSizeOverride;

  // we MUST be able to fit at least one row into a buffer

  CostScalar controlAppendedLength= ComTdbSendTop::minSendBufferSize(
                                     (Lng32)downRecordLength.getValue());
  downMessageBufferLength =
    MAXOF(controlAppendedLength,
          reasonableBufferSpace1);

  // Total size of output buffer that needs to be sent to the parent.
  CostScalar totalBufferSize = upRecordLength *  numRowsUp;

  // Divide this by number of connections to get total buffer per connection.
  CostScalar bufferSizePerConnection = totalBufferSize / adjMaxNumConnections; 

  // Aim for a situation where atleast 80 messages are sent per connection.
  CostScalar reasonableBufferSpace2 =
               bufferSizePerConnection / ActiveSchemaDB()
                 ->getDefaults().getAsLong(GEN_EXCHANGE_MSG_COUNT);

  // Now Esp has numUpBuffers of size reasonableBufferSpace2 per 
  // each stream (connection), so total memory to be allocated 
  // in this Esp would be:
  // reasonableBufferSpace2 * numUpBuffers * maxOutDegree.
  // We need to apply ceiling and floor to this memory i.e.:
  // 4MB > reasonableBufferSpace2 * numUpBuffers * maxOutDegree > 50KB.
  // OR divide both ceiling and floor by numUpBuffers * maxOutDegree.
  Int32 maxMemKB = ActiveSchemaDB()
                   ->getDefaults().getAsLong(GEN_EXCHANGE_MAX_MEM_IN_KB);
  if (maxMemKB <= 0) maxMemKB = 4000;   // 4MB if not set or negative
  CostScalar maxMem1 = maxMemKB * 1000;
  CostScalar maxMem2 = maxMemKB * 4000;
  CostScalar ceiling = MINOF(maxMem1 /
                             (numUpBuffers * adjMaxNumConnections),
                             maxMem2 /
                             (numUpBuffers * maxOutDegree));

  CostScalar floor   =  MINOF(CostScalar(50000) / 
                             (numUpBuffers * adjMaxNumConnections), 
                             CostScalar(200000) / 
                              (numUpBuffers * maxOutDegree));
    
  // Apply the floor.
  reasonableBufferSpace2 =
     MAXOF(floor, reasonableBufferSpace2);

  // Apply the ceiling.
  reasonableBufferSpace2 =
   MINOF(ceiling, reasonableBufferSpace2);

  // Make sure the floor is at least 5K to avoid performance problem.
  reasonableBufferSpace2 =
   MAXOF(CostScalar(5000), reasonableBufferSpace2);

  // Make sure that it is at most 31k-1356
  reasonableBufferSpace2 =
   MINOF( reasonableBufferSpace2, 31 * 1024 - 1356);
 
  if (upSizeOverride.isGreaterThanZero())
    reasonableBufferSpace2 = upSizeOverride;

  // we MUST be able to fit at least one row into a buffer
  controlAppendedLength = ComTdbSendTop::minReceiveBufferSize(
                                       (Lng32) (upRecordLength.getValue()) );
  upMessageBufferLength =
    MAXOF(  controlAppendedLength, reasonableBufferSpace2);
  
  // convert Buffers to kilo bytes
  upMessageBufferLength_= upMessageBufferLength = upMessageBufferLength/CostScalar(1024);
  downMessageBufferLength_ = downMessageBufferLength = downMessageBufferLength/ CostScalar(1024);  
} // Exchange::computeBufferLength()


//////////////////////////////////////////////////////////////////////
// Class pcgEspFragment related methods
//////////////////////////////////////////////////////////////////////

void pcgEspFragment::addChild(Exchange* esp)
{
   CollIndex newIndex = childEsps_.entries();
   childEsps_.insertAt(newIndex, esp);
}

// Verify that the newly added exchange node at the end of childEsps_[]
// is compatible with others.
// Note that preCodeGen traversal the query tree via a depth-first 
// search. Each time the leave exchange node in this fragment is encountered,
// this method is called,  The order of visit to the child exchanges 
// is from left to right.
// 
NABoolean pcgEspFragment::tryToReduceDoP()
{
   float threshold;
   ActiveSchemaDB()->
      getDefaults().getFloat(DOP_REDUCTION_ROWCOUNT_THRESHOLD, threshold);
   
   if ( threshold == 0.0 || getTotaleRows() >= threshold ) {
     return FALSE;
   }


   // Defensive programming. Nothing to verify when there is no child esp.
   if ( childEsps_.entries() == 0 ) return FALSE;


   // Get the ptr to last exchange 
   CollIndex lastIdx  = childEsps_.entries()-1;
   Exchange* xch = childEsps_[lastIdx];

   //
   // No dop reduction for Parallel Extract
   //
   if ( xch->getExtractProducerFlag() || xch->getExtractConsumerFlag() )
     return FALSE;

   PartitioningFunction* partFunc = 
           xch->getPhysicalProperty()->getPartitioningFunction();

   //
   // If xch's count of partitions is less than newDoP, bail out
   //
   Lng32 newDoP = CURRSTMT_OPTDEFAULTS->getDefaultDegreeOfParallelism();
   if ( partFunc->getCountOfPartitions() < newDoP ) 
     return FALSE;
 

   // Do not reduce dop if this exchange is a PA, except if it is
   // the right child of a TYPE2 join, using replicate-no-broadcast.
   // An extra exchange is needed otherwise to bridge the 
   // new DoP and all original partitions, unless the newDoP is a factor 
   // of #part of the hash2 partfunc for the PA node. 
   // 
   if ( xch->child(0)->getPhysicalProperty()
          ->getPlanExecutionLocation() == EXECUTE_IN_DP2)
   {
      if ( partFunc->isAHash2PartitioningFunction() ) {
         if ( partFunc->getCountOfPartitions() % newDoP != 0 )
           return FALSE;
      } else    
         if (!partFunc->isAReplicateNoBroadcastPartitioningFunction())
           return FALSE;
   }


   Lng32 suggestedNewDoP = newDoP;

   //
   // Make a copy of the part func as the scaling method can side effect.
   //
   PartitioningFunction* partFuncCopy = 
           xch->getPhysicalProperty()->getPartitioningFunction()->copy();

   PartitioningFunction* newPF = 
           partFuncCopy->scaleNumberOfPartitions(suggestedNewDoP);

   //
   // If the part func can not scale to newDoP, bail out.
   //
   if ( suggestedNewDoP != newDoP ) 
     return FALSE;

   //
   // Find a common dop for all child esps in the fragment first. 
   // A common dop is one associated with 1st esp that has non-
   // broadcasting part func. All other child esps with non-broadcasting
   // partFunc should be use the "common dop". This is true prior to
   // the dop reduction attempt. If it is already found (commonDoP_ > 0),
   // just use it.
   //
   if ( commonDoP_ == 0 && 
        partFuncCopy->isAReplicationPartitioningFunction() == FALSE )
      commonDoP_ = partFuncCopy->getCountOfPartitions();


   // If the dop at child exchange A can be reduced but not at
   // child exchange B, we may end up with in an inconsistent state. 
   // The following code detects it. 
   if ( commonDoP_ > 0 ) { 

      if ( 
           partFuncCopy->isAReplicationPartitioningFunction() == FALSE &&
           partFuncCopy->getCountOfPartitions() != commonDoP_
         )
        return FALSE;
   }

   //
   // The new dop is acceptable.
   //
   newDoP_ = newDoP;
   setValid(TRUE);

   return TRUE;
}

void pcgEspFragment::invalidate()
{
   setValid(FALSE);

   //for ( CollIndex i=0; i<childEsps_.entries(); i++ ) {
   //   childEsps_[i]->getEspFragPCG()->invalidate();
   //}
}

void pcgEspFragment::adjustDoP(Lng32 newDop)
{
  for ( CollIndex i=0; i<childEsps_.entries(); i++ ) {

    Exchange* xch = childEsps_[i];

    // Recursively adjust the dop for my child fragments.
    // Each exchange will have its own pcgEspFragment to work with.
    xch->doDopReduction();
  }
}

void RelExpr::doDopReduction()
{
  
  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
  {
     child(i)->doDopReduction();
  }
}

void Exchange::doDopReduction()
{
   //
   // Once this method is called for the top most Exchange, we can 
   // recursively call the same method for all the esp fragments via
   // the pointers (to esps) saved in the pcgEsgFragment  objects.
   //
   Lng32 newDop = getEspFragPCG()->getNewDop();

   if ( getEspFragPCG()->isValid() ) {

     // Adjust the partfunc for all nodes within the fragment, starting
     // from my child and down to every bottom-defining exchanges.
     child(0)->doDopReductionWithinFragment(newDop);
   }

   // Next recursively call the same method for all fragments below me.
   getEspFragPCG()->adjustDoP(newDop);
}

void RelExpr::doDopReductionWithinFragment(Lng32 newDoP)
{
  adjustTopPartFunc(newDoP);

  if ( getOperatorType() == REL_EXCHANGE ) {

     //
     // Need to adjust the Logical part of the part func if 
     // my child's part func is a LogPhy partfunc.
     //
     if ( child(0)->getPhysicalProperty()->getPlanExecutionLocation() 
             == EXECUTE_IN_DP2) 
     {
        PartitioningFunction *pf = 
           child(0)->getPhysicalProperty()->getPartitioningFunction();


        if ( pf->isALogPhysPartitioningFunction() ) 
           child(0)->adjustTopPartFunc(newDoP);
     }
     return;
  }

  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
  {
     child(i)->doDopReductionWithinFragment(newDoP);
  }
}


void RelExpr::adjustTopPartFunc(Lng32 newDop)
{
   ((PhysicalProperty*)getPhysicalProperty())->scaleNumberOfPartitions(newDop);
   setDopReduced(TRUE);
}

// Required Resource Estimate Methods - Begin
void RelExpr::computeRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
  {
    if (child(i))
      child(i)->computeRequiredResources(reqResources, inLP);
    else
      child(i).getLogExpr()->computeRequiredResources(reqResources, inLP);
  }

  computeMyRequiredResources(reqResources, inLP);
}

void Join::computeRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  Int32 nc = getArity();

  if (child(0))
    child(0)->computeRequiredResources(reqResources, inLP);
  else
    child(0).getLogExpr()->computeRequiredResources(reqResources, inLP);

  EstLogPropSharedPtr inputForRight = inLP;

  EstLogPropSharedPtr leftOutput =
    child(0).getGroupAttr()->outputLogProp(inLP);

  if(isTSJ())
  {
    inputForRight = leftOutput;
  }

  if (child(1))
    child(1)->computeRequiredResources(reqResources, inputForRight);
  else
    child(1).getLogExpr()->computeRequiredResources(reqResources, inputForRight);


  computeMyRequiredResources(reqResources, inLP);
}

void RequiredResources::accumulate(CostScalar memRsrcs,
                                   CostScalar cpuRsrcs,
                                   CostScalar dataAccessCost,
                                   CostScalar maxCard)
{
  memoryResources_ += memRsrcs;
  cpuResources_ += cpuRsrcs;
  dataAccessCost_ += dataAccessCost;

  if(maxOperMemReq_ < memRsrcs)
    maxOperMemReq_ = memRsrcs;

  if(maxOperCPUReq_ < cpuRsrcs)
    maxOperCPUReq_ = cpuRsrcs;

  if(maxOperDataAccessCost_ < dataAccessCost)
    maxOperDataAccessCost_ = dataAccessCost;

  if(maxMaxCardinality_ < maxCard)
    maxMaxCardinality_ = maxCard;
}

void RelExpr::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  CostScalar cpuResourcesRequired = csZero;
  CostScalar A = csOne;
  CostScalar B (getDefaultAsDouble(WORK_UNIT_ESP_DATA_COPY_COST));

  Int32 nc = getArity();

  for (Lng32 i = 0; i < nc; i++)
  {
    GroupAttributes * childGroupAttr = child(i).getGroupAttr();
    CostScalar childCardinality =
      childGroupAttr->outputLogProp(inLP)->getResultCardinality();
    CostScalar childRecordSize = childGroupAttr->getCharacteristicOutputs().getRowLength();

    cpuResourcesRequired +=
      (A * childCardinality) +
      (B * childCardinality * childRecordSize );
  }

  CostScalar myMaxCard = getGroupAttr()->getResultMaxCardinalityForInput(inLP);

  reqResources.accumulate(csZero,
                          cpuResourcesRequired,
                          csZero,
                          myMaxCard);
}

void RelRoot::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{

  if (hasOrderBy())
  {
    CostScalar memoryResourcesRequired = csZero;

    GroupAttributes * childGroupAttr = child(0).getGroupAttr();
    CostScalar childCardinality =
      childGroupAttr->outputLogProp(inLP)->getResultCardinality();
    CostScalar childRecordSize = childGroupAttr->getCharacteristicOutputs().getRowLength();
    memoryResourcesRequired  = (childCardinality * childRecordSize);

    reqResources.accumulate(memoryResourcesRequired, csZero, csZero);
  }

  // add the cpu resources
  RelExpr::computeMyRequiredResources(reqResources, inLP);
}

void MultiJoin::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  // get the subset analysis for this MultiJoin
  JBBSubsetAnalysis * subsetAnalysis = getJBBSubset().getJBBSubsetAnalysis();

  subsetAnalysis->computeRequiredResources(this,reqResources, inLP);
}


void Join::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  CostScalar memoryResourcesRequired = csZero;

  // only get the max card for this join. The contribution from the children
  // of this join is done inside Join::computeReequiredResource() where
  // child(i)->computeRequiredResources() is called (i=0,1). These two calls
  // will call ::computeMyRequiredResoruce() of the corresponding RelExpr.
  //
  GroupAttributes * myGroupAttr = getGroupAttr();

  CostScalar myMaxCard = myGroupAttr->getResultMaxCardinalityForInput(inLP);

  reqResources.accumulate(csZero, csZero, csZero, myMaxCard);

  if(!isTSJ())
  {
    GroupAttributes * innerChildGroupAttr = child(1).getGroupAttr();
    CostScalar innerChildCardinality =
      innerChildGroupAttr->outputLogProp(inLP)->getResultCardinality();
    CostScalar innerChildRecordSize = innerChildGroupAttr->getCharacteristicOutputs().getRowLength();
    memoryResourcesRequired = (innerChildCardinality * innerChildRecordSize);

    reqResources.accumulate(memoryResourcesRequired, csZero, csZero);

    // add the cpu resources
    RelExpr::computeMyRequiredResources(reqResources, inLP);
  }
  else{
    // isTSJ() == TRUE
    CostScalar cpuResourcesRequired = csZero;
    CostScalar A = csOne;
    CostScalar B (getDefaultAsDouble(WORK_UNIT_ESP_DATA_COPY_COST));

    Int32 nc = getArity();
    EstLogPropSharedPtr inputForChild = inLP;

    for (Lng32 i = 0; i < nc; i++)
    {
      GroupAttributes * childGroupAttr = child(i).getGroupAttr();
      CostScalar childCardinality =
        childGroupAttr->outputLogProp(inputForChild)->getResultCardinality();
      CostScalar childRecordSize = childGroupAttr->getCharacteristicOutputs().getRowLength();

      cpuResourcesRequired += (B * childCardinality * childRecordSize );

      // do this only for the left child
      if(i < 1)
        cpuResourcesRequired += (A * childCardinality);

      inputForChild = child(i).getGroupAttr()->outputLogProp(inputForChild);
    }

    reqResources.accumulate(csZero,
                            cpuResourcesRequired,
                            csZero);
  }
}

void GroupByAgg::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  CostScalar memoryResourcesRequired  = csZero;

  GroupAttributes * childGroupAttr = child(0).getGroupAttr();
  CostScalar childCardinality =
    childGroupAttr->outputLogProp(inLP)->getResultCardinality();
  CostScalar childRecordSize = childGroupAttr->getCharacteristicOutputs().getRowLength();
  memoryResourcesRequired  = (childCardinality * childRecordSize);

  reqResources.accumulate(memoryResourcesRequired, csZero, csZero);

  // add the cpu resources
  RelExpr::computeMyRequiredResources(reqResources, inLP);
}

void Scan::computeMyRequiredResources(RequiredResources & reqResources, EstLogPropSharedPtr & inLP)
{
  if(!(QueryAnalysis::Instance() &&
       QueryAnalysis::Instance()->isAnalysisON()))
    return;


  //Get a handle to ASM
  AppliedStatMan * appStatMan = QueryAnalysis::ASM();
  const TableAnalysis * tAnalysis = getTableDesc()->getTableAnalysis();
  CANodeId tableId = tAnalysis->getNodeAnalysis()->getId();

  // Find index joins and index-only scans
  addIndexInfo();

  // Base table scan is one of the index only scans.
  CostScalar cpuCostIndexOnlyScan = 
                computeCpuResourceForIndexOnlyScans(tableId);

  CostScalar cpuCostIndexJoinScan = 
                computeCpuResourceForIndexJoinScans(tableId);

  CostScalar cpuResourcesRequired = cpuCostIndexOnlyScan;
  if ( getTableDesc()->getNATable()->isHbaseTable()) 
  {
     if ( cpuCostIndexJoinScan < cpuResourcesRequired )
       cpuResourcesRequired = cpuCostIndexJoinScan;
  } 

  CostScalar dataAccessCost = tAnalysis->getFactTableNJAccessCost();
  if(dataAccessCost < 0)
  {
    CostScalar rowsToScan = appStatMan->
                   getStatsForLocalPredsOnCKPOfJBBC(tableId)->
                     getResultCardinality();

    CostScalar numOfProbes(csZero);

    // skip this for fact table under nested join
    dataAccessCost =
      tAnalysis->computeDataAccessCostForTable(numOfProbes, rowsToScan);
  }


  CostScalar myMaxCard = getGroupAttr()->getResultMaxCardinalityForInput(inLP);
  reqResources.accumulate(csZero, cpuResourcesRequired, 
                          dataAccessCost, myMaxCard
                         );

}
// Required Resource Estimate Methods - End

CostScalar
Scan::computeCpuResourceRequired(const CostScalar& rowsToScan, const CostScalar& rowSize)
{
  CostScalar A = csOne;
  CostScalar B (getDefaultAsDouble(WORK_UNIT_ESP_DATA_COPY_COST));

  CostScalar cpuResourcesRequired = (B * rowsToScan * rowSize);
  cpuResourcesRequired += (A * rowsToScan);

  return cpuResourcesRequired;
}

CostScalar Scan::computeCpuResourceForIndexOnlyScans(CANodeId tableId)
{
  // If index only scans are available, find the most
  // promising index and compute the CPU resource for it.

  const SET(IndexProperty *)& indexOnlyScans = getIndexOnlyIndexes();

  IndexProperty* smallestIndex = findSmallestIndex(indexOnlyScans);

  if ( !smallestIndex )
    return COSTSCALAR_MAX;

  IndexDesc* iDesc = smallestIndex->getIndexDesc();

  const ValueIdList &ikeys = iDesc->getIndexKey();

  AppliedStatMan * appStatMan = QueryAnalysis::ASM();

  EstLogPropSharedPtr estLpropPtr = appStatMan->
                  getStatsForLocalPredsOnPrefixOfColList(tableId, ikeys);

  if ( !(estLpropPtr.get()) )
    return COSTSCALAR_MAX;

  return computeCpuResourceRequired(estLpropPtr->getResultCardinality(), 
                                    iDesc->getRecordLength()
                                   );
}


CostScalar Scan::computeCpuResourceForIndexJoinScans(CANodeId tableId)
{
 // If index scans are available, find the index with most promising and
  // compute the CPU resource for it.
  const LIST(ScanIndexInfo *)& scanIndexJoins = getPossibleIndexJoins();

  if ( scanIndexJoins.entries() == 0 )
    return COSTSCALAR_MAX;

  IndexProperty* smallestIndex = findSmallestIndex(scanIndexJoins);
  IndexDesc* iDesc = smallestIndex->getIndexDesc();

  CostScalar rowsToScan;
  return computeCpuResourceForIndexJoin(tableId, iDesc, iDesc->getIndexKey(), rowsToScan);
}

CostScalar Scan::computeCpuResourceForIndexJoin(CANodeId tableId, IndexDesc* iDesc, 
                                                ValueIdSet& indexPredicates, 
                                                CostScalar& rowsToScan)
{
  ValueIdList ikeysCovered;

  UInt32 sz = iDesc->getIndexKey().entries();
  for (CollIndex i=0; i<sz; i++) {
    ValueId x = iDesc->getIndexKey()[i];
    if ( indexPredicates.containsAsEquiLocalPred(x) )
      ikeysCovered.insertAt(i, x);
    else
      break;
  }

  return computeCpuResourceForIndexJoin(tableId, iDesc, ikeysCovered, rowsToScan);
}

CostScalar 
Scan::computeCpuResourceForIndexJoin(CANodeId tableId, IndexDesc* iDesc, 
                                     const ValueIdList& ikeys, CostScalar& rowsToScan)
{
  AppliedStatMan * appStatMan = QueryAnalysis::ASM();

  EstLogPropSharedPtr estLpropPtr = appStatMan->
                  getStatsForLocalPredsOnPrefixOfColList(tableId, ikeys);

  if ( !(estLpropPtr.get()) ) {
    rowsToScan = COSTSCALAR_MAX;
    return COSTSCALAR_MAX;
  }

  rowsToScan = estLpropPtr->getResultCardinality();
  CostScalar rowSize = iDesc->getRecordLength();

  CostScalar cpuResourceForIndex = computeCpuResourceRequired(rowsToScan, rowSize);

  rowSize = getTableDesc()->getClusteringIndex()->getRecordLength();

  CostScalar cpuResourceForBaseTable = computeCpuResourceRequired(rowsToScan, rowSize);

  return cpuResourceForIndex + cpuResourceForBaseTable;
}

IndexProperty* Scan::findSmallestIndex(const SET(IndexProperty *)& indexes) const
{
   CollIndex entries = indexes.entries();

   if ( entries == 0 ) return NULL;

   IndexProperty* smallestIndex = indexes[0];

   for (CollIndex i=1; i<entries; i++ ) {
     IndexProperty* current = indexes[i];

     if ( smallestIndex->compareIndexPromise(current) == LESS ) {
        smallestIndex = current;
     }
   }

   return smallestIndex;
}

IndexProperty* Scan::findSmallestIndex(const LIST(ScanIndexInfo *)& possibleIndexJoins) const
{
   CollIndex entries = possibleIndexJoins_.entries();

   if ( entries == 0 ) return NULL;

   IndexProperty* smallestIndex = 
            findSmallestIndex(possibleIndexJoins[0]->usableIndexes_);

   for (CollIndex i=1; i<entries; i++ ) {
     
      IndexProperty* current = 
            findSmallestIndex(possibleIndexJoins[i]->usableIndexes_);
                        
      if ( smallestIndex->compareIndexPromise(current) == LESS ) {
          smallestIndex = current;
      }
   }

   return smallestIndex;
}

// This function checks if the passed RelExpr is a UDF rule created by a CQS
// (REL_FORCE_ANY_SCALAR_UDF).  If not, then RelExpr::patternMatch() is called. 
// If the CQS rule includes the UDF name this name is checked against the routine 
// name of this physical isolated scalar UDF.  If the CQS rule includes the action
// name, then this is checked against the action name of this physical isolated
// scalar UDF as well.  The function returns TRUE if so, otherwise FALSE.
NABoolean PhysicalIsolatedScalarUDF::patternMatch(const RelExpr & other) const
{
  // Check if CQS is a scalar UDF rule.
  if (other.getOperatorType() == REL_FORCE_ANY_SCALAR_UDF)
    {
      UDFForceWildCard &w = (UDFForceWildCard &) other;

      // Check function name, if specified in UDFForceWildCard.
      if (w.getFunctionName() != "")
        {
          QualifiedName funcName(w.getFunctionName(), 1 /* minimal 1 part name */);

          // Compare catalog, schema and udf parts separately, 
          // if they exist in the wildcard
          const NAString& catName = funcName.getCatalogName();
          const NAString& schName = funcName.getSchemaName();
          const QualifiedName& x  = getRoutineName();
   
          if ((catName.length() > 0 && x.getCatalogName() != catName) ||
              (schName.length() > 0 && x.getSchemaName()  != schName) ||
              x.getObjectName() != funcName.getObjectName())
            return FALSE;
        }
      // Check action name, if specified in UDFForceWildCard.
      if (w.getActionName() != "")
        {
          NAString actionName = w.getActionName();

          if (getActionNARoutine() &&
              getActionNARoutine()->getActionName())
            {
              // Compare only object parts.  Right now actions don't support catalogs and schemas.
              // This is because action names can have a leading '$' as part of name.
              const NAString& x = *(getActionNARoutine()->getActionName());
   
              if (x != actionName)
                return FALSE;
            }
          else return FALSE;
        }
    return TRUE;
  }
  else
    return RelExpr::patternMatch(other);
}


const NAString RelExpr::getCascadesTraceInfoStr() 
{
  NAString result("RelExpr Cascades Trace Info:\n");
  result += " parent taskid: " + istring(getParentTaskId()) + "\n";
  result += " sub taskid: " + istring(getSubTaskId()) + "\n";
  result += " birth id: " + istring(getBirthId()) + "\n";
  result += " memo exprid: " + istring(memoExprId_) + "\n";
  result += " source memo exprid: " + istring(sourceMemoExprId_) + "\n";
  result += " source groupid: " + istring(sourceGroupId_) + "\n";
  char costLimitStr[50];
  sprintf(costLimitStr," cost limit %g\n", costLimit_);
  result += costLimitStr;
  return result;
}

// remember the creator and source of this relexpr for cascades display gui
void RelExpr::setCascadesTraceInfo(RelExpr *src)
{
  CascadesTask * currentTask = CURRSTMT_OPTDEFAULTS->getCurrentTask();
  if (currentTask)
  {
    // current task created this relexpr 
    parentTaskId_ = currentTask->getParentTaskId();
    stride_ = currentTask->getSubTaskId();

    // remember time of my birth
    birthId_ = CURRSTMT_OPTDEFAULTS->getTaskCount();

    // remember my source
    sourceGroupId_ = currentTask->getGroupId();
    if (src)
      sourceMemoExprId_ = src->memoExprId_;

    // remember current task's context's CostLimit
    Context * context = currentTask->getContext();
    if(context && context->getCostLimit())
      costLimit_ = context->getCostLimit()->getCachedValue();
  }
  // get my MemoExprId and advance it
  memoExprId_ = CURRSTMT_OPTDEFAULTS->updateGetMemoExprCount();
}

NABoolean Join::childNodeContainSkew(
                      CollIndex i,                 // IN: which child
                      const ValueIdSet& joinPreds, // IN: the join predicate
                      double threshold,            // IN: the threshold
                      SkewedValueList** skList      // OUT: the skew list
                               ) const
{
   // Can not deal with multicolumn skew in this method.
   if ( joinPreds.entries() != 1 )
      return FALSE;

   NABoolean statsExist; // a place holder

   Int32 skews = 0;
   for(ValueId vid = joinPreds.init(); joinPreds.next(vid); joinPreds.advance(vid)) {
      *skList = child(i).getGroupAttr()-> getSkewedValues(vid, threshold,
                    statsExist,
                    (*GLOBAL_EMPTY_INPUT_LOGPROP),
                    isLeftJoin()/* include skewed NULLs only for left outer join */
                                                         );

      if (*skList == NULL || (*skList)->entries() == 0)
         break;
      else
         skews++;
    }

   return ( skews == joinPreds.entries() );
}


// 
// Check if some join column is of a SQL type whose run-time
// implementation has a limitation for SB to work.
//
// return 
//   TRUE: no limitation
//   FALSE: has limitation and SB should not be applied
//
NABoolean Join::singleColumnjoinPredOKforSB(ValueIdSet& joinPreds)
{
  ValueId vId((CollIndex)0); joinPreds.next(vId);

  ItemExpr* iePtr = vId.getItemExpr();

  if (iePtr->getOperatorType() == ITM_INSTANTIATE_NULL) {
     iePtr = iePtr -> child(0);
  }

  ValueIdSet vidSet;

  switch (iePtr->getOperatorType()) {
     case ITM_EQUAL: // this case is used to handle char type when
                     // no VEG is formed for a char predicate,
                     // or joins involving subqueries.
     case ITM_VEG_PREDICATE:
     case ITM_VEG_REFERENCE:

       // We only care columns of type ITM_BASECOLUMN (columns belonging to
       // base tables or table-valued stored procedures, see comment on class
       // BaseColumn).
       iePtr->findAll(ITM_BASECOLUMN, vidSet, TRUE, TRUE);

       // If no such columns can be found. Do not bother to continue further,
       // as only base table columns have the potential to be big and skewed.
       if ( vidSet.entries() == 0 )
          return FALSE;

       break;

     default:
       return FALSE;
  }

  ValueId colVid((CollIndex)0); vidSet.next(colVid);

  if ( !colVid.getType().isSkewBusterSupportedType() )
    return FALSE;

  // Additional test
  if ( colVid.getType().getTypeQualifier() == NA_NUMERIC_TYPE &&
       colVid.getType().getTypeName() == LiteralNumeric ) {

      // Exact decimal numeric such as NUMERIC(18,15) can be handled, if
      // all columns involved in join are of the exact same precision and
      // and scale. The comparison ignores NULL attribute of the type (ALM 4953).
      for(ValueId x = vidSet.init(); vidSet.next(x); vidSet.advance(x))
      {
        if ( NOT ((NumericType&)(colVid.getType())).equalIgnoreNull(x.getType()))
           return FALSE;
      }

      return TRUE;
  } else 
  if ( DFS2REC::isAnyCharacter(colVid.getType().getFSDatatype()) ) {

     if ( ((const CharType&)colVid.getType()).getStrCharLimit() > 
          (Lng32) CmpCommon::getDefaultNumeric(USTAT_MAX_CHAR_BOUNDARY_LEN) )
        return FALSE;
  }

  return TRUE;
}


NABoolean Join::multiColumnjoinPredOKforSB(ValueIdSet& joinPreds)
{
   for(ValueId x = joinPreds.init(); joinPreds.next(x); joinPreds.advance(x))
   {
      ValueIdSet dummy(x);
      if ( !singleColumnjoinPredOKforSB(dummy) )
        return FALSE;
   }
   return TRUE;
}

// The new way to capture MC skews. All such skews have been computed during
// update stats. 
NABoolean Join::childNodeContainMultiColumnSkew(
                      CollIndex i,                 // IN: which child to work on
                      const ValueIdSet& joinPreds, // IN: the join predicate
                      double mc_threshold,         // IN: multi-column threshold
                      Lng32 countOfPipelines,      // IN:
                      SkewedValueList** skList     // OUT: the skew list
                               ) 
{
   if (joinPreds.entries() <= 1)
       return FALSE;

   const ColStatDescList& theColList =
          child(i).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->colStats();


  ValueId col;
  ValueIdSet lhsCols;

  CollIndex index = NULL_COLL_INDEX;

  const ValueIdSet& joiningCols = (i==0) ? 
            getEquiJoinExprFromChild0() : getEquiJoinExprFromChild1() ;

  for (col = joiningCols.init();
             joiningCols.next(col);
             joiningCols.advance(col) )
  {
    theColList.getColStatDescIndex(index, col);
    if (index != NULL_COLL_INDEX)
       lhsCols.insert(theColList[index]->getColumn());
  }


   ValueIdList dummyList;

   const MCSkewedValueList* mcSkewList =
          ((ColStatDescList&)theColList).getMCSkewedValueListForCols(lhsCols, dummyList);

   if ( mcSkewList == NULL )
      return FALSE;

   // Apply the frequency threshold to each MC skew and store those passing
   // the thredhold test to the new skList 

   CostScalar rc = child(i).getGroupAttr()->getResultCardinalityForEmptyInput();

   CostScalar thresholdFrequency = rc * mc_threshold;

   *skList = new (CmpCommon::statementHeap()) 
                  SkewedValueList((CmpCommon::statementHeap()));

   for ( CollIndex i=0; i<mcSkewList->entries(); i++ ) {
      MCSkewedValue* itm = mcSkewList->at(i);

      if ( itm->getFrequency() >= thresholdFrequency ) {

        // Use an EncodedValue object to represent the current MC skew
        // and transfer the hash value to it. The hash value is 
        // computed in EncodedValue::computeRunTimeHashValue() and is 
        // the run-time version! No modification should be done to it 
        // from this point on. 
        EncodedValue mcSkewed = itm->getHash();

        (*skList)->insertInOrder(mcSkewed);
      }
   }

   // Set the run-time hash status flag so that we will not try to build
   // the run-time hash again in 
   // SkewedDataPartitioningFunction::buildHashListForSkewedValues().
   (*skList)->setComputeFinalHash(FALSE);
   
   if ( (*skList)->entries() == 0)
     return FALSE;

   return TRUE;
}

// The old way to guess MC skews and repartition the data stream on one
// of the columns with least skews.
NABoolean Join::childNodeContainMultiColumnSkew(
                      CollIndex i,                 // IN: which child to work on
                      const ValueIdSet& joinPreds, // IN: the join predicate
                      double mc_threshold,         // IN: multi-column threshold
                      double sc_threshold,         // IN: single-column threshold
                      Lng32 countOfPipelines,     // IN: 
                      SkewedValueList** skList,    // OUT: the skew list
                      ValueId& vidOfEquiJoinWithSkew // OUT: the valueId of the column
                                                     // whose skew list is returned
                               ) const
{
   if (joinPreds.entries() <= 1)
     return FALSE;

   typedef SkewedValueList* SkewedValueListPtr;

   SkewedValueList** skewLists;
   skewLists =
          new(CmpCommon::statementHeap()) SkewedValueListPtr[joinPreds.entries()];

   CostScalar* skewFactors =
          new(CmpCommon::statementHeap()) CostScalar[joinPreds.entries()];

   // A list of valueIdSets, each valueIdSet element contains a set of 
   // columns from the join predicates. Each set has all columns from the
   // same table participating in the join predicates.
   ARRAY(ValueIdSet) mcArray(CmpCommon::statementHeap(), joinPreds.entries());

   Int32 skews = 0, leastSkewList = 0;
   EncodedValue mostFreqVal;

   CostScalar productOfSkewFactors = csOne;
   CostScalar productOfUecs = csOne;
   CostScalar minOfSkewFactor = csMinusOne;
   CostScalar rc = csMinusOne;
   CostScalar currentSkew;
   CollIndex j = 0;
   NABoolean statsExist;

   for(ValueId vid = joinPreds.init(); joinPreds.next(vid); joinPreds.advance(vid))
   {
      // Get the skew values for the join predicate in question. 
      skewLists[skews] = child(i).getGroupAttr()-> getSkewedValues(vid, sc_threshold,
                    statsExist,
                    (*GLOBAL_EMPTY_INPUT_LOGPROP),
                    isLeftJoin() /* include skewed NULLs only for left outer join */
                                                                   );

      // When the skew list is null, there are two possibilities. 
      // 1. No stats exists, here we assume the worse (stats has not been updated), and
      // move to the next join predicate.
      // 2. The stats is present but we could not detect skews (e.g., the skews are
      // too small to pass the threshold test). We return FALSE to indicate that the
      // column is good enough to smooth out the potential skews in other columns.
      if ( skewLists[skews] == NULL ) {
        if ( !statsExist )
          continue;
        else
          return FALSE; // no stats exist
      }

      // Pick the shortest skew list seen so far. The final shortest skew list
      // will be used for run-time skew detection.
      if ( skews == 0 ||
           (skewLists[skews] && 
           skewLists[skews] -> entries() < skewLists[leastSkewList] -> entries()
           )
         ) 
      {

        // Obtain the colstat for the child of the join predicate on 
        // the other side of the join.
        CollIndex brSide = (i==0) ? 1 : 0;
        ColStatsSharedPtr colStats = child(brSide).getGroupAttr()->
              getColStatsForSkewDetection(vid, (*GLOBAL_EMPTY_INPUT_LOGPROP));

        if ( colStats == NULL )
          return FALSE; // no stats exist for the inner. assume the worst

        // get the skew list 
        const FrequentValueList & skInner = colStats->getFrequentValues();

        CollIndex index = 0;
        const SkewedValueList& newList = *skewLists[skews];

        CostScalar totalFreq = csZero;

        const NAType* nt = newList.getNAType();
        NABoolean useHash = nt->useHashRepresentation();

        for (CollIndex index = 0; index < skInner.entries(); index++)
        {

           const FrequentValue& fv = skInner[index];

          EncodedValue skew = ( useHash ) ? fv.getHash() :  fv.getEncodedValue();

           if ( nt->getTypeQualifier() == NA_NUMERIC_TYPE &&
                nt->getTypeName() == LiteralNumeric ) {

             skew = fv.getEncodedValue().computeHashForNumeric((SQLNumeric*)nt);
           }

           if ( newList.contains(skew) )
             //totalFreq += fv.getFrequency() * fv.getProbability();
             totalFreq += fv.getFrequency() ;
     
        }

        CostScalar totalInnerBroadcastInBytes =
               totalFreq * child(brSide).getGroupAttr()->getRecordLength() *
               countOfPipelines ; 

        if (totalInnerBroadcastInBytes >= 
            ActiveSchemaDB()->getDefaults()
              .getAsLong(MC_SKEW_INNER_BROADCAST_THRESHOLD))
          // ACX QUERY 5 and 8 have skews on the inner side. Better
          // to bet on partitioning on all columns to handle the dual skews.
          // This has been proved by the performance run on 3/21/2012: a 
          // 6% degradation when partition on the remaining non-skew column.
          return FALSE; 
     

        leastSkewList = skews;
        vidOfEquiJoinWithSkew = vid;
      }


      // Get the skew factor for the join predicate in question.
      skewFactors[skews] = currentSkew = child(i).getGroupAttr()->
                        getSkewnessFactor(vid, mostFreqVal, (*GLOBAL_EMPTY_INPUT_LOGPROP));

      // We compute SFa * SFb * SFc ... here
      productOfSkewFactors *= currentSkew;

      // Obtain the colstat for the ith child of the join predicate.
      ColStatsSharedPtr colStats = child(i).getGroupAttr()->
                     getColStatsForSkewDetection(vid, (*GLOBAL_EMPTY_INPUT_LOGPROP));
     
      if ( colStats == NULL )
          return FALSE; // no stats exist. Can not make the decision. return FALSE. 

      // Compute UECa * UECb * UECc ... here
      productOfUecs *= colStats->getTotalUec();

      // get the RC of the table
      if ( rc == csMinusOne )
         rc = colStats->getRowcount();

      // Compute the minimal of the skew factors seen so far
      if ( currentSkew.isGreaterThanZero() ) {
        if ( minOfSkewFactor == csMinusOne || minOfSkewFactor > currentSkew )
           minOfSkewFactor = currentSkew;
      }

      skews++;

     // Collect join columns in this predicate into joinColumns data structure.
     ValueIdSet joinColumns;
     vid.getItemExpr() -> findAll(ITM_BASECOLUMN, joinColumns, TRUE, FALSE);

     // Separate out columns in the join predicates and group them per table.
     //
     // For example, if join predicates are t.a=s.b and t.b=s.b and t.c = s.c,
     // we will have 
     //
     //              mcArray[0] = {t.a, t.b, t.c},
     //              mcArray[1] = {s.a, s.b, s.c},
     //
     // at the end of the loop over join predicates.
     //
     j = 0;
     for(ValueId x = joinColumns.init(); joinColumns.next(x); joinColumns.advance(x))
     {
        if ( !mcArray.used(j) )
           mcArray.insertAt(j, x);
        else {
           ValueIdSet& mcSet = mcArray[j];
           mcSet.insert(x);
        }

        j++;
     }
   } // end of the loop of join predicates

                
   // Now we can find the multi-column UEC, using one of the two multi-column 
   // ValueIdSets (one for each side of the equi-join predicate). The colstats
   // list for the side of the child contains the stats (including the mc ones).
   // one of the mc ones is what we are looking for.
   // 
   ColStatDescList colStatDescList =
         child(i).getGroupAttr()->
                    outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->getColStats();
   
   CostScalar mcUec = csMinusOne;

   const MultiColumnUecList* uecList = colStatDescList.getUecList();

   for(j=0; j < mcArray.entries() && mcUec == csMinusOne && uecList; j++)
   {
       const ValueIdSet& mc = mcArray[j];

       // Do a look up with mc.
       if ( uecList )
          mcUec = uecList->lookup(mc);
   }

   //
   // Compute the final value of 
   //  min( (SFa * SFb * ... *min(UECa * UECb..,RC))/UEC(abc..), 
   //        SFa, SFb, ..., )
   //  = min(productOfSkewFactors * min(productOfUecs, RC)/mcUEC, 
   //        minOfSkewFactor)   
   // 
   //  min(productOfUecs, RC)/mcUEC = 1 when mcUEC is not found
   // 
   CostScalar mcSkewFactor;
   if ( mcUec == csMinusOne || mcUec == csZero )
     mcSkewFactor = MINOF(productOfSkewFactors, minOfSkewFactor);
   else 
     mcSkewFactor = MINOF( 
               productOfSkewFactors * MINOF(productOfUecs, rc) / mcUec,
               minOfSkewFactor
                      );

   if ( mcSkewFactor > mc_threshold ) 
   {
       *skList = skewLists[leastSkewList];
       return TRUE;
   } else
       return FALSE;
}

//
// The content of this method is lifted from 
// DP2InsertCursorRule::nextSubstitute(). 
// A Note has been added in that method so that any changes
// to it should be "copied" here.
//
NABoolean Insert::isSideTreeInsertFeasible()
{
   // Sidetree insert is only supported for key sequenced, non-compressed,
   // non-audited tables with blocksize equal to 4K.
   // Return error, if this is not the case.

   Insert::InsertType itype = getInsertType();

   // Sidetree insert requested?
   if (itype != Insert::VSBB_LOAD )
     return FALSE;

   if ((getTableDesc()->getClusteringIndex()->getNAFileSet()
           ->isCompressed()) ||
        (getTableDesc()->getClusteringIndex()->getNAFileSet()
           ->getBlockSize() < 4096) ||
          (NOT getTableDesc()->getClusteringIndex()->getNAFileSet()
           ->isKeySequenced()) ||
         (getTableDesc()->getClusteringIndex()->getNAFileSet() ->isAudited())
      )
   {
      return FALSE;
   }

   if ( !getInliningInfo().hasPipelinedActions() ) 
     return TRUE;

   if (getInliningInfo().isEffectiveGU() ||
       getTolerateNonFatalError() == RelExpr::NOT_ATOMIC_)
      return FALSE;


   // SideInsert is not allowed when there are pipelined actions (RI,
   // IM or triggers) except MV range logging. This means the only rows
   // projected are the very first and last rows as the beginning and
   // end of the range.

   NABoolean rangeLoggingRequired =
        getTableDesc()->getNATable()->getMvAttributeBitmap().
          getAutomaticRangeLoggingRequired();

   if (getInliningInfo().isProjectMidRangeRows() || !rangeLoggingRequired)
      return FALSE;

   return TRUE;
}

// big memory growth percent (to be used by SSD overlow enhancement project)
short RelExpr::bmoGrowthPercent(CostScalar e, CostScalar m) 
{
  // bmo growth is 10% if 100*abs(maxcard-expected)/expected <= 100%
  // otherwise its 25%
  CostScalar expectedRows = e.minCsOne();
  CostScalar maxRows = m.minCsOne();
  CostScalar difference = maxRows - expectedRows;
  CostScalar uncertainty = 
    (_ABSOLUTE_VALUE_(difference.value()) / expectedRows.value()) * 100;
  if (uncertainty <= 100)
    return 10;
  else
    return 25;
}


CostScalar RelExpr::getChild0Cardinality(const Context* context)
{
   EstLogPropSharedPtr inLogProp = context->getInputLogProp();
   EstLogPropSharedPtr ch0OutputLogProp = child(0).outputLogProp(inLogProp);

   const CostScalar ch0RowCount =
      (ch0OutputLogProp) ? 
                 (ch0OutputLogProp->getResultCardinality()).minCsOne() :
                 csOne;

   return ch0RowCount;
}

NAString *RelExpr::getKey()
{

   if (operKey_.length() == 0)
   {
     char keyBuffer[30];
     snprintf(keyBuffer, sizeof(keyBuffer), "%ld", (Int64)this);
     operKey_ = keyBuffer;
   }
   return &operKey_;
}

Could you explain what is going on in this code? I notice that earlier in the method we already added selectionPred() to myLocalExpr, and then removed tempPulledPreds. I'm wondering why we are doing this again. Also, It's not obvious to me why newInputs will have different values than myNewInputs, which went through a similar calculation. An example might help.