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; }
// ----------------------------------------------------------------------- // Method for creating NAType from desc_struct. // ----------------------------------------------------------------------- NABoolean NAColumn::createNAType(columns_desc_struct *column_desc /*IN*/, const NATable *table /*IN*/, NAType *&type /*OUT*/, NAMemory *heap /*IN*/, Lng32 * errorCode ) { // // Compute the NAType for this column // #define REC_INTERVAL REC_MIN_INTERVAL DataType datatype = column_desc->datatype; if (REC_MIN_INTERVAL <= datatype && datatype <= REC_MAX_INTERVAL) datatype = REC_INTERVAL; Lng32 charCount = column_desc->length; if ( DFS2REC::isAnyCharacter(column_desc->datatype) ) { if ( CharInfo::isCharSetSupported(column_desc->character_set) == FALSE ) { if (!errorCode) { *CmpCommon::diags() << DgSqlCode(-4082) << DgTableName(makeTableName(table, column_desc)); } else { *errorCode = 4082; } return TRUE; // error } if ( CharInfo::is_NCHAR_MP(column_desc->character_set) ) charCount /= SQL_DBCHAR_SIZE; } switch (datatype) { case REC_BPINT_UNSIGNED : type = new (heap) SQLBPInt(column_desc->precision, column_desc->null_flag, FALSE, heap); break; case REC_BIN8_SIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, TRUE, column_desc->null_flag, heap ); else type = new (heap) SQLTiny(TRUE, column_desc->null_flag, heap ); break; case REC_BIN8_UNSIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, FALSE, column_desc->null_flag, heap ); else type = new (heap) SQLTiny(FALSE, column_desc->null_flag, heap ); break; case REC_BIN16_SIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, TRUE, column_desc->null_flag, heap ); else type = new (heap) SQLSmall(TRUE, column_desc->null_flag, heap ); break; case REC_BIN16_UNSIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, FALSE, column_desc->null_flag, heap ); else type = new (heap) SQLSmall(FALSE, column_desc->null_flag, heap ); break; case REC_BIN32_SIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, TRUE, column_desc->null_flag, heap ); else type = new (heap) SQLInt(TRUE, column_desc->null_flag, heap ); break; case REC_BIN32_UNSIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, FALSE, column_desc->null_flag, heap ); else type = new (heap) SQLInt(FALSE, column_desc->null_flag, heap ); break; case REC_BIN64_SIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, TRUE, column_desc->null_flag, heap ); else type = new (heap) SQLLargeInt(TRUE, column_desc->null_flag, heap ); break; case REC_BIN64_UNSIGNED: if (column_desc->precision > 0) type = new (heap) SQLNumeric(column_desc->length, column_desc->precision, column_desc->scale, FALSE, column_desc->null_flag, heap ); else type = new (heap) SQLLargeInt(FALSE, column_desc->null_flag, heap ); break; case REC_DECIMAL_UNSIGNED: type = new (heap) SQLDecimal(column_desc->length, column_desc->scale, FALSE, column_desc->null_flag, heap ); break; case REC_DECIMAL_LSE: type = new (heap) SQLDecimal(column_desc->length, column_desc->scale, TRUE, column_desc->null_flag, heap ); break; case REC_NUM_BIG_UNSIGNED: type = new (heap) SQLBigNum(column_desc->precision, column_desc->scale, TRUE, // is a real bignum FALSE, column_desc->null_flag, heap ); break; case REC_NUM_BIG_SIGNED: type = new (heap) SQLBigNum(column_desc->precision, column_desc->scale, TRUE, // is a real bignum TRUE, column_desc->null_flag, heap ); break; case REC_FLOAT32: type = new (heap) SQLReal(column_desc->null_flag, heap, column_desc->precision); break; case REC_FLOAT64: type = new (heap) SQLDoublePrecision(column_desc->null_flag, heap, column_desc->precision); break; case REC_BYTE_F_DOUBLE: charCount /= SQL_DBCHAR_SIZE; // divide the storage length by 2 type = new (heap) SQLChar(charCount, column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, FALSE, column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT ); break; case REC_BYTE_F_ASCII: if (column_desc->character_set == CharInfo::UTF8 || (column_desc->character_set == CharInfo::SJIS && column_desc->encoding_charset == CharInfo::SJIS)) { Lng32 maxBytesPerChar = CharInfo::maxBytesPerChar(column_desc->character_set); Lng32 sizeInChars = charCount ; // Applies when CharLenUnit == BYTES if ( column_desc->precision > 0 ) sizeInChars = column_desc->precision; type = new (heap) SQLChar(CharLenInfo(sizeInChars, charCount/*in_bytes*/), column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, FALSE, // varLenFlag column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT, // Coercibility column_desc->encoding_charset ); } else // keep the old behavior type = new (heap) SQLChar(charCount, column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, FALSE, column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT ); break; case REC_BYTE_V_DOUBLE: charCount /= SQL_DBCHAR_SIZE; // divide the storage length by 2 // fall thru case REC_BYTE_V_ASCII: if (column_desc->character_set == CharInfo::SJIS || column_desc->character_set == CharInfo::UTF8) { Lng32 maxBytesPerChar = CharInfo::maxBytesPerChar(column_desc->character_set); Lng32 sizeInChars = charCount ; // Applies when CharLenUnit == BYTES if ( column_desc->precision > 0 ) sizeInChars = column_desc->precision; type = new (heap) SQLVarChar(CharLenInfo(sizeInChars, charCount/*in_bytes*/), column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT, // Coercibility column_desc->encoding_charset ); } else // keep the old behavior type = new (heap) SQLVarChar(charCount, column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT ); break; case REC_BYTE_V_ASCII_LONG: type = new (heap) SQLLongVarChar(charCount, FALSE, column_desc->null_flag, column_desc->upshift, column_desc->caseinsensitive, column_desc->character_set, column_desc->collation_sequence, CharInfo::IMPLICIT ); break; case REC_DATETIME: type = DatetimeType::constructSubtype( column_desc->null_flag, column_desc->datetimestart, column_desc->datetimeend, column_desc->datetimefractprec, heap ); CMPASSERT(type); if (!type->isSupportedType()) { column_desc->defaultClass = COM_NO_DEFAULT; // can't set a default for these, either. // 4030 Column is an unsupported combination of datetime fields if (!errorCode) { *CmpCommon::diags() << DgSqlCode(4030) << DgColumnName(makeColumnName(table, column_desc)) << DgInt0(column_desc->datetimestart) << DgInt1(column_desc->datetimeend) << DgInt2(column_desc->datetimefractprec); } else { *errorCode = 4030; } } break; case REC_INTERVAL: type = new (heap) SQLInterval(column_desc->null_flag, column_desc->datetimestart, column_desc->intervalleadingprec, column_desc->datetimeend, column_desc->datetimefractprec, heap ); CMPASSERT(type); if (! ((SQLInterval *)type)->checkValid(CmpCommon::diags())) return TRUE; // error if (!type->isSupportedType()) { column_desc->defaultClass = COM_NO_DEFAULT; // can't set a default for these, either. if (!errorCode) *CmpCommon::diags() << DgSqlCode(3044) << DgString0(column_desc->colname); else *errorCode = 3044; } break; case REC_BLOB : type = new (heap) SQLBlob(column_desc->precision, Lob_Invalid_Storage, column_desc->null_flag); break; case REC_CLOB : type = new (heap) SQLClob(column_desc->precision, Lob_Invalid_Storage, column_desc->null_flag); break; default: { // 4031 Column %s is an unknown data type, %d. if (!errorCode) { *CmpCommon::diags() << DgSqlCode(-4031) << DgColumnName(makeColumnName(table, column_desc)) << DgInt0(column_desc->datatype); } else { *errorCode = 4031; } return TRUE; // error } } // end switch (column_desc->datatype) CMPASSERT(type); if (type->getTypeQualifier() == NA_CHARACTER_TYPE) { CharInfo::Collation co = ((CharType *)type)->getCollation(); // a "mini-cache" to avoid proc call, for perf static THREAD_P CharInfo::Collation cachedCO = CharInfo::UNKNOWN_COLLATION; static THREAD_P Int32 cachedFlags = CollationInfo::ALL_NEGATIVE_SYNTAX_FLAGS; if (cachedCO != co) { cachedCO = co; cachedFlags = CharInfo::getCollationFlags(co); } if (cachedFlags & CollationInfo::ALL_NEGATIVE_SYNTAX_FLAGS) { // //## The NCHAR/COLLATE NSK-Rel1 project is forced to disallow all user- // defined collations here. What we can't handle is: // - full support! knowledge of how to really collate! // - safe predicate-ability of collated columns, namely // . ORDER/SEQUENCE/SORT BY // MIN/MAX // < <= > >= // These *would* have been disallowed by the // CollationInfo::ORDERED_CMP_ILLEGAL flag. // . DISTINCT // GROUP BY // = <> // These *would* have been disallowed by the // CollationInfo::EQ_NE_CMP_ILLEGAL flag. // . SELECTing a collated column which is a table or index key // We *would* have done full table scan only, based on flag // . INS/UPD/DEL involving a collated column which is a key // We *would* have had to disallow this, based on flag; // otherwise we would position in wrong and corrupt either // our partitioning or b-trees or both. // See the "MPcollate.doc" document, and // see sqlcomp/DefaultValidator.cpp ValidateCollationList comments. // { // 4069 Column TBL.COL uses unsupported collation COLLAT. if (!errorCode) { *CmpCommon::diags() << DgSqlCode(-4069) << DgColumnName(makeColumnName(table, column_desc)); } else { *errorCode= 4069; } return TRUE; // error } } } return FALSE; // no error } // createNAType()
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 ExpGenerator::buildKeyInfo(keyRangeGen ** keyInfo, // out -- generated object Generator * generator, const NAColumnArray & keyColumns, const ValueIdList & listOfKeyColumns, const ValueIdList & beginKeyPred, const ValueIdList & endKeyPred, const SearchKey * searchKey, const MdamKey * mdamKeyPtr, const NABoolean reverseScan, unsigned short keytag, const ExpTupleDesc::TupleDataFormat tf, // the next few parameters are here // as part of a horrible kludge for // the PartitionAccess::codeGen() // method, which lacks a SearchKey // object and therefore exposes // things like the exclusion // expressions; with luck, later work // in the Optimizer will result in a // much cleaner interface const NABoolean useTheHorribleKludge, ItemExpr * beginKeyExclusionExpr, ItemExpr * endKeyExclusionExpr, ex_expr_lean ** unique_key_expr, ULng32 *uniqueKeyLen, NABoolean doKeyEncodeOpt, Lng32 * firstKeyColOffset, Int32 in_key_atp_index ) { Space * space = generator->getSpace(); const Int32 work_atp = 1; const Int32 key_atp_index = (in_key_atp_index <= 0 ? 2 : in_key_atp_index); const Int32 exclude_flag_atp_index = 3; const Int32 data_conv_error_atp_index = 4; const Int32 key_column_atp_index = 5; // used only for Mdam const Int32 key_column2_atp_index = 6; // used only for Mdam MDAM_BETWEEN pred; // code in BiLogic::mdamPredGenSubrange // and MdamColumn::buildDisjunct // requires this to be 1 more than // key_column_atp_index ULng32 keyLen; // add an entry to the map table for work Atp MapTable *keyBufferPartMapTable = generator->appendAtEnd(); // generate a temporary variable, which will be used for handling // data conversion errors during key building ValueIdList temp_varb_list; ItemExpr * dataConversionErrorFlag = new(generator->wHeap()) HostVar("_sys_dataConversionErrorFlag", new(generator->wHeap()) SQLInt(TRUE,FALSE), // int not null TRUE); ULng32 temp_varb_tupp_len; dataConversionErrorFlag->bindNode(generator->getBindWA()); temp_varb_list.insert(dataConversionErrorFlag->getValueId()); processValIdList(temp_varb_list, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, temp_varb_tupp_len, // out work_atp, data_conv_error_atp_index); NABoolean doEquiKeyPredOpt = FALSE; #ifdef _DEBUG if (getenv("DO_EQUI_KEY_PRED_OPT")) doEquiKeyPredOpt = (searchKey ? searchKey->areAllChosenPredsEqualPreds() : FALSE); #endif if (mdamKeyPtr == NULL) { // check to see if there is a begin key expression; if there // isn't, don't generate a key object if (beginKeyPred.entries() == 0) *keyInfo = 0; else { // For subset and range operators, generate the begin key // expression, end key expression, begin key exclusion expression // and end key exclusion expression. For unique operators, // generate only the begin key exppression. ex_expr *bk_expr = 0; ex_expr *ek_expr = 0; ex_expr *bk_excluded_expr = 0; ex_expr *ek_excluded_expr = 0; short bkey_excluded = 0; short ekey_excluded = 0; generateKeyExpr(keyColumns, beginKeyPred, work_atp, key_atp_index, dataConversionErrorFlag, tf, keyLen, // out &bk_expr, // out doKeyEncodeOpt, firstKeyColOffset, doEquiKeyPredOpt); if (&endKeyPred) generateKeyExpr(keyColumns, endKeyPred, work_atp, key_atp_index, dataConversionErrorFlag, tf, keyLen, // out -- should be the same as above &ek_expr, // out doKeyEncodeOpt, firstKeyColOffset, doEquiKeyPredOpt); if (reverseScan) { // reverse scan - swap the begin and end key predicates // Note: evidently, the Optimizer has already switched // the key predicates in this case, so what we are // really doing is switching them back. ex_expr *temp = bk_expr; bk_expr = ek_expr; ek_expr = temp; } if (searchKey) { generateExclusionExpr(searchKey->getBeginKeyExclusionExpr(), work_atp, exclude_flag_atp_index, &bk_excluded_expr); // out bkey_excluded = (short) searchKey->isBeginKeyExclusive(); generateExclusionExpr(searchKey->getEndKeyExclusionExpr(), work_atp, exclude_flag_atp_index, &ek_excluded_expr); // out ekey_excluded = (short) searchKey->isEndKeyExclusive(); if (reverseScan) { NABoolean x = bkey_excluded; bkey_excluded = ekey_excluded; #pragma nowarn(1506) // warning elimination ekey_excluded = x; #pragma warn(1506) // warning elimination ex_expr* temp = bk_excluded_expr; bk_excluded_expr = ek_excluded_expr; bk_excluded_expr = temp; } } // if searchKey else if (useTheHorribleKludge) { generateExclusionExpr(beginKeyExclusionExpr, work_atp, exclude_flag_atp_index, &bk_excluded_expr); // out generateExclusionExpr(endKeyExclusionExpr, work_atp, exclude_flag_atp_index, &ek_excluded_expr); // out // note that the old PartitionAccess::codeGen() code didn't // set values for bkey_excluded and ekey_excluded, so the // safest choice is to choose inclusion, i.e. let the flags // retain their initial value of 0. } // Build key info if (keytag > 0) keyLen += sizeof(short); if ((unique_key_expr == NULL) || (NOT generator->genLeanExpr())) { // the work cri desc is used to build key values (entry 2) and // to compute the exclusion flag (entry 3) to monitor for data // conversion errors (entry 4) and to compute values on a column // basis (entry 5 - Mdam only) ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(6, space); *keyInfo = new(space) keySingleSubsetGen( keyLen, work_cri_desc, key_atp_index, exclude_flag_atp_index, data_conv_error_atp_index, bk_expr, ek_expr, bk_excluded_expr, ek_excluded_expr, // static exclude flags (if exprs are NULL) bkey_excluded, ekey_excluded); if (unique_key_expr) *unique_key_expr = NULL; } else { if (keyInfo) *keyInfo = NULL; *unique_key_expr = (ex_expr_lean*)bk_expr; *uniqueKeyLen = keyLen; } } } // end of non-mdam case else // Mdam case { // the work cri desc is used to build key values (entry 2) and // to compute the exclusion flag (entry 3) to monitor for data // conversion errors (entry 4) and to compute values on a column // basis (entry 5 - Mdam only, and entry 6 - Mdam only, and only // for MDAM_BETWEEN predtype) ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(7, space); // compute the format of the key buffer -- We need this // so that Mdam will know, for each column, where in the buffer // to move a value and how many bytes that value takes. The // next few lines of code result in this information being stored // in the attrs array. // Some words on the technique: We create expressions whose // result datatype matches the key buffer datatypes for each key // column. Then we use the datatypes of these expressions to // compute buffer format. The expressions themselves are not // used any further; they do not result in compiled expressions // in the plan. At run time we use string moves to move key // values instead. const CollIndex keyCount = listOfKeyColumns.entries(); CollIndex i; // assert at least one column GenAssert(keyCount > 0,"MDAM: at least one key column required."); Attributes ** attrs = new(generator->wHeap()) Attributes * [keyCount]; for (i = 0; i < keyCount; i++) { ItemExpr * col_node = listOfKeyColumns[i].getItemExpr(); ItemExpr *enode = col_node; if ((tf == ExpTupleDesc::SQLMX_KEY_FORMAT) && (enode->getValueId().getType().getVarLenHdrSize() > 0)) { // varchar keys in SQL/MP tables are converted to // fixed length chars in key buffers const CharType& char_type = (CharType&)(enode->getValueId().getType()); if (!CollationInfo::isSystemCollation(char_type.getCollation())) { enode = new(generator->wHeap()) Cast(enode, (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()))); } } NABoolean desc_flag; if (keyColumns.isAscending(i)) desc_flag = reverseScan; else desc_flag = !reverseScan; #pragma nowarn(1506) // warning elimination enode = new(generator->wHeap()) CompEncode(enode,desc_flag); #pragma warn(1506) // warning elimination enode->bindNode(generator->getBindWA()); attrs[i] = (generator-> addMapInfoToThis(keyBufferPartMapTable, enode->getValueId(), 0))->getAttr(); } // for, over keyCount // Compute offsets, lengths, etc. and assign them to the right // atp and atp index processAttributes((ULng32)keyCount, attrs, tf, keyLen, work_atp, key_atp_index); // Now we have key column offsets and lengths stored in attrs. // Next, for each column, generate expressions to compute hi, // lo, non-null hi and non-null lo values, and create // MdamColumnGen structures. // Notes: In the Mdam network itself, all key values are // encoded. Hence, we generate CompEncode nodes in all of the // expressions, regardless of tuple format. In the Simulator // case, we must at run-time decode the encoded values when // moving them to the key buffer. $$$ We need an expression to // do this. This decoding work has not yet been done, so the // simulator only works correctly for columns that happen to be // correctly aligned and whose encoding function does not change // the value. $$$ MdamColumnGen * first = 0; MdamColumnGen * last = 0; LIST(NAType *) keyTypeList(generator->wHeap());//to keep the type of the keys for later for (i = 0; i < keyCount; i++) { // generate expressions to compute hi, lo, non-null hi, non-null lo NAType * targetType = (keyColumns[i]->getType())->newCopy(generator->wHeap()); // Genesis case 10-971031-9814 fix: desc_flag must take into account // both the ASC/DESC attribute of the key column and the reverseScan // attribute. Before this fix, it only took into account the first of // these. NABoolean desc_flag; if (keyColumns.isAscending(i)) desc_flag = reverseScan; else desc_flag = !reverseScan; // End Genesis case 10-971031-9814 fix. if ((tf == ExpTupleDesc::SQLMX_KEY_FORMAT) && (targetType->getVarLenHdrSize() > 0)) { // 5/9/98: add support for VARNCHAR const CharType* char_type = (CharType*)(targetType); if (!CollationInfo::isSystemCollation(char_type->getCollation())) { targetType = 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()); /* targetType->getNominalSize(), targetType->supportsSQLnull() */ } } keyTypeList.insert(targetType); // save in ith position for later // don't need to make copy of targetType in next call ItemExpr * lo = new(generator->wHeap()) ConstValue(targetType, !desc_flag, TRUE /* allow NULL */); #pragma nowarn(1506) // warning elimination lo = new(generator->wHeap()) CompEncode(lo,desc_flag); #pragma warn(1506) // warning elimination lo->bindNode(generator->getBindWA()); ValueIdList loList; loList.insert(lo->getValueId()); ex_expr *loExpr = 0; ULng32 dataLen = 0; generateContiguousMoveExpr(loList, 0, // don't add convert nodes work_atp, key_column_atp_index, tf, dataLen, &loExpr); ItemExpr * hi = new(generator->wHeap()) ConstValue(targetType->newCopy(generator->wHeap()), desc_flag, TRUE /* allow NULL */); #pragma nowarn(1506) // warning elimination hi = new(generator->wHeap()) CompEncode(hi,desc_flag); #pragma warn(1506) // warning elimination hi->bindNode(generator->getBindWA()); ValueIdList hiList; hiList.insert(hi->getValueId()); ex_expr *hiExpr = 0; generateContiguousMoveExpr(hiList, 0, // don't add convert nodes work_atp, key_column_atp_index, tf, dataLen, &hiExpr); ex_expr *nonNullLoExpr = loExpr; ex_expr *nonNullHiExpr = hiExpr; if (targetType->supportsSQLnull()) { if (desc_flag) { ItemExpr * nonNullLo = new(generator->wHeap()) ConstValue(targetType->newCopy(generator->wHeap()), !desc_flag, FALSE /* don't allow NULL */); #pragma nowarn(1506) // warning elimination nonNullLo = new(generator->wHeap()) CompEncode(nonNullLo,desc_flag); #pragma warn(1506) // warning elimination nonNullLo->bindNode(generator->getBindWA()); ValueIdList nonNullLoList; nonNullLoList.insert(nonNullLo->getValueId()); nonNullLoExpr = 0; // so we will get an expression back generateContiguousMoveExpr(nonNullLoList, 0, // don't add convert nodes work_atp, key_column_atp_index, tf, dataLen, &nonNullLoExpr); } else { ItemExpr * nonNullHi = new(generator->wHeap()) ConstValue(targetType->newCopy(generator->wHeap()), desc_flag, FALSE /* don't allow NULL */); #pragma nowarn(1506) // warning elimination nonNullHi = new(generator->wHeap()) CompEncode(nonNullHi,desc_flag); #pragma warn(1506) // warning elimination nonNullHi->bindNode(generator->getBindWA()); ValueIdList nonNullHiList; nonNullHiList.insert(nonNullHi->getValueId()); nonNullHiExpr = 0; // so we will get an expression back generateContiguousMoveExpr(nonNullHiList, 0, // don't add convert nodes work_atp, key_column_atp_index, tf, dataLen, &nonNullHiExpr); } } NABoolean useSparseProbes = mdamKeyPtr->isColumnSparse(i); // calculate offset to the beginning of the column value // (including the null indicator and the varchar length // indicator if present) ULng32 column_offset = attrs[i]->getOffset(); if (attrs[i]->getNullFlag()) column_offset = attrs[i]->getNullIndOffset(); else if (attrs[i]->getVCIndicatorLength() > 0) column_offset = attrs[i]->getVCLenIndOffset(); last = new(space) MdamColumnGen(last, dataLen, column_offset, useSparseProbes, loExpr, hiExpr, nonNullLoExpr, nonNullHiExpr); if (first == 0) first = last; } // for over keyCount // generate MdamPred's and attach to MdamColumnGen's const ColumnOrderListPtrArray &columnOrderListPtrArray = mdamKeyPtr->getColumnOrderListPtrArray(); #ifdef _DEBUG // Debug print stataments below depend on this // variable: char *ev = getenv("MDAM_PRINT"); const NABoolean mdamPrintOn = (ev != NULL AND strcmp(ev,"ON")==0); #endif #ifdef _DEBUG if (mdamPrintOn) { fprintf(stdout, "\n\n***Generating the MDAM key for table with index" " columns: "); listOfKeyColumns.display(); } #endif for (CollIndex n = 0; n < columnOrderListPtrArray.entries(); n++) { // get the list of key predicates associated with the n disjunct: const ColumnOrderList &columnOrderList = *columnOrderListPtrArray[n]; #ifdef _DEBUG if (mdamPrintOn) { fprintf(stdout,"\nDisjunct[%d]:----------------\n",n); columnOrderList.print(); } #endif MdamColumnGen * cc = first; CMPASSERT(keyCount == columnOrderList.entries()); const ValueIdSet *predsPtr = NULL; for (i = 0; i < keyCount; i++) { #ifdef _DEBUG if (mdamPrintOn) { fprintf(stdout, "Column(%d) using: ", i); if ( mdamKeyPtr->isColumnSparse(i) ) fprintf(stdout,"SPARSE probes\n"); else fprintf(stdout, "DENSE probes\n"); } #endif // get predicates for column order i: predsPtr = columnOrderList[i]; NAType * keyType = keyTypeList[i]; NABoolean descending; if (keyColumns.isAscending(i)) descending = reverseScan; else descending = !reverseScan; ValueId keyColumn = listOfKeyColumns[i]; MdamCodeGenHelper mdamHelper( n, keyType, descending, work_atp, key_column_atp_index, tf, dataConversionErrorFlag, keyColumn); MdamPred * lastPred = cc->getLastPred(); if (predsPtr != NULL) { for (ValueId predId = predsPtr->init(); predsPtr->next(predId); predsPtr->advance(predId)) { MdamPred * head = 0; // head of generated MdamPred's MdamPred * tail = 0; ItemExpr * orGroup = predId.getItemExpr(); orGroup->mdamPredGen(generator,&head,&tail,mdamHelper,NULL); if (lastPred) { if ( CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) { MdamPred* curr = lastPred; while(curr->getNext() != NULL) curr=curr->getNext(); curr->setNext(head); } else lastPred->setNext(head); } cc->setLastPred(tail); lastPred = tail; //@ZXmdam if 1st pred has head != tail, head is lost } // for over preds } // if (predsPtr != NULL) cc = cc->getNext(); } // for every order... } // for every column order list in the array (of disjuncts) // build the Mdam key info if (keytag > 0) keyLen += sizeof(short); *keyInfo = new(space) keyMdamGen(keyLen, work_cri_desc, key_atp_index, exclude_flag_atp_index, data_conv_error_atp_index, key_column_atp_index, first, last, reverseScan, generator->wHeap()); } // end of mdam case if (*keyInfo) (*keyInfo)->setKeytag(keytag); // reset map table to forget about the key object's work Atp // aside: this logic is more bloody than it should be because the // map table implementation doesn't accurately reflect the map table // abstraction generator->removeAll(keyBufferPartMapTable); // deletes anything that might have been // added after keyBufferPartMapTable (at // this writing we don't expect there to // be anything, but we want to be safe) // at this point keyBufferPartMapTable should be the last map table in the // global map table chain generator->removeLast(); // unlinks keyBufferPartMapTable and deletes it return 0; };