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 MergeUnion::codeGen(Generator * generator) { ExpGenerator * exp_gen = generator->getExpGenerator(); Space * space = generator->getSpace(); MapTable * my_map_table = generator->appendAtEnd(); //////////////////////////////////////////////////////////////////////////// // // Layout at this node: // // |------------------------------------------------------------------------| // | input data | Unioned data | left child's data | right child's data | // | ( I tupps ) | ( 1 tupp ) | ( L tupps ) | ( R tupp ) | // |------------------------------------------------------------------------| // <-- returned row to parent ---> // <------------ returned row from left child -------> // <-------------------- returned row from right child ---------------------> // // input data: the atp input to this node by its parent. // unioned data: tupp where the unioned result is moved // left child data: tupps appended by the left child // right child data: tupps appended by right child // // Input to left child: I + 1 tupps // Input to right child: I + 1 + L tupps // // Tupps returned from left and right child are only used to create the // unioned data. They are not returned to parent. // //////////////////////////////////////////////////////////////////////////// ex_cri_desc * given_desc = generator->getCriDesc(Generator::DOWN); ex_cri_desc * returned_desc = NULL; if(child(0) || child(1)) returned_desc = new(space) ex_cri_desc(given_desc->noTuples() + 1, space); else returned_desc = given_desc; // expressions to move the left and right child's output to the // unioned row. ex_expr * left_expr = 0; ex_expr * right_expr = 0; // expression to compare left and right child's output to // evaluate merge union. ex_expr * merge_expr = 0; // Expression to conditionally execute the left or right child. ex_expr *cond_expr = NULL; // Expression to handle triggered action excpetion ex_expr *trig_expr = NULL; // It is OK for neither child to exist when generating a merge union TDB // for index maintenenace. The children are filled in at build time. // GenAssert((child(0) AND child(1)) OR (NOT child(0) AND NOT (child(1))), "MergeUnion -- missing one child"); ComTdb * left_child_tdb = NULL; ComTdb * right_child_tdb = NULL; ExplainTuple *leftExplainTuple = NULL; ExplainTuple *rightExplainTuple = NULL; NABoolean afterUpdate = FALSE; NABoolean rowsFromLeft = TRUE; NABoolean rowsFromRight = TRUE; if(child(0) && child(1)) { // if an update operation is found before the execution of the // IF statement, set afterUpdate to 1 indicating that an update operation // was performed before the execution of the IF statement. Which // is used at runtime to decide whether to set rollbackTransaction in the // diagsArea if (generator->updateWithinCS() && getUnionForIF()) { afterUpdate = TRUE; } // generate the left child generator->setCriDesc(returned_desc, Generator::DOWN); child(0)->codeGen(generator); left_child_tdb = (ComTdb *)(generator->getGenObj()); leftExplainTuple = generator->getExplainTuple(); // MVs -- // If the left child does not have any outputs, don't expect any rows. if (child(0)->getGroupAttr()->getCharacteristicOutputs().isEmpty()) rowsFromLeft = FALSE; // if an update operation is found in the left subtree of this Union then // set rowsFromLeft to 0 which is passed on to execution tree indicating // that this Union node is not expecting rows from the left child, then // foundAnUpdate_ is reset so it can be reused while doing codGen() on // the right sub tree if (getUnionForIF()) { if (! getCondEmptyIfThen()) { if (generator->foundAnUpdate()) { rowsFromLeft = FALSE; generator->setFoundAnUpdate(FALSE); } } else { rowsFromLeft = FALSE; } } // descriptor returned by left child is given to right child as input. generator->setCriDesc(generator->getCriDesc(Generator::UP), Generator::DOWN); child(1)->codeGen(generator); right_child_tdb = (ComTdb *)(generator->getGenObj()); rightExplainTuple = generator->getExplainTuple(); // MVs // If the right child does not have any outputs, don't expect any rows. if (child(1)->getGroupAttr()->getCharacteristicOutputs().isEmpty()) rowsFromRight = FALSE; // if an update operation is found in the right subtree of this CS then // set rowsFromRight to 0 which is passed on to execution tree indicating // that this CS node is not expecting rows from the right child, then // foundAnUpdate_ is reset so it can be reused while doing codGen() on // the left or right child of another CS node if (getUnionForIF()) { if (! getCondEmptyIfElse()) { if (generator->foundAnUpdate()) { rowsFromRight = FALSE; } } else { rowsFromRight = FALSE; } // we cannot always expect a row from a conditional operator. If it is an // IF statement without an ELSE and the condition fails then we do not get // any rows back. So we allow a conditional union operator to handle all // errors below it and for the purposes of 8015 error / 8014 warning // treat it as an update node. In this way the nodes above it do not expect // any row from this child and do not raise an error if no row is returned. // 8014/8015 type errors within this IF statement are handled as in any // regular CS. generator->setFoundAnUpdate(TRUE); } } // Create the unioned row. // colMapTable() is a list of ValueIdUnion nodes where each node points to // the corresponding left and the right output entries. // Generate expressions to move the left and right child's output to // the unioned row. ValueIdList left_val_id_list; ValueIdList right_val_id_list; CollIndex i; for (i = 0; i < colMapTable().entries(); i++) { ValueIdUnion * vidu_node = (ValueIdUnion *)(((colMapTable()[i]).getValueDesc())->getItemExpr()); Cast * cnode; if (vidu_node->getResult().getType().getTypeQualifier() != NA_ROWSET_TYPE) { // move left child's output to result. The 'type' of Cast result is same // as that of the vidu_node. cnode = new(generator->wHeap()) Cast(((vidu_node->getLeftSource()).getValueDesc())->getItemExpr(), &(vidu_node->getResult().getType())); } else { // We indicate that the whole array is to be copied SQLRowset *rowsetInfo = (SQLRowset *) &(vidu_node->getResult().getType()); SQLRowset *newRowset = new (generator->wHeap()) SQLRowset(generator->wHeap(), rowsetInfo->getElementType(), rowsetInfo->getMaxNumElements(), rowsetInfo->getNumElements()); newRowset->useTotalSize() = TRUE; cnode = new(generator->wHeap()) Cast(((vidu_node->getLeftSource()).getValueDesc())->getItemExpr(), newRowset); } cnode->bindNode(generator->getBindWA()); left_val_id_list.insert(cnode->getValueId()); if (vidu_node->getResult().getType().getTypeQualifier() != NA_ROWSET_TYPE) { // move left child's output to result. The 'type' of Cast result is same // as that of the vidu_node. cnode = new(generator->wHeap()) Cast(((vidu_node->getRightSource()).getValueDesc())->getItemExpr(), &(vidu_node->getResult().getType())); } else { // We indicate that the whole array is to be copied SQLRowset *rowsetInfo = (SQLRowset *) &(vidu_node->getResult().getType()); SQLRowset *newRowset = new (generator->wHeap()) SQLRowset(generator->wHeap(), rowsetInfo->getElementType(), rowsetInfo->getMaxNumElements(), rowsetInfo->getNumElements()); newRowset->useTotalSize() = TRUE; cnode = new(generator->wHeap()) Cast(((vidu_node->getRightSource()).getValueDesc())->getItemExpr(), newRowset); } cnode->bindNode(generator->getBindWA()); right_val_id_list.insert(cnode->getValueId()); } ExpTupleDesc * tuple_desc = 0; ULng32 tuple_length = 0; if(child(0) && child(1)) { exp_gen->generateContiguousMoveExpr(left_val_id_list, 0, // don't add convert nodes 1, returned_desc->noTuples() - 1, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, tuple_length, &left_expr, &tuple_desc, ExpTupleDesc::SHORT_FORMAT); exp_gen->generateContiguousMoveExpr(right_val_id_list, 0, // don't add convert nodes 1, returned_desc->noTuples() - 1, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, tuple_length, &right_expr); } // add value ids for all vidu_nodes to my map table. This is the // the map table that will be returned. The attributes of the value ids // are same as that of left(or right) expression outputs. for (i = 0; i < colMapTable().entries(); i++) { ValueIdUnion * vidu_node = (ValueIdUnion *)(((colMapTable()[i]).getValueDesc())->getItemExpr()); Attributes * attr = generator->addMapInfoToThis(my_map_table, vidu_node->getValueId(), generator->getMapInfo(left_val_id_list[i])->getAttr())->getAttr(); attr->setAtp(0); } // describe the returned unioned row returned_desc->setTupleDescriptor(returned_desc->noTuples() - 1, tuple_desc); // if sort-merge union is being done, generate expression to // compare the left and the right values. // This predicate should return TRUE if the left value is // less than the right value. merge_expr = 0; if (getMergeExpr()) { // generate the merge predicate. ItemExpr * mergeExpr = new(generator->wHeap()) BoolResult(getMergeExpr()); mergeExpr->bindNode(generator->getBindWA()); exp_gen->generateExpr(mergeExpr->getValueId(), ex_expr::exp_SCAN_PRED, &merge_expr); } // If conditional union, generate conditional expression, and ignore // right child if it was just being used as a no-op. cond_expr = 0; if (NOT condExpr().isEmpty()) { ItemExpr *condExp = condExpr().rebuildExprTree(ITM_AND, TRUE, TRUE); exp_gen->generateExpr(condExp->getValueId(), ex_expr::exp_SCAN_PRED, &cond_expr); } // If conditional union, generate triggered action exception error if (NOT trigExceptExpr().isEmpty()) { ItemExpr *trigExp = trigExceptExpr().rebuildExprTree(ITM_AND, TRUE, TRUE); exp_gen->generateExpr(trigExp->getValueId(), ex_expr::exp_SCAN_PRED, &trig_expr); } // remove both children's map table. Nothing from child's context // should be visible from here on upwards. generator->removeAll(my_map_table); // Ensure the default buffer size is at least as large as the unioned output // row. UInt32 outputBuffSize = MAXOF( getDefault(GEN_UN_BUFFER_SIZE), tuple_length ); outputBuffSize = SqlBufferNeededSize( 1, // # of tuples outputBuffSize, SqlBuffer::NORMAL_ ); ComTdbUnion * union_tdb = new(space) ComTdbUnion( left_child_tdb, right_child_tdb, left_expr, right_expr, merge_expr, cond_expr, trig_expr, tuple_length, // unioned rowlen returned_desc->noTuples()-1, // tupp index for // unioned buffer given_desc, returned_desc, (queue_index)getDefault(GEN_UN_SIZE_DOWN), (queue_index)getDefault(GEN_UN_SIZE_UP), (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(), getDefault(GEN_UN_NUM_BUFFERS), outputBuffSize, getOrderedUnion(), getBlockedUnion(), //++ Triggers - hasNoOutputs(), //++ Triggers - rowsFromLeft, rowsFromRight, afterUpdate, getInNotAtomicStatement()); generator->initTdbFields(union_tdb); // If it does not have two children, this is index maintenance code and // should not be Explained if (!generator->explainDisabled()) { generator->setExplainTuple(addExplainInfo(union_tdb, leftExplainTuple, rightExplainTuple, generator)); } // restore the original down cri desc since this node changed it. generator->setCriDesc(given_desc, Generator::DOWN); // set the new up cri desc. generator->setCriDesc(returned_desc, Generator::UP); generator->setGenObj(this, union_tdb); return 0; }