const BaseTableDetailsPtr MVCandidate::getQueryTableForMvID(const NAString& id, NABoolean assertOnFailure)
{
  QRJoinSubGraphMapPtr mvMap = getMvSubGraphMap();
  if (!mvMap->contains(id))
  {
    if (assertOnFailure)
    {
      assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
                        FALSE, QRLogicException, 
			"Matching Query table not found in JoinGraphMap.");
    }
    return NULL;    
  }
  Int32 hashKeyIndex = mvMap->getIndexForTable(id);
  if (hashKeyIndex == -1)
  {
    assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
                      assertOnFailure, QRLogicException, 
		      "Matching Query table not found in JoinGraphMap.");
    return NULL;
  }

  const JoinGraphTablePtr queryTable = getQuerySubGraphMap()->getTableForIndex(hashKeyIndex);
  if (queryTable == NULL)
  {
    assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
                      assertOnFailure, QRLogicException, 
		      "Matching Query table not found in JoinGraphMap.");
    return NULL;
  }

  return queryTable->getBaseTable();
}
// ***************************************************************************
// ***************************************************************************
NABoolean MVCandidate::matchPredsFromEQSetToSubGraph(const QRJoinSubGraphPtr	    mvSubGraph, 
						     const QRJoinSubGraphPtr	    querySubGraph, 
						     const JoinGraphEqualitySetPtr  mvEqSet,
						     const JoinGraphEqualitySetPtr  queryEqSet,
						     const NAString&		    mvHalfPredID) 
{
  NABoolean extraHubColumnChecked = FALSE;
  const HalfPredicateList& queryHalfPreds = queryEqSet->getHalfPredicates();
  CollIndex maxEntries = queryHalfPreds.entries();
  for (CollIndex i=0; i<maxEntries; i++)
  {
    const JoinGraphHalfPredicatePtr queryHalfPred = queryHalfPreds[i];
    const JoinGraphTablePtr	    queryTable = queryHalfPred->getTable();
    if (querySubGraph->contains(queryTable))
    {
      // We found a join pred that's connected to the subgraph - match it.
      // Find the corresponding table in the MV join graph. 
      // ??? Don't assert if a match is not found, because maybe the join pred
      // ??? does not exist in the MV.
      const BaseTableDetailsPtr mvBaseTable = getMvTableForQueryID(queryTable->getID(), FALSE);
      if (mvBaseTable == NULL)
      {
	// Do we ever get here???
	assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
                          FALSE, QRLogicException, 
			  "MV hub table corresponding to query hub not found.");
      }
      else
      {
	// We have the MV hub table corresponding to the query hub table.
	// Now find its halfPred.
	const NAString& mvID = mvBaseTable->getID();
	const JoinGraphTablePtr mvTable = mvSubGraph->getParentGraph()->getTableByID(mvID);
	const JoinGraphHalfPredicatePtr mvHalfPred = mvEqSet->findHalfPredTo(mvTable);

	if (mvHalfPred == NULL)
	{
	  // This query pred has no corresponding mv pred.
	  // Handle rewrite instructions here (Not implemented yet).
	  //if (addJoinPredForExtraHubTable(mvHalfPredID, queryHalfPred) == FALSE)
	  return FALSE;
	}
	else
	{
	  // Finally we have both the MV halfPred and its corresponding query halfPred.
	  // Match them.
	  if (mvHalfPred->match(queryHalfPred) == FALSE)
	    return FALSE;
	}

	// Continue matching with the next halfPred.
	continue;
      }

    }
  }

  return TRUE;
}  // MVCandidate::matchPredsFromEQSetToSubGraph()
//*****************************************************************************
// thisSubgraph and otherSubGraph are equivalent subgraphs (from the MV and
// query join graphs). This method checks if they will continue to match
// if an extra-hub table is added. It checks that ALL predicates connecting 
// this table to the subgraph match those of the other table/subgraph.
// The assumption is that this is the MV table/subgraph, while the other 
// is the query table/subgraph, so the query can have additional join
// predicates over those in the MV. Those extra predicates are returned as 
// rewrite instructions.
//*****************************************************************************
NABoolean MVCandidate::matchPredsFromTableToSubGraph(const QRJoinSubGraphPtr mvSubGraph,
						     const QRJoinSubGraphPtr querySubGraph, 
						     const JoinGraphTablePtr mvGraphTable,
						     const JoinGraphTablePtr queryGraphTable)
{
  // For each join pred on this table
  CollIndex maxEntries = mvGraphTable->getEqualitySets().entries();
  for (CollIndex i=0; i<maxEntries; i++)
  {
    // Find the column its using
    const JoinGraphEqualitySetPtr mvEqSet = mvGraphTable->getEqualitySets()[i];
    const JoinGraphHalfPredicatePtr mvHalfPred = mvEqSet->findHalfPredTo(mvGraphTable);

    // Get the corresponding eqSet from the other join graph
    const JoinGraphEqualitySetPtr queryEqSet = queryGraphTable->getEqSetUsing(mvHalfPred);
    if (queryEqSet == NULL)
    {
      // Corresponding eqSet not found - match failed.
      return FALSE;
    }

    // Verify that the half-pred to the EQ set matches.
    const JoinGraphHalfPredicatePtr queryHalfPred = queryEqSet->findHalfPredTo(queryGraphTable);
    if (mvHalfPred->match(queryHalfPred) == FALSE)
      return FALSE;

    // Let the JoinPredMatching object continue with the matching from the EQ set to the subgraph.
    if (matchPredsFromEQSetToSubGraph(mvSubGraph, 
      				      querySubGraph, 
				      mvEqSet, 
				      queryEqSet,
				      mvHalfPred->getID()) == FALSE)
      return FALSE;
  }

  return TRUE;
}  // matchPredsFromTableToSubGraph()
//*****************************************************************************
// Add a table to the Self-Join analysis.
// This method, together with doneAddingTables(), implement a state machine
// with the following states:
//   ST_START:    The start state. No open segments.
//   ST_SINGLE:   Got the first table of a new segment, don't know segment type yet.
//   ST_UNIQUE:   Last two tables were different.
//   ST_SELFJOIN: Last two tables were the same.
//   ST_END:      We are done.
// The full state machine diagram is in the MVQR IS document.
//*****************************************************************************
void SelfJoinHandler::addTable(JoinGraphTablePtr table)
{
  const NAString& newTable = table->getName(); 
  // This is the current table index.
  UInt32 currentIndex = table->getTempNumber();
  if (currentIndex == -1)
  {
    // This subgraph generation was not yet started, 
    // This must be the initial self-join check.
    currentIndex = table->getOrdinalNumber();
  }

  // Is this table the same as the last one?
  // Skip the string comparison if the table is unique in the full join graph.
  NABoolean isDifferent = !table->isSelfJoinTable() || newTable != *lastTable_;

  // Which state in the state machine are we in?
  switch (state_)
  {
    case ST_START:
      segmentStart_ = currentIndex;
      state_ = ST_SINGLE;
      break;

    case ST_SINGLE:
      if (isDifferent)
        state_ = ST_UNIQUE;
      else
        state_ = ST_SELFJOIN;
      break;

    case ST_SELFJOIN:
      if (isDifferent)
      {
        // We have an open selfjoin segment and got a different table.
        // The selfjoin segment ends with the previous table, and the next
        // segment (which can be either type) starts with this table.
        addSegment(SelfJoinSegment::SELF_JOIN_SEGMENT);
        segmentStart_ = currentIndex;
        state_ = ST_SINGLE;
      }
      else
      {
        // do nothing.
      }
      break;

    case ST_UNIQUE:
      if (isDifferent)
      {
        // do nothing.
      }
      else
      {
        // OK, we have an open unique segment, and the new table is the same 
        // as the last one. So actually, the open unique segment ended with the
        // table before the prevous one, and the new self-join segment started 
        // with the previous table.
        segmentEnd_--;
        addSegment(SelfJoinSegment::UNIQUE_TABLE_SEGMENT);
        segmentStart_ = currentIndex-1;
        state_ = ST_SELFJOIN;
      }
      break;

    case ST_END:
      assertLogAndThrow(CAT_MVMEMO_JOINGRAPH, LL_MVQR_FAIL,
                        FALSE, QRLogicException, 
		        "Adding a table to SelfJoinHandler in ST_END state.");
      break;
  }

  lastTable_ = &table->getName();
  segmentEnd_ = currentIndex;
}
//*****************************************************************************
//*****************************************************************************
NABoolean MVCandidate::CheckAnExtraHubTable(const NAString*      tableID, 
					    QRJoinSubGraphMapPtr mvSubGraphMap,
					    QRJoinSubGraphMapPtr querySubGraphMap,
					    NABoolean&		 extraHubTableWasAdded)
{
  QRJoinSubGraphPtr mvSubGraph    = mvSubGraphMap->getSubGraph();
  QRJoinSubGraphPtr querySubGraph = querySubGraphMap->getSubGraph();

  const QRTablePtr queryTableElement = 
    getQueryDetails()->getElementForID(*tableID)->downCastToQRTable();
  const JoinGraphTablePtr queryGraphTable = 
    querySubGraph->getParentGraph()->getTableByID(queryTableElement->getID());

  // If this extra-hub table is not directly connected to the rest of 
  // the subgraph, skip it for now.
  if (queryGraphTable->isConnectedTo(querySubGraph) == FALSE)
  {
    // Add additional needed extra-hub tables here...

    return TRUE;
  }

  // Find the corresponding MV table
  const QRTablePtr mvTableElement = getMvDetails()->getTableFromName(queryTableElement->getTableName());
  const JoinGraphTablePtr mvGraphTable = 
    mvSubGraph->getParentGraph()->getTableByID(mvTableElement->getID());

  if (matchPredsFromTableToSubGraph(mvSubGraph, 
				    querySubGraph, 
				    mvGraphTable, 
				    queryGraphTable) == FALSE)
  {
    // Match failed.
    // Release the sandbox subgraphs and disqualify the MVCandidate.
    deletePtr(mvSubGraphMap);
    deletePtr(querySubGraphMap);
    logMVWasDisqualified1("Extra hub table %s is needed but cannot be matched.", 
			  queryTableElement->getTableName().data());
    return FALSE;
  }

  // Add the table to both MV and query subgraphs.
  querySubGraph->addTable(queryGraphTable);
  mvSubGraph->addTable(mvGraphTable);

  // To make sure that Pass 2 matching and result desxcriptor generation will 
  // work correctly, add the tables to the SubGraphMaps (they will have the same index).
  CollIndex index = querySubGraph->entries();
  querySubGraphMap->addTable(index, queryGraphTable);
  mvSubGraphMap->addTable(index, mvGraphTable);

  // We are done with this table, remove it from the list.
  extraHubTables_.remove(tableID);

  extraHubTableWasAdded = TRUE;
  QRLogger::log(CAT_MVCAND, LL_DEBUG,
    "ExtraHub table %s was matched for MV %s.",
  	        queryTableElement->getTableName().data(),
  	        mvDetails_->getMVName().data());

  return TRUE;
}  // MVCandidate::CheckAnExtraHubTable()
QRJbbSubsetPtr MVCandidatesForJBBSubset::generateDescriptor(CollHeap* heap)
{
  // No candidates - no work.
  if (candidateList_.entries() == 0)
    return NULL;

  // Construct the new QRJbbSubset object.
  QRJbbSubsetPtr resultDesc = new(heap) QRJbbSubset(ADD_MEMCHECK_ARGS(heap));
  resultDesc->setGroupBy(hasGroupBy_);
  if (hasGroupBy_)
  {
    // Add a reference to the query NodeID of the GroupBy node.
    const NAString& groupbyID = 
      getJbbCandidates()->getQueryJbb()->getGroupBy()->getID();
    resultDesc->setRef(groupbyID);
  }

  // For each table included in the subgraph
  QRJoinSubGraphPtr   subGraph = querySubGraphMap_->getSubGraph();
  for(subGraph->reset(); subGraph->hasNext(); subGraph->advance())
  {
    JoinGraphTablePtr table = subGraph->getCurrent();

    // Add it to the JBBSubset descriptor.
    QRTablePtr tableDesc = new(heap) QRTable(heap);
    tableDesc->setTableName(table->getName());
    tableDesc->setRef(table->getID());
    resultDesc->addTable(tableDesc);
  }

  // If candidates are disqualified during the loop itself,
  // Don't remove them from the list.
  isLoopingOnCandidates_ = TRUE;

  // Now add the MVCandidates.
  CollIndex maxEntries = candidateList_.entries();
  for (CollIndex i=0; i<maxEntries; i++)
  {
    MVCandidatePtr candidate = candidateList_[i];
    if (candidate == NULL)
    {
      // The candidate was disqualified.
      continue;
    }

    QRCandidatePtr candidateXml;
    try
      {
        candidateXml = candidate->generateDescriptor();
      }
    catch (...)
      {
        candidate->logMVWasDisqualified("an exception was thrown.");
        candidate->disqualify();  // Propagate the Info element to the result descriptor.

        candidateXml = NULL;
      }

    if (candidateXml == NULL)
    {
      // The candidate was disqualified when generating the descriptor.
      continue;
    }
    resultDesc->addCandidate(candidateXml);
  }

  if (resultDesc->getCandidateList()->entries() == 0)
  {
    // None of the candidates survived the result descriptor generation.
    deletePtr(resultDesc);
    return NULL;
  }

  return resultDesc;
}  // MVCandidatesForJBBSubset::generateDescriptor()