MultiJoin::MultiJoin(const JBBSubset & jbbSubset,
                     CollHeap *oHeap)
  : RelExpr(REL_MULTI_JOIN, NULL, NULL, oHeap)
  , jbbSubset_(jbbSubset)
  , childrenMap_(oHeap)
  , scheduledLSRs_(oHeap)
{
  // Need to initialize the childrenMap
  // This will set all children to NULL
  CANodeIdSet jbbcs = jbbSubset_.getJBBCs();
  Lng32 index = 0;

  for (CANodeId x= jbbcs.init();
       jbbcs.next(x);
       jbbcs.advance(x) )
  {
    JBBCExprGroupEntry* entry = new (oHeap)
      JBBCExprGroupEntry(x, (RelExpr*)NULL, oHeap);

    childrenMap_.insertAt(index, entry);
	index++;
  }

  lsrC_ = new (oHeap) LSRConfidence(oHeap);
#pragma warning (disable : 4018)  //warning elimination
  CMPASSERT (getArity() == jbbcs.entries());
#pragma warning (default : 4018)  //warning elimination
}
// This method forms the join expression with the estLogProps.
Join * AppliedStatMan::formJoinExprWithEstLogProps(
					const EstLogPropSharedPtr& leftEstLogProp,
					const EstLogPropSharedPtr& rightEstLogProp,
					const EstLogPropSharedPtr& inputEstLogProp,
					const ValueIdSet * setOfPredicates,
					const NABoolean cacheable,
					JBBSubset * combinedJBBSubset)
{
  // Form a join expression with these estLogProps.

  // form the left child. Since the estLogProps of the left and the
  // right children exist, these can be treated as Scan expressions

  Scan * leftChildExpr = new STMTHEAP Scan();
  GroupAttributes * galeft = new STMTHEAP GroupAttributes();

  // set GroupAttr of the leftChild
  galeft->inputLogPropList().insert(inputEstLogProp);
  galeft->outputLogPropList().insert(leftEstLogProp);
  CANodeIdSet * leftNodeSet = leftEstLogProp->getNodeSet();

  CANodeId nodeId;

  if (leftNodeSet)
  {
    if (leftNodeSet->entries() == 1)
    {
      nodeId = leftNodeSet->getFirst();
      if(nodeId.getNodeAnalysis()->getTableAnalysis())
	leftChildExpr->setTableAttributes(nodeId);
    }
    CostScalar minEstCard = leftNodeSet->getMinChildEstRowCount();

    galeft->setMinChildEstRowCount(minEstCard);
  }

  leftChildExpr->setGroupAttr(galeft);
  galeft->setLogExprForSynthesis(leftChildExpr);

  // form the right child and set its groupAttr
  Scan * rightChildExpr = new STMTHEAP Scan();
  GroupAttributes * garight = new STMTHEAP GroupAttributes();
  garight->inputLogPropList().insert(inputEstLogProp);
  garight->outputLogPropList().insert(rightEstLogProp);
  CANodeIdSet * rightNodeSet = rightEstLogProp->getNodeSet();

  // xxx

  JBBC * singleRightChild = NULL;
  Join * singleRightChildParentJoin = NULL;
  ValueIdSet leftOuterJoinFilterPreds;


  if (rightNodeSet)
  {
    if (rightNodeSet->entries() == 1)
    {
      nodeId = rightNodeSet->getFirst();
      if(nodeId.getNodeAnalysis()->getTableAnalysis())
	rightChildExpr->setTableAttributes(nodeId);
	  if(nodeId.getNodeAnalysis()->getJBBC())
	  {
		  singleRightChild = nodeId.getNodeAnalysis()->getJBBC();
		  if(singleRightChild)
		    singleRightChildParentJoin = singleRightChild->getOriginalParentJoin();
	  }
    }
    CostScalar minEstCard = rightNodeSet->getMinChildEstRowCount();

    garight->setMinChildEstRowCount(minEstCard);
  }

  rightChildExpr->setGroupAttr(garight);
  garight->setLogExprForSynthesis(rightChildExpr);

  Join * joinExpr = NULL;
  if(singleRightChild &&
	 singleRightChildParentJoin)
  {
      if(singleRightChildParentJoin->isSemiJoin())
        joinExpr = new STMTHEAP Join(leftChildExpr,
                                     rightChildExpr,
                                     REL_SEMIJOIN,
                                     NULL);

      if(singleRightChildParentJoin->isAntiSemiJoin())
        joinExpr = new STMTHEAP Join(leftChildExpr,
                                     rightChildExpr,
                                     REL_ANTI_SEMIJOIN,
                                     NULL);

      if(singleRightChildParentJoin->isLeftJoin())
      {
        joinExpr = new STMTHEAP Join(leftChildExpr,
			                          rightChildExpr,
									  REL_LEFT_JOIN,
									  NULL);
        leftOuterJoinFilterPreds += singleRightChild->getLeftJoinFilterPreds();
      }

      if(joinExpr)
      {
        joinExpr->setJoinPred(singleRightChild->getPredsWithPredecessors());

        joinExpr->nullInstantiatedOutput().insert(singleRightChild->
                                                    nullInstantiatedOutput());
      }
  }

  if(!joinExpr)
  {
  // now form a JoinExpr with these left and right children.
  joinExpr = new STMTHEAP Join(leftChildExpr,  // left child
				      rightChildExpr, // right child
				      REL_JOIN,	      // join type
				      NULL);	      // join predicates
  }

  ValueIdSet selPredsAndLOJFilter = leftOuterJoinFilterPreds;
  selPredsAndLOJFilter += (*setOfPredicates);
  joinExpr->setSelectionPredicates(selPredsAndLOJFilter);

  // set groupAttr of this Join expression
  GroupAttributes * gaJoin = new STMTHEAP GroupAttributes();

  // set required outputs of Join as sum of characteristic
  // outputs of the left and the right children
  ValueIdSet requiredOutputs;

  if (leftNodeSet)
    requiredOutputs.addSet(getPotentialOutputs(*(leftNodeSet)));

  if (rightNodeSet)
    requiredOutputs.addSet(getPotentialOutputs(*(rightNodeSet)));

  gaJoin->setCharacteristicOutputs(requiredOutputs);

  // set JBBSubset for this group, if all estLogProps are cacheable.
  // Else JBBSubset is NULL

  if (cacheable)
    gaJoin->getGroupAnalysis()->setLocalJBBView(combinedJBBSubset);

  gaJoin->setMinChildEstRowCount(MINOF(garight->getMinChildEstRowCount(), galeft->getMinChildEstRowCount() ) );

  joinExpr->setGroupAttr(gaJoin);

  // if there are some probes coming into the join
  // then join type = tsj.
  if ((inputEstLogProp->getResultCardinality() > 1) ||
      (inputEstLogProp->getColStats().entries() > 1))
  {
    if (cacheable)
    {
      CANodeIdSet inputNodeSet =  *(inputEstLogProp->getNodeSet());
      gaJoin->setCharacteristicInputs(getPotentialOutputs(inputNodeSet));
    }
  }

  joinExpr->setGroupAttr(gaJoin);
  gaJoin->setLogExprForSynthesis(joinExpr);
  return joinExpr;

} // AppliedStatMan::formJoinExprWithEstLogProps
CostScalar AppliedStatMan::computeJoinReduction(
          const CANodeIdSet & leftChildren,
          const CANodeIdSet & rightChildren)
{
  CostScalar result = 0;

  // get stats for left
  EstLogPropSharedPtr leftCard =
    getStatsForCANodeIdSet(leftChildren);

  // get stats for right
  EstLogPropSharedPtr rightCard =
    getStatsForCANodeIdSet(rightChildren);

  CANodeIdSet jbbcsJoinedToRight;
  CANodeIdSet allPredecessors;
  CANodeIdSet allSuccessors;

  for( CANodeId rChild = rightChildren.init();
       rightChildren.next(rChild);
       rightChildren.advance(rChild))
  {
    JBBC * rChildJBBC = rChild.getNodeAnalysis()->getJBBC();
    jbbcsJoinedToRight += rChildJBBC->getJoinedJBBCs();
    jbbcsJoinedToRight += rChildJBBC->getPredecessorJBBCs();
    allPredecessors    += rChildJBBC->getPredecessorJBBCs();
    jbbcsJoinedToRight += rChildJBBC->getSuccessorJBBCs();
    allSuccessors      += rChildJBBC->getSuccessorJBBCs();
  }

  CANodeIdSet dependencyCausingNodesFromLeft = leftChildren;
  dependencyCausingNodesFromLeft.intersectSet(allPredecessors + allSuccessors);

  CANodeIdSet leftNodesJoinedToRight = leftChildren;
  leftNodesJoinedToRight.intersectSet(jbbcsJoinedToRight);

  if(!leftNodesJoinedToRight.entries())
  {
    result = rightCard->getResultCardinality();
    return result;
  }

  CANodeIdSet leftSetPredecessors;
  CANodeIdSet newNodes = leftNodesJoinedToRight;
  CANodeIdSet nodesConsidered;

  while(newNodes.entries())
  {
    for( CANodeId lChild = newNodes.init();
         newNodes.next(lChild);
         newNodes.advance(lChild))
    {
      JBBC * lChildJBBC = lChild.getNodeAnalysis()->getJBBC();
      leftSetPredecessors += lChildJBBC->getPredecessorJBBCs();
      nodesConsidered += lChild;
    }

    leftSetPredecessors.intersectSet(leftChildren);
    newNodes = leftSetPredecessors;
    newNodes -= nodesConsidered;
  }

  leftNodesJoinedToRight += leftSetPredecessors;

  // for a JBBSubset to be legal it has to have at least one
  // independent jbbc i.e. a jbbcs connect via a innerNonSemiNonTsjJoin
  // Assumption: leftChildren represents a legal JBBSubset
  CANodeIdSet independentJBBCsInLeftNodesJoinedToRight =
    QueryAnalysis::Instance()->getInnerNonSemiNonTSJJBBCs();

  independentJBBCsInLeftNodesJoinedToRight.intersectSet(leftNodesJoinedToRight);
  
  if(!independentJBBCsInLeftNodesJoinedToRight.entries())
    leftNodesJoinedToRight += 
      leftChildren.jbbcsToJBBSubset()->
        getJBBSubsetAnalysis()->
          getLargestIndependentNode();

  EstLogPropSharedPtr cardLeftNodesJoinedToRight =
    getStatsForCANodeIdSet(leftNodesJoinedToRight);

  // All nodes connected via a join
  CANodeIdSet connectedNodes(leftNodesJoinedToRight);
  connectedNodes += rightChildren;

  EstLogPropSharedPtr cardConnectedNodes =
    joinJBBChildren(leftNodesJoinedToRight,rightChildren);

  result = cardConnectedNodes->getResultCardinality() /
             cardLeftNodesJoinedToRight->getResultCardinality();

  return result;
}
Join * AppliedStatMan::formJoinExprWithCANodeSets(
					const CANodeIdSet & leftNodeSet,
					const CANodeIdSet & rightNodeSet,
					EstLogPropSharedPtr& inLP,
					const ValueIdSet * joinPreds,
					const NABoolean cacheable)
{
  EstLogPropSharedPtr leftEstLogProp = NULL;
  EstLogPropSharedPtr rightEstLogProp = NULL;

  CANodeIdSet * inputNodeSet = NULL;
  if (inLP->isCacheable())
  {
    inputNodeSet = inLP->getNodeSet();

    // if inLP are cacheable these should have a nodeSet attached
    // if it is not for some reason, assert in debug mode. In release
    // mode do not look for properties in ASM cache, instead get them
    // from group attr cache.
    if (inputNodeSet == NULL)
    {
      CCMPASSERT(inputNodeSet != NULL);
      inLP->setCacheableFlag(FALSE);
    }
  }

  CANodeIdSet commonNodeSet = leftNodeSet;
  commonNodeSet.intersectSet(rightNodeSet);

  // remove CANodeIds which are common to both left and the right children
  // from the child, whose estLogProps are not cached. If the estLogProps
  // of both children are not cached, then remove it from the child which
  // has a larger CANodeIdSet associated with it.

  CANodeIdSet tempLeftNodeSet = leftNodeSet;
  CANodeIdSet tempRightNodeSet = rightNodeSet;

  if (commonNodeSet.entries() > 0)
  {
    if (lookup(leftNodeSet))
      tempRightNodeSet.subtractSet(commonNodeSet);
    else
      if (lookup(rightNodeSet))
	tempLeftNodeSet.subtractSet(commonNodeSet);
      else
	if (leftNodeSet.entries() > rightNodeSet.entries())
	  tempLeftNodeSet.subtractSet(commonNodeSet);
	else
	  tempRightNodeSet.subtractSet(commonNodeSet);
  }

  // get the estLogProps for the left and the right child.
  // If these are not in the cache, then synthesize them incrementally
  // starting from the left most JBBC in the JBBSubset

  if (inputNodeSet)
  {
    // leftEstLogProp cached?

    CANodeIdSet combinedNodeSetWithInput = tempLeftNodeSet;
    combinedNodeSetWithInput.insert(*inputNodeSet);

    leftEstLogProp = getCachedStatistics(&combinedNodeSetWithInput);

    combinedNodeSetWithInput = tempRightNodeSet;
    combinedNodeSetWithInput.insert(*inputNodeSet);

    rightEstLogProp = getCachedStatistics(&combinedNodeSetWithInput);
  }

  if (leftEstLogProp == NULL)
      leftEstLogProp = synthesizeLogProp(&tempLeftNodeSet, inLP);

  // if the estimate logical properties have been computed for non-cacheable
  // inLP, then these would not contain nodeSet. But we do need the nodeSet
  // to compute potential output values. Hence we shall add this now

  if (!leftEstLogProp->getNodeSet())
  {
    CANodeIdSet * copyLeftNodeSet = new (STMTHEAP) CANodeIdSet (tempLeftNodeSet);
    leftEstLogProp->setNodeSet(copyLeftNodeSet);
  }

  if (rightEstLogProp == NULL)
      rightEstLogProp = synthesizeLogProp(&tempRightNodeSet, inLP);

  if (!rightEstLogProp->getNodeSet())
  {
    CANodeIdSet * copyRightNodeSet = new (STMTHEAP) CANodeIdSet (tempRightNodeSet);
    rightEstLogProp->setNodeSet(copyRightNodeSet);
  }

  // Now form the join expressions with these EstLogProp,
  // inLP and the joinPred will be same as those for which the
  // estLogProp are to be synthesized. Cacheable flag would depend
  // on whether left, right and the outer child are caheable, or
  // if the join is on all columns or not

  // Since the join expression consists of the left and the right
  // JBBSubsets, the JBBSubset for this Join expression would be
  // the superset of left and right JBBSubset

  JBBSubset * combinedSet = leftNodeSet.jbbcsToJBBSubset();
  combinedSet->addSubset(*(rightNodeSet.jbbcsToJBBSubset()));

  // Now form the join expressions with these EstLogProp,
  // inLP and the joinPred will be same as those for which the
  // estLogProp are to be synthesized. Cacheable flag would depend
  // on whether left, right and the outer child are ccaheable, or
  // if the join is on all columns or not

  return formJoinExprWithEstLogProps(leftEstLogProp, rightEstLogProp,
			    inLP, joinPreds, cacheable, combinedSet);



} // AppliedStatMan::formJoinExprWithCANodeSets