| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| ****************************************************************************** |
| * |
| * File: GenRelMisc.C |
| * RCS: $Id: GenRelMisc.cpp,v 1.1 2007/10/09 19:38:54 Exp $ |
| * Description: MapValueId/Root/Tuple operators |
| * |
| * Created: 5/17/94 |
| * Modified: $ $Date: 2007/10/09 19:38:54 $ (GMT) |
| * Language: C++ |
| * Status: $State: Exp $ |
| * |
| * |
| * |
| ****************************************************************************** |
| */ |
| #define SQLPARSERGLOBALS_FLAGS |
| #include "ComOptIncludes.h" |
| #include "GroupAttr.h" |
| #include "ItemColRef.h" |
| #include "RelEnforcer.h" |
| #include "RelJoin.h" |
| #include "RelExeUtil.h" |
| #include "RelMisc.h" |
| #include "RelSet.h" |
| #include "RelUpdate.h" |
| #include "RelScan.h" |
| #include "RelDCL.h" |
| #include "PartFunc.h" |
| #include "Cost.h" |
| #include "GenExpGenerator.h" |
| #include "GenResources.h" |
| #include "ComTdbRoot.h" |
| #include "ComTdbTuple.h" |
| #include "ComTdbUnion.h" |
| #include "ComTdbTupleFlow.h" |
| #include "ComTdbTranspose.h" |
| #include "ComTdbSort.h" |
| #include "ComTdbPackRows.h" |
| #include "ComTdbDDL.h" |
| #include "ComTdbExeUtil.h" |
| #include "ComTdbFirstN.h" |
| #include "ComTdbStats.h" |
| #include "ComTdbCancel.h" |
| #include "ExplainTuple.h" |
| #include "ComTdbHbaseAccess.h" |
| #include "ComTdbExplain.h" |
| #include "SchemaDB.h" |
| #include "ControlDB.h" |
| #include "NATable.h" |
| #include "BindWA.h" |
| #include "ComTransInfo.h" |
| #include "DefaultConstants.h" |
| #include "FragDir.h" |
| #include "PartInputDataDesc.h" |
| #include "ExpSqlTupp.h" |
| #include "sql_buffer.h" |
| #include "ComQueue.h" |
| #include "ComSqlId.h" |
| #include "MVInfo.h" |
| #include "SequenceGeneratorAttributes.h" |
| #include "CompilationStats.h" |
| #include "RelRoutine.h" |
| #include "hs_cont.h" |
| #include "ComUnits.h" |
| |
| #include "StmtDDLCleanupObjects.h" |
| |
| #ifndef HFS2DM |
| #define HFS2DM |
| #endif // HFS2DM |
| |
| #include "ComDefs.h" // to get common defines (ROUND8) |
| #include "CmpStatement.h" |
| #include "ComSmallDefs.h" |
| #include "sql_buffer_size.h" |
| #include "ExSqlComp.h" // for NAExecTrans |
| |
| #include "ComLocationNames.h" |
| #include "ComDistribution.h" |
| #include "OptimizerSimulator.h" |
| |
| #include "ComCextdecs.h" |
| |
| #include "TrafDDLdesc.h" |
| |
| #include "SqlParserGlobals.h" // Parser Flags |
| |
| // this comes from GenExplain.cpp (sorry, should have a header file) |
| TrafDesc * createVirtExplainTableDesc(); |
| void deleteVirtExplainTableDesc(TrafDesc *); |
| |
| ///////////////////////////////////////////////////////////////////// |
| // |
| // |
| // |
| ////////////////////////////////////////////////////////////////////// |
| |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // MapValueIds::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short MapValueIds::codeGen(Generator * generator) |
| { |
| |
| // The MapValueIds node does not result in any executor nodes. It |
| // does not generate a TDB. It simply 'maps' its lower values to |
| // its upper values. It does this by doing the following for each |
| // upper and lower pairing. |
| // |
| // - creates a new expressions which converts the lower value to |
| // the type of the upper value. |
| // |
| // - Associates this new expression with the ValueId of the upper |
| // value, by replacing the ItemExpr of the upper valueId with |
| // this new expression. |
| // |
| // The end result is that in nodes above this MapValueIds node, |
| // references to the ValueIds of the upper list, will get the new |
| // expression (the Cast of the lower value). |
| // |
| // This code was initially placed in the MapValueIds::preCodeGen(), |
| // but this caused some problems with Query trees involving |
| // triggers. The issue was that with triggers it is possible for a |
| // ValueId to be sourced (produced) in two different subtrees of the |
| // query tree. One being a MapValueId. So when the expressions of |
| // the upper valueIds were replaced, it not only affected references |
| // to the ValueIds above the MapValueIds, it affected the references |
| // to the ValueIds in other subtrees. |
| |
| |
| // generate code for the child |
| child(0)->codeGen(generator); |
| |
| const ValueIdList & topValues = map_.getTopValues(); |
| |
| for (CollIndex i = 0; i < topValues.entries(); i++) |
| { |
| ValueId valId = topValues[i]; |
| |
| // if this value id is mapped to a different one (mappedId ) by |
| // this node, then convert the "mappedId" to the type of this |
| // 'valid' |
| // |
| ValueId mappedId; |
| map_.mapValueIdDown(valId,mappedId); |
| |
| if (mappedId != valId) |
| { |
| |
| // Convert the source (lower) value to the type of the |
| // target (upper) value. |
| // |
| ItemExpr * convValue |
| = new(generator->wHeap()) Cast (mappedId.getItemExpr(), |
| &(valId.getType())); |
| |
| // bind/type propagate the new node |
| convValue->bindNode(generator->getBindWA()); |
| |
| // Replace upper value with converted value. |
| // |
| valId.replaceItemExpr(convValue); |
| |
| } |
| } |
| |
| // No TDB was generated. Parent will retrieve childs TDB |
| // |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // Some helper classes and functions for PartitionAccess::codeGen |
| // |
| ///////////////////////////////////////////////////////// |
| |
| class NodeCountHelper { |
| friend class NodeHashHelper; |
| public: |
| UInt32 getCount(void) { return nodeDiskCount_;} |
| void incCount(void) { nodeDiskCount_++; } |
| inline NABoolean operator==(const NodeCountHelper& other) const |
| { |
| return (nodeDiskCount_ == other.nodeDiskCount_); |
| } |
| |
| private: |
| UInt32 nodeDiskCount_; |
| }; |
| |
| class NodeNameHelper { |
| friend class NodeHashHelper; |
| friend ULng32 nodeNameHashFunc( const NodeNameHelper& n ); |
| |
| public: |
| const char * const getNodeName(void) { return nodeName_; } |
| ULng32 hash() const { return nodeNameHashFunc(*this); } |
| inline NABoolean operator==(const NodeNameHelper& other) const |
| { |
| if (stricmp(nodeName_, other.nodeName_) != 0) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| private: |
| char nodeName_[ComGUARDIAN_SYSTEM_NAME_PART_CHAR_MAX_LEN + 1]; |
| }; |
| |
| ULng32 nodeNameHashFunc( const NodeNameHelper& n ) |
| { |
| ULng32 retval = 0; |
| const char * const c = n.nodeName_; |
| Int32 i = 0; |
| do { |
| retval += c[i++]; |
| } while (c[i]); |
| return retval; |
| } |
| |
| |
| class NodeDiskNameHelper { |
| friend class NodeHashHelper; |
| friend ULng32 nodeDiskNameHashFunc( const NodeDiskNameHelper& n ); |
| |
| public: |
| inline NABoolean operator==(const NodeDiskNameHelper& other) const |
| { |
| if (stricmp(nodeDiskName_, other.nodeDiskName_) != 0) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| |
| ULng32 hash() const { return nodeDiskNameHashFunc(*this); } |
| |
| private: |
| char nodeDiskName_[ |
| ComGUARDIAN_SYSTEM_NAME_PART_CHAR_MAX_LEN + 1 // + 1 for the dot. |
| + ComGUARDIAN_VOLUME_NAME_PART_CHAR_MAX_LEN + 1 // + 1 for the \0. |
| ]; |
| }; |
| |
| ULng32 nodeDiskNameHashFunc( const NodeDiskNameHelper& n ) |
| { |
| ULng32 retval = 0; |
| const char * const c = n.nodeDiskName_; |
| Int32 i = 0; |
| do { |
| retval += c[i++]; |
| } while (c[i]); |
| return retval; |
| } |
| |
| class NodeHashHelper { |
| |
| public: |
| NodeHashHelper ( const char * partn ) |
| { |
| memmove(nodeName_.nodeName_, partn, sizeof nodeName_.nodeName_); |
| // Convert 1st dot into a null termininator. |
| char * dotPos = strchr(nodeName_.nodeName_, '.'); |
| *dotPos = '\0'; |
| memmove(nodeDiskName_.nodeDiskName_, partn, |
| sizeof nodeDiskName_.nodeDiskName_); |
| // Convert 2nd dot into a null termininator. |
| dotPos = strchr(nodeDiskName_.nodeDiskName_, '.'); |
| dotPos = strchr(dotPos+1, '.'); |
| *dotPos = '\0'; |
| nodeDiskCount_.nodeDiskCount_ = 1; |
| } |
| NodeNameHelper * getNodeName(void) { return &nodeName_; } |
| NodeDiskNameHelper * getNodeDiskName(void) { return &nodeDiskName_; } |
| NodeCountHelper * getNodeDiskCount(void) { return &nodeDiskCount_ ; } |
| private: |
| NodeHashHelper(); |
| NodeCountHelper nodeDiskCount_ ; |
| NodeNameHelper nodeName_; |
| NodeDiskNameHelper nodeDiskName_; |
| }; |
| |
| static void replaceBaseValue(ItemExpr *incomingExpr, |
| ItemExpr *resultExpr, |
| CollHeap *heap, |
| NABoolean setFound) |
| { |
| static THREAD_P NABoolean found = FALSE; |
| |
| found = setFound; |
| Int32 nc = incomingExpr->getArity(); |
| |
| // ITM_ASSIGN operator will not be a leaf node. |
| if ((nc != 0) && (found == FALSE)) //have not found yet. |
| { |
| ItemExpr *child1 = incomingExpr->child(1); |
| ItemExpr *newExpr = NULL; |
| if (incomingExpr->getOperatorType() == ITM_ASSIGN && |
| (child1 != NULL && child1->getOperatorType() == ITM_ASSIGN) |
| ) |
| { |
| newExpr = new(heap) BiArith(ITM_PLUS, |
| resultExpr, |
| child1); |
| |
| newExpr->synthTypeAndValueId(TRUE); |
| //set type to original type after if has been changed to BigNum above |
| newExpr->getValueId().changeType(new (heap) SQLLargeInt(heap, 1 /* signed */, |
| 0 /* not null */)); |
| incomingExpr->setChild(1,newExpr); |
| found = TRUE; |
| return; |
| } |
| |
| for (Lng32 i = 0; i < (Lng32)nc; i++) |
| { |
| if (found == FALSE) |
| replaceBaseValue(incomingExpr->child(i), resultExpr, heap, found); |
| } |
| } |
| return; |
| } |
| |
| short GenericUtilExpr::codeGen(Generator * generator) |
| { |
| GenAssert(0, "GenericUtilExpr::codeGen. Should not reach here."); |
| |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // DDLExpr::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| ///////////////////////////////////////////////////////// |
| // |
| // ExeUtilExpr::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| const char * DDLExpr::getVirtualTableName() |
| { return (producesOutput() ? "DDL_EXPR__" : NULL); } |
| |
| TrafDesc *DDLExpr::createVirtualTableDesc() |
| { |
| TrafDesc * table_desc = NULL; |
| if (producesOutput()) |
| { |
| table_desc = |
| Generator::createVirtualTableDesc(getVirtualTableName(), |
| NULL, // let it decide what heap to use |
| ComTdbDDL::getVirtTableNumCols(), |
| ComTdbDDL::getVirtTableColumnInfo(), |
| ComTdbDDL::getVirtTableNumKeys(), |
| ComTdbDDL::getVirtTableKeyInfo()); |
| } |
| return table_desc; |
| } |
| |
| short DDLExpr::codeGen(Generator * generator) |
| { |
| Space * space = generator->getSpace(); |
| |
| generator->verifyUpdatableTransMode(NULL, generator->getTransMode(), NULL); |
| |
| // remove trailing blanks and append a semicolon, if one is not present. |
| char * ddlStmt = NULL; |
| |
| Int32 i = strlen(getDDLStmtText()); |
| while ((i > 0) && (getDDLStmtText()[i-1] == ' ')) |
| i--; |
| |
| if (getDDLStmtText()[i-1] == ';') |
| i--; |
| |
| ddlStmt = space->allocateAlignedSpace(i+2); |
| strncpy(ddlStmt, getDDLStmtText(), i); |
| |
| // add a semicolon to the end of str (required by the parser) |
| ddlStmt[i++] = ';'; |
| |
| ddlStmt[i] = '\0'; |
| |
| ex_cri_desc * givenDesc |
| = generator->getCriDesc(Generator::DOWN); |
| ex_cri_desc * returnedDesc = givenDesc; |
| ex_cri_desc * workCriDesc = NULL; |
| const Int32 work_atp = 1; |
| const Int32 ddl_row_atp_index = 2; |
| if (producesOutput()) |
| { |
| // allocate a map table for the retrieved columns |
| generator->appendAtEnd(); |
| |
| returnedDesc = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| workCriDesc = new(space) ex_cri_desc(4, space); |
| short rc = processOutputRow(generator, work_atp, ddl_row_atp_index, |
| returnedDesc); |
| if (rc) |
| { |
| return -1; |
| } |
| } |
| |
| NAString catSchName = |
| generator->currentCmpContext()->schemaDB_->getDefaultSchema().getSchemaNameAsAnsiString(); |
| |
| CMPASSERT(!catSchName.isNull()); // not empty |
| CMPASSERT(catSchName.first('.') != NA_NPOS); // quick test: 'cat.sch' |
| |
| char * catSchNameStr = space->allocateAlignedSpace(catSchName.length() + 1); |
| strcpy(catSchNameStr, catSchName.data()); |
| |
| ComTdbDDL * ddl_tdb = NULL; |
| |
| if (returnStatus_) |
| { |
| ComTdbDDLwithStatus *ddl_ws_tdb = new(space) |
| ComTdbDDLwithStatus(ddlStmt, |
| strlen(ddlStmt), |
| (Int16)getDDLStmtTextCharSet(), |
| catSchNameStr, strlen(catSchNameStr), |
| 0, 0, // no input expr |
| 0, 0, // no output expr |
| workCriDesc, (producesOutput() ? ddl_row_atp_index : 0), |
| givenDesc, |
| returnedDesc, |
| (queue_index)getDefault(GEN_DDL_SIZE_DOWN), |
| (queue_index)getDefault(GEN_DDL_SIZE_UP), |
| getDefault(GEN_DDL_NUM_BUFFERS), |
| getDefault(GEN_DDL_BUFFER_SIZE)); |
| |
| ddl_ws_tdb->setReturnStatus(TRUE); |
| |
| if (isCleanup_) |
| { |
| ddl_ws_tdb->setMDcleanup(TRUE); |
| |
| StmtDDLCleanupObjects * co = |
| getExprNode()->castToElemDDLNode()->castToStmtDDLCleanupObjects(); |
| |
| if (co->checkOnly()) |
| ddl_ws_tdb->setCheckOnly(TRUE); |
| |
| if (co->returnDetails()) |
| ddl_ws_tdb->setReturnDetails(TRUE); |
| |
| } |
| ddl_tdb = ddl_ws_tdb; |
| } |
| else |
| ddl_tdb = new(space) |
| ComTdbDDL(ddlStmt, |
| strlen(ddlStmt), |
| (Int16)getDDLStmtTextCharSet(), |
| catSchNameStr, strlen(catSchNameStr), |
| 0, 0, // no input expr |
| 0, 0, // no output expr |
| workCriDesc, (producesOutput() ? ddl_row_atp_index : 0), |
| givenDesc, |
| returnedDesc, |
| (queue_index)getDefault(GEN_DDL_SIZE_DOWN), |
| (queue_index)getDefault(GEN_DDL_SIZE_UP), |
| getDefault(GEN_DDL_NUM_BUFFERS), |
| getDefault(GEN_DDL_BUFFER_SIZE)); |
| |
| if (isHbase_) |
| { |
| ddl_tdb->setHbaseDDL(TRUE); |
| |
| if (hbaseDDLNoUserXn_) |
| ddl_tdb->setHbaseDDLNoUserXn(TRUE); |
| } |
| |
| generator->initTdbFields(ddl_tdb); |
| |
| if(!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(ddl_tdb, 0, 0, generator)); |
| } |
| |
| // no tupps are returned |
| generator->setCriDesc((ex_cri_desc *)(generator->getCriDesc(Generator::DOWN)), |
| Generator::UP); |
| generator->setGenObj(this, ddl_tdb); |
| |
| // Set the transaction flag. |
| if (xnNeeded()) |
| { |
| if (NOT isHbase_) |
| generator->setTransactionFlag(-1); |
| else if (getExprNode() && |
| getExprNode()->castToStmtDDLNode()->ddlXns() && |
| (NOT hbaseDDLNoUserXn_)) |
| { |
| // treat like a transactional IUD operation which need to be |
| // aborted in case of an error. |
| generator->setFoundAnUpdate(TRUE); |
| generator->setUpdAbortOnError(TRUE); |
| |
| generator->setTransactionFlag(-1); |
| } |
| else if (NOT hbaseDDLNoUserXn_) |
| generator->setTransactionFlag(-1); |
| } |
| |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // ExeUtilMetadataUpgrade::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short ExeUtilMetadataUpgrade::codeGen(Generator * generator) |
| { |
| ExpGenerator * expGen = generator->getExpGenerator(); |
| Space * space = generator->getSpace(); |
| |
| // allocate a map table for the retrieved columns |
| generator->appendAtEnd(); |
| |
| ex_cri_desc * givenDesc |
| = generator->getCriDesc(Generator::DOWN); |
| |
| ex_cri_desc * returnedDesc |
| = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| |
| ex_cri_desc * workCriDesc = new(space) ex_cri_desc(4, space); |
| const Int32 work_atp = 1; |
| const Int32 exe_util_row_atp_index = 2; |
| |
| short rc = processOutputRow(generator, work_atp, exe_util_row_atp_index, |
| returnedDesc); |
| if (rc) |
| { |
| return -1; |
| } |
| |
| ComTdbDDLwithStatus * upgd_tdb = new(space) |
| ComTdbDDLwithStatus(NULL, 0, 0, |
| NULL, 0, |
| 0, 0, // no input expr |
| 0, 0, // no output expr |
| NULL, 0, |
| givenDesc, |
| returnedDesc, |
| (queue_index)getDefault(GEN_DDL_SIZE_DOWN), |
| (queue_index)getDefault(GEN_DDL_SIZE_UP), |
| getDefault(GEN_DDL_NUM_BUFFERS), |
| getDefault(GEN_DDL_BUFFER_SIZE)); |
| |
| if (getMDVersion()) |
| upgd_tdb->setGetMDVersion(TRUE); |
| else if (getSWVersion()) |
| upgd_tdb->setGetSWVersion(TRUE); |
| else |
| upgd_tdb->setMDupgrade(TRUE); |
| |
| generator->initTdbFields(upgd_tdb); |
| |
| if(!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(upgd_tdb, 0, 0, generator)); |
| } |
| |
| generator->setCriDesc(givenDesc, Generator::DOWN); |
| generator->setCriDesc(returnedDesc, Generator::UP); |
| |
| generator->setGenObj(this, upgd_tdb); |
| |
| // Reset the transaction flag. |
| generator->setTransactionFlag(0); |
| |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // FirstN::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short FirstN::codeGen(Generator * generator) |
| { |
| ExpGenerator* expGen = generator->getExpGenerator(); |
| Space * space = generator->getSpace(); |
| |
| ex_cri_desc * given_desc = generator->getCriDesc(Generator::DOWN); |
| |
| // generate code for my child |
| child(0)->codeGen(generator); |
| ComTdb * child_tdb = (ComTdb *)(generator->getGenObj()); |
| ExplainTuple *childExplainTuple = generator->getExplainTuple(); |
| |
| ex_cri_desc * returned_desc = generator->getCriDesc(Generator::UP); |
| |
| ex_cri_desc * work_cri_desc = NULL; |
| ex_expr * firstNRowsExpr = NULL; |
| if (firstNRowsParam_) |
| { |
| Int32 work_atp = 1; // temps |
| Int32 work_atp_index = 2; // where the result row will be |
| work_cri_desc = new(space) ex_cri_desc(3, space); |
| |
| // input param is typed as nullable. Make it non-nullable and unsigned. |
| NAType * newNAT = |
| firstNRowsParam_->getValueId().getType().newCopy(generator->wHeap()); |
| newNAT->setNullable(FALSE, FALSE); |
| |
| Cast * fnp = new(generator->wHeap()) Cast(firstNRowsParam_, newNAT); |
| fnp->bindNode(generator->getBindWA()); |
| |
| ValueIdList vidL; |
| vidL.insert(fnp->getValueId()); |
| |
| UInt32 firstNValLen = 0; |
| expGen->generateContiguousMoveExpr(vidL, |
| 0, // no convert nodes, |
| work_atp, work_atp_index, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| firstNValLen, &firstNRowsExpr, |
| NULL, ExpTupleDesc::SHORT_FORMAT); |
| } |
| |
| ComTdbFirstN * firstN_tdb |
| = new(space) ComTdbFirstN( |
| child_tdb, |
| getFirstNRows(), |
| firstNRowsExpr, |
| work_cri_desc, |
| given_desc, |
| returned_desc, |
| child_tdb->getMaxQueueSizeDown(), |
| child_tdb->getMaxQueueSizeUp(), |
| 1, 4096); |
| |
| generator->initTdbFields(firstN_tdb); |
| |
| if(!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(firstN_tdb, childExplainTuple, 0, generator)); |
| } |
| |
| generator->setGenObj(this, firstN_tdb); |
| |
| return 0; |
| } |
| |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // RelRoot::genSimilarityInfo() |
| // |
| ///////////////////////////////////////////////////////// |
| TrafQuerySimilarityInfo * RelRoot::genSimilarityInfo(Generator *generator) |
| { |
| ExpGenerator * exp_gen = generator->getExpGenerator(); |
| |
| NABoolean recompOnTSMismatch = FALSE; |
| NABoolean errorOnTSMismatch = FALSE; |
| |
| // generate the similarity info. |
| Space * space = generator->getSpace(); |
| |
| NABoolean disableAutoRecomp = (CmpCommon::getDefault(AUTOMATIC_RECOMPILATION) == DF_OFF); |
| |
| Queue * siList = new(space) Queue(space); |
| TrafQuerySimilarityInfo * qsi = NULL; |
| |
| if (generator->getTrafSimTableInfoList().entries() > 0) |
| qsi = new(space) TrafQuerySimilarityInfo(siList); |
| |
| CollIndex i = 0; |
| |
| for (CollIndex i = 0; i < generator->getTrafSimTableInfoList().entries(); i++) |
| { |
| TrafSimilarityTableInfo * genTsi = |
| (TrafSimilarityTableInfo *)(generator->getTrafSimTableInfoList()[i]); |
| |
| char * genTablename = |
| space->allocateAndCopyToAlignedSpace(genTsi->tableName(), str_len(genTsi->tableName()), 0); |
| char * genRootDir = |
| space->allocateAndCopyToAlignedSpace(genTsi->hdfsRootDir(), str_len(genTsi->hdfsRootDir()), 0); |
| |
| char * genHdfsHostName = |
| space->allocateAndCopyToAlignedSpace(genTsi->hdfsHostName(), str_len(genTsi->hdfsHostName()), 0); |
| |
| TrafSimilarityTableInfo * si = |
| new(space) TrafSimilarityTableInfo(genTablename, |
| genTsi->isHive(), |
| genRootDir, |
| genTsi->modTS(), |
| genTsi->numPartnLevels(), |
| NULL, |
| genHdfsHostName, |
| genTsi->hdfsPort()); |
| qsi->siList()->insert(si); |
| } |
| |
| if (qsi) |
| { |
| qsi->setDisableAutoRecomp(disableAutoRecomp); |
| } |
| |
| return qsi; |
| } |
| |
| short RelRoot::codeGen(Generator * generator) |
| { |
| ExpGenerator * exp_gen = generator->getExpGenerator(); |
| Space * space; |
| FragmentDir *compFragDir = generator->getFragmentDir(); |
| NABoolean childTdbIsNull = FALSE; |
| |
| // -- MVs |
| // Mark in the generator that we are doing an INTERNAL REFRESH command now. |
| if (isRootOfInternalRefresh()) |
| generator->setInternalRefreshStatement(); |
| |
| MapTable * map_table = generator->appendAtEnd(); |
| |
| // create the fragment (independent code space) for the master executor |
| CollIndex myFragmentId = compFragDir->pushFragment(FragmentDir::MASTER); |
| |
| // When the master executor gets the generated space, it assumes that |
| // the root node is stored at the very beginning of the space, so |
| // make sure that the master fragment is fragment 0 (the code |
| // below makes sure that the root node is at the beginning of its |
| // fragment). |
| GenAssert(myFragmentId == 0,"NOT myFragmentId == 0"); |
| |
| // now we can get a hold of the space object for this fragment |
| space = generator->getSpace(); |
| |
| // usually space for a node is generated after generating code for |
| // its children. In case of root, generate the root tdb first. |
| // This is done so that the start of the generated code space |
| // could point to the root tdb. The tdb, however, is not initialized |
| // yet. |
| ComTdbRoot *root_tdb = new(space) ComTdbRoot(); |
| generator->initTdbFields(root_tdb); |
| |
| // tell the root tdb whether we collect statistics or not |
| if (generator->computeStats()) |
| { |
| root_tdb->setCollectStats(generator->computeStats()); |
| root_tdb->setCollectStatsType(generator->collectStatsType()); |
| root_tdb->setCollectRtsStats(generator->collectRtsStats()); |
| } |
| |
| |
| |
| //For consistent display of overflow_mode in stats. |
| root_tdb->setOverflowMode(generator->getOverflowMode()); |
| |
| // set the object for the top level fragment |
| compFragDir->setTopObj((char *) root_tdb); |
| |
| // Copy the current context-wide TransMode, |
| // then overlay with this stmt's "FOR xxx ACCESS" setting, if any. |
| TransMode * transMode = new(space) TransMode(); |
| transMode->updateTransMode(generator->getTransMode()); |
| // |
| if (accessOptions().accessType() != TransMode::ACCESS_TYPE_NOT_SPECIFIED_) |
| { |
| // "FOR xxx ACCESS" becomes an IsolationLevel, and both IL and AccessMode |
| // are set in the transMode |
| transMode->updateAccessModeFromIsolationLevel( |
| TransMode::ATtoIL(accessOptions().accessType())); |
| |
| transMode->setStmtLevelAccessOptions(); |
| } |
| else if ( ( generator->currentCmpContext()->internalCompile() == CmpContext::INTERNAL_MODULENAME) || |
| ( CmpCommon::statement()->isSMDRecompile() ) |
| ) |
| { |
| // As a nicety to everyone writing a trusted .mdf (RFORK, etc), |
| // we set this flag so that cli/Statement::execute() will not |
| // recompile those trusted stmts due to any TransMode mismatch. |
| // (Otherwise, everyone would need to add "FOR xxx ACCESS" to each stmt!) |
| // |
| transMode->setStmtLevelAccessOptions(); |
| } |
| |
| ex_expr * input_expr = 0; |
| ex_expr * output_expr = 0; |
| CollIndex i; |
| |
| ex_expr * pkey_expr = NULL; |
| ULng32 pkey_len = 0; |
| |
| ex_expr* pred_expr = NULL; |
| |
| ULng32 cacheVarsSize = 0; |
| // unsigned long tablenameCacheVarsSize = 0; |
| |
| // max number of rows in user rowwise rowset. |
| Lng32 rwrsMaxSize = 0; |
| |
| // index into the user params to find the value of the number of |
| // actual rows in the rowwise rowset buffer. |
| short rwrsInputSizeIndex = 0; |
| |
| // index into the user params to find the value of the max length |
| // of each row in the user rowwise rowset buffer. |
| short rwrsMaxInputRowlenIndex = 0; |
| |
| // index into the user params to find the value of the address |
| // of rowwise rowset buffer in user space. |
| short rwrsBufferAddrIndex = 0; |
| |
| // index into user params to find the value of the partition number |
| // where this rwrs need to be shipped to. |
| short rwrsPartnNumIndex = -1; // not specified |
| |
| // length of the each internal tuple where user's row will be moved in |
| // at runtime. |
| Lng32 rwrsMaxInternalRowlen = 0; |
| |
| RWRSInfo *rwrsInfo = NULL; |
| char *rwrsInfoBuf = NULL; |
| if (getHostArraysArea() && getHostArraysArea()->getRowwiseRowset()) |
| { |
| rwrsInfo = (RWRSInfo *) new (space) char[sizeof(RWRSInfo)]; |
| |
| rwrsInfoBuf = (char*)rwrsInfo; |
| |
| rwrsMaxSize = |
| (Lng32)((ConstValue*)getHostArraysArea()->rwrsMaxSize())-> |
| getExactNumericValue(); |
| |
| NABoolean packedFormat = FALSE; |
| NABoolean compressed = FALSE; |
| NABoolean dcompressInMaster = FALSE; |
| NABoolean compressInMaster = FALSE; |
| NABoolean partnNumInBuffer = FALSE; |
| getHostArraysArea()->getBufferAttributes(packedFormat, |
| compressed, dcompressInMaster, |
| compressInMaster, |
| partnNumInBuffer); |
| rwrsInfo->setRWRSisCompressed(compressed); |
| rwrsInfo->setDcompressInMaster(dcompressInMaster); |
| rwrsInfo->setPartnNumInBuffer(partnNumInBuffer); |
| } |
| |
| // inputVars() can contain multiple references to the same |
| // param/hostvar value, if it is specified more than once in |
| // a statement. (Seems like a bug to me, but no one is around |
| // to fix it, so i will just work around it). The CharacteristicInputs |
| // do not contain duplicate references. Use them to create a non-duplicate |
| // newInputVars list. |
| ValueIdList newInputVars, cacheVars, rwrsVars; |
| NABoolean userInputVars = FALSE; |
| short entry = 1; |
| for (i = 0; i < inputVars().entries(); i++) |
| { |
| // CharacteristicInputs contains constants. Don't add |
| // them as input. Add hostvar/params to map table, if not |
| // already added. This will remove duplicates. |
| // Add the non-duplicate input val-ids to the newInputVars list. |
| ValueId val_id = inputVars()[i]; |
| ItemExpr * item_expr = val_id.getItemExpr(); |
| |
| // We create a dummy host var in case it gets a value id from an |
| // assignment in compound statements. Such variable will not need |
| // to be processed by Cli since it gets its value inside the statement |
| NABoolean blankHV = FALSE ; |
| if (item_expr->previousHostVar()) { |
| Int32 j = 0; |
| for (j = 0; j < i; j++) { |
| ItemExpr *ie = inputVars()[j].getItemExpr(); |
| if (ie->getOperatorType() == ITM_HOSTVAR) { |
| if (item_expr->previousName() == ((HostVar *) ie)->getName()) { |
| break; |
| } |
| } |
| } |
| |
| if (i == j) { |
| NAString str1 = "previousHV__"; |
| char str2[30]; |
| str_itoa(i, str2); |
| str1 += str2; |
| item_expr = new(generator->wHeap()) HostVar(str1, |
| new(generator->wHeap()) SQLUnknown(generator->wHeap())); |
| item_expr->bindNode(generator->getBindWA()); |
| blankHV = TRUE; |
| val_id = item_expr->getValueId(); |
| } |
| } |
| |
| OperatorTypeEnum op = item_expr->getOperatorType(); |
| |
| if ((op == ITM_HOSTVAR) || |
| (op == ITM_DYN_PARAM)){ |
| userInputVars = TRUE; |
| |
| // Vicz: filter out the OUT HostVar/DynamicParam |
| |
| ComColumnDirection paramMode = item_expr->getParamMode(); |
| |
| if(paramMode == COM_OUTPUT_COLUMN) |
| continue; |
| |
| } |
| |
| // the list of operator types that was present here in R1.8 code in |
| // almost equivalent to isAUserSuppliedInput() |
| // except for Constant which cannot be handled here as its atpindex |
| // should be 0 and not 2. |
| // The num_tupps local variable just outside this IF will create |
| // attributes with atpindex 2. |
| // Constants are added later to the MapTable. |
| if (((item_expr->isAUserSuppliedInput()) && // for evaluate once functions |
| (op != ITM_CONSTANT)) && |
| (! generator->getMapInfoAsIs(val_id))) // not added yet |
| { |
| MapInfo *map = generator->addMapInfoToThis(generator->getLastMapTable(), |
| val_id, NULL); |
| |
| // Transfer the information on rowsets that is in this host variable |
| // into its attribute so we know this information at run time |
| if (op == ITM_HOSTVAR || op == ITM_DYN_PARAM) |
| { |
| Attributes *attr = map->getAttr(); |
| UInt32 rowsetInfo; |
| |
| if (op == ITM_HOSTVAR) |
| { |
| HostVar *hv = (HostVar *) (val_id.getItemExpr()); |
| rowsetInfo = hv->getRowsetInfo(); |
| if (blankHV) |
| { |
| attr->setBlankHV(); |
| } |
| } |
| else // (op == ITM_DYN_PARAM) |
| { |
| DynamicParam *dp = (DynamicParam *) (val_id.getItemExpr()); |
| rowsetInfo = dp->getRowsetInfo(); |
| |
| if (dp->isDPRowsetForInputSize()) |
| rwrsInputSizeIndex = entry; |
| else if (dp->isRowwiseRowsetInputMaxRowlen()) |
| rwrsMaxInputRowlenIndex = entry; |
| else if (dp->isRowwiseRowsetInputBuffer()) |
| rwrsBufferAddrIndex = entry; |
| else if (dp->isRowwiseRowsetPartnNum()) |
| rwrsPartnNumIndex = entry; |
| } |
| |
| attr->setRowsetInfo((Int16)rowsetInfo); |
| } |
| |
| if ((op == ITM_DYN_PARAM) && |
| ((DynamicParam *)item_expr)->isRowInRowwiseRowset()) |
| { |
| rwrsVars.insert(val_id); |
| } |
| else if (op == ITM_CACHE_PARAM) |
| { |
| // This is a parameter generated by Query Caching |
| cacheVars.insert(val_id); |
| } |
| else |
| { |
| newInputVars.insert(val_id); |
| entry++; |
| } |
| } |
| } // for |
| |
| Int32 num_tupps = 2; /* atp_index 0 for constants, 1 for temps */ |
| |
| // create a row(tuple) with input param/hostvar values and pass |
| // it to the child. |
| // assign offset to elements in the input vars list. |
| // Offsets are assigned in the input row tuple (atp index = 2). |
| Attributes ** attrs = new(generator->wHeap()) |
| Attributes * [newInputVars.entries()]; |
| for (i = 0; i < newInputVars.entries(); i++) |
| { |
| attrs[i] = generator->addMapInfo(newInputVars[i], NULL)->getAttr(); |
| } |
| |
| ULng32 input_vars_size = 0; |
| exp_gen->processAttributes(newInputVars.entries(), attrs, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| input_vars_size, 0 /*atp*/, num_tupps/*atpIdx*/); |
| |
| //++Triggers, |
| // Save the offsets of triggerStatus and UniqueExecuteId, |
| // so ex_root_tcb can set it's value |
| Lng32 triggersStatusOffset = -1; |
| Lng32 uniqueExecuteIdOffset = -1; |
| |
| for (i = 0; i < newInputVars.entries(); i++) |
| { |
| ItemExpr * item_expr = (newInputVars)[i].getItemExpr(); |
| if (item_expr->getOperatorType() == ITM_UNIQUE_EXECUTE_ID) |
| uniqueExecuteIdOffset = attrs[i]->getOffset(); |
| if (item_expr->getOperatorType() == ITM_GET_TRIGGERS_STATUS) |
| { |
| GenAssert(getTriggersList()->entries()>0, |
| "No triggers, yet TriggerStatusOffset != -1"); |
| triggersStatusOffset = attrs[i]->getOffset(); |
| } |
| } |
| |
| //--Triggers, |
| |
| num_tupps += ((newInputVars.entries() > 0) ? 1 : 0); // plus 1 to hold the input |
| // params and hostvars. |
| |
| if (updateCurrentOf()) |
| { |
| GenAssert(pkeyList().entries() > 0, "pkeyList().entries() must be > 0"); |
| |
| // create a row(tuple) with pkey hostvar values and pass |
| // it to the child. |
| // assign offset to elements in the pkeyList. |
| // Offset is assigned at atp index which one greater than where |
| // the input num_tupps is. |
| Attributes ** attrs = new(generator->wHeap()) |
| Attributes * [pkeyList().entries()]; |
| for (i = 0; i < pkeyList().entries(); i++) |
| { |
| attrs[i] = generator->addMapInfo(pkeyList()[i], NULL)->getAttr(); |
| } |
| |
| exp_gen->processAttributes(pkeyList().entries(), attrs, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| pkey_len,0/*atp*/,num_tupps/*atpIdx*/); |
| num_tupps += 1; |
| } |
| |
| // Process expressions generated by Query Caching. |
| Attributes ** cachedAttrs = NULL; |
| if (cacheVars.entries() > 0) { |
| cachedAttrs = new(generator->wHeap()) |
| Attributes * [cacheVars.entries()]; |
| for (i = 0; i < cacheVars.entries(); i++) |
| { |
| cachedAttrs[i] = generator->addMapInfo(cacheVars[i], NULL)->getAttr(); |
| } |
| |
| exp_gen->processAttributes(cacheVars.entries(), cachedAttrs, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| cacheVarsSize,0/*atp*/,num_tupps/*atpIdx*/); |
| num_tupps += 1; |
| } |
| |
| // Space needed for query caching. This is where the values of ConstantParameters |
| // will go |
| // char *queryCacheParameterBuffer = (char *) new (space) char[cacheVarsSize]; |
| NABoolean qCacheInfoIsClass = FALSE; |
| if (CmpCommon::getDefault(QUERY_CACHE_RUNTIME) == DF_ON) |
| qCacheInfoIsClass = TRUE; |
| |
| char *parameterBuffer = (char *) new (space) char[cacheVarsSize]; |
| char *qCacheInfoBuf = NULL; |
| if (qCacheInfoIsClass) |
| { |
| QCacheInfo *qCacheInfo = |
| (QCacheInfo *) new (space) char[sizeof(QCacheInfo)]; |
| qCacheInfo->setParameterBuffer(parameterBuffer); |
| qCacheInfoBuf = (char*)qCacheInfo; |
| } |
| else |
| qCacheInfoBuf = parameterBuffer; |
| |
| // Check for reasons why the query plan should not be cached. |
| // Note: This does not influence the use of cache parameters, |
| // it's too late at this time to undo that. |
| const LIST(CSEInfo *) *cseInfoList = CmpCommon::statement()->getCSEInfoList(); |
| |
| if (cseInfoList && |
| CmpCommon::getDefault(CSE_CACHE_TEMP_QUERIES) == DF_OFF) |
| for (CollIndex i=0; i<cseInfoList->entries(); i++) |
| if (cseInfoList->at(i)->usesATempTable()) |
| generator->setNonCacheableCSEPlan(TRUE); |
| |
| // compute offsets for rwrs attrs. Offsets are computed separately |
| // for rwrs vars since these values will be moved as part of input |
| // row at runtime. This input row should only contain values which are |
| // being inserted (ex, in the VALUES clause) and not any other input |
| // values (like, input size, buffer, etc). |
| // If rwrs vars was included in newInputVars before computing |
| // the offsets, then these offsets will also include the non-rwrs |
| // vars which will not be correct. |
| // |
| // Do not assign any atp index at this time. |
| // atp index will be determined at runtime |
| // when the actual rows that are extracted from the rowset, |
| // processed and moved up the queue. |
| Attributes ** rwrsAttrs = NULL; |
| |
| // next var is used if buffer need to be decompressed using the unicode |
| // decoding alogorithm. |
| NABoolean useUnicodeDcompress = FALSE; |
| |
| if (rwrsVars.entries() > 0) |
| { |
| rwrsAttrs = new(generator->wHeap()) |
| Attributes * [rwrsVars.entries()]; |
| for (i = 0; i < rwrsVars.entries(); i++) |
| { |
| rwrsAttrs[i] = generator->addMapInfo(rwrsVars[i], NULL)->getAttr(); |
| |
| if (rwrsAttrs[i]->getCharSet() != CharInfo::ISO88591) |
| useUnicodeDcompress = TRUE; |
| } |
| |
| // assign offsets. |
| // No real atp index is to be assigned. |
| // Cannot make it -1 as processAttrs doesn't like that. |
| // Make atp index to be SHRT_MAX (out of reach). |
| ULng32 len; |
| exp_gen->processAttributes(rwrsVars.entries(), rwrsAttrs, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| len, |
| 0/*atp*/, SHRT_MAX/*atpIdx*/); |
| rwrsMaxInternalRowlen = len; |
| } |
| |
| if (rwrsInfo) |
| { |
| rwrsInfo->setRwrsInfo(rwrsMaxSize, rwrsInputSizeIndex, |
| rwrsMaxInputRowlenIndex, rwrsBufferAddrIndex, |
| rwrsPartnNumIndex, |
| rwrsMaxInternalRowlen); |
| |
| if (rwrsInfo->rwrsIsCompressed()) |
| rwrsInfo->setUseUnicodeDcompress(useUnicodeDcompress); |
| } |
| |
| // generate the input expression to move in hostvar/param values |
| // from user area. |
| // Include rwrs in newInputVars before generating the input expr |
| // since we want all of these vars to be returned to the used when |
| // they are 'described' at runtime. |
| if ((newInputVars.entries() > 0) || |
| (rwrsVars.entries() > 0)) |
| { |
| newInputVars.insert(rwrsVars); |
| exp_gen->generateInputExpr(newInputVars, ex_expr::exp_INPUT_OUTPUT, |
| &input_expr); |
| } |
| |
| ex_cri_desc * cri_desc = new(space) ex_cri_desc(num_tupps, space); |
| generator->setCriDesc(cri_desc, Generator::DOWN); |
| generator->setInputExpr((void *)input_expr); |
| |
| ExplainDesc *explainDesc = NULL; |
| |
| if(!generator->explainDisabled()) |
| { |
| // Create a space object for the explain fragment |
| if(generator->getExplainFragDirIndex() == NULL_COLL_INDEX) |
| { |
| // Create an Explain Fragment |
| generator->setExplainFragDirIndex( |
| generator->getFragmentDir()->pushFragment(FragmentDir::EXPLAIN,0)); |
| generator->getFragmentDir()->popFragment(); |
| } |
| |
| ExplainFunc explainFunc; |
| TrafDesc *explainDescStruct = explainFunc.createVirtualTableDesc(); |
| |
| Space *explainSpace = generator->getFragmentDir()-> |
| getSpace(generator->getExplainFragDirIndex()); |
| |
| TrafTableDesc *tableDesc = explainDescStruct->tableDesc(); |
| |
| // Determine the length of the Explain Tuple. |
| Lng32 recLength = tableDesc->record_length; |
| |
| // Determine the number of columns in the Explain Tuple. |
| Lng32 numCols = tableDesc->colcount; |
| |
| explainDesc = |
| new(explainSpace) ExplainDesc(numCols, recLength, explainSpace); |
| |
| TrafDesc *cols = tableDesc->columns_desc; |
| |
| // For each column of the Virtual Explain Table, extract the |
| // relevant info. from the table desc and put it into the ExplainDesc |
| for(Int32 c = 0; c < numCols; c++ /* no pun intended */) |
| { |
| TrafColumnsDesc *colsDesc = (cols->columnsDesc()); |
| |
| explainDesc->setColDescr(c, |
| colsDesc->datatype, |
| colsDesc->length, |
| colsDesc->offset, |
| colsDesc->isNullable()); |
| |
| cols = cols->next; |
| } |
| |
| explainFunc.deleteVirtualTableDesc(explainDescStruct); |
| |
| compFragDir->setTopObj(generator->getExplainFragDirIndex(), |
| (char *)explainDesc); |
| } |
| |
| // Take note of whether this is a parallel extract query before |
| // generating the child tree. |
| NABoolean isExtractProducer = (numExtractStreams_ > 0 ? TRUE : FALSE); |
| NABoolean isExtractConsumer = |
| (childOperType() == REL_EXTRACT_SOURCE ? TRUE : FALSE); |
| |
| // the tree below needs to know if this is a LRU operation, hence |
| // make this check before the children are codeGened. |
| if (containsLRU()) |
| { |
| generator->setLRUOperation(TRUE); |
| } |
| if (getTolerateNonFatalError() == RelExpr::NOT_ATOMIC_) |
| { |
| generator->setTolerateNonFatalError(TRUE); |
| } |
| |
| // Copy #BMOs value from Root node into the fragment |
| compFragDir->setNumBMOs(myFragmentId, getNumBMOs()); |
| compFragDir->setBMOsMemoryUsage(myFragmentId, getBMOsMemoryUsage().value()); |
| |
| // generate child tree |
| child(0)->codeGen(generator); |
| ComTdb * child_tdb = (ComTdb *)(generator->getGenObj()); |
| if (child_tdb == (ComTdb *)NULL) |
| childTdbIsNull = TRUE; |
| |
| |
| // Remap the allocation of ESPs to Nodes/CPUs. |
| if (ActiveSchemaDB()->getDefaults().getAsLong(AFFINITY_VALUE) == -2) |
| generator->remapESPAllocationRandomly(); |
| else |
| generator->remapESPAllocationAS(); |
| |
| generator->compilerStatsInfo().affinityNumber() |
| = generator->getAffinityValueUsed(); |
| |
| // if an output expression is present, generate it. |
| if (compExpr_.entries() > 0) |
| { |
| // Special cases to consider are |
| // * stored procedure result sets |
| // * parallel extract consumers |
| // |
| // In these plans we want special table and column names in the |
| // output expression. The names will come from the root's child |
| // node and be pointed to by these two variables. For all other |
| // ("normal") statements, these two pointers will remain NULL. |
| // |
| ConstNAStringPtr *colNamesForExpr = NULL; |
| ConstQualifiedNamePtr *tblNamesForExpr = NULL; |
| |
| OperatorTypeEnum childType = |
| child(0)->castToRelExpr()->getOperatorType(); |
| |
| ComUInt32 numColumns = getRETDesc()->getDegree(); |
| |
| if ((childType == REL_SP_PROXY || isExtractConsumer) && |
| numColumns > 0) |
| { |
| ProxyFunc *proxy; |
| |
| if (childType == REL_SP_PROXY) |
| { |
| // This is a stored procedure result set |
| proxy = (ProxyFunc *) child(0)->castToRelExpr(); |
| } |
| else |
| { |
| // This is an extract consumer. The extract operator is not |
| // the direct child of the root. An exchange operator sits |
| // between the two. |
| GenAssert(childType == REL_EXCHANGE, |
| "Child of root should be exchange for consumer query"); |
| GenAssert(child(0)->child(0), |
| "Child of root should not be a leaf for consumer query"); |
| |
| OperatorTypeEnum grandChildType = |
| child(0)->child(0)->castToRelExpr()->getOperatorType(); |
| |
| GenAssert(grandChildType == REL_EXTRACT_SOURCE, |
| "Grandchild of root has unexpected type for consumer query"); |
| |
| proxy = (ProxyFunc *) child(0)->child(0)->castToRelExpr(); |
| } |
| |
| // Populate the table and column name collections that will be |
| // used below to generate the output expression. |
| colNamesForExpr = new (generator->wHeap()) |
| ConstNAStringPtr[numColumns]; |
| tblNamesForExpr = new (generator->wHeap()) |
| ConstQualifiedNamePtr[numColumns]; |
| |
| for (ComUInt32 i = 0; i < numColumns; i++) |
| { |
| colNamesForExpr[i] = proxy->getColumnNameForDescriptor(i); |
| tblNamesForExpr[i] = proxy->getTableNameForDescriptor(i); |
| } |
| } |
| |
| exp_gen->generateOutputExpr(compExpr_, |
| ex_expr::exp_INPUT_OUTPUT, |
| &output_expr, |
| getRETDesc(), |
| getSpOutParams(), |
| colNamesForExpr, |
| tblNamesForExpr); |
| } |
| |
| if (getPredExprTree()) |
| { |
| // ItemExpr * newPredTree = executorPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| exp_gen->generateExpr(getPredExprTree()->getValueId(), ex_expr::exp_SCAN_PRED, |
| &pred_expr); |
| } |
| |
| // if child's primary key columns are to be returned to be passed |
| // on to UPDATE WHERE CURRENT OF query, generate an |
| // expression to compute the pkey row. |
| ex_cri_desc * work_cri_desc = NULL; |
| |
| if (updatableSelect() == TRUE) |
| { |
| GenAssert(pkeyList().entries() > 0, "pkeyList().entries() must be > 0"); |
| |
| work_cri_desc = new(space) ex_cri_desc(3, space); |
| |
| exp_gen->generateContiguousMoveExpr(pkeyList(), |
| 1, // add convert nodes, |
| 1, 2, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| pkey_len, &pkey_expr, |
| NULL, ExpTupleDesc::SHORT_FORMAT); |
| } |
| |
| // if this is an 'update where current of' query, pass in |
| // the fetched cursor name or hvar number root tdb. |
| char * fetchedCursorName = NULL; |
| short fetchedCursorHvar = -1; |
| if (updateCurrentOf()) |
| { |
| if (currOfCursorName_->getOperatorType() == ITM_CONSTANT) |
| { |
| // cursor name is specified as a literal |
| NABoolean tv; |
| ConstValue * cv = currOfCursorName_->castToConstValue(tv); |
| |
| fetchedCursorName = |
| space->allocateAndCopyToAlignedSpace((char *)(cv->getConstValue()), |
| cv->getStorageSize(), |
| 0); |
| } |
| else |
| { |
| // cursor name was specified as a hvar |
| HostVar * cursorHvar = (HostVar *)currOfCursorName_; |
| |
| // search for this hostvar in the input hvar list |
| for (i = 0; i < inputVars().entries(); i++) |
| { |
| ValueId val_id = inputVars()[i]; |
| if (cursorHvar->getOperatorType() == ITM_HOSTVAR) |
| { |
| HostVar * hv = (HostVar *)(val_id.getItemExpr()); |
| |
| if (hv->getName() == cursorHvar->getName()) |
| fetchedCursorHvar = (short)i+1; // 1-based |
| } |
| } // more input |
| |
| } // cursor name is in a hvar |
| } |
| |
| // Create a list of update columns for UPDATE CURRENT OF or updateable |
| // SELECT statements (cursors). For UPDATE CURRENT OF, the update columns |
| // are obtained from the GenericUpate Node. For cursors, the update columns |
| // are obtained from this (RelRoot) node. |
| Lng32 numUpdateCol = 0; |
| Lng32 *updateColList = NULL; |
| |
| if (updateCurrentOf()) // UPDATE/DELETE ... where CURRENT OF query. |
| { |
| GenAssert(updateCol().entries() == 0, |
| "UPDATE CURRENT OF: updateCol non-zero"); |
| |
| // Get the update node. |
| GenericUpdate *update = (GenericUpdate*)generator->updateCurrentOfRel(); |
| |
| GenAssert(update != NULL, "UPDATE CURRENT OF: NULL update node"); |
| |
| /* |
| // create the update col list for UPDATE...WHERE CURRENT OF CURSOR only. |
| // The updateCurrentOf() is set for both UPDATE and DELETE queries. |
| if ((update->getOperatorType() == REL_DP2_UPDATE_CURSOR) || |
| (update->getOperatorType() == REL_DP2_UPDATE_UNIQUE)) |
| { |
| // Get the list of assignment expressions from the STOI of the |
| // update node. |
| SqlTableOpenInfo *updateStoi = update->getOptStoi()->getStoi(); |
| |
| // Allocate an array for the column list. |
| numUpdateCol = updateStoi->getColumnListCount(); |
| |
| GenAssert(numUpdateCol > 0, |
| "UPDATE CURRENT OF: No update columns"); |
| |
| updateColList = new(space) Lng32[numUpdateCol]; |
| |
| // Populate the array with the update columns from the left side |
| // of each expression which is a column. |
| for (i = 0; (Lng32)i < numUpdateCol; i++) |
| { |
| updateColList[i] = updateStoi->getUpdateColumn(i); |
| } |
| } // update...where current of. |
| */ |
| } |
| else if (updatableSelect()) |
| { |
| numUpdateCol = updateCol().entries(); |
| |
| if (numUpdateCol > 0) |
| { |
| // Allocate an array for the column list. |
| updateColList = new(space) Lng32[numUpdateCol]; |
| |
| // Populate the array with the update columns of this node. |
| for (i = 0; (Lng32)i < numUpdateCol; i++) |
| { |
| ValueId val_id = updateCol()[i]; |
| GenAssert(val_id.getItemExpr()->getOperatorType() == ITM_BASECOLUMN, |
| "UpdateCol should be BaseColumn"); |
| BaseColumn *col = (BaseColumn*)val_id.getItemExpr(); |
| updateColList[i] = col->getColNumber(); |
| } |
| } |
| else |
| { |
| // All columns are updateable. |
| numUpdateCol = -1; |
| } |
| } |
| |
| // copy all the tables open information into the generator space |
| // and pass it to the root tdb, which is used to check the |
| // security when opening the sql tables or views. |
| short noOfTables = (short) generator->getSqlTableOpenInfoList().entries(); |
| SqlTableOpenInfo **stoiList; |
| stoiList = new (space) SqlTableOpenInfo*[noOfTables]; |
| |
| // The Executor Statement class has logic to retry blown away |
| // opens on some statements. We can safely do so if we know that |
| // disk has not been dirtied, and no rows have been returned |
| // to the application. |
| |
| // The Executor can deduce this if the blown away open occurs |
| // on the first input row, and if the plan writes to at most |
| // one object. This follows because an open can be blown away |
| // only if no locks are held on any partition of the object. |
| |
| // The two variables below are used to compute if the plan |
| // accesses more than one object. If not, and if the query is |
| // an IUD, we will flag the root tdb as retryable. |
| |
| NABoolean moreThanOneTable = FALSE; |
| char *firstTable = NULL; |
| |
| // ++ Triggers |
| // While copying the stoi info, make sure the subjectTable flag is |
| // not set more than once for the same table ansi name |
| LIST(NAString) *subjectTables = NULL; |
| if (getTriggersList()) |
| subjectTables = new (generator->wHeap()) |
| LIST(NAString) (generator->wHeap()); |
| short j=0; |
| |
| for (; j < noOfTables; j++) |
| { |
| stoiList[j] = new (space) SqlTableOpenInfo; |
| SqlTableOpenInfo * genStoi = |
| (SqlTableOpenInfo *)generator->getSqlTableOpenInfoList()[j]; |
| *(stoiList[j]) = *genStoi; |
| |
| if (moreThanOneTable) |
| { |
| // No need to check any further. |
| } |
| else |
| { |
| if (firstTable == NULL) |
| { |
| firstTable = genStoi->fileName(); |
| } |
| else if (stricmp( firstTable, genStoi->fileName())) |
| { |
| // there is more than one distinct object name in the Stoi list |
| moreThanOneTable = TRUE; |
| } |
| } |
| stoiList[j]->setFileName(new (space) char[strlen(genStoi->fileName()) + 1]); |
| strcpy(stoiList[j]->fileName(), genStoi->fileName()); |
| |
| stoiList[j]->setAnsiName(new (space) char[strlen(genStoi->ansiName()) + 1]); |
| strcpy(stoiList[j]->nonConstAnsiName(), genStoi->ansiName()); |
| |
| // -- Triggers |
| // Prevent duplicate entries for the same subject table |
| // and entries for views |
| NAString const ansiName(genStoi->ansiName(), generator->wHeap()); |
| if (genStoi->subjectTable() && subjectTables && |
| !(subjectTables->contains(ansiName)) && !genStoi->isView()) |
| { |
| stoiList[j]->setSubjectTable(TRUE); |
| subjectTables->insert(ansiName); |
| } |
| else |
| stoiList[j]->setSubjectTable(FALSE); |
| |
| if (genStoi->getColumnListCount()) |
| { |
| stoiList[j]->setColumnList(new (space) |
| short[genStoi->getColumnListCount()]); |
| for (short k = 0; k < genStoi->getColumnListCount(); k++) |
| { |
| stoiList[j]->setUpdateColumn(k,genStoi->getUpdateColumn(k)); |
| } |
| } |
| } |
| |
| // copy the triggers list into the generator space |
| // and pass it to the root tdb, where it is used to check the |
| // enable/disable status of triggers. |
| short triggersCount = 0; |
| ComTimestamp *triggersList = NULL; |
| if (getTriggersList()) |
| { |
| GenAssert(subjectTables && (subjectTables->entries() > 0), |
| "Mismatch: Triggers without Subject Tables"); |
| delete subjectTables; |
| subjectTables = NULL; |
| |
| triggersCount = (short) getTriggersList()->entries(); |
| triggersList = new (space) ComTimestamp[triggersCount]; |
| |
| for (short k=0; k < triggersCount; k++) |
| triggersList[k] = getTriggersList()->at(k); |
| } |
| |
| // copy uninitializedMvList into generator space |
| // to pass to root tdb |
| UninitializedMvName *newMvList = NULL; |
| short uninitializedMvCount = 0; |
| |
| if (uninitializedMvList_) |
| { |
| uninitializedMvCount = (short)uninitializedMvList_->entries(); |
| if( uninitializedMvCount != 0 ) |
| { |
| newMvList = new (space) UninitializedMvName[uninitializedMvCount]; |
| for( short i = 0; i < uninitializedMvCount; i++ ) |
| { |
| UninitializedMvName *pMvName = uninitializedMvList_->at(i); |
| |
| GenAssert( pMvName, "UninitializedMvName is invalid." ); |
| |
| newMvList[i].setPhysicalName( pMvName->getPhysicalName() ); |
| newMvList[i].setAnsiName( pMvName->getAnsiName() ); |
| } |
| } |
| } |
| |
| |
| // if there were any views referenced in the query, copy the stoi |
| // to root tdb. This is used at runtime to check for existence. |
| Queue * viewStoiList = NULL; |
| if (getViewStoiList().entries() > 0) |
| { |
| for (CollIndex i = 0; i < getViewStoiList().entries(); i++) |
| { |
| |
| if (! viewStoiList) |
| viewStoiList = new(space) Queue(space); |
| |
| SqlTableOpenInfo * stoi = new(space) SqlTableOpenInfo; |
| *stoi = *getViewStoiList()[i]->getStoi(); |
| |
| stoi->setFileName( |
| new(space) char[strlen(getViewStoiList()[i]->getStoi()->fileName()) + 1]); |
| strcpy(stoi->fileName(), getViewStoiList()[i]->getStoi()->fileName()); |
| |
| stoi->setAnsiName( |
| new(space) char[strlen(getViewStoiList()[i]->getStoi()->ansiName()) + 1]); |
| strcpy(stoi->nonConstAnsiName(), getViewStoiList()[i]->getStoi()->ansiName()); |
| |
| if (getViewStoiList()[i]->getStoi()->getColumnListCount()) |
| { |
| stoi->setColumnList(new (space) |
| short[getViewStoiList()[i]->getStoi()->getColumnListCount()]); |
| for (short k = 0; k < getViewStoiList()[i]->getStoi()->getColumnListCount(); k++) |
| { |
| stoi->setUpdateColumn(k,getViewStoiList()[i]->getStoi()->getUpdateColumn(k)); |
| } |
| } |
| |
| if (CmpCommon::getDefault(VALIDATE_VIEWS_AT_OPEN_TIME) == DF_ON) |
| stoi->setValidateViewsAtOpenTime(TRUE); |
| else |
| stoi->setValidateViewsAtOpenTime(FALSE); |
| |
| viewStoiList->insert(stoi); |
| |
| // if this view name was used as a variable(hvar, envvar or define), |
| // then add it to the latename info list. |
| HostVar * hv = getViewStoiList()[i]->getCorrName().getPrototype(); |
| if (hv != NULL) |
| { |
| LateNameInfo* lateNameInfo = new(generator->wHeap()) LateNameInfo(); |
| |
| char * varName; |
| GenAssert(hv->getName().data(), "Hostvar pointer must have name"); |
| |
| lateNameInfo->setEnvVar(hv->isEnvVar()); |
| |
| lateNameInfo->setCachedParam(hv->isCachedParam()); |
| |
| varName = convertNAString(hv->getName(), generator->wHeap()); |
| strcpy(lateNameInfo->variableName(), varName); |
| |
| char * prototypeValue = convertNAString(hv->getPrototypeValue(), |
| generator->wHeap()); |
| char * compileTimeAnsiName = prototypeValue; |
| lateNameInfo->setVariable(1); |
| |
| lateNameInfo->setView(1); |
| |
| lateNameInfo->setCompileTimeName(compileTimeAnsiName, space); |
| lateNameInfo->setLastUsedName(compileTimeAnsiName, space); |
| lateNameInfo->setNameSpace(COM_TABLE_NAME); |
| |
| lateNameInfo->setInputListIndex(-1); |
| |
| generator->addLateNameInfo(lateNameInfo); |
| |
| } // hv |
| } // for |
| } |
| |
| // UDR Security |
| short noOfUdrs = generator->getBindWA()->getUdrStoiList().entries (); |
| |
| SqlTableOpenInfo **udrStoiList = NULL; |
| if ( noOfUdrs ) |
| { |
| udrStoiList = new (space) SqlTableOpenInfo*[noOfUdrs]; |
| BindWA *bindWA = generator->getBindWA (); |
| |
| for (short udrIdx=0; udrIdx < noOfUdrs; udrIdx++) |
| { |
| udrStoiList[udrIdx] = new (space) SqlTableOpenInfo; |
| SqlTableOpenInfo *genUdrStoi = |
| (SqlTableOpenInfo *)bindWA->getUdrStoiList()[udrIdx]->getUdrStoi(); |
| |
| *(udrStoiList[udrIdx]) = *genUdrStoi; |
| |
| udrStoiList[udrIdx]->setAnsiName( |
| new (space) char[strlen(genUdrStoi->ansiName()) + 1] |
| ); |
| strcpy(udrStoiList[udrIdx]->nonConstAnsiName(), genUdrStoi->ansiName()); |
| } |
| } |
| |
| // setting transaction type flags in the transmode object |
| // Determines what type of transaction will be started for this statement, |
| // if autocommit is ON. |
| |
| // setting accessMode to read_write if it was set to read_only by MX. |
| // if isolation_level is read_uncommitted we set accessmode to read only |
| // This causes trouble when we try to do DDL or IUD so we are resetting |
| // accessMode here. From here on accesMode is used only to start the transaction |
| if ((transMode->accessMode() == TransMode::READ_ONLY_) && |
| (generator->needsReadWriteTransaction())) |
| transMode->accessMode() = TransMode::READ_WRITE_ ; |
| |
| |
| if (generator->withNoRollbackUsed() || |
| (transMode->getRollbackMode() == TransMode::NO_ROLLBACK_)) |
| { |
| if (generator->withNoRollbackUsed()) |
| transMode->rollbackMode() = TransMode::NO_ROLLBACK_IN_IUD_STATEMENT_ ; |
| |
| // if (childOperType().match(REL_ANY_GEN_UPDATE)) |
| // generator->setAqrEnabled(FALSE); |
| |
| // AIInterval is set to don't abort (i.e. 0). |
| // A setting of -2 is equivalent to a setting of 0 but has the additional meaning |
| // that it will never be overriden by the executor. |
| if (transMode->getAutoAbortIntervalInSeconds() == -1) |
| transMode->autoAbortIntervalInSeconds() = -2; |
| } |
| else if ((NOT generator->needsReadWriteTransaction()) && |
| transMode->accessMode() != TransMode::READ_ONLY_SPECIFIED_BY_USER_ && |
| containsLRU() == FALSE && |
| updatableSelect() == FALSE) |
| { |
| transMode->accessMode() = TransMode::READ_ONLY_ ; |
| } |
| |
| if (transMode->getAutoAbortIntervalInSeconds() == -1) |
| { |
| if (transMode->accessMode() == TransMode::READ_ONLY_SPECIFIED_BY_USER_) |
| transMode->autoAbortIntervalInSeconds() = -2; |
| else if (transMode->accessMode() == TransMode::READ_ONLY_) |
| transMode->autoAbortIntervalInSeconds() = 0; |
| } |
| |
| // create the latename info List to be passed on to root_tdb. |
| LateNameInfoList * lnil = NULL; |
| Int32 numEntries = 0; |
| if (generator->getLateNameInfoList().entries() > 0) |
| numEntries = generator->getLateNameInfoList().entries(); |
| lnil = |
| (LateNameInfoList *) |
| space->allocateMemory( sizeof(LateNameInfoList) + |
| (numEntries * sizeof(LateNameInfo)) ); |
| |
| // Initialize LNIL from real LNIL object (this copies vtblptr into lnil). |
| LateNameInfoList lnild; |
| memcpy((char *)lnil,&lnild,sizeof(LateNameInfoList)); |
| |
| lnil->setNumEntries(generator->getLateNameInfoList().entries()); |
| |
| // This allocates an array of 64-bit pointers in lnil. |
| lnil->allocateList(space,numEntries); |
| |
| // This sets up the array elements to point to the LateNameInfo objects. |
| for (j = 0; j < numEntries; j++) |
| lnil->setLateNameInfo(j,((LateNameInfo *)(lnil + 1)) + j); |
| |
| NABoolean viewPresent = FALSE; |
| NABoolean variablePresent = FALSE; |
| |
| // olt opt is only done for tablenames which are literals |
| NABoolean doTablenameOltOpt = TRUE; |
| if (generator->getLateNameInfoList().entries() > 0) |
| { |
| for (CollIndex i = 0; |
| i < generator->getLateNameInfoList().entries(); i++) |
| { |
| LateNameInfo * tgt = &(lnil->getLateNameInfo(i)); |
| LateNameInfo * src = (LateNameInfo *)generator->getLateNameInfoList()[i]; |
| if (src->isVariable()) |
| { |
| doTablenameOltOpt = FALSE; |
| } |
| |
| // *tgt = *src wouldn't work since it doesn't copy over the vtblptr. |
| memmove(tgt,src,sizeof(LateNameInfo)); |
| // find the position of this hostvar in input var list. |
| if ((src->isVariable()) && (! src->isEnvVar())) |
| { |
| if (tgt->isCachedParam()) |
| { |
| tgt->setCachedParamOffset((Lng32)cachedAttrs[tgt->getInputListIndex()-1]->getOffset()); |
| } |
| else |
| { |
| NABoolean found = FALSE; |
| for (CollIndex i = 0; ((i < newInputVars.entries()) && (! found)); i++) |
| { |
| ValueId val_id = newInputVars[i]; |
| ItemExpr * item_expr = val_id.getItemExpr(); |
| if (item_expr->getOperatorType() == ITM_HOSTVAR) |
| { |
| HostVar * inputHV = (HostVar *)item_expr; |
| if ((inputHV->getName().length() == strlen(src->variableName())) && |
| (strcmp(inputHV->getName().data(), src->variableName()) == 0)) |
| { |
| found = TRUE; |
| tgt->setInputListIndex((short)(i+1)); |
| } |
| } // hostvar in input list |
| } // for |
| |
| if (! found) |
| GenAssert(0, "Must find prototype hvar in input hvar"); |
| } |
| } // not an env var or a define. |
| |
| if (tgt->compileTimeAnsiName()[0] == '\\') |
| { |
| if (NOT tgt->isVariable()) |
| { |
| tgt->setAnsiPhySame(TRUE); |
| } |
| else |
| { |
| if (tgt->isEnvVar()) |
| tgt->setAnsiPhySame(TRUE); |
| else |
| { |
| // hostvar |
| // If prototype is a fully qualified name, then |
| // ansi-phy names are the same. |
| if (tgt->compileTimeAnsiName()[0] == '\\') |
| tgt->setAnsiPhySame(TRUE); |
| }; |
| } |
| } |
| else |
| { |
| if (tgt->isVariable()) |
| { |
| QualifiedName qn(tgt->compileTimeAnsiName(), 1, |
| generator->wHeap(), |
| generator->getBindWA()); |
| qn.applyDefaults(generator->currentCmpContext()->schemaDB_->getDefaultSchema()); |
| char * compileTimeAnsiName = space->AllocateAndCopyToAlignedSpace( |
| qn.getQualifiedNameAsAnsiString(), 0); |
| tgt->setCompileTimeName(compileTimeAnsiName, space); |
| } |
| } // else |
| |
| if (tgt->isView()) |
| viewPresent = TRUE; |
| |
| if (tgt->isVariable()) |
| variablePresent = TRUE; |
| |
| if (tgt->lastUsedAnsiName()[0] != '\0') |
| { |
| // VO, Metadata Indexes |
| if (tgt->getNameSpace() == COM_INDEX_NAME) |
| // This lni is for an index - don't copy the compile time ansi name if the |
| // query is from a system module |
| if ( (generator->currentCmpContext()->internalCompile() != CmpContext::INTERNAL_MODULENAME) && |
| !CmpCommon::statement()->isSMDRecompile() ) |
| tgt->setLastUsedName(tgt->compileTimeAnsiName(),space); |
| } |
| |
| // Special handling for case where we are recompiling a system module |
| // query. We need to resolve the name here since it will not go |
| // through the resolveNames in the CLI. Do this only for NSK |
| } // for |
| } |
| |
| lnil->setViewPresent(viewPresent); |
| |
| lnil->setVariablePresent(variablePresent); |
| |
| // Generate info to do similarity check. |
| TrafQuerySimilarityInfo * qsi = genSimilarityInfo(generator); |
| |
| // generate the executor fragment directory <exFragDir> (list of all |
| // fragments of the plan that are executed locally or are downloaded |
| // to DP2 or to ESPs) from the generator's copy <compFragDir> and attach |
| // it to the root_tdb |
| NABoolean fragmentQuotas = CmpCommon::getDefault(ESP_MULTI_FRAGMENTS) == DF_ON; |
| ExFragDir *exFragDir = |
| new(space) ExFragDir(compFragDir->entries(),space, |
| CmpCommon::getDefault(ESP_MULTI_FRAGMENTS) == DF_ON, |
| fragmentQuotas, |
| (UInt16)CmpCommon::getDefaultLong(ESP_MULTI_FRAGMENT_QUOTA_VM), |
| (UInt8)CmpCommon::getDefaultLong(ESP_NUM_FRAGMENTS)); |
| |
| // We compute the space needed in execution time for input Rowset variables |
| for (i = 0; i < inputVars().entries(); i++) |
| { |
| ValueId val_id = inputVars()[i]; |
| ItemExpr * item_expr = val_id.getItemExpr(); |
| OperatorTypeEnum op = item_expr->getOperatorType(); |
| if (op == ITM_HOSTVAR) { |
| HostVar *hostVar = (HostVar *) item_expr; |
| if (hostVar->getType()->getTypeQualifier() == NA_ROWSET_TYPE) { |
| Lng32 thisSize = hostVar->getType()->getTotalSize(); |
| input_vars_size += thisSize; |
| } |
| } |
| } |
| |
| // find out if this is a delete where current of query. |
| NABoolean delCurrOf = FALSE; |
| short baseTablenamePosition = -1; |
| if (fetchedCursorName || (fetchedCursorHvar >= 0)) // upd/del curr of |
| { |
| if (childOperType() == REL_UNARY_DELETE) |
| delCurrOf = TRUE; |
| else |
| delCurrOf = FALSE; |
| } |
| |
| // The ansi names of the table specified in the cursor stmt must match |
| // the name specified in the upd/del where curr of stmt. This check |
| // is done at runtime. |
| // basetablenameposition is the index in the latenameinfolist of |
| // the entry whose lastUsedAnsiName contains the name of the table. |
| baseTablenamePosition = -1; |
| if (updatableSelect()) |
| { |
| // if this is an updatable select, find the ansi name of the basetable. |
| // This name will be used at runtime to compare to the tablename |
| // specified in an 'upd/del where current of' stmt. The two tablenames |
| // must be the same. |
| for (Int32 n = 0; n < noOfTables; n++) |
| { |
| SqlTableOpenInfo * stoi = stoiList[n]; |
| |
| if (NOT stoi->isIndex()) |
| { |
| if (baseTablenamePosition == -1) |
| { |
| baseTablenamePosition = n; |
| } |
| } |
| } |
| if (baseTablenamePosition == -1) |
| { |
| // no base table access used. Only index access is used. |
| // The ansiname field in latenameInfo struct is the ansi name |
| // of the base table. Use that. |
| baseTablenamePosition = 0; |
| } |
| } |
| else if (updateCurrentOf()) |
| { |
| // if this is an update/delete current of query, find the index of |
| // the base table. There might be other tables used in the plan |
| // for index maintanence and they will either be indices or |
| // specialTables with the special type being an INDEX_TABLE. |
| // Look only for the true base tables. |
| for (Int32 n = 0; n < noOfTables; n++) |
| { |
| SqlTableOpenInfo * stoi = stoiList[n]; |
| |
| if ((NOT stoi->isIndex()) && |
| (NOT stoi->specialTable()) && |
| (stoi->getUpdateAccess() || |
| stoi->getDeleteAccess() )) |
| { |
| if (baseTablenamePosition == -1) |
| { |
| baseTablenamePosition = n; |
| } |
| } |
| } |
| |
| if (baseTablenamePosition == -1) |
| { |
| // No base table found in the stoi list. |
| // Raise an error. |
| GenAssert(0, "Must find updelTableNamePosition!"); |
| } |
| } |
| |
| // find out if this was an update,delete or insert query. |
| NABoolean updDelInsert = FALSE; |
| if (childOperType().match(REL_ANY_GEN_UPDATE)) |
| updDelInsert = TRUE; |
| |
| // Do OLT optimization if: |
| // -- OLT optimization is possible |
| // -- and no upd/del where current of |
| // -- and no late name resolution |
| // -- and no views in query |
| NABoolean doOltQryOpt = FALSE; |
| if ((oltOptInfo().oltCliOpt()) && |
| (viewStoiList == NULL) && // no views |
| (doTablenameOltOpt == TRUE) && // no late name info |
| (fetchedCursorName == NULL) && // no upd/del curr of |
| (fetchedCursorHvar < 0) && |
| (delCurrOf == FALSE) && |
| (getFirstNRows() == -1)) // no firstn specified |
| { |
| doOltQryOpt = TRUE; |
| } |
| |
| // At runtime, we try to internally reexecute a statement in case of |
| // lost opens, if that query has not affected the database(inserted/updated/ |
| // deleted a row), or a row has not been returned to the application. |
| // Do not retry for lost opens of IUD queries if there are more |
| // than one tables in the query. This is because we don't know if the db |
| // was affected when the open was lost on the non-IUD table in the query. |
| // For ex: in an insert...select query, an open could be blown away for |
| // the select part of the query. |
| // If there is only one table in this query, then that row will get locked |
| // during IUD and the open could not be blown away. |
| // If some day we put in a scheme to detect that the database |
| // was not affected for a multi-table IUD, we can retry for lost |
| // opens. |
| NABoolean retryableStmt = TRUE; |
| |
| if (generator->aqrEnabled()) |
| { |
| retryableStmt = FALSE; |
| } |
| |
| if (updDelInsert && moreThanOneTable) |
| retryableStmt = FALSE; |
| |
| if (childOperType() == REL_DDL) |
| retryableStmt = FALSE; |
| |
| if (isExtractProducer || isExtractConsumer) |
| retryableStmt = FALSE; |
| |
| // For now we mark statements containing UDRs as non-retryable. |
| // This is to avoid executing a stored procedure body multiple times |
| // inside a single application request. Currently the only UDR- |
| // containing statment is CALL. |
| // |
| // There are scenarios however in which it would be correct (and |
| // helpful) to retry a UDR-containing statement. Perhaps in the |
| // future the restriction can be lifted in some cases. Safe retry |
| // scenarios include a subquery input parameter returning a blown |
| // away open error before the stored procedure body has executed, |
| // and UDR bodies that only do computation and not transactional work. |
| if (noOfUdrs > 0) |
| retryableStmt = FALSE; |
| |
| short maxResultSets = generator->getBindWA()->getMaxResultSets(); |
| |
| char *queryCostInfoBuf = NULL; |
| QueryCostInfo *queryCostInfo = |
| (QueryCostInfo *) new (space) char[sizeof(QueryCostInfo)]; |
| // fill in cost. Taken from explain code in GenExplain.cpp |
| if (getRollUpCost()) |
| { |
| double cpu, io, msg, idle, seqIOs, randIOs, total, cardinality; |
| double totalMemPerCpu, totalMemPerCpuInKB; |
| short maxCpuUsage; |
| Lng32 dummy; |
| Cost const *operatorCost = getRollUpCost(); |
| const NABoolean inMaster = generator->getEspLevel() == 0; |
| operatorCost->getExternalCostAttr(cpu, io, msg, idle, seqIOs, randIOs, total, dummy); |
| // operatorCost->getOcmCostAttr(cpu, io, msg, idle, dummy); |
| // total = MINOF(operatorCost->convertToElapsedTime(), 1e32).getValue(); |
| |
| cardinality = MINOF(getEstRowsUsed(), 1e32).value(); |
| |
| // get the totalMem that is being used divide by max dop to get an |
| // an estimate of memory usage per cpu. Convert to KB units. |
| totalMemPerCpu = |
| generator->getTotalEstimatedMemory() / |
| ((generator->compilerStatsInfo().dop() > 0) ? |
| generator->compilerStatsInfo().dop() : 1); |
| totalMemPerCpuInKB = totalMemPerCpu / 1024 ; |
| maxCpuUsage = generator->getMaxCpuUsage() ; |
| queryCostInfo->setCostInfo(cpu, io, msg, idle, seqIOs, randIOs, total, |
| cardinality, totalMemPerCpuInKB, maxCpuUsage); |
| |
| // if resourceUsage need to be set (low/medium/high), set it here. |
| // For now, set to 0 which indicates that this value is not |
| // being returned. |
| queryCostInfo->setResourceUsage(0); |
| } |
| |
| queryCostInfoBuf = (char*)queryCostInfo; |
| // |
| // CompilationStatsData |
| CompilationStats* stats = CURRENTSTMT->getCompilationStats(); |
| char *compilerId = new (space) char[COMPILER_ID_LEN]; |
| str_cpy_all(compilerId, generator->currentCmpContext()->getCompilerId(), |
| COMPILER_ID_LEN); |
| Int32 cLen = stats->getCompileInfoLen(); |
| // |
| // make it 1 at minimum |
| cLen = ( cLen < 1 ) ? 1 : cLen; |
| |
| char *compileInfo = new (space) char[cLen]; |
| stats->getCompileInfo(compileInfo); |
| // |
| // Some of the fields are set here but modified later after generator phase is |
| // complete (such as compileEndTime, CMP_PHASE_ALL, and CMP_PHASE_GENERATOR) |
| CompilationStatsData *compilationStatsData = |
| (CompilationStatsData *) new (space) |
| CompilationStatsData(stats->compileStartTime(), |
| stats->compileEndTime(), |
| compilerId, |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_ALL), |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_BINDER), |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_NORMALIZER), |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_ANALYZER), |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_OPTIMIZER), |
| stats->cmpPhaseLength(CompilationStats::CMP_PHASE_GENERATOR), |
| stats->metadataCacheHits(), |
| stats->metadataCacheLookups(), |
| stats->getQueryCacheState(), |
| stats->histogramCacheHits(), |
| stats->histogramCacheLookups(), |
| stats->stmtHeapCurrentSize(), |
| stats->cxtHeapCurrentSize(), |
| stats->optimizationTasks(), |
| stats->optimizationContexts(), |
| stats->isRecompile(), |
| compileInfo, |
| stats->getCompileInfoLen()); |
| // |
| // CompilerStats |
| char *compilerStatsInfoBuf = NULL; |
| CompilerStatsInfo *compilerStatsInfo = |
| (CompilerStatsInfo *) new (space) char[sizeof(CompilerStatsInfo)]; |
| compilerStatsInfoBuf = (char*)compilerStatsInfo; |
| *compilerStatsInfo = generator->compilerStatsInfo(); |
| |
| // remove the duplicated entries from the schema label list, and put the |
| // unique entries in TDB. During execution time, we will check the |
| // LastModTimestamp of the schema label. If the lastModTimestamp has changed, |
| // a timestamp mismatch will be returned, allowing for recompilation of the |
| // query |
| |
| |
| NABoolean validateSSTSFlag = TRUE; |
| |
| CollIndex numObjectUIDs = generator->objectUids().entries(); |
| Int64 *objectUIDsPtr = NULL; |
| if (numObjectUIDs > 0) |
| { |
| objectUIDsPtr = new (space) Int64[numObjectUIDs]; |
| for (CollIndex i = 0; i < numObjectUIDs; i++) |
| objectUIDsPtr[i] = generator->objectUids()[i]; |
| } |
| |
| Queue * listOfSnapshotscanTables = NULL; |
| NAString tmpLocNAS; |
| char * tmpLoc = NULL; |
| Int64 numObjectNames = generator->objectNames().entries(); |
| if (numObjectNames >0) |
| { |
| listOfSnapshotscanTables = new(space) Queue(space); |
| for (Lng32 i=0 ; i <generator->objectNames().entries(); i++) |
| { |
| char * nm = space->allocateAlignedSpace(generator->objectNames()[i].length() + 1); |
| strcpy(nm, generator->objectNames()[i].data()); |
| listOfSnapshotscanTables->insert(nm); |
| } |
| |
| tmpLocNAS = generator->getSnapshotScanTmpLocation(); |
| CMPASSERT(tmpLocNAS[tmpLocNAS.length()-1] =='/'); |
| tmpLoc = space->allocateAlignedSpace(tmpLocNAS.length() + 1); |
| strcpy(tmpLoc, tmpLocNAS.data()); |
| } |
| |
| |
| // for describe type commands(showshape, showplan, explain) we don't |
| // need to pass in the actual param values even if the query contains |
| // params. Reset input_expr. This is done to avoid returning |
| // an error later if the actual param value is not set. |
| OperatorTypeEnum child_op_type = childOperType(); |
| if (child_op_type == REL_EXE_UTIL || child_op_type == REL_DESCRIBE) |
| { |
| RelExpr * lc = child(0)->castToRelExpr(); |
| OperatorTypeEnum actual_op_type = lc->getOperatorType(); |
| |
| if (actual_op_type == REL_EXE_UTIL) |
| { |
| ExeUtilExpr *e = (ExeUtilExpr *)lc; |
| if (e->getExeUtilType() == ExeUtilExpr::DISPLAY_EXPLAIN_) |
| input_expr = NULL; |
| } |
| else if (actual_op_type == REL_DESCRIBE) |
| { |
| Describe *d = (Describe *)lc; |
| if (d->getFormat() == Describe::SHAPE_ |
| || d->getFormat() == Describe::PLAN_) |
| input_expr = NULL; |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // now initialize the previously allocated root tdb. note that this |
| // init *must* come before we fill in the exFragDir's info because there |
| // we compute how much space is used by the fragment the root is in. |
| // This init() call passes in the space object. The root might allocate |
| // more space for its uses inside init(). |
| // --------------------------------------------------------------------- |
| root_tdb->init(child_tdb, |
| cri_desc, // input to child |
| (InputOutputExpr *)input_expr, |
| (InputOutputExpr *)output_expr, |
| input_vars_size, |
| pkey_expr, |
| pkey_len, |
| pred_expr, |
| work_cri_desc, |
| exFragDir, |
| transMode, |
| fetchedCursorName, |
| fetchedCursorHvar, |
| delCurrOf, |
| numUpdateCol, |
| updateColList, |
| (outputVarCntValid() && outputVarCnt()), |
| noOfTables, |
| getFirstNRows(), |
| userInputVars, |
| (getRollUpCost() ? |
| getRollUpCost()->displayTotalCost().getValue() : 0), |
| stoiList, |
| lnil, |
| viewStoiList, |
| qsi, |
| space, |
| uniqueExecuteIdOffset, //++ Triggers - |
| triggersStatusOffset, |
| triggersCount, |
| triggersList, |
| (short)generator->getTempTableId(), |
| (short)baseTablenamePosition, |
| updDelInsert, |
| retryableStmt, |
| getGroupAttr()->isStream(), |
| // next flag is set for destructive stream access protocol. |
| // Not needed for hbase/seabase access. |
| (getGroupAttr()->isEmbeddedUpdateOrDelete() && |
| (NOT hdfsAccess())), |
| CmpCommon::getDefaultNumeric(STREAM_TIMEOUT), |
| generator->getPlanId(), |
| qCacheInfoBuf, |
| cacheVarsSize, |
| udrStoiList, |
| noOfUdrs, |
| maxResultSets, |
| queryCostInfoBuf, |
| newMvList, |
| uninitializedMvCount, |
| compilerStatsInfoBuf, |
| rwrsInfoBuf, |
| numObjectUIDs , |
| objectUIDsPtr, |
| compilationStatsData, |
| tmpLoc, |
| listOfSnapshotscanTables); |
| |
| root_tdb->setTdbId(generator->getAndIncTdbId()); |
| |
| if (childTdbIsNull) |
| root_tdb->setChildTdbIsNull(); |
| if (generator->explainInRms()) |
| root_tdb->setExplainInRms(); |
| |
| OperatorTypeEnum childOper = childOperType(); |
| |
| if (qCacheInfoIsClass) |
| root_tdb->setQCacheInfoIsClass(TRUE); |
| |
| if (getHostArraysArea() && getHostArraysArea()->getRowwiseRowset()) |
| { |
| root_tdb->setRowwiseRowsetInput(TRUE); |
| } |
| else |
| { |
| NABoolean singleRowInput = TRUE; |
| if ((input_expr) && ((InputOutputExpr *)input_expr)->isCall()) |
| singleRowInput = FALSE; |
| |
| if (CmpCommon::getDefault(COMP_BOOL_92) == DF_OFF) |
| singleRowInput = FALSE; |
| |
| root_tdb->setSingleRowInput(singleRowInput); |
| } |
| |
| if (childOper == REL_DDL) |
| { |
| root_tdb->setDDLQuery(TRUE); |
| } |
| |
| root_tdb->setCIFON(isCIFOn_); |
| if (generator->currentCmpContext()->isEmbeddedArkcmp()) |
| //if (IdentifyMyself::GetMyName() == I_AM_EMBEDDED_SQL_COMPILER) |
| root_tdb->setEmbeddedCompiler(TRUE); |
| else |
| root_tdb->setEmbeddedCompiler(FALSE); |
| // We check to see if this tree corresponds to a compound statement so |
| // we know this at execution time |
| |
| RelExpr* checkNode = child(0); |
| |
| if ( checkNode->getOperatorType() == REL_EXCHANGE ) |
| checkNode = checkNode->child(0); |
| |
| if ( checkNode->getOperatorType() == REL_PARTITION_ACCESS ) |
| checkNode = checkNode->child(0); |
| |
| if (checkNode->getOperatorType() == REL_COMPOUND_STMT || |
| (checkNode->getOperatorType() == REL_UNION || |
| checkNode->getOperatorType() == REL_MERGE_UNION) |
| && |
| ((Union *) (RelExpr *) checkNode)->getUnionForIF()) { |
| root_tdb->setCompoundStatement(); |
| } |
| |
| root_tdb->setDoOltQueryOpt(doOltQryOpt); |
| |
| root_tdb->setQueryType(ComTdbRoot::SQL_OTHER); |
| |
| // set the EMS Event Experience Level information |
| // the default is ADVANCED if it is not specified |
| if (CmpCommon::getDefault(USER_EXPERIENCE_LEVEL) == DF_BEGINNER) |
| { |
| root_tdb->setEMSEventExperienceLevelBeginner(TRUE); |
| } |
| if (CmpCommon::getDefault(UNC_PROCESS) == DF_ON) |
| { |
| root_tdb->setUncProcess(TRUE); |
| } |
| |
| |
| // If this is a ustat query set the query type so WMS can monitor it |
| if (childOper == REL_DDL) |
| { |
| DDLExpr *ddlExpr = (DDLExpr *)child(0)->castToRelExpr(); |
| char * stmt = ddlExpr->getDDLStmtText(); |
| NAString ddlStr = NAString(stmt); |
| ddlStr = ddlStr.strip(NAString::leading, ' '); |
| // If this is a ustat statement, set the type |
| Int32 foundUpdStat = 0; |
| |
| // check if the first token is UPDATE |
| size_t position = ddlStr.index("UPDATE", 0, NAString::ignoreCase); |
| if (position == 0) |
| { |
| // found UPDATE. See if the next token is STATISTICS. |
| ddlStr = ddlStr(6, ddlStr.length()-6); // skip over UPDATE |
| ddlStr = ddlStr.strip(NAString::leading, ' '); |
| |
| position = ddlStr.index("STATISTICS", 0, NAString::ignoreCase); |
| if (position == 0) |
| foundUpdStat = -1; |
| } |
| if (foundUpdStat) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_CAT_UTIL); |
| } |
| } |
| // Disable Cancel for some queries. But start the logic with |
| // "all queries can be canceled." |
| root_tdb->setMayNotCancel(FALSE); |
| |
| // Disallow cancel. |
| if (CmpCommon::getDefault(COMP_BOOL_20) == DF_ON) |
| root_tdb->setMayNotCancel(TRUE); |
| |
| if (generator->mayNotCancel()) |
| root_tdb->setMayNotCancel(TRUE); |
| |
| if (updDelInsert) |
| { |
| if ((childOper == REL_UNARY_INSERT) || |
| (childOper == REL_LEAF_INSERT) || |
| (childOper == REL_INSERT_CURSOR)) |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_NON_UNIQUE); |
| else if ((childOper == REL_UNARY_UPDATE) || |
| (childOper == REL_LEAF_UPDATE) || |
| (childOper == REL_UPDATE_CURSOR)) |
| root_tdb->setQueryType(ComTdbRoot::SQL_UPDATE_NON_UNIQUE); |
| else if ((childOper == REL_UNARY_DELETE) || |
| (childOper == REL_LEAF_DELETE) || |
| (childOper == REL_DELETE_CURSOR)) |
| root_tdb->setQueryType(ComTdbRoot::SQL_DELETE_NON_UNIQUE); |
| } |
| |
| if (output_expr) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SELECT_NON_UNIQUE); |
| |
| if ((updDelInsert) && |
| (root_tdb->getQueryType() == ComTdbRoot::SQL_INSERT_NON_UNIQUE) && |
| (rwrsInfo)) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_RWRS); |
| } |
| else if ((child(0)) && |
| (child(0)->castToRelExpr()->getOperatorType() == REL_UTIL_INTERNALSP)) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_CAT_UTIL); |
| } |
| else if ((child(0)) && |
| (child(0)->castToRelExpr()->getOperatorType() == REL_DESCRIBE)) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_DESCRIBE_QUERY); |
| } |
| else if ((child(0)) && |
| (child(0)->castToRelExpr()->getOperatorType() == REL_EXE_UTIL)) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_EXE_UTIL); |
| ExeUtilExpr * exeUtil = (ExeUtilExpr*)child(0)->castToRelExpr(); |
| if (exeUtil->getExeUtilType() == ExeUtilExpr::CREATE_TABLE_AS_) |
| { |
| if (CmpCommon::getDefault(REDRIVE_CTAS) == DF_OFF) |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_NON_UNIQUE); |
| else |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_CTAS); |
| } |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::GET_STATISTICS_) |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_GET_STATISTICS); |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::DISPLAY_EXPLAIN_) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_DISPLAY_EXPLAIN); |
| |
| if (CmpCommon::getDefault(EXE_UTIL_RWRS) == DF_ON) |
| root_tdb->setExeUtilRwrs(TRUE); |
| } |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::HBASE_COPROC_AGGR_) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SELECT_NON_UNIQUE); |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::HBASE_LOAD_) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_HBASE_LOAD); |
| } |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::HBASE_UNLOAD_) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_HBASE_UNLOAD); |
| } |
| else if (exeUtil->getExeUtilType() == ExeUtilExpr::LOB_EXTRACT_) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_LOB_EXTRACT); |
| } |
| else if(exeUtil->getExeUtilType() == ExeUtilExpr::LOB_UPDATE_UTIL_) |
| { |
| root_tdb->setSubqueryType(ComTdbRoot::SQL_STMT_LOB_UPDATE_UTIL |
| ); |
| } |
| |
| else if (exeUtil->isExeUtilQueryType()) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_EXE_UTIL); |
| } |
| } |
| else if ((child(0)) && |
| (child(0)->castToRelExpr()->getOperatorType() == REL_DDL)) |
| { |
| DDLExpr *ddlExpr = (DDLExpr *)child(0)->castToRelExpr(); |
| |
| if (ddlExpr->producesOutput()) |
| root_tdb->setQueryType(ComTdbRoot::SQL_EXE_UTIL); |
| } |
| else if (generator->getBindWA()->hasCallStmts()) |
| { |
| // In this version of the compiler we assume any statement that |
| // contains UDRs is either a CALL statement. |
| if (maxResultSets > 0) |
| root_tdb->setQueryType(ComTdbRoot::SQL_CALL_WITH_RESULT_SETS); |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_CALL_NO_RESULT_SETS); |
| } |
| else |
| { |
| OperatorTypeEnum currChildOper = |
| child(0)->castToRelExpr()->getOperatorType(); |
| |
| if (currChildOper == REL_CONTROL_QUERY_DEFAULT) |
| { |
| root_tdb->setMayNotCancel(TRUE); |
| ControlQueryDefault * cqd = |
| (ControlQueryDefault*)child(0)->castToRelExpr(); |
| if (cqd->dynamic()) |
| { |
| if (cqd->getAttrEnum() == CATALOG) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SET_CATALOG); |
| else if (cqd->getAttrEnum() == SCHEMA) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SET_SCHEMA); |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_CONTROL); |
| } |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_CONTROL); |
| } |
| else if ((currChildOper == REL_CONTROL_QUERY_SHAPE) || |
| (currChildOper == REL_CONTROL_TABLE)) |
| { |
| root_tdb->setMayNotCancel(TRUE); |
| root_tdb->setQueryType(ComTdbRoot::SQL_CONTROL); |
| } |
| else if (currChildOper == REL_TRANSACTION) |
| { |
| root_tdb->setMayNotCancel(TRUE); |
| if (((RelTransaction*)child(0)->castToRelExpr())->getType() == SET_TRANSACTION_) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SET_TRANSACTION); |
| } |
| else if (currChildOper == REL_SP_PROXY) |
| { |
| // This is a stored procedure result set |
| root_tdb->setQueryType(ComTdbRoot::SQL_SP_RESULT_SET); |
| } |
| else if (currChildOper == REL_EXE_UTIL) |
| { |
| ExeUtilExpr * exeUtil = (ExeUtilExpr*)child(0)->castToRelExpr(); |
| if (exeUtil->getExeUtilType() == ExeUtilExpr::CREATE_TABLE_AS_) |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_NON_UNIQUE); |
| } |
| else if (REL_EXPLAIN == currChildOper) |
| root_tdb->setMayNotCancel(TRUE); |
| else if (REL_SET_TIMEOUT == currChildOper) |
| root_tdb->setMayNotCancel(TRUE); |
| else if (REL_CONTROL_RUNNING_QUERY == currChildOper) |
| root_tdb->setMayNotCancel(TRUE); |
| } |
| |
| if (generator->isFastExtract()) |
| { |
| root_tdb->setQueryType(ComTdbRoot::SQL_SELECT_UNLOAD); |
| } |
| |
| if (child(0) && child(0)->castToRelExpr() && |
| child(0)->castToRelExpr()->getOperator().match(REL_ANY_HBASE)) |
| { |
| RelExpr * childExpr = child(0)->castToRelExpr(); |
| OperatorTypeEnum currChildOper = childExpr->getOperatorType(); |
| |
| if ((childExpr->getOperator().match(REL_ANY_HBASE_GEN_UPDATE)) && |
| (NOT output_expr)) |
| { |
| GenericUpdate * gu = (GenericUpdate *)childExpr; |
| |
| if (gu->uniqueHbaseOper()) |
| { |
| if (currChildOper == REL_HBASE_UPDATE) |
| root_tdb->setQueryType(ComTdbRoot::SQL_UPDATE_UNIQUE); |
| else if (currChildOper == REL_HBASE_DELETE) |
| root_tdb->setQueryType(ComTdbRoot::SQL_DELETE_UNIQUE); |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_UNIQUE); |
| } |
| else |
| { |
| if (currChildOper == REL_HBASE_UPDATE) |
| root_tdb->setQueryType(ComTdbRoot::SQL_UPDATE_NON_UNIQUE); |
| else if (currChildOper == REL_HBASE_DELETE) |
| root_tdb->setQueryType(ComTdbRoot::SQL_DELETE_NON_UNIQUE); |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_INSERT_NON_UNIQUE); |
| } |
| } |
| else if (currChildOper == REL_HBASE_ACCESS) |
| { |
| HbaseAccess * ha = (HbaseAccess *)childExpr; |
| if (ha->uniqueHbaseOper()) |
| root_tdb->setQueryType(ComTdbRoot::SQL_SELECT_UNIQUE); |
| else |
| root_tdb->setQueryType(ComTdbRoot::SQL_SELECT_NON_UNIQUE); |
| } |
| } |
| |
| // To help determine if it is safe to suspend. |
| if (child(0) && |
| child(0)->castToRelExpr()) |
| { |
| OperatorTypeEnum currChildOper = |
| child(0)->castToRelExpr()->getOperatorType(); |
| if ((REL_DDL == currChildOper) || |
| (REL_TRANSACTION == currChildOper) || |
| (REL_EXE_UTIL == currChildOper)) |
| root_tdb->setMayAlterDb(TRUE); |
| |
| if (REL_LOCK == currChildOper) |
| root_tdb->setSuspendMayHoldLock(TRUE); |
| } |
| |
| if (generator->anySerialiableScan()) |
| root_tdb->setSuspendMayHoldLock(TRUE); |
| |
| root_tdb->setOdbcQuery(CmpCommon::getDefault(ODBC_PROCESS) == DF_ON); |
| |
| if (generator->getTolerateNonFatalError()) { |
| root_tdb->setTolerateNonFatalError(TRUE); |
| if (CmpCommon::getDefault(NOT_ATOMIC_FAILURE_LIMIT,0) == DF_SYSTEM) |
| root_tdb->setNotAtomicFailureLimit(ComCondition::NO_LIMIT_ON_ERROR_CONDITIONS); |
| else |
| root_tdb->setNotAtomicFailureLimit(CmpCommon::getDefaultLong(NOT_ATOMIC_FAILURE_LIMIT)); |
| } |
| |
| if (generator->embeddedIUDWithLast1()) { |
| root_tdb->setEmbeddedIUDWithLast1(TRUE); |
| } |
| |
| if (generator->embeddedInsert()) { |
| root_tdb->setEmbeddedInsert(TRUE); |
| } |
| |
| if (containsLRU()) |
| { |
| root_tdb->setLRUOperation(TRUE); |
| } |
| |
| if (generator->aqrEnabled()) |
| root_tdb->setAqrEnabled(TRUE); |
| |
| if (generator->cantReclaimQuery()) |
| root_tdb->setCantReclaimQuery(TRUE); |
| |
| // if a transaction is needed at runtime to execute this query, |
| // set that information in the root tdb. Generator synthesized |
| // this information based on the kind of query or if START_XN |
| // define was set. |
| // Certain queries (insert, update, delete) ALWAYS require a transaction |
| // at runtime. |
| // After parser support for REPEATABLE ACCESS, etc, is in, this |
| // information will come from the parse tree for scans. |
| if (generator->isTransactionNeeded()) |
| { |
| root_tdb->setTransactionReqd(); |
| |
| if (generator->foundAnUpdate()) |
| { |
| if (generator->updAbortOnError() == TRUE) |
| { |
| // if transaction has to be aborted at runtime after an error, |
| // set that info in root_tdb. |
| root_tdb->setUpdAbortOnError(-1); |
| } |
| else if (generator->updPartialOnError() == TRUE) |
| { |
| root_tdb->setUpdPartialOnError(-1); |
| } |
| else if (generator->updErrorInternalOnError() == TRUE) |
| { |
| root_tdb->setUpdErrorOnError(-1); |
| } |
| else if (generator->updErrorOnError() == FALSE) |
| { |
| if (generator->updSavepointOnError() == TRUE) |
| { |
| root_tdb->setUpdSavepointOnError(-1); |
| } |
| else |
| root_tdb->setUpdAbortOnError(-1); |
| } |
| else |
| root_tdb->setUpdErrorOnError(-1); |
| } |
| else |
| { |
| root_tdb->setUpdErrorOnError(-1); |
| } |
| } // transactionNeeded |
| |
| if ((oltOptLean()) && |
| (doOltQryOpt)) |
| { |
| if ((NOT root_tdb->getUpdAbortOnError()) && |
| (NOT root_tdb->getUpdSavepointOnError()) && |
| (NOT root_tdb->getUpdPartialOnError()) && |
| (retryableStmt) && |
| (NOT root_tdb->thereIsACompoundStatement())) |
| { |
| root_tdb->setDoOltQueryOptLean(TRUE); |
| |
| child_tdb->setDoOltQueryOptLean(TRUE); |
| } |
| } |
| |
| if (generator->dp2XnsEnabled()) |
| { |
| root_tdb->setDp2XnsEnabled(generator->dp2XnsEnabled()); |
| } |
| |
| |
| if (generator->processLOB()) |
| root_tdb->setProcessLOB(TRUE); |
| |
| |
| // Self-referencing updates |
| if (avoidHalloween_) |
| { |
| if (Generator::DP2LOCKS == generator->getHalloweenProtection()) |
| { |
| // Plan was generated without resetting the generator's |
| // HalloweenProtectionType from DP2Locks, therefore we are |
| // using DP2 locks, and cannot allow auto commit off. |
| root_tdb->setCheckAutoCommit(TRUE); |
| } |
| } |
| else if (CmpCommon::getDefault(AQR_WNR_DELETE_NO_ROWCOUNT) == DF_ON) |
| { |
| // Allow non-ACID AQR of NO ROLLBACK DELETE that may have changed |
| // target. Query type (DELETE vs others) and WNR will be evaluated |
| // at runtime. |
| root_tdb->setAqrWnrDeleteContinue(TRUE); |
| } |
| if (CmpCommon::getDefault(PSHOLD_CLOSE_ON_ROLLBACK) == DF_ON) |
| root_tdb->setPsholdCloseOnRollback(TRUE); |
| else |
| root_tdb->setPsholdCloseOnRollback(FALSE); |
| if (CmpCommon::getDefault(PSHOLD_UPDATE_BEFORE_FETCH) == DF_ON) |
| root_tdb->setPsholdUpdateBeforeFetch(TRUE); |
| else |
| root_tdb->setPsholdUpdateBeforeFetch(FALSE); |
| |
| root_tdb->setAbendType( |
| (Lng32) CmpCommon::getDefaultNumeric(COMP_INT_38) ); |
| |
| double cpuLimitCheckFreq = CmpCommon::getDefaultNumeric(COMP_INT_48); |
| if (cpuLimitCheckFreq > SHRT_MAX) |
| cpuLimitCheckFreq = SHRT_MAX; |
| root_tdb->setCpuLimitCheckFreq((short) cpuLimitCheckFreq); |
| |
| // Config query execution limits. |
| Lng32 cpuLimit = (Lng32) CmpCommon::getDefaultNumeric(QUERY_LIMIT_SQL_PROCESS_CPU); |
| if (cpuLimit > 0) |
| root_tdb->setCpuLimit(cpuLimit); |
| |
| if (CmpCommon::getDefault(QUERY_LIMIT_SQL_PROCESS_CPU_DEBUG) == DF_ON) |
| root_tdb->setQueryLimitDebug(); |
| |
| if (generator->inMemoryObjectDefn()) |
| root_tdb->setInMemoryObjectDefn(TRUE); |
| |
| if (CmpCommon::getDefault(READONLY_CURSOR) == DF_ON) |
| root_tdb->setCursorType(SQL_READONLY_CURSOR); |
| else |
| root_tdb->setCursorType(SQL_UPDATABLE_CURSOR); |
| |
| if (CmpCommon::getDefault(WMS_QUERY_MONITORING) == DF_ON) |
| root_tdb->setWmsMonitorQuery(TRUE); |
| else |
| root_tdb->setWmsMonitorQuery(FALSE); |
| |
| if (CmpCommon::getDefault(WMS_CHILD_QUERY_MONITORING) == DF_ON) |
| root_tdb->setWmsChildMonitorQuery(TRUE); |
| else |
| root_tdb->setWmsChildMonitorQuery(FALSE); |
| |
| if (hdfsAccess()) |
| root_tdb->setHdfsAccess(TRUE); |
| |
| if(generator->hiveAccess()) |
| root_tdb->setHiveAccess(TRUE); |
| |
| root_tdb->setBmoMemoryLimitPerNode(ActiveSchemaDB()->getDefaults().getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB)); |
| root_tdb->setEstBmoMemoryPerNode(generator->getTotalBMOsMemoryPerNode().value()); |
| |
| Int32 numSikEntries = securityKeySet_.entries(); |
| if (numSikEntries > 0) |
| { |
| ComSecurityKey * sikValues = new (space) ComSecurityKey[numSikEntries]; |
| |
| for (Int32 sv = 0; sv < numSikEntries; sv++) |
| sikValues[sv] = securityKeySet_[sv]; |
| |
| SecurityInvKeyInfo * sikInfo = new (space) SecurityInvKeyInfo( |
| numSikEntries, sikValues); |
| root_tdb->setSikInfo(sikInfo); |
| } |
| |
| if (!generator->explainDisabled()) |
| { |
| // finish up EXPLAIN |
| ExplainTuple *rootExplainTuple = |
| addExplainInfo(root_tdb, 0, 0, generator); |
| explainDesc->setExplainTreeRoot(rootExplainTuple); |
| |
| ExplainTuple *childExplainTuple = generator->getExplainTuple(); |
| |
| rootExplainTuple->child(0) = childExplainTuple; |
| if(childExplainTuple) |
| { |
| childExplainTuple->setParent(rootExplainTuple); |
| rootExplainTuple->setChildSeqNum( |
| 0, |
| childExplainTuple->getSeqNum()); |
| } |
| |
| generator->setExplainTuple(rootExplainTuple); |
| } |
| |
| // Generate a list of scratch file options |
| exFragDir->setScratchFileOptions(genScratchFileOptions(generator)); |
| |
| // move ESP nodemask into frag dir |
| exFragDir->setNodeMask((ULng32) getDefault(PARALLEL_ESP_NODEMASK)); |
| |
| // generate the partition input data descriptor from the compile-time |
| // partitioning attributes |
| ExPartInputDataDesc **partInputDataDescs = |
| new(generator->wHeap()) ExPartInputDataDesc *[compFragDir->entries()]; |
| ExEspNodeMap **nodeMap = |
| new(generator->wHeap()) ExEspNodeMap *[compFragDir->entries()]; |
| for (i = 0; i < compFragDir->entries(); i++) |
| { |
| if (compFragDir->getPartitioningFunction(i) != NULL) |
| { |
| // This fragment has partitioning info, generate it |
| ((PartitioningFunction *) compFragDir->getPartitioningFunction(i))-> |
| codeGen(generator, |
| compFragDir->getPartInputDataLength(i)); |
| partInputDataDescs[i] = |
| (ExPartInputDataDesc *) (generator->getGenObj()); |
| NodeMap::codeGen(compFragDir->getPartitioningFunction(i), |
| compFragDir->getNumESPs(i), |
| generator); |
| nodeMap[i] = (ExEspNodeMap *) (generator->getGenObj()); |
| } |
| else |
| { |
| partInputDataDescs[i] = NULL; |
| nodeMap[i] = NULL; |
| } |
| } |
| |
| // remove maptable for child tree |
| generator->removeAll(map_table); |
| |
| // remove my map table |
| generator->removeLast(); |
| generator->setMapTable(NULL); |
| |
| // move data entry by entry from the generator's copy into the executor copy |
| Lng32 offset = 0; |
| Lng32 currLength; |
| Lng32 compressThreshold = getDefault(FRAG_COMPRESSION_THRESHOLD); |
| NABoolean anyEspFragments = FALSE; |
| |
| for (i = 0; i < compFragDir->entries(); i++) |
| { |
| // translate fragment type enums |
| ExFragDir::ExFragEntryType runTimeType = ExFragDir::MASTER; |
| currLength = compFragDir->getFragmentLength(i); |
| compilerStatsInfo->totalFragmentSize() += currLength; |
| switch (compFragDir->getType(i)) |
| { |
| case FragmentDir::MASTER: |
| runTimeType = ExFragDir::MASTER; |
| compilerStatsInfo->masterFragmentSize() += currLength; |
| break; |
| case FragmentDir::DP2: |
| runTimeType = ExFragDir::DP2; |
| compilerStatsInfo->dp2FragmentSize() += currLength; |
| break; |
| case FragmentDir::ESP: |
| runTimeType = ExFragDir::ESP; |
| anyEspFragments = TRUE; |
| compilerStatsInfo->espFragmentSize() += currLength; |
| break; |
| case FragmentDir::EXPLAIN: |
| runTimeType = ExFragDir::EXPLAIN; |
| compilerStatsInfo->masterFragmentSize() += currLength; |
| break; |
| default: |
| ABORT("Internal error, invalid fragment type"); |
| } |
| |
| // take the pointer of the top-level object in this fragment and |
| // convert it to a fragment-relative offset |
| Lng32 offsetOfTopNode = compFragDir->getSpace(i)-> |
| convertToOffset((char *)(compFragDir->getTopNode(i))); |
| |
| // now set the values of the previously allocated directory entry |
| |
| NABoolean mlimitPerNode = CmpCommon::getDefaultLong(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0; |
| UInt16 BMOsMemoryUsage = 0; |
| if (mlimitPerNode == TRUE) |
| BMOsMemoryUsage = (UInt16)compFragDir->getBMOsMemoryUsage(i); |
| else if (compFragDir->getNumBMOs(i) > 1 || |
| (compFragDir->getNumBMOs(i) == 1 && CmpCommon::getDefault(EXE_SINGLE_BMO_QUOTA) == DF_ON)) |
| BMOsMemoryUsage = (UInt16)CmpCommon::getDefaultLong(EXE_MEMORY_AVAILABLE_IN_MB); |
| |
| exFragDir->set(i, |
| runTimeType, |
| (ExFragId) compFragDir->getParentId(i), |
| offset, |
| currLength, |
| -offsetOfTopNode, |
| partInputDataDescs[i], |
| nodeMap[i], |
| compFragDir->getNumESPs(i), |
| compFragDir->getEspLevel(i), |
| compFragDir->getNeedsTransaction(i), |
| (compressThreshold > 0 && |
| runTimeType == ExFragDir::ESP && |
| compressThreshold <= compFragDir->getNumESPs(i))? |
| TRUE: FALSE, // DP2 fragment will be compressed later |
| // if parent ESP fragment is compressed |
| // see executor/ex_frag_rt.cpp |
| compFragDir->getSoloFragment(i), |
| BMOsMemoryUsage, |
| compFragDir->getNumBMOs(i) > 0 |
| ); |
| offset += currLength; |
| } // for each fragment |
| |
| compilerStatsInfo->totalFragmentSize() /= 1024; |
| compilerStatsInfo->masterFragmentSize() /= 1024; |
| compilerStatsInfo->espFragmentSize() /= 1024; |
| compilerStatsInfo->dp2FragmentSize() /= 1024; |
| compilerStatsInfo->collectStatsType() = generator->collectStatsType(); |
| compilerStatsInfo->udr() = noOfUdrs; |
| compilerStatsInfo->ofMode() = generator->getOverflowMode(); |
| compilerStatsInfo->ofSize() = 0; |
| compilerStatsInfo->bmo() = generator->getTotalNumBMOs(); |
| compilerStatsInfo->queryType() = (Int16)root_tdb->getQueryType(); |
| compilerStatsInfo->subqueryType() = (Int16)root_tdb->getSubqueryType(); |
| compilerStatsInfo->bmoMemLimitPerNode() = root_tdb->getBmoMemoryLimitPerNode(); |
| compilerStatsInfo->estBmoMemPerNode() = root_tdb->getEstBmoMemoryPerNode(); |
| |
| NADELETEBASIC(partInputDataDescs, generator->wHeap()); |
| NADELETEBASIC(nodeMap, generator->wHeap()); |
| |
| // Genesis 10-990114-6293: |
| // don't recompile a SELECT query if transmode changes to READ ONLY. |
| if (readOnlyTransIsOK()) root_tdb->setReadonlyTransactionOK(); |
| |
| // Inserts into non-audited indexes do not need to run in a transaction, |
| // if one does not exist. If one exists (which is the case during a create |
| // index operation), need to pass transid to all ESPs during the load |
| // index phase, otherwise they will get error 73s returned when they open |
| // the index. Store this information in the root TDB, so that the transaction |
| // can be passed to ESPs if needed. Dp2Insert::codeGen has set this |
| // generator flag. |
| |
| NABoolean recompWarn = |
| (CmpCommon::getDefault(RECOMPILATION_WARNINGS) == DF_ON); |
| if (recompWarn) root_tdb->setRecompWarn(); |
| |
| // Set the FROM_SHOWPLAN flag if the statement is from a showplan |
| const NAString * val = |
| ActiveControlDB()->getControlSessionValue("SHOWPLAN"); |
| if ( !(childOperType_ == REL_CONTROL_SESSION) |
| && (val) && (*val == "ON") ) |
| root_tdb->setFromShowplan(); |
| |
| if (CmpCommon::getDefault(EXE_LOG_RETRY_IPC) == DF_ON) |
| root_tdb->setLogRetriedIpcErrors(TRUE); |
| |
| if (anyEspFragments) |
| { |
| if (generator->getBindWA()->queryCanUseSeaMonster() && |
| generator->getQueryUsesSM()) |
| root_tdb->setQueryUsesSM(); |
| } |
| |
| generator->setGenObj(this, root_tdb); |
| |
| return 0; |
| |
| } // RelRoot::codeGen() |
| |
| short Sort::generateTdb(Generator * generator, |
| ComTdb * child_tdb, |
| ex_expr * sortKeyExpr, |
| ex_expr * sortRecExpr, |
| ULng32 sortKeyLen, |
| ULng32 sortRecLen, |
| ULng32 sortPrefixKeyLen, |
| ex_cri_desc * given_desc, |
| ex_cri_desc * returned_desc, |
| ex_cri_desc * work_cri_desc, |
| Lng32 saveNumEsps, |
| ExplainTuple *childExplainTuple, |
| NABoolean resizeCifRecord, |
| NABoolean considerBufferDefrag, |
| NABoolean operatorCIF) |
| { |
| |
| NADefaults &defs = ActiveSchemaDB()->getDefaults(); |
| |
| ULng32 numBuffers = (ULng32)getDefault(GEN_SORT_NUM_BUFFERS); |
| |
| CostScalar bufferSize = getDefault(GEN_SORT_MAX_BUFFER_SIZE); |
| |
| UInt32 bufferSize_as_uint32 = |
| (UInt32)(MINOF(CostScalar(UINT_MAX), bufferSize)).getValue(); |
| |
| // allocate buffer to hold atlease one row |
| bufferSize_as_uint32 = MAXOF(bufferSize_as_uint32, sortRecLen); |
| |
| GenAssert(sortRecLen <= bufferSize_as_uint32, |
| "Record Len greater than GEN_SORT_MAX_BUFFER_SIZE"); |
| |
| ComTdbSort * sort_tdb = 0; |
| // always start with quick sort. Sort will switch to |
| // replacement sort in case of overflow at runtime. |
| SortOptions *sort_options = new(generator->getSpace()) SortOptions(); |
| |
| Lng32 max_num_buffers = (Lng32)numBuffers; |
| NAString tmp; |
| CmpCommon::getDefault(SORT_ALGO, tmp, -1); |
| if(tmp == "HEAP") |
| sort_options->sortType() = SortOptions::ITER_HEAP; |
| else if(tmp == "REPSEL") |
| sort_options->sortType() = SortOptions::REPLACEMENT_SELECT; |
| else if(tmp == "IQS") |
| sort_options->sortType() = SortOptions::ITER_QUICK; |
| else if(tmp == "QS") |
| sort_options->sortType() = SortOptions::QUICKSORT; |
| max_num_buffers = (Lng32)getDefault(GEN_SORT_MAX_NUM_BUFFERS); |
| sort_options->internalSort() = TRUE; |
| |
| unsigned short threshold = (unsigned short) CmpCommon::getDefaultLong(SCRATCH_FREESPACE_THRESHOLD_PERCENT); |
| sort_options->scratchFreeSpaceThresholdPct() = threshold; |
| sort_options->sortMaxHeapSize() = (short)getDefault(SORT_MAX_HEAP_SIZE_MB); |
| sort_options->mergeBufferUnit() = (short)getDefault(SORT_MERGE_BUFFER_UNIT_56KB); |
| |
| //512kb default size initiliazed in sort_options. |
| if(sortRecLen >= sort_options->scratchIOBlockSize()) |
| { |
| Int32 maxScratchIOBlockSize = (Int32)getDefault(SCRATCH_IO_BLOCKSIZE_SORT_MAX); |
| // allocate space for atleast one row. |
| maxScratchIOBlockSize = MAXOF(maxScratchIOBlockSize, sortRecLen); |
| |
| GenAssert(sortRecLen <= maxScratchIOBlockSize, |
| "sortRecLen is greater than SCRATCH_IO_BLOCKSIZE_SORT_MAX"); |
| sort_options->scratchIOBlockSize() = MINOF(sortRecLen * 128, maxScratchIOBlockSize); |
| } |
| |
| sort_options->scratchIOVectorSize() = (Int16)getDefault(SCRATCH_IO_VECTOR_SIZE_SORT); |
| |
| if (CmpCommon::getDefault(EXE_BMO_SET_BUFFERED_WRITES) == DF_ON) |
| sort_options->setBufferedWrites(TRUE); |
| if (CmpCommon::getDefault(EXE_DIAGNOSTIC_EVENTS) == DF_ON) |
| sort_options->setLogDiagnostics(TRUE); |
| |
| // Disable Compiler Hints checks if: CQD is ON or if SYSTEM - only for HDD |
| if ( |
| (CmpCommon::getDefault(EXE_BMO_DISABLE_CMP_HINTS_OVERFLOW_SORT) == DF_ON) |
| || |
| ( |
| ((generator->getOverflowMode()== ComTdb::OFM_DISK) || |
| (generator->getOverflowMode()== ComTdb::OFM_MMAP)) |
| && |
| (CmpCommon::getDefault(EXE_BMO_DISABLE_CMP_HINTS_OVERFLOW_SORT) |
| == DF_SYSTEM ) |
| ) |
| ) |
| sort_options->setDisableCmpHintsOverflow(TRUE); |
| |
| if (CmpCommon::getDefault(EXE_BMO_DISABLE_OVERFLOW) == DF_ON) |
| sort_options->dontOverflow() = TRUE; |
| if (CmpCommon::getDefault(SORT_INTERMEDIATE_SCRATCH_CLEANUP) == DF_ON) |
| sort_options->setIntermediateScratchCleanup(TRUE); |
| |
| sort_options->setResizeCifRecord(resizeCifRecord); |
| sort_options->setConsiderBufferDefrag(considerBufferDefrag); |
| |
| short memoryQuotaMB = 0; |
| double memoryQuotaRatio; |
| Lng32 numStreams; |
| double bmoMemoryUsagePerNode = generator->getEstMemPerNode(getKey(), numStreams); |
| |
| if (CmpCommon::getDefault(SORT_MEMORY_QUOTA_SYSTEM) != DF_OFF) |
| { |
| // The CQD EXE_MEM_LIMIT_PER_BMO_IN_MB has precedence over the mem quota sys |
| memoryQuotaMB = (UInt16)defs.getAsDouble(EXE_MEM_LIMIT_PER_BMO_IN_MB); |
| |
| if (memoryQuotaMB > 0) { |
| sort_options->memoryQuotaMB() = memoryQuotaMB; |
| } else { |
| |
| UInt16 numBMOsInFrag = (UInt16)generator->getFragmentDir()->getNumBMOs(); |
| |
| // Apply quota system if either one the following two is true: |
| // 1. the memory limit feature is turned off and more than one BMOs |
| // 2. the memory limit feature is turned on |
| |
| NABoolean mlimitPerNode = defs.getAsDouble(BMO_MEMORY_LIMIT_PER_NODE_IN_MB) > 0; |
| |
| if ( mlimitPerNode || numBMOsInFrag > 1 || |
| (numBMOsInFrag == 1 && CmpCommon::getDefault(EXE_SINGLE_BMO_QUOTA) == DF_ON)) { |
| |
| memoryQuotaMB = (short) |
| computeMemoryQuota(generator->getEspLevel() == 0, |
| mlimitPerNode, |
| generator->getBMOsMemoryLimitPerNode().value(), |
| generator->getTotalNumBMOs(), |
| generator->getTotalBMOsMemoryPerNode().value(), |
| numBMOsInFrag, |
| bmoMemoryUsagePerNode, |
| numStreams, |
| memoryQuotaRatio |
| ); |
| |
| } |
| Lng32 sortMemoryLowbound = defs.getAsLong(BMO_MEMORY_LIMIT_LOWER_BOUND_SORT); |
| Lng32 memoryUpperbound = defs.getAsLong(BMO_MEMORY_LIMIT_UPPER_BOUND); |
| |
| if ( memoryQuotaMB < sortMemoryLowbound ) { |
| memoryQuotaMB = (short)sortMemoryLowbound; |
| memoryQuotaRatio = BMOQuotaRatio::MIN_QUOTA; |
| } |
| else if (memoryQuotaMB > memoryUpperbound) |
| memoryQuotaMB = memoryUpperbound; |
| } |
| } |
| |
| //BMO settings. By Default set this value to max available |
| //irrespective of quota is enabled or disabled. Sort at run time |
| //will manage to check for quota and available physical memory |
| //before consuming memory. Note that if memoryQuota is set zero, |
| //sort may not do physical memory or memory pressure checks. |
| if ( memoryQuotaMB <= 0 && |
| ! sort_options->disableCmpHintsOverflow() ) // compiler hints enabled |
| { |
| memoryQuotaMB = (UInt16)defs.getAsLong(EXE_MEMORY_AVAILABLE_IN_MB); |
| } |
| sort_options->memoryQuotaMB() = memoryQuotaMB; |
| |
| if(generator->getOverflowMode() == ComTdb::OFM_SSD ) |
| sort_options->bmoMaxMemThresholdMB() = (UInt16)defs.getAsLong(SSD_BMO_MAX_MEM_THRESHOLD_IN_MB); |
| else |
| sort_options->bmoMaxMemThresholdMB() = (UInt16)defs.getAsLong(EXE_MEMORY_AVAILABLE_IN_MB); |
| |
| sort_options->pressureThreshold() = |
| (short)getDefault(GEN_MEM_PRESSURE_THRESHOLD); |
| |
| short sortGrowthPercent = |
| RelExpr::bmoGrowthPercent(getEstRowsUsed(), getMaxCardEst()); |
| |
| sort_tdb = new(generator->getSpace()) |
| ComTdbSort(sortKeyExpr, |
| sortRecExpr, |
| sortKeyLen, |
| sortRecLen, |
| sortPrefixKeyLen, |
| returned_desc->noTuples() - 1, |
| child_tdb, |
| given_desc, |
| returned_desc, |
| work_cri_desc, |
| |
| // if sort input is from top, switch the UP and DOWN queue |
| // sizes |
| (sortFromTop() |
| ? (queue_index)getDefault(GEN_SORT_SIZE_UP) |
| : (queue_index)getDefault(GEN_SORT_SIZE_DOWN)), |
| (queue_index)getDefault(GEN_SORT_SIZE_UP), |
| (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(), |
| numBuffers, |
| bufferSize_as_uint32, |
| max_num_buffers, |
| sort_options, |
| sortGrowthPercent); |
| sort_tdb->setCollectNFErrors(this->collectNFErrors()); |
| |
| sort_tdb->setSortFromTop(sortFromTop()); |
| sort_tdb->setOverflowMode(generator->getOverflowMode()); |
| sort_tdb->setTopNSortEnabled(CmpCommon::getDefault(GEN_SORT_TOPN) == DF_ON); |
| sort_tdb->setBmoQuotaRatio(memoryQuotaRatio); |
| |
| if (generator->getUserSidetreeInsert()) |
| sort_tdb->setUserSidetreeInsert(TRUE); |
| |
| if (getTolerateNonFatalError() == RelExpr::NOT_ATOMIC_) |
| sort_tdb->setTolerateNonFatalError(TRUE); |
| |
| sort_tdb->setCIFON(operatorCIF); |
| |
| generator->initTdbFields(sort_tdb); |
| |
| double sortMemEst = generator->getEstMemPerInst(getKey()); |
| sort_tdb->setEstimatedMemoryUsage(sortMemEst / 1024); |
| generator->addToTotalEstimatedMemory(sortMemEst); |
| |
| if (sortPrefixKeyLen > 0) |
| ((ComTdbSort *)sort_tdb)->setPartialSort(TRUE); // do partial sort |
| |
| if(CmpCommon::getDefaultLong(SORT_REC_THRESHOLD) > 0) |
| ((ComTdbSort *)sort_tdb)->setMinimalSortRecs(CmpCommon::getDefaultLong(SORT_REC_THRESHOLD)); |
| |
| sort_tdb->setMemoryContingencyMB(getDefault(PHY_MEM_CONTINGENCY_MB)); |
| float bmoCtzFactor; |
| defs.getFloat(BMO_CITIZENSHIP_FACTOR, bmoCtzFactor); |
| sort_tdb->setBmoCitizenshipFactor((Float32)bmoCtzFactor); |
| sort_tdb->setSortMemEstInKBPerNode(bmoMemoryUsagePerNode /1024); |
| if (sortNRows()) |
| sort_tdb->setTopNThreshold(defs.getAsLong(GEN_SORT_TOPN_THRESHOLD)); |
| if (!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(sort_tdb, childExplainTuple, 0, generator)); |
| } |
| |
| // set the new up cri desc. |
| generator->setCriDesc(returned_desc, Generator::UP); |
| |
| generator->setGenObj(this, sort_tdb); |
| |
| // reset the expression generation flag to generate float validation pcode |
| generator->setGenNoFloatValidatePCode(FALSE); |
| |
| return 0; |
| } |
| |
| ////////////////////////////////////////////////////////////// |
| // |
| // Sort::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short Sort::codeGen(Generator * generator) |
| { |
| ExpGenerator * exp_gen = generator->getExpGenerator(); |
| Space * space = generator->getSpace(); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Layout at this node: |
| // |
| // |-------------------------------------------------| |
| // | input data | Sorted data | child's data | |
| // | ( I tupps ) | ( 1 tupp ) | ( C tupps ) | |
| // |-------------------------------------------------| |
| // <-- returned row to parent ---> |
| // <------------ returned row from child ------------> |
| // |
| // input data: the atp input to this node by its parent. |
| // sorted data: tupp where the sorted row is. |
| // this data is accessed by the key and by the data |
| // separately. |
| // The key data is in SQLMX_KEY_FORMAT and the data |
| // will be in either internal or exploded format. |
| // child data: tupps appended by the child |
| // |
| // Input to child: I + 1 tupps |
| // |
| // Tupps returned from child are only used to create the |
| // sorted data. They are not returned to parent. |
| // |
| ///////////////////////////////////////////////////////////////////////// |
| // Tupps returned from child are only used to create the |
| // sorted data. They are not returned to parent. |
| // |
| ///////////////////////////////////////////////////////////////////////// |
| |
| 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); |
| |
| Int32 work_atp = 1; // temps |
| Int32 work_atp_index = 2; // where the result row will be |
| ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(3, space); |
| |
| //All the records in the table will not be processed by a single sort |
| //instance if multiple sort instances are involved within ESPs. |
| Lng32 saveNumEsps = generator->getNumESPs(); |
| |
| if (sortFromTop()) |
| generator->setCriDesc(returned_desc, Generator::DOWN); |
| |
| // generate code for child tree |
| child(0)->codeGen(generator); |
| |
| //This value is set inside generator by my parent exchange node, |
| //as a global variable. Reset the saveNumEsps value back into |
| //generator since codegen of my children exchange nodes may have |
| //changed it. Resetting is performed here so the codegen of right |
| //child nodes of my parent gets the proper value. |
| generator->setNumESPs(saveNumEsps); |
| |
| ComTdb * child_tdb = (ComTdb *)(generator->getGenObj()); |
| ExplainTuple *childExplainTuple = generator->getExplainTuple(); |
| |
| // Before generating any expression for this node, set the |
| // the expression generation flag not to generate float |
| // validation PCode. This is to speed up PCode evaluation |
| generator->setGenNoFloatValidatePCode(TRUE); |
| |
| // generate an expression to create the input row |
| // to be sent to sort. |
| // The input row consists of: |
| // n + m values |
| // where, n is the number of encoded key columns. |
| // m is the total number of column values. |
| // At runtime, a contiguous row of n + m columns is created |
| // and then given to sort. |
| // sort prefix key columns are indexed from 0 to k where k < n. |
| |
| |
| // The data within the Sort buffer will be contiguous in the format |
| // | encoded keys | returned column values | |
| // ---------------------------------------- |
| // The keys will be in key format. |
| // The returned column values will be in Exploded or Compressed internal |
| // format. |
| |
| // generate the key encode value id list used for sorting |
| UInt32 sortKeyLen = 0; |
| UInt32 sortPrefixKeyLen = 0; |
| Int32 prefixKeyCnt = getPrefixSortKey().entries(); |
| ValueIdList sortKeyValIdList; |
| CollIndex sortKeyListIndex; |
| CollIndex sortRecListIndex; |
| for (sortKeyListIndex = 0; |
| sortKeyListIndex < getSortKey().entries(); |
| sortKeyListIndex++) |
| { |
| ItemExpr * skey_node = |
| ((getSortKey()[sortKeyListIndex]).getValueDesc())->getItemExpr(); |
| |
| short desc_flag = FALSE; |
| |
| if (skey_node->getOperatorType() == ITM_INVERSE) |
| { |
| desc_flag = TRUE; |
| } |
| |
| if (skey_node->getValueId().getType().getVarLenHdrSize() > 0) |
| { |
| // 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&)(skey_node->getValueId().getType()); |
| |
| //no cast to fixed char in the case of collation (Czech) |
| if (!CollationInfo::isSystemCollation(char_type.getCollation())) |
| { |
| skey_node = |
| new(generator->wHeap()) |
| Cast (skey_node, |
| (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() |
| ) |
| ) |
| ); |
| } |
| } |
| |
| CompEncode * enode |
| = new(generator->wHeap()) CompEncode(skey_node, desc_flag); |
| |
| enode->bindNode(generator->getBindWA()); |
| |
| sortKeyLen += enode->getValueId().getType().getTotalSize(); |
| |
| sortKeyValIdList.insert(enode->getValueId()); |
| |
| if (sortKeyListIndex < (CollIndex) prefixKeyCnt) |
| // sort key length is the prefix sort key length |
| // Note we need do this assignment only once. Need a better way |
| sortPrefixKeyLen = sortKeyLen; |
| } |
| /* |
| // Generate the key encode expression ... |
| ex_expr * sortKeyExpr = 0; |
| exp_gen->generateContiguousMoveExpr(sortKeyValIdList, |
| 0, // no conv nodes |
| work_atp, work_atp_index, |
| ExpTupleDesc::SQLMX_KEY_FORMAT, |
| sortKeyLen, &sortKeyExpr, |
| 0, // no tupp descr |
| ExpTupleDesc::SHORT_FORMAT); |
| */ |
| // Now generate the returned column value value id list that moves the input |
| // data into the sort input buffer using convert nodes. |
| ValueIdList sortRecValIdList; |
| ValueId valId; |
| for (valId = getGroupAttr()->getCharacteristicOutputs().init(); |
| getGroupAttr()->getCharacteristicOutputs().next(valId); |
| getGroupAttr()->getCharacteristicOutputs().advance(valId)) |
| { |
| // add the convert node |
| Convert * convNode = new(generator->wHeap())Convert(valId.getItemExpr()); |
| convNode->bindNode(generator->getBindWA()); |
| sortRecValIdList.insert(convNode->getValueId()); |
| } |
| |
| UInt32 sortRecLen = 0; |
| ex_expr * sortRecExpr = 0; |
| ex_expr * sortKeyExpr = 0; |
| ExpTupleDesc * tuple_desc = 0; |
| |
| |
| // contains the value ids that are being returned (the sorted values) |
| MapTable * returnedMapTable = 0; |
| ExpTupleDesc::TupleDataFormat tupleFormat = generator->getInternalFormat(); |
| |
| // resizeCifRecord indicator that tells us whether we need to resize the CIF row or not |
| // if CIF is not used then no resizing is done |
| // if CIF is used the based on some logic that will be defined we determine whether we need |
| // to resize the row or not |
| NABoolean resizeCifRecord = FALSE; |
| // Sometimes only the key value id list has entries and there are no |
| // additional characteristic outputs. |
| // This happens when Sort is used as a blocking operator for NAR (Non Atomic |
| // Rowsets). |
| NABoolean bmo_affinity = (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO_AFFINITY) == DF_ON); |
| NABoolean considerBufferDefrag = FALSE; |
| |
| if (sortRecValIdList.entries() > 0) |
| { |
| if (! bmo_affinity && |
| getCachedTupleFormat() != ExpTupleDesc::UNINITIALIZED_FORMAT && |
| CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT) == DF_SYSTEM && |
| CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_SYSTEM) |
| { |
| resizeCifRecord = getCachedResizeCIFRecord(); |
| tupleFormat = getCachedTupleFormat(); |
| considerBufferDefrag = getCachedDefrag() && resizeCifRecord; |
| } |
| else |
| { |
| //apply heuristic to determine the tuple format and whether we need to resize the row or not |
| tupleFormat = determineInternalFormat( sortRecValIdList, |
| this, |
| resizeCifRecord, |
| generator, |
| bmo_affinity, |
| considerBufferDefrag); |
| considerBufferDefrag = considerBufferDefrag && resizeCifRecord; |
| } |
| |
| } |
| |
| |
| if (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT) |
| { |
| exp_gen->generateContiguousMoveExpr(sortKeyValIdList, |
| 0, // no conv nodes |
| work_atp, work_atp_index, |
| ExpTupleDesc::SQLMX_KEY_FORMAT, |
| sortKeyLen, &sortKeyExpr, |
| 0, // no tupp descr |
| ExpTupleDesc::SHORT_FORMAT); |
| |
| exp_gen->generateContiguousMoveExpr(sortRecValIdList, |
| 0, // no convert nodes |
| work_atp, work_atp_index, |
| tupleFormat, |
| sortRecLen, &sortRecExpr, |
| &tuple_desc, ExpTupleDesc::SHORT_FORMAT); |
| sortRecLen += sortKeyLen; |
| if (resizeCifRecord) |
| { |
| // with CIF if we need to resize the rows then we need to keep track of the length |
| // of the row with the row itself |
| // in this case the allocated space for the row will be as folows |
| // |row size --(4 bytes)|sort key (sortkeyLen)| data (data length --may be variable) | |
| // ------------------------------------ |
| sortRecLen += sizeof(UInt32); |
| } |
| } |
| else |
| { |
| CMPASSERT(resizeCifRecord == FALSE); |
| sortKeyValIdList.insertSet(sortRecValIdList); |
| exp_gen->generateContiguousMoveExpr(sortKeyValIdList, |
| 0, // no conv nodes |
| work_atp, work_atp_index, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| sortRecLen, &sortRecExpr, |
| &tuple_desc, |
| ExpTupleDesc::SHORT_FORMAT); |
| |
| } |
| |
| |
| // add in the total area for the keys since we generated this separately |
| // and ensure the size is a factor of 8 so the data aligns correctly |
| |
| |
| |
| |
| |
| // describe the returned row |
| returned_desc->setTupleDescriptor((UInt16)returned_desc->noTuples() - 1, |
| tuple_desc); |
| |
| returnedMapTable = generator->appendAtEnd(); // allocates a new map table |
| generator->unlinkLast(); |
| |
| // Add the returned values to the map table. We get the returned value |
| // value id from the char outputs and the item expr from the sort record |
| // value id list, starting with the first item expr added to the sort |
| // record value id list by the for loop immediately preceding this one. |
| sortRecListIndex = 0; |
| for (valId = getGroupAttr()->getCharacteristicOutputs().init(); |
| getGroupAttr()->getCharacteristicOutputs().next(valId); |
| getGroupAttr()->getCharacteristicOutputs().advance(valId)) |
| { |
| // ????????????The first time through this loop, sortRecListIndex will point |
| // to the first return value convert node. This is because we |
| // stopped incrementing the sortRecListIndex after the last sort |
| // key was added and we did not increment it when we added the |
| // return value convert nodes. |
| Attributes *attr = |
| generator->getMapInfo(sortRecValIdList[sortRecListIndex++])->getAttr(); |
| |
| // ...add it to the new map table as if it belonged to |
| // the original value id... |
| MapInfo * mi = |
| generator->addMapInfoToThis(returnedMapTable, valId, attr); |
| |
| // All reference to the returned values from this point on |
| // will be at atp = 0, atp_index = last entry in returned desc. |
| // Offset will be the same as in the workAtp. |
| mi->getAttr()->setAtp(0); |
| mi->getAttr()->setAtpIndex(returned_desc->noTuples() - 1); |
| |
| // ... and make sure no more code gets generated for it. |
| mi->codeGenerated(); |
| |
| } |
| |
| // remove all appended map tables and return the returnedMapTable |
| generator->removeAll(last_map_table); |
| generator->appendAtEnd(returnedMapTable); |
| |
| short rc = |
| generateTdb(generator, |
| child_tdb, |
| sortKeyExpr, |
| sortRecExpr, |
| sortKeyLen, |
| sortRecLen, |
| sortPrefixKeyLen, |
| given_desc, |
| returned_desc, |
| work_cri_desc, |
| saveNumEsps, |
| childExplainTuple, |
| resizeCifRecord, |
| considerBufferDefrag, |
| (tupleFormat == ExpTupleDesc::SQLMX_ALIGNED_FORMAT)); |
| |
| return rc; |
| } |
| |
| |
| ExpTupleDesc::TupleDataFormat Sort::determineInternalFormat( const ValueIdList & valIdList, |
| RelExpr * relExpr, |
| NABoolean & resizeCifRecord, |
| Generator * generator, |
| NABoolean bmo_affinity, |
| NABoolean & considerBufferDefrag) |
| { |
| |
| RelExpr::CifUseOptions bmo_cif = RelExpr::CIF_SYSTEM; |
| |
| |
| if (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_OFF) |
| { |
| bmo_cif = RelExpr::CIF_OFF; |
| } |
| else |
| if (CmpCommon::getDefault(COMPRESSED_INTERNAL_FORMAT_BMO) == DF_ON) |
| { |
| bmo_cif = RelExpr::CIF_ON; |
| } |
| |
| //CIF_SYSTEM |
| |
| UInt32 sortKeyLength = getSortKey().getRowLength(); |
| return generator->determineInternalFormat(valIdList, |
| relExpr, |
| resizeCifRecord, |
| bmo_cif, |
| bmo_affinity, |
| considerBufferDefrag, |
| sortKeyLength); |
| |
| } |
| ////////////////////////////////////////////////////////////// |
| // |
| // SortFromTop::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short SortFromTop::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); |
| |
| // no tuples returned from this operator |
| ex_cri_desc * returned_desc = given_desc; |
| |
| // child gets one sorted row created in this operator. |
| ex_cri_desc * child_desc |
| = new(space) ex_cri_desc(given_desc->noTuples() + 1, space); |
| |
| Int32 work_atp = 1; // temps |
| Int32 work_atp_index = 2; // where the result row will be |
| ex_cri_desc * work_cri_desc = new(space) ex_cri_desc(3, space); |
| |
| //All the records in the table will not be processed by a single sort |
| //instance if multiple sort instances are involved within ESPs. |
| Lng32 saveNumEsps = generator->getNumESPs(); |
| |
| generator->setCriDesc(child_desc, Generator::DOWN); |
| |
| // Before generating any expression for this node, set the |
| // the expression generation flag not to generate float |
| // validation PCode. This is to speed up PCode evaluation |
| generator->setGenNoFloatValidatePCode(TRUE); |
| |
| MapTable *myMapTable = generator->appendAtEnd(); |
| |
| ULng32 sort_rec_len = 0; |
| ULng32 sort_key_len = 0; |
| ex_expr * sort_rec_expr = 0; |
| ex_expr * sort_key_expr = 0; |
| ExpTupleDesc * tuple_desc = 0; |
| |
| ValueIdList sortRecVIDlist; |
| ValueIdList sortKeyVIDlist; |
| CollIndex ii = 0; |
| for (ii = 0; ii < getSortKey().entries(); ii++) |
| { |
| ItemExpr * skey_node = |
| ((getSortKey()[ii]).getValueDesc())->getItemExpr(); |
| |
| if (skey_node->getOperatorType() != ITM_INDEXCOLUMN) |
| GenAssert(0, "Must be IndexColumn"); |
| |
| IndexColumn * ic = (IndexColumn*)skey_node; |
| NABoolean found = FALSE; |
| CollIndex jj = 0; |
| while ((NOT found) && (jj < getSortRecExpr().entries())) |
| { |
| const ItemExpr *assignExpr = |
| getSortRecExpr()[jj].getItemExpr(); |
| |
| ItemExpr * tgtCol = assignExpr->child(0)->castToItemExpr(); |
| if (tgtCol->getOperatorType() != ITM_BASECOLUMN) |
| GenAssert(0, "Must be BaseColumn"); |
| |
| ValueId tgtValueId = tgtCol->getValueId(); |
| |
| ValueId srcValueId = |
| assignExpr->child(1)->castToItemExpr()->getValueId(); |
| |
| ItemExpr * srcVal = assignExpr->child(1)->castToItemExpr(); |
| |
| if (ic->getNAColumn()->getColName() == |
| ((BaseColumn*)tgtCol)->getNAColumn()->getColName()) |
| { |
| found = TRUE; |
| |
| //***TBD*** need to handle descnding. Get that from index desc. |
| short desc_flag = FALSE; |
| if (ic->getNAColumn()->getClusteringKeyOrdering() == DESCENDING) |
| { |
| desc_flag = TRUE; |
| } |
| |
| if (skey_node->getValueId().getType().getVarLenHdrSize() > 0) |
| { |
| // Explode varchars by moving them to a fixed field |
| // whose length is equal to the max length of varchar. |
| |
| const CharType& char_type = |
| (CharType&)(skey_node->getValueId().getType()); |
| |
| //no cast to fixed char in the case of collation (Czech) |
| if (!CollationInfo::isSystemCollation(char_type.getCollation())) |
| { |
| skey_node = |
| new(generator->wHeap()) |
| Cast (srcVal, |
| (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() |
| ) |
| ) |
| ); |
| } |
| } |
| else |
| { |
| skey_node = new(generator->wHeap()) |
| Cast(srcVal, &tgtValueId.getType()); |
| } |
| |
| CompEncode * enode |
| = new(generator->wHeap()) CompEncode(skey_node, desc_flag); |
| |
| enode->bindNode(generator->getBindWA()); |
| |
| sort_key_len += enode->getValueId().getType().getTotalSize(); |
| |
| sortKeyVIDlist.insert(enode->getValueId()); |
| } // if |
| |
| jj++; |
| } // while |
| |
| if (NOT found) |
| { |
| GenAssert(0, "Key not found in newRecExprArray"); |
| } |
| } // for |
| // genearate sort key expr |
| //ex_expr * sort_key_expr = 0; |
| if (sortKeyVIDlist.entries() > 0) |
| { |
| exp_gen->generateContiguousMoveExpr(sortKeyVIDlist, |
| 0, // no conv nodes |
| work_atp, work_atp_index, |
| ExpTupleDesc::SQLMX_KEY_FORMAT, |
| sort_key_len, &sort_key_expr, |
| 0, // no tupp descr |
| ExpTupleDesc::SHORT_FORMAT); |
| |
| } |
| //sortRecVIDlist = sortKeyVIDlist; |
| |
| for (ii = 0; ii < getSortRecExpr().entries(); ii++) |
| { |
| const ItemExpr *assignExpr = |
| getSortRecExpr()[ii].getItemExpr(); |
| |
| ValueId tgtValueId = |
| assignExpr->child(0)->castToItemExpr()->getValueId(); |
| ValueId srcValueId = |
| assignExpr->child(1)->castToItemExpr()->getValueId(); |
| |
| ItemExpr * ie = NULL; |
| ie = new(generator->wHeap()) |
| Cast(assignExpr->child(1), |
| &tgtValueId.getType()); |
| |
| ie->bindNode(generator->getBindWA()); |
| sortRecVIDlist.insert(ie->getValueId()); |
| } // for |
| |
| ExpTupleDesc::TupleDataFormat tupleFormat = generator->getInternalFormat(); |
| NABoolean resizeCifRecord = FALSE; |
| NABoolean considerBufferDefrag = FALSE; |
| |
| if (sortRecVIDlist.entries()>0) |
| { |
| tupleFormat = determineInternalFormat( sortRecVIDlist, |
| this, |
| resizeCifRecord, |
| generator, |
| FALSE, |
| considerBufferDefrag); |
| |
| exp_gen->generateContiguousMoveExpr(sortRecVIDlist, |
| 0 /*don't add conv nodes*/, |
| work_atp, work_atp_index, |
| tupleFormat, |
| sort_rec_len, &sort_rec_expr, |
| &tuple_desc, ExpTupleDesc::SHORT_FORMAT); |
| } |
| //sort_key_len = ROUND8(sort_key_len); ????????? |
| |
| sort_rec_len += sort_key_len; |
| |
| if (resizeCifRecord) |
| { |
| // with CIF if we need to resize the rows then we need to keep track of the length |
| // of the row with the row itself |
| // in this case the allocated space for the row will be as folows |
| // |row size --(4 bytes)|sort key (sortkeyLen)| data (data length --may be variable) | |
| // ------------------------------------ |
| sort_rec_len += sizeof(UInt32); |
| } |
| |
| |
| for (CollIndex i = 0; i < (CollIndex) sortRecVIDlist.entries(); i++) |
| { |
| ItemExpr * cn = (sortRecVIDlist[i]).getItemExpr(); |
| |
| Attributes *attrib = |
| generator->getMapInfo(cn->getValueId())->getAttr(); |
| |
| MapInfo * mi = |
| generator-> |
| addMapInfoToThis(myMapTable, |
| cn->child(0)->castToItemExpr()->getValueId(), |
| attrib); |
| |
| // All reference to the sorted values from this point on |
| // will be at atp = 0, atp_index = last entry in child desc. |
| // Offset will be the same as in the workAtp. |
| mi->getAttr()->setAtp(0); |
| mi->getAttr()->setAtpIndex(child_desc->noTuples() - 1); |
| |
| // ... and make sure no more code gets generated for it. |
| mi->codeGenerated(); |
| } |
| |
| // generate code for child tree |
| child(0)->codeGen(generator); |
| |
| //This value is set inside generator by my parent exchange node, |
| //as a global variable. Reset the saveNumEsps value back into |
| //generator since codegen of my children exchange nodes may have |
| //changed it. Resetting is performed here so the codegen of right |
| //child nodes of my parent gets the proper value. |
| generator->setNumESPs(saveNumEsps); |
| |
| ComTdb * child_tdb = (ComTdb *)(generator->getGenObj()); |
| ExplainTuple *childExplainTuple = generator->getExplainTuple(); |
| |
| // remove all appended map tables. No values are returned by |
| // this node. |
| generator->removeAll(last_map_table); |
| |
| short rc = |
| generateTdb(generator, |
| child_tdb, |
| sort_key_expr, |
| sort_rec_expr, |
| sort_key_len, |
| sort_rec_len, |
| 0, //sort_prefix_key_len, |
| given_desc, |
| child_desc, |
| work_cri_desc, |
| saveNumEsps, |
| childExplainTuple, |
| resizeCifRecord, |
| considerBufferDefrag); |
| |
| return rc; |
| } |
| |
| CostScalar Sort::getEstimatedRunTimeMemoryUsage(Generator *generator, NABoolean perNode, Lng32 *numStreams) |
| { |
| GroupAttributes * childGroupAttr = child(0).getGroupAttr(); |
| Lng32 childRecordSize = |
| childGroupAttr->getCharacteristicOutputs().getRowLength(); |
| CostScalar rowsUsed; |
| if (sortNRows() && (topNRows_ > 0) |
| && (topNRows_ <= getDefault(GEN_SORT_TOPN_THRESHOLD))) |
| rowsUsed = topNRows_; |
| else |
| rowsUsed = getEstRowsUsed(); |
| CostScalar totalMemory = rowsUsed * childRecordSize; |
| CostScalar estMemPerNode; |
| CostScalar estMemPerInst; |
| |
| //TODO: Line below dumps core at times |
| //const CostScalar maxCard = childGroupAttr->getResultMaxCardinalityForEmptyInput(); |
| const CostScalar maxCard = 0; |
| |
| Lng32 numOfStreams = 1; |
| const PhysicalProperty* const phyProp = getPhysicalProperty(); |
| if (phyProp != NULL) |
| { |
| PartitioningFunction * partFunc = phyProp -> getPartitioningFunction() ; |
| numOfStreams = partFunc->getCountOfPartitions(); |
| if (numOfStreams <= 0) |
| numOfStreams = 1; |
| } |
| if (numStreams != NULL) |
| *numStreams = numOfStreams; |
| estMemPerNode = totalMemory / MINOF(MAXOF(gpClusterInfo->getTotalNumberOfCPUs(), 1), numOfStreams); |
| estMemPerInst = totalMemory / numOfStreams; |
| OperBMOQuota *operBMOQuota = new (generator->wHeap()) OperBMOQuota(getKey(), numOfStreams, |
| estMemPerNode, estMemPerInst, rowsUsed, maxCard); |
| generator->getBMOQuotaMap()->insert(operBMOQuota); |
| if (perNode) |
| return estMemPerNode; |
| else |
| return estMemPerInst; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // Tuple::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short Tuple::codeGen(Generator * generator) |
| { |
| // code generation for this node doesn't do anything. |
| // A Tuple node returns one tuple of expression of constants. This |
| // expression is evaluated where it is used. Since this node doesn't |
| // produce anything, the returned cri desc is same as the input cri desc. |
| // We could do away with generating something here but the |
| // parent node expects a child node to return 'something' before it |
| // can continue. |
| Queue * qList = new(generator->getSpace()) Queue(generator->getSpace()); |
| 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); |
| } |
| |
| ComTdbTupleLeaf *tuple_tdb = new(generator->getSpace()) |
| ComTdbTupleLeaf(qList, |
| 0, // no tuple returned. Length = 0. |
| 0, // no tupp index |
| predExpr, |
| generator->getCriDesc(Generator::DOWN), |
| generator->getCriDesc(Generator::DOWN), |
| (queue_index)getDefault(GEN_TUPL_SIZE_DOWN), |
| (queue_index)getDefault(GEN_TUPL_SIZE_UP), |
| (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(), |
| getDefault(GEN_TUPL_NUM_BUFFERS), |
| getDefault(GEN_TUPL_BUFFER_SIZE)); |
| generator->initTdbFields(tuple_tdb); |
| |
| if(!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(tuple_tdb, 0, 0, generator)); |
| } |
| |
| generator->setGenObj(this, tuple_tdb); |
| generator->setCriDesc(generator->getCriDesc(Generator::DOWN), |
| Generator::UP); |
| |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // TupleList::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short TupleList::codeGen(Generator * generator) |
| { |
| Space * space = generator->getSpace(); |
| ExpGenerator * expGen = generator->getExpGenerator(); |
| ex_cri_desc * givenDesc |
| = generator->getCriDesc(Generator::DOWN); |
| ex_cri_desc * returnedDesc |
| = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| Int32 tuppIndex = returnedDesc->noTuples() - 1; |
| |
| // disable common subexpression elimination for now. |
| // There is a problem which shows up due to common subexpression |
| // elimination. See case 10-040402-2209. |
| // After the problem is diagnosed and fixed in tuple list, |
| // we won't need to disable subexp elimination. |
| generator->getExpGenerator()->setEnableCommonSubexpressionElimination(FALSE); |
| |
| Queue * qList = new(generator->getSpace()) Queue(generator->getSpace()); |
| ULng32 tupleLen = 0; |
| ExpTupleDesc * tupleDesc = 0; |
| ExprValueId eVid(tupleExprTree()); |
| ItemExprTreeAsList tupleList(&eVid, ITM_ITEM_LIST); |
| CollIndex nTupEntries = (CollIndex) tupleList.entries(); |
| for (CollIndex i = 0; i < nTupEntries; i++) |
| { |
| ex_expr * moveExpr = NULL; |
| |
| ItemExpr * tuple = |
| ((ItemExpr *) tupleList[i])->child(0)->castToItemExpr(); |
| |
| ExprValueId tVid(tuple); |
| ItemExprTreeAsList tupleTree(&tVid, ITM_ITEM_LIST); |
| |
| ValueIdList convVIDlist; |
| BindWA *bindWA = generator->getBindWA(); |
| |
| NABoolean castTo = castToList().entries() > 0; |
| for (CollIndex j = 0; j < tupleExpr().entries(); j++) |
| { |
| ItemExpr * castNode = tupleExpr()[j].getItemExpr(); |
| ItemExpr * childNode = (ItemExpr *) tupleTree[j]; |
| if (castTo) |
| { |
| // When we have ins/upd target cols which are |
| // MP NCHAR in MX-NSK-Rel1 (i.e., SINGLE-byte), |
| // and the source was from a Tuple/TupleList, |
| // then we must do some magic Assign binding |
| // to ensure that the single-byte even-num-of-bytes |
| // "constraint" is not violated. |
| // |
| // Build + copy this "constraint" -- |
| // NOTE: tmpAssign MUST BE ON HEAP -- see TupleList::bindNode() ! |
| |
| Assign *tmpAssign = new(bindWA->wHeap()) |
| Assign(castToList()[j].getItemExpr(), childNode); |
| |
| //*************************************************************** |
| // 10-0414-2428: Note that this assign is for inserts or updates |
| // (1) castTo is set only when insert DMLs |
| // (2) Assign constructor argument UserSpecified is set to TRUE |
| //*************************************************************** |
| setInUpdateOrInsert(bindWA, NULL, REL_INSERT); |
| tmpAssign = (Assign *)tmpAssign->bindNode(bindWA); |
| setInUpdateOrInsert(bindWA, NULL); |
| childNode = tmpAssign->getSource().getItemExpr(); |
| //don't allow LOB insert in a tuple list |
| if (childNode->getOperatorType() == ITM_LOBINSERT) |
| { |
| // cannot have this function in a values list with |
| // multiple tuples. Use a single tuple. |
| *CmpCommon::diags() << DgSqlCode(-4483); |
| GenExit(); |
| return -1; |
| |
| } |
| castNode->child(0) = childNode; |
| } |
| else |
| { |
| childNode = childNode->bindNode(bindWA); |
| |
| if ( (castNode->child(0)) && |
| (castNode->child(0)->getOperatorType() == ITM_INSTANTIATE_NULL) ) |
| { |
| // if this tuplelist is part of a subquery an additional node |
| // of type ITM_INSTANTIATE_NULL is placed in the binder; check if |
| // that is the case |
| castNode->child(0)->child(0) = childNode; // need to fix this |
| } |
| else |
| { |
| castNode->child(0) = childNode; |
| } |
| } |
| |
| castNode->bindNode(bindWA); |
| |
| // if any unknown type in the tuple, |
| // coerce it to the target type. |
| childNode->bindNode(bindWA); |
| childNode->getValueId().coerceType(castNode->getValueId().getType()); |
| |
| MapInfo * mInfo = generator->getMapInfoAsIs(castNode->getValueId()); |
| if (mInfo) |
| mInfo->resetCodeGenerated(); |
| castNode->unmarkAsPreCodeGenned(); |
| convVIDlist.insert(castNode->getValueId()); |
| } |
| |
| GenAssert(!bindWA->errStatus(), "bindWA"); |
| expGen->generateContiguousMoveExpr(convVIDlist, |
| 0 /*don't add conv nodes*/, |
| 0 /*atp*/, |
| tuppIndex, |
| generator->getInternalFormat(), |
| tupleLen, |
| &moveExpr, |
| &tupleDesc, |
| ExpTupleDesc::SHORT_FORMAT); |
| qList->insert(moveExpr); |
| } |
| |
| returnedDesc->setTupleDescriptor(tuppIndex, tupleDesc); |
| |
| // generate expression for selection predicate, if it exists |
| ex_expr *predExpr = NULL; |
| if(NOT selectionPred().isEmpty()) |
| { |
| ItemExpr* pred = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| expGen->generateExpr(pred->getValueId(),ex_expr::exp_SCAN_PRED, |
| &predExpr); |
| } |
| |
| // Compute the buffer size based on upqueue size and row size. |
| // Try to get enough buffer space to hold twice as many records |
| // as the up queue. |
| ULng32 buffersize = getDefault(GEN_TUPL_BUFFER_SIZE); |
| Int32 numBuffers = getDefault(GEN_TUPL_NUM_BUFFERS); |
| queue_index upqueuelength = (queue_index)getDefault(GEN_TUPL_SIZE_UP); |
| ULng32 cbuffersize = |
| ((tupleLen + sizeof(tupp_descriptor)) |
| * (upqueuelength * 2/numBuffers)) + |
| SqlBufferNeededSize(0,0); |
| buffersize = buffersize > cbuffersize ? buffersize : cbuffersize; |
| |
| ComTdbTupleLeaf *tupleTdb = new(generator->getSpace()) |
| ComTdbTupleLeaf(qList, |
| tupleLen, |
| tuppIndex, |
| predExpr, |
| givenDesc, |
| returnedDesc, |
| (queue_index)getDefault(GEN_TUPL_SIZE_DOWN), |
| (queue_index)upqueuelength, |
| (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(), |
| getDefault(GEN_TUPL_NUM_BUFFERS), |
| buffersize); |
| generator->initTdbFields(tupleTdb); |
| |
| if(!generator->explainDisabled()) |
| { |
| generator->setExplainTuple( |
| addExplainInfo(tupleTdb, 0, 0, generator)); |
| } |
| |
| generator->setGenObj(this, tupleTdb); |
| generator->setCriDesc(givenDesc, Generator::DOWN); |
| generator->setCriDesc(returnedDesc, Generator::UP); |
| |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // ExplainFunc::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short ExplainFunc::codeGen(Generator * generator) |
| { |
| ExpGenerator * expGen = generator->getExpGenerator(); |
| Space * space = generator->getSpace(); |
| |
| // allocate a map table for the retrieved columns |
| generator->appendAtEnd(); |
| |
| ex_expr *explainExpr = 0; |
| |
| ex_cri_desc * givenDesc |
| = generator->getCriDesc(Generator::DOWN); |
| |
| ex_cri_desc * returnedDesc |
| = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| |
| ex_cri_desc * paramsDesc |
| = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| |
| |
| // Assumption (for now): retrievedCols contains ALL columns from |
| // the table/index. This is because this operator does |
| // not support projection of columns. Add all columns from this table |
| // to the map table. |
| // |
| // The row retrieved from filesystem is returned as the last entry in |
| // the returned atp. |
| |
| const ValueIdList & columnList = getTableDesc()->getColumnList(); |
| const CollIndex numColumns = columnList.entries(); |
| |
| Attributes ** attrs = new(generator->wHeap()) Attributes * [numColumns]; |
| |
| for (CollIndex i = 0; i < numColumns; i++) |
| { |
| ItemExpr * col_node = ((columnList[i]).getValueDesc())->getItemExpr(); |
| |
| attrs[i] = (generator->addMapInfo(col_node->getValueId(), 0))-> |
| getAttr(); |
| } |
| |
| ExpTupleDesc *explTupleDesc = 0; |
| ULng32 explTupleLength = 0; |
| expGen->processAttributes(numColumns, |
| attrs, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| explTupleLength, |
| 0, returnedDesc->noTuples() - 1, |
| &explTupleDesc, ExpTupleDesc::SHORT_FORMAT); |
| |
| // delete [] attrs; |
| // NADELETEBASIC is used because compiler does not support delete[] |
| // operator yet. Should be changed back later when compiler supports |
| // it. |
| NADELETEBASIC(attrs, generator->wHeap()); |
| |
| // add this descriptor to the work cri descriptor. |
| returnedDesc->setTupleDescriptor(returnedDesc->noTuples()-1, explTupleDesc); |
| |
| // generate explain selection expression, if present |
| if (! selectionPred().isEmpty()) |
| { |
| ItemExpr * newPredTree = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| expGen->generateExpr(newPredTree->getValueId(), ex_expr::exp_SCAN_PRED, |
| &explainExpr); |
| } |
| |
| // generate move expression for the parameter list |
| ex_expr *moveExpr = 0; |
| |
| ExpTupleDesc *tupleDesc = 0; |
| ULng32 tupleLength = 0; |
| if (! getProcInputParamsVids().isEmpty()) |
| { |
| |
| expGen->generateContiguousMoveExpr(getProcInputParamsVids(), |
| -1, |
| 0, |
| paramsDesc->noTuples()-1, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| tupleLength, |
| &moveExpr, |
| &tupleDesc, |
| ExpTupleDesc::LONG_FORMAT); |
| |
| // add this descriptor to the work cri descriptor. |
| paramsDesc->setTupleDescriptor(paramsDesc->noTuples()-1, tupleDesc); |
| } |
| |
| // allocate buffer space to contain atleast 2 rows. |
| ULng32 bufferSize = (explTupleLength+100/*padding*/) * 2/*rows*/; |
| bufferSize = MAXOF(bufferSize, 30000); // min buf size 30000 |
| Int32 numBuffers = 3; // allocate 3 buffers |
| |
| ComTdbExplain *explainTdb |
| = new(space) |
| ComTdbExplain(givenDesc, // given_cri_desc |
| returnedDesc, // returned cri desc |
| 8, // Down queue size |
| 16, // Up queue size0 |
| returnedDesc->noTuples() - 1, // Index in atp of return |
| // tuple. |
| explainExpr, // predicate |
| paramsDesc, // Descriptor of params Atp |
| tupleLength, // Length of params Tuple |
| moveExpr, // expression to calculate |
| // the explain parameters |
| numBuffers, // Number of buffers to allocate |
| bufferSize); // Size of each buffer |
| generator->initTdbFields(explainTdb); |
| |
| // 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(explainTdb, 0, 0, generator)); |
| } |
| generator->setCriDesc(givenDesc, Generator::DOWN); |
| generator->setCriDesc(returnedDesc, Generator::UP); |
| generator->setGenObj(this, explainTdb); |
| |
| return 0; |
| } |
| |
| // PhysTranspose::codeGen() ------------------------------------------ |
| // Generate code (a TDB node with corresponding Expr expressions) for |
| // the PhysTranspose node. This node implements the TRANSPOSE operator. |
| // |
| // 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. |
| // |
| // Side Effects: Generates an ComTdbTranspose along with all the required |
| // expressions for the transpose node. |
| // |
| // Generates explain info. |
| // |
| // Alters the state of the generator, including modifing |
| // the map table, setting references to the generated Tdb |
| // and explain info. etc. |
| // |
| short |
| PhysTranspose::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(); |
| |
| |
| // Generate the given and returned composite row descriptors |
| // Transpose adds a tupp to the row returned by the *child* |
| // or the Given row, if no columns of the child are outputs of |
| // this node. |
| // |
| ex_cri_desc *givenCriDesc = generator->getCriDesc(Generator::DOWN); |
| |
| |
| // Determine if any of the childs outputs are also outputs of the node. |
| // (if not, then don't pass them up the queue, later we will remove them |
| // from the map table.) |
| // |
| |
| // Get the outputs of this node. |
| // |
| ValueIdSet transOutputs = getGroupAttr()->getCharacteristicOutputs(); |
| |
| // Get all the ValueIds that this node generates. |
| // |
| ValueIdSet transVals; |
| |
| for(CollIndex v = 0; v < transUnionVectorSize(); v++) { |
| transVals.insertList(transUnionVector()[v]); |
| } |
| |
| // Remove from the outputs, those values that are generated by this |
| // node. (if none are left, then the child's outputs are not needed |
| // above, otherwise they are.) |
| // |
| transOutputs -= transVals; |
| |
| ex_cri_desc *returnedCriDesc = 0; |
| |
| if (transOutputs.isEmpty()) { |
| |
| // The child's outputs are not needed above, so do not pass them |
| // along. |
| // |
| returnedCriDesc = |
| new(space) ex_cri_desc(givenCriDesc->noTuples() + 1, space); |
| |
| // 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 reomved from the map table) |
| // |
| localMapTable->setAllAtp(1); |
| |
| } else { |
| |
| // The child's outputs are needed above, so must pass them along. |
| // |
| returnedCriDesc = |
| new(space) ex_cri_desc(childCriDesc->noTuples() + 1, space); |
| } |
| |
| // transposeCols is the last Tp in Atp 0. |
| // |
| const Int32 transColsAtpIndex = returnedCriDesc->noTuples() - 1; |
| const Int32 transColsAtp = 0; |
| |
| // The length of the new tuple which will contain the columns |
| // generated by transpose. |
| // |
| ULng32 transColsTupleLen; |
| |
| // The Transpose node contains a vector of ValueIdLists. There is |
| // one entry for each transpose set, plus one entry for the key |
| // values. Each entry contains a list of ValueIdUnion Nodes. The |
| // first entry contains a list with one ValueIdUnion node. This node |
| // is for the Const. Values (1 - N) representing the Key Values. The |
| // other entries contain lists of ValueIdUnion nodes for the |
| // Transposed Values. Each of these entries of the vector represent |
| // a transpose set. If the transpose set contains a list of values, |
| // then there will be only one ValueIdUnion node in the list. If |
| // the transpose set contains a list of lists of values, then there |
| // will be as many ValueIdUnion nodes as there are items in the |
| // sublists. Each ValueIdUnion within a list must contain the same |
| // number of elements. |
| // |
| |
| // The cardinality of an entry in the vector is the number of entries |
| // in each of its ValueIdUnion nodes. The cardinality of the first |
| // entry (the key values) is equal to the sum of the cardinalities of |
| // the subsequent entries (the transpose values). |
| |
| // In order to generate the expressions for the transpose node the |
| // ValueIdUnion nodes for the values expressions (entries 1 - 3 below) |
| // have to be indexed from 0 - N (0 - 6 in the example below). |
| // In order to translate from an index from 0 - N to an index of the |
| // vector and an index within the ValueIdUnion, numExprs contains the |
| // number of ValExprs for each ValueIdUnion. Eg. if the transUnionVector |
| // contains these entries: |
| // |
| // Vector Num |
| // Entry Entries Card. |
| // 0 1 7 ValueIdUnion(1,2,3,4,5,6,7) |
| // 1 1 2 ValueIdUnion(A,B) |
| // 2 1 3 ValueIdUnion(X,Y,Z) |
| // 3 2 2 ValueIdUnion(1,2) , ValueIdUnion('hello', 'world') |
| // |
| // Then numUnionLists would be 4 |
| // |
| // The numKeyExprs would be 7 (the cardinality of the Key Values, see below) |
| // |
| // The total cardinality of the rest of the ValueIdUnions also equals 7. |
| // |
| // numExprs will have 4 (numUnionLists) entries with values 7, 2, 3 and 2. |
| // |
| // The number of entries in the transUnionVector. |
| // |
| CollIndex numUnionLists = transUnionVectorSize(); |
| |
| // Allocate a vector of size numUnionLists to hold the cardinality of each |
| // entry in the vector. |
| // |
| CollIndex *numExprs = new(space) CollIndex[numUnionLists]; |
| |
| // The Tuple Desc describing the tuple containing the new transpose columns |
| // It is generated when the expressions are generated. |
| // |
| ExpTupleDesc *transColsTupleDesc = 0; |
| |
| // Loop index used throughout |
| // |
| CollIndex unionListNum; |
| |
| // Used below for a sanity check (assert). |
| // |
| CollIndex totNumExprs = 0; |
| |
| // Populate the numExprs array. |
| // |
| for(unionListNum = 0; unionListNum < numUnionLists; unionListNum++) { |
| |
| // If the entry of the vector has at least one ValueIdUnion ... |
| // (All ValueIdUnions in an entry of the vector should have the |
| // same number of enties, so use the first entry) |
| // |
| if(transUnionVector()[unionListNum].entries() > 0) |
| numExprs[unionListNum] = |
| ((ValueIdUnion *)(transUnionVector()[unionListNum])[0]. |
| getValueDesc()->getItemExpr())->entries(); |
| else |
| // If the keyCol was not specified, then the first entry of the |
| // vector may contain no ValueIdUnion nodes. |
| // |
| numExprs[unionListNum] = 0; |
| |
| // Used in an assert below. |
| // |
| totNumExprs += numExprs[unionListNum]; |
| } |
| |
| // The number of Key Exprs. The item expressions should be the Constants |
| // 1 -> numKeyExprs or non-existant. The Key Exprs are the first entry. |
| // |
| const CollIndex numKeyExprs = numExprs[0]; |
| |
| // If there are no key expressions, the the number of expressions |
| // to generate is the totNumExprs. |
| // |
| const CollIndex numMovExprs = (numKeyExprs == 0 ? totNumExprs : numKeyExprs); |
| |
| // The total number of item expressions in the value unions should equal |
| // the number of key values in the first ValueIdUnion. |
| // |
| GenAssert(totNumExprs == numKeyExprs * 2 || numKeyExprs == 0, |
| "Transpose: Internal Error"); |
| |
| // Allocate space to hold numMovExprs expressions. Each expression will |
| // compute one value for each of the ValueIdUnions. |
| // A constant Key Value will be computed in each expression, |
| // but only value expressions from one vector entry will be computed. |
| // All other value expressions will be NULL. |
| // |
| ex_expr ** transExprs = new(space)ex_expr*[numMovExprs]; |
| |
| // A list of the ValueIds will be constructed and used to generate |
| // the expressions |
| // |
| ValueIdList ValueIds; |
| |
| // A new item expression which casts the result to the proper type |
| // will be constructed. |
| // |
| Cast *castExpr = 0; |
| |
| // Loop index |
| // |
| CollIndex exprNum; |
| |
| // For each transpose value expression (also equal to the number of key |
| // values if they exist) |
| // |
| for(exprNum = 0; exprNum < numMovExprs; exprNum++) { |
| |
| // Clear the list of value Ids (used in the previous iteration |
| // through this loop). |
| // |
| ValueIds.clear(); |
| |
| // Contruct Key Expression with Cast node if they exist. |
| // The key ValueIdUnion is in position 0. |
| // |
| if(numKeyExprs != 0) { |
| |
| // Get the key ValueIdUnion. |
| // |
| ValueIdUnion *keyValIdUnion = |
| (ValueIdUnion *)((transUnionVector()[0])[0]. |
| getValueDesc()->getItemExpr()); |
| |
| // Extract one (exprNum) entry from the ValueIdUnion and add a |
| // Cast to cast it to the proper type. |
| // |
| castExpr = new(generator->wHeap()) |
| Cast(keyValIdUnion->getSource(exprNum).getValueDesc()->getItemExpr(), |
| &(keyValIdUnion->getResult().getType())); |
| |
| // Bind this new item expression. |
| // |
| castExpr->bindNode(generator->getBindWA()); |
| |
| // This should never fail at this point !!! |
| // |
| GenAssert(! generator->getBindWA()->errStatus(), |
| "Transpose: Internal Error"); |
| |
| // Insert the Value Id for this item expression into the list of |
| // Value Ids that will be used to generate the final expression. |
| // |
| ValueIds.insert(castExpr->getValueId()); |
| } |
| |
| // ValueIds may now contains one ValueId representing the key value. |
| // |
| |
| // Translate the expression number (exprNum) from 0 -> numKeyExprs - 1 |
| // to vector entry number (vectorEntryNum) and expression number |
| // (valExprNum) |
| // |
| |
| // Binary expression indexes. |
| // |
| CollIndex vectorEntryNum, valExprNum; |
| |
| // Keep track of how many expressions are covered by the current |
| // ValueIdUnion and those previous. |
| // |
| CollIndex numCoveredExprs; |
| |
| // Translate the Unary expression index (exprNum) into a binary |
| // index (vectorEntryNum, valExprNum) |
| // (A for-loop with no body) |
| // |
| for((vectorEntryNum = 1, |
| numCoveredExprs = numExprs[1], |
| valExprNum = exprNum); |
| |
| // Does this ValueIdUnion cover this expression number |
| // |
| numCoveredExprs <= exprNum; |
| |
| // If not, adjust the indexes and try the next one. |
| (numCoveredExprs += numExprs[vectorEntryNum + 1], |
| valExprNum -= numExprs[vectorEntryNum], |
| vectorEntryNum++)); |
| |
| // At this point: |
| // vectorEntryNum is index of the transUnionVector for this exprNum. |
| // valExprNum is index into the ValueIdUnion. |
| |
| GenAssert(vectorEntryNum > 0 && vectorEntryNum < numUnionLists, |
| "Transpose: Internal Error"); |
| |
| GenAssert(valExprNum < numExprs[vectorEntryNum], |
| "Transpose: Internal Error"); |
| |
| // Generate all Value Expressions. |
| // One will be all of the value expressions indexed by vectorEntryNum |
| // and valExprNum and the others will be NULL. |
| // |
| for(unionListNum = 1; unionListNum < numUnionLists; unionListNum++) { |
| |
| // For Each ValueIdUnion in an entry of the vector. |
| // |
| for(CollIndex numValUnions = 0; |
| numValUnions < transUnionVector()[unionListNum].entries(); |
| numValUnions++) { |
| |
| ValueIdUnion *valValIdUnion = |
| (ValueIdUnion *)((transUnionVector()[unionListNum])[numValUnions]. |
| getValueDesc()->getItemExpr()); |
| |
| if(unionListNum == vectorEntryNum){ |
| |
| // Construct the value expression cast to the proper type. |
| // |
| castExpr = new(generator->wHeap()) |
| Cast(valValIdUnion-> |
| getSource(valExprNum).getValueDesc()->getItemExpr(), |
| &(valValIdUnion->getResult().getType())); |
| } else { |
| |
| // Construct NULL expression cast to the proper type. |
| // |
| castExpr = new(generator->wHeap()) |
| Cast(new (space) ConstValue(), |
| &(valValIdUnion->getResult().getType())); |
| } |
| |
| // Bind the CASTed itemed expression. |
| // |
| castExpr->bindNode(generator->getBindWA()); |
| |
| // This should never fail at this point !!! |
| // |
| GenAssert(! generator->getBindWA()->errStatus(), |
| "Transpose: Internal Error"); |
| |
| // Add the ValueId to the list of value ids which will be used |
| // to generate the final expression. |
| // |
| ValueIds.insert(castExpr->getValueId()); |
| } |
| } |
| |
| // Generate the expression. |
| |
| // Initialize the pointer to the expression to be generated |
| // to be NULL. |
| // |
| transExprs[exprNum] = 0; |
| |
| // Generate the expressions. |
| // |
| // ValueIds - refers to the expressions for 1 key value, and N |
| // transpose values, all but one will be NULL. |
| // |
| // 0 - Do not add conv. nodes. |
| // |
| // transColsAtp - this expression will be evaluated on the |
| // transColsAtp (0) ATP. |
| // |
| // transColsAtpIndex - within the transColsAtp (0) ATP, the destination |
| // for this move expression will be the transColsAtpIndex TP. This should |
| // be the last TP of the ATP. |
| // |
| // SQLARK_EXPLODED_FORMAT - generate the move expression to construct |
| // the destination tuple in EXPLODED FORMAT. |
| // |
| // transColsTupleLen - This is an output which will contain the length |
| // of the destination Tuple. This better return the same value for each |
| // interation of this loop. |
| // |
| // &transExprs[exprNum] - The address of the pointer to the expression |
| // which will be generated. |
| // |
| // &transColsTupleDesc - The address of the tuple descriptor which is |
| // generated. This describes the destination tuple of the move expression. |
| // This will be generated only the first time through the loop. The tuple |
| // better have the same format each time. A NULL value inticates that |
| // the descriptor should not be generated. |
| // |
| // SHORT_FORMAT - generate the transColsTupleDesc in the SHORT FORMAT. |
| // |
| expGen->generateContiguousMoveExpr(ValueIds, |
| 0, |
| transColsAtp, |
| transColsAtpIndex, |
| generator->getInternalFormat(), |
| transColsTupleLen, |
| &transExprs[exprNum], |
| (exprNum == 0 |
| ? &transColsTupleDesc |
| : 0), |
| ExpTupleDesc::SHORT_FORMAT); |
| // Set the tuple descriptor in the returned CRI descriptor. |
| // This will be set only the first time through the loop. |
| // |
| if(exprNum == 0) { |
| returnedCriDesc->setTupleDescriptor(transColsAtpIndex, |
| transColsTupleDesc); |
| |
| // Take the result (ie. the Cast nodes) of generateContiguousMove |
| // and set the mapInfo for the ValueIdUnion nodes to be the same so |
| // that our parent can access the computed expressions |
| // This code is executed only the first time through the loop. |
| // The info generated from the expression generator should be the |
| // same each time through the loop. |
| // |
| |
| // An index into the ValueIds that we just generated. |
| // (It is assumed that the above code inserts the value Ids into |
| // ValueIds in the same order as the loops below). |
| // |
| CollIndex valIdNum = 0; |
| |
| // For each entry in the transUnionVector... |
| // |
| for(unionListNum = 0; unionListNum < numUnionLists; unionListNum++) { |
| |
| // For each ValueIdUnion in the entry... |
| // |
| for(CollIndex numValUnions = 0; |
| numValUnions < transUnionVector()[unionListNum].entries(); |
| numValUnions++) { |
| |
| // Get the ValueId of the ValueIdUnion node. |
| // |
| ValueId valId = |
| (transUnionVector()[unionListNum])[numValUnions]; |
| |
| // Add a map entry to the map table for this ValueIdUnion node. |
| // The mapInfo is the same as the mapInfo of the result of |
| // generating an expression for the corresponding Cast expression. |
| // |
| MapInfo * mapInfo = |
| generator->addMapInfoToThis(localMapTable, |
| valId, |
| generator->getMapInfo(ValueIds[valIdNum])->getAttr()); |
| |
| // Indicate that code was generated for this map table entry. |
| // |
| mapInfo->codeGenerated(); |
| |
| // Advance the index to the ValueIds. |
| // |
| valIdNum++; |
| } |
| } |
| } |
| |
| //localMapTable->removeLast(); |
| generator->removeLast(); |
| } |
| |
| // Generate expression to evaluate predicate on the transposed row |
| // |
| ex_expr *afterTransPred = 0; |
| |
| if (! selectionPred().isEmpty()) { |
| ItemExpr * newPredTree = |
| selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| |
| expGen->generateExpr(newPredTree->getValueId(), ex_expr::exp_SCAN_PRED, |
| &afterTransPred); |
| } |
| |
| // Allocate the Transpose TDB |
| // |
| queue_index upQ = (queue_index)getDefault(GEN_TRSP_SIZE_UP); |
| |
| ComTdbTranspose *transTdb = |
| new (space) ComTdbTranspose(childTdb, |
| transExprs, |
| numMovExprs, |
| afterTransPred, |
| transColsTupleLen, |
| transColsAtpIndex, |
| givenCriDesc, |
| returnedCriDesc, |
| (queue_index)getDefault(GEN_TRSP_SIZE_DOWN), |
| //(queue_index)getDefault(GEN_TRSP_SIZE_UP), |
| upQ, |
| (Cardinality) (getInputCardinality() * getEstRowsUsed()).getValue(), |
| getDefault(GEN_TRSP_NUM_BUFFERS), |
| getDefault(GEN_TRSP_BUFFER_SIZE), |
| space); |
| generator->initTdbFields(transTdb); |
| // If the child's outputs are not needed above this node, |
| // remove the entries from the map table. |
| // |
| if (transOutputs.isEmpty()) { |
| |
| // Remove child's outputs from mapTable, They are not needed |
| // above. |
| // |
| generator->removeAll(localMapTable); |
| NADELETEARRAY(numExprs,numUnionLists,CollIndex,space); |
| numExprs = NULL; |
| |
| } |
| |
| |
| // 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(transTdb, |
| 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, transTdb); |
| |
| return 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // PhyPack::codeGen() |
| // ----------------------------------------------------------------------- |
| short PhyPack::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(); |
| |
| // Get a description of what kind of tuple my parent gives me. |
| ex_cri_desc* givenCriDesc = generator->getCriDesc(Generator::DOWN); |
| |
| // PhyPack adds one tupp to what its parent gives in its return tuple. |
| unsigned short returnedNoOfTuples = givenCriDesc->noTuples() + 1; |
| ex_cri_desc* returnedCriDesc = |
| new (space) ex_cri_desc(returnedNoOfTuples,space); |
| |
| // Get a handle of the last map table before allowing child to code gen. |
| MapTable* lastMapTable = generator->getLastMapTable(); |
| |
| // Create new attributes for the packed columns. |
| CollIndex noOfPackedCols = packingExpr().entries(); |
| Attributes** attrs = |
| new (generator->wHeap()) Attributes * [noOfPackedCols]; |
| |
| // Add the packed columns to the map table. (using ATP0) |
| for(CollIndex i = 0; i < noOfPackedCols; i++) |
| attrs[i] = (generator->addMapInfo(packingExpr().at(i),0))->getAttr(); |
| |
| // PhyPack adds one tupp to the tail of its parent's ATP (stored as ATP0) |
| const Int32 atpIndex = returnedNoOfTuples - 1; |
| const Int32 atp = 0; |
| |
| // To store length of the last tupp introduced by PhyPack. |
| ULng32 tupleLen = 0; |
| |
| // Will be generated to describe the last tupp introduced by PhyPack. |
| ExpTupleDesc* tupleDesc = 0; |
| |
| // From here up we go back into Exploded Internal format (if not there |
| // already) since it doesn't make sense for column wise rowsets to |
| // be in Compressed Internal format yet. |
| generator->setExplodedInternalFormat(); |
| |
| // Fill in offsets and other info in the attributes list and computes the |
| // length and descriptor of the resulting tuple generated. |
| // The format of packed rows in rowsets is always Exploded Internal format. |
| expGen->processAttributes(noOfPackedCols, |
| attrs, |
| ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| tupleLen, |
| atp, |
| atpIndex, |
| &tupleDesc, |
| ExpTupleDesc::SHORT_FORMAT); |
| |
| // Copies of Attributes have been made to store in the tuple descriptor. |
| NADELETEBASIC(attrs,generator->wHeap()); |
| |
| // Store the computed tuple desc for the new tuple added by PhyPack. |
| returnedCriDesc->setTupleDescriptor(returnedNoOfTuples - 1,tupleDesc); |
| |
| // Set the DOWN descriptor in the generator to what my child gets from me. |
| generator->setCriDesc(returnedCriDesc,Generator::DOWN); |
| |
| // Get the child code generated and retrieve useful stuffs from it. |
| child(0)->codeGen(generator); |
| ComTdb* childTdb = (ComTdb*)(generator->getGenObj()); |
| |
| // Generate an expression to pack the rows. |
| ex_expr* packExpr = 0; |
| expGen->generateListExpr(packingExpr(),ex_expr::exp_ARITH_EXPR,&packExpr); |
| |
| // Generate selection predicate on the packed cols if there are any. |
| ex_expr* predExpr = 0; |
| if(NOT selectionPred().isEmpty()) |
| { |
| ItemExpr* pred = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| expGen->generateExpr(pred->getValueId(),ex_expr::exp_SCAN_PRED, |
| &predExpr); |
| } |
| |
| //????????? Generate an expression to copy result row to result buffer. |
| // ex_expr* copyExpr = 0; |
| // expGen->generateContiguousMoveExpr(packingExpr(), |
| // -1, // add conv |
| // 1, // copy to ATP1 |
| // returnedNoOfTuples - 1, // ATP index |
| // ExpTupleDesc::SQLARK_EXPLODED_FORMAT, |
| // tupleLen, |
| // ©Expr); |
| // |
| // Note that my parent is gonna find the non-copied version of attributes |
| // information in my map table, since it is refering to the vid's of the |
| // originals but not the copies. Coincidentally this is good for us since: |
| // 1. My parent is expected to see things at ATP0 rather than ATP1. |
| // 2. The format (or order) of the tuple doesn't get changed after copying |
| // so that the old attributes are refering to the right offsets. |
| // |
| // $$$ Seems copyExpr_ is no longer needed after ComTdbPackRows code were written. |
| |
| // Values at the child's context as well as the intermediates during expr |
| // generation are no longer visible any more from this point. |
| generator->removeAll(lastMapTable); |
| |
| // Create the Pack node's TDB. |
| ComTdbPackRows* packTdb = new (space) ComTdbPackRows (childTdb, |
| packExpr, |
| predExpr, |
| returnedNoOfTuples - 1, |
| tupleLen, |
| givenCriDesc, |
| returnedCriDesc, |
| (queue_index) 8, |
| (queue_index) 8); |
| generator->initTdbFields(packTdb); |
| |
| // Add explain info of this node to the EXPLAIN fragment. Set explainTuple |
| // pointer in the generator so the parent of this node can get a handle on |
| // this explainTuple. |
| // |
| if(NOT generator->explainDisabled()) |
| { |
| // Child's explain tuple. |
| ExplainTuple* childExplainTuple = generator->getExplainTuple(); |
| |
| generator->setExplainTuple(addExplainInfo(packTdb, |
| childExplainTuple, |
| 0, |
| generator)); |
| } |
| |
| // Set up the new up CRI desc. |
| generator->setCriDesc(returnedCriDesc,Generator::UP); |
| |
| // Restore original down CRI desc since this node changed it. |
| generator->setCriDesc(givenCriDesc,Generator::DOWN); |
| |
| // Return generated object (the TDB) to the generator. |
| generator->setGenObj(this, packTdb); |
| |
| return 0; |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| // TableValuedFunction methods |
| // ----------------------------------------------------------------------- |
| const char * TableValuedFunction::getVirtualTableName() |
| { return "??TableValuedFunction??"; } |
| |
| TrafDesc *TableValuedFunction::createVirtualTableDesc() |
| { return NULL; } |
| |
| void TableValuedFunction::deleteVirtualTableDesc(TrafDesc *vtd) |
| { |
| } |
| |
| // ----------------------------------------------------------------------- |
| // StatisticsFunc methods |
| // ----------------------------------------------------------------------- |
| const char * StatisticsFunc::getVirtualTableName() |
| //{ return "STATISTICS__"; } |
| {return getVirtualTableNameStr();} |
| |
| TrafDesc *StatisticsFunc::createVirtualTableDesc() |
| { |
| TrafDesc * table_desc = |
| Generator::createVirtualTableDesc(getVirtualTableName(), |
| NULL, // let it decide what heap to use |
| ComTdbStats::getVirtTableNumCols(), |
| ComTdbStats::getVirtTableColumnInfo(), |
| ComTdbStats::getVirtTableNumKeys(), |
| ComTdbStats::getVirtTableKeyInfo()); |
| return table_desc; |
| } |
| |
| |
| short StatisticsFunc::codeGen(Generator* generator) |
| { |
| ExpGenerator * expGen = generator->getExpGenerator(); |
| Space * space = generator->getSpace(); |
| |
| // allocate a map table for the retrieved columns |
| generator->appendAtEnd(); |
| |
| ex_expr *scanExpr = 0; |
| ex_expr *projExpr = 0; |
| |
| ex_cri_desc * givenDesc |
| = generator->getCriDesc(Generator::DOWN); |
| |
| ex_cri_desc * returnedDesc |
| = new(space) ex_cri_desc(givenDesc->noTuples() + 1, space); |
| |
| // cri descriptor for work atp has 4 entries: |
| // first two entries for consts and temps. |
| // Entry 1(index #2) is |
| // where the stats row will be moved to evaluate |
| // the 'where' predicate. |
| // Entry 2(index #3) is where the input row will be built. |
| |
| ex_cri_desc * workCriDesc = new(space) ex_cri_desc(4, space); |
| const Int32 work_atp = 1; |
| const Int32 stats_row_atp_index = 2; |
| const Int32 input_row_atp_index = 3; |
| |
| // Assumption (for now): retrievedCols contains ALL columns from |
| // the table/index. This is because this operator does |
| // not support projection of columns. Add all columns from this table |
| // to the map table. |
| // |
| // The row retrieved from filesystem is returned as the last entry in |
| // the returned atp. |
| |
| Attributes ** attrs = |
| new(generator->wHeap()) |
| Attributes * [getTableDesc()->getColumnList().entries()]; |
| |
| for (CollIndex i = 0; i < getTableDesc()->getColumnList().entries(); i++) |
| { |
| ItemExpr * col_node |
| = (((getTableDesc()->getColumnList())[i]).getValueDesc())-> |
| getItemExpr(); |
| |
| attrs[i] = (generator->addMapInfo(col_node->getValueId(), 0))-> |
| getAttr(); |
| } |
| |
| ExpTupleDesc *tupleDesc = 0; |
| ULng32 tupleLength = 0; |
| |
| // StatisticsFunc must use Exploded Format for now. |
| ExpTupleDesc::TupleDataFormat tupleFormat = ExpTupleDesc::SQLARK_EXPLODED_FORMAT; |
| |
| expGen->processAttributes(getTableDesc()->getColumnList().entries(), |
| attrs, tupleFormat, |
| tupleLength, |
| work_atp, stats_row_atp_index, |
| &tupleDesc, ExpTupleDesc::LONG_FORMAT); |
| |
| // delete [] attrs; |
| // NADELETEBASIC is used because compiler does not support delete[] |
| // operator yet. Should be changed back later when compiler supports |
| // it. |
| NADELETEBASIC(attrs, generator->wHeap()); |
| |
| // add this descriptor to the work cri descriptor. |
| workCriDesc->setTupleDescriptor(stats_row_atp_index, tupleDesc); |
| |
| // generate stats selection expression, if present |
| if (! selectionPred().isEmpty()) |
| { |
| ItemExpr * newPredTree = selectionPred().rebuildExprTree(ITM_AND,TRUE,TRUE); |
| expGen->generateExpr(newPredTree->getValueId(), ex_expr::exp_SCAN_PRED, |
| &scanExpr); |
| } |
| |
| // generate move expression for the parameter list |
| ex_expr *inputExpr = 0; |
| ExpTupleDesc *inputTupleDesc = 0; |
| ULng32 inputTupleLength = 0; |
| |
| if (! getProcInputParamsVids().isEmpty()) |
| { |
| |
| expGen->generateContiguousMoveExpr(getProcInputParamsVids(), |
| -1, |
| work_atp, |
| input_row_atp_index, |
| tupleFormat, |
| inputTupleLength, |
| &inputExpr, |
| &inputTupleDesc, |
| ExpTupleDesc::LONG_FORMAT); |
| |
| // add this descriptor to the work cri descriptor. |
| workCriDesc->setTupleDescriptor(input_row_atp_index, inputTupleDesc); |
| } |
| |
| // The stats row will be returned as the last entry of the returned atp. |
| // Change the atp and atpindex of the returned values to indicate that. |
| expGen->assignAtpAndAtpIndex(getTableDesc()->getColumnList(), |
| 0, returnedDesc->noTuples()-1); |
| |
| Lng32 numBuffers = 3; |
| Lng32 bufferSize = 10 * tupleLength; |
| ComTdbStats *statsTdb = new(space) ComTdbStats |
| ( |
| tupleLength, // Length of stats Tuple |
| tupleLength, // Length of returned tuple |
| inputTupleLength, // length of input tuple |
| givenDesc, // given_cri_desc |
| returnedDesc, // returned cri desc |
| 8, // Down queue size |
| 16, // Up queue size |
| numBuffers, // Number of buffers to |
| // allocate |
| bufferSize, // Size of each buffer |
| scanExpr, // predicate |
| inputExpr, |
| projExpr, |
| workCriDesc, // Descriptor of work Atp |
| stats_row_atp_index, |
| input_row_atp_index); |
| |
| generator->initTdbFields(statsTdb); |
| |
| // 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(statsTdb, 0, 0, generator)); |
| } |
| generator->setCriDesc(givenDesc, Generator::DOWN); |
| generator->setCriDesc(returnedDesc, Generator::UP); |
| generator->setGenObj(this, statsTdb); |
| |
| return 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // ProxyFunc methods |
| // ----------------------------------------------------------------------- |
| |
| static void initProxyKeyDescStruct(TrafKeysDesc *tgt, ComUInt32& src) |
| { |
| tgt->keyseqnumber = 1; |
| tgt->tablecolnumber = 0; |
| tgt->setDescending(FALSE); |
| } |
| |
| static Lng32 createDescStructsForProxy(const ProxyFunc &proxy, |
| char *tableName, |
| TrafDesc *&colDescs, |
| TrafDesc *&keyDescs) |
| { |
| colDescs = NULL; |
| keyDescs = NULL; |
| Lng32 reclen = 0; |
| TrafDesc *prev_desc = NULL; |
| ComUInt32 numCols = proxy.getNumColumns(); |
| |
| // Creates and populates column descs |
| proxy.populateColumnDesc(tableName, colDescs, reclen); |
| |
| // Create key descs |
| prev_desc = NULL; |
| numCols = 1; |
| for (ComUInt32 keyNum = 0; keyNum < numCols; keyNum++) |
| { |
| TrafDesc *key_desc = TrafAllocateDDLdesc(DESC_KEYS_TYPE, NULL); |
| if (prev_desc) |
| prev_desc->next = key_desc; |
| else |
| keyDescs = key_desc; |
| |
| prev_desc = key_desc; |
| |
| initProxyKeyDescStruct(key_desc->keysDesc(), keyNum); |
| } |
| |
| return reclen; |
| } |
| |
| TrafDesc *ProxyFunc::createVirtualTableDesc() |
| { |
| // TrafAllocateDDLdesc() requires that HEAP (STMTHEAP) |
| // be used for operator new herein |
| |
| TrafDesc *table_desc = TrafAllocateDDLdesc(DESC_TABLE_TYPE, NULL); |
| const char *tableName = getVirtualTableName(); |
| table_desc->tableDesc()->tablename = new HEAP char[strlen(tableName)+1]; |
| strcpy(table_desc->tableDesc()->tablename, tableName); |
| |
| table_desc->tableDesc()->setSystemTableCode(TRUE); |
| |
| TrafDesc *files_desc = TrafAllocateDDLdesc(DESC_FILES_TYPE, NULL); |
| files_desc->filesDesc()->setAudited(TRUE); // audited table |
| table_desc->tableDesc()->files_desc = files_desc; |
| |
| TrafDesc *cols_descs = NULL; |
| TrafDesc *keys_descs = NULL; |
| |
| table_desc->tableDesc()->colcount = (Int32) getNumColumns(); |
| |
| table_desc->tableDesc()->record_length = |
| createDescStructsForProxy(*this, |
| table_desc->tableDesc()->tablename, |
| cols_descs, |
| keys_descs); |
| |
| TrafDesc *index_desc = TrafAllocateDDLdesc(DESC_INDEXES_TYPE, NULL); |
| index_desc->indexesDesc()->tablename = |
| table_desc->tableDesc()->tablename; |
| index_desc->indexesDesc()->indexname = |
| table_desc->tableDesc()->tablename; |
| index_desc->indexesDesc()->keytag = 0; // primary index |
| index_desc->indexesDesc()->record_length = |
| table_desc->tableDesc()->record_length; |
| index_desc->indexesDesc()->colcount = |
| table_desc->tableDesc()->colcount; |
| index_desc->indexesDesc()->blocksize = 4096; // doesn't matter. |
| |
| TrafDesc *i_files_desc = TrafAllocateDDLdesc(DESC_FILES_TYPE, NULL); |
| i_files_desc->filesDesc()->setAudited(TRUE); // audited table |
| index_desc->indexesDesc()->files_desc = i_files_desc; |
| |
| index_desc->indexesDesc()->keys_desc = keys_descs; |
| table_desc->tableDesc()->columns_desc = cols_descs; |
| table_desc->tableDesc()->indexes_desc = index_desc; |
| |
| return table_desc; |
| } |
| |
| short PhysicalExtractSource::codeGen(Generator *) |
| { |
| GenAssert(0, "PhysicalExtractSource::codeGen() should never be called"); |
| return 0; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // ControlRunningQuery::codeGen() |
| // |
| ///////////////////////////////////////////////////////// |
| short ControlRunningQuery::codeGen(Generator * generator) |
| { |
| Space * space = generator->getSpace(); |
| |
| GenAssert((child(0) == NULL) && (child(1) == NULL), |
| "ControlRunningQuery does not expect any child."); |
| |
| char *qid = |
| space->allocateAndCopyToAlignedSpace(queryId_, str_len(queryId_), 0); |
| |
| char *pname = |
| space->allocateAndCopyToAlignedSpace(pname_, str_len(pname_), 0); |
| |
| char *comment = |
| space->allocateAndCopyToAlignedSpace(comment_, str_len(comment_), 0); |
| |
| ComTdbCancel::Action a = ComTdbCancel::InvalidAction; |
| switch (action_) |
| { |
| case Cancel: |
| { |
| switch (qs_) |
| { |
| case ControlQid: |
| a = ComTdbCancel::CancelByQid; |
| break; |
| case ControlPname: |
| case ControlNidPid: |
| break; |
| default: |
| GenAssert(0, "Invalid ControlRunningQuery::qs_"); break; |
| } |
| break; |
| } |
| case Suspend: |
| case Activate: |
| break; |
| default: GenAssert(0, "Invalid ControlRunningQuery::action_"); |
| } |
| |
| if (a == ComTdbCancel::InvalidAction) |
| { |
| *CmpCommon::diags() << DgSqlCode(-1010); |
| GenExit(); |
| return -1; |
| } |
| |
| ComTdbCancel * exe_cancel_tdb = new(space) |
| ComTdbCancel(qid, pname, nid_, pid_, |
| getDefault(CANCEL_MINIMUM_BLOCKING_INTERVAL), |
| a, forced_, comment, |
| generator->getCriDesc(Generator::DOWN), |
| generator->getCriDesc(Generator::DOWN), |
| (queue_index)getDefault(GEN_DDL_SIZE_DOWN), |
| (queue_index)getDefault(GEN_DDL_SIZE_UP) |
| ); |
| |
| generator->initTdbFields(exe_cancel_tdb); |
| |
| // no tupps are returned |
| generator->setCriDesc((ex_cri_desc *) |
| (generator->getCriDesc(Generator::DOWN)), |
| Generator::UP); |
| generator->setGenObj(this, exe_cancel_tdb); |
| |
| if(!generator->explainDisabled()) { |
| generator->setExplainTuple( |
| addExplainInfo(exe_cancel_tdb, 0, 0, generator)); |
| } |
| |
| return 0; |
| } |
| |