| /********************************************************************** |
| // @@@ 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: ValueDesc.C |
| * Description: Value Descriptor |
| * |
| * Created: 11/16/1994 |
| * Language: C++ |
| * |
| * |
| ****************************************************************************** |
| */ |
| |
| #define SQLPARSERGLOBALS_FLAGS // must precede all #include's |
| #define SQLPARSERGLOBALS_NADEFAULTS |
| |
| #include "ValueDesc.h" |
| #include "Sqlcomp.h" |
| #include "BindWA.h" |
| #include "NormWA.h" |
| #include "GroupAttr.h" |
| #include "AllItemExpr.h" |
| #include "RelJoin.h" |
| #include "CmpStatement.h" |
| #include "Generator.h" |
| #include "GenExpGenerator.h" |
| #include "ExpCriDesc.h" |
| #include "ExpAtp.h" |
| #include "exp_dp2_expr.h" |
| #include "exp_clause_derived.h" |
| #include "SQLCLIdev.h" |
| #include "GroupAttr.h" |
| #include "BindWA.h" |
| #include "ItemOther.h" |
| #include "ComDefs.h" |
| #include "OptimizerSimulator.h" |
| ////////////////////////////// |
| #include "Analyzer.h" |
| ////////////////////////////// |
| // |
| #include "NATable.h" |
| #include "EncodedKeyValue.h" |
| #include "TrafDDLdesc.h" |
| |
| #include "SqlParserGlobals.h" // must be last #include |
| |
| #define TEXT_DISPLAY_LENGTH 1001 |
| |
| // *********************************************************************** |
| // A function having an external linkage to allow display() to |
| // be called on a tree object. This is a workaround for bugs/missing |
| // functionality in ObjectCenter that cause display() to become |
| // an undefined symbol. |
| // *********************************************************************** |
| |
| void displayMap(const ValueIdMap & map) |
| { |
| map.getTopValues().display(); |
| map.getBottomValues().display(); |
| } |
| |
| void displaySet(const ValueIdSet & vs) |
| { |
| vs.display(); |
| } |
| |
| void displayList(const ValueIdList & vl) |
| { |
| vl.display(); |
| } |
| |
| void DisplayVid(CollIndex id) // useful when debugging in MSDEV on NT |
| { |
| ValueId vid(id); |
| NAString unp(CmpCommon::statementHeap()); |
| vid.getItemExpr()->unparse(unp); |
| cerr << "[" << id << "]" << unp << endl; |
| } |
| |
| // *********************************************************************** |
| // Methods for class ValueId |
| // *********************************************************************** |
| |
| ValueId::ValueId(ValueDesc *vdesc) |
| { |
| ActiveSchemaDB()->insertValueDesc(vdesc); |
| id_ = vdesc->getValueId(); |
| } |
| |
| ValueDesc* ValueId::getValueDesc() const |
| { |
| ValueDesc *vdesc = (ActiveSchemaDB()->getValueDescArray())[id_]; |
| // The if() is for easier debugging, to break on the assertion. |
| if (vdesc == NULL) |
| CMPASSERT(vdesc); |
| return vdesc; |
| } |
| |
| ItemExpr* ValueId::getItemExpr() const |
| { |
| return getValueDesc()->getItemExpr(); |
| } |
| |
| void ValueId::replaceItemExpr(ItemExpr * iePtr) |
| { |
| iePtr->setValueId(id_); |
| getValueDesc()->replaceItemExpr(iePtr); |
| } |
| |
| const NAType& ValueId::getType() const |
| { |
| return getValueDesc()->getDomainDesc()->getType(); |
| } |
| |
| // return TRUE iff i'm a string literal with unknown char set |
| NABoolean ValueId::inferableCharType() |
| { |
| if (getType().getTypeQualifier() != NA_CHARACTER_TYPE || |
| getItemExpr()->getOperatorType() != ITM_CONSTANT) { |
| return FALSE; |
| } |
| else { |
| return ((CharType*)(&getType()))->getCharSet() == CharInfo::UnknownCharSet; |
| } |
| } |
| |
| // return TRUE iff i'm a char type with known char set |
| NABoolean ValueId::hasKnownCharSet(const CharType **ctp) |
| { |
| if (getType().getTypeQualifier() != NA_CHARACTER_TYPE) { |
| return FALSE; |
| } |
| else { |
| *ctp = (const CharType*)(&getType()); |
| return ((CharType*)(&getType()))->getCharSet() != CharInfo::UnknownCharSet; |
| } |
| } |
| |
| void ValueId::changeType(const NAType *type) |
| { |
| getValueDesc()->getDomainDesc()->changeType(type); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // returns TRUE if the valueId is a veg_ref referring columns from more |
| // than one tables |
| // ---------------------------------------------------------------------- |
| |
| NABoolean ValueId::isInvolvedInJoinAndConst() const |
| { |
| |
| ValueIdSet tempSet(*this); |
| |
| if (!tempSet.referencesAConstExpr()) |
| return FALSE; |
| |
| ValueIdSet columns; |
| |
| getItemExpr()->findAll( ITM_BASECOLUMN, columns, TRUE, TRUE ); |
| |
| // Now get all the base tables in this VEG_reference |
| |
| // see if this column is being joined to a column of another table |
| SET(TableDesc *) * tablesJoined = columns.getAllTables(); |
| if (tablesJoined->entries() > 1) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| //////////////////////////////////////////// |
| ColAnalysis* ValueId::colAnalysis() const |
| { |
| ItemExpr *ie = getItemExpr(); |
| switch (ie->getOperatorType()) |
| { |
| case ITM_BASECOLUMN: |
| return QueryAnalysis::Instance()->getColAnalysis(id_); |
| case ITM_INDEXCOLUMN: |
| return QueryAnalysis::Instance()->getColAnalysis(((IndexColumn *)ie)->getDefinition()); |
| default: |
| return NULL; |
| // if (okIfNotColumn) |
| // return NULL; |
| // CMPASSERT("ColAnalysis call by non column"); |
| } |
| // return NULL; |
| } |
| |
| ColAnalysis* |
| ValueId::baseColAnalysis(NABoolean *isaConstant, ValueId &vid) const |
| { |
| vid = getBaseColumn(isaConstant); |
| if (isaConstant AND *isaConstant) |
| return NULL; |
| else |
| return vid.colAnalysis(); |
| } |
| |
| // should be inlined |
| PredAnalysis* ValueId::predAnalysis() const |
| { |
| return QueryAnalysis::Instance()->getPredAnalysis(id_); |
| } |
| //////////////////////////////////////////// |
| |
| void ValueId::coerceType(enum NABuiltInTypeEnum desiredQualifier, |
| enum CharInfo::CharSet charSet) |
| { |
| // |
| // If the ValueId has already been typed or the desired type is unknown, |
| // do nothing. |
| // |
| const NAType& originalType = getType(); |
| if (originalType.getTypeQualifier() != NA_UNKNOWN_TYPE || |
| desiredQualifier == NA_UNKNOWN_TYPE) |
| return; |
| // If charset is unknown then take the default charset |
| if(charSet==CharInfo::UnknownCharSet) |
| charSet = CharInfo::DefaultCharSet; |
| // |
| // Create the desired type, retaining the original null attribute, |
| // with any UPSHIFT set to FALSE. |
| // |
| NAType *desiredType = NULL; |
| switch (desiredQualifier) { |
| case NA_BOOLEAN_TYPE: |
| desiredType = new STMTHEAP SQLBooleanNative(STMTHEAP, originalType.supportsSQLnull()); |
| break; |
| case NA_CHARACTER_TYPE: |
| { |
| Lng32 len = CmpCommon::getDefaultNumeric(VARCHAR_PARAM_DEFAULT_SIZE); |
| |
| desiredType = new STMTHEAP |
| SQLVarChar(STMTHEAP, len, //DEFAULT_CHARACTER_LENGTH, |
| originalType.supportsSQLnull(), |
| FALSE/*isUpShifted*/, |
| FALSE/*isCaseinsensitive*/, |
| charSet, |
| CharInfo::DefaultCollation, |
| CharInfo::COERCIBLE); |
| } |
| break; |
| case NA_NUMERIC_TYPE: |
| { |
| Int16 DisAmbiguate = 0; |
| desiredType = new STMTHEAP |
| SQLNumeric(STMTHEAP, TRUE, // signed |
| MAX_NUMERIC_PRECISION, // precision |
| 6, // scale |
| DisAmbiguate, // added for 64bit proj. |
| originalType.supportsSQLnull()); |
| } |
| break; |
| default: |
| ABORT("Internal error in ValueId::coerceType()"); |
| } |
| |
| // Propagate (pushDowntype()) this type to the children of this valueid |
| // in case one of the children could not be typed. |
| // |
| const NAType *newType = getItemExpr()->pushDownType(*desiredType); |
| changeType(newType); |
| } |
| |
| void ValueId::coerceType(const NAType& desiredType, |
| enum NABuiltInTypeEnum defaultQualifier) |
| { |
| // |
| // If the ValueId has already been typed, do nothing. |
| // |
| const NAType& originalType = getType(); |
| NABoolean NotaCharType = TRUE; |
| |
| NAType *newType = NULL; |
| |
| // If the original is of CHARACTER type and the charset field is |
| // known, return. |
| if ( originalType.getTypeQualifier() == NA_CHARACTER_TYPE ) |
| { |
| if ( ((CharType&)originalType).getCharSet() == CharInfo::ISO88591 && |
| desiredType.getTypeQualifier() == NA_CHARACTER_TYPE && |
| ((CharType&)originalType).getEncodingCharSet() != ((CharType&)desiredType).getEncodingCharSet()) { |
| NotaCharType = FALSE; |
| newType = originalType.newCopy(STMTHEAP); |
| ((CharType*)newType) -> setEncodingCharSet(((CharType&)desiredType).getEncodingCharSet()); // for now ... |
| if ( getItemExpr()->getOperatorType() == ITM_CONSTANT ) { |
| ((ConstValue*)getItemExpr())->setRebindNeeded(TRUE); |
| } |
| } else |
| // experimenting code end |
| if ( ((CharType&)originalType).getCharSet() != CharInfo::UnknownCharSet ) { |
| return; |
| } else { |
| NotaCharType = FALSE; |
| if ( getItemExpr()->getOperatorType() == ITM_CONSTANT ) { |
| ((ConstValue*)getItemExpr())->setRebindNeeded(TRUE); |
| } |
| } |
| |
| |
| // CharSet is unknown but the desired type is not CHARACTER, assume the |
| // charset is ISo88591 |
| if ( desiredType.getTypeQualifier() != NA_CHARACTER_TYPE ) { |
| newType = originalType.newCopy(STMTHEAP); |
| ((CharType*)newType) -> setCharSet(CharInfo::ISO88591); |
| } |
| |
| } else { |
| if ( originalType.getTypeQualifier() != NA_UNKNOWN_TYPE ) |
| return; |
| |
| // |
| // If the desired type is unknown, set the ValueId's type according to the |
| // default qualifier. |
| // |
| if (desiredType.getTypeQualifier() == NA_UNKNOWN_TYPE) { |
| coerceType(defaultQualifier); |
| return; |
| } |
| } |
| |
| // Copy the desired type, retaining the original null attribute, |
| // but dropping any UPSHIFT attribute. |
| // |
| if (NotaCharType && // We do not want to force a characters storage values to be float |
| (desiredType.getFSDatatype() == REC_FLOAT32 || |
| desiredType.getFSDatatype() == REC_FLOAT64 )) |
| { |
| if (desiredType.getFSDatatype() == REC_FLOAT32) |
| newType = new STMTHEAP |
| SQLReal(STMTHEAP, desiredType.supportsSQLnull(), |
| desiredType.getPrecision()); |
| else |
| // ieee double, tandem real and tandem double are all |
| // cast as IEEE double. Tandem real is cast as ieee double as |
| // it won't 'fit' into ieee real. |
| newType = new STMTHEAP |
| SQLDoublePrecision(STMTHEAP, desiredType.supportsSQLnull(), |
| desiredType.getPrecision()); |
| } |
| else { |
| if ( newType == NULL ) |
| { |
| // if param default is OFF, type tinyint as smallint. |
| // This is needed until all callers/drivers have full support to |
| // handle IO of tinyint datatypes. |
| if (((desiredType.getFSDatatype() == REC_BIN8_SIGNED) || |
| (desiredType.getFSDatatype() == REC_BIN8_UNSIGNED)) && |
| ((CmpCommon::getDefault(TRAF_TINYINT_SUPPORT) == DF_OFF) || |
| (CmpCommon::getDefault(TRAF_TINYINT_INPUT_PARAMS) == DF_OFF))) |
| { |
| const NumericType &numType = (NumericType&)desiredType; |
| |
| NABoolean isSigned = numType.isSigned(); |
| if (numType.getScale() == 0) |
| newType = new (STMTHEAP) |
| SQLSmall(STMTHEAP, isSigned, desiredType.supportsSQLnull()); |
| else |
| newType = new (STMTHEAP) |
| SQLNumeric(STMTHEAP, sizeof(short), |
| numType.getPrecision(), |
| numType.getScale(), |
| isSigned, |
| desiredType.supportsSQLnull()); |
| |
| } // TinyInt |
| else if ((desiredType.getFSDatatype() == REC_BIN64_UNSIGNED) && |
| (CmpCommon::getDefault(TRAF_LARGEINT_UNSIGNED_IO) == DF_OFF)) |
| { |
| NumericType &nTyp = (NumericType &)desiredType; |
| if (CmpCommon::getDefault(BIGNUM_IO) == DF_OFF) |
| { |
| Int16 DisAmbiguate = 0; |
| newType = new (STMTHEAP) |
| SQLLargeInt(STMTHEAP, nTyp.getScale(), |
| DisAmbiguate, |
| TRUE, |
| nTyp.supportsSQLnull()); |
| } |
| else |
| { |
| newType = new (STMTHEAP) |
| SQLBigNum(STMTHEAP, MAX_HARDWARE_SUPPORTED_UNSIGNED_NUMERIC_PRECISION, |
| nTyp.getScale(), |
| FALSE, |
| FALSE, |
| nTyp.supportsSQLnull()); |
| } |
| } |
| else if ((desiredType.getFSDatatype() == REC_BOOLEAN) && |
| (CmpCommon::getDefault(TRAF_BOOLEAN_IO) == DF_OFF)) |
| { |
| newType = new (STMTHEAP) |
| SQLVarChar(STMTHEAP, SQL_BOOLEAN_DISPLAY_SIZE, |
| desiredType.supportsSQLnull()); |
| } |
| else if (DFS2REC::isBigNum(desiredType.getFSDatatype())) |
| { |
| // If bignum IO is not enabled or |
| // if max numeric precision allowed is what is supported in |
| // hardware, do what was previously being done: generate |
| // equivalent SQLNumeric class. |
| // Can't do this inside of closestEquivalentExternalType method |
| // since CmpCommon::getDefault method is not accessible from it. |
| |
| NABoolean bignumIO = FALSE; |
| if (CmpCommon::getDefault(BIGNUM_IO) == DF_ON) |
| bignumIO = TRUE; // explicitely set to ON |
| else if (CmpCommon::getDefault(BIGNUM_IO) == DF_OFF) |
| bignumIO = FALSE; // explicitely set to OFF |
| else if (CmpCommon::getDefault(BIGNUM_IO) == DF_SYSTEM) |
| { |
| if (((SQLBigNum&)desiredType).isARealBigNum()) |
| bignumIO = TRUE; |
| } |
| if (CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED) == |
| MAX_HARDWARE_SUPPORTED_SIGNED_NUMERIC_PRECISION) |
| bignumIO = FALSE; |
| |
| if (bignumIO) |
| { |
| Lng32 precision = |
| MINOF(desiredType.getPrecision(), (Lng32) |
| CmpCommon::getDefaultNumeric(MAX_NUMERIC_PRECISION_ALLOWED)); |
| Lng32 scale = MINOF(desiredType.getScale(), precision); |
| newType = new (STMTHEAP) |
| SQLBigNum(STMTHEAP, precision, scale, |
| ((SQLBigNum&)desiredType).isARealBigNum(), |
| ((NumericType&)desiredType).isSigned(), |
| desiredType.supportsSQLnull()); |
| } |
| else |
| { |
| Lng32 precision = MINOF(desiredType.getPrecision(), MAX_NUMERIC_PRECISION); |
| Lng32 scale = MINOF(desiredType.getScale(), precision); |
| NABoolean isSigned = ((NumericType&)desiredType).isSigned(); |
| if ((precision > MAX_HARDWARE_SUPPORTED_UNSIGNED_NUMERIC_PRECISION) && |
| (precision <= MAX_NUMERIC_PRECISION)) |
| isSigned = TRUE; |
| |
| Int16 DisAmbiguate = 0; |
| newType = new (STMTHEAP) |
| SQLNumeric(STMTHEAP, isSigned, |
| precision, |
| scale, |
| DisAmbiguate, // added for 64bit proj. |
| desiredType.supportsSQLnull()); |
| } |
| |
| } // BigNum |
| else |
| newType = desiredType.closestEquivalentExternalType(STMTHEAP); |
| } |
| } |
| |
| NAType *tempType = originalType.newCopy(STMTHEAP); |
| |
| // In rowsets, we preserve the nullability of the type |
| if (getItemExpr()->origOpType() == ITM_ROWSETARRAY_SCAN) { |
| tempType->setNullable(*newType); |
| } |
| |
| // If param is being typed from odbc or jdbc, preserve the |
| // nullability of the type. |
| NABoolean ODBC = (CmpCommon::getDefault(ODBC_PROCESS) == DF_ON); |
| NABoolean JDBC = (CmpCommon::getDefault(JDBC_PROCESS) == DF_ON); |
| if ((ODBC || JDBC) && |
| // (CmpCommon::getDefault(GENERATE_LEAN_EXPR) == DF_ON)) { |
| ((CmpCommon::getDefaultLong(QUERY_OPTIMIZATION_OPTIONS) & QO_PARAM_NULLABILITY_OPT) != 0)) { |
| if ((getItemExpr()->origOpType()) == ITM_DYN_PARAM) |
| tempType->setNullable(*newType); |
| } |
| |
| newType->setNullable(*tempType); |
| |
| if (newType->getTypeQualifier() == NA_CHARACTER_TYPE) { |
| CharType *ct = (CharType *)newType; |
| ct->setUpshifted(FALSE); |
| } |
| |
| // Propagate (pushDowntype()) this type to the children of this valueid |
| // in case one of the children could not be typed. |
| // |
| const NAType* synthesizedNewType = |
| getItemExpr()->pushDownType(*newType, defaultQualifier); |
| |
| changeType(synthesizedNewType); |
| } |
| |
| |
| NAColumn* |
| ValueId::getNAColumn(NABoolean okIfNotColumn) const |
| { |
| ItemExpr *ie = getItemExpr(); |
| switch (ie->getOperatorType()) { |
| case ITM_BASECOLUMN: |
| return ((BaseColumn *)ie)->getNAColumn(); |
| case ITM_INDEXCOLUMN: |
| return ((IndexColumn *)ie)->getNAColumn(); |
| case ITM_UNPACKCOL: |
| case ITM_NOTCOVERED: |
| return ie->child(0).getValueId().getNAColumn(okIfNotColumn); |
| case ITM_INSTANTIATE_NULL: |
| return (*ie)[0].getNAColumn(okIfNotColumn); |
| default: |
| if (okIfNotColumn) |
| return NULL; |
| CMPASSERT(okIfNotColumn); |
| } |
| return NULL; |
| } |
| |
| |
| NABoolean ValueId::isColumnWithNonNullNonCurrentDefault() const{ |
| NAColumn * nac = NULL; |
| ItemExpr *ck = getItemExpr(); |
| if ( ck == NULL ) |
| return FALSE; |
| switch (ck->getOperatorType()){ |
| case ITM_BASECOLUMN: |
| nac = ((BaseColumn*)ck)->getNAColumn(); |
| break; |
| case ITM_INDEXCOLUMN: |
| nac = ((IndexColumn*)ck)->getNAColumn(); |
| break; |
| default: |
| break; |
| } |
| if (nac && nac->getDefaultValue() && nac->getDefaultClass()!=COM_NULL_DEFAULT && nac->getDefaultClass()!=COM_CURRENT_DEFAULT && nac->getDefaultClass()!=COM_CURRENT_UT_DEFAULT) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| |
| // Since we *can* have an INSTANTIATE_NULL inside a VEG_REFERENCE, a loop |
| // was required for the function below. |
| // |
| // Genesis case # 10-981118-9063 was the impetus for this change; basically, |
| // three left joins caused the previous version of this function to have an |
| // assertion failure. |
| |
| BaseColumn * |
| ValueId::castToBaseColumn(NABoolean *isaConstant) const |
| { |
| ItemExpr *ie = getItemExpr(); |
| |
| // loop until we RETURN from this function or its a NULL expression |
| while (ie) |
| { |
| ValueId vid = NULL_VALUE_ID; |
| |
| switch (ie->getOperatorType()) |
| { |
| case ITM_BASECOLUMN: |
| return (BaseColumn *)ie; |
| |
| case ITM_INDEXCOLUMN: |
| return (BaseColumn *) ((IndexColumn *) ie)-> |
| getDefinition().getItemExpr(); |
| |
| case ITM_INSTANTIATE_NULL: |
| case ITM_UNPACKCOL: |
| ie = (*ie)[0] ; |
| break; |
| |
| case ITM_VALUEIDUNION: |
| // In case of ValueiId Union, get the child, which should be a |
| // base column. |
| vid = ((ValueIdUnion *)ie)->getSource(0); |
| if (vid == NULL_VALUE_ID) |
| { |
| ie = NULL; |
| break; |
| } |
| |
| ie = vid.getItemExpr(); |
| break; |
| |
| case ITM_ROWSETARRAY_SCAN: |
| // $$$ mar: no good way to do this easily, so for now we punt |
| // $$$ --> post-FCS, do something better |
| // |
| // $$$ ste: return 80 yards for a touch down. |
| // $$$ ---> score: ste 6, mar 0 |
| return NULL ; |
| // ie = ((ValueIdUnion *)ie)->getSource(0) ; |
| break; |
| |
| case ITM_VEG_REFERENCE: |
| { |
| ValueId kid; |
| ((VEGReference*)ie)->getVEG()->getAllValues().getFirst(kid); |
| ie = kid.getItemExpr(); |
| break; |
| } |
| |
| default: |
| if (ie->getArity() > 0) |
| { |
| ie = (*ie)[0]; |
| break; |
| } |
| else |
| { |
| if (isaConstant) |
| { |
| *isaConstant = ie->doesExprEvaluateToConstant |
| (FALSE,TRUE); |
| } |
| return NULL; |
| } |
| } |
| } // end of while |
| return NULL; |
| } |
| |
| ValueId |
| ValueId::getBaseColumn(NABoolean *isaConstant) const |
| { |
| BaseColumn *bc = castToBaseColumn(isaConstant); |
| return bc==NULL ? NULL_VALUE_ID : bc->getValueId(); |
| } |
| |
| ValueId ValueId::nullInstantiate(BindWA *bindWAPtr, NABoolean forceCast) const |
| { |
| const NAType &sourceType = getType(); |
| |
| // For Joins, the Normalizer requires the cast to null-instantiation |
| // (InstantiateNull is a subclass of Cast), |
| // so Binder here passes forceCast as TRUE. |
| if (sourceType.supportsSQLnull()) |
| if (!forceCast) |
| return *this; |
| //## // It would be nice not to put an InstNull on top of an InstNull. |
| //## // Unfortunately, TEST014 q15 fails when we do so -- |
| //## // should investigate why and fix that! |
| //## else if (getItemExpr()->getOperatorType() == ITM_INSTANTIATE_NULL) |
| //## return *this; |
| const NAType *resultType = sourceType.synthesizeNullableType(STMTHEAP); |
| InstantiateNull *instNull = new (bindWAPtr->wHeap()) |
| InstantiateNull(getItemExpr(), resultType); |
| instNull->bindNode(bindWAPtr); |
| CMPASSERT(NOT bindWAPtr->errStatus()); |
| return instNull->getValueId(); |
| }; |
| |
| // ----------------------------------------------------------------------- |
| // get the value id's of all ValueIdUnion nodes which exists as sub expr |
| // of the item expr which has this valueid. |
| // Used in MergeUnion::preCodeGen() to minimize the set of outputs the |
| // operator should produce. |
| // ----------------------------------------------------------------------- |
| void ValueId::getSubExprRootedByVidUnion(ValueIdSet & vs) |
| { |
| ItemExpr* thisItemExpr = getItemExpr(); |
| if ( thisItemExpr->getOperatorType() == ITM_VALUEIDUNION ) |
| vs.insert( *this ); |
| else |
| for ( Lng32 i = 0; i < thisItemExpr->getArity(); i++ ) |
| thisItemExpr->child(i).getValueId().getSubExprRootedByVidUnion( vs ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // replace any BaseColumn (of the given column name) in of this value |
| // expression with the given expression. |
| // used in Insert::bindNode() to move constraints from the target table |
| // to the source table. |
| // ---------------------------------------------------------------------- |
| void ValueId::replaceBaseColWithExpr(const NAString& colName, |
| const ValueId & vid) |
| { |
| ItemExpr* thisItemExpr = getItemExpr(); |
| for( Lng32 i = 0; i < thisItemExpr->getArity(); i++ ) |
| { |
| ValueId childValueId = thisItemExpr->child(i).getValueId(); |
| ItemExpr* childItemExpr = childValueId.getItemExpr(); |
| |
| if( childItemExpr->getOperatorType() == ITM_BASECOLUMN ) |
| { |
| if( ((BaseColumn*)childItemExpr)->getColName() == colName ) |
| thisItemExpr->setChild( i, vid.getItemExpr() ); |
| } |
| childValueId.replaceBaseColWithExpr( colName, vid ); |
| } |
| } |
| |
| // --------------------------------------------------- |
| // Method for applying normalizeNode() to a ValueId. |
| // --------------------------------------------------- |
| NABoolean ValueId::normalizeNode(NormWA & normWARef) |
| { |
| ItemExpr * nePtr; // normalized expr. |
| NABoolean transformed = FALSE; |
| ItemExpr * iePtr = getItemExpr(); // original expr. |
| // Transform the Item Expression. |
| nePtr = iePtr->getReplacementExpr()->normalizeNode( normWARef ); |
| // If the original expression was transformed, update the id |
| if ( nePtr->castToItemExpr() != iePtr || |
| nePtr->getValueId() != *this) |
| { |
| transformed = TRUE; |
| id_ = nePtr->castToItemExpr()->getValueId(); |
| } |
| |
| return transformed; |
| } |
| |
| // *********************************************************************** |
| // Methods for class ValueIdList |
| // (normalizer-related methods further below) |
| // *********************************************************************** |
| |
| THREAD_P ObjectCounter (*ValueIdList::counter_)(0); |
| |
| ValueIdList::ValueIdList(const ValueIdSet &vs, CollHeap *h) : |
| NAList<ValueId>(h, vs.entries()) |
| { |
| CollIndex index = 0; |
| for (ValueId vid = vs.init(); vs.next(vid); vs.advance(vid)) |
| insertAt(index++,vid); |
| CMPASSERT(index == vs.entries()); |
| (*counter_).incrementCounter(); |
| } |
| |
| // Initialization of list assumes array is densely populated or full |
| ValueIdList::ValueIdList(const ValueIdArray &va, CollHeap *h) : |
| NAList<ValueId>(h, va.entries()) |
| { |
| for (CollIndex i = 0; i < va.entries(); i++) |
| if (va.used(i)) |
| insert(va[i]); |
| (*counter_).incrementCounter(); |
| } |
| |
| void ValueIdList::insertSet(const ValueIdSet &other) |
| { |
| CollIndex index = entries(); |
| for (ValueId vid = other.init(); other.next(vid); other.advance(vid)) |
| insertAt(index++,vid); |
| } |
| |
| Lng32 ValueIdList::getRowLength() const |
| { |
| Lng32 result = 0; |
| |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| result += at(i).getType().getTotalSize(); |
| } |
| |
| return result; |
| } |
| |
| Lng32 ValueIdList::getRowLengthOfNumericCols() const |
| { |
| Lng32 result = 0; |
| |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| if (at(i).getType().isNumeric()) |
| result += at(i).getType().getTotalSize(); |
| } |
| |
| return result; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Calculate the space needed in an sql_buffer for Mdam values. |
| // These values are: high, low, non-null high, non-null low, and current. |
| // There are five per key column. Data in sql_buffer is eight-byte |
| // alligned. |
| // ----------------------------------------------------------------------- |
| Lng32 ValueIdList::getMdamSqlBufferSpace() const |
| { |
| Lng32 result = 0; |
| |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| result += 5 * (ROUND8(at(i).getType().getEncodedKeyLength()) |
| + sizeof(tupp_descriptor)); |
| } |
| |
| return result; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // prefixCoveredInSet() |
| // Is a prefix of this list covered in the provided set? If so, |
| // return the number of covered elements. Otherwise, return 0. |
| // ----------------------------------------------------------------------- |
| Int32 ValueIdList::prefixCoveredInSet (const ValueIdSet& vidSet) const |
| { |
| Int32 i = 0; |
| ValueId simpVid; |
| ValueId noInverseVid; |
| |
| // if the set's empty, clearly it doesn't cover anything! |
| if ( vidSet.entries() == 0 ) return 0 ; |
| |
| while (i < (Int32)entries()) |
| { |
| // simplify the expression |
| // e.g. remove any INVERSE nodes on top, |
| // and simplify any bi-arith ops involving constants to their |
| // base value. |
| simpVid = (*this)[i].getItemExpr()->simplifyOrderExpr()->getValueId(); |
| // Also compute the value id that results when the only |
| // simplification that is done is to remove any INVERSE nodes - |
| // i.e. if the valueId is a bi-arith op or is a bi-arith op that |
| // is the child of an INVERSE node, it will remain. |
| noInverseVid = (*this)[i].getItemExpr()->removeInverseOrder()->getValueId(); |
| |
| // Stop unless we find the completely simplified or partially |
| // simplified version of the expression in the provided set. |
| if (NOT (vidSet.contains (simpVid) OR vidSet.contains(noInverseVid)) ) |
| { |
| return i; |
| } |
| else |
| i++; |
| } |
| |
| return i; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // allMembersCoveredInSet() |
| // Are all members of this list are covered in the provided set? |
| // If exactMatch is set: |
| // If all the members of this list are covered in the provided set AND |
| // if they are exact match, return TRUE. Otherwise, return FALSE. |
| // If exactMatch is not set: |
| // If all the members of this list are covered in the provided set, |
| // return TRUE. Otherwise, return FALSE. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdList::allMembersCoveredInSet(const ValueIdSet& vidSet, |
| NABoolean exactMatch) const |
| { |
| // Get the length of the covered prefix |
| CollIndex numCovered = prefixCoveredInSet(vidSet); |
| |
| // Check if all the members of this list are covered. |
| if (numCovered == entries()) |
| { |
| // All members of this list are covered. |
| // If they are exact match, return TRUE. |
| if (entries() == vidSet.entries()) |
| return TRUE; |
| else |
| { |
| // The provided set has additional memebers. |
| // If exactMatch is set, return FALSE. Otherwise, return TRUE. |
| if (exactMatch) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| } |
| // All members of this list are NOT covered, so return FALSE. |
| return FALSE; |
| } |
| |
| // --------------------------------------------------------------------- |
| // removeUncoveredSuffix() |
| // Check whether a prefix of this list is covered by the provided set. |
| // For the remaining suffix, remove those items from this list. |
| // --------------------------------------------------------------------- |
| void ValueIdList::removeUncoveredSuffix (const ValueIdSet& vidSet) |
| { |
| Int32 index = prefixCoveredInSet(vidSet); // last covered id in this list |
| Int32 i = (Int32)entries() - 1; // index of last key column |
| |
| // Remove all entries following the index |
| while (i >= index) |
| removeAt(i--); |
| } |
| |
| // --------------------------------------------------------------------- |
| // findNJEquiJoinColumns() |
| // Determine the maximum prefix of this list that is covered |
| // by either a constant, a parameter/host variable, or an equi-join |
| // column. Then return the list that is composed of just the equi-join |
| // columns. Also returns in an output parameter the columns |
| // of this list that were not covered. |
| // Input: child0Outputs, child1Inputs |
| // Output: uncoveredCols |
| // Return value: a list representing the equijoin columns found |
| // --------------------------------------------------------------------- |
| ValueIdList ValueIdList::findNJEquiJoinCols( |
| const ValueIdSet & child0Outputs, |
| const ValueIdSet & child1Inputs, |
| ValueIdList & uncoveredCols) const |
| { |
| ValueIdList equiJoinCols; |
| NABoolean isChild0Output,isChild1Input; |
| GroupAttributes emptyGA; |
| ValueIdSet dummy; |
| CollIndex i = 0; |
| CollIndex j = 0; |
| |
| // Start off assuming that all columns are not covered |
| uncoveredCols = *this; |
| |
| while ( i < entries() ) |
| { |
| // simplify the expression |
| // e.g. remove any INVERSE nodes on top (i.e. DESC) |
| const ValueId & noInverseVid = |
| (*this)[i].getItemExpr()->removeInverseOrder()->getValueId(); |
| |
| // Is the value a constant or produced by child0? |
| isChild0Output = emptyGA.covers( noInverseVid, child0Outputs, dummy ); |
| // Is the value a parameter/host variable or produced by |
| // child0 or the parent? |
| isChild1Input = child1Inputs.contains( noInverseVid ); |
| |
| // If the value is produced by child0 and it is also an input |
| // value, then it must be an equi-join column. |
| if ( isChild0Output AND isChild1Input ) |
| { |
| equiJoinCols.insert( (*this)[i] ); |
| uncoveredCols.removeAt(j); |
| j--; |
| } |
| // Otherwise, check to see if the value is a constant or a |
| // parameter/host variable. |
| else if ( isChild0Output OR isChild1Input ) |
| { |
| uncoveredCols.removeAt(j); |
| j--; |
| } |
| // Otherwise, the column is not covered and we must stop now. |
| else |
| { |
| return equiJoinCols; |
| } |
| // advance loop |
| i++; |
| j++; |
| } // end while more entries in the list |
| |
| return equiJoinCols; |
| } // findNJEquiJoinCols() |
| |
| // ----------------------------------------------------------------------- |
| // complifyAndCheckPrefixCovered() |
| // Is a prefix of this list covered in the unsimplified provided set? If so, |
| // return the number of covered elements. Otherwise, if an element |
| // fails the check, check to see if it is in the simplified version of |
| // the provided set. If the element is found there, then remove the |
| // original version from the list and replace it with the unsimplified |
| // version of that element from the provided set, and keep going. |
| // Otherwise, return the length of the prefix seen so far. If there |
| // were no matches, then we will return 0. |
| // ----------------------------------------------------------------------- |
| Int32 ValueIdList::complifyAndCheckPrefixCovered (const ValueIdSet& vidSet, |
| const GroupAttributes *ga) |
| { |
| // if the set's empty, clearly it doesn't cover anything! |
| if ( vidSet.entries() == 0 ) return 0 ; |
| |
| Int32 i = 0; |
| ValueId thisVid; |
| |
| // Set up an empty GA so we can test for coverage by the provided set. |
| ValueIdSet referencedInputs; |
| GroupAttributes emptyGA; |
| // The provided set will be the char. outputs that provide coverage |
| emptyGA.setCharacteristicOutputs(vidSet); |
| |
| while (i < (Int32)entries()) |
| { |
| // Get the current expression |
| thisVid = (*this)[i]; |
| |
| // Stop unless the provided set covers the current value id expression, |
| // or unless we are able to do a "complify" operation. |
| if (NOT emptyGA.covers(thisVid, |
| vidSet, |
| referencedInputs)) |
| { |
| // Prepare to see if we can do a "complify" operation. |
| // First, simplify the expression |
| // e.g. remove any INVERSE nodes on top, |
| // and simplify any bi-arith ops involving constants to their |
| // base value. |
| OrderComparison order1,order2; |
| const ValueId & simpThisVid = |
| (*this)[i].getItemExpr()->simplifyOrderExpr(&order1)->getValueId(); |
| // compute the simplified version of the provided set |
| ValueIdSet simpleVidSet = vidSet.simplifyOrderExpr(); |
| |
| // The current expression was not covered by the unsimplified version |
| // of the provided set. So, see if the simplified version of the current |
| // expression is in the simplified version of the provided set. |
| // |
| // Note that the typical usage of this method is to check if a sort key |
| // is covered by the group attributes characteristic outputs. We need |
| // to use the simplified version of the current expression because |
| // inverse nodes (for DESC orders) will never be in the group attributes, |
| // so we must strip them off. Also note that the simplifyOrderExpr |
| // method does not yet handle all the simplifications that we would |
| // like. For example, it does not simplify CAST expressions. It also |
| // does not simplify expressions involving multiplication, because |
| // multiplying a column by a negative number reverses the ordering of |
| // the column. This is an example of why we test for strict containment |
| // here with only very basic simplifications allowed: since the "complify" |
| // expression is actually substituting a different expression into the |
| // sort key, we must ensure that the new expression is in fact completely |
| // equivalent to the old. In some cases, such as CAST, we could |
| // probably be more liberal, but in other cases, such as multiplication, |
| // we cannot. |
| if (simpleVidSet.contains(simpThisVid)) |
| { |
| // Need to replace the current entry with one that is based on |
| // the value id of the unsimplified version in the provided set. |
| |
| // Compute the corresponding value id in the provided set |
| ValueId newThisVid; |
| for (ValueId vid = vidSet.init(); |
| vidSet.next(vid); |
| vidSet.advance(vid)) |
| { |
| const ValueId & simpOtherVid = |
| vid.getItemExpr()->simplifyOrderExpr(&order2)->getValueId(); |
| if (simpOtherVid == simpThisVid) |
| { |
| newThisVid = vid; |
| break; |
| } |
| } |
| |
| if (order1==order2) |
| { |
| // No inverse function to worry about - just replace the |
| // current valid with the one from the provided set. |
| (*this)[i] = newThisVid; |
| } |
| else |
| { |
| // Need to add an inverse operator on top of the provided |
| // set expression. |
| ItemExpr *inverseCol; |
| inverseCol = new(CmpCommon::statementHeap()) |
| InverseOrder(newThisVid.getItemExpr()); |
| inverseCol->synthTypeAndValueId(); |
| (*this)[i] = inverseCol->getValueId(); |
| } |
| } // end if the simp. set contains the simp. expresssion |
| else if (ga && ga->canEliminateOrderColumnBasedOnEqualsPred(thisVid)) |
| { |
| // if this column is constrained to a single value, then |
| // simply remove it from "this" |
| removeAt(i--); |
| } |
| else |
| return i; |
| } |
| i++; |
| } // end while |
| |
| return i; |
| } // complifyAndCheckPrefixCovered |
| |
| // --------------------------------------------------------------------- |
| // complifyAndRemoveUncoveredSuffix() |
| // Check whether a prefix of this list is covered by the provided set. |
| // This might involve replacing some elements of the list with their |
| // corresponding versions in the provided set. |
| // For the remaining suffix, remove those items from this list. |
| // --------------------------------------------------------------------- |
| void ValueIdList::complifyAndRemoveUncoveredSuffix (const ValueIdSet& vidSet, |
| const GroupAttributes *ga) |
| { |
| // last covered id in this list |
| Int32 index = complifyAndCheckPrefixCovered(vidSet, ga); |
| Int32 i = (Int32)entries() - 1; // index of last key column |
| |
| // Remove all entries following the index |
| while (i >= index) |
| removeAt(i--); |
| } |
| |
| // --------------------------------------------------------------------- |
| // simplifyOrderExpr() |
| // |
| // Returns a version of the list that has all expressions involving |
| // columns and constants or columns and inverse nodes simplified to be |
| // the column only. |
| // --------------------------------------------------------------------- |
| ValueIdList ValueIdList::simplifyOrderExpr() const |
| { |
| CollIndex i; |
| ValueId svid; |
| ValueIdList simpleSortCols; |
| |
| for (i = 0; i < entries(); i++) |
| { |
| svid = (*this)[i].getItemExpr()->simplifyOrderExpr()->getValueId(); |
| simpleSortCols.insert(svid); |
| } |
| |
| return simpleSortCols; |
| } |
| |
| // --------------------------------------------------------------------- |
| // removeInverseOrder() |
| // |
| // Returns a version of the list that has all expressions involving |
| // columns and inverse nodes simplified to be the column only. |
| // --------------------------------------------------------------------- |
| ValueIdList ValueIdList::removeInverseOrder() const |
| { |
| CollIndex i; |
| ValueId svid; |
| ValueIdList noInverseSortCols; |
| |
| for (i = 0; i < entries(); i++) |
| { |
| svid = (*this)[i].getItemExpr()->removeInverseOrder()->getValueId(); |
| noInverseSortCols.insert(svid); |
| } |
| |
| return noInverseSortCols; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Method for applying normalizeNode() to a ValueIdList. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdList::normalizeNode(NormWA & normWARef) |
| { |
| ItemExpr * nePtr; // normalized expr. |
| NABoolean transformed = FALSE; |
| |
| for ( CollIndex index = 0; index < entries(); index++ ) |
| { |
| ItemExpr * iePtr = at(index).getItemExpr(); // original expr. |
| // Transform the Item Expression. |
| nePtr = iePtr->getReplacementExpr()->normalizeNode( normWARef ); |
| // If the original expression was transformed, update the list |
| if ( nePtr->castToItemExpr() != iePtr || |
| nePtr->getValueId() != at(index) ) |
| { |
| transformed = TRUE; |
| at(index) = nePtr->castToItemExpr()->getValueId(); |
| } |
| } // loop over expressions list |
| |
| return transformed; |
| |
| } // ValueIdList::normalizeNode() |
| |
| // ----------------------------------------------------------------------- |
| // Method for rebuilding a predicate tree from a ValueIdList. |
| // ----------------------------------------------------------------------- |
| ItemExpr * ValueIdList::rebuildExprTree |
| (OperatorTypeEnum op, |
| NABoolean redriveTypeSynthesisFlag, |
| NABoolean createFinalizeResultNode) const |
| { |
| ItemExpr * iePtr; |
| ItemExpr * leftPtr = NULL; |
| // --------------------------------------------------------------------- |
| // Iterate over the predicate factors in the given predicate tree, |
| // constructing a left-linear backbone of the specified node type. |
| // --------------------------------------------------------------------- |
| for (CollIndex ix = 0; ix < entries(); ix++) |
| { |
| iePtr = at(ix).getItemExpr(); |
| if (leftPtr) |
| switch (op) |
| { |
| case ITM_AND: |
| case ITM_OR: |
| leftPtr = new STMTHEAP BiLogic(op,leftPtr,iePtr); |
| break; |
| |
| case ITM_ITEM_LIST: |
| leftPtr = new STMTHEAP ItemList(leftPtr,iePtr); |
| break; |
| |
| default: |
| ABORT("Can't do backbones with this node type"); |
| } |
| else |
| leftPtr = iePtr; |
| } // loop over predTree |
| |
| // ----------------------------------------------------------------- |
| // Create a BoolResult node on top of the ANDed tree, if asked for. |
| // Used by generator to finalize results. |
| // ----------------------------------------------------------------- |
| if (leftPtr && createFinalizeResultNode) |
| { |
| CMPASSERT(op == ITM_AND); |
| leftPtr = new STMTHEAP BoolResult(leftPtr); |
| } |
| |
| // --------------------------------------------------------------------- |
| // Synthesize the data type for the operators in the tree and allocate |
| // their ValueIds. |
| // --------------------------------------------------------------------- |
| if (leftPtr) |
| leftPtr->synthTypeAndValueId(redriveTypeSynthesisFlag); |
| |
| return leftPtr; |
| |
| } // ValueIdList::rebuildExprTree() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdList::replaceVEGExpressions() |
| // |
| // This method is used by the code generator for rewriting |
| // expressions that belong to a ValueIdList. A reference to |
| // a VEG, i.e., a ValueId Equality Group is replaced with |
| // an expression that belongs the VEG as well as to the |
| // set of availableValues that is supplied. |
| // ----------------------------------------------------------------------- |
| void ValueIdList::replaceVEGExpressions |
| (const ValueIdSet & availableValues, |
| const ValueIdSet & inputValues, |
| NABoolean thisIsAKeyPredicate, |
| VEGRewritePairs * lookup, |
| NABoolean replicateExpression, |
| const ValueIdSet * outputExpr, |
| const ValueIdSet * joinInputAndPotentialOutput, |
| const IndexDesc * iDesc, |
| const GroupAttributes * left_ga, |
| const GroupAttributes * right_ga |
| ) |
| { |
| // --------------------------------------------------------------------- |
| // Iterate over the predicate factors in the given predicate tree. |
| // --------------------------------------------------------------------- |
| NABoolean savedReplicateExpression = replicateExpression ; |
| for (CollIndex ix = 0; ix < entries(); ix++) |
| { |
| // ----------------------------------------------------------------- |
| // Walk through the item expression tree and replace any |
| // VEGPredicates or VEGReferences that are found. |
| // ----------------------------------------------------------------- |
| ValueId exprId = at(ix); |
| if (outputExpr && outputExpr->containsTheGivenValue(exprId)) |
| replicateExpression = FALSE; |
| |
| ItemExpr * iePtr = NULL; |
| if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON && |
| exprId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC) |
| { |
| iePtr = exprId.getItemExpr()->child(1)->replaceVEGExpressions |
| (availableValues, |
| inputValues, |
| thisIsAKeyPredicate, |
| lookup, |
| replicateExpression, |
| joinInputAndPotentialOutput, |
| iDesc, |
| left_ga, |
| right_ga); |
| } |
| else |
| { |
| |
| iePtr = exprId.getItemExpr()->replaceVEGExpressions |
| (availableValues, |
| inputValues, |
| thisIsAKeyPredicate, |
| lookup, |
| replicateExpression, |
| joinInputAndPotentialOutput, |
| iDesc, |
| left_ga, |
| right_ga); |
| } |
| |
| replicateExpression = savedReplicateExpression; |
| if (iePtr) // wild card expansion was successful |
| { |
| if (!iePtr->nodeIsBound() || iePtr->isPreCodeGenNATypeChanged()) |
| // redrive type synthesis for both self and child nodes |
| iePtr->synthTypeAndValueId(TRUE, TRUE); |
| if (iePtr->getValueId() != exprId) |
| at(ix) = iePtr->getValueId(); |
| } |
| else // discard element from the list |
| { |
| removeAt(ix); |
| ix--; |
| } |
| |
| } // loop over expression list |
| |
| } // ValueIdList::replaceVEGExpressions() |
| |
| OrderComparison ValueIdList::satisfiesReqdOrder(const ValueIdList & reqdOrder, |
| GroupAttributes *ga, |
| const ValueIdSet *preds) const |
| { |
| NAUnsigned numReqdEntries = reqdOrder.entries(); |
| OrderComparison allCols = SAME_ORDER; // sort order comparison of all columns together |
| OrderComparison oneCol; // one column compared with its counterpart |
| |
| CollIndex thisIx = 0; |
| CollIndex reqdIx = 0; |
| |
| ValueIdList tempThis(*this); |
| |
| // if group attributes are provided, remove any constant expressions, |
| // since those should not appear in the requirements and since they |
| // are not relevant to the ordering |
| if (ga) |
| tempThis.removeCoveredExprs(ga->getCharacteristicInputs()); |
| |
| CollIndex numActEntries = tempThis.entries(); |
| |
| // we assume that reqdOrder went through a RequirementGenerator |
| // and is therefore already optimized (e.g. no duplicate columns) |
| |
| // is the actual sort key at least as long as the required one? |
| if (numActEntries < numReqdEntries) |
| return DIFFERENT_ORDER; |
| |
| for (CollIndex reqdIx = 0; reqdIx < numReqdEntries; reqdIx++) |
| { |
| NABoolean done = FALSE; |
| |
| // compare the next required order column with one or more |
| // ValueIds in tempThis |
| while (!done) |
| { |
| // make sure we have enough columns in "tempThis" |
| if (thisIx >= numActEntries) |
| return DIFFERENT_ORDER; |
| |
| // compare the next column of tempThis with the required order |
| oneCol = reqdOrder[reqdIx].getItemExpr()->sameOrder( |
| tempThis[thisIx].getItemExpr()); |
| |
| done = TRUE; |
| |
| // If we didn't find the column, then try to find an equals |
| // predicate that equates the column to a constant, so that |
| // we can skip over it. This typically happens with computed |
| // columns such as salt or division that don't have associated |
| // VEGs, which are handled by tempThis.removeCoveredExprs() above. |
| if (oneCol == DIFFERENT_ORDER && |
| ga && |
| (numActEntries-thisIx) > (numReqdEntries-reqdIx) && // extra cols in this |
| ga->tryToEliminateOrderColumnBasedOnEqualsPred(tempThis[thisIx], |
| preds)) |
| { |
| // this is a predicate of the form col = const |
| // and col is our current ValueId in tempThis, |
| // therefore skip over it and try again |
| thisIx++; |
| done = FALSE; |
| } |
| } |
| |
| |
| if (reqdIx == 0) |
| allCols = oneCol; |
| else |
| allCols = combineOrderComparisons(allCols,oneCol); |
| |
| if (allCols == DIFFERENT_ORDER) |
| return allCols; |
| |
| thisIx++; |
| } |
| return allCols; |
| } |
| |
| NABoolean ValueIdList::satisfiesReqdArrangement( |
| const ValueIdSet &reqdArrangement, |
| GroupAttributes * ga, |
| const ValueIdSet *preds) const |
| { |
| // --------------------------------------------------------------------- |
| // check requirement for arrangement of data (check whether the required |
| // arranged columns form a prefix of the sort key or whether the |
| // table contains at most one row, in which case it satisfies any |
| // required ordering) |
| // --------------------------------------------------------------------- |
| ValueIdSet arrCols(reqdArrangement); |
| NABoolean found = TRUE; |
| CollIndex startCol = 0; |
| |
| // if group attributes are provided, remove any constant expressions, |
| // since those should not appear in the requirements and since they |
| // are not relevant to the ordering |
| if (ga) |
| arrCols.removeCoveredExprs(ga->getCharacteristicInputs()); |
| |
| // walk along the sort key, as long as all columns in the sort |
| // keys are part of the required set of arranged columns |
| |
| for (CollIndex i = startCol; i < entries() AND found; i++) |
| { |
| found = FALSE; |
| |
| // try a shortcut first: is the value id a required column? |
| if (reqdArrangement.contains((*this)[i])) |
| { |
| // yes, the sort key column is one of the columns passed in |
| // through "reqdArrangement", this required arrangement col. |
| // is satisfied |
| found = TRUE; |
| arrCols -= (*this)[i]; |
| } |
| else |
| { |
| // do it the long way: is the sort key colum in the same order |
| // as one of the columns that are required for the arrangement? |
| |
| ItemExpr *sortKeyCol = (*this)[i].getItemExpr(); |
| |
| for (ValueId x = reqdArrangement.init(); |
| reqdArrangement.next(x); |
| reqdArrangement.advance(x)) |
| { |
| if (sortKeyCol->sameOrder(x.getItemExpr()) != DIFFERENT_ORDER) |
| { |
| // yes, the sort key column has the same order as or the |
| // inverse order of the value x that was passed in |
| // through "reqdArrangement", this required arrangement col. |
| // is satisfied |
| found = TRUE; |
| arrCols -= x; |
| } |
| } |
| |
| if ((NOT found) && ga) |
| { |
| // if this column is constrained to a single value, |
| // we can skip over it and continue (we set found to |
| // true but don't remove anything from arrCols) |
| found = ga->tryToEliminateOrderColumnBasedOnEqualsPred( |
| (*this)[i], |
| preds); |
| } |
| } |
| } |
| |
| // if there are leftover columns from the required arrangement that |
| // haven't been satisfied yet, return FALSE |
| |
| return (arrCols.isEmpty()); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Method for applying transformNode() to a ValueIdList. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdList::transformNode(NormWA & normWARef, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| NABoolean transformed = FALSE; |
| RelExpr * saveOrigPtr = introduceSemiJoinHere; |
| |
| for (CollIndex index = 0; index < entries(); index++) |
| { |
| ItemExpr * iePtr = at(index).getItemExpr(); // original expr. |
| ExprValueId nePtr(iePtr); // normalized expr. |
| // Transform the Item Expression. |
| iePtr->getReplacementExpr()->transformNode(normWARef, nePtr, |
| introduceSemiJoinHere, externalInputs); |
| // If the original expression was transformed, update the list |
| if (nePtr != (const ItemExpr *)iePtr) |
| { |
| at(index) = nePtr->getValueId(); |
| } |
| // if the original expression contains an ITM_ONE_ROW aggregate as output, |
| // replace it with its child (or children) |
| |
| if ( iePtr->getReplacementExpr()->getOperatorType() == ITM_ONE_ROW ) |
| { |
| ItemExpr* ReplacedExpr = |
| iePtr->getReplacementExpr()->transformOneRowAggregate( normWARef ); |
| |
| // redrive type synthesis for both self and child nodes |
| ReplacedExpr->synthTypeAndValueId( TRUE, TRUE ); |
| |
| // For now (as of 11/17/98) assume arity of childIE is at most one |
| CMPASSERT(ReplacedExpr->child(0)->castToItemExpr()->getArity() <= 1 ); |
| |
| at(index) = ReplacedExpr->child(0)->castToItemExpr()->getValueId(); |
| } |
| } // loop over expressions list |
| |
| // if a subquery was transformed, set transformed flag |
| if (introduceSemiJoinHere != (const RelExpr *)saveOrigPtr) |
| transformed = TRUE; |
| |
| return transformed; |
| |
| } // ValueIdList::transformNode() |
| |
| // ------------------------------------------------------------------------ |
| // ValueIdList::removeCoveredExprs() |
| // |
| // Removes from the valueid set those expressions that are |
| // covered by the available values |
| // ------------------------------------------------------------------------ |
| |
| void ValueIdList::removeCoveredExprs(const ValueIdSet & newExternalInputs) |
| { |
| NABoolean coverFlag; |
| ValueIdSet referencedInputs; |
| GroupAttributes emptyGA; |
| |
| emptyGA.setCharacteristicOutputs(newExternalInputs); |
| for (CollIndex ix = 0; ix < entries(); ix++) |
| { |
| ValueId exprId = at(ix); |
| if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| VEG * vegPtr = ((VEGPredicate *)(exprId.getItemExpr()))->getVEG(); |
| coverFlag = emptyGA.covers(vegPtr->getVEGReference()->getValueId(), |
| newExternalInputs, |
| referencedInputs); |
| } |
| else |
| coverFlag = emptyGA.covers(exprId, |
| newExternalInputs, |
| referencedInputs); |
| if (coverFlag) |
| { |
| removeAt(ix); |
| ix--; |
| } |
| } // for |
| } // ValueIdList::removeCoveredExprs |
| |
| // ------------------------------------------------------------------------ |
| // ValueIdList::removeUnCoveredExprs() |
| // |
| // Removes from the valueid list those expressions that are NOT covered by |
| // the available values. |
| // |
| // When the first uncovered element is found, that and all subsequent list |
| // elements are removed. |
| // ------------------------------------------------------------------------ |
| |
| void ValueIdList::removeUnCoveredExprs(const ValueIdSet & newExternalInputs) |
| { |
| NABoolean coverFlag; |
| ValueIdSet referencedInputs; |
| GroupAttributes emptyGA; |
| |
| emptyGA.setCharacteristicOutputs(newExternalInputs); |
| for (CollIndex ix = 0; ix < entries(); ix++) |
| { |
| ValueId exprId = at(ix); |
| if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| VEG * vegPtr = ((VEGPredicate *)(exprId.getItemExpr()))->getVEG(); |
| coverFlag = emptyGA.covers(vegPtr->getVEGReference()->getValueId(), |
| newExternalInputs, |
| referencedInputs); |
| } |
| else |
| coverFlag = emptyGA.covers(exprId, |
| newExternalInputs, |
| referencedInputs); |
| if (!coverFlag) |
| { |
| CollIndex entriesToRemove = entries()-ix ; |
| while (entriesToRemove > 0) |
| { |
| removeAt (entries()-1) ; // remove the last elt, n times |
| entriesToRemove-- ; |
| } |
| } |
| } // for |
| } // ValueIdList::removeUnCoveredExprs |
| |
| // --------------------------------------------------------------------- |
| // removeDuplicateValueIds() |
| // This method removes duplicate value ids(vid) from the ValueIdList object. |
| // First get the last vid and walks through the list comparing this last vid |
| // with the rest of vids. If the previous vid is same as the next one then, |
| // remove the last one. |
| // --------------------------------------------------------------------- |
| void ValueIdList::removeDuplicateValueIds() |
| { |
| CollIndex entry = entries()-1; |
| for (CollIndex last = entry; last > 0; last--) |
| { |
| // Get the last vid from the List. |
| // simplify the expression |
| // e.g. remove any INVERSE nodes on top (i.e. DESC) |
| ValueId prev = |
| (*this)[last].getItemExpr()->removeInverseOrder()->getValueId(); |
| // walk through the list comparing next vid with the prev vid. |
| for (Int32 start = 0; start < (Int32)last; start++) |
| { |
| // Get the next value id from the List. |
| // simplify the expression |
| // e.g. remove any INVERSE nodes on top (i.e. DESC) |
| ValueId next = |
| (*this)[start].getItemExpr()->removeInverseOrder()->getValueId(); |
| // If there is a duplicate vid, then remove the last one. |
| if (next == prev) |
| { |
| removeAt(last); |
| break; |
| } |
| } // for start |
| } // for last |
| } // removeDuplicateValueIds() |
| |
| |
| // --------------------------------------------------------------------- |
| // substituteValueIds() |
| // This method replaces the vid with the replacementVid |
| // --------------------------------------------------------------------- |
| void ValueIdList::substituteValueIds(const ValueId vid, const ValueId replacementVid) |
| { |
| for (CollIndex i=0; i < entries(); i++) |
| { |
| // walk through the list comparing next cvid with vid. |
| ValueId cvid = (*this)[i]; |
| |
| if (cvid == vid) |
| { |
| // found one to replace, replace it. |
| removeAt(i); |
| insertAt(i, replacementVid); |
| } |
| } // for i |
| } // substituteValueIds() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdList::removeSubqueryOrIsolatedUDFunctionPredicates() |
| // |
| // This method walks through the ItemExprs that belong to this set, |
| // collects the ValueIds of the subqueries or UDFs in the list. It also |
| // collects any ValueIdProxies associated with the said subquiery or UDF. |
| // ----------------------------------------------------------------------- |
| void ValueIdList::removeSubqueryOrIsolatedUDFunctionPredicates(ValueIdSet & subqueryExpr) |
| { |
| |
| for (CollIndex ix = 0; ix < entries(); ix++) |
| { |
| ValueId exprId = at(ix); |
| |
| if (exprId.getItemExpr()->containsValueIdProxySibling(subqueryExpr) || |
| exprId.getItemExpr()->containsSubquery() || |
| exprId.getItemExpr()->containsIsolatedUDFunction()) |
| |
| { |
| subqueryExpr.addElement(exprId); |
| removeAt(ix); |
| ix--; |
| |
| } |
| } |
| } // ValueIdList::removeSubqueryOrIsolatedUDFunctionPredicates() |
| |
| void ValueIdList::unparse(NAString &result, |
| PhaseEnum phase, |
| UnparseFormatEnum form, |
| TableDesc * tabId) const |
| { |
| //result += "("; |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| if (i > 0) result += ", "; |
| |
| if ( form == ERROR_MSG_FORMAT ) // ERROR_MSG_FORMAT : don't print vid's |
| { |
| at(i).getItemExpr()->unparse(result,phase,EXPLAIN_FORMAT,tabId); |
| } |
| else |
| { |
| //never print vid's |
| /* char vidbuf[TEXT_DISPLAY_LENGTH]; |
| sprintf(vidbuf, "[%u]", (CollIndex)at(i)); |
| result += vidbuf; |
| */ |
| |
| at(i).getItemExpr()->unparse(result,phase,form,tabId); |
| } |
| } |
| //result += ")"; |
| |
| |
| } // ValueIdList::unparse |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdList::replaceOperandsOfInstantiateNull() |
| // This method is used by the code generator for replacing the |
| // operands of an ITM_INSTANTIATE_NULL with a value that belongs |
| // to availableValues. |
| // ----------------------------------------------------------------------- |
| void ValueIdList::replaceOperandsOfInstantiateNull |
| (const ValueIdSet & availableValues, |
| const ValueIdSet & inputValues) |
| { |
| for (CollIndex j = 0; j < entries(); j++) |
| { |
| at(j).getItemExpr()->replaceOperandsOfInstantiateNull(availableValues,inputValues); |
| } |
| } // ValueIdList::replaceOperandsOfInstantiateNull() |
| |
| void ValueIdList::print(FILE* ofd, const char* indent, const char* title, |
| CollHeap *c, char *buf) const |
| { |
| BUMP_INDENT(indent); |
| Space * space = (Space *)c; |
| char mybuf[1000]; |
| |
| snprintf(mybuf, sizeof(mybuf), "%s%s\n",NEW_INDENT,title); |
| PRINTIT(ofd, c, space, buf, mybuf); |
| |
| for (CollIndex j = 0; j < entries(); j++) |
| { |
| Int32 i = (Int32)((CollIndex) (at(j))); // valueid as an integer |
| NAString unparsed(CmpCommon::statementHeap()); |
| |
| if (at(j).getItemExpr()) |
| at(j).getItemExpr()->unparse(unparsed); // expression as ascii string |
| |
| snprintf(mybuf, sizeof(mybuf), "%4d: %s\n",i,(const char *) unparsed); |
| PRINTIT(ofd, c, space, buf, mybuf); |
| } |
| } // ValueIdList::print() |
| |
| void ValueIdList::display() const // To be called from the debugger |
| { |
| print(); |
| } |
| |
| // *********************************************************************** |
| // Methods for class ValueIdSet |
| // *********************************************************************** |
| |
| THREAD_P ObjectCounter (*ValueIdSet::counter_)(0); |
| |
| ValueIdSet::ValueIdSet() : |
| ClusteredBitmap(CmpCommon::statementHeap()) |
| { |
| (*counter_).incrementCounter(); |
| } |
| |
| ValueIdSet::ValueIdSet(const ValueIdSet &other) : |
| ClusteredBitmap(other, CmpCommon::statementHeap()) |
| { |
| (*counter_).incrementCounter(); |
| } |
| |
| ValueIdSet::ValueIdSet(const ValueId& vid) : |
| ClusteredBitmap(CmpCommon::statementHeap()) |
| { |
| addElement(vid.id_); |
| (*counter_).incrementCounter(); |
| } |
| |
| ValueIdSet::ValueIdSet(const ValueIdList &other) : |
| ClusteredBitmap(CmpCommon::statementHeap()) |
| { |
| for (CollIndex i = 0; i < other.entries(); i++) |
| { |
| *this += other[i]; |
| } |
| (*counter_).incrementCounter(); |
| } |
| |
| ValueIdSet::ValueIdSet(ValueDescArray *arr) : |
| ClusteredBitmap(CmpCommon::statementHeap()) |
| { |
| (*counter_).incrementCounter(); |
| } |
| |
| NABoolean ValueIdSet::operator == (const ValueIdSet & other) const |
| { |
| if (entries() != other.entries()) return FALSE; |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (other.contains(vid) == FALSE) |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| void ValueIdSet::getFirst(ValueId & v) const |
| { |
| v = init(); |
| if (NOT next(v)) |
| v = NULL_VALUE_ID; |
| } |
| //------------------------------------------------------------------------ |
| // ValueIdSet::convertToBaseids() |
| // Method that synthesizes a valueIdset that is comprised of the basecolumn |
| // valueids of this valueidset. So the ids in this set are expected to be |
| // for Columns. |
| //------------------------------------------------------------------------ |
| ValueIdSet ValueIdSet::convertToBaseIds() const |
| { |
| ValueIdSet result; |
| for(ValueId vid=init(); next(vid); advance(vid)) |
| { |
| // Since we can call this on any ValueIdSet, make sure we actually have |
| // an IndexColumn. |
| DCMPASSERT(vid.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN); |
| result.insert(((BaseColumn *)(((IndexColumn *)vid.getItemExpr())-> |
| getDefinition().getItemExpr()))->getValueId()); |
| } |
| return result; |
| } |
| |
| // -------------------------------------------------------------------- |
| // ValueIdSet::getMinOrigUecOfJoiningCols |
| // Out of all columns in the set, return the one with minimum UEC |
| // -------------------------------------------------------------------- |
| |
| CostScalar ValueIdSet::getMinOrigUecOfJoiningCols() |
| { |
| BaseColumn * result = NULL; |
| CostScalar minUec = COSTSCALAR_MAX; |
| |
| for(ValueId vid= init(); next(vid); advance(vid)) |
| { |
| // Find the base column for the input column: |
| |
| ItemExpr *inputColumnIEPtr = vid.getItemExpr(); |
| |
| if (inputColumnIEPtr == NULL) |
| continue; |
| |
| ValueId baseColumnForInputColumn = NULL_VALUE_ID; |
| switch ( inputColumnIEPtr->getOperatorType() ) |
| { |
| case ITM_VEG_REFERENCE: |
| { |
| // The inputColumn is a VEG reference |
| // Loop through the columns until you find it: |
| ValueIdSet columnSet; |
| |
| inputColumnIEPtr->findAll(ITM_BASECOLUMN, columnSet, TRUE, TRUE); |
| if (!columnSet.isEmpty() ) |
| columnSet.getFirst(baseColumnForInputColumn); |
| break; |
| } |
| |
| case ITM_INSTANTIATE_NULL: |
| case ITM_BASECOLUMN: |
| // The inputColumn is a base column. |
| baseColumnForInputColumn = vid; |
| break; |
| |
| case ITM_INDEXCOLUMN: |
| // Get the base column for the index column: |
| { |
| const BaseColumn *bcIEPtrForIndexColumn = |
| (BaseColumn *) ((IndexColumn *) inputColumnIEPtr)-> |
| getDefinition().getItemExpr(); |
| |
| baseColumnForInputColumn = bcIEPtrForIndexColumn->getValueId(); |
| break; |
| } |
| |
| default: |
| // The value id in inputColumns *must* be either an index column or |
| // a base table column; anything else, when we reach here, is an |
| // internal error. |
| break; |
| } // switch on type of input column |
| |
| if (baseColumnForInputColumn != NULL_VALUE_ID) |
| result = (BaseColumn *)(vid.getItemExpr() ); |
| |
| if (result) |
| { |
| ColStatDescList & colStatsList = result->getTableDesc()->tableColStats(); |
| |
| CollIndex index; |
| NABoolean found = colStatsList.getColStatDescIndexForColumn( index, baseColumnForInputColumn ); |
| |
| if (found) |
| { |
| CostScalar uec = colStatsList[index]->getColStats()->getTotalUec(); |
| if (uec < minUec) |
| { |
| minUec = uec; |
| } |
| } |
| } |
| } |
| |
| return minUec; |
| } |
| |
| NABoolean ValueIdSet::coversFirstN(const ValueIdList &other, Int32 N) const |
| { |
| Int32 i, limit = N <= (Int32)other.entries() ? N : (Int32)other.entries(); |
| for (i = 0; i < limit; i++) { |
| // if other is an indexcolumn, compare against its definition, |
| // ie, compare against what column it indexes |
| if (other[i].getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN) { |
| IndexColumn *ixCol = (IndexColumn*)(other[i].getItemExpr()); |
| if (!contains(ixCol->getDefinition())) { return FALSE; } |
| } |
| else if (!contains(other[i])) { return FALSE; } |
| } |
| return TRUE; // this covers first N elements of other |
| } |
| |
| // how many prefix columns of other are found in this predicate? |
| Int32 ValueIdSet::prefixMatchesOf(const ValueIdList &other) const |
| { |
| Int32 result = 0; // assume no prefix match until proven otherwise |
| ValueId vId; |
| |
| for (CollIndex i = 0; i < other.entries(); i++) |
| { |
| if (referencesTheGivenValue(other[i], vId)) |
| result++; |
| else |
| return result; // quit on first prefix mismatch |
| } |
| // all elements of other are found in this |
| return result; |
| } |
| |
| NABoolean ValueIdSet::isDensePrefix(const ValueIdList &other) const |
| { |
| ValueIdSet notContainedInThis(*this); |
| NABoolean found = TRUE; |
| |
| for (CollIndex i = 0; i < other.entries() AND found; i++) |
| { |
| if (contains(other[i])) |
| notContainedInThis -= other[i]; |
| else |
| found = FALSE; |
| } |
| |
| // if all value ids of this set are contained in the prefix, return TRUE |
| return notContainedInThis.isEmpty(); |
| } |
| |
| Lng32 ValueIdSet::getRowLength() const |
| { |
| Lng32 result = 0; |
| |
| for (ValueId x = init(); next(x); advance(x)) |
| { |
| result += x.getType().getTotalSize(); |
| } |
| |
| return result; |
| } |
| |
| Lng32 ValueIdSet::getRowLengthOfNumericCols() const |
| { |
| Lng32 result = 0; |
| |
| for (ValueId x = init(); next(x); advance(x)) |
| { |
| if (x.getType().isNumeric()) |
| result += x.getType().getTotalSize(); |
| } |
| |
| return result; |
| } |
| |
| |
| // *********************************************************************** |
| // normalizer-related methods for class ValueIdSet |
| // *********************************************************************** |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::accumulateReferencedValues() |
| // |
| // This method accumulates those values that are members of the |
| // referencedSet and are referenced either individually or in a |
| // VEGReference in the referencingSet, if the optional parameters |
| // allow it. The original contents of this ValueIdSet are augmented. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::accumulateReferencedValues(const ValueIdSet & referencedSet, |
| const ValueIdSet & referencingSet, |
| NABoolean doNotDigInsideVegRefs, |
| NABoolean doNotDigInsideInstNulls) |
| { |
| for (ValueId referencedExpr = referencedSet.init(); |
| referencedSet.next(referencedExpr); |
| referencedSet.advance(referencedExpr)) |
| { |
| for (ValueId referencingExpr = referencingSet.init(); |
| referencingSet.next(referencingExpr); |
| referencingSet.advance(referencingExpr)) |
| { |
| if (referencingExpr.getItemExpr() |
| ->referencesTheGivenValue( |
| referencedExpr, |
| doNotDigInsideVegRefs, |
| doNotDigInsideInstNulls)) |
| addElement(referencedExpr); |
| } // referencingExpr loop |
| } // referencedExpr loop |
| |
| } // ValueIdSet::accumulateReferencedValues() |
| |
| // ----------------------------------------------------------------------- |
| // Walk through an ItemExpr tree and gather the ValueIds of those |
| // expressions that behave as if they are "leaves" for the sake of |
| // the coverage test, e.g., expressions that have no children, or |
| // aggregate functions, or instantiate null. These are usually values |
| // that are produced in one "scope" and referenced above that "scope" |
| // in the dataflow tree for the query. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::getLeafValuesForCoverTest(ValueIdSet & leafValueIds, |
| const GroupAttributes& coveringGA, |
| const ValueIdSet & newExternalInputs) const |
| { |
| for (ValueId exprId = init(); next(exprId); advance(exprId) ) |
| { |
| exprId.getItemExpr()->getLeafValuesForCoverTest(leafValueIds, coveringGA, |
| newExternalInputs); |
| } |
| } // ValueIdSet::getLeafValuesForCoverTest() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::referencesOneValueFromTheSet() |
| // |
| // Check whether an expression contained in this ValueIdSet |
| // references an expression that is a member of the otherSet. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::referencesOneValueFromTheSet |
| (const ValueIdSet & otherSet) const |
| { |
| for (ValueId myExpr = init(); next(myExpr); advance(myExpr)) |
| { |
| for (ValueId otherExpr = otherSet.init(); |
| otherSet.next(otherExpr); |
| otherSet.advance(otherExpr)) |
| { |
| if (myExpr.getItemExpr()->referencesTheGivenValue(otherExpr)) |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } // ValueIdSet::referencesOneValueFromTheSet() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::referencesAllValuesFromMe() |
| // |
| // Check whether all expressions contained in this ValueIdSet |
| // references an expression that is a member of the otherSet. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::referencesAllValuesFromMe |
| (const ValueIdSet & otherSet) const |
| { |
| ValueId id; |
| for (ValueId myExpr = init(); next(myExpr); advance(myExpr)) |
| { |
| if (NOT(otherSet.referencesTheGivenValue(myExpr, id))) |
| return FALSE; |
| } |
| return TRUE; |
| } // ValueIdSet::referencesAllValuesFromMe() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::referencesTheGivenValue() |
| // |
| // Check whether any expression contained in this ValueIdSet |
| // references the given value. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::referencesTheGivenValue(const ValueId & theValue, |
| ValueId & exprId, |
| NABoolean doNotDigInsideVegRefs, |
| NABoolean doNotDigInsideInstNulls) const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->referencesTheGivenValue( |
| theValue, |
| doNotDigInsideVegRefs, |
| doNotDigInsideInstNulls)) |
| { |
| exprId = vid; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } // ValueIdSet::referencesTheGivenValue() |
| |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::containsTheGivenValue() |
| // |
| // Check whether any expression contained in this ValueIdSet |
| // references the given value. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsTheGivenValue( const ValueId & theValue ) const |
| { |
| for ( ValueId vid = init(); next(vid); advance(vid) ) |
| { |
| if ( vid.getItemExpr()->containsTheGivenValue( theValue ) ) |
| { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } // ValueIdSet::containsTheGivenValue() |
| |
| // return true iff set has RandomNum expr |
| NABoolean ValueIdSet::hasRandom() const |
| { |
| for ( ValueId vid = init(); next(vid); advance(vid) ) |
| { |
| if ( vid.getItemExpr()->getOperatorType() == ITM_RANDOMNUM ) |
| { |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::membersCoveredInSet() |
| // |
| // Check whether any of the members of this ValueIdSet are contained |
| // in the provided ValueIdSet. Return the number of members found. |
| // ----------------------------------------------------------------------- |
| Int32 ValueIdSet::membersCoveredInSet (const ValueIdSet& vidSet, NABoolean lookBelowInstantiateNull) const |
| { |
| NABoolean coverFlag = FALSE; |
| ItemExpr * memberExpr = NULL; |
| ValueId memberValId; |
| |
| Int32 membersFound = 0; |
| |
| for ( ValueId vid = init(); next(vid); advance(vid) ) |
| { |
| // If any member of this set contains the INVERSE function (for |
| // specifying DESC ordering), examine the valueid of the child of |
| // the inverse expression for coverage. |
| memberExpr = vid.getItemExpr(); |
| if ((memberExpr->getOperatorType() == ITM_INVERSE) || |
| (lookBelowInstantiateNull && memberExpr->getOperatorType() == ITM_INSTANTIATE_NULL)) |
| memberValId = ((ItemExpr *)memberExpr->child(0))->getValueId(); |
| else |
| memberValId = vid; |
| |
| if ( vidSet.contains( memberValId ) ) |
| membersFound++; |
| } |
| |
| return (membersFound); |
| } |
| |
| //------------------------------------------------------- |
| //removeCoveredVidSet() |
| //Input: other set |
| //output: reduced *this set by other set |
| //Constraints: will remove the ids as long as |
| //other set contains a reference or the id itself. |
| //--------------------------------------------------------- |
| void ValueIdSet::removeCoveredVIdSet(const ValueIdSet & otherSet) |
| { |
| for ( ValueId id = otherSet.init(); otherSet.next(id); otherSet.advance(id) ) |
| { |
| if ( id.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE ) |
| { |
| const ValueIdSet & insideSet = |
| ((VEGReference *)(id.getItemExpr()))->getVEG()->getAllValues(); |
| remove( insideSet ); |
| } |
| else |
| { |
| remove( id ); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::removeUnReferencedVEGPreds() |
| // |
| // Check whether a VEGPred contained in this ValueIdSet references |
| // references a value that is a member of the otherSet and remove it |
| // othewise. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::removeUnReferencedVEGPreds |
| (const ValueIdSet & otherSet) |
| { |
| for (ValueId myExpr = init(); next(myExpr); advance(myExpr)) |
| { |
| if (myExpr.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| ValueIdSet predSet; |
| predSet += myExpr; |
| // If any of the vegMembers is provided by the available values |
| // we want to keep the predicate |
| if(NOT predSet.referencesOneValueFromTheSet(otherSet)) |
| subtractElement(myExpr); // delete original expression from set |
| } |
| } |
| } // ValueIdSet::removeUnReferencedVEGPreds() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::removeSubqueryOrIsolatedUDFunctionPredicates() |
| // |
| // This method walks through the ItemExprs that belong to this set, |
| // collects the ValueIds of the expressions at their leaves and |
| // returns them. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::removeSubqueryOrIsolatedUDFunctionPredicates(ValueIdSet & subqueryExpr) |
| { |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| if (exprId.getItemExpr()->containsSubquery() || |
| (exprId.getItemExpr()->containsIsolatedUDFunction())) |
| { |
| subqueryExpr.addElement(exprId); |
| subtractElement(exprId); // delete original expression from set |
| } |
| } |
| } // ValueIdSet::removeSubqueryOrIsolatedUDFunctionPredicates() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::transformNode() |
| // |
| // Method for applying transformNode() to a ValueIdSet. |
| // If the ValueIdSet represents a predicate tree, then the caller is |
| // expected to set movePredicate to TRUE. Whenever movePredicate is |
| // is set and a subquery is transformed, the transformed predicate |
| // is moved to the SemiJoin or Join that is introduced by the |
| // subquery transformation. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::transformNode(NormWA & normWARef, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs, |
| const NABoolean movePredicates, |
| const NABoolean postJoinPredicates) |
| { |
| // --------------------------------------------------------------------- |
| // Save the contents of introduceSemiJoinHere in order to be able to |
| // detect whether a subquery transformation was performed while |
| // processing one of the elements of this set. |
| // --------------------------------------------------------------------- |
| RelExpr * saveOrigPtr = introduceSemiJoinHere; |
| // --------------------------------------------------------------------- |
| // newJoin is allocated for referencing a SemiJoin or a Join that is |
| // introduced by a subquery transformation. |
| // Set its initial value to be the contents of introduceSemiJoinHere. |
| // --------------------------------------------------------------------- |
| RelExpr * newJoin = (RelExpr *) introduceSemiJoinHere.getPtr(); |
| // --------------------------------------------------------------------- |
| // newExpr will contain the ValueIds of new expressions that are |
| // introduced by the normalizing transformations that are performed |
| // during the transformation phase of query normalization. |
| // --------------------------------------------------------------------- |
| ValueIdSet newExpr; |
| // --------------------------------------------------------------------- |
| // Iterate over all the values (scalar expressions) in this set. |
| // --------------------------------------------------------------------- |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| ItemExpr *iePtr = exprId.getItemExpr(); // original expr. |
| ExprValueId nePtr(iePtr); // normalized expr. |
| // ----------------------------------------------------------------- |
| // Transform the Item Expression. |
| // ----------------------------------------------------------------- |
| iePtr->getReplacementExpr()->transformNode(normWARef, nePtr, |
| introduceSemiJoinHere, |
| externalInputs); |
| // ----------------------------------------------------------------- |
| // If the original expression was transformed .. |
| // ----------------------------------------------------------------- |
| if (nePtr != (const ItemExpr *)iePtr) |
| { |
| // ------------------------------------------------------------- |
| // Delete the original expression from this set. |
| // ------------------------------------------------------------- |
| subtractElement(exprId); |
| // ------------------------------------------------------------- |
| // If the original expression was not eliminated by transformation, |
| // then if its replacement is an unconditional True, eliminate it now |
| // otherwise, accumulate the replacement in the set newExpr. |
| // ------------------------------------------------------------- |
| if (nePtr.getPtr()) |
| { |
| if (nePtr.getPtr()->getOperatorType() == ITM_RETURN_TRUE && |
| nePtr.getPtr()->getArity() == 0) |
| nePtr = NULL; |
| else |
| { |
| // Make sure selectivity hint information is carried over correctly. |
| if(iePtr->isSelectivitySetUsingHint()) |
| { |
| nePtr.getPtr()->setSelectivitySetUsingHint(); |
| nePtr.getPtr()->setSelectivityFactor(iePtr->getSelectivityFactor()); |
| } |
| |
| // See if UDF_SUBQ_IN_AGGS_AND_GBYS is enabled. It is |
| // enabled if the default is ON, or if the default is |
| // SYSTEM and ALLOW_UDF is ON. |
| NABoolean udfSubqInAggGrby_Enabled = FALSE; |
| DefaultToken udfSubqTok = |
| CmpCommon::getDefault(UDF_SUBQ_IN_AGGS_AND_GBYS); |
| if ((udfSubqTok == DF_ON) || |
| (udfSubqTok == DF_SYSTEM)) |
| udfSubqInAggGrby_Enabled = TRUE; |
| |
| if (!udfSubqInAggGrby_Enabled) |
| nePtr->convertToValueIdSet(newExpr); |
| else |
| // Since we can have UDFs or Subqueries in the group by |
| // we now can get an ITM_ITEM_LIST back from a transformation |
| // of a MVF or multi-degree subqueries. So we have to |
| // flatten any such itemExpr |
| nePtr->convertToValueIdSet(newExpr, NULL, |
| ITM_AND, TRUE, TRUE/*flattenlist*/); |
| } |
| } |
| } |
| // ----------------------------------------------------------------- |
| // If a subquery transformation has introduced a new Join ... |
| // ----------------------------------------------------------------- |
| if (introduceSemiJoinHere.getPtr() != newJoin) |
| { |
| // ------------------------------------------------------------- |
| // Get addressability to the new Join node. |
| // ------------------------------------------------------------- |
| newJoin = (RelExpr *) introduceSemiJoinHere.getPtr(); |
| // ------------------------------------------------------------- |
| // Note that it is possible to encounter a subquery in each |
| // scalar expression that is processed per iteration of the loop. |
| // Hence, introduceSemiJoinHere can contain a different |
| // SemiJoin or a Join per iteration of the loop. Therefore, the |
| // movement of predicates must be done inside the loop. |
| // ------------------------------------------------------------- |
| if (movePredicates) |
| { // this ValueIdSet represents a predicate tree |
| // --------------------------------------------------------- |
| // If the subquery predicate is buried in a complex |
| // expression, such as T1.x = 10 OR EXISTS ( ... ), then |
| // the predicate that replaces the subquery cannot be moved |
| // independently. The entire expression in which it is |
| // contained must be moved. Delete the original expression |
| // from this set. |
| // --------------------------------------------------------- |
| subtractElement(exprId); |
| |
| if (nePtr.getPtr()) |
| { // if the subquery was not eliminated |
| // ----------------------------------------------------- |
| // Remove the expression that replaces the subquery |
| // predicate (*nePtr) from newExpr because it will be |
| // evaluated as a member of another expression tree. |
| // ----------------------------------------------------- |
| newExpr -= nePtr->getValueId(); |
| // ----------------------------------------------------- |
| // Add it to newJoin (the SemiJoin or Join) node that |
| // was introduced by the subquery transformation. |
| // ----------------------------------------------------- |
| ValueIdSet transformedExpr; |
| nePtr->convertToValueIdSet(transformedExpr, |
| NULL, ITM_AND, FALSE); |
| |
| //-------------------------------------------------------- |
| // Generator does not know how to transmit |
| // ONE_ROW of columns yet from a child to its parent. So do not |
| // ask for it. One Row check is already performed at the |
| // concerned GroupBy node. |
| //-------------------------------------------------------- |
| |
| transformedExpr.replaceOneRowbyList( normWARef ); |
| |
| if (newJoin->getOperator().match(REL_ANY_SEMIJOIN)) |
| ((Join *)newJoin)->joinPred() += transformedExpr; // ON clause |
| else |
| newJoin->selectionPred() += transformedExpr; |
| // ----------------------------------------------------- |
| // Compute the additional set of external inputs that |
| // should be supplied to newJoin so that the expression |
| // that replaces the subquery predicate (*nePtr) can |
| // be evaluated. |
| // ----------------------------------------------------- |
| ValueIdSet neededValues(externalInputs); |
| ValueIdSet outputValues; |
| newJoin->getPotentialOutputValues(outputValues); |
| neededValues -= outputValues; |
| // ----------------------------------------------------- |
| // neededValues contains the external inputs. Remove |
| // all those values from it that are not referenced |
| // by the expression that replaces the subquery |
| // predicate (*nePtr). |
| // ----------------------------------------------------- |
| transformedExpr.weedOutUnreferenced(neededValues); |
| newJoin->getGroupAttr()->addCharacteristicInputs(neededValues); |
| } // if the subquery was not eliminated |
| } // this ValueIdSet represents a predicate tree |
| else |
| { |
| // --------------------------------------------------------- |
| // If we are not moving predicates, newJoin should not be |
| // a SemiJoin. This is because a SemiJoin can only be |
| // introduced by the transformation of a subquery predicate. |
| // --------------------------------------------------------- |
| CMPASSERT(NOT newJoin->getOperator().match(REL_ANY_SEMIJOIN)); |
| } |
| } // if (introduceSemiJoinHere != newJoin) |
| // ----------------------------------------------------------------- |
| // If the given predicates are post-join predicates, i.e., they |
| // are evaluated after the join is performed, check whether a |
| // left join can be converted to an inner join by the transformed |
| // predicate. The check initiates the transformation of a left |
| // join to an inner join as a side effect. |
| // ----------------------------------------------------------------- |
| if (postJoinPredicates) |
| { |
| ValueIdSet outerReferences; |
| newJoin->getGroupAttr()->getCharacteristicInputs(). |
| getOuterReferences(outerReferences); |
| if (nePtr != (const ItemExpr *)iePtr) |
| { |
| if (nePtr.getPtr()) // transformation hasn't eliminated it |
| nePtr->predicateEliminatesNullAugmentedRows(normWARef, outerReferences); |
| } |
| else |
| iePtr->predicateEliminatesNullAugmentedRows(normWARef, outerReferences); |
| } |
| // --------------------------------------------------------------------- |
| // try conditionally eliminate LJ even for join preds. The code |
| // in the else clause pretty much resembles the code in predicateElim- |
| // inateNullAugmentRows(). The only difference is due to the nature of |
| // join predicate, we need to check for whether the instantiate null |
| // value involved comes from the right before transforming the associated |
| // left join into an inner join. The characteristics outputs from the |
| // right child is needed to determine this. Ideally, this additional |
| // parameter could be passed in predicateEliminateNullAugmentedRows() |
| // so that the condition could be determined there. However, for now, |
| // I don't have time to recompile a bunch of stuffs after changing a |
| // header file. So,... promise to move this code into there after FCS. |
| // --------------------------------------------------------------------- |
| else |
| { |
| if (saveOrigPtr) |
| { |
| const ValueIdSet & outputs = saveOrigPtr->getGroupAttr()-> |
| getCharacteristicOutputs(); |
| ItemExpr *ie; |
| if (nePtr != (const ItemExpr *)iePtr) |
| { |
| if (nePtr.getPtr()) // transformation hasn't eliminated it |
| ie = nePtr.getPtr(); |
| else |
| ie = NULL; |
| } |
| else ie = iePtr; |
| |
| if (ie) |
| { |
| // emulate predicateEliminatesNullAugmentedRows() in BiRelat |
| // |
| |
| NABoolean isFOJ = FALSE; |
| if (normWARef.getCurrentOwnerExpr()) |
| isFOJ = (normWARef.getCurrentOwnerExpr() |
| ->getOperatorType() == REL_FULL_JOIN); |
| |
| if ( (NOT normWARef.walkingAnExprTree()) AND |
| (NOT isFOJ) AND |
| (ie->getOperatorType() == ITM_EQUAL OR |
| ie->getOperatorType() == ITM_NOT_EQUAL OR |
| ie->getOperatorType() == ITM_LESS OR |
| ie->getOperatorType() == ITM_LESS_EQ OR |
| ie->getOperatorType() == ITM_GREATER OR |
| ie->getOperatorType() == ITM_GREATER_EQ) ) |
| { |
| if (ie->child(0)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = |
| (InstantiateNull *)(ie->child(0)->castToItemExpr()); |
| if (NOT inst->NoCheckforLeftToInnerJoin) |
| { |
| // Only for values coming from right hand side. |
| // |
| if (outputs.contains(ie->child(0)->getValueId())) |
| { |
| ie->child(0) = ie->child(0)-> |
| initiateLeftToInnerJoinTransformation(normWARef); |
| } |
| } |
| } |
| if (ie->child(1)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = |
| (InstantiateNull *)(ie->child(1)->castToItemExpr()); |
| if (NOT inst->NoCheckforLeftToInnerJoin) |
| { |
| // Only for values coming from right hand side. |
| // |
| if (outputs.contains(ie->child(1)->getValueId())) |
| { |
| ie->child(1) = ie->child(1)-> |
| initiateLeftToInnerJoinTransformation(normWARef); |
| } |
| } |
| } |
| } // end of emulation of predicateEliminatesNullAugmentedRows() |
| } // if(ie) |
| } // endif(saveOrigPtr) |
| } // endif(postJoinPredicates) |
| |
| } // loop over given expressions |
| |
| addSet(newExpr); // add transformed expressions to the given set |
| |
| // Indicate whether a subquery was transformed to the caller. |
| return (introduceSemiJoinHere != (const RelExpr *)saveOrigPtr); |
| |
| } // ValueIdSet::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // Method for applying normalizeNode() to a ValueIdSet. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::normalizeNode(NormWA & normWARef) |
| { |
| NABoolean transformed = FALSE; |
| ValueIdSet newExpr; |
| ItemExpr * nePtr; // normalized expr. |
| for (ValueId exprId = init(); next(exprId); advance(exprId) ) |
| { |
| ItemExpr *iePtr = exprId.getItemExpr(); // original expr. |
| // Transform the Item Expression. |
| nePtr = iePtr->getReplacementExpr()->normalizeNode(normWARef); |
| // If the original expression was transformed, remember transformed ids |
| if ( nePtr != iePtr || |
| nePtr->getValueId() != exprId ) |
| { |
| transformed = TRUE; |
| |
| // Need to set selectivity correctly when JOIN predicate (t1.a = t2.a) is eliminated: |
| // select * from t1, t2 where t1.a = t2.a <<+ selectivity 10e-3 >> and t1.a = 3; |
| if(iePtr->isSelectivitySetUsingHint() && |
| nePtr->getOperatorType() == ITM_VEG_PREDICATE ) |
| { |
| VEG * predVEG = ((VEGPredicate *)nePtr)->getVEG(); |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| ItemExpr *constant = NULL; |
| if(VEGGroup.referencesAConstValue(&constant) || |
| VEGGroup.referencesAConstExpr(&constant)) |
| { |
| ValueIdSet cols; |
| nePtr->findAll(ITM_BASECOLUMN, cols, TRUE, TRUE); |
| |
| CollIndex tblCount = cols.getAllTables()->entries(); |
| if(tblCount > 0) |
| { |
| // The following line is to make sure selectivity pushed down to Scan will result |
| // in cumulative selectivity equal to that specifed originally by the user.In the |
| // example above, the selectivity pushed down will be pow(sel, 1/2) = sqrt(0.01) = 0.1. |
| double selFactor = pow(iePtr->getSelectivityFactor(), ((double)1/tblCount)); |
| nePtr->setSelectivitySetUsingHint(); |
| nePtr->setSelectivityFactor(selFactor); |
| } |
| } |
| } |
| subtractElement(exprId); // delete original expression from set |
| // accumulate new expressions in another set |
| if (nePtr) |
| nePtr->convertToValueIdSet(newExpr); |
| } |
| } // loop over given expressions |
| |
| addSet(newExpr); // add transformed expressions to the given set |
| |
| return transformed; |
| } // ValueIdSet::normalizeNode |
| |
| // ----------------------------------------------------------------------- |
| // lookForVEGReferences() |
| // Accumulate ValueIds of VEGReferences, if any, in vs. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::lookForVEGReferences(ValueIdSet & VEGRefSet) const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) |
| VEGRefSet += vid; |
| } |
| } // ValueIdSet::lookForVEGReferences |
| |
| // *this is a set of (join) equality predicates. |
| // grcols is a set of group by columns. |
| // if c is in grcols and c is an opd of an equality predicate then |
| // add the equality's other opd to grcols. |
| void ValueIdSet::introduceMissingVEGRefs(ValueIdSet & grcols) const |
| { |
| // iterate over the equality predicates |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_EQUAL) |
| { |
| BiRelat *eq = (BiRelat*)vid.getItemExpr(); |
| ValueId v0, v1; |
| if (eq->child(0)->getOperatorType() == ITM_VEG_REFERENCE) |
| v0 = eq->child(0)->getValueId(); |
| if (eq->child(1)->getOperatorType() == ITM_VEG_REFERENCE) |
| v1 = eq->child(1)->getValueId(); |
| // if left child is in grcols then add right child to grcols |
| if (grcols.containsTheGivenValue(v0) AND v1 != NULL_VALUE_ID) |
| { |
| grcols += v1; |
| } |
| // if right child is in grcols then add left child to grcols |
| else if (grcols.containsTheGivenValue(v1) AND v0 != NULL_VALUE_ID) |
| { |
| grcols += v0; |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // lookForVEGPredicates() |
| // Accumulate ValueIds of VEGPredicates, if any, in vs. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::lookForVEGPredicates(ValueIdSet & VEGPredSet) const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| VEGPredSet += vid; |
| } |
| } // ValueIdSet::lookForVEGPredicates |
| |
| // return TRUE iff this has no equality predicates at all |
| // put in joinCols the simple base columns being equijoined |
| // given "a-1=b and c=d+1", joinCols should get "b,c" |
| NABoolean ValueIdSet::hasNoEquiPredicates(ValueIdSet& joinCols) const |
| { |
| NABoolean result = TRUE; // until proven otherwise |
| if (entries() <= 0) |
| return result; // has no predicates at all |
| |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| ItemExpr *expr = vid.getItemExpr(); |
| switch (expr->getOperatorType()) |
| { |
| case ITM_VEG_PREDICATE: |
| // given equi-predicate "a=b", add "a,b" to joinCols |
| expr->findAll(ITM_BASECOLUMN, joinCols, TRUE, TRUE); |
| result = FALSE; // has at least one equality predicate |
| break; |
| case ITM_EQUAL: |
| // given equi-predicate "a=b+1", add "a" to joinCols |
| if (expr->child(0)->getOperatorType() == ITM_VEG_REFERENCE) |
| expr->child(0)->findAll(ITM_BASECOLUMN, joinCols, TRUE, TRUE); |
| // given equi-predicate "a-1=b", add "b" to joinCols |
| if (expr->child(1)->getOperatorType() == ITM_VEG_REFERENCE) |
| expr->child(1)->findAll(ITM_BASECOLUMN, joinCols, TRUE, TRUE); |
| result = FALSE; // has at least one equality predicate |
| break; |
| default: |
| continue; // keep looking |
| } |
| } |
| return result; |
| } // ValueIdSet::isAllEquiPredicates |
| |
| NABoolean ValueIdSet::containsAsEquiLocalPred(ValueId x) const |
| { |
| NABoolean result = FALSE; // until proven otherwise |
| if (entries() <= 0) |
| return result; // has no predicates at all |
| |
| ItemExpr* constExpr = NULL; |
| |
| NABoolean found = FALSE; |
| |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| ItemExpr *expr = vid.getItemExpr(); |
| switch (expr->getOperatorType()) |
| { |
| case ITM_VEG_PREDICATE: |
| { |
| ValueIdSet vegMembers = |
| ((VEGPredicate*)expr)->getVEG()->getAllValues(); |
| |
| return (vegMembers.contains(x) && |
| vegMembers.referencesAConstExpr(&constExpr)); |
| } |
| break; |
| |
| case ITM_EQUAL: |
| |
| // consider equi-predicate "a=1", or "1=a" |
| for (CollIndex i=0; i<2; i++ ) { |
| if ( expr->child(i)->getOperatorType() == ITM_VEG_REFERENCE ) |
| { |
| ItemExpr* childExpr = expr->child(i); |
| found = ((VEGReference*)(childExpr))->getVEG() |
| ->getAllValues().contains(x); |
| |
| |
| } else |
| found = (expr->child(i) == x); |
| |
| if (found) { |
| |
| CollIndex j = (i==0) ? 1 : 0; |
| ValueIdSet vset(expr->child(j)); |
| if ( vset.entries() == 1 && |
| vset.referencesAConstExpr(&constExpr) == TRUE ) |
| return TRUE; |
| |
| } |
| } |
| |
| break; |
| |
| default: |
| return FALSE; |
| } |
| } |
| return result; |
| |
| } // ValueIdSet::containsAsEquiPred() |
| |
| NABoolean ValueIdSet::containsAsRangeLocalPred(ValueId x) const |
| { |
| NABoolean result = FALSE; // until proven otherwise |
| if (entries() <= 0) |
| return result; // has no predicates at all |
| ItemExpr* constExpr = NULL; |
| NABoolean found = FALSE; |
| |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| ItemExpr *expr = vid.getItemExpr(); |
| switch (expr->getOperatorType()) |
| { |
| case ITM_LESS: |
| case ITM_LESS_EQ: |
| case ITM_LESS_OR_LE: |
| case ITM_GREATER: |
| case ITM_GREATER_EQ: |
| case ITM_GREATER_OR_GE: |
| |
| // consider range-predicate "a<1", "1>a", "a>1" or "1<xa" |
| for (CollIndex i=0; i<2; i++ ) { |
| if ( expr->child(i)->getOperatorType() == ITM_VEG_REFERENCE ) |
| { |
| ItemExpr* childExpr = expr->child(i); |
| found = ((VEGReference*)(childExpr))->getVEG() |
| ->getAllValues().contains(x); |
| } else |
| found = (expr->child(i) == x); |
| |
| if (found) { |
| CollIndex j = (i==0) ? 1 : 0; |
| ValueIdSet vset(expr->child(j)); |
| if ( vset.entries() == 1 && |
| vset.referencesAConstExpr(&constExpr) == TRUE ) |
| return TRUE; |
| } |
| } |
| break; |
| |
| default: |
| return FALSE; |
| } |
| } |
| return result; |
| |
| } // ValueIdSet::containsAsRangeLocalPred() |
| |
| |
| void ValueIdSet::insertList(const ValueIdList &other) |
| { |
| for (CollIndex i = 0; i < other.entries(); i++) |
| insert(other[i]); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // isCovered() |
| // Returns TRUE only if ALL the members of the set are covered. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::isCovered(const ValueIdSet & newExternalInputs, |
| const GroupAttributes & coveringGA, |
| ValueIdSet & referencedInputs, |
| ValueIdSet & coveredSubExpr, |
| ValueIdSet & unCoveredExpr) const |
| { |
| NABoolean coverFlag, exprIsCovered = TRUE; |
| |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| // Perform the coverage test for each ItemExpr. |
| // This set is covered if EVERY ItemExpr that belongs to it isCovered. |
| coverFlag = coveringGA.covers(exprId, |
| newExternalInputs, |
| referencedInputs, |
| &coveredSubExpr, |
| &unCoveredExpr); |
| if (coverFlag) |
| coveredSubExpr += exprId; |
| exprIsCovered &= coverFlag; |
| } // for |
| |
| return exprIsCovered; |
| |
| } // ValueIdSet::isCovered() |
| |
| // ------------------------------------------------------------------------ |
| // ValueIdSet::removeCoveredExprs() |
| // |
| // Removes from the valueid set those expressions that are |
| // covered by the available values |
| // ------------------------------------------------------------------------ |
| |
| Int32 ValueIdSet::removeCoveredExprs(const ValueIdSet & newExternalInputs, |
| ValueIdSet* usedInputs) |
| { |
| Int32 result = 0; |
| NABoolean coverFlag; |
| ValueIdSet referencedInputs; |
| GroupAttributes emptyGA; |
| |
| emptyGA.setCharacteristicOutputs(newExternalInputs); |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| VEG * vegPtr = ((VEGPredicate *)(exprId.getItemExpr()))->getVEG(); |
| coverFlag = emptyGA.covers(vegPtr->getVEGReference()->getValueId(), |
| newExternalInputs, |
| referencedInputs); |
| } |
| else |
| coverFlag = emptyGA.covers(exprId, |
| newExternalInputs, |
| referencedInputs); |
| if (coverFlag) { |
| subtractElement(exprId); |
| result++; |
| if (usedInputs) |
| *usedInputs += referencedInputs ; |
| } |
| } // for |
| |
| return result; |
| } // ValueIdSet::removeCoveredExprs |
| |
| // ------------------------------------------------------------------------ |
| // ValueIdSet::removeUnCoveredExprs() |
| // |
| // Removes from the valueid set those expressions that are |
| // NOT covered by the available values |
| // ------------------------------------------------------------------------ |
| |
| Int32 ValueIdSet::removeUnCoveredExprs(const ValueIdSet & newExternalInputs) |
| { |
| Int32 result = 0; |
| NABoolean coverFlag; |
| ValueIdSet referencedInputs; |
| GroupAttributes emptyGA; |
| |
| emptyGA.setCharacteristicOutputs(newExternalInputs); |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| if (exprId.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| VEG * vegPtr = ((VEGPredicate *)(exprId.getItemExpr()))->getVEG(); |
| coverFlag = emptyGA.covers(vegPtr->getVEGReference()->getValueId(), |
| newExternalInputs, |
| referencedInputs); |
| } |
| else |
| coverFlag = emptyGA.covers(exprId, |
| newExternalInputs, |
| referencedInputs); |
| if (!coverFlag) |
| { |
| subtractElement(exprId); |
| result++; |
| } |
| } // for |
| |
| return result; |
| } // ValueIdSet::removeUnCoveredExprs |
| |
| // --------------------------------------------------------------------- |
| // simplifyOrderExpr() |
| // |
| // Returns a version of the set that has all expressions involving |
| // columns and constants or columns and inverse nodes simplified to be |
| // the column only. |
| // --------------------------------------------------------------------- |
| ValueIdSet ValueIdSet::simplifyOrderExpr() const |
| { |
| ValueIdSet simpleVidSet; |
| for (ValueId vid = init(); |
| next(vid); |
| advance(vid)) |
| { |
| simpleVidSet += |
| vid.getItemExpr()->simplifyOrderExpr()->getValueId(); |
| } |
| |
| return simpleVidSet; |
| } |
| |
| // --------------------------------------------------------------------- |
| // removeInverseOrder() |
| // |
| // Returns a version of the set that has all expressions involving |
| // columns and inverse nodes simplified to be the column only. |
| // --------------------------------------------------------------------- |
| ValueIdSet ValueIdSet::removeInverseOrder() const |
| { |
| ValueIdSet noInverseVidSet; |
| for (ValueId vid = init(); |
| next(vid); |
| advance(vid)) |
| { |
| noInverseVidSet += |
| vid.getItemExpr()->removeInverseOrder()->getValueId(); |
| } |
| |
| return noInverseVidSet; |
| } |
| |
| // --------------------------------------------------------------------- |
| // findCommonSubexpressions() |
| // |
| // Find common subexpression between "this" and "other". Remove all |
| // value ids that are not common subexpressions from "this" (so that |
| // "this" contains the common subexpressions). Optionally, remove |
| // the common subexpressions from "other" (note that those don't need |
| // to have the same value ids, just be equivalent). |
| // --------------------------------------------------------------------- |
| void ValueIdSet::findCommonSubexpressions(ValueIdSet &other, |
| NABoolean removeCommonExprFromOther) |
| { |
| ValueIdList otherList; |
| CollIndex otherNumEntries; |
| LIST(HashValue) otherHashValues(STMTHEAP); |
| ValueId vid; |
| CollIndex j; |
| HashValue hashVal; |
| |
| otherList.insertSet(other); |
| otherNumEntries = otherList.entries(); |
| |
| // This is an n**2 (really n * m) algorithm, but for realistic cases |
| // it should be good enough. To avoid performance problems, exit |
| // this method for very large sets (suggested to use a hash |
| // table of the hash values if this ever becomes a problem) |
| if (entries() * otherNumEntries > 2000) |
| { |
| clear(); |
| return; |
| } |
| |
| // make hash values for the other set |
| for (j=0; j<otherNumEntries; j++) |
| { |
| otherHashValues.insert(otherList[j].getItemExpr()->treeHash()); |
| } |
| |
| // Go through my set and hash each member. If we find it, do a precise |
| // comparison using duplicateMatch. Otherwise, delete the member. |
| for (vid=init(); next(vid); advance(vid)) |
| { |
| NABoolean found = FALSE; |
| |
| hashVal = vid.getItemExpr()->treeHash(); |
| for (j=0; j<otherNumEntries; j++) |
| { |
| if (otherHashValues[j] == hashVal) |
| if (vid.getItemExpr()->duplicateMatch( |
| *(otherList[j]).getItemExpr())) |
| { |
| found = TRUE; |
| break; |
| } |
| } |
| |
| if (found) |
| { |
| // found a common subexpression |
| if (removeCommonExprFromOther) |
| other -= otherList[j]; |
| } |
| else |
| { |
| // didn't find a common subexpression, remove element from my set |
| *this -= vid; |
| } |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // Build predicate like the originals in the set, that substitues the |
| // given column reference with a computed column and the corresponding |
| // computed column expression. |
| // |
| // For example: |
| // predicate is : A = 2 |
| // computed Column is: SQR |
| // computed Column expr is: POWER(A, 2) |
| // Then this method will return a ValueIdSet that contains |
| // a predicate that looks like this: |
| // SQR = POWER(VEG(A,2),2) |
| // --------------------------------------------------------------------- |
| ValueIdSet ValueIdSet::createMirrorPreds(ValueId &computedCol, |
| ValueIdSet underlyingCols) |
| { |
| ValueIdSet newComputedPreds; |
| ItemExpr *iePtr = computedCol.getItemExpr(); |
| |
| CMPASSERT( iePtr->getOperatorType() == ITM_BASECOLUMN ); |
| |
| ItemExpr *compExpr = ((BaseColumn *) iePtr)->getComputedColumnExpr().getItemExpr(); |
| ItemExpr *prevPred = NULL; |
| |
| for (ValueId kpv = init(); next(kpv); advance(kpv)) |
| { |
| ItemExpr *piePtr = kpv.getItemExpr(); |
| switch (piePtr->getOperatorType()) |
| { |
| case ITM_VEG_PREDICATE: |
| case ITM_EQUAL: |
| case ITM_LESS: |
| case ITM_LESS_EQ: |
| case ITM_LESS_OR_LE: |
| case ITM_GREATER: |
| case ITM_GREATER_EQ: |
| case ITM_GREATER_OR_GE: |
| { |
| ItemExpr * newPred = piePtr->createMirrorPred(iePtr, compExpr, underlyingCols); |
| if (newPred != NULL) |
| { |
| // look for the following pattern, which is common |
| // when a range of values falls within one division |
| // col1 >= const1 AND col1 <= const1 |
| // and transform this into the single predicate col1 = const1 |
| if (prevPred) |
| { |
| OperatorTypeEnum prevOp = prevPred->getOperatorType(); |
| OperatorTypeEnum newOp = newPred->getOperatorType(); |
| NABoolean neg1, neg2; |
| ConstValue *prevConst = prevPred->child(1)->castToConstValue(neg1); |
| ConstValue *newConst = newPred->child(1)->castToConstValue(neg2); |
| |
| if ((prevOp == ITM_GREATER_EQ && newOp == ITM_LESS_EQ || |
| newOp == ITM_GREATER_EQ && prevOp == ITM_LESS_EQ) && |
| prevPred->child(0) == newPred->child(0) && |
| prevConst && newConst && |
| prevConst->duplicateMatch(*newConst) && neg1 == neg2 && |
| static_cast<BiRelat *>(prevPred)->getSpecialNulls() == |
| static_cast<BiRelat *>(newPred)->getSpecialNulls()) |
| { |
| // make the <= or >= predicate of the pattern into an = predicate |
| BiRelat *newEqualPred = new(CmpCommon::statementHeap()) |
| BiRelat(ITM_EQUAL, |
| newPred->child(0), |
| newPred->child(1)); |
| newEqualPred->setSpecialNulls( |
| static_cast<BiRelat *>(newPred)->getSpecialNulls()); |
| newEqualPred->synthTypeAndValueId(); |
| |
| newComputedPreds -= prevPred->getValueId(); |
| newPred = newEqualPred; |
| } |
| } |
| |
| newComputedPreds += newPred->getValueId(); |
| prevPred = newPred; |
| } |
| break; |
| } |
| case ITM_ASSIGN: |
| default: |
| // XXX Don't expect this case to ever happen |
| CMPASSERT(0); |
| break; |
| } |
| } |
| return newComputedPreds; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Method for rebuilding a predicate tree from a ValueIdSet. |
| // ----------------------------------------------------------------------- |
| ItemExpr * ValueIdSet::rebuildExprTree |
| (OperatorTypeEnum op, |
| NABoolean redriveTypeSynthesisFlag, |
| NABoolean createFinalizeResultNode) const |
| { |
| ValueIdList vidList; |
| vidList.insertSet(*this); |
| return vidList.rebuildExprTree(op, |
| redriveTypeSynthesisFlag, |
| createFinalizeResultNode); |
| |
| } // ValueIdSet::rebuildExprTree() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::replaceVEGExpressionsAndCopy() |
| // The wildcard expansion is limited to the VEGReferences that are |
| // found in this set. There is no attempt to traverse ItemExpr |
| // trees in order to replace VEGReferences that are not at the |
| // root. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::replaceVEGExpressionsAndCopy |
| (const ValueIdSet & setContainingWildCards) |
| { |
| clear(); |
| for (ValueId vid = setContainingWildCards.init(); |
| setContainingWildCards.next(vid); |
| setContainingWildCards.advance(vid)) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| // For an expression that is a VegRef, we invoke |
| // VEG::getAndExpandAllValues(). That routine traverses |
| // expression tree only for VegReferences at root. |
| // (case 10-001201-9972: changed the following to prevent an |
| // infinite loop) |
| |
| ValueIdSet recurse; |
| |
| ((VEGReference *)(vid.getItemExpr()))-> |
| getVEG()->getAndExpandAllValues(recurse); |
| |
| addSet(recurse); |
| } |
| else |
| addElement(vid); |
| } |
| } // ValueIdSet::replaceVEGExpressionsAndCopy() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::getUnReferencedVEGmembers() |
| // Called only from preCodeGen. Gets all the VEG members that |
| // have not been bound yet. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::getUnReferencedVEGmembers |
| (const ValueIdSet & setContainingWildCards) |
| { |
| clear(); |
| for (ValueId vid = setContainingWildCards.init(); |
| setContainingWildCards.next(vid); |
| setContainingWildCards.advance(vid)) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| VEGReference * vegRef = ((VEGReference *)(vid.getItemExpr())); |
| |
| ValueIdSet unReferencedValues; |
| unReferencedValues.replaceVEGExpressionsAndCopy(vegRef->getVEG()->getAllValues()); |
| unReferencedValues -= vegRef->getVEG()->getReferencedValues(); |
| |
| addSet(unReferencedValues); |
| } |
| else |
| addElement(vid); |
| } |
| } // ValueIdSet::getUnReferencedVEGmembers() |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::replaceVEGExpressions() |
| // |
| // This method is used by the code generator for rewriting |
| // expressions that belong to a ValueIdSet. A reference to |
| // a VEG, i.e., a ValueId Equality Group is replaced with |
| // an expression that belongs the VEG as well as to the |
| // set of availableValues that is supplied. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::replaceVEGExpressions |
| (const ValueIdSet & availableValues, |
| const ValueIdSet & inputValues, |
| NABoolean thisIsAKeyPredicate, |
| VEGRewritePairs * lookup, |
| NABoolean replicateExpression, |
| const ValueIdSet * outputExpr, |
| const ValueIdSet * joinInputAndPotentialOutput, |
| const IndexDesc * iDesc, |
| const GroupAttributes * left_ga, |
| const GroupAttributes * right_ga) |
| { |
| ValueIdSet newExpr; |
| ItemExpr * iePtr; |
| NABoolean savedReplicateExpression = replicateExpression ; |
| // --------------------------------------------------------------------- |
| // Iterate over the predicate factors in the given predicate tree. |
| // --------------------------------------------------------------------- |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| if (outputExpr && outputExpr->containsTheGivenValue(exprId)) |
| replicateExpression = FALSE; |
| // ----------------------------------------------------------------- |
| // Walk through the item expression tree and replace any |
| // VEGPredicates or VEGReferences that are found. |
| // ----------------------------------------------------------------- |
| // Modifying for core/TEST029 since copyTopNode() not available for VEG_Reference |
| if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON && |
| exprId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC) |
| { |
| iePtr = exprId.getItemExpr()->child(1)->replaceVEGExpressions(availableValues, |
| inputValues, |
| thisIsAKeyPredicate, |
| lookup, |
| replicateExpression, |
| joinInputAndPotentialOutput, |
| iDesc, |
| left_ga, |
| right_ga); |
| } |
| else |
| { |
| iePtr = exprId.getItemExpr()->replaceVEGExpressions(availableValues, |
| inputValues, |
| thisIsAKeyPredicate, |
| lookup, |
| replicateExpression, |
| joinInputAndPotentialOutput, |
| iDesc, |
| left_ga, |
| right_ga); |
| } |
| |
| replicateExpression = savedReplicateExpression; |
| |
| if (iePtr) // expression was not discarded |
| { |
| if(!iePtr->nodeIsBound() || iePtr->isPreCodeGenNATypeChanged()) |
| // redrive type synthesis for both self and child nodes |
| iePtr->synthTypeAndValueId(TRUE, TRUE); |
| if (iePtr != exprId.getItemExpr()) // a replacement was done |
| { |
| subtractElement(exprId); // remove existing ValueId |
| //insert new expression(s) |
| if (iePtr->getOperatorType() == ITM_AND) |
| //The replacement of a RangeSpec could be an AND, convert ANDed predicates into additional values in newExpr. |
| iePtr->convertToValueIdSet(newExpr, NULL, ITM_AND, FALSE, FALSE); |
| else |
| newExpr += iePtr->getValueId(); // replace with a new one |
| } |
| } |
| else // delete the ValueId of the VEGPredicate/VEGReference from the set |
| subtractElement(exprId); |
| } // loop over predTree |
| |
| addSet(newExpr); // add the replacement expressions |
| |
| } // ValueIdSet::replaceVEGExpressions() |
| |
| // -------------------------------------------------------------------- |
| // ValueIdSet::getVEGesWithMultipleConsts() |
| // This method accumulates those VEGes that have more than one constant |
| // references in them |
| // -------------------------------------------------------------------- |
| |
| void ValueIdSet::getVEGesWithMultipleConsts(ValueIdSet & keyPredsToBeEvaluated) |
| { |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| ItemExpr * vegItemExpr = exprId.getItemExpr(); |
| if ((vegItemExpr->getOperatorType() == ITM_VEG_PREDICATE) |
| || (vegItemExpr->getOperatorType() == ITM_VEG_REFERENCE)) |
| { |
| const VEG * predVEG = ((VEGPredicate *)vegItemExpr)->getVEG(); |
| // Now, get all members of the VEG group |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| if (VEGGroup.referencesConstExprCount() > 1) |
| keyPredsToBeEvaluated += exprId; |
| } |
| |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // ValueIdSet::replaceOperandsOfInstantiateNull() |
| // This method is used by the code generator for replacing the |
| // operands of an ITM_INSTANTIATE_NULL with a value that belongs |
| // to availableValues. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::replaceOperandsOfInstantiateNull |
| (const ValueIdSet & availableValues, |
| const ValueIdSet & inputValues) |
| { |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) |
| { |
| exprId.getItemExpr()->replaceOperandsOfInstantiateNull |
| (availableValues,inputValues); |
| } |
| } // ValueIdSet::replaceOperandsOfInstantiateNull() |
| |
| // ----------------------------------------------------------------------- |
| // weedOutUnreferenced(ValueIdSet & other) |
| // For each ValueId in other check whether it is referenced anywhere |
| // within an ItemExpr whose ValueId belongs to this set. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::weedOutUnreferenced(ValueIdSet & other) const |
| { |
| ValueIdSet unrefSet; |
| NABoolean notFound; |
| |
| // loop over other |
| for ( ValueId otherId = other.init(); |
| other.next(otherId); |
| other.advance(otherId) ) |
| { |
| notFound = TRUE; |
| |
| const ValueId & otherId2 = |
| otherId.getItemExpr()->getReplacementExpr()->getValueId(); |
| |
| // loop over this |
| for ( ValueId exprId = init(); next(exprId); advance(exprId) ) |
| { |
| if ( exprId.getItemExpr()->referencesTheGivenValue(otherId2) ) |
| { |
| notFound = FALSE; |
| break; |
| } |
| } // iterator on this set |
| if ( notFound ) // accumulate the ValueIds that are |
| unrefSet += otherId; // not referenced |
| } // iterator on other |
| |
| // Delete unreferenced values from other. |
| other -= unrefSet; |
| |
| } // ValueIdSet::weedOutUnreferenced() |
| |
| void ValueIdSet::weedOutNonEquiPreds() |
| { |
| for (ValueId v = init(); next(v); advance(v)) |
| { |
| if (v.getItemExpr()->getOperatorType() != ITM_VEG_PREDICATE && |
| v.getItemExpr()->getOperatorType() != ITM_EQUAL) |
| *this -= v; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // referencesAConstValue |
| // |
| // Does this set references a constant either directly (or indirectly |
| // thru a VEGReference)? |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::referencesAConstValue(ItemExpr ** constant) const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| if (pred->getOperatorType() == ITM_CONSTANT) |
| { |
| *constant = id.getItemExpr(); |
| return TRUE; |
| } |
| else if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const VEG * predVEG = ((VEGPredicate *)pred)->getVEG(); |
| |
| if (predVEG->seenBefore()) |
| return FALSE; |
| |
| predVEG->markAsSeenBefore(); |
| |
| // Now, get all members of the VEG group |
| ItemExpr *constant2; |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| if (VEGGroup.referencesAConstValue(&constant2)) |
| { |
| *constant = constant2; |
| predVEG->markAsNotSeenBefore(); |
| return TRUE; |
| } |
| predVEG->markAsNotSeenBefore(); |
| } |
| } |
| return FALSE; |
| } // ValueIdSet::referencesAConstValue() |
| |
| // ----------------------------------------------------------------------- |
| // referenceAConstExpr |
| // |
| // This method is invoked to determine whether any element of the |
| // valueidset contains a "constant expression", i.e. a constant, |
| // host variable or a parameter. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::referencesAConstExpr(ItemExpr** constExprPtr) const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| |
| // |
| //check if the item expr is a non-strict constant |
| //a strict constant is somethine like cos(1) |
| //where as cos(?p) can be considered a constant |
| //in the non-strict definition since it remains |
| //constant for a given execution of a query |
| if (pred->doesExprEvaluateToConstant(FALSE,TRUE)) |
| { |
| if ( constExprPtr ) |
| *constExprPtr = id.getItemExpr(); |
| |
| return TRUE; |
| } |
| else if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const VEG * predVEG = ((VEGPredicate *)pred)->getVEG(); |
| |
| // To avoid infinite loop for cases where a VEG references itself. |
| // get to each element of the VEG, only if it has not been seen |
| // before |
| if (predVEG->seenBefore() ) |
| return FALSE; |
| |
| predVEG->markAsSeenBefore(); |
| |
| // Now, get all members of the VEG group |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| if (VEGGroup.referencesAConstExpr(constExprPtr)) |
| { |
| predVEG->markAsNotSeenBefore(); |
| return TRUE; |
| } |
| predVEG->markAsNotSeenBefore(); |
| } |
| } |
| return FALSE; |
| } // referencesAConstExpr() |
| |
| NABoolean ValueIdSet::doAllExprsEvaluateToConstant(NABoolean isStrict, |
| NABoolean considerVEG) const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| if (!pred->doesExprEvaluateToConstant(isStrict,considerVEG)) |
| return FALSE; |
| } |
| return TRUE ; |
| } // doAllExprsEvaluateToConstant() |
| |
| void ValueIdSet::removeConstExprReferences(NABoolean considerExpressions) |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *expr = id.getItemExpr(); |
| |
| // we want to consider hostvars and parameters are consts |
| NABoolean strict = FALSE; |
| |
| // we want to dig inside VEGREFS to see if there is an |
| // equality with a constant |
| NABoolean considerVEG = TRUE; |
| |
| if (expr->doesExprEvaluateToConstant(strict,considerVEG)) |
| { |
| subtractElement(id); |
| } |
| else if (expr->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const VEG * exprVEG = ((VEGReference *)expr)->getVEG(); |
| |
| // Now, get all members of the VEG group, only if this VEG has not |
| // been seen before. This is to avoid infinite looping for cases |
| // where a VEG references itself |
| if (!exprVEG->seenBefore()) |
| { |
| exprVEG->markAsSeenBefore(); |
| const ValueIdSet & VEGGroup = exprVEG->getAllValues(); |
| if (VEGGroup.referencesAConstExpr()) |
| { |
| subtractElement(id); |
| } |
| exprVEG->markAsNotSeenBefore(); |
| } |
| } |
| } |
| } // removeConstExprReferences() |
| |
| // ----------------------------------------------------------------------- |
| // referenceConstExprCount |
| // |
| // This method is invoked to determine how many elements of the |
| // valueidset contains a "constant expression", i.e. a constant, |
| // host variable or a parameter. |
| // ----------------------------------------------------------------------- |
| Int32 ValueIdSet::referencesConstExprCount() const |
| { |
| Int32 count = 0; |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| |
| // |
| //check if the item expr is a non-strict constant |
| //a strict constant is somethine like cos(1) |
| //where as cos(?p) can be considered a constant |
| //in the non-strict definition since it remains |
| //constant for a given execution of a query |
| if (pred->doesExprEvaluateToConstant(FALSE,TRUE)) |
| { |
| count = count + 1; |
| } |
| else if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const VEG * predVEG = ((VEGPredicate *)pred)->getVEG(); |
| // Now, get all members of the VEG group, only if this VEG has not |
| // been seen before. This is to avoid infinite looping for cases |
| // where a VEG references itself |
| if (!predVEG->seenBefore()) |
| { |
| predVEG->markAsSeenBefore(); |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| count = count + VEGGroup.referencesConstExprCount(); |
| predVEG->markAsNotSeenBefore(); |
| } |
| } |
| } |
| return count; |
| } // referencesConstExprCount() |
| |
| |
| // ----------------------------------------------------------------------- |
| // referenceAHostvariableorParam |
| // |
| // Checks if the valueidset is a veg predicate and if it contains a |
| // hostvariable, or param. |
| // referencesAConstValue() returns TRUE if the VEGGroup contains a constant |
| // referencesAConstExpr() returns TRUE if the VEGGroup contains a constant |
| // or a hostvar/dynamic parameter |
| // Thus, if referencesAConstExpr returns TRUE, but referencesAConstValue |
| // returns FALSE, then there's a hostvar, not a constant. |
| // ----------------------------------------------------------------------- |
| // The patch is added to take care of the case when predicate is an equality |
| // predicate with column or vegref on the left side and CAST of dynamic |
| // parameter on the right side (TPCC Q7) |
| |
| NABoolean ValueIdSet::referencesAHostvariableorParam() const |
| { |
| for( ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr * pred = id.getItemExpr(); |
| |
| if ( pred->getOperatorType() == ITM_VEG_PREDICATE ) |
| { |
| VEG * predVEG = ((VEGPredicate *)pred)->getVEG(); |
| |
| // Now, get all members of the VEG group, only if this VEG has not |
| // been seen before. This is to avoid infinite looping for cases |
| // where a VEG references itself |
| if (predVEG->seenBefore()) |
| return FALSE; |
| |
| predVEG->markAsSeenBefore(); |
| |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| |
| // See if the VEG group contains a constant. |
| // NOTE: A VEG group should not contain more than 1 constant; but if |
| // that should happen, referencesAConstValue will return the first |
| // constant found. |
| |
| ItemExpr *constant = NULL; |
| NABoolean containsConstant = VEGGroup.referencesAConstValue( &constant ); |
| NABoolean containsConstExpr = VEGGroup.referencesAConstExpr(); |
| NABoolean containsHostvar = ( containsConstExpr && !containsConstant ); |
| |
| predVEG->markAsNotSeenBefore(); |
| if(containsHostvar) |
| return TRUE; |
| } |
| |
| if ( pred->getOperatorType() == ITM_EQUAL ) |
| { |
| const ItemExpr *leftExpr = pred->child(0); |
| const ItemExpr *rightExpr = pred->child(1); |
| |
| const ValueId &leftChildVid = leftExpr->getValueId(); |
| const ValueId &rightChildVid = rightExpr->getValueId(); |
| |
| //check if the item expr is a non-strict constant |
| //a strict constant is somethine like cos(1), CAST(1) |
| //where as cos(?p), CAST(?p) can be considered a constant |
| //in the non-strict definition since it remains |
| //constant for a given execution of a query |
| if ( leftExpr->doesExprEvaluateToConstant(FALSE) OR |
| rightExpr->doesExprEvaluateToConstant(FALSE) ) |
| { |
| return TRUE; |
| } |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| ValueIdSet |
| ValueIdSet::replaceRangeSpecRefs() const |
| { |
| // If not using rangespec, just return the original set. |
| if (CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) != DF_ON) |
| return *this; |
| |
| ValueIdSet derangedPreds; |
| ValueIdSet vs; |
| ItemExpr* pred; |
| |
| // For each pred, it is a RangespecRef, add its expansion to the new set. |
| // Otherwise, just add the pred itself to the new set. |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| pred = vid.getItemExpr(); |
| if (pred->getOperatorType() == ITM_RANGE_SPEC_FUNC) |
| { |
| vs.clear(); |
| (static_cast<RangeSpecRef*>(pred))->getValueIdSetForReconsItemExpr(vs); |
| derangedPreds += vs; |
| } |
| else |
| // Add the predicate to the new set. |
| derangedPreds += vid; |
| } |
| |
| return derangedPreds; |
| } |
| |
| ValueIdSet |
| ValueIdSet::removePredsWithHostVars() const |
| { |
| ValueIdSet localPreds; |
| localPreds.clear(); |
| |
| // check if any predicate in this set is a host variable |
| for( ValueId id = init(); next(id); advance(id)) |
| { |
| if (id.getItemExpr()->referencesAHostVar()) |
| { |
| // continue to evaluate next pred |
| continue; |
| } |
| // else add the predicate to the new set |
| localPreds.insert(id); |
| } |
| return localPreds; |
| } |
| |
| // Return TRUE if any itemExpr is of Bignum type. |
| NABoolean ValueIdSet::referencesBignumNumericDataType() const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| |
| |
| if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const VEG * predVEG = ((VEGReference *)pred)->getVEG(); |
| // Now, get all members of the VEG group, only if this VEG has not |
| // been seen before. This is to avoid infinite looping for cases |
| // where a VEG references itself |
| if (!predVEG->seenBefore()) |
| { |
| predVEG->markAsSeenBefore(); |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| NABoolean result = VEGGroup.referencesBignumNumericDataType(); |
| predVEG->markAsNotSeenBefore(); |
| if ( result == TRUE ) return TRUE; |
| } |
| } |
| else if (pred->getOperatorType() == ITM_VEG_PREDICATE) |
| { |
| const VEG * predVEG = ((VEGPredicate *)pred)->getVEG(); |
| // Now, get all members of the VEG group, only if this VEG has not |
| // been seen before. This is to avoid infinite looping for cases |
| // where a VEG references itself |
| if (!predVEG->seenBefore()) |
| { |
| predVEG->markAsSeenBefore(); |
| const ValueIdSet & VEGGroup = predVEG->getAllValues(); |
| NABoolean result = VEGGroup.referencesBignumNumericDataType(); |
| predVEG->markAsNotSeenBefore(); |
| if ( result == TRUE ) return TRUE; |
| } |
| } |
| else |
| { |
| const NAType * type = &(pred->getValueId().getType()); |
| |
| if ( (type->getTypeQualifier() == NA_NUMERIC_TYPE && |
| ((const NumericType *)type)->isBigNum()) ) |
| { |
| return TRUE; |
| } |
| |
| } |
| } |
| return FALSE; |
| } |
| // --------------------------------------------------------------------- |
| // Remove any predicate with rolling columns from the |
| // set of predicates |
| // --------------------------------------------------------------------- |
| ValueIdSet |
| ValueIdSet::removePredsWithRollingCols(TableDesc *tdesc) const |
| { |
| ValueIdSet localPreds; |
| localPreds.clear(); |
| const TableAnalysis * ta = tdesc->getTableAnalysis(); |
| |
| // it is not a table, so don't bother to call fetchCount |
| if (!ta) |
| return localPreds; |
| |
| // get all user columns of this table |
| ValueIdSet usedCols = ta->getUsedCols(); |
| |
| // traverse thru all predicates |
| for( ValueId id = init(); next(id); advance(id)) |
| { |
| // if the predicate is an equality or a range predicate |
| // check to see if it is on a date column and if the constant |
| // it is being compared to is greater than the value in the |
| // histogram |
| ItemExpr * pred = id.getItemExpr(); |
| OperatorTypeEnum op = pred->getOperatorType(); |
| |
| // if it is not an equality or range pred, insert for evaluation by fetchCount |
| if (!((op == ITM_VEG_PREDICATE ) || |
| (op == ITM_EQUAL) || |
| (op == ITM_NOT_EQUAL) || |
| (op == ITM_LESS) || |
| (op == ITM_LESS_EQ) || |
| (op == ITM_GREATER) || |
| (op == ITM_GREATER_EQ) || |
| (op == ITM_OR) || |
| (op == ITM_AND))) |
| { |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // if the type of the predicate is month(col), dayofmonth(col) etc |
| // then use fetchCount |
| if ( (op != ITM_VEG_PREDICATE) && |
| ((pred->child(0)->getOperatorType() == ITM_EXTRACT_ODBC) || |
| (pred->child(0)->getOperatorType() == ITM_EXTRACT)) ) |
| { |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // get the column from the predicate |
| ValueIdSet predCols; |
| pred->findAll(ITM_BASECOLUMN, predCols, TRUE, TRUE); |
| |
| // from this column set extract columns of this table |
| predCols = predCols.intersect(usedCols); |
| if (predCols.entries() != 1) |
| { |
| // if there are more than one column from this table in the |
| // predicate, use the predicate for fetchCount |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // there is only one column from this table in the predicate |
| // check if it is date type |
| ValueId colId; |
| predCols.getFirst(colId); |
| if (colId == NULL_VALUE_ID) |
| { |
| localPreds.insert(id); |
| continue; |
| } |
| |
| if (colId.getType().getTypeQualifier() != NA_DATETIME_TYPE ) |
| { |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // it is date type, get the constant value from the predicate |
| ValueIdSet leafValues; |
| pred->findAll(ITM_CONSTANT, leafValues, TRUE, TRUE); |
| |
| if(leafValues.entries() == 0) |
| { |
| // there is no constant in the predicate, insert the predicate for CTS |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // get the histogram for the column. If the histogram is fake |
| // insert the predicate for evaluation |
| ColStatsSharedPtr colStats = tdesc->getTableColStats().getColStatsPtrForColumn(colId); |
| |
| if(colStats == NULL) |
| { |
| // No histograms fetched for the predicate column, it could be a DDL query, |
| // continue. |
| continue; |
| } |
| |
| if (colStats->isFakeHistogram()) |
| { |
| // histogram is fake, so no need to comapre the values. Use fetchCount |
| localPreds.insert(id); |
| continue; |
| } |
| |
| // for OR and AND, there could be more than one date values |
| // go thru each date value, and if even one of them lies outside |
| // histogram range, do not use fetchcount on that predicate |
| |
| NABoolean insertPred = TRUE; |
| for (ValueId cid = leafValues.init(); |
| leafValues.next(cid); |
| leafValues.advance(cid)) |
| { |
| // from all the leaf values extract the constant value |
| ItemExpr *constant = NULL; |
| ValueIdSet cidSet(cid); |
| cidSet.referencesAConstValue( &constant ); |
| |
| // compare the constant value with the min and the max values of the histogram |
| NABoolean neg = FALSE; |
| ConstValue * cv = NULL; |
| |
| if (constant) |
| cv = constant->castToConstValue(neg); |
| else |
| insertPred = FALSE; |
| |
| // get the encoded value of the constant if any |
| EncodedValue val (UNINIT_ENCODEDVALUE) ; |
| |
| if (cv != NULL) |
| { |
| // get the encoded format for the constant |
| val = EncodedValue (cv, neg); |
| |
| // valid constant, compare the value with the histogram range |
| // if within the range use fetchCount, else use the rolling |
| // column adjustment done by the histogram |
| if ((val < colStats->getMinValue()) || |
| (val > colStats->getMaxValue())) |
| { |
| insertPred = FALSE; |
| break; |
| } |
| } |
| } // for all leafValues; |
| if (insertPred) |
| { |
| localPreds.insert(id); |
| } |
| } |
| return localPreds; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // getColumnsForHistogram |
| // |
| // returns columns which are referred in the expressions containing constants, |
| // constant expressions or host variables |
| // -------------------------------------------------------------------------- |
| |
| ValueIdSet ValueIdSet::getColumnsForHistogram() const |
| { |
| ValueIdSet result; |
| result.clear(); |
| |
| // traverse through all ValueIds, looking for VEG preds |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| VEG * predVEG = NULL; |
| ItemExpr * expr = id.getItemExpr(); |
| |
| // Presently we are considering only columns in vegPredicates |
| // There could be cases where we have a Cast over a column. |
| // Example, Cast(col1) = Cast(col2). In this case, the predicate is |
| // not transformed into a VEG_PREDICATE, instead it remains as an |
| // equality predicate. If we find cases where histograms for these |
| // columns are not being picked up during synthEstLogProp. We should |
| // revisit this code to add condition for ITM_EQUAL |
| |
| if ( expr->getOperatorType() == ITM_VEG_PREDICATE ) |
| predVEG = ((VEGPredicate *)expr)->getVEG(); |
| else |
| continue ; // it is not a VEG predicate, so continue |
| |
| ValueIdSet columns; |
| expr->findAll( ITM_BASECOLUMN, columns, TRUE, TRUE ); |
| |
| // Now get all the base tables in this predicate |
| |
| // see if this column is being joined to a column of another table |
| SET(TableDesc *) * tablesJoined = columns.getAllTables(); |
| if (tablesJoined->entries() > 1) |
| { |
| ValueId vegREF = predVEG->getVEGReference()->getValueId(); |
| result.insert(vegREF); |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| |
| void ValueIdSet::getConstants(ValueIdSet & addConstantsToThisSet, NABoolean includeCacheParams) const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| if (pred->getOperatorType() == ITM_CONSTANT) |
| addConstantsToThisSet += id; |
| else |
| if (includeCacheParams == TRUE && pred->getOperatorType() == ITM_CACHE_PARAM ) |
| addConstantsToThisSet += id; |
| } |
| } |
| |
| void ValueIdSet::getConstantExprs(ValueIdSet & addConstantExprsToThisSet, |
| NABoolean isStrict) const |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| ItemExpr *ie = id.getItemExpr(); |
| |
| // |
| //check if the item expr is a non-strict constant |
| //a strict constant is somethine like cos(1) |
| //where as cos(?p) can be considered a constant |
| //in the non-strict definition since it remains |
| //constant for a given execution of a query |
| // if isStrict we look only for constants and not parameters and hostvars |
| // However we still look for constants inside a VEG |
| if (ie->doesExprEvaluateToConstant(isStrict,TRUE) OR |
| ((ie->previousHostVar() == TRUE) && (NOT isStrict))) |
| addConstantExprsToThisSet += id; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Get Outer References |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::getOuterReferences(ValueIdSet & OuterRefs) const |
| { |
| NABoolean doNotInsert ; |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| doNotInsert = FALSE ; |
| if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| |
| |
| { |
| ItemExpr *constant = NULL; |
| const ValueIdSet & VEGGroup =((VEGReference *)pred)->getVEG()->getAllValues(); |
| if (VEGGroup.referencesAConstExpr(&constant)) |
| doNotInsert = TRUE ; |
| } |
| else if (pred->isAUserSuppliedInput()) |
| doNotInsert = TRUE ; |
| |
| if (NOT doNotInsert) |
| { |
| OuterRefs.insert (id); |
| } |
| } |
| } // ValueIdSet::getOuterReferences() |
| |
| // ----------------------------------------------------------------------- |
| // categorizePredicates: |
| // Group the predicates in the following categories, to be applied |
| // in the following order: |
| // |
| // (1) BiLogic: AND, or OR |
| // (2) Local Predicates - those that do not contain outer references |
| // a) VEG equality predicates |
| // b) other non-VEG predicates supported by synthesis (for now, |
| // these include class BiRelat predicates, and some UnLogic, only) |
| // (3) Non-local predicates - those that do contain outer references |
| // a) VEG predicates |
| // b) other non-VEG class BiRelat and UnLogic predicates |
| // ----------------------------------------------------------------------- |
| |
| void ValueIdSet::categorizePredicates (const ValueIdSet & outerReferences, |
| ValueIdSet & EqLocalPreds, |
| ValueIdSet & OtherLocalPreds, |
| ValueIdSet & EqNonLocalPreds, |
| ValueIdSet & OtherNonLocalPreds, |
| ValueIdSet & BiLogicPreds, |
| ValueIdSet & DefaultPreds ) const |
| { |
| |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| |
| if (pred->synthSupportedOp() && // does the operator support synthesis?? |
| ((0 > pred->getSelectivityFactor()) || //++MV does this operator has an assigned selectivity factor |
| ((ItemExpr *)pred)->isSelectivitySetUsingHint())) // Unless selectivity is specified by user |
| { |
| OperatorTypeEnum op = pred->getOperatorType(); |
| if (op == ITM_VEG_PREDICATE) // Equality (transitive closure) predicate? |
| { |
| if (outerReferences.entries() > 0) |
| { |
| if (pred->referencesOneValueFrom(outerReferences)) |
| EqNonLocalPreds.insert (id); |
| else |
| EqLocalPreds.insert (id); |
| } |
| else |
| EqLocalPreds.insert (id); |
| } |
| else if ((op == ITM_LESS) || (op == ITM_LESS_EQ) || |
| (op == ITM_GREATER) || (op == ITM_GREATER_EQ) || |
| (op == ITM_EQUAL) || (op == ITM_NOT_EQUAL) || |
| (op == ITM_IS_NULL) || (op == ITM_IS_NOT_NULL) || |
| (op == ITM_IS_UNKNOWN) || (op == ITM_IS_NOT_UNKNOWN)) |
| { |
| if (outerReferences.entries () > 0) |
| { |
| if (pred->referencesOneValueFrom(outerReferences)) |
| OtherNonLocalPreds.insert (id); |
| else |
| OtherLocalPreds.insert (id); |
| } |
| else |
| OtherLocalPreds.insert (id); |
| } |
| else if ((op == ITM_OR) || (op == ITM_AND)) |
| { |
| BiLogicPreds.insert (id); |
| } |
| else |
| { |
| CCMPASSERT ("Unsupported predicate found"); |
| continue; |
| } |
| |
| } // endif (pred->synthSupportedOp()) |
| |
| else // operator not supported by synthesis |
| |
| DefaultPreds.insert (id); |
| |
| } // end for loop over setOfPredicates |
| |
| } // categorizePredicates |
| |
| // This method returns TRUE if there are any predicates for which the |
| // cardinality cannot be accurately estimated by the optimizer |
| // Conditions are: |
| // 1. Any predicate with a hos variable or a dynamic parameter |
| // 2. Any predicate for which the histograms cannot be directly used. These include |
| // Case expressions, LIKE predicates, or any other predicate |
| // 3. For more than one range predicate |
| // 4. For one range and one or more equality predicate |
| // 5. Any Bilogic pred |
| // 6. Equality predicaes |
| // a. Any equality on an expression |
| // b. Equality on more than one column of the same table. T1.a = T1.b |
| // c. More than one equality predicate, and multi col UEC does not exist for that set of columns |
| // Also the columns do not constitute unique index |
| |
| NABoolean |
| ValueIdSet::predsReqFetchCnt(TableDesc * tdesc) |
| { |
| // categorize all predicates so it is to determine if the |
| // predicates are complex or not |
| |
| // if there are no predicates, return FALSE |
| if (entries() == 0) |
| return FALSE; |
| |
| // check to see if any of the predicates contain a hostVar or dynamic |
| // parameter |
| |
| ItemExpr *constant = NULL; |
| NABoolean containsConstant = referencesAConstValue( &constant ); |
| NABoolean containsConstExpr = referencesAConstExpr(); |
| NABoolean containsHostvar = ( containsConstExpr && !containsConstant ); |
| |
| // if they do, return FALSE, we do not want to call fetchCount with |
| // host variables or dynamic parameters |
| if (containsHostvar) |
| return TRUE; |
| |
| ValueIdSet eqLocalPreds, otherLocalPreds, eqNonLocalPreds, |
| otherNonLocalPreds, biLogicPreds, defaultPreds; |
| |
| // We will not concern ourselves with outer refernces at this time |
| ValueIdSet outerRefs; |
| outerRefs.clear(); |
| |
| categorizePredicates (outerRefs, eqLocalPreds, |
| otherLocalPreds, eqNonLocalPreds, |
| otherNonLocalPreds, biLogicPreds, |
| defaultPreds); |
| |
| // Complex predicates are defined as: |
| // 1. Predicates for which the optimizer will use default selectivity |
| // 2. Bi-logic preds (AND / OR) |
| // 3. More than one range predicates |
| if ( ((otherLocalPreds.entries() > 0) && (eqLocalPreds.entries() > 0)) || |
| (otherLocalPreds.entries() > 1) || |
| (biLogicPreds.entries() > 0) || |
| (defaultPreds.entries() > 0) || |
| (eqNonLocalPreds.entries() > 0) || |
| (otherNonLocalPreds.entries() > 0)) |
| return TRUE; |
| |
| // for range predicates, use fetchcount: |
| // 1. If there is one range predicate but it is on an expression |
| // example a+2 > 10 |
| if (otherLocalPreds.entries() == 1) |
| { |
| ValueId pred; |
| otherLocalPreds.getFirst(pred); |
| // if predicate is an expression such as a + 2 > 10 |
| // use fetchCount |
| if (pred.anExpression()) |
| return TRUE; |
| |
| // if predicate involves more than one column from the same |
| // table such as a > b use fetchCount |
| ValueIdSet colsOfThisTableInPred; |
| |
| if (pred.moreThanOneColFromSameTabOrCharDate(tdesc, colsOfThisTableInPred)) |
| return TRUE; |
| |
| // for all other cases return FALSE |
| return FALSE; |
| } |
| |
| if (eqLocalPreds.entries() > 0) |
| { |
| // check if any predicate is an equality on more |
| // than one column, such as a = b where both a and b |
| // belong to the same table |
| // During this process also collect base columns from |
| // all predicate to check for correlation and multi-column |
| // stats later |
| ValueIdSet cols; |
| for(ValueId predId = eqLocalPreds.init();eqLocalPreds.next(predId);eqLocalPreds.advance(predId)) |
| { |
| ValueIdSet colsOfThisTableInPred; |
| // There are one or less columns from this table participating |
| // in the predicates, so no need to use fetchCount |
| if (predId.moreThanOneColFromSameTabOrCharDate(tdesc, colsOfThisTableInPred)) |
| return TRUE; |
| |
| cols.insert(colsOfThisTableInPred); |
| } |
| |
| // if the columns constitute unique index, no need to use fetchCount |
| if (cols.doColumnsConstituteUniqueIndex(tdesc)) |
| return FALSE; |
| |
| // There are more than one columns from this table, see if multi-column |
| // stats exists for this set of cols, if not use fetchCount |
| const MultiColumnUecList* uecList = tdesc->getTableColStats().getUecList(); |
| |
| if ( uecList ) { |
| CostScalar mcUec = uecList->lookup(cols); |
| if (mcUec <= 0) |
| return TRUE; |
| } |
| } |
| if (entries() > (ActiveSchemaDB()->getDefaults()).getAsDouble(COMP_INT_43)) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| NABoolean |
| ValueId::anExpression() |
| { |
| ItemExpr * expr = getItemExpr(); |
| if (!expr) |
| return FALSE; |
| |
| for (Int32 i=0; i<expr->getArity(); i++) |
| { |
| // predicate is an expression |
| if (expr->child(i)->getArity() >= 1) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| |
| NABoolean |
| ValueId::moreThanOneColFromSameTabOrCharDate(TableDesc * tdesc, |
| ValueIdSet & colsOfThisTable) |
| { |
| // if the predicate is like a > b such that both a and b |
| // belong to same table, then also we want to call fetchCount |
| // optimizer will use default selectivity of 33% |
| ValueIdSet predCol; |
| ValueIdSet predSet(*this); |
| |
| predSet.findAllReferencedBaseCols(predCol); |
| |
| // if there is only one column in the predicate |
| // check for only character columns; |
| NABoolean checkForCharCol = FALSE; |
| if (predCol.entries() <= 1) |
| checkForCharCol = TRUE; |
| |
| if (tdesc && tdesc->getTableAnalysis() ) |
| { |
| ValueIdSet usedCols = tdesc->getTableAnalysis()->getUsedCols(); |
| colsOfThisTable = predCol.intersect(usedCols); |
| if (colsOfThisTable.entries() > 1) |
| return TRUE; |
| if (checkForCharCol || (colsOfThisTable.entries() == 1)) |
| { |
| ValueId colId; |
| colsOfThisTable.getFirst(colId); |
| if (colId == NULL_VALUE_ID) |
| return FALSE; |
| |
| // get column type and return TRUE if date time type |
| if (colId.getType().getTypeQualifier() == NA_DATETIME_TYPE ) |
| return TRUE; |
| // if the intervals of the histograms had to be merged due |
| // to conflicting interval boundaries use |
| // fetchCount. |
| ColStatsSharedPtr colStats = tdesc->getTableColStats().getColStatsPtrForColumn(colId); |
| if (colStats == NULL) |
| return FALSE; |
| if (colStats->isColWithBndryConflict()) |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| NABoolean ValueId::isDivisioningColumn() const |
| { |
| ItemExpr *ck = getItemExpr(); |
| |
| if ( ck == NULL ) |
| return FALSE; |
| |
| if (ck->getOperatorType() == ITM_INDEXCOLUMN) |
| ck = ((IndexColumn *) ck)->getDefinition().getItemExpr(); |
| |
| if (ck->getOperatorType() == ITM_BASECOLUMN) |
| return (((BaseColumn *) ck)->getNAColumn()->isDivisioningColumn()); |
| else |
| return FALSE; |
| } |
| |
| NABoolean ValueId::isSaltColumn() const |
| { |
| ItemExpr *ck = getItemExpr(); |
| |
| if ( ck == NULL ) |
| return FALSE; |
| |
| if (ck->getOperatorType() == ITM_INDEXCOLUMN) |
| ck = ((IndexColumn *) ck)->getDefinition().getItemExpr(); |
| |
| if (ck->getOperatorType() == ITM_BASECOLUMN) |
| return (((BaseColumn *) ck)->getNAColumn()->isSaltColumn()); |
| else |
| return FALSE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // getReferencedPredicates: |
| // This method goes through the ValueIdSet pointed to by the this pointer. |
| // All those valueids that are referenced by any member |
| // of the valueIdSet that is the first argument to this method are inserted |
| // into the ValueIdSet that is the second argument. This method can be used |
| // get all the predicates in the selection predicate of a node that contain |
| // outer references. Method returns TRUE if any predicate is found that |
| // references a valueid from the first set. |
| // |
| // The default behavior of the routine is to skip any expression that |
| // evaluates to a constant(see ItemExpr::referencesOneValueFrom()). If |
| // you want those expressions to be searched as well, set searchConstExpr |
| // to TRUE. |
| // ----------------------------------------------------------------------- |
| |
| NABoolean ValueIdSet::getReferencedPredicates (const ValueIdSet & outerReferences, |
| ValueIdSet & nonLocalPreds) const |
| { |
| NABoolean found = FALSE; |
| if (outerReferences.entries() > 0) |
| { |
| for (ValueId id = init(); next(id); advance(id)) |
| { |
| const ItemExpr *pred = id.getItemExpr(); |
| if (pred->referencesOneValueFrom(outerReferences)) |
| { |
| nonLocalPreds.insert (id); |
| found = TRUE ; |
| } |
| } |
| } |
| return found; |
| } |
| |
| // --------------------------------------------------------------------- |
| // ValueIdSet::getAllTables() |
| // This method will get all tables whose columns are included in the set |
| // We assume there are all base columns. Hence use all base columns in this |
| // set. |
| // ---------------------------------------------------------------------- |
| |
| SET(TableDesc *) * ValueIdSet::getAllTables() |
| { |
| |
| SET(TableDesc *) * tableSet = new STMTHEAP SET(TableDesc *)(STMTHEAP); |
| |
| for (ValueId column = init(); next(column); advance(column) ) |
| { |
| const ItemExpr * colExpr = column.getItemExpr() ; |
| if (colExpr->getOperatorType() != ITM_BASECOLUMN) continue; |
| |
| TableDesc * tableDescForCol = ((BaseColumn *)colExpr)->getTableDesc(); |
| |
| tableSet->insert(tableDescForCol); |
| } |
| return tableSet; |
| } |
| |
| // --------------------------------------------------------------------- |
| // ValueIdSet::doColumnsConstituteUniqueIndex(const NATable * table) |
| // Returns a boolean to indicate if these columns constitute a primary |
| // key or unique index. |
| // --------------------------------------------------------------------- |
| NABoolean ValueIdSet::doColumnsConstituteUniqueIndex(TableDesc * table, NABoolean considerStats) |
| { |
| NABoolean colsConstUniqueIndex = FALSE; |
| |
| ValueIdSet primaryKeyColumns = table->getPrimaryKeyColumns(); |
| |
| if ((primaryKeyColumns.entries() > 0) && |
| (contains(primaryKeyColumns)) ) |
| colsConstUniqueIndex = TRUE; |
| else |
| { |
| // Complete primary key is not covered by the joining columns. See if |
| // if any unique index is covered |
| |
| const LIST(IndexDesc *) &uniqueIndexes = table->getUniqueIndexes(); |
| for (CollIndex i = 0; i < uniqueIndexes.entries(); i++) |
| { |
| IndexDesc * index = uniqueIndexes[i]; |
| ValueIdSet indexColumns(index->getIndexKey()); |
| } |
| } |
| |
| // if the column set is not unique based on semantics, check to see if it is unique based on |
| // its statistics. That is if any column in the set is unique, or has UEC equal to rowcount |
| // then we say that the column set is unique |
| // Do that only if it is being tested for cardinality estimation. |
| if ((colsConstUniqueIndex == FALSE) && considerStats) |
| { |
| // get the rowcount from the table colstats |
| ColStatDescList colStats = table->tableColStats(); |
| CostScalar rc = colStats[0]->getColStats()->getRowcount(); |
| for (ValueId vid = init(); next (vid); advance (vid)) |
| { |
| ColStatsSharedPtr cs = colStats.getColStatsPtrForColumn(vid); |
| if (cs) |
| { |
| if (cs->getUecBeforePreds() == rc) |
| { |
| colsConstUniqueIndex = TRUE; |
| break; |
| } |
| } |
| } |
| } |
| return colsConstUniqueIndex; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // containsSubquery() |
| // determine if any of the vids correspond to a subquery, in this vs. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsSubquery() const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->containsSubquery()) |
| return TRUE; |
| } |
| return FALSE; |
| } // ValueIdSet::containsSubquery |
| |
| // ----------------------------------------------------------------------- |
| // containsUDF() |
| // determine if any of the vids correspond to a UDF, in this vs. |
| // ----------------------------------------------------------------------- |
| ItemExpr *ValueIdSet::containsUDF() const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->containsUDF()) |
| return vid.getItemExpr()->containsUDF(); |
| } |
| return 0; |
| } // ValueIdSet::containsUDF |
| |
| // ----------------------------------------------------------------------- |
| // containsIsolatedUDFunction() |
| // determine if any of the vids correspond to an Isolated UDFunction,in this vs. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsIsolatedUDFunction() const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->containsIsolatedUDFunction()) |
| return TRUE; |
| } |
| return FALSE; |
| } // ValueIdSet::containsIsolatedUDFunction |
| |
| // ----------------------------------------------------------------------- |
| // containsCount() |
| // determine if any of the vids correspond to a count, in this vs. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsCount() const |
| { |
| for (ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if ((vid.getItemExpr()->origOpType() == ITM_COUNT) || |
| (vid.getItemExpr()->origOpType() == ITM_COUNT_STAR__ORIGINALLY)) |
| return TRUE; |
| } |
| return FALSE; |
| } // ValueIdSet::containsCount |
| |
| // ----------------------------------------------------------------------- |
| // containsOneTrue() |
| // determine if any of the vids correspond to a oneTrue, in this vs. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsOneTrue(ValueId &refOneTrue ) const |
| { |
| |
| for ( ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->isAnAggregate() && |
| (vid.getItemExpr()->getOperatorType() == ITM_ONE_TRUE)) |
| { |
| refOneTrue = vid; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } // ValueIdSet::containsOneTrue |
| |
| |
| // ----------------------------------------------------------------------- |
| // containsAnyTrue() |
| // determine if any of the vids correspond to a anyTrue, in this vs. |
| // ----------------------------------------------------------------------- |
| NABoolean ValueIdSet::containsAnyTrue(ValueId &refAnyTrue ) const |
| { |
| |
| for ( ValueId vid = init(); next(vid); advance(vid)) |
| { |
| if (vid.getItemExpr()->isAnAggregate() && |
| (vid.getItemExpr()->getOperatorType() == ITM_ANY_TRUE)) |
| { |
| refAnyTrue = vid; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } // ValueIdSet::containsAnyTrue |
| |
| NABoolean ValueIdSet::containsFalseConstant(ValueId &falseConstant ) const |
| { |
| |
| for ( ValueId vid = init(); next(vid); advance(vid)) |
| { |
| OperatorTypeEnum OpType = vid.getItemExpr()->getOperatorType(); |
| |
| if( OpType == ITM_RETURN_FALSE ) |
| { |
| falseConstant = vid; |
| return TRUE; |
| } |
| else if ( OpType == ITM_CONSTANT ) |
| { |
| NABoolean negate; |
| ConstValue *cv = vid.getItemExpr()->castToConstValue(negate); |
| if( cv && cv->isAFalseConstant()) |
| { |
| falseConstant = vid; |
| return TRUE; |
| } |
| } |
| } |
| return FALSE; |
| |
| } // ValueIdSet::containsFalseConstant |
| |
| // ----------------------------------------------------------------------- |
| // Methods used for debugging and display. |
| // ----------------------------------------------------------------------- |
| void ValueIdSet::unparse(NAString &result, |
| PhaseEnum phase, |
| UnparseFormatEnum form, |
| TableDesc * tabId) const |
| { |
| NABoolean first = TRUE; |
| // MVs -- |
| |
| NAString connectorText; |
| |
| if ((form == MVINFO_FORMAT) || (form == QUERY_FORMAT)) |
| connectorText = " AND "; |
| else |
| connectorText = " , "; |
| |
| result += "("; |
| for (ValueId x = init(); next(x); advance(x)) |
| { |
| if (first) |
| first = FALSE; |
| else |
| result += connectorText; |
| |
| if (form == ERROR_MSG_FORMAT) // ERROR_MSG_FORMAT : don't print vid's |
| { |
| x.getItemExpr()->unparse(result,phase,EXPLAIN_FORMAT,tabId); |
| } |
| else if ((form == MVINFO_FORMAT) || |
| (form == QUERY_FORMAT)) |
| { |
| // MVINFO_FORMAT or QUERY_FORMAT: don't print vid's |
| x.getItemExpr()->unparse(result,phase,form,tabId); |
| } |
| else |
| { |
| char vidbuf[TEXT_DISPLAY_LENGTH]; |
| sprintf(vidbuf, "[%u]", (CollIndex)x); |
| result += vidbuf; |
| |
| x.getItemExpr()->unparse(result,phase,form,tabId); |
| } |
| } |
| result += ")"; |
| } // ValueIdSet::unparse |
| |
| void ValueIdSet::print(FILE* ofd, const char* indent, const char* title, |
| CollHeap *c, char *buf) const |
| { |
| BUMP_INDENT(indent); |
| Space * space = (Space *)c; |
| char mybuf[1000]; |
| |
| snprintf(mybuf, sizeof(mybuf), "%s%s\n",NEW_INDENT,title); |
| PRINTIT(ofd, c, space, buf, mybuf); |
| |
| for (ValueId x = init(); next(x); advance(x)) |
| { |
| Int32 i = (Int32)((CollIndex) x); // valueid as an integer |
| NAString unparsed(CmpCommon::statementHeap()); |
| |
| if (x.getItemExpr()) |
| x.getItemExpr()->unparse(unparsed); // expression as ascii string |
| |
| snprintf(mybuf, sizeof(mybuf),"%4d: %s\n",i,(const char *) unparsed); |
| PRINTIT(ofd, c, space, buf, mybuf); |
| } |
| } // ValueIdSet::print() |
| |
| void ValueIdSet::display() const // To be called from the debugger |
| { |
| print(); |
| } |
| |
| ex_expr::exp_return_type ValueIdList::evalAtCompileTime |
| (short addConvNodes, // (IN) : 1 to add conv nodes, 0 otherwise |
| ExpTupleDesc::TupleDataFormat tf, // (IN) : tuple format of resulting expr(s) |
| char* resultBuffer, // (INOUT): tuple buffer of resulting expr(s) |
| ULng32 resultBufferLength, // (IN): length of the result buffer |
| Lng32 *length, // (OUT) : length of 1st result expr |
| Lng32 *offset, // (OUT) : offset of 1st result expr |
| ComDiagsArea *diagsArea |
| ) const |
| { |
| // set up binder/generator stuff so expressions could be generated. |
| BindWA bindWA(ActiveSchemaDB(), CmpCommon::context()); |
| Generator generator(CmpCommon::context()); |
| ExpGenerator expGen(&generator); |
| generator.appendAtEnd(); |
| |
| // Do not perform pcode optimizations for expressions being evaluated at |
| // compile-time. This is mainly being done to address a native expressions |
| // bug. However, there is no need to optimize in this case. |
| Int16 savedPCodeMode = (Int16) expGen.getPCodeMode(); |
| expGen.setPCodeMode(savedPCodeMode & ~(ex_expr::PCODE_LLO)); |
| |
| generator.setBindWA(&bindWA); |
| generator.setExpGenerator(&expGen); |
| FragmentDir * compFragDir = generator.getFragmentDir(); |
| |
| // create the fragment (independent code space) for this expression |
| CollIndex myFragmentId = compFragDir->pushFragment(FragmentDir::MASTER); |
| |
| // space where compile-time expr(s) will be generated |
| Space * space = generator.getSpace(); |
| |
| // allocate a work cri desc to encode expr(s). It has |
| // 3 entries: 0, for consts. 1, for temps. 2, for the encoded expr(s). |
| ex_cri_desc * workCriDesc = new(space) ex_cri_desc(3, space); |
| short keyAtpIndex = 2; // where the encoded expr(s) will be built |
| |
| ULng32 encodedKeyLen; |
| ex_expr * keExpr = 0; |
| ValueIdList tempValueIdList; |
| |
| // call ExpGenerator::generateContiguousMoveExpr to create an ex_expr |
| // to "assign" expr(s) into the resultBuffer |
| expGen.generateContiguousMoveExpr |
| (*this, addConvNodes, 0, keyAtpIndex, tf, encodedKeyLen, &keExpr, |
| NULL, ExpTupleDesc::SHORT_FORMAT, NULL, &tempValueIdList, |
| 0, NULL, TRUE /*disable const folding*/); |
| |
| if(encodedKeyLen > resultBufferLength) |
| { |
| generator.removeAll(); |
| return ex_expr::EXPR_ERROR; |
| } |
| |
| // create a DP2 expression and initialize it with the expr(s) |
| ExpDP2Expr * keyEncodeExpr = new(space) ExpDP2Expr(keExpr, |
| workCriDesc, |
| space); |
| keyEncodeExpr->getExpr()->fixup(0,0,0,space,STMTHEAP, FALSE, NULL); |
| |
| atp_struct * workAtp = keyEncodeExpr->getWorkAtp(); |
| workAtp->getTupp(keyAtpIndex).setDataPointer(resultBuffer); |
| |
| // set the diagsArea for the caller to get warnings |
| if (workAtp->getDiagsArea() != diagsArea) |
| workAtp->setDiagsArea(diagsArea); |
| // evaluate the expr(s) into resultBuffer |
| ex_expr::exp_return_type rc = |
| keyEncodeExpr->getExpr()->eval(workAtp, 0, STMTHEAP); |
| |
| // constant folding caller (ValueIdList::evaluateTree) wants to get the |
| // length, offset of 1st expr because it assumes it has only one element. |
| // other callers (CacheData::backpatchConstParams) are not interested in |
| // length, offset and these two parameters default to NULL addresses. |
| if (rc != ex_expr::EXPR_ERROR && (length || offset)) { |
| const ValueId & tempValueId = |
| tempValueIdList[0].getItemExpr()->getValueId(); |
| Attributes *attr = generator.getMapInfo(tempValueId)->getAttr(); |
| if (length) { |
| if (attr->getVCIndicatorLength() > 0) { |
| char temp[8]; |
| str_cpy_all(temp, resultBuffer + attr->getVCLenIndOffset(), |
| attr->getVCIndicatorLength()); |
| if (attr->getVCIndicatorLength() == sizeof(short)) |
| *length = *(short *)temp; |
| else |
| *length = *(Lng32 *)temp; |
| } |
| else { |
| *length = attr->getLength(); |
| } |
| } |
| if (offset) *offset = attr->getOffset(); |
| } |
| |
| // Restore pcode mode. |
| expGen.setPCodeMode(savedPCodeMode); |
| |
| generator.removeAll(); |
| return rc; |
| |
| } |
| |
| // Used by constant folding. Calls the executor evaluator. The parameters are: |
| // 1.- The root of the expression tree to evaluate. It is assumed that it |
| // contains constants only. |
| // 2.- encodedKeyBuffer, which is a pointer to the result. |
| // 3.- The length of the result |
| // 4.- The position in which the result starts |
| short ValueIdList::evaluateTree( const ItemExpr * root, |
| char * encodedKeyBuffer, |
| ULng32 encodedKeyLength, |
| Lng32 *length, |
| Lng32 *offset, |
| ComDiagsArea *diagsArea) |
| { |
| // Let's start with a list of size 4 rather than resizing continuously (?) |
| ValueIdList encodedValueIdList(4); |
| encodedValueIdList.insert(root->getValueId()); |
| |
| if (encodedValueIdList.evalAtCompileTime |
| (1, ExpTupleDesc::SQLMX_KEY_FORMAT, encodedKeyBuffer, encodedKeyLength, |
| length, offset, diagsArea) == ex_expr::EXPR_ERROR) |
| return -1; |
| else |
| return 0; |
| } |
| |
| // This function assumes that the tree rooted at ch contains constants only. |
| // Parent is the parent of ch and childNumber is the number of ch. The function |
| // computes the value represented by the subtree rooted at ch and puts |
| // the new value in the position of ch. Used by constant folding. |
| Lng32 ValueIdList::evaluateConstantTree( const ValueId &parent, |
| const ValueId & ch, |
| Int32 childNumber, |
| ItemExpr ** outItemExpr, |
| ComDiagsArea *diagsArea) |
| { |
| // eval requires CASE to be IF_THEN_ELSE's parent |
| if( (ch.getItemExpr()->getOperatorType() == ITM_IF_THEN_ELSE) && |
| (parent.getItemExpr()->getOperatorType() != ITM_CASE) ) |
| return 0; |
| |
| #define RESULT_SIZE 1000 |
| Lng32 length; |
| char value[RESULT_SIZE]; |
| |
| Lng32 offset; |
| |
| if (outItemExpr) |
| *outItemExpr = NULL; |
| |
| // Compute the value represented by the subtree rooted at ch |
| Int32 error = 0; |
| error = evaluateTree(ch.getItemExpr(), value, RESULT_SIZE, &length, |
| &offset, diagsArea); |
| if(error) |
| return error; |
| |
| const NAType & type = ch.getType(); |
| |
| NAString text(CmpCommon::statementHeap()); |
| ItemExpr *item; |
| |
| if ( type.getTypeQualifier() != NA_CHARACTER_TYPE ) { |
| |
| Int32 index = 0; |
| |
| value[length + offset] = 0; |
| |
| // See how many positions the result will take in the display |
| // long t = NAType::getDisplayLength(type.getFSDatatype(), |
| Lng32 t = type.getDisplayLength(type.getFSDatatype(), |
| length, |
| type.getPrecision(), |
| type.getScale(), |
| 0); |
| |
| |
| char *result = new char[t + 1]; |
| CMPASSERT( result != NULL ); |
| memset( result, ' ', t ); |
| |
| // Get the ASCII representation |
| ex_expr::exp_return_type retcode = |
| convDoIt(value + offset, |
| length, |
| (short)type.getFSDatatype(), |
| type.getPrecisionOrMaxNumChars(), |
| type.getScaleOrCharset(), |
| result, |
| t, // target length |
| REC_BYTE_F_ASCII, // target type |
| 0, // no char limit |
| SQLCHARSETCODE_ISO88591, // ISO 8859-1 |
| NULL, // no vc length |
| 0, // not a varchar |
| CmpCommon::statementHeap(), // heap |
| 0, // don't pass a diags area |
| CONV_UNKNOWN_LEFTPAD, // left pad with zeroes |
| NULL, // no conv error flag |
| CONV_ALLOW_INVALID_CODE_VALUE); // non ISO characters are lost, |
| // see Bugzilla 2938 |
| |
| // CMPASSERT(retcode == ex_expr::EXPR_OK); //Called by constant folding, no need to panic |
| if ( retcode != ex_expr::EXPR_OK ) |
| return (-1); |
| |
| result[t] = 0; |
| |
| // Create a new node containing the result |
| text = result; |
| |
| delete [] result; |
| |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral((NAType *) &type, value, offset + length, &text); |
| |
| } else { |
| |
| // for CHARACTER typed result, do not convert to ASCII format, because |
| // the result contained in 'value' is in the right format already. |
| // |
| // Besides, we will have inconsistent result if the conversion did |
| // happen for UCS2 'ab': |
| // SystemLiteral.text_ is 'ab' and |
| // type.Unicode=UCS2. |
| // |
| // The correct representation should be: |
| // stemLiteral.text_ is 'a\0b\0' and |
| // type.Unicode=UCS2. |
| |
| |
| const CharType& ctype = (CharType&)type; |
| |
| NAString strval(value+offset, length); |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(strval, ctype.getCharSet(),ctype.getCollation(), |
| ctype.getCoercibility()); |
| } |
| |
| item->synthTypeAndValueId(); |
| |
| // Replace the subtree just computed with the node containing the result |
| if (childNumber >= 0) |
| parent.getItemExpr()->setChild(childNumber,item); |
| |
| if (outItemExpr) |
| *outItemExpr = item; |
| |
| return error; |
| |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Tells us if some sort of algebraic simplification can be done in a tree. |
| // Used by constant folding. The parameters are: |
| // 1.- Points to the root of the tree in question |
| // 2.- Parent of node pointed to by parameter 1 |
| // 3.- The index of one of the childs of parameter 1. If it is a constant, |
| // then some sort of simplification may be done |
| // 4.- The index of the node represented by parameter 1 |
| // 5.- Indicates which of the children of the node pointed by parameter 1 |
| // will not occupy its place after the simplification is done |
| //////////////////////////////////////////////////////////////////////////// |
| NABoolean ValueIdList::canSimplify(ItemExpr *itemExpr, |
| const ValueId &parent, |
| Int32 i, |
| Int32 childNumber, |
| Int32 &moved) |
| { |
| char value[4]; |
| Int32 length; |
| ItemExpr *item; |
| NAString tempStr(CmpCommon::statementHeap()); |
| |
| if (itemExpr->child(i)->getOperatorType() != ITM_CONSTANT) { |
| return FALSE; |
| } |
| |
| ItemExpr *tempItem1 = itemExpr->child(i); |
| ConstValue *c = (ConstValue *) tempItem1; |
| |
| short type = c->getType()->getFSDatatype(); |
| if (!((type >= REC_MIN_NUMERIC && type <= REC_MAX_NUMERIC) || |
| tempItem1->getValueId().getType().getTypeQualifier() == NA_BOOLEAN_TYPE)) { |
| return FALSE; |
| } |
| |
| if ((type >= REC_MIN_NUMERIC && type <= REC_MAX_NUMERIC)&& |
| (tempItem1->getValueId().getType().getTypeQualifier() != NA_BOOLEAN_TYPE)) |
| { |
| // must be exact numeric with scale of zero and max |
| // precision is less than 10 (so it is a long and atoi can |
| // convert that value). |
| NumericType *ntyp = (NumericType *) c->getType(); |
| if ((! ntyp->isExact()) || |
| (ntyp->getScale() != 0) || |
| (ntyp->getPrecision() >= 10)) |
| return FALSE; |
| } |
| |
| NAString strValue(c->getText(), CmpCommon::statementHeap()); |
| Int32 val; |
| val = atoi(strValue); |
| |
| // We simplify the following cases: |
| // <expr> AND FALSE => FALSE; FALSE AND <expr> => FALSE |
| // <expr> AND TRUE => expr; TRUE AND <expr> => <expr> |
| // <expr> OR TRUE => TRUE; <expr> OR TRUE => TRUE |
| // <expr> OR FALSE => expr; FALSE OR <expr> => <expr> |
| // NOT FALSE => TRUE |
| // NOT TRUE => FALSE |
| // <expr> + 0 => <expr>; 0 + <expr> => <expr> |
| // <expr> * 0 => 0; 0 * <expr> => 0 |
| // <expr> * 1 => <expr>; 1 * <expr> => <expr> |
| // <expr> - 0 => <expr> |
| // <expr> / 1 => <expr> |
| |
| // If we have an OR such that one of its operands is TRUE, replace the operator |
| // with TRUE |
| if ((itemExpr->getOperatorType() == ITM_OR) && |
| (val == 1)) { |
| value[0] = 1; value[1] = 0; value[2] = 0; value[3] = 0; |
| length = 4; |
| tempStr = "1"; |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(itemExpr->synthesizeType(), value, length, &tempStr); |
| |
| item->synthTypeAndValueId(); |
| parent.getItemExpr()->setChild(childNumber,item); |
| |
| moved = i; |
| return TRUE; |
| } |
| |
| // We have an OR such that one of its operands is FALSE, then we may replace |
| // the OR with its other child |
| if ((itemExpr->getOperatorType() == ITM_OR) && |
| (val == 0)) { |
| |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(1 - i)); |
| moved = 1 - i; |
| return TRUE; |
| } |
| |
| // If we have an AND operator such that one of its operands is FALSE, replace |
| // the operator with FALSE |
| if ((itemExpr->getOperatorType() == ITM_AND) && |
| (val == 0)) { |
| |
| value[0] = 0; value[1] = 0; value[2] = 0; value[3] = 0; |
| length = 4; |
| tempStr = "0"; |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(itemExpr->synthesizeType(), value, length, &tempStr); |
| item->synthTypeAndValueId(); |
| parent.getItemExpr()->setChild(childNumber,item); |
| moved = i; |
| return TRUE; |
| } |
| |
| // We have an AND operator such that one of its operators is TRUE. We may replace |
| // the operator with its other child |
| if ((itemExpr->getOperatorType() == ITM_AND) && |
| (val == 1)) { |
| |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(1 - i)); |
| moved = 1 - i; |
| return TRUE; |
| } |
| |
| // We have a NOT operator with TRUE as the child. Replace the operator with |
| // a FALSE node |
| if ((itemExpr->getOperatorType() == ITM_NOT) && |
| (val == 1)) { |
| |
| value[0] = 0; value[1] = 0; value[2] = 0; value[3] = 0; |
| length = 4; |
| tempStr = "0"; |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(itemExpr->synthesizeType(), value, length, &tempStr); |
| item->synthTypeAndValueId(); |
| parent.getItemExpr()->setChild(childNumber,item); |
| moved = i; |
| |
| return TRUE; |
| } |
| |
| // We have a NOT operator with FALSE as the child. Replace the operator with |
| // a TRUE node |
| if ((itemExpr->getOperatorType() == ITM_NOT) && |
| (val == 0)) { |
| |
| value[0] = 1; value[1] = 0; value[2] = 0; value[3] = 0; |
| length = 4; |
| tempStr = "1"; |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(itemExpr->synthesizeType(), value, length, &tempStr); |
| item->synthTypeAndValueId(); |
| parent.getItemExpr()->setChild(childNumber,item); |
| moved = i; |
| |
| return TRUE; |
| } |
| |
| // We have a + such that one of its operands is 0, then we may replace |
| // the + with its other child |
| if ((itemExpr->getOperatorType() == ITM_PLUS) && |
| (val == 0)) { |
| |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(1 - i)); |
| moved = 1 - i; |
| return TRUE; |
| } |
| |
| // We have a * operator with 0 as a child. Replace the operator with |
| // a 0 node |
| if ((itemExpr->getOperatorType() == ITM_TIMES) && |
| (val == 0)) { |
| |
| item = new (CmpCommon::statementHeap()) SystemLiteral(0); |
| item->synthTypeAndValueId(); |
| parent.getItemExpr()->setChild(childNumber,item); |
| moved = i; |
| |
| return TRUE; |
| } |
| |
| // We have an * operator such that one of its operators is 1. We may replace |
| // the operator with its other child |
| if ((itemExpr->getOperatorType() == ITM_TIMES) && |
| (val == 1)) { |
| |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(1 - i)); |
| moved = 1 - i; |
| return TRUE; |
| } |
| |
| // We have the case <expr> - 0 |
| if (itemExpr->getOperatorType() == ITM_MINUS) { |
| |
| if (i != 1) |
| return FALSE; |
| |
| if (val == 0) { |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(0)); |
| moved = 0; |
| return TRUE; |
| } |
| } |
| |
| // We have the case <expr> / 1 |
| if (itemExpr->getOperatorType() == ITM_DIVIDE) { |
| |
| if (i != 1) |
| return FALSE; |
| |
| if (val == 1) { |
| parent.getItemExpr()->setChild(childNumber,itemExpr->getChild(0)); |
| moved = 0; |
| return TRUE; |
| } |
| else { |
| // do not change division to multiplication, if this div is |
| // to downscale the result and rounding is to be done. Div to |
| // round off the value does special processing which cannot be |
| // replaced by a multiplication. |
| if ((((BiArith*)itemExpr)->getDivToDownscale()) && |
| (((BiArith*)itemExpr)->getRoundingMode() > 0)) |
| return FALSE; |
| |
| // see if we can replace "<expr> / val" by "<expr> * <newval>" |
| // where <newval> is equal to "1 / <val>". |
| Int64 temp = val; |
| Int64 numerator = 1; |
| Lng32 scale = 0; |
| while (temp > 0) |
| { |
| temp = temp / 10; |
| numerator = numerator * 10; |
| scale++; |
| } |
| if (numerator == 1) |
| return FALSE; |
| |
| Int64 div = numerator / val; |
| if ( div * val != numerator) |
| return FALSE; |
| |
| NumericType * ntyp = new (CmpCommon::statementHeap()) |
| SQLNumeric(CmpCommon::statementHeap(), 4, scale, scale, TRUE, FALSE); |
| Lng32 cval = (Lng32)div; |
| char cvalStr[20]; |
| cvalStr[0] = '.'; |
| str_itoa(cval, &cvalStr[1]); |
| NAString nas(cvalStr, CmpCommon::statementHeap()); |
| item = new (CmpCommon::statementHeap()) |
| SystemLiteral(ntyp, &cval, 4, &nas); |
| item->synthTypeAndValueId(); |
| |
| BiArith * ba = new (CmpCommon::statementHeap()) |
| BiArith(ITM_TIMES, itemExpr->getChild(0)->castToItemExpr(), item); |
| ba->synthTypeAndValueId(); |
| |
| parent.getItemExpr()->setChild(childNumber,ba); |
| |
| moved = 0; |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| // Evaluate all constant subexpressions of the tree whose root is ch; after |
| // this routine is called, all subtrees that contain constants only will |
| // be replaced by the corresponding equivalent constant. |
| // Parent is the parent of ch and childNumber is the number of ch. |
| // |
| // RETURN: |
| // TRUE, if 'ch' contains all constants |
| // FALSE, if 'ch' doesn't contain all constants |
| // -1, if error during const expr evaluation |
| // |
| // If evalAllConsts is TRUE and 'ch' contains an all const expr, then that |
| // expr is evaluated. If outAllConstsItemExpr is passed in, then on return |
| // it contains a ConstValue(SystemLiteral) node representing the result of |
| // the const expression. |
| Int32 ValueIdList::evaluateExpr( const ValueId & parent, |
| const ValueId & ch, |
| Int32 childNumber, |
| NABoolean simplifyExpr, |
| NABoolean evalAllConsts, |
| ItemExpr ** outAllConstsItemExpr, |
| ComDiagsArea *diagsArea) |
| { |
| ItemExpr *itemExpr; |
| |
| itemExpr = ch.getItemExpr(); |
| Int32 nc = itemExpr->getArity(); |
| Int32 op = itemExpr->getOperatorType(); |
| |
| if (outAllConstsItemExpr) |
| *outAllConstsItemExpr = itemExpr; |
| |
| // No aggregate or sequence functions since their arguments are |
| // represented as constants when they are not. Ditto for translations. |
| // Null-instantiated constants aren't constant either. Should |
| // really do a cover test here. Since it handles this issue now |
| // and in the future. |
| if (itemExpr->isAnAggregate() || |
| itemExpr->isASequenceFunction() || |
| (op == ITM_INSTANTIATE_NULL) || |
| (op == ITM_TRANSLATE) || |
| (op == ITM_ROWSETARRAY_INTO)) { |
| return FALSE; |
| } |
| |
| if (itemExpr->constFoldingDisabled()) |
| { |
| ItemExpr *outExpr = NULL; |
| |
| for (Int32 i = 0; i < nc; i++) |
| { |
| Int32 rc = |
| evaluateExpr(ch,itemExpr->child(i)->getValueId(),i, |
| simplifyExpr, evalAllConsts, &outExpr, |
| diagsArea); |
| if ((rc < 0) || |
| (diagsArea && diagsArea->getNumber() > 0)) |
| return -1; |
| else if ((outExpr) && |
| (evalAllConsts)) |
| { |
| itemExpr->child(i) = outExpr; |
| } |
| } |
| return FALSE; |
| } |
| |
| // If the tree has no subtrees, we just say if it is a constant or not. |
| if (nc == 0) { |
| if (itemExpr->getOperatorType() != ITM_CONSTANT) { |
| return FALSE; |
| } |
| else { |
| ConstValue *c = (ConstValue *) itemExpr; |
| if (c->isNull()) { // not dealing with NULLs |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| } |
| |
| // Assume by now that the tree rooted at ch contains constants only |
| Int32 allConstants = TRUE; |
| |
| const Int32 constantTreeArrayDim = 2; |
| Int32 constantTreeArray[constantTreeArrayDim]; |
| // You probably don't like constants, but |
| // no tree should have more than 2 children. |
| |
| Int32 *constantTree = 0; |
| |
| if ( nc > constantTreeArrayDim ) { |
| constantTree = new (CmpCommon::statementHeap()) Int32[nc]; |
| } else |
| constantTree = constantTreeArray; |
| |
| Lng32 i = 0; |
| for (; i < nc; i++) { |
| |
| // We call this routine recursively; therefore after this call the |
| // subtree rooted at child number i will not contain subtrees that |
| // contain constants only, since all of them would have been evaluated; |
| // We store in an array a boolean value that tells us if this subtree |
| // contains constants only, or not |
| constantTree[i] = evaluateExpr(ch,itemExpr->child(i)->getValueId(),i, |
| simplifyExpr, FALSE, NULL, diagsArea); |
| if (constantTree[i] < 0) { |
| Int32 x = constantTree[i]; |
| if ( nc > constantTreeArrayDim ) |
| NADELETEBASIC(constantTree, CmpCommon::statementHeap()); |
| return x; |
| } |
| |
| // The variable allConstants tells us if the tree rooted at ch consists |
| // of constants only so far |
| allConstants = (allConstants && constantTree[i]); |
| |
| } |
| |
| // If we only contain constants, we indicate so and we are done |
| if (allConstants) { |
| if ( nc > constantTreeArrayDim ) |
| NADELETEBASIC(constantTree, CmpCommon::statementHeap()); |
| |
| if (evalAllConsts) |
| { |
| ValueId dummy; |
| |
| Lng32 error = evaluateConstantTree( dummy, itemExpr->getValueId(), -1, |
| outAllConstsItemExpr, diagsArea ); |
| if (error) // -1 |
| return error; |
| } |
| |
| return TRUE; |
| } |
| |
| // Otherwise, we limit ourselves to evaluate those subtrees that contain |
| // constants only. We indicate that the tree rooted at ch does not only |
| // contain constants |
| for (i = 0; i < nc; i++) { |
| |
| if (constantTree[i]) { |
| |
| // Evaluate the tree since we know it only contains constants |
| if (itemExpr->child(i)->getArity() != 0) { |
| Int32 error = 0; |
| error = evaluateConstantTree(ch,itemExpr->child(i)->getValueId(),i,NULL, |
| diagsArea); |
| if (error) { |
| if ( nc > constantTreeArrayDim ) |
| NADELETEBASIC(constantTree, CmpCommon::statementHeap()); |
| return error; |
| } |
| } |
| |
| if (simplifyExpr) { |
| Int32 moved; |
| // Find out if some sort of simplification can be done; if |
| // so, we do it and let know the parent of this subtree |
| // whether the result contains all constants or not. |
| if (canSimplify(itemExpr, parent, i, childNumber, moved)) { |
| if ( nc > constantTreeArrayDim ) |
| NADELETEBASIC(constantTree, CmpCommon::statementHeap()); |
| return (moved == i); |
| } |
| } |
| } |
| } |
| |
| if ( nc > constantTreeArrayDim ) |
| NADELETEBASIC(constantTree, CmpCommon::statementHeap()); |
| |
| return FALSE; |
| |
| } |
| NABoolean ValueIdList::hasVarChars() const |
| { |
| ValueIdList list = *this; |
| |
| for (CollIndex i = 0; i < list.entries(); i++) |
| { |
| ValueId vid = list[i]; |
| |
| if (vid.getType().getVarLenHdrSize()>0) |
| { |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| // Simplify subexpressions that contain constants only; for |
| // example, if the valueIdList looks like x < 3 + 4 |
| // then the list returns as x < 7 and the function result will be FALSE. |
| // If we see "1=1 and 2=2", |
| // then the list returns as "1." and the function result will be TRUE. |
| // If error, the list returned will be empty! |
| NABoolean ValueIdList::constantFolding() |
| { |
| ItemExpr *tempItem1; |
| ConstValue *c; |
| Int32 constValue; |
| |
| ValueIdList list = *this; |
| clear(); |
| |
| // visit each subtree |
| ItemExpr *item = NULL; |
| |
| // Tells if the whole expression is TRUE |
| NABoolean allTrue = TRUE; |
| |
| for (CollIndex i = 0; i < list.entries(); i++) |
| { |
| const ValueId & vid = list[i]; |
| |
| // If this constant changes to 1, then we have a TRUE boolean constant as result; |
| // if it changes to 0, we have a FALSE value. Otherwise we have any other expression. |
| constValue = -1; |
| |
| // We create a temporary constant to serve as a dummy tree root |
| item = new (CmpCommon::statementHeap()) SystemLiteral(); |
| item->synthTypeAndValueId(); |
| item->setChild(0,vid.getItemExpr()); |
| |
| if ((vid != NULL_VALUE_ID) && (vid.getItemExpr()->getOperatorType() == ITM_OR)) |
| { |
| ValueIdList eqList; |
| NABoolean status = vid.getItemExpr()->convertToValueIdList(eqList,NULL, ITM_OR); |
| |
| if(!status && eqList.entries() > CmpCommon::getDefaultNumeric(MAX_EXPRS_USED_FOR_CONST_FOLDING)) |
| { |
| // if the number of branches in this OR predicate are more than max_exprs_used_for_constant_folding |
| // we will not try to simplify the expression. Add the original expression to the list and continue |
| // to evaluate next predicate |
| insert(vid); |
| // also since we have not done constant folding we don;t know if that expression evaluates to TRUE or not |
| // so to be on the safe side we will make allTrue = FALSE |
| allTrue = FALSE; |
| continue; |
| } |
| } |
| |
| |
| Int32 allConstants; |
| |
| // We evaluate the tree rooted at item. If it consists of constants only, |
| // evaluateExpr will let us know; otherwise all subtrees that contain only |
| // constants will be evaluated |
| const ValueId & tempVal = item->getValueId(); |
| allConstants = evaluateExpr( tempVal, vid, 0 ); |
| if (allConstants < 0) { |
| clear(); |
| return FALSE; |
| } |
| |
| // If we only have constants, do the evaluation |
| if (allConstants) { |
| |
| if (item->child(0)->getArity() > 0) { |
| Int32 error = 0; |
| const ValueId & tempVal1 = item->getValueId(); |
| error = evaluateConstantTree( tempVal1, vid, 0, NULL ); |
| if (error) { |
| clear(); |
| return FALSE; |
| } |
| } |
| |
| tempItem1 = item->child(0); |
| c = (ConstValue *) tempItem1; |
| |
| constValue = atoi(c->getText()); |
| |
| if (constValue == 0) { // the FALSE value |
| clear(); |
| insert(item->child(0)->getValueId()); |
| return FALSE; |
| } |
| |
| if (constValue != 1) { |
| allTrue = FALSE; |
| } |
| |
| } |
| else { |
| allTrue = FALSE; |
| } |
| |
| // Get the simplified tree inserted in resulting list; unless it is a TRUE |
| // node. |
| if (constValue != 1) { |
| insert(item->child(0)->getValueId()); |
| } |
| |
| } |
| |
| // If the whole expression evaluates to TRUE, return TRUE |
| if (allTrue) { |
| clear(); |
| insert(item->child(0)->getValueId()); |
| } |
| |
| return allTrue; |
| } |
| |
| // --------------------------------------------------------------------- |
| // findCommonElements() |
| // |
| // Find common elements between "this" and "other". Remove all |
| // value ids that are not common elements from "this" (so that |
| // "this" contains the common subexpressions). |
| // --------------------------------------------------------------------- |
| void ValueIdList::findCommonElements(const ValueIdSet &other) |
| { |
| NAList<CollIndex> listCopy(CmpCommon::statementHeap()); |
| listCopy.clear(); |
| CollIndex i; |
| |
| for (i = 0; i < entries() ; i++) |
| { |
| // copy the index of the element that is not contained in the "other" set |
| // this should be removed later |
| if (!other.contains((*this)[i]) ) |
| listCopy.insert(i); |
| } |
| // now go and remove all the elements indexed by i. Start from the back of the |
| // list. If all elements of this exist in other, then return. |
| |
| for (i = listCopy.entries(); i > 0 ;i--) |
| removeAt(listCopy[i-1]); |
| } |
| |
| // --------------------------------------------------------------------- |
| // findCommonElementsFromList() |
| // |
| // Find common elements between "this" and "other". |
| // --------------------------------------------------------------------- |
| ValueIdList ValueIdList::findCommonElementsFromList(const ValueIdList &other) |
| { |
| ValueIdList listCopy; |
| listCopy.clear(); |
| CollIndex i, j; |
| |
| // We use "other' as the main loop, as it is important to maintain the order |
| // of the other in "this" |
| for (i = 0; i < other.entries() ; i++) |
| { |
| ValueId currentOtherId = other[i]; |
| |
| // Find all occurences of "other" in "this" |
| |
| for (j = 0; j < entries(); j++) |
| { |
| // copy the index of the element that is not contained in the "other" set |
| // this should be removed later |
| if ( (*this)[j] == currentOtherId ) |
| listCopy.insert(currentOtherId); |
| } |
| } |
| |
| return listCopy; |
| } |
| |
| // *********************************************************************** |
| // Methods for class ValueIdMap |
| // *********************************************************************** |
| |
| ValueIdMap::ValueIdMap(const ValueIdSet &identity) |
| { |
| for (ValueId x = identity.init(); |
| identity.next(x); |
| identity.advance(x)) |
| { |
| topValues_.insert(x); |
| bottomValues_.insert(x); |
| } |
| } |
| |
| NABoolean ValueIdMap::operator == (const ValueIdMap &other) const |
| { |
| // shortcut: identical lists of value ids |
| if (topValues_ == other.topValues_ AND |
| bottomValues_ == other.bottomValues_) |
| return TRUE; |
| |
| return FALSE; // for now |
| } |
| |
| void ValueIdMap::remapTopValue(const ValueId &newTopValue, |
| const ValueId &bottomValue) |
| { |
| CollIndex ix = bottomValues_.index(bottomValue); |
| |
| if (ix != NULL_COLL_INDEX) |
| topValues_[ix] = newTopValue; |
| else |
| addMapEntry(newTopValue,bottomValue); |
| } |
| |
| void ValueIdMap::remapBottomValue(const ValueId &topValue, |
| const ValueId &newBottomValue) |
| { |
| CollIndex ix = topValues_.index(topValue); |
| |
| if (ix != NULL_COLL_INDEX) |
| bottomValues_[ix] = newBottomValue; |
| else |
| addMapEntry(topValue,newBottomValue); |
| } |
| |
| void ValueIdMap::addMapEntry(const ValueId &newTopValue, |
| const ValueId &newBottomValue) |
| { |
| topValues_.insert(newTopValue); |
| bottomValues_.insert(newBottomValue); |
| } |
| |
| void ValueIdMap::mapValueIdUp(ValueId &topValue, |
| const ValueId &bottomValue) const |
| { |
| CollIndex ix = bottomValues_.index(bottomValue); |
| |
| if (ix != NULL_COLL_INDEX) |
| topValue = topValues_[ix]; |
| else |
| topValue = bottomValue; |
| } |
| |
| void ValueIdMap::mapValueIdUpWithIndex(ValueId &topValue, |
| const ValueId &bottomValue, CollIndex i) const |
| { |
| CollIndex currEntry = i; |
| CollIndex result = i; |
| NABoolean found = FALSE; |
| CollIndex bottomEntries = bottomValues_.entries(); |
| |
| while (currEntry != NULL_COLL_INDEX) |
| { |
| // get the next used entry of bottomValues_ from the index currEntry_ |
| // This methos is the const version of usedEntry |
| if (bottomValues_.constEntry(currEntry) == bottomValue) |
| { |
| currEntry = result; |
| found = TRUE; |
| break; |
| } |
| else |
| { |
| // advance to the next entry |
| result++; |
| currEntry = bottomValues_.getUsage(currEntry); |
| } |
| } |
| |
| if (found) |
| topValue = topValues_[currEntry]; |
| else |
| topValue = bottomValue; |
| } |
| |
| |
| void ValueIdMap::mapValueIdDown(const ValueId &topValue, |
| ValueId &bottomValue) const |
| { |
| CollIndex ix = topValues_.index(topValue); |
| |
| if (ix != NULL_COLL_INDEX) |
| { |
| bottomValue = bottomValues_[ix]; |
| return; |
| } |
| //++ Triggers - |
| // If a generic update has a trigger, |
| // topValues_ might contain VEG_REFERENCEs |
| for (ix=0; ix < topValues_.entries() ; ix++) |
| { |
| ItemExpr *pred = topValues_[ix].getItemExpr(); |
| |
| if (pred->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| const ValueIdSet & VEGGroup =((VEGReference *)pred)->getVEG()->getAllValues(); |
| if (VEGGroup.contains(topValue)) |
| { |
| bottomValue = bottomValues_[ix]; |
| return; |
| } |
| } |
| } |
| //-- Triggers - |
| bottomValue = topValue; |
| } |
| |
| void ValueIdMap::mapValueIdListUp(ValueIdList &topValues, |
| const ValueIdList &bottomValues) const |
| { |
| CMPASSERT(topValues.entries() == 0); |
| |
| for (CollIndex i = 0; i < bottomValues.entries(); i++) |
| { |
| ValueId u; |
| mapValueIdUp(u,bottomValues[i]); |
| topValues.insert(u); |
| } |
| } |
| |
| void ValueIdMap::mapValueIdListDown(const ValueIdList &topValues, |
| ValueIdList &bottomValues) const |
| { |
| CMPASSERT(bottomValues.entries() == 0); |
| |
| for (CollIndex i = 0; i < topValues.entries(); i++) |
| { |
| ValueId l; |
| mapValueIdDown(topValues[i],l); |
| bottomValues.insert(l); |
| } |
| } |
| |
| void ValueIdMap::mapValueIdSetUp(ValueIdSet &topValues, |
| const ValueIdSet &bottomValues) const |
| { |
| CMPASSERT(topValues.isEmpty()); |
| |
| for (ValueId l = bottomValues.init(); |
| bottomValues.next(l); bottomValues.advance(l)) |
| { |
| ValueId u; |
| mapValueIdUp(u,l); |
| topValues.insert(u); |
| } |
| } |
| |
| void ValueIdMap::mapValueIdSetDown(const ValueIdSet &topValues, |
| ValueIdSet &bottomValues) const |
| { |
| CMPASSERT(bottomValues.isEmpty()); |
| |
| for (ValueId u = topValues.init(); |
| topValues.next(u); topValues.advance(u)) |
| { |
| ValueId l; |
| mapValueIdDown(u,l); |
| bottomValues.insert(l); |
| } |
| } |
| |
| void ValueIdMap::rewriteValueIdUp(ValueId &topValue, |
| const ValueId &bottomValue) |
| { |
| topValue = bottomValue.getItemExpr()->mapAndRewrite(*this,FALSE); |
| } |
| |
| void ValueIdMap::rewriteValueIdDown(const ValueId &topValue, |
| ValueId &bottomValue) |
| { |
| bottomValue = topValue.getItemExpr()->mapAndRewrite(*this,TRUE); |
| } |
| |
| void ValueIdMap::rewriteValueIdUpWithIndex(ValueId &topValue, |
| const ValueId &bottomValue, CollIndex i) |
| { |
| topValue = bottomValue.getItemExpr()->mapAndRewriteWithIndx(*this,i); |
| } |
| |
| void ValueIdMap::rewriteValueIdListUp(ValueIdList &topValues, |
| const ValueIdList &bottomValues) |
| { |
| CMPASSERT(topValues.entries() == 0); |
| |
| for (CollIndex i = 0; i < bottomValues.entries(); i++) |
| { |
| ValueId u; |
| rewriteValueIdUp(u,bottomValues[i]); |
| topValues.insert(u); |
| } |
| } |
| |
| void ValueIdMap::rewriteValueIdListUpWithIndex(ValueIdList &topValues, |
| const ValueIdList &bottomValues) |
| { |
| CMPASSERT(topValues.entries() == 0); |
| |
| for (CollIndex i = 0; i < bottomValues.entries(); i++) |
| { |
| ValueId u; |
| rewriteValueIdUpWithIndex(u,bottomValues[i],i); |
| topValues.insert(u); |
| } |
| } |
| |
| void ValueIdMap::rewriteValueIdListDown(const ValueIdList &topValues, |
| ValueIdList &bottomValues) |
| { |
| CMPASSERT(bottomValues.entries() == 0); |
| |
| for (CollIndex i = 0; i < topValues.entries(); i++) |
| { |
| ValueId l; |
| rewriteValueIdDown(topValues[i],l); |
| bottomValues.insert(l); |
| } |
| } |
| |
| void ValueIdMap::rewriteValueIdSetUp(ValueIdSet &topValues, |
| const ValueIdSet &bottomValues) |
| { |
| CMPASSERT(topValues.isEmpty()); |
| |
| for (ValueId l = bottomValues.init(); |
| bottomValues.next(l); bottomValues.advance(l)) |
| { |
| ValueId u; |
| rewriteValueIdUp(u,l); |
| topValues.insert(u); |
| } |
| } |
| |
| void ValueIdMap::rewriteValueIdSetDown(const ValueIdSet &topValues, |
| ValueIdSet &bottomValues) |
| { |
| CMPASSERT(bottomValues.isEmpty()); |
| |
| for (ValueId u = topValues.init(); topValues.next(u); topValues.advance(u)) |
| { |
| ValueId l; |
| rewriteValueIdDown(u,l); |
| bottomValues.insert(l); |
| } |
| } |
| |
| void ValueIdMap::flipSides() |
| { |
| ValueIdList flipList = topValues_; |
| topValues_ = bottomValues_; |
| bottomValues_ = flipList; |
| } |
| |
| void ValueIdMap::augmentForVEG(NABoolean addVEGPreds, |
| NABoolean addVEGRefs, |
| NABoolean compareConstants, |
| const ValueIdSet *topInputsToCheck, |
| const ValueIdSet *bottomInputsToCheck, |
| ValueIdSet *vegRefsWithDifferentConstants, |
| ValueIdSet *vegRefsWithDifferentInputs) |
| { |
| // If a ValueIdMap maps one VEGReference x to another VEGReference y, |
| // we may want to be able to map the corresponding VEGPredicates as |
| // well. This method enables that by finding such pairs of VEGReferences |
| // and augmenting the map with their VEGPredicates. The method can also |
| // augment the map from VEGPredicates to VEGReferences. |
| |
| // NOTE: Before using this method, make sure it is applicable in |
| // your case. What it does is somewhat questionable. We may have |
| // a VEG(a,b,1) in the top values and a VEG(c,d,2) in the bottom |
| // values. Replacing one VEGPred into another may or may not be |
| // what we want. Furthermore, a,b,c may be local values, d may |
| // be a characteristic input. Again, it is questionable whether |
| // the rewrite is what's desired. |
| |
| // The method allows to restrict the rewrite somewhat: |
| // - compareConstants requires top and bottom VEGPreds to |
| // have the same constant (or no constants at all) |
| // - top/bottom inputs to check can be used to exclude |
| // VEGPreds that differ in the way they use inputs. |
| |
| // There are other issues that still may go wrong with this |
| // method, maybe with predicates like COL1=COL2. |
| |
| CollIndex ne = topValues_.entries(); |
| |
| for (CollIndex i=0; i<ne; i++) |
| { |
| ItemExpr *t = topValues_[i].getItemExpr(); |
| ItemExpr *b = bottomValues_[i].getItemExpr(); |
| OperatorTypeEnum to = t->getOperatorType(); |
| OperatorTypeEnum bo = b->getOperatorType(); |
| |
| if (addVEGPreds && |
| to == ITM_VEG_REFERENCE && |
| bo == ITM_VEG_REFERENCE) |
| { |
| VEG *vegT = static_cast<VEGReference *>(t)->getVEG(); |
| VEG *vegB = static_cast<VEGReference *>(b)->getVEG(); |
| ValueId topPred(vegT->getVEGPredicate()->getValueId()); |
| |
| if (! topValues_.contains(topPred)) |
| { |
| NABoolean ok = TRUE; |
| ValueId constT = vegT->getAConstant(TRUE); |
| ValueId constB = vegB->getAConstant(TRUE); |
| |
| // check whether constants match |
| // (or are both NULL_VALUE_ID) |
| if (compareConstants) |
| ok = (constT == constB); |
| |
| if (!ok && vegRefsWithDifferentConstants) |
| *vegRefsWithDifferentConstants += |
| vegT->getVEGReference()->getValueId(); |
| |
| if (ok && |
| ((topInputsToCheck && topInputsToCheck->entries() > 0) || |
| (bottomInputsToCheck && bottomInputsToCheck->entries() > 0))) |
| { |
| ValueIdSet topInputs; |
| ValueIdSet bottomInputs; |
| |
| if (topInputsToCheck) |
| topInputs = *topInputsToCheck; |
| if (bottomInputsToCheck) |
| bottomInputs = *bottomInputsToCheck; |
| |
| topInputs.intersectSet(vegT->getAllValues()); |
| bottomInputs.intersectSet(vegB->getAllValues()); |
| // if the caller provided inputs to check, we only |
| // rewrite VEGPreds if their VEGies refer to the |
| // same inputs (or if they don't refer to any inputs) |
| ok = (topInputs == bottomInputs); |
| |
| if (!ok && vegRefsWithDifferentInputs) |
| *vegRefsWithDifferentInputs += |
| vegT->getVEGReference()->getValueId(); |
| } |
| |
| if (ok) |
| { |
| topValues_.insert(topPred); |
| bottomValues_.insert(vegB->getVEGPredicate()->getValueId()); |
| } |
| } |
| } |
| |
| if (addVEGRefs && |
| to == ITM_VEG_PREDICATE && |
| bo == ITM_VEG_PREDICATE) |
| { |
| ValueId topRef(static_cast<VEGPredicate *>(t)-> |
| getVEG()->getVEGReference()->getValueId()); |
| |
| if (! topValues_.contains(topRef)) |
| { |
| topValues_.insert(topRef); |
| bottomValues_.insert( |
| static_cast<VEGPredicate *>(b)-> |
| getVEG()->getVEGReference()->getValueId()); |
| } |
| } |
| } |
| } |
| |
| NABoolean ValueIdMap::normalizeNode(NormWA & normWARef) |
| { |
| NABoolean t1,t2; |
| |
| // Transform both lists |
| t1 = topValues_.normalizeNode(normWARef); |
| t2 = bottomValues_.normalizeNode(normWARef); |
| |
| return (t1 || t2); |
| } |
| |
| // Remove unused entries from the Map if they are no longer required |
| // based on the require values. |
| // |
| void ValueIdMap::removeUnusedEntries(const ValueIdSet &requiredValues, NABoolean matchWithTopValues) |
| { |
| if (matchWithTopValues) |
| { |
| for (CollIndex ix=0; ix < topValues_.entries() ; ix++) |
| { |
| if(!requiredValues.contains(topValues_[ix])) |
| { |
| topValues_.removeAt(ix); |
| bottomValues_.removeAt(ix); |
| ix--; |
| } |
| } |
| } |
| else |
| { |
| for (CollIndex ix=0; ix < bottomValues_.entries() ; ix++) |
| { |
| if(!requiredValues.contains(bottomValues_[ix])) |
| { |
| topValues_.removeAt(ix); |
| bottomValues_.removeAt(ix); |
| ix--; |
| } |
| } |
| } |
| } |
| |
| // *********************************************************************** |
| // Constructor for ValueDesc |
| // |
| // Use the ActiveSchemaDB() consistently in ALL methods for ValueDescs |
| // and ValueIds. |
| // *********************************************************************** |
| |
| ValueDesc::ValueDesc(ItemExpr *expr) |
| : exprPtr_(expr), domId_(NULL) |
| { |
| ActiveSchemaDB()->insertValueDesc(this); |
| } |
| |
| // static ctor |
| ValueId ValueDesc::create(ItemExpr *expr, const NAType *type, CollHeap *h) |
| { |
| ValueDesc *vdesc = new (h) ValueDesc(expr); |
| vdesc->setDomainDesc(new (h) DomainDesc(ActiveSchemaDB(), *type)); |
| return vdesc->getValueId(); |
| } |
| |
| // *********************************************************************** |
| // Methods for class ValueDescArray |
| // *********************************************************************** |
| void ValueDescArray::print(FILE* ofd, const char* indent, const char* title, |
| NABoolean dontDisplayErrors) const |
| { |
| #ifndef NDEBUG |
| BUMP_INDENT(indent); |
| fprintf(ofd, "%s%s %p (%d entries)\n", |
| NEW_INDENT, title, this, entries()); |
| |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| ValueDesc *vd = at(i); |
| if (vd && used(i)) |
| { |
| Int32 otyp = 0; |
| NAString unparsed(CmpCommon::statementHeap()); |
| ItemExpr *ie = vd->getItemExpr(); |
| if (ie) |
| { |
| CollIndex ivid = (CollIndex)vd->getValueId(); |
| if (i != ivid) |
| { |
| //fprintf(ofd, "%4d != %d: ERROR\n", i, |
| otyp = -1; |
| char cvid[TEXT_DISPLAY_LENGTH]; |
| sprintf(cvid, "ERROR: vid %d", ivid); |
| unparsed = cvid; |
| } |
| else |
| { |
| otyp = ie->getOperatorType(); |
| ie->unparse(unparsed); |
| } |
| } |
| if (otyp > 0 || !dontDisplayErrors) |
| fprintf(ofd, "%4d %p %4d: %s\n", i, ie, otyp, unparsed.data()); |
| } |
| } |
| #endif |
| } // ValueDescArray::print() |
| |
| void ValueDescArray::display(NABoolean dontDisplayErrors) const |
| { |
| print(stdout, DEFAULT_INDENT, "ValueDescArray", dontDisplayErrors); |
| } |
| |
| /*static*/ void ValueDescArray::Display(NABoolean dontDisplayErrors) |
| { |
| ActiveSchemaDB()->getValueDescArray().print( |
| stdout, DEFAULT_INDENT, "Global ValueDescArray", dontDisplayErrors); |
| } |
| |
| //-------------------------------------------------------- |
| // Replace aggregates of type ITM_ONE_ROW(I,J,K) with (I,J,K); |
| // Generator does not know how to transmit |
| // ONE_ROW of columns from a child to parent. So do not |
| // ask for it. One Row check would be performed at the |
| // concerned GroupBy node. |
| // |
| // If we have a expression tree where ITM_ONE_ROW node is an |
| // interior node with arity one, replace it as well. |
| //-------------------------------------------------------- |
| void ValueIdSet::replaceOneRowbyList( NormWA & normref ) |
| { |
| |
| ValueId exprId; |
| ValueIdSet replaceSet; |
| for (exprId = init(); next(exprId); advance(exprId)) |
| { |
| ItemExpr *thisIE = exprId.getItemExpr(); |
| if ( (thisIE->getArity() >= 1) && |
| (thisIE->containsOneRowAggregate()) ) |
| |
| { |
| ItemExpr *tfm = NULL; |
| tfm = thisIE->transformOneRowAggregate( normref ); |
| CMPASSERT( tfm ); |
| |
| exprId.replaceItemExpr(tfm); |
| // redrive type synthesis for both self and child nodes |
| tfm->synthTypeAndValueId(TRUE, TRUE); |
| |
| tfm = tfm->transformMultiValuePredicate(FALSE); |
| if (tfm) // transform was needed |
| { |
| exprId.replaceItemExpr(tfm); |
| // redrive type synthesis for both self and child nodes |
| tfm->synthTypeAndValueId(TRUE, TRUE); |
| tfm->convertToValueIdSet(replaceSet, NULL, ITM_AND, FALSE); |
| subtractElement(exprId); |
| } |
| else |
| { |
| // this itemexpression tree contains a onerow aggregate node, yet no |
| // transformation was done. Remove the aggregate node from the tree so |
| // that the relational expression won't ask for a onerow valueid. |
| tfm = thisIE->removeOneRowAggregate( thisIE, normref ); |
| CMPASSERT( tfm ); |
| exprId.replaceItemExpr(tfm); |
| tfm->convertToValueIdSet(replaceSet, NULL, ITM_AND, FALSE); |
| subtractElement(exprId); |
| } |
| } // if contains oneRow aggregate. |
| |
| // Even though some scalar aggregates do not contain the node |
| // ITM_ONE_ROW, they implicitly contain them. There is special logic |
| // in Subquery::transformNode to identify them. If we are seeing one |
| // of those here, they need to be retransformed. |
| |
| // Go through the ItemExpression Tree, anchored by the Variable |
| // 'ThisIE' and retransform it; case Id: 10-990122-0552 |
| |
| if ( thisIE->markPathToUnTransformedNode() ) |
| |
| // Returns TRUE if any node in the whole tree anchored by thisIE |
| // contains an untransformed node. |
| // |
| // Marks the entire path, from this node to the untransformed node, as |
| // untransformed. |
| // |
| // I believe that this modification of what was originally coded |
| // handles the case when the item expression tree is deep -- that |
| // is, when there is one or more nodes betwen thisIE and the |
| // currently untransformed node. See his comment below: |
| // |
| // $$$ This fix is temporary as it would only work if the item |
| // $$$ expression tree is shallow. Need to do special processing if the |
| // $$$ tree is deep. .... and I have a deadline to keep - 1/29/99 |
| |
| { |
| CMPASSERT( ! thisIE->nodeIsTransformed() ); |
| } |
| |
| // If we don't mark the entire path as untransformed (from the |
| // untransformed child to thisIE), when it comes time to transforming |
| // thisIE, we won't walk down the tree to the untransformed node. |
| // Look at the beginning of all ::transformNode() methods -- the first |
| // thing that each one does is check to see if it's already |
| // transformed. If so, returns, without looking at its children. |
| // Thus, if there are one or more nodes marked "transformed" between |
| // thisIE (marked "UNtransformed") and the untransformed child node, |
| // we won't transform that child node when we call |
| // thisIE->transformNode(). |
| // |
| // There is a general assumption in all other transformation code: if |
| // any node in the tree is transformed, then its children have been |
| // transformed already. |
| |
| } // for |
| |
| addSet(replaceSet); |
| } // ValueIdSet::replaceOneRowbyList() |
| |
| // is this list cacheable after this phase? |
| NABoolean ValueIdList::isCacheableExpr(CacheWA& cwa) |
| { |
| CollIndex ix, limit = entries(); |
| for (ix = 0; ix < limit; ix++) { |
| if (!((*this)[ix].getItemExpr()->isCacheableExpr(cwa))) { |
| return FALSE; |
| } |
| } |
| return TRUE; |
| } |
| |
| ValueId ValueIdList::extractVEGRefForEquiPredicate(ValueId x) const |
| { |
| ItemExpr* ie = x.getItemExpr(); |
| switch ( ie->getOperatorType() ) |
| { |
| case ITM_EQUAL: |
| { |
| for (CollIndex i = 0; i < 2; i++) |
| if ( contains(ie->child(i)->getValueId()) ) |
| return ie->child(i)->getValueId(); |
| |
| break; |
| } |
| |
| case ITM_VEG_PREDICATE: |
| { |
| VEG* veg = ((VEGPredicate*)ie)->getVEG(); |
| |
| // Simple case first: the veg predicate refers to a VEG |
| // that appears in the ValueIdList of this. |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| ItemExpr* iei = at(i).getItemExpr(); |
| |
| if (iei->getOperatorType() == ITM_INSTANTIATE_NULL) |
| iei=iei->child(0)->castToItemExpr(); |
| |
| if ( iei->getOperatorType() == ITM_VEG_REFERENCE && |
| ((VEGReference*)iei)->getVEG() == veg ) |
| { |
| return at(i); |
| } |
| } |
| |
| // Next we drill down the VEG predicate to see if there is a |
| // VEG reference that appears in ValueIdList of this. We return |
| // that VEG reference when the lookup succeeds. |
| // |
| // The following loop is necessary to deal with a VEG predicate of form |
| // D.A = VEGRef(T.A) |
| // ValueIdList of this contains {VEGRef(T.A), VEGRef(T.B)) |
| // The MC join predicate is |
| // T.A = D.A and T.B = D.B |
| const ValueIdList vegMembers(veg->getAllValues()); |
| |
| for (CollIndex i=0; i<vegMembers.entries(); i++) { |
| // we will be getting D.A, VegRef(T.A) |
| ItemExpr* iei = vegMembers[i].getItemExpr(); |
| |
| if (iei->getOperatorType() == ITM_INSTANTIATE_NULL) |
| iei=iei->child(0)->castToItemExpr(); |
| |
| if ( iei->getOperatorType() == ITM_VEG_REFERENCE && |
| this->contains(vegMembers[i]) |
| ) |
| return vegMembers[i]; |
| |
| } |
| |
| break; |
| |
| } |
| |
| default: |
| break; |
| } |
| |
| return NULL_VALUE_ID; |
| } |
| |
| // change literals of a ValueIdList into ConstantParameters |
| void ValueIdList::normalizeForCache(CacheWA& cwa, BindWA& bindWA) |
| { |
| // Iterate over all the values (scalar expressions) in this list. |
| CollIndex ix, limit = entries(); |
| for (ix = 0; ix < limit; ix++) { |
| ItemExpr *iePtr = at(ix).getItemExpr(); // original expr |
| // apply normalizeForCache on the item expr |
| ItemExpr *nePtr = iePtr->normalizeForCache(cwa, bindWA); |
| // if normalizeForCache changed the original expr, update the list |
| if (nePtr->castToItemExpr() != iePtr || nePtr->getValueId() != at(ix)) { |
| at(ix) = nePtr->castToItemExpr()->getValueId(); |
| } |
| } |
| } |
| |
| ConstValue* ValueIdList::getConstant(ItemExpr* ie) |
| { |
| switch (ie->getOperatorType()) |
| { |
| case ITM_CACHE_PARAM: |
| { |
| ConstantParameter*cp = (ConstantParameter*)ie; |
| return cp->getConstVal(); |
| } |
| break; |
| |
| case ITM_CONSTANT: |
| return (ConstValue*)ie; |
| break; |
| |
| default: |
| { |
| if ( ie->getOperatorType() == ITM_VEG_REFERENCE ) |
| { |
| ValueIdSet result; |
| ie->findAll(ITM_CONSTANT, result, TRUE, FALSE); |
| |
| if ( result.entries() == 0 ) |
| ie->findAll(ITM_CACHE_PARAM, result, TRUE, FALSE); |
| |
| if ( result.entries() == 1 ) |
| { |
| for (ValueId vid= result.init(); result.next(vid); result.advance(vid)) |
| { |
| ConstValue * constVal = getConstant(vid.getItemExpr()); |
| return constVal; |
| } // for |
| } // if |
| } // if |
| } // default |
| break; |
| } // switch |
| |
| return NULL; |
| } |
| |
| void ValueIdList::convertToTextKey(const ValueIdList& keyList, NAString& result) |
| { |
| result.resize(0); |
| |
| CollIndex i = 0; |
| for (i = 0; i < entries(); i++) |
| { |
| ItemExpr* ie = at(i).getItemExpr(); |
| |
| ConstValue* constVal = getConstant(ie); |
| |
| if ( constVal ) { |
| const NAType &type = keyList.at(i).getType(); |
| const NAType *constType = constVal->getType(); |
| |
| NAString val = *constVal->getRawText(); |
| short len = 0; |
| |
| /////////////////////////////////////////////////////////////////////// |
| // Value Layout: |
| // 2 bytes total length | 2 bytes null value, if nullable | data |
| // (null indicator length(2 bytes) + data length) | |
| //////////////////////////////////////////////////////////////////////// |
| if (type.supportsSQLnull()) |
| { |
| len = 2; |
| } |
| |
| if (constVal->isNull()) |
| { |
| if (type.supportsSQLnull()) |
| { |
| result.append((char*)&len, sizeof(short)); |
| |
| short nullVal = -1; |
| result.append((char*)&nullVal, sizeof(short)); |
| } |
| } |
| else if ((val == "<min>") || |
| (val == "<max>")) |
| { |
| Lng32 bufLen = |
| (type.isVaryingLen() ? |
| (type.getNominalSize() + type.getVarLenHdrSize()) : |
| type.getNominalSize()); |
| char * buf = new(CmpCommon::statementHeap()) char[bufLen+10]; |
| |
| NAString *mmVal = NULL; |
| if (val == "<min>") |
| { |
| type.minRepresentableValue(buf, &bufLen, &mmVal, |
| CmpCommon::statementHeap()); |
| |
| if ((type.getTypeQualifier() == NA_DATETIME_TYPE) || |
| (type.getTypeQualifier() == NA_INTERVAL_TYPE)) |
| { |
| // string value returned is of the form: DATE '2013-06-20' or |
| // INTERVAL '10' YEAR. |
| // Extract the actual string from the returned value. |
| Lng32 start = mmVal->index("'"); |
| if (start > 0) |
| { |
| Lng32 end = mmVal->index("'", start+1); |
| if (end > 0) |
| { |
| *mmVal = (*mmVal)(start+1, (end-start-1)); |
| |
| if (type.getTypeQualifier() == NA_INTERVAL_TYPE) |
| { |
| // prepend '-' to the output |
| (*mmVal).prepend('-', 1); |
| } |
| } |
| } |
| } |
| else if (type.getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| // string literal looks like _ISO88591'abc' and is |
| // in UTF-8, what we want here is the actual character |
| // values in the type's character set |
| *mmVal = NAString(buf, bufLen); |
| } |
| } |
| else |
| { |
| type.maxRepresentableValue(buf, &bufLen, &mmVal, |
| CmpCommon::statementHeap()); |
| |
| if ((type.getTypeQualifier() == NA_DATETIME_TYPE) || |
| (type.getTypeQualifier() == NA_INTERVAL_TYPE)) |
| { |
| // string value returned is of the form: DATE '2013-06-20' or |
| // INTERVAL '10' YEAR. |
| // Extract the actual string from the returned value. |
| Lng32 start = mmVal->index("'"); |
| if (start > 0) |
| { |
| Lng32 end = mmVal->index("'", start+1); |
| if (end > 0) |
| { |
| *mmVal = (*mmVal)(start+1, (end-start-1)); |
| } |
| } |
| } |
| else if (type.getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| // string literal looks like _ISO88591'abc' and is |
| // in UTF-8, what we want here is the actual character |
| // values in the type's character set |
| *mmVal = NAString(buf, bufLen); |
| } |
| } |
| |
| short vLen = 0; |
| if (type.isVaryingLen()) |
| { |
| vLen = *(short*)mmVal->data(); |
| } |
| else |
| { |
| vLen = mmVal->length(); |
| } |
| |
| len += vLen; |
| result.append((char*)&len, sizeof(short)); |
| if (type.supportsSQLnull()) |
| { |
| short nullVal = 0; |
| result.append((char*)&nullVal, sizeof(short)); |
| } |
| |
| if (type.isVaryingLen()) |
| result.append(&mmVal->data()[type.getVarLenHdrSize()], vLen); |
| else |
| result.append(mmVal->data(), vLen); |
| } |
| else |
| { |
| short vLen = val.length(); |
| |
| if (constType->getTypeQualifier() == NA_INTERVAL_TYPE) |
| { |
| // In some code paths, the text may have "INTERVAL 'xxx' <qualifier>" |
| // junk around it so we have to strip that off. (Example: An equality |
| // predicate when query caching has been turned off via |
| // CQD QUERY_CACHE '0'. Another example happens with BETWEEN, whether |
| // or not query caching is turned off. See JIRA TRAFODION-3088 for |
| // that example.) |
| Lng32 start = val.index("'"); |
| Lng32 minus = val.index("-"); |
| if (start > 0) |
| { |
| Lng32 end = val.index("'", start+1); |
| if (end > 0) |
| { |
| val = val(start+1, (end-start-1)); |
| if ((minus > 0) && (minus < start)) // '-' before the string part |
| { |
| // prepend '-' to the output |
| val.prepend('-', 1); |
| } |
| vLen = val.length(); |
| } |
| } |
| } |
| else if ((constType->getTypeQualifier() == NA_NUMERIC_TYPE) && |
| (((NumericType*)constType)->isExact()) && |
| (NOT ((NumericType*)constType)->isBigNum()) && |
| (constType->getScale() > 0)) |
| { |
| // See how many positions the result will take in the display |
| Lng32 t = constType->getDisplayLength(constType->getFSDatatype(), |
| constType->getNominalSize(), |
| constType->getPrecision(), |
| constType->getScale(), |
| 0); |
| |
| char strval[t+1]; |
| memset( strval, ' ', t ); |
| |
| // Get the ASCII representation |
| ex_expr::exp_return_type retcode = |
| convDoIt((char*)constVal->getConstValue(), |
| constVal->getStorageSize(), |
| (short)constType->getFSDatatype(), |
| constType->getPrecision(), |
| constType->getScale(), |
| strval, |
| t, // target length |
| REC_BYTE_F_ASCII, // target type |
| 0, // no char limit |
| SQLCHARSETCODE_ISO88591, // ISO 8859-1 |
| NULL, // no vc length |
| 0); // not a varchar |
| |
| if ( retcode == ex_expr::EXPR_OK ) |
| { |
| strval[t] = 0; |
| val = strval; |
| val = val.strip(NAString::trailing, ' '); |
| } |
| |
| vLen = val.length(); |
| } // exact numeric |
| |
| len += vLen; |
| |
| result.append((char*)&len, sizeof(short)); |
| |
| if (type.supportsSQLnull()) |
| { |
| short nullVal = 0; |
| result.append((char*)&nullVal, sizeof(short)); |
| } |
| |
| result.append(val.data(), vLen); |
| } |
| } else |
| return; |
| } // for |
| } |
| |
| Int32 ValueIdList::countConstantsAsPrefixes() |
| { |
| Int32 ct = 0; |
| for (CollIndex i = 0; i < entries(); i++) |
| { |
| ItemExpr* ie = at(i).getItemExpr(); |
| |
| switch (ie->getOperatorType()) { |
| case ITM_CACHE_PARAM: |
| case ITM_CONSTANT: |
| ct++; |
| continue; |
| |
| case ITM_VEG_REFERENCE: |
| case ITM_VEG_PREDICATE: |
| { |
| ValueIdSet result; |
| ie->findAll(ITM_CONSTANT, result, TRUE, FALSE); |
| ie->findAll(ITM_CACHE_PARAM, result, TRUE, FALSE); |
| |
| ct += result.entries(); |
| } |
| continue; |
| |
| default: |
| break; |
| } |
| } |
| |
| return ct; |
| } |
| |
| // change literals of a ValueIdSet into ConstantParameters |
| void ValueIdSet::normalizeForCache(CacheWA& cwa, BindWA& bindWA) |
| { |
| NABoolean changed = FALSE; |
| ValueIdSet newExpr; |
| // Iterate over all the values (scalar expressions) in this set. |
| for (ValueId exprId = init(); next(exprId); advance(exprId)) { |
| ItemExpr *iePtr = exprId.getItemExpr(); // original expr. |
| // apply normalizeForCache on the item expr |
| ItemExpr *nePtr = iePtr->normalizeForCache(cwa, bindWA); |
| // if normalizeForCache changed the original expr, update the set |
| if (nePtr != iePtr || nePtr->getValueId() != exprId) { |
| changed = TRUE; |
| subtractElement(exprId); // delete original expression from set |
| // accumulate new expressions in another set |
| if (nePtr) { |
| nePtr->convertToValueIdSet(newExpr); |
| } |
| } |
| } |
| if (changed) { |
| addSet(newExpr); // add normalized expressions to the given set |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| //Check whether the Valudidset contains any CAST expressions && and replace |
| // it with the original expression |
| //--------------------------------------------------------------------------- |
| void ValueIdSet::replaceCastExprWithOriginal(const ValueIdSet &originalOutputs, |
| const RelExpr * parent) |
| { |
| ValueId vid, vid1; |
| RelExpr *child = parent->child(0); |
| ValueIdSet childOutputs(child->getGroupAttr()->getCharacteristicOutputs()); |
| |
| for (vid = originalOutputs.init(); |
| originalOutputs.next(vid); |
| originalOutputs.advance(vid) ) |
| { |
| if (vid.getItemExpr()->getOperatorType() == ITM_CAST) |
| { |
| Cast *castExpr = (Cast *)vid.getItemExpr(); |
| |
| // can the child produce the valueid vid1? |
| vid1 = castExpr->getExpr()->getValueId(); |
| |
| if (childOutputs.contains(vid1)) |
| { |
| subtractElement(vid); |
| addElement(vid1); |
| } |
| // for case statement, do not produce any expressions downwards |
| // compute the case statement at the root |
| else if (castExpr->getExpr()->getOperatorType() == ITM_CASE) |
| { |
| ValueIdSet coveredValues, emptySet; |
| const GroupAttributes emptyGA; |
| castExpr->getExpr()->getLeafValuesForCoverTest(coveredValues, emptyGA, emptySet); |
| // take an intersection of child outputs and covered values |
| // and request from child those ooutputs |
| coveredValues.intersectSet(childOutputs); |
| //10-070201-2242 -Begin |
| //Replace the cast itemExpr only if the child can |
| //provide ALL values to evaluate it. |
| if(!coveredValues.isEmpty()) |
| { |
| NABoolean found = TRUE; |
| for(ValueId v = coveredValues.init(); |
| coveredValues.next(v); |
| coveredValues.advance(v)) |
| { |
| if(!childOutputs.contains(v)) |
| { |
| found = FALSE; |
| break; |
| } |
| } |
| if(found) |
| { |
| subtractElement(vid); |
| addSet(coveredValues); |
| } |
| //10-070201-2242-End |
| } |
| } |
| } |
| } |
| } // ValueIdSet::replaceCastExprWithOriginal() |
| |
| //----------------------------------------------------------------------- |
| // Check whether the ValueIdSet contains expressions of the form |
| // InstNull(Cast(aggregate))) and modify them to aggregate. This is done |
| // if the left child of the argument joinExpr can potentially produce |
| // the aggregate; typically the valueid set represents output values of |
| // a join operator. This is called from Join::pushdownCoveredExpr(). |
| // Input: joinExpression |
| // Potentially sideeffects the this object |
| //----------------------------------------------------------------------- |
| void ValueIdSet::replaceInstnullCastAggregateWithAggregateInLeftJoins |
| ( RelExpr *joinExpr) |
| { |
| if (joinExpr->getOperatorType() != REL_LEFT_JOIN) |
| return; |
| |
| ValueId vid; |
| for (vid = init(); |
| next(vid); |
| advance(vid) ) |
| { |
| ItemExpr *ie= vid.getItemExpr(); |
| if (ie->getOperatorType() != ITM_INSTANTIATE_NULL) |
| continue; |
| if (ie->child(0)->getOperatorType() != ITM_CAST) |
| continue; |
| Cast *castIE = (Cast *)ie->child(0)->castToItemExpr(); |
| if (castIE->getExpr()->isAnAggregate()) |
| { |
| // can the right child produce the aggregate? |
| ValueIdSet outputs; |
| joinExpr->child(1)->getPotentialOutputValues(outputs); |
| if (outputs.contains(castIE->getExpr()->getValueId())) |
| |
| { |
| subtractElement(vid); |
| addElement(castIE->getExpr()->getValueId()); |
| } |
| |
| } |
| } |
| } // ValueIdSet::replaceInstnullCastAggregateWithAggregateInLeftJoins |
| |
| // ----------------------------------------------------------------------- |
| // This method is a slight modification of intersectSet. Here instead of |
| // modifying this operand it returns the modified set |
| // ----------------------------------------------------------------------- |
| ValueIdSet ValueIdSet::intersect(const ValueIdSet & v) const |
| { |
| ValueIdSet thisCopy(*this); |
| thisCopy.intersectSet(v); |
| return thisCopy; |
| } |
| |
| ValueIdSet& ValueIdSet::intersectSetDeep(const ValueIdSet & v) |
| { |
| for (ValueId tgt = init(); next(tgt); advance(tgt)) |
| { |
| NABoolean found = FALSE; |
| for (ValueId vid = v.init(); v.next(vid) && !found; v.advance(vid)) |
| { |
| if (vid.getItemExpr()->referencesTheGivenValue(tgt,FALSE,FALSE)) |
| found = TRUE; // keep it |
| } |
| if (!found) |
| subtractElement(tgt); // remove unreferenced element |
| } |
| return *this; |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // return true iff ValueIdSet has predicates that guarantee |
| // that opd is not nullable |
| // -------------------------------------------------------------------- |
| NABoolean ValueIdSet::isNotNullable(const ValueId& opd) |
| { |
| // if opd's type is not nullable then return TRUE |
| if (!opd.getType().supportsSQLnullLogical()) { |
| return TRUE; |
| } |
| // opd's type is nullable. |
| |
| // search ValueIdSet for a predicate conjunct |
| // that can guarantee that opd is not null |
| for (ValueId vid = init(); next(vid); advance(vid) ) { |
| // predicates are implicitly connected by AND |
| ItemExpr *iePtr = vid.getItemExpr(); |
| switch (iePtr->getOperatorType()) { |
| case ITM_IS_NOT_NULL: |
| if (iePtr->child(0)->containsTheGivenValue( opd )) { |
| // found "opd IS NOT NULL" predicate conjunct |
| return TRUE; // opd is not null |
| } |
| break; // keep looking |
| case ITM_EQUAL: |
| case ITM_NOT_EQUAL: |
| case ITM_LESS: |
| case ITM_LESS_EQ: |
| case ITM_GREATER: |
| case ITM_GREATER_EQ: |
| if (iePtr->child(0)->containsTheGivenValue( opd ) || |
| iePtr->child(1)->containsTheGivenValue( opd )) { |
| // found "opd compop expr" predicate conjunct |
| return TRUE; // opd is not null |
| } |
| break; // keep looking |
| case ITM_IS_NULL: |
| if (iePtr->child(0)->containsTheGivenValue( opd )) { |
| // found "opd IS NULL" predicate conjunct |
| return FALSE; // opd is null |
| } |
| break; |
| default: |
| break; // keep looking |
| } |
| } |
| return FALSE; |
| } |
| |
| void ValueIdList::addMember(ItemExpr* x) |
| { |
| insert(x->getValueId()); |
| } |
| |
| void ValueIdSet::addMember(ItemExpr* x) |
| { |
| (*this) += (x->getValueId()); |
| } |
| |
| Lng32 ValueIdList::findPrefixLength(const ValueIdSet& x) const |
| { |
| CollIndex count = this->entries(); |
| Lng32 ct = 0; |
| for (CollIndex i=0; i<count; i++) |
| { |
| if ( x.contains((*this)[i]) ) |
| ct++; |
| else |
| break; |
| } |
| return ct; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // replace any ColReference (of the given column name) in of this value |
| // expression with the given expression. |
| // used in ValueId::computeEncodedKey() to assign key values into the |
| // salt/DivisionByto expression. |
| // ---------------------------------------------------------------------- |
| void ValueId::replaceColReferenceWithExpr(const NAString& colName, |
| const ValueId & vid) |
| { |
| ItemExpr* thisItemExpr = getItemExpr(); |
| for( Lng32 i = 0; i < thisItemExpr->getArity(); i++ ) |
| { |
| ValueId childValueId = thisItemExpr->child(i).getValueId(); |
| ItemExpr* childItemExpr = childValueId.getItemExpr(); |
| |
| if( childItemExpr->getOperatorType() == ITM_REFERENCE) |
| { |
| if( ((ColReference*)childItemExpr)->getColRefNameObj().getColName() == colName ) |
| thisItemExpr->setChild( i, vid.getItemExpr() ); |
| } |
| childValueId.replaceColReferenceWithExpr( colName, vid ); |
| } |
| } |
| |
| |
| char* |
| ValueIdList::computeEncodedKey(const TableDesc* tDesc, NABoolean isMaxKey, |
| char*& encodedKeyBuffer, Int32& keyBufLen) const |
| { |
| const NATable* naTable = tDesc->getNATable(); |
| |
| |
| CollIndex count = entries(); |
| NAString** inputStrings = new (STMTHEAP) NAStringPtr[count]; |
| |
| for (Int32 j=0; j<count; j++ ) |
| inputStrings[j] = NULL; |
| |
| for (Int32 j=0; j<count; j++ ) { |
| |
| ValueId vid = (*this)[j]; |
| ItemExpr* ie = vid.getItemExpr(); |
| |
| if ( ie->getOperatorType() != ITM_CONSTANT ) { |
| |
| ConstValue* value = NULL; |
| if ( ie->doesExprEvaluateToConstant(TRUE, TRUE) ) { |
| ValueIdSet availableValues; |
| // do a simple VEG replacement with no available values |
| // and no inputs, all VEGies should have constants in |
| // them and should be replaced with those |
| ie = ie->replaceVEGExpressions(availableValues, availableValues); |
| if (ie->getOperatorType() == ITM_CONSTANT) |
| value = static_cast<ConstValue *>(ie); |
| else |
| value = ie->evaluate(STMTHEAP); |
| |
| if ( !value ) |
| return NULL; |
| } else |
| return NULL; |
| |
| inputStrings[j] = new (STMTHEAP) NAString(value->getConstStr(FALSE)); |
| } else { |
| // no need to prefix with charset prefix. |
| inputStrings[j] = new (STMTHEAP) NAString(((ConstValue*) ie)->getConstStr(FALSE)); |
| |
| if ( *inputStrings[j] == "<min>" || *inputStrings[j] == "<max>" ) |
| inputStrings[j] = NULL; |
| } |
| } |
| |
| const NAFileSet * naf = naTable->getClusteringIndex(); |
| const TrafDesc * tableDesc = naTable->getTableDesc(); |
| TrafDesc * colDescs = tableDesc->tableDesc()->columns_desc; |
| TrafDesc * keyDescs = (TrafDesc*)naf->getKeysDesc(); |
| |
| // cast away const since the method may compute and store the length |
| keyBufLen = ((NAFileSet*)naf)->getEncodedKeyLength(); |
| |
| if ( (count > 0) && (inputStrings[0]) && |
| ( naTable->isHbaseCellTable() || naTable->isHbaseRowTable() ) ) { |
| // the encoded key for Native Hbase table is a null-terminated string ('<key>') |
| NAString key; |
| key.append("("); |
| |
| size_t idx = inputStrings[0]->index("_ISO88591"); |
| if ( idx == 0 ) |
| key.append(inputStrings[0]->remove(0, 9)); |
| else |
| key.append(*inputStrings[0]); |
| |
| key.append(")"); |
| |
| keyBufLen = inputStrings[0]->length() + 5; // extra 4 bytes for (,', ', ), and one byte for null. |
| |
| if (!encodedKeyBuffer ) |
| encodedKeyBuffer = new (STMTHEAP) char[keyBufLen]; |
| |
| memcpy(encodedKeyBuffer, key.data(), key.length()); |
| encodedKeyBuffer[key.length()] = NULL; |
| |
| NADELETEARRAY(inputStrings, count, NAStringPtr, STMTHEAP); |
| |
| return encodedKeyBuffer; |
| |
| } else { |
| keyBufLen = ((NAFileSet*)naf)->getEncodedKeyLength(); |
| |
| if (!encodedKeyBuffer ) |
| encodedKeyBuffer = new (STMTHEAP) char[keyBufLen]; |
| |
| short ok = encodeKeyValues(colDescs, keyDescs, |
| inputStrings, // INPUT |
| FALSE, // not isIndex |
| isMaxKey, |
| encodedKeyBuffer,// OUTPUT |
| STMTHEAP, CmpCommon::diags()); |
| |
| NADELETEARRAY(inputStrings, count, NAStringPtr, STMTHEAP); |
| |
| return ( ok == 0 ) ? encodedKeyBuffer : NULL; |
| } |
| } |
| |
| void ValueIdSet::findAllOpType(OperatorTypeEnum type, ValueIdSet & result) const |
| { |
| for(ValueId valId = init(); next(valId); advance(valId)) { |
| |
| ItemExpr *itmExpr = valId.getItemExpr(); |
| if(itmExpr->getOperatorType() == type) |
| result += valId; |
| } |
| } |
| |
| void ValueIdSet::findAllChildren(ValueIdSet & result) const |
| { |
| for(ValueId valId = init(); next(valId); advance(valId)) { |
| |
| ItemExpr *itmExpr = valId.getItemExpr(); |
| |
| for ( Lng32 i = 0; i < itmExpr->getArity(); i++ ) |
| result += itmExpr->child(i).getValueId(); |
| } |
| } |
| |
| void ValueIdSet::addOlapLeadFuncs(const ValueIdSet& input, ValueIdSet& result) |
| { |
| for(ValueId valId = init(); next(valId); advance(valId)) { |
| ItemExpr *itmExpr = valId.getItemExpr(); |
| if ( itmExpr->getOperatorType() == ITM_OLAP_LEAD ) { |
| |
| ItmLeadOlapFunction* me = (ItmLeadOlapFunction*)(itmExpr); |
| |
| for(ValueId j= input.init(); input.next(j); input.advance(j)) { |
| ItemExpr* child = j.getItemExpr(); |
| ItmLeadOlapFunction* lead = |
| new (STMTHEAP) ItmLeadOlapFunction(child, me->getOffset()); |
| lead->synthTypeAndValueId(); |
| result.insert(lead->getValueId()); |
| } |
| } |
| } |
| } |
| |