Join * MultiJoin::getPreferredJoin() { if(!getGroupAttr()->existsLogExprForSynthesis()) synthLogProp(); return (Join*) getGroupAttr()->getLogExprForSynthesis(); }
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()); }
// -------------------------------------------------------------------- // Create a MultiJoin that is joining a subset of this MultiJoin children. // Note: We assume JBBCs are already primed before calling this method. // THis is a fine assumption at the analysis and post analysis stage. // -------------------------------------------------------------------- MultiJoin* MultiJoin::createSubsetMultiJoin(const JBBSubset & subset, NABoolean reUseMJ) const { MultiJoin * result = NULL; if(reUseMJ) result = subset.getSubsetMJ(); if(result) return result; CMPASSERT (subset.getGB() == NULL_CA_ID); // no GBs for now CMPASSERT (subset.getJBBCs().entries() > 1); // everything here goes to statement heap CollHeap* outHeap = CmpCommon::statementHeap(); result = new (outHeap) MultiJoin(subset, outHeap); result->setChildren(childrenMap_); result->setGroupAttr(new (outHeap) GroupAttributes()); // Assign my Characteristic Inputs to the newly created subset multi-join. result->getGroupAttr()->addCharacteristicInputs (getGroupAttr()->getCharacteristicInputs()); // The following call primes the result Characteristic Outputs. // It ensures that the inputs are minimal and outputs are maximal. result->primeGroupAttributes(); // Now compute the GroupAnalysis fields result->primeGroupAnalysis(); return result; }
void MultiJoin::synthLogProp(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()) return; 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())); join->synthLogProp(normWAPtr); getGroupAttr()->setLogExprForSynthesis(join); join->setJoinFromMJSynthLogProp(); jbbSubset_.setSubsetMJ(this); CASortedList * synthLogPropPath = new (CmpCommon::statementHeap()) CASortedList(CmpCommon::statementHeap(), jbbcs.entries()); synthLogPropPath->insert((*(left.jbbcsToJBBSubset()->getSynthLogPropPath()))); synthLogPropPath->insert(right.getFirst()); jbbSubset_.setSynthLogPropPath(synthLogPropPath); CMPASSERT ( getGroupAttr()->getNumJoinedTables() >= getArity()); }
CostScalar Scan::computeBaseSelectivity() const { CostScalar scanCardWithoutHint = getGroupAttr()->outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->\ getColStats().getScanRowCountWithoutHint(); double cardAfterLocalPreds = scanCardWithoutHint.getValue(); double baseRowCount = getTableDesc()->tableColStats()[0]->getColStats()->getRowcount().getValue() ; // both the minimum and the base row count have to be minimum 1. // This is ensured in the called routines. So no need to check here. return cardAfterLocalPreds/baseRowCount; }
// ----------------------------------------------------------------------- // MultJoin::recomputeOuterReferences() // ----------------------------------------------------------------------- void MultiJoin::recomputeOuterReferences() { // --------------------------------------------------------------------- // Delete all those input values that are no longer referenced on // this operator. // --------------------------------------------------------------------- if (NOT getGroupAttr()->getCharacteristicInputs().isEmpty()) { ValueIdSet outerRefs = getGroupAttr()->getCharacteristicInputs(); // Weed out those expressions not needed by my selectionPred and joinPred // xxx instead of taking this from getLocalJoinPreds, should I take it // from MultiJoin selectionPred??? refer to getLocalJoinPreds definition // and consider preds that referencing inputs!!! ValueIdSet exprSet = jbbSubset_.getLocalJoinPreds(); // from JbbSubsetAnalysis // Need to include LocalDependentPreds later when supported. Ok now for inner MultiJoins exprSet.weedOutUnreferenced(outerRefs); // Add back those expressiones needed by my children Int32 arity = getArity(); // outputs produced by JBBCs in this MultiJoin ValueIdSet jbbcOutputs; for (Int32 i = 0; i < arity; i++) { outerRefs += child(i)->getGroupAttr()->getCharacteristicInputs(); jbbcOutputs += child(i)->getGroupAttr()->getCharacteristicOutputs(); // these inputs are provided by jbbcs in this MultiJoin } // account for TSJs i.e. values flowing from // one jbbc to another within this MultiJoin outerRefs -= jbbcOutputs; getGroupAttr()->setCharacteristicInputs(outerRefs); } return; } // MultiJoin::recomputeOuterReferences()
// LCOV_EXCL_START // Used for other RelExpr but not MultiJoin void MultiJoin::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp) { CMPASSERT(inputEstLogProp->getNodeSet()); Join * preferredJoin = jbbSubset_.getPreferredJoin(); CMPASSERT(preferredJoin->isJoinFromMJSynthLogProp()); EstLogPropSharedPtr myEstLogProp = preferredJoin->getGroupAttr()->outputLogProp(inputEstLogProp); getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp, NULL); } // MultiJoin::synthEstLogProp
// change literals of a cacheable query into ConstantParameters RelExpr* RelExpr::normalizeForCache(CacheWA& cwa, BindWA& bindWA) { if (nodeIsNormalizedForCache()) { return this; } // replace descendants' literals into ConstantParameters normalizeKidsForCache(cwa, bindWA); if (cwa.getPhase() >= CmpMain::BIND) { if (selection_) { selection_ = selection_->normalizeForCache(cwa, bindWA); } else { selectionPred().normalizeForCache(cwa, bindWA); } // RelExpr::bindSelf has done this line during binding; but, we // must redo it to recognize any new constantparameters created // by the above normalizeForCache call(s) as RelExpr inputs. getGroupAttr()->addCharacteristicInputs (bindWA.getCurrentScope()->getOuterRefs()); } markAsNormalizedForCache(); return this; }
short ProbeCache::codeGen(Generator *generator) { ExpGenerator * exp_gen = generator->getExpGenerator(); Space * space = generator->getSpace(); MapTable * last_map_table = generator->getLastMapTable(); ex_cri_desc * given_desc = generator->getCriDesc(Generator::DOWN); ex_cri_desc * returned_desc = new(space) ex_cri_desc(given_desc->noTuples() + 1, space); // cri descriptor for work atp has 5 entries: // entry #0 for const // entry #1 for temp // entry #2 for hash value of probe input data in Probe Cache Manager // entry #3 for encoded probe input data in Probe Cache Manager // enrry #4 for inner table row data in this operator's cache buffer Int32 work_atp = 1; ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(5, space); unsigned short hashValIdx = 2; unsigned short encodedProbeDataIdx = 3; unsigned short innerRowDataIdx = 4; // generate code for child tree, and get its tdb and explain tuple. child(0)->codeGen(generator); ComTdb * child_tdb = (ComTdb *)(generator->getGenObj()); ExplainTuple *childExplainTuple = generator->getExplainTuple(); ////////////////////////////////////////////////////// // Generate up to 4 runtime expressions. ////////////////////////////////////////////////////// // Will use child's char. inputs (+ execution count) for the next // two runtime expressions. ValueIdList inputsToUse = child(0).getGroupAttr()->getCharacteristicInputs(); inputsToUse.insert(generator->getOrAddStatementExecutionCount()); // Expression #1 gets the hash value of the probe input data ValueIdList hvAsList; // Executor has hard-coded assumption that the result is long, // so add a Cast node to convert result to a long. ItemExpr *probeHashAsIe = new (generator->wHeap()) HashDistPartHash(inputsToUse.rebuildExprTree(ITM_ITEM_LIST)); probeHashAsIe->bindNode(generator->getBindWA()); NumericType &nTyp = (NumericType &)probeHashAsIe->getValueId().getType(); GenAssert(nTyp.isSigned() == FALSE, "Unexpected signed HashDistPartHash."); GenAssert(probeHashAsIe->getValueId().getType().supportsSQLnullLogical() == FALSE, "Unexpected nullable HashDistPartHash."); ItemExpr *hvAsIe = new (generator->wHeap()) Cast( probeHashAsIe, new (generator->wHeap()) SQLInt(FALSE, // false == unsigned. FALSE // false == not nullable. )); hvAsIe->bindNode(generator->getBindWA()); hvAsList.insert(hvAsIe->getValueId()); ex_expr *hvExpr = NULL; ULng32 hvLength; exp_gen->generateContiguousMoveExpr( hvAsList, 0, // don't add convert node work_atp, hashValIdx, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, hvLength, &hvExpr); GenAssert(hvLength == sizeof(Lng32), "Unexpected length of result of hash function."); // Expression #2 encodes the probe input data for storage in // the ProbeCacheManager. ValueIdList encodeInputAsList; CollIndex inputListIndex; for (inputListIndex = 0; inputListIndex < inputsToUse.entries(); inputListIndex++) { ItemExpr *inputIe = (inputsToUse[inputListIndex].getValueDesc())->getItemExpr(); if (inputIe->getValueId().getType().getVarLenHdrSize() > 0) { // This logic copied from Sort::codeGen(). // Explode varchars by moving them to a fixed field // whose length is equal to the max length of varchar. // 5/8/98: add support for VARNCHAR const CharType& char_type = (CharType&)(inputIe->getValueId().getType()); if (!CollationInfo::isSystemCollation(char_type.getCollation())) { inputIe = new(generator->wHeap()) Cast (inputIe, (new(generator->wHeap()) SQLChar( CharLenInfo(char_type.getStrCharLimit(), char_type.getDataStorageSize()), char_type.supportsSQLnull(), FALSE, FALSE, FALSE, char_type.getCharSet(), char_type.getCollation(), char_type.getCoercibility() ) ) ); } } CompEncode * enode = new(generator->wHeap()) CompEncode(inputIe, FALSE /* ascend/descend doesn't matter*/); enode->bindNode(generator->getBindWA()); encodeInputAsList.insert(enode->getValueId()); } ex_expr *encodeInputExpr = NULL; ULng32 encodedInputLength; exp_gen->generateContiguousMoveExpr(encodeInputAsList, 0, //don't add conv nodes work_atp, encodedProbeDataIdx, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, encodedInputLength, &encodeInputExpr); // Expression #3 moves the inner table data into a buffer pool. // This is also the tuple returned to ProbeCache's parent. ex_expr * innerRecExpr = NULL; ValueIdList innerTableAsList = getGroupAttr()->getCharacteristicOutputs(); ////////////////////////////////////////////////////// // Describe the returned row and add the returned // values to the map table. ////////////////////////////////////////////////////// // determine internal format NABoolean useCif = FALSE; ExpTupleDesc::TupleDataFormat tupleFormat = generator->getInternalFormat(); //tupleFormat = determineInternalFormat( innerTableAsList, this, useCif,generator); ULng32 innerRecLength = 0; ExpTupleDesc * innerRecTupleDesc = 0; MapTable * returnedMapTable = NULL; exp_gen->generateContiguousMoveExpr(innerTableAsList, -1, // do add conv nodes work_atp, innerRowDataIdx, tupleFormat, innerRecLength, &innerRecExpr, &innerRecTupleDesc, ExpTupleDesc::SHORT_FORMAT, &returnedMapTable); returned_desc->setTupleDescriptor(returned_desc->noTuples() - 1, innerRecTupleDesc); // remove all appended map tables and return the returnedMapTable generator->removeAll(last_map_table); generator->appendAtEnd(returnedMapTable); // This returnedMapTable will contain the value ids that are being returned // (the inner table probed). // Massage the atp and atp_index of the innerTableAsList. for (CollIndex i = 0; i < innerTableAsList.entries(); i++) { ValueId innerValId = innerTableAsList[i]; Attributes *attrib = generator->getMapInfo(innerValId)->getAttr(); // All reference to the returned values from this point on // will be at atp = 0, atp_index = last entry in returned desc. attrib->setAtp(0); attrib->setAtpIndex(returned_desc->noTuples() - 1); } // Expression #4 is a selection predicate, to be applied // before returning rows to the parent ex_expr * selectPred = NULL; if (!selectionPred().isEmpty()) { ItemExpr * selPredTree = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); exp_gen->generateExpr(selPredTree->getValueId(), ex_expr::exp_SCAN_PRED, &selectPred); } ////////////////////////////////////////////////////// // Prepare params for ComTdbProbeCache. ////////////////////////////////////////////////////// queue_index pDownSize = (queue_index)getDefault(GEN_PROBE_CACHE_SIZE_DOWN); queue_index pUpSize = (queue_index)getDefault(GEN_PROBE_CACHE_SIZE_UP); // Make sure that the ProbeCache queues can support the childs queues. if(pDownSize < child_tdb->getInitialQueueSizeDown()) { pDownSize = child_tdb->getInitialQueueSizeDown(); pDownSize = MINOF(pDownSize, 32768); } if(pUpSize < child_tdb->getInitialQueueSizeUp()) { pUpSize = child_tdb->getInitialQueueSizeUp(); pUpSize = MINOF(pUpSize, 32768); } ULng32 pcNumEntries = numCachedProbes_; // Number of entries in the probe cache cannot be less than // max parent down queue size. Before testing and adjusting the // max queue size, it is necessary to make sure it is a power of // two, rounding up if necessary. This is to match the logic in // ex_queue::resize. queue_index pdq2 = 1; queue_index bits = pDownSize; while (bits && pdq2 < pDownSize) { bits = bits >> 1; pdq2 = pdq2 << 1; } if (pcNumEntries < pdq2) pcNumEntries = pdq2; numInnerTuples_ = getDefault(GEN_PROBE_CACHE_NUM_INNER); if (innerRecExpr == NULL) { // For semi-join and anti-semi-join, executor need not allocate // a buffer. Set the tdb's buffer size to 0 to be consistent. numInnerTuples_ = 0; } else if (numInnerTuples_ == 0) { // Handle special value, 0, which tells code gen to // decided on buffer size: i.e., large enough to accomodate // all parent up queue entries and all probe cache entries // having a different inner table row. // As we did for the down queue, make sure the up queue size // specified is a power of two. queue_index puq2 = 1; queue_index bits = pUpSize; while (bits && puq2 < pUpSize) { bits = bits >> 1; puq2 = puq2 << 1; } numInnerTuples_ = puq2 + pcNumEntries; }
short RelInternalSP::codeGen(Generator * generator) { Space * space = generator->getSpace(); ExpGenerator * exp_gen = generator->getExpGenerator(); MapTable * last_map_table = generator->getLastMapTable(); ex_expr * input_expr = NULL; ex_expr * output_expr = NULL; //////////////////////////////////////////////////////////////////////////// // // Returned atp layout: // // |--------------------------------| // | input data | stored proc row | // | ( I tupps ) | ( 1 tupp ) | // |--------------------------------| // <-- returned row to parent ----> // // input data: the atp input to this node by its parent. // stored proc row: tupp where the row read from SP is moved. // //////////////////////////////////////////////////////////////////////////// ex_cri_desc * given_desc = generator->getCriDesc(Generator::DOWN); ex_cri_desc * returned_desc = new(space) ex_cri_desc(given_desc->noTuples() + 1, space); // cri descriptor for work atp has 3 entries: // -- the first two entries for consts and temps. // -- Entry 3(index #2) is where the input and output rows will be created. ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(3, space); const Int32 work_atp = 1; const Int32 work_atp_index = 2; ExpTupleDesc * input_tuple_desc = NULL; ExpTupleDesc * output_tuple_desc = NULL; // Generate expression to create the input row that will be // given to the stored proc. // The input value is in sp->getProcAllParams() // and has to be converted to sp->procType(). // Generate Cast node to convert procParam to ProcType. // If procType is a varchar, explode it. This is done // so that values could be extracted correctly. ValueIdList procVIDList; for (CollIndex i = 0; i < procTypes().entries(); i++) { Cast * cn; if ((procTypes())[i].getType().getVarLenHdrSize() > 0) { // 5/9/98: add support for VARNCHAR const CharType& char_type = (CharType&)((procTypes())[i].getType()); // Explode varchars by moving them to a fixed field // whose length is equal to the max length of varchar. cn = new(generator->wHeap()) Cast ((getProcAllParamsVids())[i].getItemExpr(), (new(generator->wHeap()) SQLChar(generator->wHeap(), CharLenInfo(char_type.getStrCharLimit(), char_type.getDataStorageSize()), char_type.supportsSQLnull(), FALSE, FALSE, FALSE, char_type.getCharSet(), char_type.getCollation(), char_type.getCoercibility() /* (procTypes())[i].getType().getNominalSize(), (procTypes())[i].getType().supportsSQLnull() */ ) ) ); // Move the exploded field to a varchar field since // procType is varchar. // Can optimize by adding an option to convert node to // blankpad. TBD. // cn = new(generator->wHeap()) Cast(cn, &((procTypes())[i].getType())); } else cn = new(generator->wHeap()) Cast((getProcAllParamsVids())[i].getItemExpr(), &((procTypes())[i].getType())); cn->bindNode(generator->getBindWA()); procVIDList.insert(cn->getValueId()); } ULng32 inputRowlen_ = 0; exp_gen->generateContiguousMoveExpr(procVIDList, -1, /*add conv nodes*/ work_atp, work_atp_index, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, inputRowlen_, &input_expr, &input_tuple_desc, ExpTupleDesc::LONG_FORMAT); // add all columns from this SP to the map table. ULng32 tupleLength; exp_gen->processValIdList(getTableDesc()->getColumnList(), ExpTupleDesc::SQLARK_EXPLODED_FORMAT, tupleLength, work_atp, work_atp_index); // Generate expression to move the output row returned by the // stored proc back to parent. ULng32 outputRowlen_ = 0; MapTable * returnedMapTable = 0; exp_gen->generateContiguousMoveExpr(getTableDesc()->getColumnList(), -1 /*add conv nodes*/, 0, returned_desc->noTuples() - 1, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, outputRowlen_, &output_expr, &output_tuple_desc, ExpTupleDesc::LONG_FORMAT, &returnedMapTable); // Now generate expressions used to extract or move input or // output values. See class ExSPInputOutput. ExSPInputOutput * extractInputExpr = NULL; ExSPInputOutput * moveOutputExpr = NULL; generateSPIOExpr(this, generator, extractInputExpr, moveOutputExpr); // done with expressions at this operator. Remove the appended map tables. generator->removeAll(last_map_table); // append the map table containing the returned columns generator->appendAtEnd(returnedMapTable); NAString procNameAsNAString(procName_); char * sp_name = space->allocateAndCopyToAlignedSpace(procNameAsNAString, procNameAsNAString.length(), 0); ExpGenerator *expGen = generator->getExpGenerator(); // expression to conditionally return 0 or more rows. ex_expr *predExpr = NULL; // generate tuple selection expression, if present if(NOT selectionPred().isEmpty()) { ItemExpr* pred = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); expGen->generateExpr(pred->getValueId(),ex_expr::exp_SCAN_PRED,&predExpr); } ComTdbStoredProc * sp_tdb = new(space) ComTdbStoredProc(sp_name, input_expr, inputRowlen_, output_expr, outputRowlen_, work_cri_desc, work_atp_index, given_desc, returned_desc, extractInputExpr, moveOutputExpr, 2, 1024, (Cardinality) getGroupAttr()-> getOutputLogPropList()[0]-> getResultCardinality().value(), 5, 64000, //10240 predExpr, (UInt16) arkcmpInfo_); generator->initTdbFields(sp_tdb); if(!generator->explainDisabled()) { generator->setExplainTuple( addExplainInfo(sp_tdb, 0, 0, generator)); } // Do not infer that any transaction started can // be in READ ONLY mode if ISPs are present. generator->setNeedsReadWriteTransaction(TRUE); generator->setCriDesc(given_desc, Generator::DOWN); generator->setCriDesc(returned_desc, Generator::UP); generator->setGenObj(this, sp_tdb); // Some built-in functions require a TMF transaction // because they get their information from catman generator->setTransactionFlag(getRequiresTMFTransaction()); return 0; }
short Join::generateShape(CollHeap * c, char * buf, NAString * shapeStr) { Space * space = (Space *)c; char mybuf[100]; switch (getOperatorType()) { case REL_NESTED_JOIN: case REL_LEFT_NESTED_JOIN: case REL_NESTED_SEMIJOIN: case REL_NESTED_ANTI_SEMIJOIN: case REL_NESTED_JOIN_FLOW: sprintf(mybuf, "nested_join("); break; case REL_MERGE_JOIN: case REL_LEFT_MERGE_JOIN: case REL_MERGE_SEMIJOIN: case REL_MERGE_ANTI_SEMIJOIN: sprintf(mybuf, "merge_join("); break; case REL_HASH_SEMIJOIN: case REL_HASH_ANTI_SEMIJOIN: sprintf(mybuf, "hash_join("); break; case REL_LEFT_HYBRID_HASH_JOIN: case REL_HYBRID_HASH_SEMIJOIN: case REL_HYBRID_HASH_ANTI_SEMIJOIN: case REL_FULL_HYBRID_HASH_JOIN: sprintf(mybuf, "hybrid_hash_join("); break; case REL_LEFT_ORDERED_HASH_JOIN: case REL_ORDERED_HASH_JOIN: case REL_ORDERED_HASH_SEMIJOIN: case REL_ORDERED_HASH_ANTI_SEMIJOIN: sprintf(mybuf, "ordered_hash_join("); break; case REL_HYBRID_HASH_JOIN: if (((HashJoin *)this)->isOrderedCrossProduct()) sprintf(mybuf, "ordered_cross_product("); else sprintf(mybuf, "hybrid_hash_join("); break; default: sprintf(mybuf, "add_to_Join::generateShape("); } outputBuffer(space, buf, mybuf, shapeStr); child(0)->generateShape(space, buf, shapeStr); sprintf(mybuf, ","); outputBuffer(space, buf, mybuf, shapeStr); child(1)->generateShape(space, buf, shapeStr); // is it IndexJoin? if both children are scans and number of base tables is // 1, it is index join if (getGroupAttr()->getNumBaseTables() == 1) { NABoolean child0isScan = FALSE; NABoolean child1isScan = FALSE; RelExpr *lChild = child(0)->castToRelExpr(); while (lChild->getArity() == 1) { lChild=lChild->child(0)->castToRelExpr(); } switch( lChild->castToRelExpr()->getOperatorType()) { case REL_SCAN: case REL_FILE_SCAN: case REL_HBASE_ACCESS: case REL_HDFS_SCAN: child0isScan = TRUE; } RelExpr *rChild = child(1)->castToRelExpr(); while (rChild->getArity() == 1) { rChild = rChild->child(0)->castToRelExpr(); } switch (rChild->castToRelExpr()->getOperatorType()) { case REL_SCAN: case REL_FILE_SCAN: case REL_HBASE_ACCESS: case REL_HDFS_SCAN: child1isScan = TRUE; } if (child0isScan && child1isScan) { sprintf(mybuf, ",INDEXJOIN)"); } else { sprintf(mybuf, ")"); } } else { sprintf(mybuf, ")"); } outputBuffer(space, buf, mybuf, shapeStr); return 0; }
// PhysUnPackRows::preCodeGen() ------------------------------------------- // Perform local query rewrites such as for the creation and // population of intermediate tables, for accessing partitioned // data. Rewrite the value expressions after minimizing the dataflow // using the transitive closure of equality predicates. // // PhysUnPackRows::preCodeGen() - is basically the same as the RelExpr:: // preCodeGen() except that here we replace the VEG references in the // unPackExpr() and packingFactor(), as well as the selectionPred(). // // Parameters: // // Generator *generator // IN/OUT : A pointer to the generator object which contains the state, // and tools (e.g. expression generator) to generate code for // this node. // // ValueIdSet &externalInputs // IN : The set of external Inputs available to this node. // // RelExpr * PhysUnPackRows::preCodeGen(Generator * generator, const ValueIdSet & externalInputs, ValueIdSet &pulledNewInputs) { if (nodeIsPreCodeGenned()) return this; // Resolve the VEGReferences and VEGPredicates, if any, that appear // in the Characteristic Inputs, in terms of the externalInputs. // getGroupAttr()->resolveCharacteristicInputs(externalInputs); // My Characteristic Inputs become the external inputs for my children. // ValueIdSet childPulledInputs; child(0) = child(0)->preCodeGen(generator, externalInputs, pulledNewInputs); if (! child(0).getPtr()) return NULL; // process additional input value ids the child wants getGroupAttr()->addCharacteristicInputs(childPulledInputs); pulledNewInputs += childPulledInputs; // The VEG expressions in the selection predicates and the characteristic // outputs can reference any expression that is either a potential output // or a characteristic input for this RelExpr. Supply these values for // rewriting the VEG expressions. // ValueIdSet availableValues; getInputValuesFromParentAndChildren(availableValues); // The unPackExpr() and packingFactor() expressions have access // to only the Input Values. These can come from the parent or be // the outputs of the child. // unPackExpr(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); packingFactor(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); if (rowwiseRowset()) { if (rwrsInputSizeExpr()) ((ItemExpr*)rwrsInputSizeExpr())-> replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); if (rwrsMaxInputRowlenExpr()) ((ItemExpr*)rwrsMaxInputRowlenExpr())-> replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); if (rwrsBufferAddrExpr()) ((ItemExpr*)rwrsBufferAddrExpr())-> replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); } // The selectionPred has access to only the output values generated by // UnPackRows and input values from the parent. // getInputAndPotentialOutputValues(availableValues); // Rewrite the selection predicates. // NABoolean replicatePredicates = TRUE; selectionPred().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs(), FALSE, // no key predicates here 0 /* no need for idempotence here */, replicatePredicates ); // Replace VEG references in the outputs and remove redundant // outputs. // getGroupAttr()->resolveCharacteristicOutputs (availableValues, getGroupAttr()->getCharacteristicInputs()); generator->oltOptInfo()->setMultipleRowsReturned(TRUE); markAsPreCodeGenned(); return this; } // PhysUnPackRows::preCodeGen
short PhysUnPackRows::codeGen(Generator *generator) { // Get handles on expression generator, map table, and heap allocator // ExpGenerator *expGen = generator->getExpGenerator(); Space *space = generator->getSpace(); // Allocate a new map table for this operation // MapTable *localMapTable = generator->appendAtEnd(); // Generate the child and capture the task definition block and a description // of the reply composite row layout and the explain information. // child(0)->codeGen(generator); ComTdb *childTdb = (ComTdb*)(generator->getGenObj()); ex_cri_desc *childCriDesc = generator->getCriDesc(Generator::UP); ExplainTuple *childExplainTuple = generator->getExplainTuple(); // Make all of my child's outputs map to ATP 1. Since they are // not needed above, they will not be in the work ATP (0). // (Later, they will be removed from the map table) // localMapTable->setAllAtp(1); // Generate the given and returned composite row descriptors. // unPackRows adds a tupp (for the generated outputs) to the // row given by the parent. The workAtp will have the 2 more // tupps (1 for the generated outputs and another for the // indexValue) than the given. // ex_cri_desc *givenCriDesc = generator->getCriDesc(Generator::DOWN); ex_cri_desc *returnedCriDesc = #pragma nowarn(1506) // warning elimination new(space) ex_cri_desc(givenCriDesc->noTuples() + 1, space); #pragma warn(1506) // warning elimination ex_cri_desc *workCriDesc = #pragma nowarn(1506) // warning elimination new(space) ex_cri_desc(givenCriDesc->noTuples() + 2, space); #pragma warn(1506) // warning elimination // unPackCols is the next to the last Tp in Atp 0, the work ATP. // and the last Tp in the returned ATP. // const Int32 unPackColsAtpIndex = workCriDesc->noTuples() - 2; const Int32 unPackColsAtp = 0; // The length of the new tuple which will contain the columns // generated by unPackRows // ULng32 unPackColsTupleLen; // The Tuple Desc describing the tuple containing the new unPacked columns // It is generated when the expression is generated. // ExpTupleDesc *unPackColsTupleDesc = 0; // indexValue is the last Tp in Atp 0, the work ATP. // const Int32 indexValueAtpIndex = workCriDesc->noTuples() - 1; const Int32 indexValueAtp = 0; // The length of the work tuple which will contain the value // of the index. This should always be sizeof(int). // ULng32 indexValueTupleLen = 0; // The Tuple Desc describing the tuple containing the new unPacked columns // It is generated when the expression is generated. // ExpTupleDesc *indexValueTupleDesc = 0; ValueIdList indexValueList; if (indexValue() != NULL_VALUE_ID) { indexValueList.insert(indexValue()); expGen->processValIdList(indexValueList, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, indexValueTupleLen, indexValueAtp, indexValueAtpIndex, &indexValueTupleDesc, ExpTupleDesc::SHORT_FORMAT); GenAssert(indexValueTupleLen == sizeof(Int32), "UnPackRows::codeGen: Internal Error"); } // If a packingFactor exists, generate a move expression for this. // It is assumed that the packingFactor expression evaluates to a // 4 byte integer. // ex_expr *packingFactorExpr = 0; ULng32 packingFactorTupleLen; if(packingFactor().entries() > 0) { expGen->generateContiguousMoveExpr(packingFactor(), -1, unPackColsAtp, unPackColsAtpIndex, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, packingFactorTupleLen, &packingFactorExpr); GenAssert(packingFactorTupleLen == sizeof(Int32), "UnPackRows::codeGen: Internal Error"); } // Generate the UnPack expressions. // // characteristicOutputs() - refers to the list of expressions // to be move to another tuple. // // 0 - Do not add conv. nodes. // // unPackColsAtp - this expression will move data to the // unPackColsAtp (0) ATP. // // unPackColsAtpIndex - within the unPackColsAtp (0) ATP, the destination // for this move expression will be the unPackColsAtpIndex TP. This should // be the next to the last TP of the work ATP. (The indexValue will be in // the last position) // // SQLARK_EXPLODED_FORMAT - generate the move expression to construct // the destination tuple in EXPLODED FORMAT. // // unPackColsTupleLen - This is an output which will contain the length // of the destination Tuple. // // &unPackColsExpr - The address of the pointer to the expression // which will be generated. // // &unPackColsTupleDesc - The address of the tuple descriptor which is // generated. This describes the destination tuple of the move expression. // // SHORT_FORMAT - generate the unPackColsTupleDesc in the SHORT FORMAT. // ex_expr *unPackColsExpr = 0; expGen-> genGuardedContigMoveExpr(selectionPred(), getGroupAttr()->getCharacteristicOutputs(), 0, // No Convert Nodes added unPackColsAtp, unPackColsAtpIndex, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, unPackColsTupleLen, &unPackColsExpr, &unPackColsTupleDesc, ExpTupleDesc::SHORT_FORMAT); #pragma nowarn(1506) // warning elimination workCriDesc->setTupleDescriptor(unPackColsAtpIndex, #pragma warn(1506) // warning elimination unPackColsTupleDesc); #pragma nowarn(1506) // warning elimination returnedCriDesc->setTupleDescriptor(unPackColsAtpIndex, #pragma warn(1506) // warning elimination unPackColsTupleDesc); // expressions for rowwise rowset implementation. ex_expr * rwrsInputSizeExpr = 0; ex_expr * rwrsMaxInputRowlenExpr = 0; ex_expr * rwrsBufferAddrExpr = 0; ULng32 rwrsInputSizeExprLen = 0; ULng32 rwrsMaxInputRowlenExprLen = 0; ULng32 rwrsBufferAddrExprLen = 0; const Int32 rwrsAtp = 1; const Int32 rwrsAtpIndex = workCriDesc->noTuples() - 2; ExpTupleDesc * rwrsTupleDesc = 0; ValueIdList rwrsVidList; if (rowwiseRowset()) { rwrsVidList.insert(this->rwrsInputSizeExpr()->getValueId()); expGen->generateContiguousMoveExpr(rwrsVidList, 0 /*don't add conv nodes*/, rwrsAtp, rwrsAtpIndex, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, rwrsInputSizeExprLen, &rwrsInputSizeExpr, &rwrsTupleDesc,ExpTupleDesc::SHORT_FORMAT); rwrsVidList.clear(); rwrsVidList.insert(this->rwrsMaxInputRowlenExpr()->getValueId()); expGen->generateContiguousMoveExpr(rwrsVidList, 0 /*don't add conv nodes*/, rwrsAtp, rwrsAtpIndex, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, rwrsMaxInputRowlenExprLen, &rwrsMaxInputRowlenExpr, &rwrsTupleDesc,ExpTupleDesc::SHORT_FORMAT); rwrsVidList.clear(); rwrsVidList.insert(this->rwrsBufferAddrExpr()->getValueId()); expGen->generateContiguousMoveExpr(rwrsVidList, 0 /*don't add conv nodes*/, rwrsAtp, rwrsAtpIndex, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, rwrsBufferAddrExprLen, &rwrsBufferAddrExpr, &rwrsTupleDesc,ExpTupleDesc::SHORT_FORMAT); expGen->assignAtpAndAtpIndex(rwrsOutputVids(), unPackColsAtp, unPackColsAtpIndex); } // Move the generated maptable entries, to the localMapTable, // so that all other entries can later be removed. // for(ValueId outputValId = getGroupAttr()->getCharacteristicOutputs().init(); getGroupAttr()->getCharacteristicOutputs().next(outputValId); getGroupAttr()->getCharacteristicOutputs().advance(outputValId)) { generator->addMapInfoToThis(localMapTable, outputValId, generator->getMapInfo(outputValId)-> getAttr()); // Indicate that code was generated for this map table entry. // generator->getMapInfoFromThis(localMapTable, outputValId)->codeGenerated(); } NABoolean tolerateNonFatalError = FALSE; if (isRowsetIterator() && (generator->getTolerateNonFatalError())) { tolerateNonFatalError = TRUE; setTolerateNonFatalError(RelExpr::NOT_ATOMIC_); } // Allocate the UnPack TDB // ComTdbUnPackRows *unPackTdb = NULL; if (rowwiseRowset()) { unPackTdb = new (space) ComTdbUnPackRows(NULL, //childTdb, rwrsInputSizeExpr, rwrsMaxInputRowlenExpr, rwrsBufferAddrExpr, rwrsAtpIndex, givenCriDesc, returnedCriDesc, workCriDesc, 16, 1024, (Cardinality) getGroupAttr()-> getOutputLogPropList()[0]-> getResultCardinality().value(), 2, 20000); } else { // Base the initial queue size on the est. cardinality. // UnPackRows does not do dyn queue resize, so passed in // queue size values represent initial (and final) queue // sizes (not max queue sizes). // queue_index upQueueSize = (queue_index)getGroupAttr()->getOutputLogPropList()[0]->getResultCardinality().value(); // Make sure it is at least 1024. upQueueSize = (upQueueSize < 1024 ? 1024 : upQueueSize); // Make sure that it is not more the 64K. upQueueSize = (upQueueSize > 65536 ? 65536 : upQueueSize); unPackTdb = new (space) ComTdbUnPackRows(childTdb, packingFactorExpr, unPackColsExpr, #pragma nowarn(1506) // warning elimination unPackColsTupleLen, unPackColsAtpIndex, indexValueAtpIndex, givenCriDesc, returnedCriDesc, workCriDesc, 16, upQueueSize, (Cardinality) getGroupAttr()-> getOutputLogPropList()[0]-> getResultCardinality().value(), isRowsetIterator(), tolerateNonFatalError); } #pragma warn(1506) // warning elimination generator->initTdbFields(unPackTdb); // Remove child's outputs from mapTable, They are not needed // above. // generator->removeAll(localMapTable); // Add the explain Information for this node to the EXPLAIN // Fragment. Set the explainTuple pointer in the generator so // the parent of this node can get a handle on this explainTuple. // if(!generator->explainDisabled()) { generator->setExplainTuple(addExplainInfo(unPackTdb, childExplainTuple, 0, generator)); } // Restore the Cri Desc's and set the return object. // generator->setCriDesc(givenCriDesc, Generator::DOWN); generator->setCriDesc(returnedCriDesc, Generator::UP); generator->setGenObj(this, unPackTdb); return 0; }
// PhysSequence::preCodeGen() ------------------------------------------- // Perform local query rewrites such as for the creation and // population of intermediate tables, for accessing partitioned // data. Rewrite the value expressions after minimizing the dataflow // using the transitive closure of equality predicates. // // PhysSequence::preCodeGen() - is basically the same as the RelExpr:: // preCodeGen() except that here we replace the VEG references in the // sortKey() list, as well as the selectionPred(). // // Parameters: // // Generator *generator // IN/OUT : A pointer to the generator object which contains the state, // and tools (e.g. expression generator) to generate code for // this node. // // ValueIdSet &externalInputs // IN : The set of external Inputs available to this node. // // RelExpr * PhysSequence::preCodeGen(Generator * generator, const ValueIdSet & externalInputs, ValueIdSet &pulledNewInputs) { if (nodeIsPreCodeGenned()) return this; // Resolve the VEGReferences and VEGPredicates, if any, that appear // in the Characteristic Inputs, in terms of the externalInputs. // getGroupAttr()->resolveCharacteristicInputs(externalInputs); // My Characteristic Inputs become the external inputs for my children. // ValueIdSet childPulledInputs; child(0) = child(0)->preCodeGen(generator, externalInputs, pulledNewInputs); if (! child(0).getPtr()) return NULL; // process additional input value ids the child wants getGroupAttr()->addCharacteristicInputs(childPulledInputs); pulledNewInputs += childPulledInputs; // The VEG expressions in the selection predicates and the characteristic // outputs can reference any expression that is either a potential output // or a characteristic input for this RelExpr. Supply these values for // rewriting the VEG expressions. // ValueIdSet availableValues; getInputValuesFromParentAndChildren(availableValues); // The sequenceFunctions() have access to only the Input // Values. These can come from the parent or be the outputs of the // child. // sequenceFunctions(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); sequencedColumns(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); requiredOrder(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); cancelExpr(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); partition(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); //checkPartitionChangeExpr(). // replaceVEGExpressions(availableValues, // getGroupAttr()->getCharacteristicInputs()); // The selectionPred has access to only the output values generated by // Sequence and input values from the parent. // getInputAndPotentialOutputValues(availableValues); // Rewrite the selection predicates. // NABoolean replicatePredicates = TRUE; selectionPred().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs(), FALSE, // no key predicates here 0 /* no need for idempotence here */, replicatePredicates ); // Replace VEG references in the outputs and remove redundant // outputs. // getGroupAttr()->resolveCharacteristicOutputs (availableValues, getGroupAttr()->getCharacteristicInputs()); addCancelExpr(generator->wHeap()); ///addCheckPartitionChangeExpr(generator->wHeap()); transformOlapFunctions(generator->wHeap()); if ( getUnboundedFollowing() ) { // Count this Seq as a BMO and add its needed memory to the total needed generator->incrNumBMOs(); if ((ActiveSchemaDB()->getDefaults()). getAsDouble(EXE_MEMORY_LIMIT_PER_CPU) > 0) generator->incrBMOsMemory(getEstimatedRunTimeMemoryUsage(TRUE)); } else generator->incrNBMOsMemoryPerCPU(getEstimatedRunTimeMemoryUsage(TRUE)); markAsPreCodeGenned(); return this; } // PhysSequence::preCodeGen
// PhysSample::preCodeGen() ------------------------------------------- // Perform local query rewrites such as for the creation and // population of intermediate tables, for accessing partitioned // data. Rewrite the value expressions after minimizing the dataflow // using the transitive closure of equality predicates. // // PhysSample::preCodeGen() - is basically the same as the RelExpr:: // preCodeGen() except that here we replace the VEG references in the // sortKey() list, as well as the selectionPred(). // // Parameters: // // Generator *generator // IN/OUT : A pointer to the generator object which contains the state, // and tools (e.g. expression generator) to generate code for // this node. // // ValueIdSet &externalInputs // IN : The set of external Inputs available to this node. // // RelExpr * PhysSample::preCodeGen(Generator * generator, const ValueIdSet & externalInputs, ValueIdSet &pulledNewInputs) { // Do nothing if this node has already been processed. // if (nodeIsPreCodeGenned()) return this; // Resolve the VEGReferences and VEGPredicates, if any, that appear // in the Characteristic Inputs, in terms of the externalInputs. // getGroupAttr()->resolveCharacteristicInputs(externalInputs); // My Characteristics Inputs become the external inputs for my children. // preCodeGen my only child. // ValueIdSet childPulledInputs; child(0) = child(0)->preCodeGen(generator, externalInputs, pulledNewInputs); if(!child(0).getPtr()) return NULL; // Process additional any additional inputs the child wants. // getGroupAttr()->addCharacteristicInputs(childPulledInputs); pulledNewInputs += childPulledInputs; // The sampledCols() only have access to the Input Values. // These can come from the parent or be the outputs of the child. // Compute the set of available values for the sampledCols() and use // these to resolve any VEG references that the sampledCols() may need. // ValueIdSet availableValues; getInputValuesFromParentAndChildren(availableValues); sampledColumns().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs()); // Ditto, for the balance expression. // balanceExpr().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs()); requiredOrder(). replaceVEGExpressions(availableValues, getGroupAttr()->getCharacteristicInputs()); // The selectionPred has access to only the output values generated by // Sequence and input values from the parent. Compute the set of available // values for the selectionPred and resolve any VEG references // that the selection predicates may need. // getInputAndPotentialOutputValues(availableValues); NABoolean replicatePredicates = TRUE; selectionPred().replaceVEGExpressions (availableValues, getGroupAttr()->getCharacteristicInputs(), FALSE, // no key predicates here 0 /* no need for idempotence here */, replicatePredicates ); // Resolve VEG references in the outputs and remove redundant // outputs. // getGroupAttr()->resolveCharacteristicOutputs (availableValues, getGroupAttr()->getCharacteristicInputs()); // Mark this node as done and return. // markAsPreCodeGenned(); return this; } // PhysSample::preCodeGen
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; }