// ***************************************************************************
// ***************************************************************************
void MVDetails::addEqualitySet(QRJoinPredPtr joinPredElement, 
                               NABoolean     isFromExpr, 
                               QROutputPtr   output, 
                               CollHeap*     heap)
{
  const ElementPtrList& equalitySet = joinPredElement->getEqualityList();

  for (CollIndex i=0; i<equalitySet.entries(); i++)
  {
    const QRElementPtr halfPred = equalitySet[i];

    // Now lets look at the equality set members
    // Ignore members that are not simple columns.
    if (halfPred->getElementType() != ET_Column)
      continue;

    const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn();

    // If this JoinPred element is a column of an LOJ table,
    // skip it because it is not really equal to the others.
    const QRElementPtr tableElem = getElementForID(eqSetColumn->getTableID());
    if (tableElem->downCastToQRTable()->hasLOJParent())
      continue;

    // Compute the "ID Qualified" column name as the key.
    const NAString* idQualifiedColName = 
      calcIDQualifiedColumnName(eqSetColumn->getTableID(),  eqSetColumn->getColumnName(), heap);

    // Insert into the hash table.
    if (isFromExpr)
      outputByInputColumns_.insert(idQualifiedColName, output);
    else
      outputByColumnName_.insert(idQualifiedColName, output);
  }
} // MVDetails::addEqualitySet
// ***************************************************************************
// ***************************************************************************
void DescriptorDetails::initJoinPredList(const QRJoinPredListPtr jpList)
{
  if (jpList == NULL)
    return;

  CollIndex maxEntries = jpList->entries();
  //Iterate over JoinPred list
  for (CollIndex i=0; i<maxEntries; i++)
  {
    const QRJoinPredPtr joinPred = (*jpList)[i];
    const ElementPtrList& equalitySet = joinPred->getEqualityList();

    // Iterate over equality set in JoinPred.
    for (CollIndex i=0; i<equalitySet.entries(); i++)
    {
      const QRElementPtr halfPred = equalitySet[i]->getReferencedElement();

      // Now lets look at the equality set members
      // Ignore members that are not simple columns.
      if (halfPred->getElementType() != ET_Column)
        continue;

      const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn();

      // Skip columns from other JBBs.
      if (getElementForID(eqSetColumn->getTableID()) == NULL)
        continue;

      // Skip columns from LOJ tables.
      if (isColumnFromLojTable(eqSetColumn))
        continue;

      const NAString& columnID = eqSetColumn->getID();
      joinPredHash_.insert(&columnID, joinPred);
    }
  }
}
// ***************************************************************************
// ***************************************************************************
void JBBDetails::initRangePreds(CollHeap* heap)
{
  // Initialize the Range predicates.
  const QRRangePredListPtr rangePreds = jbbDesc_->getHub()->getRangePredList();
  if (rangePreds==NULL || rangePreds->entries() == 0)
  {
    hasNoRangePredicates_ = TRUE;
    return;
  }

  for (CollIndex k=0; k<rangePreds->entries(); k++)
  {
    const QRRangePredPtr rangePred = (*rangePreds)[k];
    const QRElementPtr rangeElem = rangePred->getRangeItem()->getReferencedElement();

    if (rangeElem->getElementType() == ET_Column && rangePred->isSingleValue())
    {
      constColumns_.insert(&rangeElem->getID());
    }

    // For query descriptors, only check for const columns.
    if (!isAnMV_)
      continue;

    if (rangeElem->getElementType() == ET_Column)
    {
      const QRColumnPtr rangeColumn = rangeElem->downCastToQRColumn();

      if (!descDetails_->isFromJoin(rangeColumn))
      {
        // This column is NOT part of a join predicate.
        BaseTableDetailsPtr rangeTable = getBaseTableByID(rangeColumn->getTableID());
        rangeTable->addRangePredicateOnColumn(rangePred, rangeColumn);
      }
      else
      {
        // This column IS part of a join predicate.
        // Insert the range pred for all the participating columns.
        const QRJoinPredPtr jp = descDetails_->getJoinPred(rangeColumn);
        const ElementPtrList& equalitySet = jp->getEqualityList();
        for (CollIndex i=0; i<equalitySet.entries(); i++)
        {
	  const QRElementPtr halfPred = equalitySet[i];

	  // Now lets look at the equality set members
	  // Ignore members that are not simple columns.
	  if (halfPred->getElementType() != ET_Column)
	    continue;

	  const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn();
          BaseTableDetailsPtr rangeTable = getBaseTableByID(eqSetColumn->getTableID());
          rangeTable->addRangePredicateOnColumn(rangePred, eqSetColumn);
        }
      }
    }
    else if (rangeElem->getElementType() == ET_JoinPred)
    {
      assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
                        (rangeElem->getElementType() == ET_JoinPred), QRLogicException, 
			"Unexpected JoinPred element.");

      // If its an equality set, insert the range pred for all the participating columns.
      const QRJoinPredPtr joinPredElement = rangeElem->downCastToQRJoinPred();
      const ElementPtrList& equalitySet = joinPredElement->getEqualityList();
  
      for (CollIndex i=0; i<equalitySet.entries(); i++)
      {
	const QRElementPtr halfPred = equalitySet[i];

	// Now lets look at the equality set members
	// Ignore members that are not simple columns.
	if (halfPred->getElementType() != ET_Column)
	  continue;

	const QRColumnPtr eqSetColumn = halfPred->getReferencedElement()->downCastToQRColumn();
        BaseTableDetailsPtr rangeTable = getBaseTableByID(eqSetColumn->getTableID());
        rangeTable->addRangePredicateOnColumn(rangePred, eqSetColumn);
      }
    }
    else
    {
      // The range pred is on an expression. Insert it for all the expression's input columns.
      const QRExprPtr rangeExpr = rangeElem->downCastToQRExpr();

      if (rangeExpr->getExprRoot()->containsAnAggregate(heap))
      {
        hasHavingPredicates_ = TRUE;
      }

      // Verify we have a single input column
      // No input columns mean its a COUNT(*), which is OK too.
      const ElementPtrList& inputs = rangeExpr->getInputColumns(heap);
      assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
                        (inputs.entries() <= 1), QRLogicException, 
			"Range predicate expression must have a single input at most.");

      const NAString& predText = rangeExpr->getExprText();

      // Look it up in the hash table: do we already have a range pred with this text?
      RangePredPtrList* predList = getRangePredsOnExpression(predText);

      if (predList == NULL)
	{
	  // No, its the first one. Create a new list.
	  predList = new(heap) RangePredPtrList(heap);
	  // And insert it into the hash table.
	  rangePredicates_.insert(&predText, predList);
	}

      // Insert the predicate into the pred list.
      predList->insert(rangePred);
    }
  }
}  // JBBDetails::init()
LatticeIndexablePtr QRGroupLattice::elementToKey(const QRElementPtr      element, 
                                                 QRJoinSubGraphMapPtr    map,
						 DescriptorDetailsPtr    queryDetails, 
                                                 NABoolean               insertMode,
                                                 NABoolean               isRecursive)
{
  QRElementPtr elem = element->getReferencedElement();
  QRColumnPtr col = NULL;
  if (elem->getElementType() == ET_Column)
  {
    col = elem->downCastToQRColumn();
    if (queryDetails->isFromJoin(col) && !isRecursive)
    {
    // This groupby element is a join pred.
    // During insert, this loop inserts the first column.
    // During search, try each equality set element until we find one that 
    // appears in our array.
      const QRJoinPredPtr eqSet = queryDetails->getJoinPred(col);
      const ElementPtrList& equalityList = eqSet->getEqualityList();

      if (insertMode)
      {
        // Insert mode - pick the first column of the equality set.
        col = equalityList[0]->downCastToQRColumn();
      }
      else
      {
        // Search mode - pick the first equality set entry that is found in this LatticeIndex.
        for (CollIndex i=0; i<equalityList.entries(); i++)
        {
          QRElementPtr entry = equalityList[i]->getReferencedElement();
          // Call recursively for each join pred column.
          LatticeIndexablePtr entryKey = elementToKey(entry, map, queryDetails, insertMode, TRUE);
          if (entryKey == NULL)
            continue;

          if (lattice_->contains(entryKey))
          {
            // Found it in the lattice index.
            col = entry->downCastToQRColumn();
            break;
          }
        } // for ( )

        // If none of the entries was found - give up now.
        if (col == NULL)
          return NULL;

      }  // if insert mode
    }  // if JoinPred
  } // if Column

  NAString* key = NULL;
  if (col != NULL)
  {
    col = col->getReferencedElement()->downCastToQRColumn();
    const NAString& tableID = col->getTableID();
    Int32 Inx = map->getIndexForTable(tableID);
    if (Inx == -1)
    {
      assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR,
                        !insertMode, QRLogicException, 
		        "Table index not found in Insert mode (Inserting a multi-JBB MV?).");
      return NULL;
    }

    char buffer[4096+5];
    sprintf(buffer, "%d.%s", Inx, col->getColumnName().data() );

    key = new(heap_) NAString(buffer, heap_);
  }
  else
  {
    // This is an expression.
    // TBD We still do not handle expressions using columns from self-join tables.
    key = new(heap_) NAString(element->getSortName(), heap_);
  }

  // The reverseKeyHash should always use the most recent MV inserted.
  if (insertMode)
  {
    reverseKeyHash_.remove(key);
    reverseKeyHash_.insert(key, element);
  }
  return key;
}