// ------------------------------------------------------------------------ // Create a left linear subtree of joins from this MultiJoin // ------------------------------------------------------------------------ Join* MultiJoin::leftLinearize(NABoolean fixJoinOrder, NABoolean createPriviledgedJoins) const { // At this point assert that the subsets has no group by member. // If we want to allow GBs in this method in the future we should // remember to use computeJBBSubset() instead of the faster // jbbcsToJBBSubset() used currently in this method.. CMPASSERT ( (jbbSubset_.getGB() == NULL_CA_ID)); const CANodeIdSet & jbbcs = jbbSubset_.getJBBCs(); // pick some child to make right child of join CANodeId jbbcRight(jbbcs.getFirst()); CANodeIdSet right(jbbcRight); CANodeIdSet left(jbbcs); left -= jbbcRight; NABoolean nonExpander = (left.jbbcsToJBBSubset()-> isGuaranteedNonExpandingJoin((*jbbcRight.getNodeAnalysis()->getJBBC()))); Join* result = splitSubset(*(left.jbbcsToJBBSubset()), *(right.jbbcsToJBBSubset())); if (fixJoinOrder) { // disallow left shift rule on the join result->contextInsensRules() += JoinLeftShiftRuleNumber; // disallow join commutativity on the join result->contextInsensRules() += JoinCommutativityRuleNumber; } if (createPriviledgedJoins) { result->setJoinFromPTRule(); if ( CmpCommon::getDefault(COMP_BOOL_120) == DF_OFF) { result->updatePotential(-2); } } // If left subset is a multiJoin, then linearize it too. if (left.entries() > 1) { // left child must be multiJoin at this point. May be add assert. MultiJoin* leftChild = (MultiJoin*)(result->child(0).getPtr()); result->child(0) = leftChild->leftLinearize(fixJoinOrder,createPriviledgedJoins); } return result; } // MultiJoin::leftLinearize()
void MultiJoin::synthLogPropWithMJReuse(NormWA * normWAPtr) { // Check to see whether this GA has already been associated // with a logExpr for synthesis. If so, no need to resynthesize // for this equivalent log. expression. if (getGroupAttr()->existsLogExprForSynthesis()) { Join * joinExprForSynth = (Join *) getGroupAttr()->getLogExprForSynthesis(); if(joinExprForSynth->isJoinFromMJSynthLogProp()) return; } NABoolean reUseMJ = TRUE; CMPASSERT ( (jbbSubset_.getGB() == NULL_CA_ID)); const CANodeIdSet & jbbcs = jbbSubset_.getJBBCs(); // Instead of always picking the first JBBC as the right child // pick the one with minimum JBBC connections. This will avoid // all unnecessary crossproducts CANodeId jbbcRight; jbbcRight = jbbcs.getJBBCwithMinConnectionsToThisJBBSubset(); CANodeIdSet right(jbbcRight); CANodeIdSet left(jbbcs); left -= jbbcRight; Join* join = splitSubset(*(left.jbbcsToJBBSubset()), *(right.jbbcsToJBBSubset()), reUseMJ); //if the left is a MultiJoin, synthesize it using reUse //this has to be done before join->synthLogProp to avoid //calling MultiJoin::synthLogProp on the left MultiJoin //because that does not reUse if(left.entries() > 1) { RelExpr * leftRelExpr = join->child(0)->castToRelExpr(); if(leftRelExpr && leftRelExpr->getOperator() == REL_MULTI_JOIN) ((MultiJoin *) leftRelExpr)->synthLogPropWithMJReuse(normWAPtr); } join->synthLogProp(normWAPtr); join->setJoinFromMJSynthLogProp(); getGroupAttr()->setLogExprForSynthesis(join); jbbSubset_.setSubsetMJ(this); CMPASSERT ( getGroupAttr()->getNumJoinedTables() >= getArity()); }
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()
Join* MultiJoin::splitSubset(const JBBSubset & leftSet, const JBBSubset & rightSet, NABoolean reUseMJ) const { // At this point assert that none of the subsets has a group by member CMPASSERT ( (jbbSubset_.getGB() == NULL_CA_ID) && (leftSet.getGB() == NULL_CA_ID) && (rightSet.getGB() == NULL_CA_ID) ); #ifndef NDEBUG // assert that left + right == subSet_ // and left intersect right = phi CANodeIdSet unionSet(leftSet.getJBBCs()); CANodeIdSet intersectSet(leftSet.getJBBCs()); unionSet += rightSet.getJBBCs(); intersectSet.intersectSet(rightSet.getJBBCs()); CMPASSERT ( (unionSet == jbbSubset_.getJBBCs()) && (intersectSet.entries() == 0 )); #endif // Note: Joins including left, semi, anti semi are only created when // a single jbbc connected via one of them is split as a single right // child. InnerNonSemi joins can be created for any split i.e. any // number of jbbcs on the left and the right of the join, but special // joins (i.e. left, semi and anti semi joins) are only created when // there is a single right child i.e. the rightSet contains only one // jbbc that is connected via a special join. This is enforced as follows // // * The leftSet should be legal: This means that for every jbbc in the // leftSet any predecessor jbbcs should be present in the leftSet. // * The rightSet is either a single jbbc or if the rightSet has more // than one jbbc then it should be legal, note that a jbbc connected // via a special join is not a legal set by itself but we allow // creation of special joins assuming the predecessors are present // in the leftSet. // // An implicit assumption here is that 'this' MultiJoin is legal, which // is fair since apart from the top level multijoin, rest of the multijoins // are produced by splitting the top level multijoin. This method should // not produce illegal multijoins, since we check both leftSet and rightSet // for legality. Only time we don't check for legality is when the rightChild // is a single jbbc, and a single jbbc does not result in a multijoin. if(!leftSet.legal()) return NULL; if((rightSet.getJBBCs().entries() > 1) && (!rightSet.legal())) return NULL; // everything here goes to statement heap CollHeap* outHeap = CmpCommon::statementHeap(); RelExpr* child0 = generateSubsetExpr(leftSet, reUseMJ); RelExpr* child1 = generateSubsetExpr(rightSet, reUseMJ); // Flag to remember to pass on the derivedFromRoutineJoin flag if needed. NABoolean derivedFromRoutineJoin(FALSE); // now form a JoinExpr with these left and right children. Join * result = NULL; // if the rightSet is a single jbbc, then it could be connected via // a special join. In such a case we have to create the appropriate // join operator if(rightSet.getJBBCs().entries() == 1){ JBBC * rightChild = rightSet.getJBBCs().getFirst().getNodeAnalysis() ->getJBBC(); Join * rightChildParentJoin = rightChild->getOriginalParentJoin(); // If rightChildParentJoin is NULL, then the child is the left // child of the left most join and is considered to be connected // via a InnerNonSemi join. if(rightChildParentJoin) { if(rightChildParentJoin->derivedFromRoutineJoin()) derivedFromRoutineJoin = TRUE; if(rightChildParentJoin->isSemiJoin()) result = new (outHeap) Join(child0, child1, REL_SEMIJOIN, NULL); if(rightChildParentJoin->isAntiSemiJoin()) result = new (outHeap) Join(child0, child1, REL_ANTI_SEMIJOIN, NULL); if(rightChildParentJoin->isLeftJoin()) { // left joins can have filter preds, i.e. predicates that // are applied as filters after applying the join predicate. // We need to set them here. result = new (outHeap) Join(child0, child1, REL_LEFT_JOIN, NULL); result->setSelectionPredicates(rightChild->getLeftJoinFilterPreds()); } if(rightChildParentJoin->isRoutineJoin()) { derivedFromRoutineJoin = TRUE; result = new (outHeap) Join(child0, child1, REL_ROUTINE_JOIN, NULL); ValueIdSet routineJoinFilterPreds = rightChild->getRoutineJoinFilterPreds(); ValueIdSet predsToAddToRoutineJoin; // add covered filter preds for (ValueId filterPred= routineJoinFilterPreds.init(); routineJoinFilterPreds.next(filterPred); routineJoinFilterPreds.advance(filterPred) ) { if(jbbSubset_.coversExpr(filterPred)) predsToAddToRoutineJoin += filterPred; } result->setSelectionPredicates(predsToAddToRoutineJoin); } if(result) { // set the join predicate for special joins, note predicates // for regular InnerNonSemi joins are set as selection predicates // in the join relexpr. result->setJoinPred(rightChild->getPredsWithPredecessors()); result->nullInstantiatedOutput().insert(rightChild-> nullInstantiatedOutput()); } } } // The join to be created is a regular InnerNonSemi join if (!result) result = new (outHeap) Join(child0, child1, REL_JOIN, NULL); // Make sure we carry the derivedFromRoutineJoin flag with us if (derivedFromRoutineJoin) result->setDerivedFromRoutineJoin(); // Share my groupAttr with result result->setGroupAttr(getGroupAttr()); // get inner join predicates ValueIdSet selPreds = rightSet.joinPredsWithOther(leftSet); // get left join filter preds if any selPreds += result->getSelectionPredicates(); result->setSelectionPredicates(selPreds); result->findEquiJoinPredicates(); // May be I could save a little if i pushdown only to the child(ren) // that are not already JBBCs, i.e. multijoins result->pushdownCoveredExpr (result->getGroupAttr()->getCharacteristicOutputs(), result->getGroupAttr()->getCharacteristicInputs(), result->selectionPred()); // We used CutOp as children, to avoid pushing predicates to JBBCs. // Now put the actual expression back in case the child is a JBBCs if(leftSet.getJBBCs().entries() == 1) result->setChild(0, getJBBCRelExpr(leftSet.getJBBCs().getFirst())); // We used CutOp as children, to avoid pushing predicates to JBBCs. // Now put the actual expression back in case the child is a JBBCs if(rightSet.getJBBCs().entries() == 1) result->setChild(1, getJBBCRelExpr(rightSet.getJBBCs().getFirst())); // Temp fixup. We need to take the selectionPred out of MultiJoin // for now to prevent that pushed expr from being there. selectionPred // is not being used now in MultiJoin xxx. if (leftSet.getJBBCs().entries() > 1) result->child(0)->selectionPred().clear(); if (rightSet.getJBBCs().entries() > 1) result->child(1)->selectionPred().clear(); return result; }