QRJbbResultPtr MVCandidatesForJBB::generateDescriptor(CollHeap* heap)
{
  // Allocate the JBBResult element.
  QRJbbResultPtr resultDesc = new(heap) QRJbbResult(ADD_MEMCHECK_ARGS(heap));
  // Set the reference to the JBB in the query descriptor.
  resultDesc->setRef(queryJbb_->getID());

  CollIndex maxEntries = jbbSubsets_.entries();
//  for (CollIndex i=0; i<maxEntries; i++)
  for (Int32 i=maxEntries-1; i>=0; i--)
  {
    MVCandidatesForJBBSubsetPtr jbbSubset = jbbSubsets_[i];
    QRJbbSubsetPtr jbbSubsetResult = jbbSubset->generateDescriptor(heap);
    if (jbbSubsetResult != NULL)
      resultDesc->addJbbSubset(jbbSubsetResult);
  }

  maxEntries = infoList_.entries();
  for (CollIndex j=0; j<maxEntries; j++)
  {
    QRInfoPtr info = new (heap) QRInfo(ADD_MEMCHECK_ARGS(heap));
    info->setText(*(infoList_[j]));
    resultDesc->addInfoItem(info);
  }

  // If all the MV candidates of all the JBBSubsets for this JBB has been 
  // disqualified, Don't denerate an element for it.
  if (resultDesc->getJbbSubsets().entries() == 0 && maxEntries == 0)
  {
    deletePtr(resultDesc);
    resultDesc = NULL;
  }

  return resultDesc;
}  // MVCandidatesForJBB::generateDescriptor()
MVCandidate::MVCandidate(const MVDetailsPtr	      mvDetails, 
			 const DescriptorDetailsPtr   query,
			 const QRJBBPtr		      jbb,
			 MVCandidatesForJBBSubsetPtr  jbbSubset,
			 ADD_MEMCHECK_ARGS_DEF(CollHeap* heap))
  : NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap))
    ,heap_(heap)
    ,mvDetails_(mvDetails)
    ,queryDetails_(query)
    ,queryJbb_(jbb)
    ,queryJbbDetails_(NULL)
    ,jbbSubset_(jbbSubset)
    ,mvSubGraphMap_(NULL)
    ,extraGroupingColumns_(NULL)
    ,isPreferredMatch_(FALSE)
    ,outputMatching_(ADD_MEMCHECK_ARGS(heap))
    ,rangeMatching_(ADD_MEMCHECK_ARGS(heap))
    ,residualMatching_(ADD_MEMCHECK_ARGS(heap))
    ,groupingMatching_(ADD_MEMCHECK_ARGS(heap))
    ,joinPredMatching_(ADD_MEMCHECK_ARGS(heap))
    ,lojMatching_(ADD_MEMCHECK_ARGS(heap))
    ,matchingType_(ANT_NO_INIT)
    ,extraHubTables_(heap)
    ,backJoinTables_(heap)
    ,wasDisqualified_(FALSE)
    ,disqualifiedReason_(NULL)
    ,tableIDCache_(hashKey, INIT_HASH_SIZE_SMALL, FALSE, heap)
{ 
  if (jbbSubset)
    jbbSubset->registerCandidate(this);

  outputMatching_.setCandidate(this);
  rangeMatching_.setCandidate(this);
  residualMatching_.setCandidate(this);
  groupingMatching_.setCandidate(this);
  joinPredMatching_.setCandidate(this);
  lojMatching_.setCandidate(this);
}
MVCandidatesForJBBSubsetPtr QRGroupLattice::search(QRJBBPtr	           queryJbb, 
						   MVCandidatesForJBBPtr   mvCandidates, 
						   QRJoinSubGraphMapPtr    map,
                                                   ElementPtrList*         minimizedGroupingList)
{
  QRTRACER("QRGroupLattice::search()");
  DescriptorDetailsPtr queryDetails = mvCandidates->getAllCandidates()->getQueryDetails();
  LatticeKeyList keyList(heap_);

  if (minimizedGroupingList)
  {
    if (!elementListToKeyList(*minimizedGroupingList, keyList, map, queryDetails, FALSE))
      return NULL;
  }
  else
  {
    // Get the grouping columns of the passed query JBB, and create a key list
    // from them. Return now if there are no grouping items -- a query without a
    // Group By can't match an MV that has one.
    QRGroupByPtr groupBy = queryJbb->getGroupBy();
    if (!groupBy)
      return NULL;

    // An aggregate query with no GroupBy has an empty key list.
    if (!getGroupingLatticeKeys(keyList, map, queryDetails, queryJbb, TRUE, FALSE))
      return NULL;
  }

  // Create the search node, disallowing the addition of any new keys to the hash
  // table used by the lattice; if the key isn't already used, the node can have
  // no supersets.
  QRLatticeIndexSearchNode searchNode(keyList, lattice_, TRUE, heap_);
  if (!searchNode.isValid())   // because a key wasn't added
    return NULL;

  NAString keysText;
  searchNode.dumpNode(keysText, *lattice_->getKeyArr());
  QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, 
    "Searching LatticeIndex for: %s.", keysText.data());

  // Find the supersets of the query's group-by list in the MV group lattice
  // index. For each lattice index node returned, create an MVCandidate
  // corresponding to each MV represented by that node, and add it to the
  // candidate list passed by the caller.
  NAPtrList<QRLatticeIndexNodePtr> nodes;
  lattice_->findSupersets(searchNode, nodes);

  if (nodes.entries() == 0)
  {
    QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, "No match found.");
    return NULL;
  }

  MVCandidatesForJBBSubsetPtr jbbSubset = new(heap_) 
    MVCandidatesForJBBSubset(mvCandidates, ADD_MEMCHECK_ARGS(heap_));
  jbbSubset->setSubGraphMap(map);
  jbbSubset->setGroupBy(TRUE);

  if (minimizedGroupingList != NULL)
    jbbSubset->setMinimizedGroupingList(minimizedGroupingList);

  for (CollIndex i=0; i<nodes.entries(); i++)
    {
      QRLatticeIndexNodePtr thisNode = nodes[i];
      const SubGraphMapList& matchingMVs = thisNode->getMVs();
      NABoolean isPreferred = (*thisNode == searchNode); // preferred if exact match

      GroupingList* extraGroupingColumns = new(heap_) GroupingList(heap_);
      if (!isPreferred)
      {
	// For preferred match modes, there are no extraGroupingColumns.
	LatticeKeySubArray* diff = thisNode->computeDiff(searchNode, heap_);
	for(CollIndex i = 0; diff->nextUsed(i); i++)
	{
          LatticeIndexablePtr key = diff->element(i);
          QRElementPtr elem = keyToElement(key, map);
	  extraGroupingColumns->insert(elem);
	}
	delete diff;
      }

      for (CollIndex j=0; j<matchingMVs.entries(); j++)
        {
	  MVDetailsPtr mv = matchingMVs[j]->getMVDetails();
          QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, 
            "Found MV: %s!", mv->getMVName().data() );
	  jbbSubset->insert(mv, queryJbb, isPreferred, extraGroupingColumns, NULL, heap_);
        }
    }

  return jbbSubset;

}  // QRGroupLattice::search()
NABoolean MVCandidate::CheckExtraHubTables()
{
  if (extraHubTables_.entries() == 0)
    return TRUE;

  // Allocate a new subGraph and subGraphMap for the MV - the one we have is from MVMemo,
  // so its considered read-only.
  QRJoinSubGraphMapPtr mvSubGraphMap = new(heap_) 
    QRJoinSubGraphMap(*getMvSubGraphMap(), ADD_MEMCHECK_ARGS(heap_));

  // The query subGraph is shared between the MVCandidates that were found 
  // in the same MVMemo group, so work on a copy.
  QRJoinSubGraphMapPtr querySubGraphMap = new(heap_) 
    QRJoinSubGraphMap(*getJbbSubset()->getQuerySubGraphMap(), ADD_MEMCHECK_ARGS(heap_));

  // Loop over the list, each time picking an extra-hub table that is
  // directly connected to a table already in the matched sub-graph,
  // until all the tables are checked.
  do 
  {
    NABoolean extraHubTableWasAdded = FALSE;
    // Do the loop backwards because we remove checked extra-hub tables from the list.
    for (Int32 i=(CollIndex)extraHubTables_.entries()-1; i>=0; i--)
    {
      if (CheckAnExtraHubTable(extraHubTables_[i], 
			       mvSubGraphMap,    // IN/OUT
			       querySubGraphMap, // IN/OUT
			       extraHubTableWasAdded) == FALSE)
	 return FALSE;
    }  // for

    // Keep looping as long as there are extra-hub tables that are not connected, 
    // and as long as we keep adding them. 
    if (!extraHubTableWasAdded && extraHubTables_.entries() > 0)
    {
      logMVWasDisqualified1("Extra hub table %s is needed but cannot be matched.", 
  			    extraHubTables_[0]->data());
      return FALSE;
    }
//    assertLogAndThrow(extraHubTableWasAdded, QRLogicException, 
//		      "Extra-hub table is not connected to the hub.");

  } while (extraHubTables_.entries() > 0);

  // Update the MV new subGraphMap
  mvSubGraphMap_ = mvSubGraphMap;

  // We have added at least one extra-hub table - 
  // now we need to reorg the JBBSubsets accordingly.
  MVCandidatesForJBBPtr jbb = getJbbSubset()->getJbbCandidates();
  MVCandidatesForJBBSubsetPtr newJbbSubset = NULL;
  QRJoinSubGraphPtr querySubGraph = querySubGraphMap->getSubGraph();
  if (jbb->contains(querySubGraph))
  {
    // The JBBSubset with the new extra-hub tables already exists.
    newJbbSubset = jbb->getJbbSubsetFor(querySubGraph);
  }
  else
  {
    // Need to create a new JBBSubset.
    newJbbSubset = new (heap_) MVCandidatesForJBBSubset(jbb, ADD_MEMCHECK_ARGS(heap_));
    newJbbSubset->setSubGraphMap(querySubGraphMap);

    // Add the new JbbSubset to the JBB.
    jbb->insert(newJbbSubset);
  }

  // Move the candidate from the current JbbSubset to the new one.
  getJbbSubset()->remove(this);
  newJbbSubset->insert(this);
  setJbbSubset(newJbbSubset);

  return TRUE;
}  // CheckExtraHubTables()