Join* MultiJoin::createLeftLinearJoinTree
                   (const NAList<CANodeIdSet> * const leftDeepJoinSequence,
                    NAList<MJJoinDirective *> * joinDirectives) const
{
  Join* result = NULL;

  Join* currentJoin=NULL;

  NABoolean reUseMultiJoins = FALSE;

  //Set of all JBBCs in this multi-join.
  //This set will be broken up to make the join tree
  //representing the substitue.
  //The loop below will construct the join tree,
  //starting from the top join.
  CANodeIdSet childSet = getJBBSubset().getJBBCs();

  // variables used in loop below
  MultiJoin * currentMJoin = (MultiJoin *) this;

  // in an iteration this is the parent join
  // e.g. when we are create JOIN3, this will
  // be JOIN2
  Join * parentJoin = NULL;

#ifdef _DEBUG
  if ( CmpCommon::getDefault( NSK_DBG ) == DF_ON  &&
       CmpCommon::getDefault( NSK_DBG_MJRULES_TRACKING ) == DF_ON )
  {
// LCOV_EXCL_START - dpm
    CURRCONTEXT_OPTDEBUG->stream() << "Following is left deep join sequence: " << endl;
    CURRCONTEXT_OPTDEBUG->stream() << endl;
// LCOV_EXCL_STOP
  }
#endif

  UInt32 numJoinChildren = leftDeepJoinSequence->entries();

  CANodeId currentTable = NULL_CA_ID;

  for (UInt32 i = 0; i < (numJoinChildren-1); i++)
  {
    //create JBBSubset representing a comprising component of the
    //leftDeepJoinSequence.
    JBBSubset * joinRightChild = ((*leftDeepJoinSequence)[i]).computeJBBSubset();

    MJJoinDirective * joinDirective = (*joinDirectives)[i];

    //remove all tables that will become right side of join
    childSet.remove((*leftDeepJoinSequence)[i]);

#ifdef _DEBUG
    //print the right child of the current join
    if ( CmpCommon::getDefault( NSK_DBG ) == DF_ON  &&
       CmpCommon::getDefault( NSK_DBG_MJRULES_TRACKING ) == DF_ON )
    {
      CURRCONTEXT_OPTDEBUG->stream() << ((*leftDeepJoinSequence)[i]).getText() << endl; // LCOV_EXCL_LINE - dpm
    }
#endif
    //Get JBBSubset for left side of join
    JBBSubset * joinLeftChild = childSet.computeJBBSubset();

    //create the join by doing a split of the multi-join
    currentJoin = currentMJoin->splitSubset(*joinLeftChild, *joinRightChild, reUseMultiJoins);

    joinDirective->setupJoin(currentJoin);

    if ( CmpCommon::getDefault(COMP_BOOL_120) == DF_OFF)
      currentJoin->updatePotential(-3);

    // if this is the first iteration
    // set the result to be the top join
    if (i == 0)
      result = currentJoin;

    //set the current multi-join to the left child of the
    //join just created
    //change getChild to child().getPointer
    currentMJoin = (MultiJoin*) currentJoin->getChild(0);

    //if there was a parent join, set the left child to
    //point to the new join we just created i.e. currentJoin.
    if (parentJoin)
      parentJoin->setChild(0,currentJoin);

    //set currentJoin to be the parent for the next iteration
    parentJoin = currentJoin;

  }

#ifdef _DEBUG
  //print the left most child
  if ( CmpCommon::getDefault( NSK_DBG ) == DF_ON  &&
       CmpCommon::getDefault( NSK_DBG_MJRULES_TRACKING ) == DF_ON )
  {
// LCOV_EXCL_START  - dpm
    CURRCONTEXT_OPTDEBUG->stream() << ((*leftDeepJoinSequence)[(numJoinChildren-1)]).getText() << endl;
    CURRCONTEXT_OPTDEBUG->stream() << endl;
// LCOV_EXCL_STOP
  }
#endif

  // end - construct the join tree

  // synth the join
  result->synthLogProp();

  //if the right child of the top-most join is a multi-Join,
  //synthesize_it
  if(result->child(1))
    if(result->child(1)->getOperatorType()==REL_MULTI_JOIN)
      result->child(1)->synthLogProp();

  // synth the left child too
  result->child(0)->synthLogProp();

  return result;

} // MultiJoin::createLeftLinearJoinTree()
// LCOV_EXCL_START :cnu
EstLogPropSharedPtr AppliedStatMan::joinEstLogProps (
              const EstLogPropSharedPtr& leftEstLogProp,
              const EstLogPropSharedPtr& rightEstLogProp,
              const EstLogPropSharedPtr& inLP)
{
  EstLogPropSharedPtr outputEstLogProp;

  NABoolean cacheable = FALSE;

  CANodeIdSet * inputNodeSet = inLP->getNodeSet();

  // These nodesets could be NULL, if the estLogProps to which they
  // belong are not cacheable

  CANodeIdSet * leftNodeSet = leftEstLogProp->getNodeSet();
  CANodeIdSet * rightNodeSet = rightEstLogProp->getNodeSet();

  if ((leftEstLogProp->isCacheable()) &&
     (rightEstLogProp->isCacheable()) &&
     (inLP->isCacheable()) )
  {
    CCMPASSERT(leftNodeSet != NULL);
    CCMPASSERT(rightNodeSet != NULL);
    CCMPASSERT(inputNodeSet != NULL);
    if (leftNodeSet && rightNodeSet && inputNodeSet)
    {
      cacheable = TRUE;
    }
  }

  if (cacheable)
  {
    // check the ASM cache to see if outputEstLogProp for these
    // NodeSets appear for the given inputEstLogProp

    CANodeIdSet combineNodeSet = *leftNodeSet;
    combineNodeSet.insert(*rightNodeSet);

    CANodeIdSet combinedWithInputNodeSet = combineNodeSet;
    combinedWithInputNodeSet.insert(*inputNodeSet);

    outputEstLogProp = getCachedStatistics(&combinedWithInputNodeSet);
    if (outputEstLogProp != NULL)
      return outputEstLogProp;
  }

  JBBSubset * newJBBSubset = NULL;

  ValueIdSet setOfPredicates;

  if  (leftNodeSet && rightNodeSet)
  {
    // join predicates can be obtained from EstLogProp, only
    // if these corresponded to complete set of predicates -
    // all local or complete join. Also, we need a
    // combinedJBBSubset to set in the fake join expression
    // that we will be creating.

    newJBBSubset = leftNodeSet->computeJBBSubset();
    JBBSubset rightJBBSubset = *(rightNodeSet->computeJBBSubset());
    setOfPredicates = newJBBSubset->joinPredsWithOther(rightJBBSubset);

    // Since the properties from this group are cacheable, hence the
    // group attributes for the new join expression should contain
    // the combined JBBsubset of the left and the right children

    newJBBSubset->addSubset(rightJBBSubset);
  }

  // inputEstLogProp would be either empty input estLogProp or from the
  // outer child. If cacheable is TRUE, then newJBBsubset should
  // contain the combined left and the right JBB subset. But if
  // cacheable is FALSE, newJBBsubset should be NULL

  Join * joinExpr = formJoinExprWithEstLogProps(
				      leftEstLogProp,
				      rightEstLogProp,
				      inLP,
				      &setOfPredicates,
				      cacheable,
				      newJBBSubset);

  // Now do the actual synthesis and cache statistics in the cache

  outputEstLogProp = joinExpr->getGroupAttr()->outputLogProp(inLP);
  return outputEstLogProp;
}