| /********************************************************************** |
| // @@@ 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: NormItemExpr.C |
| * Description: Item expressions (normalizer-related methods) |
| * Created: 12/02/94 |
| * Language: C++ |
| * |
| * |
| * |
| * "If you cannot convince them, then confuse them" |
| * Harry S. Truman |
| * |
| ****************************************************************************** |
| */ |
| |
| |
| #include "Debug.h" |
| #include "Sqlcomp.h" |
| #include "GroupAttr.h" |
| #include "NormWA.h" |
| #include "AllItemExpr.h" |
| #include "mdam.h" |
| #include "ValueDesc.h" |
| #include "RelGrby.h" |
| #include "RelJoin.h" |
| #include "RelUpdate.h" |
| #include "Refresh.h" |
| #include "ItemSample.h" |
| #include "ItmFlowControlFunction.h" |
| #include "ItemFuncUDF.h" |
| |
| #ifndef TRANSFORM_DEBUG_DECL // artifact of NSK's OptAll.cpp ... |
| #define TRANSFORM_DEBUG_DECL |
| DBGDECLDBG( dbg; ) |
| DBGDECL( static NAString unp; ) |
| #endif |
| |
| |
| // ----------------------------------------------------------------------- |
| // ----------------------------------------------------------------------- |
| static NABoolean canBeSQLUnknown(const ItemExpr *ie, |
| NABoolean typeMustBeSQLBoolean = TRUE) |
| { |
| const NAType &typ = ie->getValueId().getType(); |
| if (typ.getTypeQualifier() == NA_BOOLEAN_TYPE) |
| return ((SQLBooleanRelat &)typ).canBeSQLUnknown(); |
| CMPASSERT(!typeMustBeSQLBoolean); |
| return FALSE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Ansi 8.12 |
| // ----------------------------------------------------------------------- |
| static void applyTruthTable(ItemExpr * ie, ExprValueId & locationOfPointerToMe) |
| { |
| enum truthResultEnum { TRU_ = ITM_RETURN_TRUE, // true |
| FAL_ = ITM_RETURN_FALSE, // false |
| UNK_ = ITM_RETURN_NULL, // unknown |
| SIB_ = ITM_CASE, // sibling's truth value |
| NOP_ = ITM_NO_OP }; // continue|leave as is |
| truthResultEnum t, f, u, result = NOP_; |
| Int32 ucnt = 0; |
| ItemExpr *ptrToMe = locationOfPointerToMe.getPtr(); |
| if (!ptrToMe) return; |
| switch (ptrToMe->getOperatorType()) |
| { |
| // Only the two BiLogic's use SIB_ and NOP_ |
| case ITM_AND: t = SIB_; f = FAL_; u = NOP_; break; |
| case ITM_OR: t = TRU_; f = SIB_; u = NOP_; break; |
| case ITM_NOT: t = FAL_; f = TRU_; u = UNK_; break; |
| case ITM_IS_TRUE: t = TRU_; f = FAL_; u = FAL_; break; |
| case ITM_IS_FALSE: t = FAL_; f = TRU_; u = FAL_; break; |
| case ITM_IS_UNKNOWN: t = FAL_; f = FAL_; u = TRU_; break; |
| case ITM_IS_NOT_UNKNOWN: t = TRU_; f = TRU_; u = FAL_; break; |
| default: return; |
| } |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( cerr << "## " << ptrToMe->getText(); ) |
| Int32 i = ptrToMe->getArity(); |
| for (; result == NOP_ && i--; ) |
| { |
| ItemExpr *child = ptrToMe->child(i); |
| // If child was eliminated, it must have always evaluated to True |
| if (!child) |
| { |
| result = t; |
| DBGIF( cerr << " # elim/true"; ) |
| } |
| else if (child->getArity() == 0) // unconditional ITM_RETURN_xxx |
| { |
| DBGIF( cerr << " # " << child->getText(); ) |
| switch (child->getOperatorType()) |
| { |
| case ITM_RETURN_TRUE: result = t; break; |
| case ITM_RETURN_FALSE: result = f; break; |
| case ITM_RETURN_NULL: result = u; ucnt++; break; |
| default: break; |
| } |
| } |
| else DBGIF( cerr << " #- " << child->getText(); ) |
| } |
| if (ucnt && result == NOP_) result = UNK_; // BiLogic's only |
| if (result != NOP_) |
| { |
| ItemExpr *replacement; |
| if (result == SIB_) |
| { |
| if (i == 1) i = 0; |
| else if (i == 0) i = 1; |
| else CMPASSERT(FALSE); // see for-loop test |
| replacement = ptrToMe->child(i); |
| } |
| else |
| replacement = new HEAP BoolVal(OperatorTypeEnum(result)); |
| |
| DBGIF( cerr << " ==> " << replacement->getText(); ) |
| |
| ie->getValueId().replaceItemExpr(replacement); |
| replacement->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = replacement; |
| } |
| DBGIF( cerr << endl; ) |
| } // applyTruthTable() |
| |
| // ----------------------------------------------------------------------- |
| // This is a utility for building a tree of predicates that |
| // has a backbone of AND/OR nodes. |
| // ----------------------------------------------------------------------- |
| static ItemExpr * buildPredTree(ItemExpr * rootPtr, ItemExpr * subtreePtr, |
| OperatorTypeEnum logicalConnectiveType = ITM_AND) |
| { |
| ItemExpr * newrootPtr; |
| if (rootPtr) |
| { |
| newrootPtr = new HEAP BiLogic(logicalConnectiveType, rootPtr, subtreePtr); |
| newrootPtr->markAsTransformed(); |
| } |
| else |
| newrootPtr = subtreePtr; |
| // Walk through this new tree, synthesizing the type and |
| // allocating a ValueId for each operator that requires it. |
| newrootPtr->synthTypeAndValueId(); |
| return newrootPtr; |
| } // buildPredTree() |
| |
| // ----------------------------------------------------------------------- |
| // This is a utility for building a tree of comparison predicates. |
| // ----------------------------------------------------------------------- |
| ItemExpr * buildComparisonPred( ItemExpr * rootPtr, |
| ItemExpr * leftSubtreePtr, |
| ItemExpr * rightSubtreePtr, |
| OperatorTypeEnum comparisonOpType = ITM_EQUAL, |
| NABoolean specialNulls=FALSE //++MV - Irena |
| ) |
| { |
| ItemExpr * eqPredPtr = |
| new HEAP BiRelat ( comparisonOpType, |
| leftSubtreePtr, |
| rightSubtreePtr, |
| specialNulls //++MV - Irena |
| ); |
| return buildPredTree(rootPtr, eqPredPtr, ITM_AND); |
| } // buildComparisonPred() |
| |
| inline static Int32 isAColumnOrUserInput(const ItemExpr *ie) |
| { |
| if (((ItemExpr *)ie)->isAColumnReference()) return 10; |
| // VEG on ITM_UNIQUE_EXECUTE_ID is disabled, see triggers team for explanation |
| if (ie->isAUserSuppliedInput() && ie->getOperatorType()!=ITM_UNIQUE_EXECUTE_ID) // -- Triggers |
| return 1; |
| return 0; |
| } |
| |
| |
| // -------------------------------------------------------------------- |
| // |
| // If a recursive/cascaded triggers backbone has a connection to the |
| // triggering generic update the connection is using @old and @new |
| // columns from the triggering generic update. |
| // We found out that equality relation between each two levels of cascaded |
| // triggers might cause the creation of a huge VEGREF that the optimizer |
| // can't handle in a reasonable time. |
| // To prevent this problem we will block the creation of VEGs on equal predicates |
| // involving a @old/ @new column on high cascade levels. |
| // Cascade levels are counted by inBlockedUnionCount_, since every cascaded |
| // trigger backbone top node is a BlockedUnion. |
| // |
| // -------------------------------------------------------------------- |
| inline static Int32 bothAreColumnsOrUserInput( // AND AT LEAST ONE IS A COLUMN |
| NormWA & normWARef, const ItemExpr *i0, const ItemExpr *i1) |
| { |
| Int32 ret = isAColumnOrUserInput(i0); |
| |
| if (ret) |
| { |
| ret += isAColumnOrUserInput(i1); |
| |
| // If both are columns, ret is 20, fine; |
| // if one is a column and the other an input, ret is 11, fine; |
| // if one is a column and the other is not col nor inp, ret is 10, reject; |
| // if one or both are just inputs, ret is 1 or 2, reject. |
| if (ret < 11) ret = 0; |
| |
| } |
| return ret; |
| } |
| |
| // *********************************************************************** |
| // $$$$ ItemExpr |
| // member functions for class ItemExpr |
| // *********************************************************************** |
| |
| // ----------------------------------------------------------------------- |
| // Each operator supports a (virtual) method for transforming a |
| // scalar expression to a canonical form |
| // ----------------------------------------------------------------------- |
| void ItemExpr::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| Int32 arity = getArity(); |
| |
| for (Int32 i = 0; i < arity; i++) |
| child(i)->getReplacementExpr()->transformNode(normWARef, child(i), |
| introduceSemiJoinHere, externalInputs); |
| |
| markAsTransformed(); |
| |
| // Set return value |
| locationOfPointerToMe = this; |
| } // ItemExpr::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // A method for checking whether a predicate eliminates null |
| // augmented rows produced by a left join. |
| // ---------------------------------------------------------------------- |
| NABoolean ItemExpr::predicateEliminatesNullAugmentedRows |
| (NormWA & /* normWARef */, |
| ValueIdSet & /* outerReferences */) |
| { |
| return FALSE; |
| } // ItemExpr::predicateEliminatesNullAugmentedRows() |
| |
| // ----------------------------------------------------------------------- |
| // A method for inverting (finding the inverse of) the operators |
| // in a subtree that is rooted in a NOT. |
| // ---------------------------------------------------------------------- |
| ItemExpr * ItemExpr::transformSubtreeOfNot(NormWA & /* normWARef */, |
| OperatorTypeEnum /* falseOrNot */) |
| { |
| return this; |
| } // ItemExpr::transformSubtreeOfNot() |
| |
| // ----------------------------------------------------------------------- |
| // ItemExpr::initiateLeftToInnerJoinTransformation() |
| // ---------------------------------------------------------------------- |
| ItemExpr * ItemExpr::initiateLeftToInnerJoinTransformation(NormWA & /*normWARef*/) |
| { |
| return getReplacementExpr(); // return the replacement expression |
| } // ItemExpr::initiateLeftToInnerJoinTransformation() |
| |
| // ----------------------------------------------------------------------- |
| // Each operator supports a (virtual) method for transforming a query |
| // tree to a canonical form |
| // ----------------------------------------------------------------------- |
| ItemExpr * ItemExpr::normalizeNode(NormWA & normWARef) |
| { |
| // --------------------------------------------------------------------- |
| // If the expression is column reference (such as a base column, an |
| // index column, a rename column or an instantiate null) or a user |
| // given value (such as a constant, host variable or a parameter) |
| // check whether it is a member of a ValueId Equality Group (VEG). |
| // If so, replace the expression with a wild card expression, |
| // called a VEGReference, which is associated with its VEG. |
| // --------------------------------------------------------------------- |
| if (isAColumnOrUserInput(this)) |
| { |
| // ----------------------------------------------------------------- |
| // Normalize the child. |
| // ----------------------------------------------------------------- |
| Int32 arity = getArity(); |
| |
| for (Int32 i = 0; i < arity; i++) |
| child(i) = child(i)->getReplacementExpr()->normalizeNode(normWARef); |
| |
| // Now do non-recursive part |
| return( normalizeNode2( normWARef ) ); |
| } |
| // --------------------------------------------------------------------- |
| // Normalize each child subtree. |
| // --------------------------------------------------------------------- |
| else |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| |
| if ((NOT normWARef.isInJoinPredicate()) || |
| (CmpCommon::getDefault(COMP_BOOL_124) == DF_ON)) |
| { |
| markAsNormalized(); |
| Int32 arity = getArity(); |
| for (Int32 i = 0; i < arity; i++) |
| child(i) = child(i)->getReplacementExpr()->normalizeNode(normWARef); |
| } |
| else |
| { |
| Int32 arity = getArity(); |
| ItemExpr *newExpr = this->copyTopNode(NULL,CmpCommon::statementHeap()); |
| |
| for (Int32 i = 0; i < arity; i++) |
| newExpr->setChild(i,child(i)->getReplacementExpr()->normalizeNode(normWARef)); |
| newExpr->setReplacementExpr(newExpr); |
| |
| newExpr->synthTypeAndValueId(); |
| newExpr->markAsNormalized(); |
| |
| return newExpr->getReplacementExpr(); |
| } |
| } |
| |
| return getReplacementExpr(); |
| |
| } // ItemExpr::normalizeNode() |
| |
| |
| // |
| // normalizeNode2() - a helper routine for ItemExpr::normalizeNode() |
| // |
| // NOTE: The code in this routine came from the previous version of |
| // ItemExpr::normalizeNode(). It has been pulled out into a separate |
| // routine so that the C++ compiler will produce code that needs |
| // signficantly less stack space for the recursive |
| // ItemExpr::normalizeNode() routine. |
| // |
| ItemExpr * ItemExpr::normalizeNode2(NormWA & normWARef) |
| { |
| // ----------------------------------------------------------------- |
| // If this expression is a member of a ValueId Equality Group |
| // (VEG), then replace it with a reference to the VEG. |
| // ----------------------------------------------------------------- |
| ItemExpr * vegrefPtr = normWARef.getVEGReference(getValueId()); |
| // we either got a VEGRef or an ITM_CONSTANT back |
| if (vegrefPtr) |
| // return without setting the replacement expression |
| // the same valueid could map to different VEGRefs in different |
| // regions |
| return vegrefPtr; |
| else |
| return getReplacementExpr(); // returns this |
| } |
| |
| // |
| // This method is invoked from inside a ROWS SINCE operator. |
| // Traverses tree looking for THIS functions. |
| // |
| void ItemExpr::transformNotTHISFunction() |
| { |
| Int32 arity = getArity(); |
| |
| for (Int32 i = 0; i < arity; i++) |
| { |
| if (child(i)->containsTHISFunction()) |
| { |
| if (child(i)->getOperatorType() != ITM_THIS) |
| { |
| child(i)->transformNotTHISFunction(); |
| } |
| } |
| else |
| { |
| ItmSeqNotTHISFunction *newChild = new HEAP ItmSeqNotTHISFunction (child(i)); |
| newChild->synthTypeAndValueId(TRUE); |
| child(i) = newChild; |
| // child(i)->getValueId().replaceItemExpr(newChild); |
| // setReplacementExpr(newChild); |
| } |
| } |
| } // ItemExpr::transformNotTHISFunction() |
| |
| // |
| // Virtual function that allows the above NOT THIS transformation |
| // Redefined for ItmSequenceFunction classes |
| |
| NABoolean ItemExpr::containsTHISFunction() |
| { |
| Int32 arity = getArity(); |
| NABoolean result = FALSE; |
| |
| // -------------------------------------------------------- |
| // This check cannot exit early, because it also must |
| // perform an illegal nesting check on all the children. |
| // -------------------------------------------------------- |
| for (Int32 i = 0; i < arity; i++) |
| { |
| if (child(i)->containsTHISFunction()) |
| result = TRUE; |
| } |
| return result; |
| } // ItemExpr::containsThis() |
| |
| |
| NABoolean ItemExpr::canTransformToSemiJoin(ItemExprList& valuesListIE, |
| TableDesc* tdesc, Lng32& numParams, |
| ValueId& colVid, CollHeap* h) const |
| { |
| valuesListIE.clear(); |
| |
| if (ActiveSchemaDB()->getDefaults().getAsLong(OR_PRED_TO_SEMIJOIN)== 0) |
| return FALSE ; // feature is turned OFF |
| |
| ItemExpr *predIE = (ItemExpr *) this; |
| if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) |
| predIE = revertBackToOldTree(h, (ItemExpr *)this); |
| |
| if (predIE->getOperatorType() != ITM_OR) // If its not an OR there is nothing to transform |
| return FALSE; // may need to relax this for anti-semi-join NOT(OR ...) |
| |
| ValueIdList eqList; |
| NABoolean status = predIE->convertToValueIdList(eqList,NULL, ITM_OR); |
| |
| if(status) |
| return FALSE; |
| |
| ((BiLogic *) predIE)->setNumLeaves(eqList.entries()); |
| ValueIdSet noCharacteristicInputs, dummy1, dummy2, dummy3; // empty sets |
| GroupAttributes scanGA; |
| NABoolean isCovered = FALSE; |
| BiRelat * eqExpr = NULL; |
| ItemExpr * rightChild = NULL; |
| ItemExpr * colExpr = NULL; |
| ValueId leftChildId; |
| Convert * cnv = NULL; |
| NABuiltInTypeEnum rcTypeEnum = NA_UNKNOWN_TYPE; |
| |
| if (eqList[0].getItemExpr()->getOperatorType() == ITM_EQUAL) |
| { |
| scanGA.addCharacteristicOutputs(tdesc->getColumnList()); |
| eqExpr = (BiRelat *) eqList[0].getItemExpr() ; |
| colExpr = eqExpr->child(0).getPtr(); |
| rightChild = eqExpr->child(1).getPtr(); |
| |
| isCovered = colExpr->isCovered(noCharacteristicInputs, |
| scanGA, |
| dummy1, |
| dummy2, |
| dummy3); |
| |
| // check if left side of '=' is covered by columns of TableDesc |
| // 'a', 'a+1", 'a+b' are all allowed expressions. |
| if (isCovered) |
| leftChildId = colExpr->getValueId(); |
| else |
| return FALSE; |
| |
| rcTypeEnum = (rightChild->getValueId()).getType().getTypeQualifier(); |
| if (rcTypeEnum == NA_UNKNOWN_TYPE) |
| return FALSE; |
| } |
| |
| numParams = 0; |
| for (CollIndex i = 0; i < eqList.entries(); i++) |
| { |
| if (eqList[i].getItemExpr()->getOperatorType() != ITM_EQUAL) |
| return FALSE; |
| |
| eqExpr = (BiRelat *) eqList[i].getItemExpr() ; |
| if (eqExpr->child(0)->getValueId() != leftChildId) |
| return FALSE; // left side of '=' has different expressions, a = 1 OR b = 1 |
| rightChild = eqExpr->child(1).getPtr(); |
| if (rcTypeEnum != (rightChild->getValueId()).getType().getTypeQualifier()) |
| return FALSE; // right side of '=' has to be compatible |
| |
| if (!(rightChild->doesExprEvaluateToConstant(FALSE,TRUE))) |
| return FALSE; // right side of '=' must be nonstrict constant (we also look inside VEG) |
| if (!(rightChild->doesExprEvaluateToConstant(TRUE,TRUE))) // look for hostvars or params |
| numParams++; // look for strict constants so that we can keep track of hostvars/params |
| |
| if (rightChild->getOperatorType() == ITM_VEG_REFERENCE) |
| { |
| // if we have a VEG on the right side, get the underlysing constant |
| // we don't want to put a VEG in the tupleExprTree_ as this causes an |
| // abort when the tupleExprTree_ is copied in later phases. A VEGRef cannot |
| // be currently copied with copyTopNode. |
| const VEG * refVEG = ((VEGReference *)rightChild)->getVEG(); |
| rightChild = (refVEG->getAConstantHostVarOrParameter()).getItemExpr(); |
| } |
| |
| cnv = new(h) Convert(rightChild); |
| cnv->synthTypeAndValueId(); |
| valuesListIE.insert(cnv); |
| } |
| colVid = colExpr->getValueId(); |
| return TRUE; |
| } |
| |
| |
| // *********************************************************************** |
| // $$$$ Aggregate |
| // member functions for class Aggregate |
| // *********************************************************************** |
| void Aggregate::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| // --------------------------------------------------------------------- |
| // Transform the operands of the Aggregate |
| // --------------------------------------------------------------------- |
| normWARef.setNullFlag(); |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| normWARef.restoreNullFlag(); |
| |
| // --------------------------------------------------------------------- |
| // If this aggregate function is not sensitive to duplicate values |
| // (like MIN, MAX) then a DISTINCT in this aggregate is unnecessary, |
| // so transform, for example, MAX(DISTINCT x) into MAX(x) |
| // --------------------------------------------------------------------- |
| if (!isSensitiveToDuplicates()) |
| setDistinct(FALSE); |
| |
| // Set return value |
| locationOfPointerToMe = this; |
| } // Aggregate::transformNode() |
| |
| ItemExpr * Aggregate::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| markAsNormalized(); |
| |
| // Set the null flag if we care about null values |
| normWARef.setNullFlag(); |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| |
| if (isDistinct()) |
| distinctId_ = distinctId_.getItemExpr()->getReplacementExpr()-> |
| normalizeNode(normWARef)->getValueId(); |
| |
| normWARef.restoreNullFlag(); |
| |
| return this; |
| } // Aggregate::normalizeNode() |
| |
| // *********************************************************************** |
| // $$$$ Assign |
| // member functions for class Assign |
| // *********************************************************************** |
| ItemExpr * Assign::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| markAsNormalized(); |
| |
| // Only the source needs to be normalized |
| child(1) = child(1)->getReplacementExpr()->normalizeNode(normWARef); |
| |
| return this; |
| } |
| |
| |
| |
| |
| |
| // *************************************************************************** |
| // $$$$ Between |
| // member functions for class Between |
| // *************************************************************************** |
| |
| //---------------------------------------------------------------------------- |
| //++ MV OZ |
| void |
| Between::setDirectionVector(const IntegerList & directionVector, |
| CollHeap* heap) |
| { |
| CMPASSERT(0 != directionVector.entries() && NULL != heap); |
| |
| pDirectionVector_ = new (heap)IntegerList(heap); |
| *pDirectionVector_ = directionVector; |
| } |
| |
| |
| //---------------------------------------------------------------------------- |
| ItemExpr * Between::transformIntoTwoComparisons() |
| { |
| ItemExpr *tfm = NULL; |
| OperatorTypeEnum leftBoundryOp = ITM_GREATER; |
| OperatorTypeEnum rightBoundryOp = ITM_LESS; |
| ItemExpr *leftVal = child(0).getPtr(); |
| ItemExpr *startVal = child(1).getPtr(); |
| ItemExpr *endVal = child(2).getPtr(); |
| NABoolean optimizeForEquals = (leftBoundryIncluded_ AND |
| rightBoundryIncluded_ AND |
| pDirectionVector_ == NULL); |
| |
| |
| // THE DEFAULT BEHAVIOUR |
| if (TRUE == leftBoundryIncluded_) |
| { |
| leftBoundryOp = ITM_GREATER_EQ; |
| } |
| |
| if (TRUE == rightBoundryIncluded_) |
| { |
| rightBoundryOp = ITM_LESS_EQ; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Try to optimize the case "a between x and x" and produce an |
| // equals predicate instead, do this first for prefixes of multi- |
| // valued between predicates like (a,b,c) between (1,1,2) and (1,1,4) |
| // --------------------------------------------------------------------- |
| if (optimizeForEquals AND |
| leftVal->getOperatorType() == ITM_ITEM_LIST) |
| { |
| ItemExprList leftList(leftVal, HEAP); |
| ItemExprList startList(startVal, HEAP); |
| ItemExprList endList(endVal, HEAP); |
| |
| while (leftList.entries() > 0 AND |
| startList[0] == endList[0]) |
| { |
| ItemExpr *eqPred = |
| new HEAP BiRelat(ITM_EQUAL, |
| leftList[0], |
| startList[0]); |
| if (tfm) |
| tfm = new HEAP BiLogic(ITM_AND, tfm, eqPred); |
| else |
| tfm = eqPred; |
| |
| leftList.removeAt(0); |
| startList.removeAt(0); |
| endList.removeAt(0); |
| } |
| |
| if (tfm) |
| { |
| // we took care of a prefix of the comparisons, |
| // make a new list without those equal values |
| if (leftList.entries() > 0) |
| { |
| leftVal = leftList.convertToItemExpr(); |
| startVal = startList.convertToItemExpr(); |
| endVal = endList.convertToItemExpr(); |
| } |
| else |
| leftVal = startVal = endVal = NULL; |
| } |
| } |
| |
| if (leftVal) |
| { |
| // --------------------------------------------------------------------- |
| // Replace col between val1 and val2 with |
| // col >= val1 and col <= val2 |
| // NOTE: This transformation assumes the ascii collating sequence. |
| // When other collations are supported, the transformation |
| // rule will have to be different. |
| // --------------------------------------------------------------------- |
| |
| BiRelat * leftCondition = |
| new HEAP BiRelat(leftBoundryOp, leftVal, startVal); |
| |
| BiRelat * rightCondition = |
| new HEAP BiRelat(rightBoundryOp, leftVal, endVal); |
| |
| |
| // NOTE: we copy the pointer as is. it should probably have been marked |
| // as const but there was to much damn code to change so I gave up. |
| //++ MV OZ |
| leftCondition->setDirectionVector((IntegerList *)pDirectionVector_); |
| rightCondition->setDirectionVector((IntegerList *)pDirectionVector_); |
| |
| |
| ItemExpr *noneqPreds = new HEAP BiLogic(ITM_AND, leftCondition, rightCondition); |
| |
| if (tfm) |
| tfm = new HEAP BiLogic(ITM_AND, tfm, noneqPreds); |
| else |
| tfm = noneqPreds; |
| } |
| |
| CMPASSERT(tfm); |
| return tfm; |
| } |
| |
| ItemExpr * Between::transformMultiValuePredicate( |
| NABoolean flattenSubqueries, |
| ChildCondition condBiRelat) |
| { |
| ItemExpr *tfm = transformIntoTwoComparisons()-> |
| transformMultiValuePredicate(flattenSubqueries, condBiRelat); |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| if (tfm) { |
| unp = ""; |
| tfm->unparse(unp); |
| cerr << "Between MVP: " << unp << " " << (void*)this << endl; |
| } |
| ) |
| |
| return tfm; |
| } |
| |
| void Between::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| unparse(unp); |
| cerr << (Int32)getOperatorType() << " " |
| << (Int32)getValueId() << " " |
| << (void *)this << " " |
| << unp << endl; |
| ) |
| |
| // --------------------------------------------------------------------- |
| // Transform the operands of the Between |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| ItemExpr *tfm = transformIntoTwoComparisons(); |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| if(isSelectivitySetUsingHint()) |
| { |
| double newSelFactor = sqrt(getSelectivityFactor()); |
| |
| locationOfPointerToMe->child(0)->setSelectivitySetUsingHint(); |
| locationOfPointerToMe->child(0)->setSelectivityFactor(newSelFactor); |
| |
| locationOfPointerToMe->child(1)->setSelectivitySetUsingHint(); |
| locationOfPointerToMe->child(1)->setSelectivityFactor(newSelFactor); |
| } |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // Between::transformNode() |
| |
| ItemExpr * Between::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| ItemExpr *tfm = transformIntoTwoComparisons(); |
| |
| // Do not synthTypeAndValueId() yet |
| return tfm->transformSubtreeOfNot(normWARef,falseOrNot); |
| } // Between::transformSubtreeOfNot() |
| |
| // *********************************************************************** |
| // $$$$ BuiltinFunction |
| // member functions for class BuiltinFunction |
| // *********************************************************************** |
| void BuiltinFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| // Indicate that we are processing a complex scalar expression. |
| normWARef.setComplexScalarExprFlag(); |
| // --------------------------------------------------------------------- |
| // Transform the operands of the Builtinfunction. |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| normWARef.restoreComplexScalarExprFlag(); |
| |
| } // BuiltinFunction::transformNode() |
| |
| ItemExpr * BuiltinFunction::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| |
| // Indicate that we are processing a complex scalar expression. |
| normWARef.setComplexScalarExprFlag(); |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the Builtinfunction. |
| // --------------------------------------------------------------------- |
| // Must call the base class version of normalizeNode. We cannot |
| // call normalizeNode on the child directly. This is because |
| // a builtin function node can be a "columnOrUserSuppliedInput". Only |
| // the base class version has code for this. We could duplicate it |
| // here, but then that would be duplicated code. |
| ItemExpr * normalizedExpr = ItemExpr::normalizeNode(normWARef); |
| normWARef.restoreComplexScalarExprFlag(); |
| |
| return normalizedExpr; |
| |
| } // BuiltinFunction::normalizeNode() |
| |
| |
| // *********************************************************************** |
| // UDFunction |
| // member functions for class UDFunction |
| // *********************************************************************** |
| void UDFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| // Return the address of the expression that was used for replacing |
| // this subquery in an earlier call. |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // Indicate that we are processing a complex scalar expression. |
| normWARef.setComplexScalarExprFlag(); |
| |
| |
| // Want to transform our inputs before we transform ourselves. |
| // This maintains a tree were the UDF is on the right side of a RoutineJoin. |
| if (inputVars_.transformNode(normWARef, introduceSemiJoinHere, |
| externalInputs)) |
| { |
| introduceSemiJoinHere->transformNode(normWARef, introduceSemiJoinHere); |
| CMPASSERT( introduceSemiJoinHere->nodeIsTransformed()); |
| } |
| |
| |
| // --------------------------------------------------------------------- |
| // Transform the UDFunction ItemExpr into a RoutineJoin and an |
| // IsolatedScalarUDF |
| // --------------------------------------------------------------------- |
| transformToRelExpr(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| markAsTransformed(); |
| |
| setReplacementExpr(locationOfPointerToMe); |
| |
| normWARef.restoreComplexScalarExprFlag(); |
| |
| } // UDFunction::transformNode() |
| |
| |
| |
| // ----------------------------------------------------------------------- |
| // UDFunction::transformToRelExpr() |
| // Perform the UDFunction -> RoutineJoin + IsolatedScalarUDF transformation |
| // ----------------------------------------------------------------------- |
| void UDFunction::transformToRelExpr(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| return; |
| markAsTransformed(); |
| |
| const RoutineDesc *rdesc = getRoutineDesc(); |
| |
| CMPASSERT(rdesc); // Make sure we actually have one.. |
| |
| if (rdesc->getEffectiveNARoutine() != NULL && |
| (!rdesc->getEffectiveNARoutine()->isIsolate())) |
| return; // Don't want to transform trusted Functions to RelExprs. |
| |
| CMPASSERT(getOperatorType() == ITM_USER_DEF_FUNCTION); |
| |
| // This is going to work very similar to what we do for subqueries. |
| // We are going to create a RoutineJoin, and an appropriate right child |
| // the join. For starters it will be an IsolatedScalarUDF RelExpr. |
| |
| |
| RelExpr *newRightChild; |
| // ValueIdSet inputValues; |
| ValueIdSet leafInputValues; |
| GroupAttributes emptyGA; |
| |
| // We use the dummyOutputs when we generate the UDF relexpr |
| // The real outputs of the routine will be assigned below. |
| ValueIdList dummyOutputs; |
| |
| switch (getRoutineDesc()->getEffectiveNARoutine()->getRoutineType()) |
| { |
| case COM_SCALAR_UDF_TYPE: |
| case COM_UNIVERSAL_UDF_TYPE: |
| case COM_ACTION_UDF_TYPE: |
| { |
| |
| ItemExprList *params = new(normWARef.wHeap()) |
| ItemExprList(normWARef.wHeap()); |
| |
| Int32 arity = getArity(); |
| |
| IsolatedScalarUDF *isUdf; |
| |
| for (Int32 i=0; i< arity; i++) |
| { |
| params->insertAt(i, child(i)->castToItemExpr()); |
| } |
| |
| isUdf = new(normWARef.wHeap()) |
| IsolatedScalarUDF( |
| getRoutineDesc()->getEffectiveNARoutine()->getSqlName(), |
| params, dummyOutputs, normWARef.wHeap()); |
| |
| |
| delete params; // We don't need this list any more |
| |
| // We want to make make sure we mark the node as bound as some of |
| // the RelRoutine methods behave different if the node is bound or not. |
| isUdf->markAsBound(); |
| |
| // The routine desc points to both the NARoutine structs. |
| isUdf->setRoutineDesc(udfDesc_); |
| isUdf->getGroupAttr()->setHasNonDeterministicUDRs( |
| !getRoutineDesc()->getEffectiveNARoutine()->isDeterministic()); |
| |
| // Get the valueIds of the parameters |
| isUdf->gatherParamValueIds(isUdf->getProcAllParamsTree(), |
| isUdf->getProcAllParamsVids()); |
| |
| // Clear out the params Tree, we don't need it anymore. |
| isUdf->setProcAllParamsTree(NULL); |
| |
| // For UDFs all parameters are inputs |
| |
| isUdf->getProcInputParamsVids() = isUdf->getProcAllParamsVids(); |
| |
| |
| // The inputVars_ do not have the casts that we create |
| // at bind time for compatible types. Use it for our actual inputs. |
| leafInputValues = inputVars_; |
| |
| |
| // The characteristic outputs of the IsolatedScalarUDF RelExpr will |
| // be that of the return parameters of the UDFunction |
| // |
| |
| newRightChild = isUdf; |
| |
| break; |
| } |
| case COM_TABLE_UDF_TYPE: |
| case COM_UNKNOWN_ROUTINE_TYPE: |
| default: |
| { |
| *CmpCommon::diags() << DgSqlCode(2997) |
| << DgString1("Unsupported Function type"); |
| |
| return ; |
| break; |
| } |
| |
| } |
| |
| |
| ItemExpr *replacementExpr = NULL; |
| |
| |
| if (normWARef.inValueIdProxy()) |
| { |
| // We have already split the output of the MVF earlier and |
| // represented them with ValueIdProxies. This happens when the UDF |
| // is part of select lists or equivalent (set clause in update etc) |
| // and we only need to associate the valueId for the UDF with the |
| // first of its output. |
| replacementExpr = udfDesc_->getOutputColumnList()[0].getItemExpr(); |
| } |
| else |
| { |
| replacementExpr = udfDesc_->getOutputColumnList().rebuildExprTree( |
| ITM_ITEM_LIST,FALSE,FALSE); |
| } |
| |
| |
| // Pass along what the function needs to be replaced with. |
| locationOfPointerToMe = replacementExpr; |
| |
| // Replace the itemExpr the valueId for UDFunction points to |
| // to point to the output of the ScalarIsolatedUDF relexpr. |
| |
| ValueId oldOutId = replacementExpr->getValueId(); |
| getValueId().replaceItemExpr(replacementExpr); |
| |
| // Update the valuedId we know the output column as in the RoutineDesc. |
| // This will now be the new output value for the RelRoutine |
| |
| udfDesc_->getOutputColumnList().substituteValueIds(oldOutId, getValueId()); |
| |
| Join *newJoin; |
| |
| newJoin = new(normWARef.wHeap()) |
| Join(introduceSemiJoinHere, newRightChild, REL_ROUTINE_JOIN); |
| newJoin->setGroupAttr(new(normWARef.wHeap()) GroupAttributes()); |
| |
| |
| ValueIdSet outputValues; |
| ValueIdSet outerReferences; |
| |
| // ------------------------------------------------------------- |
| // Setup the characteristic inputs and outputs for the IsolatedScalarUDF node |
| // ------------------------------------------------------------- |
| newRightChild->getPotentialOutputValues(outputValues); |
| newRightChild->getGroupAttr()->addCharacteristicOutputs(outputValues); |
| newRightChild->getGroupAttr()->addCharacteristicInputs(leafInputValues); |
| |
| // ------------------------------------------------------------- |
| // Now minimize the characteristic inputs for the IsolatedScalarUDF node |
| // This will weed out any constants, etc that doesn't need to flow... |
| // ------------------------------------------------------------- |
| |
| newRightChild->getGroupAttr()->getCharacteristicInputs(). |
| getOuterReferences(outerReferences); |
| newRightChild->getGroupAttr()->setCharacteristicInputs(outerReferences); |
| |
| // --------------------------------------------------------------------- |
| // Initialize the dataflow for the tuple-substitution join that was |
| // allocated above. |
| // --------------------------------------------------------------------- |
| newJoin->getPotentialOutputValues(outputValues); |
| newJoin->getGroupAttr()->addCharacteristicOutputs(outputValues); |
| |
| // subtract from the external inputs to the joins values that are |
| // produced by the children of the join. |
| ValueIdSet realExternalInputs = externalInputs; |
| realExternalInputs -= outputValues; |
| newJoin->getGroupAttr()->addCharacteristicInputs(realExternalInputs); |
| |
| newJoin->child(1)->getGroupAttr()->addCharacteristicInputs(externalInputs); |
| newJoin->child(1)->getGroupAttr()->addCharacteristicInputs |
| (newJoin->child(0)->getGroupAttr()->getCharacteristicOutputs()); |
| |
| |
| introduceSemiJoinHere = newJoin; |
| |
| |
| } // UDFunction::transformToRelExpr() |
| |
| |
| ItemExpr * UDFunction::normalizeNode(NormWA & normWARef) |
| { |
| // Should never get here as we transform all UDFunctions. However, if we do |
| // that means that we missed one somewhere... |
| CMPASSERT(0); |
| |
| return NULL; |
| |
| } // UDFunction::normalizeNode() |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class InstantiateNull |
| // ----------------------------------------------------------------------- |
| void InstantiateNull::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| // Indicate that we are processing a complex scalar expression. |
| normWARef.setComplexScalarExprFlag(); |
| // --------------------------------------------------------------------- |
| // Transform the operands of the Instantiate Null. |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| normWARef.restoreComplexScalarExprFlag(); |
| } // InstantiateNull::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // InstantiateNull::initiateLeftToInnerJoinTransformation() |
| // For each node in the tree of the form |
| // InstantiateNull(InstantiateNull(InstantiateNull(X))) |
| // 1) Mark the Zone where the value is first produced as "To Be Merged". |
| // 2) Recurse on InstantiateNulls |
| // 3) Return a pointer to X. |
| // ----------------------------------------------------------------------- |
| ItemExpr * InstantiateNull::initiateLeftToInnerJoinTransformation(NormWA & normWARef) |
| { |
| if (!NoCheckforLeftToInnerJoin) |
| { |
| // --------------------------------------------------------------------- |
| // Locate the VEGRegion that provides the data that is null-instantiated |
| // by this InstantiateNull and mark it as "To Be Merged". |
| // --------------------------------------------------------------------- |
| normWARef.locateVEGRegionAndMarkToBeMergedRecursively(getValueId()); |
| |
| // --------------------------------------------------------------------- |
| // Recursively visit the children of the InstantiateNull. |
| // --------------------------------------------------------------------- |
| setReplacementExpr(getExpr() |
| ->initiateLeftToInnerJoinTransformation(normWARef)); |
| |
| //---------------------------------------------------------------------- |
| //10-040116-2480: Mark this InstantiateNull as in transition to go away |
| // at the end of Transformation. |
| //---------------------------------------------------------------------- |
| beginLOJTransform_ = TRUE; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // 10-031023-0723: do not return the replacement expression yet. The |
| // transformation gets completed in the normalization. This is causing |
| // certain predicates not to be pulled up properly. |
| //---------------------------------------------------------------------- |
| return this; |
| |
| } // ItemExpr::initiateLeftToInnerJoinTransformation() |
| |
| //****************************************************** |
| // ItemExpr * InstantiateNull::getReplacementExpr() |
| // If this instantiate null is going away (Ouetr Join is |
| // becoming an inner join) then during the transformation |
| // phase, we want to return the Instantantiate Null operator |
| // But after transformation phase we want to return the |
| // actual replacement expression. |
| // |
| // Otherwise, certain operators may have |
| // references to the instantiate null (outer references) |
| // and they may not know anything |
| // about replacement expression until the transformation |
| // is complete. |
| //****************************************************** |
| ItemExpr * InstantiateNull::getReplacementExpr() const |
| { |
| InstantiateNull * iNull= (InstantiateNull * const) this; |
| |
| if (beginLOJTransform_) |
| return iNull; |
| |
| ItemExpr* ie=ItemExpr::getReplacementExpr(); |
| |
| if ((ItemExpr *)iNull == ie) |
| return ie; |
| |
| while (ie != ie->getReplacementExpr() && |
| ie->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| ie=ie->getReplacementExpr(); |
| } |
| |
| return ie; |
| } // InstantiateNull::getReplacementExpr() |
| |
| //****************************************************** |
| // Soln: 10-060105-3714 |
| // This is a new overloaded function introduced to |
| // remove NESTED Null Instantiates. We Introduce multiple |
| // InstantiatedNull Nodes from the binder via |
| // ValueId::nullInstantiate(...). Removing this additional |
| // nodes when they are initially added in the binder |
| // resulted in lots of side-effects, so instead of that we |
| // had chosen to remove it in normalization, when we are |
| // sure that we no longer need them. It is currently |
| // (at the time the call was written) used in |
| // VEGRegion::replaceInstantiateNullMembers() method. |
| //****************************************************** |
| ItemExpr * InstantiateNull::getReplacementExpr(NABoolean forceNavigate) const |
| { |
| InstantiateNull * iNull= (InstantiateNull * const) this; |
| |
| if (NOT forceNavigate) |
| return iNull->getReplacementExpr(); |
| |
| ItemExpr* ie=ItemExpr::getReplacementExpr(); |
| |
| if ((ItemExpr *)iNull == ie) |
| return ie; |
| |
| if(ie->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| iNull = (InstantiateNull * const)ie; |
| ie = iNull->getReplacementExpr(TRUE); |
| } |
| |
| return ie; |
| } // InstantiateNull::getReplacementExpr(NABoolean) |
| |
| ItemExpr * InstantiateNull::normalizeNode(NormWA & normWARef) |
| { |
| // --------------------------------------------------------------------- |
| // During the transformation phase of normalization the normalizer |
| // can decide to convert a Left Join to an Inner Join. When this |
| // happens, each InstantiateNull operator that is produced by such |
| // a Left Join must be replaced with the expression for which it will |
| // instantiate a null. The normalizer makes the latter expression the |
| // "replacement expression" for the InstantiateNull and uses it for |
| // replacing the InstantiateNull. The replacementExpr is nominally |
| // set to the "this" pointer for an operator. |
| // --------------------------------------------------------------------- |
| |
| #if 0 |
| // --------------------------------------------------------------------- |
| // Try to rewrite null-inst into VEGRef. Unfortunately, there seems to |
| // be a lot of code in histogram/costing which relies on null-inst |
| // values being not rewritten into VEGRef. So, comment out until more |
| // time is allowed to understand and resolve the impact of doing this. |
| // --------------------------------------------------------------------- |
| if (nodeIsNormalized()) |
| { |
| ItemExpr * vegrefPtr = normWARef.getVEGReference(getValueId()); |
| |
| // we either got a VEGRef or an ITM_CONSTANT back |
| if (vegrefPtr) |
| // return without setting the replacement expression |
| // the same valueid could map to different VEGRefs in different |
| // regions |
| return vegrefPtr; |
| else |
| // return this |
| return getReplacementExpr(); |
| } |
| #endif |
| |
| beginLOJTransform_=FALSE; |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| markAsNormalized(); |
| |
| // Indicate that we are processing a complex scalar expression. |
| normWARef.setComplexScalarExprFlag(); |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| normWARef.restoreComplexScalarExprFlag(); |
| |
| // Left Join eliminated. |
| // Notes by PK, 24MAR00: |
| // Calling setReplacementExpr should not be necessary, as the |
| // method initiateLeftToInnerJoinTransformation should have |
| // already set the replacement expression correctly. |
| // This code probably just sets the replacement expr to what |
| // it already was. This code should be removed. |
| if (getReplacementExpr() != this) |
| setReplacementExpr(child(0).getPtr()); |
| |
| #if 0 |
| // --------------------------------------------------------------------- |
| // Try to rewrite null-inst into VEGRef. Unfortunately, there seems to |
| // be a lot of code in histogram/costing which relies on null-inst |
| // values being not rewritten into VEGRef. So, comment out until more |
| // time is allowed to understand and resolve the impact of doing this. |
| // --------------------------------------------------------------------- |
| else |
| { |
| ItemExpr * vegrefPtr = normWARef.getVEGReference(getValueId()); |
| |
| // we either got a VEGRef or an ITM_CONSTANT back |
| if (vegrefPtr) |
| // return without setting the replacement expression |
| // the same valueid could map to different VEGRefs in different |
| // regions |
| return vegrefPtr; |
| else |
| // return this |
| return getReplacementExpr(); |
| } |
| #endif |
| |
| return getReplacementExpr(); |
| } // InstantiateNull::normalizeNode() |
| |
| // *********************************************************************** |
| // $$$$ BoolVal |
| // member functions for class BoolVal |
| // *********************************************************************** |
| |
| // ----------------------------------------------------------------------- |
| // Perform an MDAM tree walk on BoolVal |
| // ----------------------------------------------------------------------- |
| DisjunctArray * BoolVal::mdamTreeWalk() |
| { |
| DisjunctArray * disjunctArray = new HEAP DisjunctArray |
| (new HEAP ValueIdSet(getValueId())); |
| return disjunctArray; |
| } // BoolVal::mdamTreeWalk() |
| |
| |
| // *********************************************************************** |
| // $$$$ BiLogic |
| // member functions for class BiLogic |
| // *********************************************************************** |
| ItemExpr * BiLogic::transformMultiValuePredicate( |
| NABoolean flatten, |
| ChildCondition condBiRelat) |
| { |
| ItemExpr *t0 = child(0)->transformMultiValuePredicate(flatten, condBiRelat); |
| ItemExpr *t1 = child(1)->transformMultiValuePredicate(flatten, condBiRelat); |
| |
| if(t0 && ( t0->getOperatorType() == ITM_AND)) |
| { |
| t0->synthTypeAndValueId(TRUE); |
| // check for the added prefix predicate |
| ItemExpr * leftChildOfAND = (ItemExpr *)t0->child(0); |
| |
| if((leftChildOfAND->isARangePredicate()) && |
| ((BiRelat *)leftChildOfAND)->isDerivedFromMCRP()) |
| { |
| BiRelat * addedComparison = (BiRelat *) leftChildOfAND; |
| addedComparison->translateListOfComparisonsIntoValueIds(); |
| } |
| } |
| |
| if(t1 && ( t1->getOperatorType() == ITM_AND)) |
| { |
| t1->synthTypeAndValueId(TRUE); |
| // check for the added prefix predicate |
| ItemExpr * leftChildOfAND = (ItemExpr *)t1->child(0); |
| |
| if((leftChildOfAND->isARangePredicate()) && |
| ((BiRelat *)leftChildOfAND)->isDerivedFromMCRP()) |
| { |
| BiRelat * addedComparison = (BiRelat *) leftChildOfAND; |
| addedComparison->translateListOfComparisonsIntoValueIds(); |
| } |
| } |
| |
| // Let caller do any retry necessary |
| if (!t0 && !t1) return NULL; |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| if (child(0)) child(0)->unparse(unp); |
| cerr << "BiLogic c0: " << (Int32)condBiRelat << " " << unp << " " << endl; |
| unp = ""; |
| if (child(1)) child(1)->unparse(unp); |
| cerr << "BiLogic c1: " << (Int32)condBiRelat << " " << unp << " " << endl; |
| unp = ""; |
| if (t0) t0->unparse(unp); |
| cerr << "BiLogic t0: " << (Int32)condBiRelat << " " << unp << " " << endl; |
| unp = ""; |
| if (t1) t1->unparse(unp); |
| cerr << "BiLogic t1: " << (Int32)condBiRelat << " " << unp << " " << endl; |
| ) |
| |
| OperatorTypeEnum op = ITM_AND; |
| |
| if (condBiRelat == ANY_CHILD_RAW) |
| { |
| if (!t0) return t1; |
| if (!t1) return t0; |
| // If BOTH BiRelats returned a raw-transform of any of their children, |
| // fall thru, returning tfm, the explicit AND of these two tfms. |
| } |
| else |
| { |
| // Retry any untransformed non-raw children in raw fashion; |
| // if there is a raw tfm, then the child is (itself AND raw-tfm). |
| if (!t0 && condBiRelat == ANY_CHILD) |
| { |
| t0 = child(0)->transformMultiValuePredicate(flatten, ANY_CHILD_RAW); |
| if (t0) t0 = new HEAP BiLogic(ITM_AND, t0, child(0)); |
| } |
| if (!t0) t0 = child(0); |
| |
| if (!t1 && condBiRelat == ANY_CHILD) |
| { |
| t1 = child(1)->transformMultiValuePredicate(flatten, ANY_CHILD_RAW); |
| if (t1) t1 = new HEAP BiLogic(ITM_AND, t1, child(1)); |
| } |
| if (!t1) t1 = child(1); |
| |
| op = getOperatorType(); |
| } |
| |
| ItemExpr *tfm = new HEAP BiLogic(op, t0, t1); |
| CMPASSERT(tfm); |
| return tfm; |
| } |
| |
| void BiLogic::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| unparse(unp); |
| cerr << (Int32)getOperatorType() << " " |
| << (Int32)getValueId() << " " |
| << (void *)this << " " |
| << unp << endl; |
| ) |
| |
| switch(getOperatorType()) |
| { |
| case ITM_AND: |
| { |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| // Eliminate the AND from the tree if either of its children |
| // have been eliminated. |
| if (!child(0).getPtr() && !child(1).getPtr()) |
| locationOfPointerToMe = NULL; |
| else if (!child(0).getPtr()) |
| locationOfPointerToMe = child(1); |
| else if (!child(1).getPtr()) |
| locationOfPointerToMe = child(0); |
| break; |
| } |
| case ITM_OR: |
| { |
| normWARef.setOrFlag(); |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| normWARef.restoreOrFlag(); |
| |
| // for the topmost OR, try to find common subexpressions and |
| // factor those out |
| if (NOT normWARef.haveAnOrAncestor()) |
| { |
| locationOfPointerToMe = applyInverseDistributivityLaw(); |
| getValueId().replaceItemExpr(locationOfPointerToMe); |
| locationOfPointerToMe->synthTypeAndValueId(); |
| // Make sure the new nodes are transformed (note that |
| // unfortunately this may attempt to do the inverse |
| // distributivity law transformation a second time) |
| locationOfPointerToMe->transformNode(normWARef, |
| locationOfPointerToMe, |
| introduceSemiJoinHere, |
| externalInputs); |
| } |
| |
| break; |
| } |
| default: |
| ABORT("Internal error in BiLogic::transformNode() - unknown operator"); |
| break; |
| } |
| |
| applyTruthTable(this, locationOfPointerToMe); |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // BiLogic::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // Apply De-Morgan's Laws. Distribute a NOT over each subtree. |
| // ----------------------------------------------------------------------- |
| ItemExpr * BiLogic::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| ItemExpr * rv = NULL; |
| |
| switch(getOperatorType()) |
| { |
| case ITM_AND: |
| // NOT(A AND B) ==> NOT A OR NOT B |
| rv = new(normWARef.wHeap()) |
| BiLogic(ITM_OR, |
| new(normWARef.wHeap()) |
| UnLogic(falseOrNot, child(0).getPtr()), |
| new(normWARef.wHeap()) |
| UnLogic(falseOrNot, child(1).getPtr())); |
| break; |
| case ITM_OR: |
| // NOT(A OR B) ==> NOT A AND NOT B |
| rv = new(normWARef.wHeap()) |
| BiLogic(ITM_AND, |
| new(normWARef.wHeap()) |
| UnLogic(falseOrNot, child(0).getPtr()), |
| new(normWARef.wHeap()) |
| UnLogic(falseOrNot, child(1).getPtr())); |
| break; |
| default: |
| ABORT("Internal error in BiLogic::InvertNode() unknown op type"); |
| } |
| // Do not synthTypeAndValueId() yet |
| return rv; |
| } // BiLogic::transformSubtreeOfNot() |
| |
| // ----------------------------------------------------------------------- |
| // predicateEliminatesNullAugmentedRows() |
| // The following method determines whether a predicate is capable of |
| // discarding null augmented rows produced by a left join. |
| // ----------------------------------------------------------------------- |
| NABoolean BiLogic::predicateEliminatesNullAugmentedRows(NormWA & normWARef, |
| ValueIdSet & outerReferences) |
| { |
| NABoolean returnValue = FALSE; |
| |
| // Short circuit the non-null rejecting ones. |
| switch(getOperatorType()) |
| { |
| // we could process the OR if we could determine readily that |
| // both sides of the operator would be null rejecting |
| // at the moment that would mean to take a copy of the tree before |
| // allowing the recursion to either child, since the recursion has |
| // sideeffects. |
| // |
| // Leaving it for another day... |
| |
| case ITM_OR: |
| return returnValue; |
| } |
| |
| // ----------------------------------------------------------------- |
| // An InstantiateNull is used by the LeftJoin for instantiating a |
| // null value in place of a value that is produced as output by |
| // its second child. An InstantiateNull that appears in a binary |
| // comparison predicate eliminates null augmented rows. As a |
| // consequence, the LeftJoin becomes an InnerJoin. For example, |
| // having |
| // GB ------> A.x < D.x AND A.x > D.y |
| // | |
| // LJ |
| // / \ |
| // A LJ |
| // / \ |
| // B LJ |
| // / \ |
| // C D |
| // The HAVING clause predicate A.x < D.x AND A.x > D.y will cause all the |
| // LeftJoin (LJ) operators to be transformed to InnerJoins. |
| // ----------------------------------------------------------------- |
| if (!normWARef.walkingAnExprTree() && !normWARef.subqUnderExprTree()) |
| { // does NOT lie within an expression tree |
| // ------------------------------------------------------------- |
| // Initiate the left to inner join transformation. Replace |
| // the InstantiateNull with the original expression that |
| // is the subject for null instantiation. |
| // ------------------------------------------------------------- |
| |
| // |
| // NOTE: Use heap in the following lines, not stack. |
| // We are in a recursive method, so we must keep our stack |
| // requirements to a minimum. |
| // |
| GroupAttributes * emptyGA = new (STMTHEAP) GroupAttributes ; |
| emptyGA->setCharacteristicOutputs(outerReferences); |
| ValueIdSet * emptySet = new (STMTHEAP) ValueIdSet ; |
| ValueIdSet * emptySet1 = new (STMTHEAP) ValueIdSet ; |
| ValueIdSet * coveredExpr = new (STMTHEAP) ValueIdSet ; |
| ValueIdSet * coveredSubExpr = new (STMTHEAP) ValueIdSet ; |
| ValueIdSet * instNullValue = new (STMTHEAP) ValueIdSet ; |
| NABoolean containsOuterReferencesInSelectList = FALSE; |
| for (CollIndex i = 0; i < 2; i++) |
| { |
| if (child(i)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = (InstantiateNull *)child(i)->castToItemExpr(); |
| if ((normWARef.inSelectList())&&!(inst->NoCheckforLeftToInnerJoin)) |
| { |
| instNullValue->insert(inst->getValueId()); |
| emptyGA->coverTest( |
| *instNullValue, |
| *emptySet, |
| *coveredExpr, |
| *emptySet1, |
| coveredSubExpr); |
| if(!coveredExpr->isEmpty()) |
| { |
| containsOuterReferencesInSelectList = TRUE; |
| } |
| } |
| if ((!inst->NoCheckforLeftToInnerJoin)&&!containsOuterReferencesInSelectList) |
| { |
| child(i) = child(i)->initiateLeftToInnerJoinTransformation |
| (normWARef); |
| returnValue = TRUE; |
| } |
| // we have to dig deeper |
| } else if (child(i)->predicateEliminatesNullAugmentedRows |
| (normWARef, outerReferences) == TRUE) |
| { |
| returnValue = TRUE; |
| } |
| containsOuterReferencesInSelectList = FALSE; |
| instNullValue->clear(); |
| coveredExpr->clear(); |
| coveredSubExpr->clear(); |
| } |
| NADELETE( emptyGA , GroupAttributes, STMTHEAP ); |
| NADELETE( emptySet , ValueIdSet, STMTHEAP ); |
| NADELETE( emptySet1 , ValueIdSet, STMTHEAP ); |
| NADELETE( coveredExpr , ValueIdSet, STMTHEAP ); |
| NADELETE( coveredSubExpr , ValueIdSet, STMTHEAP ); |
| NADELETE( instNullValue , ValueIdSet, STMTHEAP ); |
| } |
| |
| return returnValue; |
| } // BiLogic::predicateEliminatesNullAugmentedRows() |
| |
| ItemExpr * BiLogic::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| |
| ItemExpr *normalizedExpr = NULL; |
| |
| switch(getOperatorType()) |
| { |
| case ITM_AND: |
| normalizedExpr = ItemExpr::normalizeNode(normWARef); |
| break; |
| |
| case ITM_OR: |
| { |
| normWARef.setOrFlag(); // set flag to indicate in OR subtree |
| normalizedExpr = ItemExpr::normalizeNode(normWARef); |
| normWARef.restoreOrFlag(); |
| } |
| break; |
| |
| default: |
| ABORT("Internal error in BiLogic::normalizeNode() - unknown operator"); |
| } |
| |
| return normalizedExpr; |
| |
| } // BiLogic::normalizeNode() |
| |
| |
| // MDAMR |
| // ----------------------------------------------------------------------- |
| // Perform an MDAM tree walk on BiLogic |
| // ----------------------------------------------------------------------- |
| DisjunctArray * BiLogic::mdamTreeWalk() |
| { |
| // perform the tree walk on the left child |
| DisjunctArray * leftDisjunctArray = child(0)->getReplacementExpr()->mdamTreeWalk(); |
| //10-040128-2749 -begin |
| // If Any of the Disjuncts are NULL then we must have ABORTed disjunct |
| // generation at some point of time so we must return. To save time. |
| // |
| // NOTE : there are currently 4 more mdamTreeWalk call for |
| // Veggies,uniLogic,Boolean and BiRelate ItemExprs but these are trivial. |
| // Only this one BiLogic is complex. |
| if(!leftDisjunctArray) |
| return NULL; |
| //10-040128-2749 -end |
| |
| // perform the tree walk on the right child |
| DisjunctArray * rightDisjunctArray = child(1)->getReplacementExpr()->mdamTreeWalk(); |
| //10-040128-2749 -begin |
| if(!rightDisjunctArray) |
| return NULL; |
| //10-040128-2749 -end |
| |
| switch(getOperatorType()) // what node are we on? |
| { |
| case ITM_AND: // we are at an AND node |
| { |
| // ----------------------------------------------------------------- |
| // The left and right disjunct arrays are ANDed together. The |
| // resulting disjunct array, returned by mdamANDDisjunctArrays, is |
| // sent back to the previous ItemExpr as this method recurses back |
| // up the expression tree. |
| // ----------------------------------------------------------------- |
| return mdamANDDisjunctArrays( leftDisjunctArray, |
| rightDisjunctArray ); |
| } |
| case ITM_OR: // we are at an OR node |
| { |
| // ----------------------------------------------------------------- |
| // The left and right disjunct arrays are ORed together. The |
| // resulting disjunct array, returned by mdamORDisjunctArrays, is |
| // sent back to the previous ItemExpr as this method recurses back |
| // up the expression tree. |
| // ----------------------------------------------------------------- |
| return mdamORDisjunctArrays( leftDisjunctArray, |
| rightDisjunctArray ); |
| } |
| default: |
| ABORT("Internal error in BiLogic::mdamTreeWalk() - unknown operator"); |
| break; |
| } |
| |
| return 0; |
| } // BiLogic::mdamTreeWalk() |
| |
| // MDAMR |
| |
| // *********************************************************************** |
| // $$$$ BiRelat |
| // member functions for class BiRelat |
| // *********************************************************************** |
| |
| static NABoolean switchLeftAndRightChildren(ItemExpr* leftChild, |
| ItemExpr* rightChild) |
| { |
| // Fastpath for expression of the form |
| // <column> <op> <expression>, <expression> op <column> |
| if (leftChild->getOperatorType() == ITM_BASECOLUMN || |
| leftChild->getOperatorType() == ITM_INDEXCOLUMN) |
| return FALSE; |
| else if (rightChild->getOperatorType() == ITM_BASECOLUMN || |
| rightChild->getOperatorType() == ITM_INDEXCOLUMN) |
| return TRUE; |
| |
| ValueId exprId; |
| ValueIdSet leftLeafValues, rightLeafValues; |
| NABoolean leftColumnFlag = FALSE; |
| NABoolean rightColumnFlag = FALSE; |
| |
| // Gather the ValueIds of all the expressions of arity 0 |
| leftChild->getLeafValueIds(leftLeafValues); |
| rightChild->getLeafValueIds(rightLeafValues); |
| |
| // Do the transformation when the left child contains constants only |
| // and the right child contains at least one column |
| for (exprId = leftLeafValues.init(); |
| leftLeafValues.next(exprId); |
| leftLeafValues.advance(exprId)) |
| { |
| if ((exprId.getItemExpr()->getOperatorType() == ITM_BASECOLUMN) || |
| (exprId.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN) ) |
| { |
| leftColumnFlag = TRUE; |
| break; |
| } |
| } |
| |
| for (exprId = rightLeafValues.init(); |
| rightLeafValues.next(exprId); |
| rightLeafValues.advance(exprId)) |
| { |
| if ( (exprId.getItemExpr()->getOperatorType() == ITM_BASECOLUMN) || |
| (exprId.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN) ) |
| { |
| rightColumnFlag = TRUE; |
| break; |
| } |
| } |
| |
| // if leftColumn contains no base column, and the right child |
| // contains even one. Do the transformation |
| if (rightColumnFlag && !leftColumnFlag) |
| return TRUE; |
| else |
| return FALSE; |
| |
| } // switchLeftAndRightChildren() |
| |
| // ----------------------------------------------------------------------- |
| // Get the operator type for performing the reverse comparison. |
| // ----------------------------------------------------------------------- |
| OperatorTypeEnum BiRelat::getReverseOperatorType() const |
| { |
| |
| switch(getOperatorType()) |
| { |
| case ITM_GREATER_EQ: |
| return ITM_LESS_EQ; |
| case ITM_GREATER: |
| return ITM_LESS; |
| case ITM_LESS: |
| return ITM_GREATER; |
| case ITM_LESS_EQ: |
| return ITM_GREATER_EQ; |
| default: |
| return getOperatorType(); |
| } |
| } // BiRelat::getReverseOperatorType |
| |
| // ----------------------------------------------------------------------- |
| // Find all subqueries directly contained in the ItemList child(ren) |
| // of thisIE. Leaving the subqueries where they are, construct a list of preds |
| // "((subq) IS NULL) IS NOT UNKNOWN [AND...]". |
| // |
| // These predicates will of course always evaluate either to TRUE or |
| // to CardinalityViolation; this is merely a handy way for us to return |
| // a valid predicate containing all subqueries up to our caller, |
| // ItemExpr::convertToValueIdSet(). These preds will then be easily separable |
| // by removeSubqueryPredicates() in RelExpr::transformSelectPred() |
| // and the ITM_ONE_ROW aggregation will thus be enforced. |
| // |
| // Finally, note that GroupbyAgg::normalizeNode() will end up eliminating |
| // the "IS NULL IS NOT UNKNOWN" pred from its HAVING clause, |
| // leaving the ITM_ONE_ROW aggregate as is. |
| // ----------------------------------------------------------------------- |
| static ItemExpr * dissectOutSubqueries(ItemExpr *thisIE, |
| NABoolean flattenSubqueries, |
| NABoolean considerDirectChildSubqueries) |
| { |
| if (CmpCommon::getDefault(COMP_BOOL_137) == DF_OFF) |
| return NULL ; |
| |
| if (flattenSubqueries) return NULL; // makes no sense, otherwise |
| |
| ItemExpr *newPred = NULL; |
| |
| Int32 i = thisIE->getArity(); |
| for (; i--; ) |
| if (thisIE->child(i)->getOperatorType() == ITM_ITEM_LIST) |
| { |
| ItemExprList list(thisIE->child(i).getPtr(), HEAP,ITM_ITEM_LIST,FALSE); |
| for (CollIndex i = list.entries(); i--; ) |
| if (list[i]->isASubquery()) |
| { |
| ItemExpr *u = new HEAP UnLogic(ITM_IS_NOT_UNKNOWN, |
| new HEAP UnLogic(ITM_IS_NULL, list[i])); |
| newPred = (!newPred) ? u : new HEAP BiLogic(ITM_AND, u, newPred); |
| } |
| } |
| else if (considerDirectChildSubqueries && thisIE->child(i)->isASubquery()) |
| { |
| ItemExpr *u = new HEAP UnLogic(ITM_IS_NOT_UNKNOWN, |
| new HEAP UnLogic(ITM_IS_NULL, thisIE->child(i))); |
| newPred = (!newPred) ? u : new HEAP BiLogic(ITM_AND, u, newPred); |
| } |
| |
| return newPred; |
| } // dissectOutSubqueries |
| |
| // ----------------------------------------------------------------------- |
| // BiRelat::transformMultiValuePredicate() |
| // |
| // If the comparison operator is = (equality), |
| // transform a predicate of the form (A,B) = (C,D) |
| // to A = C AND B = D. |
| // |
| // If the comparison operator is <> (inequality), |
| // transform a predicate of the form (A,B) <> (C,D) |
| // to A <> C OR B <> D. |
| // |
| // If the comparison operator is < or <= or > or >=, |
| // transform a predicate of the form (A,B,C) {original-op} (X,Y,Z) |
| // to: |
| // (A {new-op} X) OR |
| // (A = X) AND ((B {new-op} Y) OR |
| // (B = Y) AND (C {original-op} Z)) |
| // where the <new-op> is: |
| // if original-op is < or <=, new-op is < |
| // if original-op is > or >=, new-op is > |
| // |
| // MV -- |
| // The optional directionVector parameter is for comparing index columns |
| // that are not all directed the same. For example if the index is on |
| // columns (A ASC, B DESC, C ASC), we can use the special syntax (internal |
| // use only) of: (a, b, c) > (x, y, z) DIRECTEDBY ( ASC, DESC, ASC). |
| // The direction vector is a list of integers: 1 for ASC, and -1 for DESC. |
| // The result will be: |
| // (A > X) OR |
| // (A = X) AND ((B < Y) OR |
| // (B = Y) AND (C > Z)) |
| // ----------------------------------------------------------------------- |
| static ItemExpr * transformMultiValueComparison(BiRelat *thisCmp, |
| NABoolean flattenSubq, |
| NABoolean flattenUDF, |
| IntegerList *directionVector) |
| { |
| // Convert the child subtrees into lists. |
| ItemExprList lhs(thisCmp->child(0).getPtr(), HEAP,ITM_ITEM_LIST, |
| flattenSubq, flattenUDF); |
| ItemExprList rhs(thisCmp->child(1).getPtr(), HEAP,ITM_ITEM_LIST, |
| flattenSubq, flattenUDF); |
| |
| if (lhs.entries() != rhs.entries()) |
| { |
| if (!flattenSubq) return NULL; |
| CMPASSERT(lhs.entries() == rhs.entries()); |
| } |
| |
| if ( lhs.entries() == 0 && rhs.entries() == 0) |
| { |
| return NULL; // this is possible in certain kinds of aggregate transformations |
| // For exampl OneRow aggregate |
| } |
| |
| Int32 i = lhs.entries() - 1; |
| |
| // As an extension to Ansi, we allow predicates like |
| // select x,y from xy where((select a,b from t),x) = (y,(select m,n from s)) |
| // which obviously cannot be transformed when not flattening subqueries |
| // (i.e. when called from convertToValueIdSet). |
| // |
| if (!flattenSubq) |
| { |
| CollIndex ll, rr; |
| while (i--) |
| { |
| ll = rr = 1; // non-subq's are single items, no lists |
| if (lhs[i]->isASubquery()) |
| ll = ((Subquery *)lhs[i])->getSubquery()->getDegree(); |
| if (rhs[i]->isASubquery()) |
| rr = ((Subquery *)rhs[i])->getSubquery()->getDegree(); |
| if (ll != rr) return NULL; |
| } |
| i = lhs.entries() - 1; // reset for loops following |
| } |
| |
| ItemExpr * tfm; |
| NABoolean special = thisCmp->getSpecialNulls(); |
| |
| if (thisCmp->getOperatorType() == ITM_EQUAL) |
| { |
| tfm = new HEAP BiRelat(ITM_EQUAL, lhs[i], rhs[i], special); |
| |
| while (i--) |
| tfm = new HEAP BiLogic(ITM_AND, |
| new HEAP |
| BiRelat(ITM_EQUAL, lhs[i], rhs[i], special), |
| tfm |
| ); |
| } |
| else |
| { |
| // convert '<=' to '<' and '>=' to '>'; leave '<', '>', '<>' as is. |
| OperatorTypeEnum origOp, newOp, origRevOp, newRevOp, thisOp; |
| origOp = newOp = thisCmp->getOperatorType(); |
| origRevOp = newRevOp = thisCmp->getReverseOperatorType(); |
| if (newOp == ITM_LESS_EQ) |
| newOp = ITM_LESS; |
| else if (newOp == ITM_GREATER_EQ) |
| newOp = ITM_GREATER; |
| if (newRevOp == ITM_LESS_EQ) |
| newRevOp = ITM_LESS; |
| else if (newRevOp == ITM_GREATER_EQ) |
| newRevOp = ITM_GREATER; |
| |
| // An ItemExprList to capture the comparison for each column |
| // Consider predicate (a, b) > (1, 2) |
| // The above predicate will be transformed to (a > 1) or (a = 1 and b > 2) |
| // The list of comparisons in this case will be have |
| // 1. '>' ItemExpr from a > 1 |
| // 2. '>' ItemExpr from b > 2 |
| // |
| // Consider predicate (a, b) > (1, 2) DIRECTED BY (ASC, DESC) |
| // The above predicate will be transformed to (a > 1) or (a = 1 and b < 2) |
| // The list of comparisons in this case will be have |
| // 1. '>' ItemExpr from a > 1 |
| // 2. '<' ItemExpr from b < 2 |
| // |
| // This list of comparisons is later used in BiRelat::transformNode |
| // to get: |
| // 1. list of the left children of (a, b) > (1, 2), |
| // The list will have the ValueIds |
| // 1. VEG containing column A |
| // 2. VEG containing column B |
| // 2. list of the right Children of (a, b) > (1, 2), |
| // The list will have the ValueIds |
| // 1. VEG containing literal 1 |
| // 2. VEG containing literal 2 |
| // |
| // In addition the list of comparisons is also used to match key column |
| // clustering order (i.e. asc, desc) to the type of comparison (i.e. >, <). |
| // |
| // This information is used in the scan optimizer to figure out if |
| // a multicolumn range predicate can be used to specify a begin/end key |
| // for a given access path |
| ItemExprList * listOfComparisons = new HEAP ItemExprList(HEAP); |
| |
| // If the direction is DESC flip the sign. |
| thisOp = origOp; |
| if (directionVector!=NULL && directionVector->at(i)==-1) |
| thisOp = origRevOp; |
| tfm = new HEAP BiRelat(thisOp, lhs[i], rhs[i], special); |
| |
| // add the first comparison |
| listOfComparisons->insertAt(0,tfm); |
| |
| while (i--) // we MUST build THESE preds BACKWARDS |
| { |
| // If the direction is DESC flip the sign. |
| thisOp = newOp; |
| if (directionVector!=NULL && directionVector->at(i)==-1) |
| thisOp = newRevOp; |
| BiRelat * b1 = new HEAP BiRelat(thisOp, lhs[i], rhs[i], special); |
| |
| // add comparison to list of comparisons |
| listOfComparisons->insertAt(0,b1); |
| |
| if (newOp != ITM_NOT_EQUAL) |
| tfm = new HEAP BiLogic(ITM_AND, |
| new HEAP |
| BiRelat(ITM_EQUAL, lhs[i], rhs[i], special), |
| tfm); |
| |
| tfm = new HEAP BiLogic(ITM_OR, b1, tfm); |
| } |
| |
| // add an extra single column range predicate, the predicate is implied by |
| // the MCRP. Consider MCRP (a, b) > (1, 2), this predicate also implies |
| // predicate (a >= 1), therefore the MCRP is transformed to |
| // (a >= 1) and (a, b) > (1, 2). |
| // Since (a, b) > (1, 2) has already been transformed to |
| // (a > 1) or (a = 1 and b > 2), the transformed predicate looks like |
| // ((a >= 1) and ((a > 1) or (a = 1 and b > 2)) |
| // the extra predicate helps the single subset scan optimizer choose |
| // a begin/end key based on the MCRP (a, b) > (1, 2). |
| // If this is not done the whole table, since the single subset scan optimizer |
| // cannot deal with disjunct i.e. ORs and (a, b) > (1, 2) is transformed |
| // into an OR. |
| if ((tfm->getOperatorType() == ITM_OR) && (origOp != ITM_NOT_EQUAL)) |
| { |
| BiRelat * prefixComparison = (BiRelat *) (*listOfComparisons)[0]; |
| OperatorTypeEnum prefixCompOper = prefixComparison->getOperatorType(); |
| |
| if(prefixCompOper == ITM_GREATER) |
| prefixCompOper = ITM_GREATER_EQ; |
| |
| if(prefixCompOper == ITM_LESS) |
| prefixCompOper = ITM_LESS_EQ; |
| |
| ItemExpr * compLeftChild = (ItemExpr *) prefixComparison->child(0); |
| ItemExpr * compRightChild = (ItemExpr *) prefixComparison->child(1); |
| |
| BiRelat * addedComparison = new HEAP BiRelat(prefixCompOper, |
| compLeftChild, |
| compRightChild, |
| prefixComparison->getSpecialNulls()); |
| |
| if (thisCmp->isPreferredForSubsetScanKey()) |
| addedComparison->setPreferForSubsetScanKey(); |
| |
| addedComparison->setDerivedFromMCRP(); |
| addedComparison->setListOfComparisons(listOfComparisons); |
| |
| tfm = new HEAP BiLogic(ITM_AND, |
| addedComparison, |
| tfm); |
| } |
| |
| } |
| |
| // Do not synthTypeAndValueId() yet |
| CMPASSERT(tfm); |
| return tfm; |
| |
| } // transformMultiValueComparison() |
| |
| ItemExpr * BiRelat::transformMultiValuePredicate( |
| NABoolean flattenSubqueries, // default TRUE |
| ChildCondition tfmIf) // ANY_CHILD |
| |
| { |
| NABoolean tfmNeeded; |
| NABoolean flattenUDFs( flattenSubqueries ); // we use the same rules.. |
| |
| // MV -- |
| // Handle the direction vector correctly. |
| if (directionVector_ != NULL) |
| tfmNeeded = TRUE; |
| else if (tfmIf == ANY_CHILD) |
| tfmNeeded = (child(0)->getOperatorType() == ITM_ITEM_LIST || |
| child(1)->getOperatorType() == ITM_ITEM_LIST) || |
| (child(0)->getOperatorType() == ITM_ONE_ROW || |
| child(1)->getOperatorType() == ITM_ONE_ROW); |
| |
| else if (tfmIf == ALL_CHILDREN) |
| tfmNeeded = child(0)->getOperatorType() == ITM_ITEM_LIST && |
| child(1)->getOperatorType() == ITM_ITEM_LIST; |
| |
| else |
| tfmNeeded = TRUE; |
| |
| if (!tfmNeeded) return NULL; // no transform needed |
| |
| ItemExpr *tfm; |
| if (tfmIf == ANY_CHILD_RAW) |
| tfm = dissectOutSubqueries(this, flattenSubqueries, TRUE); |
| else |
| tfm = transformMultiValueComparison(this, flattenSubqueries, |
| flattenUDFs, directionVector_); |
| |
| return tfm; |
| } // BiRelat::transformMultiValuePredicate() |
| |
| void BiRelat::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| unparse(unp); |
| cerr << (Int32)getOperatorType() << " " |
| << (Int32)getValueId() << " " |
| << (void *)this << " " |
| << unp << endl; |
| ) |
| |
| // --------------------------------------------------------------------- |
| // Transform multi-value equality predicates. |
| // Special transformation (i.e., done only if both children) |
| // is enabled only for QuantifiedComp-generated BiRelat's. |
| // --------------------------------------------------------------------- |
| ChildCondition tfmIf = |
| specialMultiValuePredicateTransformation() ? ALL_CHILDREN : ANY_CHILD; |
| ItemExpr * transformedMultiValue = transformMultiValuePredicate(FALSE, tfmIf); |
| |
| if (transformedMultiValue) |
| { |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(transformedMultiValue); |
| locationOfPointerToMe = transformedMultiValue; |
| locationOfPointerToMe->synthTypeAndValueId(TRUE); |
| |
| if( transformedMultiValue->getOperatorType() == ITM_AND) |
| { |
| // check for the added prefix predicate |
| ItemExpr * leftChildOfAND = (ItemExpr *)transformedMultiValue->child(0); |
| |
| if((leftChildOfAND->isARangePredicate()) && |
| ((BiRelat *)leftChildOfAND)->isDerivedFromMCRP()) |
| { |
| BiRelat * addedComparison = (BiRelat *) leftChildOfAND; |
| addedComparison->translateListOfComparisonsIntoValueIds(); |
| } |
| } |
| |
| // ----------------------------------------------------------------- |
| // Transform the transformed tree. |
| // ----------------------------------------------------------------- |
| locationOfPointerToMe->transformNode |
| (normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| } |
| |
| // --------------------------------------------------------------------- |
| // Switch the left and the right subtrees if the latter contains |
| // an expression that only contains BaseColumns at its leaves. |
| // --------------------------------------------------------------------- |
| else if (switchLeftAndRightChildren(child(0).getPtr(), child(1).getPtr())) |
| { |
| // ----------------------------------------------------------------- |
| // Replace me with my inverse comparison. |
| // a) > with < |
| // b) >= with <= |
| // c) < with > |
| // d) <= with => |
| // e) = and <> remain unchanged. |
| // ----------------------------------------------------------------- |
| BiRelat * flipflop = new(normWARef.wHeap()) |
| BiRelat(getReverseOperatorType(), |
| child(1).getPtr(), |
| child(0).getPtr()); |
| |
| flipflop->specialMultiValuePredicateTransformation() = |
| specialMultiValuePredicateTransformation(); |
| //++MV - Irena |
| flipflop->setSpecialNulls(getSpecialNulls()); |
| //--MV - Irena |
| |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(flipflop); |
| locationOfPointerToMe->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = flipflop; |
| // ----------------------------------------------------------------- |
| // Transform the transformed tree. |
| // ----------------------------------------------------------------- |
| locationOfPointerToMe->transformNode |
| (normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| } |
| else |
| { |
| // ----------------------------------------------------------------- |
| // Transform the left and right children. |
| // ----------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // ----------------------------------------------------------------- |
| // Allocate a VEG that contains my left and right subtrees |
| // provided this equality predicate is not encountered |
| // while performing a tree walk in a scalar expression, |
| // such as in a predicate tree rooted in an OR or in a CASE statement. |
| // |
| // NOTE: added nov 27, 95 |
| // The support for the generation of a VEGs for an equality |
| // predicate that appear in a subtree of an OR or an IS NULL/ |
| // IS UNKNOWN is already in place. It is also easy to enable below. |
| // However, the coverage test for a VEGPredicate permits it to |
| // be pushed down even when, in fact, only one member of the VEG is |
| // available. This property gives rise to a few interesting |
| // problems when this support is enabled. For example, |
| // 1) The normalization of the predicate isnull(anyTrue(x=y)), which |
| // is introduced by a subquery transformation, causes it to |
| // be transformed to isnull(anyTrue(VEGPredicate(x,y))). The |
| // presence of the VEGPredicate causes an internal error to be |
| // issued by FileScan::computeRetrievedColumns(). (I haven't |
| // debugged the cause of this problem nov 27, 95) |
| // 2) The predicate for T1 IJ T2 on T1.x = 10 or T2.y = 20 is |
| // normalized to VEGPredicate(T1.x,10) OR VEGPredicate(T2.x,20). |
| // Predicate pushdown causes each of T1 and T2 to compute the |
| // subexpressions VEGPredicate(T1.x,10) OR VEGPredicate(T2.x,20) |
| // respectively and eliminates the OR predicate from the join. |
| // The OR predicate should not be eliminated from the join |
| // because that changes the semantics of such queries. |
| // However, I do not have a solution for this problem yet. |
| // |
| // In order to enable the creation of a VEG for predicates |
| // underneath an OR, replace the code: |
| // !normWARef.walkingAnExprTree() |
| // with |
| // (!normWARef.walkingAnExprTree() || |
| // (normWARef.haveAnOrAncestor() && |
| // !normWARef.inAComplexScalarExpr())) |
| // |
| // ----------------------------------------------------------------- |
| if (getOperatorType() == ITM_EQUAL && |
| !normWARef.walkingAnExprTree()) |
| { |
| |
| // Do constant folding here, |
| // This will result in more VEGs being created |
| // because expressions will be folded in constant |
| // and they will be considered user input. |
| // e.g. predicate x = 1 + 3 will create a VEG |
| // between (x,4) because now expression 1 + 3 |
| // will be folded into ITM_CONSTANT 4 which is considered |
| // a user input |
| |
| // Do not attempt folding if one of the operands |
| // of the ITM_EQUAL is a ITM_ITEM_LIST since constant |
| // folding has a problem with that. It crashes |
| // in the generator. |
| |
| if((child(0)->getOperatorType() != ITM_ITEM_LIST) ) |
| { |
| // Get ValueIdList to invoke constant folding |
| ValueIdList child0; |
| child0.insert(child(0)->getValueId()); |
| |
| //attempt constant folding |
| child0.constantFolding(); |
| |
| if(!child0.isEmpty()) |
| //set child to be the folded expressions |
| setChild(0, child0[0].getItemExpr()); |
| } |
| |
| if((child(1)->getOperatorType() != ITM_ITEM_LIST)) |
| { |
| ValueIdList child1; |
| child1.insert(child(1)->getValueId()); |
| |
| //attempt constant folding |
| child1.constantFolding(); |
| |
| if(!child1.isEmpty()) |
| //set child to be the folded expressions |
| setChild(1, child1[0].getItemExpr()); |
| } |
| |
| if (bothAreColumnsOrUserInput(normWARef, child(0), child(1))) |
| { |
| // (fixes Genesis Case: 10-980827-2993) |
| // To fix the problem arising from a VEG with a single column in |
| // it, from now on we unconditionally transform {T1.A=T1.A --> |
| // T1.A IS NOT NULL} |
| // -------------- |
| // MVs: |
| // When specialNulls_ flag is set, nulls are observed as values |
| // and so they are also in scope (null=null). |
| // That is why we don't want ITM_IS_NOT_NULL to be turned on |
| // in that case. |
| // Irena |
| if ( child(0)->getValueId() == child(1)->getValueId() |
| && NOT getSpecialNulls() // ++MV - Irena |
| ) |
| { |
| // the check for whether T1.A is nullable is done inside |
| // UnLogic::transformIsNull(), which gets called by |
| // UnLogic::transformNode() |
| UnLogic * newNode = |
| new (normWARef.wHeap()) UnLogic (ITM_IS_NOT_NULL, child(0)) ; |
| |
| getValueId().replaceItemExpr(newNode); |
| locationOfPointerToMe->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = newNode; |
| // ----------------------------------------------------------------- |
| // Transform the transformed tree. |
| // ----------------------------------------------------------------- |
| locationOfPointerToMe->transformNode |
| (normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| } |
| else |
| { |
| // QSTUFF |
| // We don't push predicates below a generic update root. |
| // Vegpreds for equality predicates are "implicitly" pushed |
| // down due the the veg logic. By not allowing vegpreds in case |
| // of a genericupdate root we can retain those predicates. |
| // The characteristics of the ITM_EQUAL BiRelat that must be |
| // retained are that either 1) both children are column refs to |
| // the same table or index, or 2) either child is a user-input. |
| NABoolean putInVeg = TRUE; |
| if ( normWARef.isInEmbeddedUpdateOrDelete() || |
| normWARef.isInEmbeddedInsert()) { |
| if (child(0)->isAColumnReference() && |
| child(1)->isAColumnReference()) { |
| |
| const NATable *table0 = NULL; |
| if(child(0)->getOperatorType() == ITM_BASECOLUMN) { |
| table0=((BaseColumn*)(ItemExpr *)child(0))->getTableDesc()->getNATable(); |
| } |
| else if(child(0)->getOperatorType() == ITM_INDEXCOLUMN) { |
| table0=((IndexColumn*)(ItemExpr *)child(0))->getNAColumn()->getNATable(); |
| } |
| |
| const NATable *table1 = NULL; |
| if(child(1)->getOperatorType() == ITM_BASECOLUMN) { |
| table1=((BaseColumn*)(ItemExpr *)child(1))->getTableDesc()->getNATable(); |
| } |
| else if(child(1)->getOperatorType() == ITM_INDEXCOLUMN) { |
| table1=((IndexColumn*)(ItemExpr *)child(1))->getNAColumn()->getNATable(); |
| } |
| |
| if( (table0 == table1) && |
| (table0 != NULL) ){ |
| // It's not valid to say table0 is the same as table1, unless |
| // both have been set to non-null. However, there is no |
| // reason to test nullness of both, since if table0 is |
| // not null, and table1 == table0, then table1 is not null |
| // either. The following assertion is my guarantee. |
| CMPASSERT( table1 != NULL); |
| putInVeg = FALSE; |
| } |
| } |
| else if ( child(0)->isAUserSuppliedInput() || |
| child(1)->isAUserSuppliedInput() ) { |
| putInVeg = FALSE; |
| } |
| } |
| //case 10-060531-0086/10-060714-4662 Soln 10-060531-6865 Begin |
| //VEG between varchar and char might give wrong result based |
| //on the data they have when columns are substituted elsewhere say |
| //in select list. |
| //For ex:create table tab(c1 char(5), c2 varchar(5)); |
| // insert into tab('aaa ','aaa'); |
| // select c2 || c1 from tab where c1 = c2; |
| // incorrect result : aaa aaa |
| // Without VEG : aaaaaa |
| // Incorrect result occurs when c1 is chosen out of the VEG which is |
| // fixed character variable and it is substituted in the select list. |
| // Though we would have cast node to convert it to varchar, but it |
| // would have left trailing spaces.The ANSI semantics states that when |
| // a cast node is converting a fixed char to varchar it retains the maximum |
| // length of characters of varchar variable and the rest are truncated. |
| // This is also seen between to varchar variables. |
| |
| const NAType &type1 = child(0)->getValueId().getType(); |
| const NAType &type2 = child(1)->getValueId().getType(); |
| |
| if (type1.getTypeQualifier() == NA_CHARACTER_TYPE && |
| type2.getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| if((NOT DFS2REC::isAnyVarChar(type1.getFSDatatype()) |
| && DFS2REC::isAnyVarChar(type2.getFSDatatype())) || |
| (DFS2REC::isAnyVarChar(type1.getFSDatatype()) && |
| NOT DFS2REC::isAnyVarChar(type2.getFSDatatype())) || |
| (DFS2REC::isAnyVarChar(type1.getFSDatatype()) && |
| DFS2REC::isAnyVarChar(type2.getFSDatatype()))) |
| { |
| if (CmpCommon::getDefault(COMP_BOOL_158) == DF_OFF) |
| putInVeg = FALSE; |
| } |
| } |
| |
| //case 10-060531-0086/10-060714-4662 Soln 10-060531-6865 End |
| |
| // Solution 10-080923-6039 |
| // If we have an expression like T0.I1=T0.I2 and both |
| // T0.I1 and T0.I2 are inputs, do not establish a VEG as |
| // that would have been done above us in the tree if it was |
| // necessary. |
| |
| if (externalInputs.contains(child(0)->getValueId()) && |
| externalInputs.contains(child(1)->getValueId())) |
| { |
| putInVeg = FALSE; |
| } |
| |
| // Part of fix to bugzilla cases 3405, 3408, 3409. |
| // For an UPSERT of the form: |
| // merge into T on c1=c2 |
| // when matched then update ... where c2=2 |
| // when not matched then insert ...; |
| // we cannot safely allow a VEG to form for "c2=2" |
| // because it applies only to the "when matched" clause. |
| // Otherwise, the VEG "c2=2=c1" would incorrectly try to |
| // do "insert ..." when "c2=2=c1" is false. |
| // The correct behavior here is to do "insert ..." |
| // only when "c1=c2" is false. |
| if (putInVeg && normWARef.inMergeUpdWhere()) |
| { |
| putInVeg = FALSE; |
| } |
| |
| if (putInVeg) |
| { |
| normWARef.addVEG(child(0)->getValueId(),child(1)->getValueId()); |
| |
| // if the column is nullable and part of a clustering key then that |
| // column VEG was set as special nulls so the equality predicate |
| // of the base and index column returns NULL equal NULL as TRUE |
| // during an index join. |
| // However if that column is part of an user predicate then there is |
| // no need to set this flag as we do not return NULL values |
| // anyway so resetting the special nulls falg if the column is part |
| // of a user predicate |
| |
| ItemExpr * vegrefPtr = normWARef.getVEGReference(child(0)->getValueId()); |
| if (vegrefPtr) |
| { |
| ((VEGReference *)vegrefPtr)->getVEG()->setSpecialNulls(FALSE); |
| |
| if(isSelectivitySetUsingHint()) |
| { |
| VEGPredicate *vegPred = ((VEGReference *)vegrefPtr)->getVEG()->getVEGPredicate(); |
| vegPred->getPredsWithSelectivities().insert(getValueId()); |
| vegPred->setSelectivitySetUsingHint(); |
| } |
| } |
| } |
| } // if child(0)->getValueId() == child(1)->getValueId() ... else ... |
| } // if bothAreColumnsOrUserInput |
| } // if getOperatorType() == ITM_EQUAL && !normWARef.walkingAnExprTree()) |
| } // if transformedMultiValue ... elseif switchLeftAndRightChildren ... else .... |
| |
| markAsTransformed(); |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // BiRelat::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // predicateEliminatesNullAugmentedRows() |
| // The following method determines whether a predicate is capable of |
| // discarding null augmented rows produced by a left join. |
| // ----------------------------------------------------------------------- |
| NABoolean BiRelat::predicateEliminatesNullAugmentedRows(NormWA & normWARef, |
| ValueIdSet & outerReferences) |
| { |
| NABoolean returnValue = FALSE; |
| // ----------------------------------------------------------------- |
| // An InstantiateNull is used by the LeftJoin for instantiating a |
| // null value in place of a value that is produced as output by |
| // its second child. An InstantiateNull that appears in a binary |
| // comparison predicate eliminates null augmented rows. As a |
| // consequence, the LeftJoin becomes an InnerJoin. For example, |
| // having |
| // GB ------> A.x < D.x |
| // | |
| // LJ |
| // / \ |
| // A LJ |
| // / \ |
| // B LJ |
| // / \ |
| // C D |
| // The HAVING clause predicate A.x < D.x will cause all the |
| // LeftJoin (LJ) operators to be transformed to InnerJoins. |
| // ----------------------------------------------------------------- |
| if (!normWARef.walkingAnExprTree() && !normWARef.subqUnderExprTree()) |
| { // does NOT lie within an expression tree |
| // ------------------------------------------------------------- |
| // Initiate the left to inner join transformation. Replace |
| // the InstantiateNull with the original expression that |
| // is the subject for null instantiation. |
| // ------------------------------------------------------------- |
| GroupAttributes emptyGA; |
| emptyGA.setCharacteristicOutputs(outerReferences); |
| ValueIdSet emptySet, emptySet1, coveredExpr, coveredSubExpr; |
| ValueIdSet instNullValue; |
| NABoolean containsOuterReferencesInSelectList = FALSE; |
| for (CollIndex i = 0; i < 2; i++) |
| { |
| if (child(i)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = (InstantiateNull *)child(i)->castToItemExpr(); |
| if ((normWARef.inSelectList())&&!(inst->NoCheckforLeftToInnerJoin)) |
| { |
| instNullValue.insert(inst->getValueId()); |
| emptyGA.coverTest( |
| instNullValue, |
| emptySet, |
| coveredExpr, |
| emptySet1, |
| &coveredSubExpr); |
| if(!coveredExpr.isEmpty()) |
| { |
| containsOuterReferencesInSelectList = TRUE; |
| } |
| } |
| if ((!inst->NoCheckforLeftToInnerJoin)&&!containsOuterReferencesInSelectList) |
| { |
| child(i) = child(i)->initiateLeftToInnerJoinTransformation |
| (normWARef); |
| returnValue = TRUE; |
| } |
| } |
| containsOuterReferencesInSelectList = FALSE; |
| instNullValue.clear(); |
| coveredExpr.clear(); |
| coveredSubExpr.clear(); |
| } |
| } |
| |
| return returnValue; |
| } // BiRelat::predicateEliminatesNullAugmentedRows() |
| |
| ItemExpr * BiRelat::normalizeNode(NormWA & normWARef) |
| { |
| ItemExpr * normalizedExpr = 0; |
| |
| // --------------------------------------------------------------------- |
| // NOTE: added nov 27, 95 |
| // The support for the generation of a VEGs for an equality |
| // predicate that appear in a subtree of an OR or an IS NULL/ |
| // IS UNKNOWN is already in place. It is also easy to enable below. |
| // However, the coverage test for a VEGPredicate permits it to |
| // be pushed down even when, in fact, only one member of the VEG is |
| // available. This property gives rise to a few interesting |
| // problems when this support is enabled. For example, |
| // 1) The normalization of the predicate isnull(anyTrue(x=y)), which |
| // is introduced by a subquery transformation, causes it to |
| // be transformed to isnull(anyTrue(VEGPredicate(x,y))). The |
| // presence of the VEGPredicate causes an internal error to be |
| // issued by FileScan::computeRetrievedColumns(). (I haven't |
| // debugged the cause of this problem nov 27, 95) |
| // 2) The predicate for T1 IJ T2 on T1.x = 10 or T2.y = 20 is |
| // normalized to VEGPredicate(T1.x,10) OR VEGPredicate(T2.x,20). |
| // Predicate pushdown causes each of T1 and T2 to compute the |
| // subexpressions VEGPredicate(T1.x,10) OR VEGPredicate(T2.x,20) |
| // respectively and eliminates the OR predicate from the join. |
| // The OR predicate should not be eliminated from the join |
| // because that changes the semantics of such queries. |
| // However, I do not have a solution for this problem yet. |
| // |
| // In order to enable the replacement of a VEG for predicates |
| // underneath an OR, replace the code: |
| // !normWARef.walkingAnExprTree() |
| // with |
| // (!normWARef.walkingAnExprTree() || |
| // (normWARef.haveAnOrAncestor() && |
| // !normWARef.inAComplexScalarExpr())) |
| // |
| // ----------------------------------------------------------------- |
| if (getOperatorType() == ITM_EQUAL && |
| !normWARef.walkingAnExprTree() && |
| bothAreColumnsOrUserInput(normWARef, child(0), child(1))) |
| { |
| |
| if (!( child(0)->getValueId() == child(1)->getValueId() )) |
| { |
| |
| ItemExpr * vegRef0Ptr = normWARef.getVEGReference |
| (child(0)->getValueId()); |
| ItemExpr * vegRef1Ptr = normWARef.getVEGReference |
| (child(1)->getValueId()); |
| if (vegRef0Ptr && vegRef0Ptr == vegRef1Ptr) |
| { |
| normalizedExpr = normWARef.performTC(child(0)->getValueId()); |
| } |
| //-------------------------------------------------------------------- |
| // Case-10-040630-8369 |
| // If performTC returns null just call ItemExpr::normalizeNode(normWARef) and |
| // continue. |
| //-------------------------------------------------------------------- |
| if (normalizedExpr == 0) |
| { |
| normalizedExpr = ItemExpr::normalizeNode(normWARef); |
| } |
| } |
| else |
| { |
| |
| //-------------------------------------------------------------------- |
| // Genesis case: 10-001006-2811: |
| // We would like to transform T1.A = T1.A to T1.A is not NULL in all |
| // cases. This is normally done in transformation, but in some cases |
| // constant folding is done after this ItemExpression is transformed |
| // In those cases, we do that transformation in Normalizer |
| //-------------------------------------------------------------------- |
| |
| UnLogic * newNode = |
| new (normWARef.wHeap()) UnLogic (ITM_IS_NOT_NULL, child(0)) ; |
| |
| getValueId().replaceItemExpr(newNode); |
| newNode->synthTypeAndValueId(TRUE); |
| |
| return newNode->normalizeNode(normWARef); |
| } |
| } |
| else |
| { |
| normalizedExpr = ItemExpr::normalizeNode(normWARef); |
| } |
| //++MV - Irena |
| ((VEGPredicate*)normalizedExpr)->setSpecialNulls(getSpecialNulls()); |
| //--MV - Irena |
| return normalizedExpr; |
| |
| } // BiRelat::normalizeNode() |
| |
| // ----------------------------------------------------------------------- |
| // Invert the comparison type when the operator is the child of a NOT. |
| // ----------------------------------------------------------------------- |
| ItemExpr * BiRelat::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| if (falseOrNot != ITM_NOT) |
| return this; |
| |
| OperatorTypeEnum otype = NO_OPERATOR_TYPE; |
| |
| switch(getOperatorType()) |
| { |
| case ITM_GREATER: |
| otype = ITM_LESS_EQ; |
| break; |
| case ITM_GREATER_EQ: |
| otype = ITM_LESS; |
| break; |
| case ITM_EQUAL: |
| otype = ITM_NOT_EQUAL; |
| break; |
| case ITM_NOT_EQUAL: |
| otype = ITM_EQUAL; |
| break; |
| case ITM_LESS_EQ: |
| otype = ITM_GREATER; |
| break; |
| case ITM_LESS: |
| otype = ITM_GREATER_EQ; |
| break; |
| default: |
| ABORT("Internal error in BiRelat::transformSubtreeOfNot() unknown comparison operator"); |
| break; |
| } |
| |
| BiRelat * newComp = new(normWARef.wHeap()) |
| BiRelat(otype, child(0).getPtr(), child(1).getPtr()); |
| |
| newComp->setDirectionVector(directionVector_); |
| |
| newComp->specialMultiValuePredicateTransformation() = |
| specialMultiValuePredicateTransformation(); |
| |
| |
| //++MV - Irena |
| newComp->setSpecialNulls(getSpecialNulls()); |
| //--MV - Irena |
| |
| // If the original BiRelat expression was a derivative of LIKE pred |
| // then copy the LkeParentId and the Selectivity assigned to the |
| // original expression to the new BiRelat expression too |
| |
| if (derivativeOfLike() ) |
| { |
| newComp->setLikeSelectivity(1 - getLikeSelectivity() ); |
| newComp->setOriginalLikeExprId(getValueId() ); |
| } |
| |
| // Do not synthTypeAndValueId() yet |
| return newComp; |
| } // BiRelat::transformSubtreeOfNot() |
| |
| // MDAMR |
| // ----------------------------------------------------------------------- |
| // Perform an MDAM tree walk on BiRelat |
| // ----------------------------------------------------------------------- |
| DisjunctArray * BiRelat::mdamTreeWalk() |
| { |
| // --------------------------------------------------------------------- |
| // First a DisjunctArray is allocated. Then a ValueIdSet is allocated |
| // with the value id of this predicate added to the set. The pointer |
| // to this ValueIdSet is then inserted as the first entry of the array. |
| // The pointer to the DisjunctArray is returned to the previous BiLogic |
| // node as this method recurses back up the expression tree. |
| // --------------------------------------------------------------------- |
| return new HEAP DisjunctArray(new HEAP ValueIdSet(getValueId())); |
| |
| } // BiRelat::mdamTreeWalk() |
| //MDAMR |
| |
| |
| // ----------------------------------------------------------------------- |
| // predicateEliminatesNullAugmentedRows() |
| // The following method determines whether a predicate is capable of |
| // discarding null augmented rows produced by a left join. |
| // ----------------------------------------------------------------------- |
| NABoolean PatternMatchingFunction::predicateEliminatesNullAugmentedRows(NormWA & normWARef, |
| ValueIdSet & outerReferences) |
| { |
| NABoolean returnValue = FALSE; |
| // ----------------------------------------------------------------- |
| // An InstantiateNull is used by the LeftJoin for instantiating a |
| // null value in place of a value that is produced as output by |
| // its second child. An InstantiateNull that appears in a binary |
| // comparison predicate eliminates null augmented rows. As a |
| // consequence, the LeftJoin becomes an InnerJoin. For example, |
| // |
| // select t1.x |
| // from t1 left join t2 on t1.x = t2.x |
| // where t2.x like '_B%'; |
| |
| |
| // SelPred: |
| // ------> T2.x Like 'Hello%' |
| // | |
| // LJ JoinPred: t1.x = t2.x |
| // / \ |
| // T1 T2 |
| // The Selection predicate T2.x LIKE 'Hello%' will cause all the |
| // LeftJoin (LJ) operators to be transformed to InnerJoins. |
| // ----------------------------------------------------------------- |
| if (!normWARef.walkingAnExprTree() && !normWARef.subqUnderExprTree()) |
| { // does NOT lie within an expression tree |
| // ------------------------------------------------------------- |
| // Initiate the left to inner join transformation. Replace |
| // the InstantiateNull with the original expression that |
| // is the subject for null instantiation. |
| // |
| // We only need to check child 0 of the Like function. |
| // ------------------------------------------------------------- |
| |
| GroupAttributes emptyGA; |
| emptyGA.setCharacteristicOutputs(outerReferences); |
| ValueIdSet emptySet, emptySet1, coveredExpr, coveredSubExpr; |
| ValueIdSet instNullValue; |
| NABoolean containsOuterReferencesInSelectList = FALSE; |
| if (child(0)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = (InstantiateNull *)child(0)->castToItemExpr(); |
| if ((normWARef.inSelectList())&&!(inst->NoCheckforLeftToInnerJoin)) |
| { |
| instNullValue.insert(inst->getValueId()); |
| emptyGA.coverTest( |
| instNullValue, |
| emptySet, |
| coveredExpr, |
| emptySet1, |
| &coveredSubExpr); |
| if(!coveredExpr.isEmpty()) |
| { |
| containsOuterReferencesInSelectList = TRUE; |
| } |
| } |
| if ((!inst->NoCheckforLeftToInnerJoin)&&!containsOuterReferencesInSelectList) |
| { |
| child(0) = child(0)->initiateLeftToInnerJoinTransformation |
| (normWARef); |
| returnValue = TRUE; |
| } |
| } |
| } |
| |
| return returnValue; |
| } // Like::predicateEliminatesNullAugmentedRows() |
| |
| void NotIn::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| |
| |
| if (CmpCommon::getDefault(NOT_IN_ANSI_NULL_SEMANTICS) == DF_OFF) |
| { |
| ItemExpr * tfm = NULL; |
| // ignore ANSI NULL semantics |
| // NOT IN produces same results as equivalent NOT EXISTS |
| tfm = new (normWARef.wHeap()) |
| BiRelat(ITM_EQUAL, |
| child(0), |
| child(1)); |
| if (tfm) |
| { |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } |
| } |
| |
| |
| |
| } // NotIn::transformNode() |
| |
| // NotIn::rewriteMultiColNotInPredicate( ValueId ninValId, ValueIdSet jPred) |
| // This method rewrites a multi-column NotIn (where the left cild is the |
| // item list O1,O2,O3...On and the right child is the item list I1,I2,I3...In) |
| // as follows: |
| // - If all inner and outer columns are not nullable or are nullable |
| // but have no NULL values then the predicate is rewritten into: |
| // ((O1 = I1) AND (O2 = I2) ... AND (On = In)) |
| // - If all inner and outer columns are nullable and may have NULL values |
| // then the predicate is rewritten into: |
| // NOT (( (O1 <> I1) OR (O2 <> I2) .... OR (On <> In)) IS TRUE) |
| // - If m ( where 0 < m < n) inner and outer columns are not nullable or are nullable |
| // but will have no NULL values and p (where p =n-m) columns are nullable and may |
| // have NULL values then the predicate is rewritten to: |
| // ((O1 = I1) AND (O2 = I2) ... AND (Om = Im)) AND |
| // NOT (( (Om+1 <> Im+1) OR (Om_2 <> Im+2) OR (On <> In)) IS TRUE) |
| |
| ValueIdSet NotIn::rewriteMultiColNotInPredicate( |
| ValueId ninValId, |
| ValueIdSet jPred, |
| ValueIdSet selPred) |
| { |
| ItemExpr * itmExpr = ninValId.getItemExpr(); |
| |
| CMPASSERT ( itmExpr->getOperatorType() == ITM_NOT_IN && |
| itmExpr->child(0)->getOperatorType() == ITM_ITEM_LIST && |
| itmExpr->child(1)->getOperatorType() == ITM_ITEM_LIST); |
| |
| ItemExprList outer(itmExpr->child(0).getPtr(),HEAP, ITM_ITEM_LIST, FALSE); |
| ItemExprList inner(itmExpr->child(1).getPtr(),HEAP, ITM_ITEM_LIST, FALSE); |
| |
| CMPASSERT(outer.entries() >0 && |
| outer.entries() == inner.entries() ); |
| |
| ValueIdSet predSet; |
| |
| ItemExpr * nonEquiPred = NULL; |
| |
| jPred-= ninValId; |
| selPred-= ninValId; |
| |
| for (CollIndex i = 0 ; i < outer.entries() ; i++) |
| { |
| const NAType &innerType = inner[i]->getValueId().getType(); |
| const NAType &outerType = outer[i]->getValueId().getType(); |
| |
| if (( !innerType.supportsSQLnull() || |
| jPred.isNotNullable(inner[i]->getValueId())) && |
| ( !outerType.supportsSQLnull() || |
| selPred.isNotNullable(outer[i]->getValueId()))) |
| { |
| ItemExpr * newPred = new (CmpCommon::statementHeap()) |
| BiRelat(ITM_EQUAL, |
| outer[i], |
| inner[i]); |
| |
| newPred->synthTypeAndValueId(TRUE); |
| predSet += newPred->getValueId(); |
| |
| } |
| else |
| { |
| ItemExpr * newPred = new (CmpCommon::statementHeap()) |
| BiRelat(ITM_NOT_EQUAL, |
| outer[i], |
| inner[i]); |
| |
| if (!nonEquiPred) |
| { |
| nonEquiPred = newPred; |
| } |
| else |
| { |
| nonEquiPred = new (CmpCommon::statementHeap()) |
| BiLogic(ITM_OR, |
| nonEquiPred, |
| newPred); |
| } |
| } |
| }//for (CollIndex i = 0 ; i < outer.entries() ; i++) |
| |
| if (nonEquiPred) |
| { |
| nonEquiPred = new (CmpCommon::statementHeap()) |
| UnLogic(ITM_IS_TRUE, |
| nonEquiPred); |
| nonEquiPred = new (CmpCommon::statementHeap()) |
| UnLogic(ITM_NOT, |
| nonEquiPred); |
| nonEquiPred->synthTypeAndValueId(TRUE); |
| |
| predSet += nonEquiPred->getValueId(); |
| |
| }//if (nonEquiPred) |
| |
| return predSet; |
| } |
| |
| |
| // *********************************************************************** |
| // $$$$ Subquery |
| // *********************************************************************** |
| |
| // ----------------------------------------------------------------------- |
| // Subquery::transformNode() |
| // ----------------------------------------------------------------------- |
| void Subquery::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| // Return the address of the expression that was used for replacing |
| // this subquery in an earlier call. |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // L <comparison op> ALL (R ==> L <inverse comparison op> ANY(R |
| // --------------------------------------------------------------------- |
| if (isAnAllSubquery() && normWARef.walkingAnExprTree()) |
| { |
| ExprValueId newRoot = new(normWARef.wHeap()) |
| UnLogic(ITM_NOT, |
| new(normWARef.wHeap()) |
| QuantifiedComp(getComplementOfOperatorType(), |
| child(0).getPtr(), |
| getSubquery(), |
| getAvoidHalloweenR2())); |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(newRoot); |
| |
| newRoot->synthTypeAndValueId(TRUE); |
| |
| ((QuantifiedComp *)newRoot->child(0)->castToItemExpr())-> |
| setCreatedFromALLpred(TRUE); |
| // ----------------------------------------------------------------- |
| // Perform the subquery transformation. |
| // ----------------------------------------------------------------- |
| newRoot->child(0)->transformNode(normWARef, newRoot->child(0), |
| introduceSemiJoinHere, externalInputs); |
| |
| locationOfPointerToMe = newRoot; |
| } |
| else |
| { |
| // ----------------------------------------------------------------- |
| // Perform the Join-Aggregate or SemiJoin subquery transformation. |
| // ----------------------------------------------------------------- |
| transformToRelExpr(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| } |
| |
| // --------------------------------------------------------------------- |
| // Needs to remember this subquery is under an expr, so that when we go |
| // back to transform the new Join and its subtree introduced, we won't |
| // incorrectly use the selection predicates in the subquery to convert |
| // left join into inner join. |
| // --------------------------------------------------------------------- |
| if (normWARef.walkingAnExprTree()) |
| normWARef.setSubqUnderExprTreeFlag(); |
| |
| markAsTransformed(); |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // Subquery::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // Subquery::evaluateCandidateForUnnesting() |
| // See if we can unnest this query, if so set appropriate flags |
| // ----------------------------------------------------------------------- |
| void Subquery::evaluateCandidateForUnnesting(NormWA & normWARef, Join *newJoin, GroupByAgg *newGrby) |
| { |
| NABoolean wasAnAllSubquery = FALSE ; |
| NABoolean skipUnnesting = FALSE ; |
| |
| if (isAnAnySubquery()) |
| wasAnAllSubquery = ((QuantifiedComp*)this)->createdFromALLpred(); |
| |
| |
| if (NOT ((isAnExistsSubquery() && normWARef.isChildOfANot()) || // NOT EXISTS |
| (wasAnAllSubquery || isAnAllSubquery()) || // [NOT] ALL, NOT IN |
| newGrby->aggregateExpr().containsCount() || // COUNT(*), COUNT(a) |
| normWARef.isChildOfAnIsNull() || // IS NULL, IS NOT NULL |
| normWARef.inSelectList() || // in select list |
| normWARef.inGenericUpdateAssign() || |
| normWARef.haveAnOrAncestor())) // have an OR ancestor |
| { |
| newGrby->setContainsNullRejectingPredicates(TRUE); |
| } |
| |
| // Skip Unnesting for the following reasons: |
| // 1) comp_bool_167 = ON means every subquery gets a VEGRegion as |
| // in R2.2 |
| if (CmpCommon::getDefault(COMP_BOOL_167) == DF_ON) |
| { |
| newGrby->setContainsNullRejectingPredicates(FALSE); |
| skipUnnesting = TRUE; |
| } |
| |
| // skip unnesting if we are in the context of a BEFORE trigger. |
| // With BEFORE triggers, subqueries can only be present in the condition |
| // of the trigger. Unnesting in this case will distrupt the logic |
| // of execution of the BEFORE trigger condition |
| if (normWARef.isInBeforeTrigger()) |
| { |
| skipUnnesting = TRUE; |
| } |
| |
| //------------------------------------------------------------------- |
| // If this is a scalar row subquery |
| // and subquery unnesting is enabled, mark this joins node as |
| // a candidate for unnesting during the SemanticQueryOptimize |
| // subphase of the normalizer |
| //-------------------------------------------------------------------- |
| if ((CmpCommon::getDefault(SUBQUERY_UNNESTING) != DF_OFF) && |
| (skipUnnesting == FALSE)) |
| { |
| // Give a hint for QA that we are trying to unnest |
| if (CmpCommon::getDefault(SUBQUERY_UNNESTING) == DF_DEBUG) |
| *CmpCommon::diags() << DgSqlCode(2997) |
| << DgString1("Attempting to unnest Subquery"); |
| |
| newJoin->setCandidateForSubqueryUnnest(TRUE); |
| normWARef.incrementCorrelatedSubqCount(); |
| |
| if (!newGrby->containsNullRejectingPredicates() ) |
| |
| // If phase2 unnesting disabled |
| // do not unnest this subquery as it contains |
| // non-NullRejectingPreds |
| if (CmpCommon::getDefault(SUBQUERY_UNNESTING_P2) == DF_OFF) |
| { |
| // Disable non-NullRejecting predicate unnesting |
| newJoin->setCandidateForSubqueryUnnest(FALSE); |
| normWARef.decrementCorrelatedSubqCount(); |
| |
| // Give a hint to QA that we skipped this subquery |
| if (CmpCommon::getDefault(SUBQUERY_UNNESTING) == DF_DEBUG) |
| { |
| *CmpCommon::diags() << DgSqlCode(2997) |
| << DgString1("Skipping unnesting of Subquery due to NonNullRejecting Predicates"); |
| } |
| |
| } |
| else |
| { |
| // Try to unnest this subquery. |
| newJoin->setCandidateForSubqueryLeftJoinConversion(TRUE); |
| } |
| } |
| } // Subquery::evaluateCandidateForUnnesting() |
| |
| // ----------------------------------------------------------------------- |
| // Subquery::transformToRelExpr() |
| // Perform the Join-Aggregate or SemiJoin subquery transformation. |
| // ----------------------------------------------------------------------- |
| void Subquery::transformToRelExpr(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| return; |
| markAsTransformed(); |
| |
| CMPASSERT(getSubquery()->getOperatorType() == REL_ROOT); |
| |
| RelExpr *childOfRoot = getSubquery()->child(0); |
| |
| if (!isARowSubquery()) |
| { |
| // ----------------------------------------------------------------- |
| // If it is a quantified subquery |
| // + if the subquery contains a DISTINCT, remove it |
| // + if it is an exist and a scalar aggregate with no having clause |
| // return true. |
| // ----------------------------------------------------------------- |
| if (childOfRoot->getOperatorType() == REL_GROUPBY) |
| { |
| GroupByAgg *aggNode = (GroupByAgg *)childOfRoot; |
| |
| // If the group by has a group by list and no aggregate |
| // functions we can eliminate it. Keep ROLLUP groupbys |
| // for now. This could later be improved, if we have |
| // "null-rejecting" predicates that exclude the extra |
| // rows included by the rollup. |
| if (!aggNode->groupExpr().isEmpty() && |
| aggNode->aggregateExpr().isEmpty() && |
| !aggNode->isRollup()) |
| { |
| // Remove the aggNode and pass the selection predicates |
| // and inputs to the new child of the Root |
| childOfRoot = aggNode->child(0); |
| if (childOfRoot->getOperator() == REL_ROOT) |
| { |
| // Another RelRoot. Remove it |
| childOfRoot->child(0)->selectionPred() += |
| childOfRoot->selectionPred(); |
| childOfRoot->child(0)->getGroupAttr()->addCharacteristicInputs( |
| childOfRoot->getGroupAttr()->getCharacteristicInputs()); |
| childOfRoot = childOfRoot->child(0); |
| } |
| getSubquery()->child(0) = childOfRoot; |
| childOfRoot->selectionPred() += aggNode->selectionPred(); |
| childOfRoot->getGroupAttr()->addCharacteristicInputs( |
| aggNode->getGroupAttr()->getCharacteristicInputs()); |
| |
| } |
| } |
| } |
| |
| childOfRoot = getSubquery()->child(0); |
| |
| // ---------------------------------------------------------------------- |
| // If the subquery is a scalar aggregate there are certain optimizations |
| // we can do now. |
| // ---------------------------------------------------------------------- |
| GroupByAgg *aggNode = NULL; |
| if (childOfRoot->getOperatorType() == REL_GROUPBY) |
| { |
| aggNode = (GroupByAgg *)childOfRoot; |
| |
| if (!aggNode->groupExpr().isEmpty()) |
| // Not a scalar Agg since we have a group expression. |
| aggNode = NULL; |
| } |
| |
| // An Exists on top of a scalar aggregate can be transformed to |
| // a boolean constant if there are no having predicates. |
| // Ansi 6.5 GR 1a+2a: COUNT always returns a valid numeric value; |
| // GR 2b(i): other aggs always return AT LEAST "the null value", |
| // which is also a single row. |
| // Thus {EXISTS on top of any agg} == {EXISTS atop guaranteed-single-row} |
| // == {always TRUE}. |
| if (aggNode && |
| aggNode->selectionPred().isEmpty() && |
| isAnExistsSubquery() ) |
| { |
| ItemExpr *tf; |
| |
| if (isNotExists()) |
| tf = new(normWARef.wHeap()) BoolVal(ITM_RETURN_FALSE); |
| else |
| tf = new(normWARef.wHeap()) BoolVal(ITM_RETURN_TRUE); |
| getValueId().replaceItemExpr(tf); |
| tf->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tf; |
| return; |
| } |
| |
| Join * newJoin; |
| ValueIdSet aggregateExpr; // for parameter passing |
| ValueIdSet outputValues; |
| |
| // --------------------------------------------------------------------- |
| // Allocate a new Join, create its group attributes and make it |
| // the parent of the Groupby. |
| // When a subquery contains an outer reference, only a tuple |
| // substitution join (tsj) can be performed. At this stage of the |
| // transformation since we do not know whether the subquery contains |
| // an outer reference, we use a tsj. The normalizer will transform |
| // it to a join later if it detects that a tsj is unnecessary. |
| // --------------------------------------------------------------------- |
| if (isARowSubquery() || normWARef.walkingAnExprTree()) |
| { |
| GroupByAgg * newGrby; |
| |
| // ------------------------------------------------------------- |
| // If the child is a scalar aggregate we need a new groupby agg |
| // only if are some having predicates. Also use the blackbox agg |
| // for NOT EXISTS |
| // ------------------------------------------------------------- |
| if (!aggNode || |
| !aggNode->selectionPred().isEmpty() || |
| isNotExists()) |
| { |
| // ------------------------------------------------------------- |
| // Get aggregate functions and predicates that are required for |
| // performing the subquery transformation. |
| // Also, get the expression for replacing me (a subquery). |
| // aggregateExpr contains the aggregate expressions. |
| // ----------------------------------------------------------------- |
| locationOfPointerToMe = getBlackBoxExpr(normWARef, aggregateExpr); |
| |
| // ------------------------------------------------------------- |
| // Allocate a new Groupby. It has an empty Groupby list. |
| // Its operand is the subquery tree. |
| // aggregateExpr contains the aggregate expressions. |
| // ------------------------------------------------------------- |
| newGrby = new(normWARef.wHeap()) |
| GroupByAgg(getSubquery(), aggregateExpr); |
| |
| newGrby->setGroupAttr(new(normWARef.wHeap()) |
| GroupAttributes()); |
| |
| // ------------------------------------------------------------- |
| // Add the inputs that the RelRoot under the Group By has to |
| // those of the Group By. |
| // ------------------------------------------------------------- |
| newGrby->getPotentialOutputValues(outputValues); |
| newGrby->getGroupAttr()->addCharacteristicOutputs(outputValues); |
| newGrby->getGroupAttr()->addCharacteristicInputs |
| (newGrby->child(0)->getGroupAttr()->getCharacteristicInputs()); |
| |
| |
| } |
| else |
| { |
| newGrby = aggNode; |
| if (isAnExistsSubquery()) |
| { |
| // No predicate to apply |
| locationOfPointerToMe = (ItemExpr *) NULL; |
| } |
| else if (isAnAnySubquery() || isAnAllSubquery()) |
| { |
| // ------------------------------------------------------------- |
| // This codeblock cloned from QuantifiedComp::getBlackBoxExpr(). |
| // ------------------------------------------------------------- |
| // Replace the quantified predicate with a corresponding |
| // predicate that does not have a quantifier. |
| // ------------------------------------------------------------- |
| OperatorTypeEnum otype; |
| otype = ((QuantifiedComp*)this)->getSimpleOperatorType(); |
| |
| BiRelat * pred = new(normWARef.wHeap()) |
| BiRelat(otype, child(0).getPtr(), |
| getSubquery()->selectList()); |
| |
| pred->specialMultiValuePredicateTransformation() = TRUE; |
| |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(pred); |
| |
| pred->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = pred; |
| } |
| else // must be row subquery |
| { |
| // Simply replace the row subquery with the selectlist |
| // list of the subquery. We already have a guarantee |
| // that at most one row will be returned. |
| |
| // Depending on if the subquery has already been flatten, |
| // we either have to return the first element or the whole |
| // of the select list. |
| |
| RelRoot *subqRoot = (RelRoot *) getSubquery(); |
| |
| if (normWARef.inValueIdProxy()) |
| { |
| locationOfPointerToMe = subqRoot->compExpr()[0]; |
| } |
| else |
| { |
| locationOfPointerToMe = getSubquery()->selectList(); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------- |
| // Allocate a new tuple-substitution join. |
| // It doesn't need to be a semi join since the group by has |
| // no group by list and therefore is guaranteed to provide only |
| // one value. |
| // ----------------------------------------------------------------- |
| newJoin = new(normWARef.wHeap()) |
| Join(introduceSemiJoinHere, newGrby, REL_TSJ); |
| newJoin->setGroupAttr(new(normWARef.wHeap()) GroupAttributes()); |
| evaluateCandidateForUnnesting(normWARef, newJoin, newGrby); |
| |
| } |
| else |
| { |
| // ------------------------------------------------------------- |
| // Get aggregate functions and predicates that are required for |
| // performing the subquery transformation. |
| // Also, get the expression for replacing me (a subquery). |
| // aggregateExpr contains the aggregate expressions. |
| // ----------------------------------------------------------------- |
| locationOfPointerToMe = getBlackBoxExpr(normWARef, aggregateExpr); |
| |
| if(isSelectivitySetUsingHint()) |
| { |
| locationOfPointerToMe->setSelectivitySetUsingHint(); |
| locationOfPointerToMe->setSelectivityFactor(getSelectivityFactor()); |
| } |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| aggregateExpr.unparse(unp); |
| cerr << "QComp BlkBox: " << unp << " " << (void*)this << endl; |
| ) |
| |
| // ----------------------------------------------------------------- |
| // Allocate a new semijoin or anti semijoin |
| // ----------------------------------------------------------------- |
| OperatorTypeEnum joinOp = REL_SEMITSJ; |
| if (isAnAllSubquery() || isNotExists()) |
| joinOp = REL_ANTI_SEMITSJ; |
| |
| newJoin = new(normWARef.wHeap()) |
| Join(introduceSemiJoinHere, getSubquery(), joinOp); |
| |
| |
| newJoin->setGroupAttr(new(normWARef.wHeap()) GroupAttributes()); |
| |
| if (CmpCommon::getDefault |
| (SEMIJOIN_TO_INNERJOIN_TRANSFORMATION) != DF_OFF) |
| { |
| if (joinOp == REL_SEMITSJ) |
| { |
| newJoin->setCandidateForSemiJoinTransform(TRUE); |
| normWARef.setContainsSemiJoinsToBeTransformed(TRUE); |
| } |
| } |
| |
| if ((joinOp == REL_SEMITSJ) && (aggNode != NULL)) |
| { |
| // setup unnesting flags if applicable |
| evaluateCandidateForUnnesting(normWARef, newJoin, aggNode); |
| } |
| |
| // If this was an EXISTS the predicate in locationOfPointerToMe |
| // should be null |
| if (isAnExistsSubquery()) |
| { |
| CMPASSERT(!locationOfPointerToMe.getPtr()); |
| } |
| else |
| { |
| // Move the predicate to the ON clause of the semiJoin. |
| // This is because we want to preserve one instance of the row |
| // that satisfies the subquery predicate. |
| newJoin->joinPred() += locationOfPointerToMe->getValueId(); |
| |
| if (isAnAllSubquery() && |
| !normWARef.walkingAnExprTree() && |
| ( CmpCommon::getDefault(COMP_BOOL_139) == DF_ON)) |
| { |
| ItemExpr *filt = new(normWARef.wHeap()) |
| UnLogic(ITM_IS_NOT_NULL, child(0).getPtr()); |
| filt->synthTypeAndValueId(TRUE); |
| |
| newJoin->selectionPred() += filt->getValueId(); |
| } |
| |
| locationOfPointerToMe = (ItemExpr *) NULL; |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // Initialize the dataflow for the tuple-substitution join that was |
| // allocated above. |
| // --------------------------------------------------------------------- |
| newJoin->getPotentialOutputValues(outputValues); |
| newJoin->getGroupAttr()->addCharacteristicOutputs(outputValues); |
| |
| // subtract from the external inputs to the joins values that are |
| // produced by the children of the join. |
| ValueIdSet realExternalInputs = externalInputs; |
| realExternalInputs -= outputValues; |
| newJoin->getGroupAttr()->addCharacteristicInputs(realExternalInputs); |
| |
| newJoin->child(1)->getGroupAttr()->addCharacteristicInputs(externalInputs); |
| newJoin->child(1)->getGroupAttr()->addCharacteristicInputs |
| (newJoin->child(0)->getGroupAttr()->getCharacteristicOutputs()); |
| |
| // Transfer the avoidHalloweenR2 flag from the Subquery to the new |
| // join. If set in the join, this will cause the join to be |
| // implemented as a hash join. This causes the source to be blocked |
| // which avoid the halloween problem. |
| // |
| if (newJoin->isSemiJoin() || newJoin->isAntiSemiJoin()) |
| { |
| newJoin->setAvoidHalloweenR2(avoidHalloweenR2()); |
| } |
| else if(getAvoidHalloweenR2()) |
| { |
| getAvoidHalloweenR2()->resetAvoidHalloweenR2(); |
| } |
| |
| // CMPASSERT(!avoidHalloweenR2() || |
| // (newJoin->isSemiJoin() || newJoin->isAntiSemiJoin())); |
| |
| // --------------------------------------------------------------------- |
| // Assign the SemiJoin to the original query tree. |
| // A new SemiJoin is added in introduceSemiJoinHere. It has not been |
| // transformed yet. It will eventually be transformed either by |
| // it's parent or one of it's children if the SemiJoin becomes |
| // became the ancestor of the child while the child was being |
| // transformed. |
| // --------------------------------------------------------------------- |
| introduceSemiJoinHere = newJoin; |
| |
| // NB: The predicate just added to the joinPred may still contain |
| // some row subqueries. |
| |
| // --------------------------------------------------------------------- |
| // Transform the item expression that replaces the subquery. |
| // --------------------------------------------------------------------- |
| // if (locationOfPointerToMe.getPtr()) |
| // locationOfPointerToMe |
| // ->transformNode(normWARef, locationOfPointerToMe, |
| // introduceSemiJoinHere, externalInputs); |
| |
| } // Subquery::transformToRelExpr() |
| |
| // ----------------------------------------------------------------------- |
| // Subquery::getBlackBoxExpr() |
| // ----------------------------------------------------------------------- |
| ItemExpr * Subquery::getBlackBoxExpr(NormWA & /*normWARef*/, |
| ValueIdSet & /*blackBoxExpr*/) |
| { |
| ABORT("Internal Error: Implementation for Subquery::getBlackBoxExpr() is missing"); |
| return NULL; |
| } // Subquery::getBlackBoxExpr() |
| |
| // ----------------------------------------------------------------------- |
| // A method for inverting (finding the inverse of) the operators |
| // in a subtree that is rooted in a NOT. |
| // ----------------------------------------------------------------------- |
| ItemExpr * Subquery::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| return ItemExpr::transformSubtreeOfNot(normWARef, falseOrNot); |
| } // Subquery::transformSubtreeOfNot(normWARef); |
| |
| // ----------------------------------------------------------------------- |
| // Invert the quantified comparison type when this operator |
| // is the child of a NOT. |
| // ----------------------------------------------------------------------- |
| OperatorTypeEnum Subquery::getComplementOfOperatorType() |
| { |
| |
| switch(getOperatorType()) |
| { |
| case ITM_GREATER_ALL: |
| return ITM_LESS_EQ_ANY; |
| case ITM_GREATER_EQ_ALL: |
| return ITM_LESS_ANY; |
| case ITM_EQUAL_ALL: |
| return ITM_NOT_EQUAL_ANY; |
| case ITM_NOT_EQUAL_ALL: |
| return ITM_EQUAL_ANY; |
| case ITM_LESS_EQ_ALL: |
| return ITM_GREATER_ANY; |
| case ITM_LESS_ALL: |
| return ITM_GREATER_EQ_ANY; |
| |
| case ITM_LESS_EQ_ANY: |
| return ITM_GREATER_ALL; |
| case ITM_LESS_ANY: |
| return ITM_GREATER_EQ_ALL; |
| case ITM_NOT_EQUAL_ANY: |
| return ITM_EQUAL_ALL; |
| case ITM_EQUAL_ANY: |
| return ITM_NOT_EQUAL_ALL; |
| case ITM_GREATER_ANY: |
| return ITM_LESS_EQ_ALL; |
| case ITM_GREATER_EQ_ANY: |
| return ITM_LESS_ALL; |
| |
| default: |
| return getOperatorType(); |
| } |
| |
| } // Subquery::getComplementOfOperatorType |
| |
| // *********************************************************************** |
| // $$$$ RowSubquery |
| // *********************************************************************** |
| ItemExpr * RowSubquery::getBlackBoxExpr(NormWA & normWARef , |
| ValueIdSet & blackBoxExpr) |
| { |
| // --------------------------------------------------------------------- |
| // The oneRow aggregate function implements the row subquery semantics. |
| // --------------------------------------------------------------------- |
| ItemExpr * aggExpr = new(normWARef.wHeap()) |
| Aggregate(ITM_ONE_ROW, |
| getSubquery()->selectList()); |
| aggExpr->synthTypeAndValueId(TRUE); |
| aggExpr->convertToValueIdSet(blackBoxExpr); |
| ((Aggregate *)aggExpr)->isOneRowTransformed_ = FALSE; |
| aggExpr = aggExpr->transformOneRowAggregate( normWARef ); |
| |
| // replace the definition of this valueId |
| ItemExpr * aggChild = aggExpr->child(0)->castToItemExpr(); |
| if ((aggChild->getOperatorType() == ITM_CAST) || |
| (aggChild->getOperatorType() == ITM_INSTANTIATE_NULL)) |
| { |
| // The valueId referes to the child of aggExpr, if the aggregate expression |
| // contains a child that is not a list |
| getValueId().replaceItemExpr(aggChild); |
| getValueId().changeType( |
| ( (Cast *) aggChild )->getType() ); |
| return aggChild; |
| } |
| else |
| { |
| getValueId().replaceItemExpr(aggExpr); |
| return aggExpr; |
| } |
| } // RowSubquery::getBlackBoxExpr() |
| |
| |
| // *********************************************************************** |
| // $$$$ EXISTS |
| // *********************************************************************** |
| // ----------------------------------------------------------------------- |
| // A method for inverting (finding the inverse of) the operators |
| // in a subtree that is rooted in a NOT. |
| // ----------------------------------------------------------------------- |
| ItemExpr * Exists::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| ItemExpr *rv = this; |
| |
| // Genesis case 10-070717-7303 |
| // Turn an EXISTS subquery into the NOT_EXISTS flavor, which can be |
| // transformed into an anti-semijoin. |
| // This fix was added late and it can be turned off by default for safety. |
| // This is the only place where we generate the ITM_NOT_EXISTS operator. |
| // Generate NOT_EXISTS only if we will be making the semijoin/anti-semijoin |
| // transformation. If we will be making the Join-Aggregate transformation |
| // (as indicated by walkingAnExprTreeCount > 1) then do not generate NOT_EXISTS |
| // Note that the caller UnLogic::transformSubtreeOfNot increments |
| // WalkingAnExprTreeCount_ by 1 and we are trying to account for that here. |
| |
| if ((CmpCommon::getDefault(COMP_BOOL_162) == DF_ON) && |
| (normWARef.getWalkingAnExprTreeCount() == 1)) |
| { |
| rv = new(normWARef.wHeap()) Exists(getSubquery()); |
| |
| // invert the operator type |
| if (isNotExists()) |
| rv->setOperatorType(ITM_EXISTS); |
| else |
| rv->setOperatorType(ITM_NOT_EXISTS); |
| } |
| |
| // the caller (UnLogic::transformNode()) expects that we do NOT |
| // call synthTypeAndValueId() on the result |
| |
| return rv; |
| } |
| |
| ItemExpr * Exists::getBlackBoxExpr(NormWA & normWARef, ValueIdSet & blackBoxExpr) |
| { |
| ItemExpr * aggExpr; |
| |
| // --------------------------------------------------------------------- |
| // The oneTrue aggregate function implements the exists subquery |
| // semantics. The aggregate function is required only when the |
| // exists subquery has an OR ancestor. Otherwise, a semijoin or |
| // anti-semijoin (for NOT EXISTS) is performed between the operator |
| // that contains the subquery and the subquery tree. |
| // --------------------------------------------------------------------- |
| if (normWARef.walkingAnExprTree()) |
| { |
| ItemExpr *tf = new(normWARef.wHeap()) BoolVal(ITM_RETURN_TRUE); |
| |
| aggExpr = new(normWARef.wHeap()) Aggregate(ITM_ONE_TRUE, tf); |
| |
| CMPASSERT(NOT isNotExists()); |
| |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(aggExpr); |
| |
| aggExpr->synthTypeAndValueId(TRUE); |
| aggExpr->convertToValueIdSet(blackBoxExpr); |
| } |
| else |
| { |
| blackBoxExpr.clear(); |
| aggExpr = NULL; |
| } |
| |
| return aggExpr; |
| |
| } // Exists::getBlackBoxExpr() |
| |
| // ----------------------------------------------------------------------- |
| // $$$$ QuantifiedComp |
| // ----------------------------------------------------------------------- |
| ItemExpr * QuantifiedComp::transformMultiValuePredicate( |
| NABoolean flattenSubqueries, |
| ChildCondition tfmIf) |
| { |
| if (tfmIf == ANY_CHILD_RAW) |
| return dissectOutSubqueries(this, flattenSubqueries, FALSE); |
| return NULL; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Invert the quantified comparison type when this operator |
| // is the child of a NOT. |
| // ----------------------------------------------------------------------- |
| ItemExpr * QuantifiedComp::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| |
| if (falseOrNot != ITM_NOT) |
| return this; |
| |
| OperatorTypeEnum otype = getComplementOfOperatorType(); |
| |
| if (otype != getOperatorType()) // any ALL inverted to an ANY |
| { |
| ItemExpr * newComp = new(normWARef.wHeap()) |
| QuantifiedComp(otype, |
| child(0).getPtr(), |
| getSubquery(), |
| getAvoidHalloweenR2()); |
| return newComp; |
| } |
| else |
| return this; |
| } // QuantifiedComp::transformSubtreeOfNot() |
| |
| OperatorTypeEnum QuantifiedComp::getSimpleOperatorType() const |
| { |
| OperatorTypeEnum otype = NO_OPERATOR_TYPE; |
| // --------------------------------------------------------------------- |
| // Deduce the comparison operator that is used together with the |
| // quantifier. |
| // --------------------------------------------------------------------- |
| switch (getOperatorType()) |
| { |
| case ITM_GREATER_ALL: |
| otype = ITM_GREATER; |
| break; |
| case ITM_GREATER_EQ_ALL: |
| otype = ITM_GREATER_EQ; |
| break; |
| case ITM_EQUAL_ALL: |
| otype = ITM_EQUAL; |
| break; |
| case ITM_NOT_EQUAL_ALL: |
| otype = ITM_NOT_EQUAL; |
| break; |
| case ITM_LESS_EQ_ALL: |
| otype = ITM_LESS_EQ; |
| break; |
| case ITM_LESS_ALL: |
| otype = ITM_LESS; |
| break; |
| |
| case ITM_EQUAL_ANY: |
| otype = ITM_EQUAL; |
| break; |
| case ITM_NOT_EQUAL_ANY: |
| otype = ITM_NOT_EQUAL; |
| break; |
| case ITM_GREATER_ANY: |
| otype = ITM_GREATER; |
| break; |
| case ITM_GREATER_EQ_ANY: |
| otype = ITM_GREATER_EQ; |
| break; |
| case ITM_LESS_EQ_ANY: |
| otype = ITM_LESS_EQ; |
| break; |
| case ITM_LESS_ANY: |
| otype = ITM_LESS; |
| break; |
| |
| default: |
| ABORT("Internal error in QuantifiedComp::getSimpleOperatorType"); |
| } |
| |
| return otype; |
| } |
| |
| ItemExpr * QuantifiedComp::getBlackBoxExpr(NormWA & normWARef, ValueIdSet & blackBoxExpr) |
| { |
| ItemExpr * rv; |
| OperatorTypeEnum otype = getSimpleOperatorType(); |
| |
| // --------------------------------------------------------------------- |
| // Replace the quantified predicate with a corresponding predicate |
| // that does not have a quantifier. |
| // --------------------------------------------------------------------- |
| ItemExpr * pred = new(normWARef.wHeap()) |
| BiRelat(otype, child(0).getPtr(), |
| getSubquery()->selectList()); |
| // --------------------------------------------------------------------- |
| // Next is a hack to influence a later call to |
| // BiRelat::transformNode/MultiValuePredicate on this pred. |
| // |
| // Probably would not be necessary if our caller Subquery::transformToRelExpr |
| // extracted subquery-containing preds from the blackBoxExpr |
| // (built by convertToValueIdSet below and returned by this method) |
| // and processed them to introduce GroupByAggs. |
| // But this special hack works for now. |
| // --------------------------------------------------------------------- |
| ((BiRelat *)pred)->specialMultiValuePredicateTransformation() = TRUE; |
| |
| // If this is ALL subquery and we are not walking an expression |
| // tree we will generate an anti semijoin looking for a row where |
| // the predicate is not true (either false or null) |
| if (isAnAllSubquery() && !normWARef.walkingAnExprTree()) |
| { |
| if (CmpCommon::getDefault(NOT_IN_OPTIMIZATION) == DF_ON && |
| getOperatorType() == ITM_NOT_EQUAL_ALL) |
| { |
| |
| //create a new NotIn with the child0 and child1 of birelat |
| // |
| pred = new(normWARef.wHeap()) |
| NotIn(pred->child(0), |
| pred->child(1)); |
| } |
| else |
| { |
| pred = new(normWARef.wHeap()) |
| UnLogic(ITM_IS_TRUE, pred); |
| pred = new(normWARef.wHeap()) |
| UnLogic(ITM_NOT,pred); |
| |
| // the code below is not required anymore after implementing the NOT IN optimization |
| // and may be removed in the future (maybe in R2.6) |
| if ( (CmpCommon::getDefault(COMP_BOOL_139) == DF_ON) |
| && (otype == ITM_NOT_EQUAL)) |
| { |
| // Instead of the NOT(A <> B) create an (A = B) predicate |
| // This allows the optimizer to take advantage of the |
| // equi-predicate (Hash join will generate hash expression, |
| // match partition joins will be considered ...) |
| // |
| |
| // Only do this is all of the RHS values are not null. |
| // |
| ItemExpr *selList = getSubquery()->selectList(); |
| |
| NABoolean allNotNull = TRUE; |
| |
| while(selList) |
| { |
| if(selList->getOperatorType() == ITM_ITEM_LIST) |
| { |
| if(selList->child(1)-> |
| getValueId().getType().supportsSQLnullLogical()) |
| { |
| |
| allNotNull = FALSE; |
| break; |
| } |
| selList = selList->child(0); |
| } |
| else |
| { |
| if(selList->getValueId().getType().supportsSQLnullLogical()) |
| { |
| allNotNull = FALSE; |
| } |
| selList = NULL; |
| } |
| } |
| |
| if(allNotNull) |
| { |
| pred = new(normWARef.wHeap()) |
| BiRelat(ITM_EQUAL, child(0).getPtr(), |
| getSubquery()->selectList()); |
| |
| // Make sure to mark this new pred as was done above. |
| // |
| ((BiRelat *)pred)-> |
| specialMultiValuePredicateTransformation() = TRUE; |
| } |
| } |
| } |
| } |
| |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(pred); |
| |
| pred->synthTypeAndValueId(TRUE); |
| |
| // --------------------------------------------------------------------- |
| // If this ANY subquery does not have an OR, NOT, ISNULL or IS UNKNOWN |
| // as an ancestor, then the magic aggregate function is not required. |
| // --------------------------------------------------------------------- |
| if (isAnAnySubquery() && normWARef.walkingAnExprTree()) |
| { |
| // anyTrue(left operand = select list of subquery) |
| ItemExpr * aggExpr = new(normWARef.wHeap()) |
| Aggregate(ITM_ANY_TRUE, pred); |
| aggExpr->synthTypeAndValueId(TRUE); |
| aggExpr->convertToValueIdSet(blackBoxExpr); |
| |
| rv = aggExpr; |
| } |
| else |
| { |
| pred->convertToValueIdSet(blackBoxExpr); |
| |
| rv = pred; |
| } |
| |
| // --------------------------------------------------------------------- |
| // We cannot have an ALL subquery if we are walking an expression |
| // tree. It would have been converted to a NOT ANY |
| // assert it to make sure. |
| // --------------------------------------------------------------------- |
| CMPASSERT(!isAnAllSubquery() || !normWARef.walkingAnExprTree()); |
| |
| return rv; |
| |
| } // QuantifiedComp::getBlackBoxExpr() |
| |
| // *********************************************************************** |
| // $$$$ UnLogic |
| // member functions for class UnLogic |
| // *********************************************************************** |
| ItemExpr * UnLogic::transformMultiValuePredicate( |
| NABoolean flattenSubqueries, |
| ChildCondition condBiRelat) |
| { |
| // An MVP of the form "(an,item,list) IS [NOT] NULL" |
| // is adequately handled in UnLogic::transformIsNull, |
| // so no need to code for these two cases here. |
| if (getOperatorType() == ITM_IS_NULL || |
| getOperatorType() == ITM_IS_NOT_NULL) |
| return NULL; |
| |
| ItemExpr *tfm = |
| child(0)->transformMultiValuePredicate(flattenSubqueries, condBiRelat); |
| |
| if (tfm && condBiRelat != ANY_CHILD_RAW) |
| { |
| tfm = new HEAP UnLogic(getOperatorType(), tfm); |
| CMPASSERT(tfm); |
| } |
| |
| return tfm; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // If this item is of the form |
| // NOT(IS_UNKNOWN(x)) or IS_NOT_UNKNOWN(x) |
| // where x is |
| // NOT*(truefalse(y)) (i.e. zero or more NOTs) |
| // where truefalse is any of the "IS ..." predicates (yield T or F, never UNK), |
| // then return item y; else return NULL. |
| // |
| // The caller can determine if item y has any side effects (e.g. aggregation); |
| // if none, then this entire item can be eliminated as it always evals as TRUE. |
| // ----------------------------------------------------------------------- |
| ItemExpr * UnLogicMayBeAnEliminableTruthTest(ItemExpr *unlogic, NABoolean aggOK) |
| { |
| ItemExpr *itm = unlogic; |
| Int32 notVal = 0; |
| for (; itm->getOperatorType() == ITM_NOT; notVal++) |
| itm = itm->child(0); |
| notVal %= 2; |
| if ((notVal && itm->getOperatorType() == ITM_IS_UNKNOWN) || |
| (!notVal && itm->getOperatorType() == ITM_IS_NOT_UNKNOWN)) |
| { |
| itm = itm->child(0); |
| if (!canBeSQLUnknown(itm, FALSE)) |
| { |
| // Drill down past any more UnLogics |
| // ## Should make a virtual ItemExpr::isUnLogic() method ... |
| while (itm->getOperatorType() == ITM_NOT || |
| itm->getOperatorType() == ITM_IS_NULL || |
| itm->getOperatorType() == ITM_IS_NOT_NULL || |
| itm->getOperatorType() == ITM_IS_TRUE || |
| itm->getOperatorType() == ITM_IS_FALSE || |
| itm->getOperatorType() == ITM_IS_UNKNOWN || |
| itm->getOperatorType() == ITM_IS_NOT_UNKNOWN) |
| itm = itm->child(0); |
| |
| if (!itm->isASubquery() && |
| (aggOK || !itm->isAnAggregate())) |
| { |
| // Now we return itm, which will be one of: |
| // . an EXISTS or MATCH predicate |
| // . an IS [NOT] NULL operand (e.g. colref, item-list, row-subq) |
| // . an aggregate (e.g. ITM_ONE_ROW) |
| |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| cerr << (Int32)itm->getOperatorType() << " " << (void *)itm |
| << " of " |
| << (Int32)unlogic->getOperatorType() << " " << (void*)unlogic |
| << " may be eliminable" << endl; |
| ) |
| return itm; |
| } |
| } |
| } |
| return NULL; |
| } // UnLogicMayBeAnEliminableTruthTest() |
| |
| |
| // |
| // transformNode2() - a helper routine for UnLogic::transformNode() |
| // |
| // NOTE: The code in this routine came from the previous version of |
| // UnLogic::transformNode(). It has been pulled out |
| // into a separate routine so that the C++ compiler will produce |
| // code that needs signficantly less stack space for the |
| // UnLogic::transformNode() routine which get used in |
| // recursive code. |
| // |
| void UnLogic::transformNode2(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| unp = ""; |
| unparse(unp); |
| cerr << (Int32)getOperatorType() << " " |
| << (Int32)getValueId() << " " |
| << (void *)this << " " |
| << unp << endl; |
| ) |
| |
| // First convert an IS FALSE to a NOT if they are equivalent. |
| // If we are transforming a constraint, we cannot use its |
| // allowsUnknown property -- that would be putting the cart before the horse |
| // -- that property is indeed what we wish to enforce in the contraint, |
| // and rely on when not transforming a constraint. |
| // |
| if (getOperatorType() == ITM_IS_FALSE && |
| !normWARef.inConstraints() && |
| (!normWARef.walkingAnExprTree() || |
| !canBeSQLUnknown(child(0))) |
| ) |
| { |
| setOperatorType(ITM_NOT); |
| } |
| |
| if (!normWARef.isChildOfANot()) |
| { |
| // This short-circuits some of the logic in transformIsNull(), |
| // doing some special cases a bit faster and I think more completely: |
| // |
| // We can eliminate ourself entirely if we will always eval as TRUE ... |
| // (Too dangerous to do on an aggregate; let GroupByAgg handle that!) |
| // |
| NABoolean eliminate = TRUE; |
| ItemExpr *descendant = UnLogicMayBeAnEliminableTruthTest(this); |
| if (!descendant) |
| { |
| eliminate = FALSE; |
| UnLogic notVal(ITM_NOT, this); |
| descendant = UnLogicMayBeAnEliminableTruthTest(¬Val); |
| notVal.setChild(0, NULL); // so that "this" won't be destructed |
| } |
| if (descendant) |
| { |
| DBGSETDBG( "TRANSFORM_DEBUG" ) |
| DBGIF( |
| cerr << (eliminate ? "Eliminating" : "Collapsing") << " pred " |
| << (Int32)getOperatorType() << " " << (void *)descendant |
| << " in this " << (void *)this << endl; |
| ) |
| markAsTransformed(); |
| if (eliminate) |
| locationOfPointerToMe = NULL; |
| else |
| { |
| locationOfPointerToMe = new(normWARef.wHeap()) |
| BoolVal(ITM_RETURN_FALSE); |
| locationOfPointerToMe->synthTypeAndValueId(TRUE); |
| } |
| setReplacementExpr(locationOfPointerToMe); |
| return; |
| } |
| } |
| |
| } |
| void UnLogic::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| UnLogic::transformNode2( normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs ) ; |
| switch(getOperatorType()) |
| { |
| case ITM_NOT: |
| case ITM_IS_FALSE: |
| { |
| ItemExpr * rv = transformSubtreeOfNot(normWARef, getOperatorType()); |
| // If the transformation has eliminated this NOT, then |
| // replace it with its substitute expression. |
| if (rv != this) |
| { |
| // Replace the valueId of NOT with the negated rv |
| getValueId().replaceItemExpr(rv); |
| |
| // All the virtual functions transfromSubtreeOfNot() should not |
| // call synthTypeAndValueId() because we want to do it here. |
| rv->synthTypeAndValueId(TRUE); |
| |
| locationOfPointerToMe = rv; |
| rv->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| } |
| else |
| { |
| // this must be a NOT or IS FALSE |
| markAsTransformed(); |
| normWARef.setNotFlag(); |
| child(0)->getReplacementExpr()->transformNode(normWARef, |
| child(0), |
| introduceSemiJoinHere, |
| externalInputs); |
| normWARef.restoreNotFlag(); |
| } |
| } |
| break; |
| case ITM_IS_NULL: |
| case ITM_IS_NOT_NULL: |
| case ITM_IS_UNKNOWN: |
| case ITM_IS_NOT_UNKNOWN: |
| transformIsNull(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| break; |
| case ITM_IS_TRUE: |
| { |
| // See comments in UnLogic::transformSubtreeOfNot() |
| // about why testing !normWARef.inConstraints() is unnecessary here. |
| |
| if (!canBeSQLUnknown(child(0)) || !normWARef.walkingAnExprTree()) |
| { |
| locationOfPointerToMe = child(0)->getReplacementExpr(); |
| child(0)->getReplacementExpr()->transformNode(normWARef, |
| locationOfPointerToMe, |
| introduceSemiJoinHere, |
| externalInputs); |
| } |
| else |
| { |
| normWARef.setNotFlag(); |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| normWARef.restoreNotFlag(); |
| |
| if (!child(0).getPtr()) |
| locationOfPointerToMe = NULL; |
| else if (!canBeSQLUnknown(child(0))) |
| locationOfPointerToMe = child(0); |
| } |
| } |
| break; |
| default: |
| CMPASSERT(FALSE); |
| break; |
| } |
| |
| markAsTransformed(); |
| applyTruthTable(this, locationOfPointerToMe); |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // UnLogic::transformNode() |
| |
| // ----------------------------------------------------------------------- |
| // A method for transforming a subtree rooted in a NOT. |
| // ----------------------------------------------------------------------- |
| ItemExpr * UnLogic::transformSubtreeOfNot(NormWA & normWARef, |
| OperatorTypeEnum falseOrNot) |
| { |
| ItemExpr * newChild = NULL; |
| |
| // Only NOT or IS FALSE is processed here |
| if (getOperatorType() != ITM_NOT && |
| getOperatorType() != ITM_IS_FALSE) |
| return this; |
| |
| if (getOperatorType() == ITM_NOT && // we do not want to apply this transformation NOT IN (...) |
| child(0)->getOperatorType() == ITM_OR) // type queries. The jump table implementation of |
| { |
| ValueIdList eqList; |
| NABoolean status = convertToValueIdList(eqList,NULL, ITM_OR); |
| if (!status) |
| { |
| NABoolean leaveAsOrExpr = TRUE; |
| BiRelat * eqExpr = NULL; |
| ValueId leftChildId; |
| for (CollIndex i = 0; (leaveAsOrExpr && (i < eqList.entries())); i++) |
| { |
| if (eqList[i].getItemExpr()->getOperatorType() != ITM_EQUAL) |
| leaveAsOrExpr = FALSE; |
| |
| eqExpr = (BiRelat *) eqList[i].getItemExpr() ; |
| if (i == 0) |
| leftChildId = eqExpr->child(0)->getValueId() ; |
| if (eqExpr->child(0)->getValueId() != leftChildId) |
| leaveAsOrExpr = FALSE; // left side of '=' has different expressions, a = 1 OR b = 1 |
| } |
| if (leaveAsOrExpr) |
| return this; // do not apply DeMorganRule here as OR will handle them efficiently. |
| } |
| } |
| |
| // If my child is a redundant IS TRUE eliminate it first |
| while (child(0)->getOperatorType() == ITM_IS_TRUE) |
| { |
| // No need to also check !normWARef.inConstraints() |
| // since if grandchild can't be Unknown, there must be a |
| // physical or logical CHECK(col IS NOT NULL) constraint, |
| // and that constraint will be violated at run-time |
| // rather than this CHECK(pred IS TRUE) constraint |
| // (and it doesn't matter which constraint is violated as long as one is). |
| // |
| if (!canBeSQLUnknown(child(0)->child(0))) |
| child(0) = child(0)->child(0); |
| else |
| break; |
| } |
| |
| // First convert a child from IS FALSE to a NOT if they are equivalent. |
| if (child(0)->getOperatorType() == ITM_IS_FALSE && |
| !normWARef.inConstraints() && |
| !canBeSQLUnknown(child(0))) |
| { |
| child(0)->setOperatorType(ITM_NOT); |
| } |
| |
| // -------------------------------------------------------------------- |
| // Eliminate redundant NOTs: NOT(NOT(x)) ==> x |
| // NOT(NOT(p)) ==> p |
| // ISFALSE(NOT(p)) ==> ISTRUE(p) |
| // -------------------------------------------------------------------- |
| if (child(0)->getOperatorType() == ITM_NOT) |
| { |
| // Eliminate the child NOT here. |
| // Perform the transformation of the grandChild independently |
| // in the caller. This is because when two NOTs are eliminated |
| // it is sufficient to call transformNode; a call to transformSubtreeOfNot |
| // may not be required on the child. For example, |
| // NOT ( NOT (a = 10) ) should be a = 10 and not a <> 10. |
| newChild = child(0)->child(0)->castToItemExpr(); |
| |
| if (falseOrNot == ITM_IS_FALSE) |
| { |
| newChild = new(normWARef.wHeap()) |
| UnLogic(ITM_IS_TRUE, newChild); |
| } |
| } |
| else if (child(0)->getOperatorType() == ITM_IS_NOT_NULL && |
| child(0)->child(0)->getOperatorType() != ITM_ITEM_LIST) |
| { |
| // NOT (x) IS NOT NULL ==> x IS NULL provided x is not a list |
| newChild = child(0); |
| newChild->setOperatorType(ITM_IS_NULL); |
| setChild(0, NULL); |
| } |
| else if (child(0)->getOperatorType() == ITM_IS_NULL && |
| child(0)->child(0)->getOperatorType() != ITM_ITEM_LIST) |
| { |
| // NOT (x) IS NULL ==> x IS NOT NULL provided x is not a list |
| newChild = child(0); |
| newChild->setOperatorType(ITM_IS_NOT_NULL); |
| setChild(0, NULL); |
| } |
| else // child is not a NOT |
| { |
| // Invert the subtree |
| normWARef.setNotFlag(); |
| |
| // Perform transformations in my child subtree |
| newChild = child(0)->transformSubtreeOfNot(normWARef, falseOrNot); |
| |
| normWARef.restoreNotFlag(); |
| |
| } // child is not a NOT |
| |
| // If the child of the NOT has been inverted, it already reflects the |
| // effect of the NOT. Otherwise, inversion was not possible, |
| // so return the original expression. |
| if (newChild == child(0)) |
| newChild = this; |
| |
| // Do not synthTypeAndValueId() yet |
| return newChild; |
| |
| } // UnLogic::transformSubtreeOfNot() |
| |
| // ----------------------------------------------------------------------- |
| // A method for transforming a subtree rooted in an ISNULL or IS NOT NULL. |
| // ----------------------------------------------------------------------- |
| void UnLogic::transformIsNull(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (child(0)->isAnAggregate()) |
| { |
| if (child(0)->getOperatorType() == ITM_ONE_ROW) return; |
| // ## other aggs? e.g. ONE_TRUE, ANY_TRUE, ...? |
| } |
| |
| // Eliminate redundant EXISTs subquery : ISNULL(EXISTS ... ) ==> FALSE |
| // ISNOTNULL(EXISTS ... ) ==> TRUE |
| |
| if (child(0)->getOperatorType() == ITM_EXISTS || |
| child(0)->getOperatorType() == ITM_NOT_EXISTS) // i.e. isAnExistsSubquery() |
| { |
| // If the ISNULL is not within a complex scalar expression, i.e., it |
| // is either a lone predicate factor or the direct descendant of an |
| // AND, which, in turn, is not within a complex expression, then |
| // eliminate the predicate tree rooted in the ISNULL. |
| if (getOperatorType() == ITM_IS_NOT_UNKNOWN || |
| getOperatorType() == ITM_IS_UNKNOWN) |
| { |
| ItemExpr *tf = new(normWARef.wHeap()) |
| BoolVal(getOperatorType() == ITM_IS_NOT_UNKNOWN ? |
| ITM_RETURN_TRUE : ITM_RETURN_FALSE); |
| |
| getValueId().replaceItemExpr(tf); |
| tf->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tf; |
| return; |
| } |
| } // child is an EXISTS |
| else |
| if (child(0)->getOperatorType() == ITM_ITEM_LIST) |
| { |
| ///////////////////////////////////////////////////////////////// |
| // multivalue is null, is not null, is unknown, is not unknown. |
| // (a,b) IS NULL ==> a IS NULL AND b IS NULL |
| // (a,b) IS NOT NULL ==> a IS NOT NULL AND b IS NOT NULL |
| //////////////////////////////////////////////////////////////// |
| |
| // Convert the child subtree into list WITHOUT FLATTENING SUBQUERIES, |
| // so they will be transformed to ONE_ROW aggregates. |
| ItemExprList childList(child(0).getPtr(), normWARef.wHeap(), |
| ITM_ITEM_LIST, FALSE/*don't flatten subquery*/); |
| |
| ItemExpr * rootPtr = NULL; |
| for (CollIndex i = 0; i < childList.entries(); i++) |
| { |
| ItemExpr * unLogPtr = new HEAP |
| UnLogic(getOperatorType(), childList[i]); |
| if (!rootPtr) |
| rootPtr = unLogPtr; |
| else |
| rootPtr = new HEAP BiLogic(ITM_AND, rootPtr, unLogPtr); |
| } |
| |
| // replace the definition of this valueId |
| getValueId().replaceItemExpr(rootPtr); |
| |
| // Synthesize its type |
| rootPtr->synthTypeAndValueId(TRUE); |
| |
| locationOfPointerToMe = rootPtr; |
| |
| normWARef.setNullFlag(); // set flag for influencing subquery transformations |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, |
| externalInputs); |
| normWARef.restoreNullFlag(); |
| } |
| else |
| { |
| ExprValueId saveLocationOfPointerToMe = locationOfPointerToMe; |
| |
| normWARef.setNullFlag(); // set flag for influencing subquery transform |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| normWARef.restoreNullFlag(); |
| |
| // If (x) is not nullable, then |
| // (x) IS NOT NULL ==> always True; (x) IS NULL ==> always False |
| // If (x) is nullable, then check for |
| // NULL IS NULL ==> always True; NULL IS NOT NULL ==> always False |
| // |
| OperatorTypeEnum op = getOperatorType(); |
| ItemExpr *operand = child(0).getPtr(); |
| const NAType &operandType = child(0)->getValueId().getType(); |
| if (saveLocationOfPointerToMe == locationOfPointerToMe && |
| (op == ITM_IS_NOT_NULL || op == ITM_IS_NULL) && |
| !normWARef.inConstraints() && !normWARef.subqUnderExprTree() && |
| !normWARef.isInBeforeTrigger()) |
| { |
| ItemExpr *tf = NULL; |
| if (!operandType.supportsSQLnullLogical()) // check for non-nullability |
| tf = new(normWARef.wHeap()) BoolVal(op == ITM_IS_NOT_NULL |
| ? ITM_RETURN_TRUE |
| : ITM_RETURN_FALSE); |
| else if (operand->getOperator() == ITM_CONSTANT && // check for |
| static_cast<ConstValue*>(operand)->isNull()) // NULL constant |
| tf = new(normWARef.wHeap()) BoolVal(op == ITM_IS_NOT_NULL |
| ? ITM_RETURN_FALSE |
| : ITM_RETURN_TRUE); |
| if (tf) |
| { |
| getValueId().replaceItemExpr(tf); |
| tf->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tf; |
| } |
| } |
| } |
| } // UnLogic::transformIsNull |
| |
| // ----------------------------------------------------------------------- |
| // predicateEliminatesNullAugmentedRows() |
| // The following method determines whether a predicate is capable of |
| // discarding null augmented rows produced by a left join. |
| // ----------------------------------------------------------------------- |
| NABoolean UnLogic::predicateEliminatesNullAugmentedRows(NormWA & normWARef, |
| ValueIdSet & outerReferences) |
| { |
| NABoolean returnValue = FALSE; |
| |
| // Short circuit the non-null rejecting ones. |
| switch(getOperatorType()) |
| { |
| case ITM_IS_NULL: |
| case ITM_IS_UNKNOWN: |
| return returnValue; |
| } |
| |
| |
| // ----------------------------------------------------------------- |
| // An InstantiateNull is used by the LeftJoin for instantiating a |
| // null value in place of a value that is produced as output by |
| // its second child. An InstantiateNull that appears in a binary |
| // comparison predicate eliminates null augmented rows. As a |
| // consequence, the LeftJoin becomes an InnerJoin. For example, |
| |
| // select t1.x |
| // from t1 left join t2 on t1.x = t2.x |
| // where NOT t2.x like '_B%'; |
| // |
| // SelPred: |
| // ------> NOT T2.x Like 'Hello%' |
| // | |
| // LJ JoinPred: t1.x = t2.x |
| // / \ |
| // T1 T2 |
| // The Selection predicate NOT T2.x LIKE 'Hello%' will cause all the |
| // LeftJoin (LJ) operators to be transformed to InnerJoins. |
| // ----------------------------------------------------------------- |
| if (!normWARef.walkingAnExprTree() && !normWARef.subqUnderExprTree()) |
| { // does NOT lie within an expression tree |
| // ------------------------------------------------------------- |
| // Initiate the left to inner join transformation. Replace |
| // the InstantiateNull with the original expression that |
| // is the subject for null instantiation. |
| // |
| // We only need to check child 0 of the Like function. |
| // ------------------------------------------------------------- |
| |
| GroupAttributes emptyGA; |
| emptyGA.setCharacteristicOutputs(outerReferences); |
| ValueIdSet emptySet, emptySet1, coveredExpr, coveredSubExpr; |
| ValueIdSet instNullValue; |
| NABoolean containsOuterReferencesInSelectList = FALSE; |
| if (child(0)->getOperatorType() == ITM_INSTANTIATE_NULL) |
| { |
| InstantiateNull *inst = (InstantiateNull *)child(0)->castToItemExpr(); |
| if ((normWARef.inSelectList())&&!(inst->NoCheckforLeftToInnerJoin)) |
| { |
| instNullValue.insert(inst->getValueId()); |
| emptyGA.coverTest( |
| instNullValue, |
| emptySet, |
| coveredExpr, |
| emptySet1, |
| &coveredSubExpr); |
| if(!coveredExpr.isEmpty()) |
| { |
| containsOuterReferencesInSelectList = TRUE; |
| } |
| } |
| if ((!inst->NoCheckforLeftToInnerJoin)&&!containsOuterReferencesInSelectList) |
| { |
| child(0) = child(0)->initiateLeftToInnerJoinTransformation |
| (normWARef); |
| returnValue = TRUE; |
| } |
| // we have to dig deeper |
| } else if (child(0)->predicateEliminatesNullAugmentedRows |
| (normWARef, outerReferences) == TRUE) |
| { |
| returnValue = TRUE; |
| } |
| } |
| |
| return returnValue; |
| } // UnLogic::predicateEliminatesNullAugmentedRows() |
| |
| |
| // MDAMR |
| |
| |
| // ----------------------------------------------------------------------- |
| // Perform an MDAM tree walk on UnLogic |
| // ----------------------------------------------------------------------- |
| DisjunctArray * UnLogic::mdamTreeWalk() |
| { |
| // --------------------------------------------------------------------- |
| // First a DisjunctArray is allocated. Then a ValueIdSet is allocated |
| // with the value id of this predicate added to the set. The pointer |
| // to this ValueIdSet is then inserted as the first entry of the array. |
| // The pointer to the DisjunctArray is returned to the previous BiLogic |
| // node as this method recurses back up the expression tree. |
| // --------------------------------------------------------------------- |
| return new HEAP DisjunctArray(new HEAP ValueIdSet(getValueId())); |
| |
| } // UnLogic::mdamTreeWalk() |
| |
| // MDAMR |
| |
| |
| ItemExpr * UnLogic::normalizeNode(NormWA & normWARef) |
| { |
| if (nodeIsNormalized()) |
| return getReplacementExpr(); |
| markAsNormalized(); |
| |
| switch(getOperatorType()) |
| { |
| case ITM_NOT: |
| case ITM_IS_FALSE: |
| normWARef.setNotFlag(); |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| normWARef.restoreNotFlag(); |
| break; |
| |
| case ITM_IS_NULL: |
| case ITM_IS_NOT_NULL: |
| case ITM_IS_UNKNOWN: |
| case ITM_IS_NOT_UNKNOWN: |
| normWARef.setNullFlag(); |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| normWARef.restoreNullFlag(); |
| break; |
| |
| case ITM_IS_TRUE: |
| // "canBeSQLUnknown" should always be true, so you can only take |
| // the if branch when not walking an expression tree. |
| if (!canBeSQLUnknown(child(0)) || !normWARef.walkingAnExprTree()) |
| { |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| } |
| else |
| { |
| normWARef.setNotFlag(); |
| child(0) = child(0)->getReplacementExpr()->normalizeNode(normWARef); |
| normWARef.restoreNotFlag(); |
| } |
| break; |
| |
| default: |
| CMPASSERT(FALSE); |
| break; |
| } // end switch |
| |
| return getReplacementExpr(); |
| |
| } // UnLogic::normalizeNode() |
| |
| |
| // *********************************************************************** |
| // $$$$ VEGReference |
| // member functions for class VEGReference |
| // *********************************************************************** |
| ItemExpr * VEGReference::normalizeNode(NormWA & /*normWARef */) |
| { |
| // --------------------------------------------------------------------- |
| // If a reference to a VEG is encountered here, replace with with the |
| // VEGReference of its VEG. By doing so, we account for any merges |
| // of VEGs that have happened. |
| // original VEGReference -> original VEG |
| // After a merge: |
| // original VEGReference -> new VEG -> new VEGReference |
| // --------------------------------------------------------------------- |
| return ((VEGReference *)this)->getVEG()->getVEGReference(); |
| } // VEGReference::normalizeNode() |
| |
| // *********************************************************************** |
| // $$$$ VEGPredicate |
| // member functions for class VEGPredicate |
| // *********************************************************************** |
| ItemExpr * VEGPredicate::normalizeNode(NormWA & /* normWARef */) |
| { |
| // --------------------------------------------------------------------- |
| // If a reference to a VEG is encountered here, replace with with the |
| // VEGReference of its VEG. By doing so, we account for any merges |
| // of VEGs that have happened. |
| // original VEGReference -> original VEG |
| // After a merge: |
| // original VEGReference -> new VEG -> new VEGReference |
| // --------------------------------------------------------------------- |
| return ((VEGPredicate *)this)->getVEG()->getVEGPredicate(); |
| } // VEGPredicate::normalizeNode() |
| |
| // MDAMR |
| // ----------------------------------------------------------------------- |
| // Perform an MDAM tree walk on VEGPredicate |
| // ----------------------------------------------------------------------- |
| DisjunctArray * VEGPredicate::mdamTreeWalk() |
| { |
| // --------------------------------------------------------------------- |
| // First a DisjunctArray is allocated. Then a ValueIdSet is allocated |
| // with the value id of this predicate added to the set. The pointer |
| // to this ValueIdSet is then inserted as the first entry of the array. |
| // The pointer to the DisjunctArray is returned to the previous BiLogic |
| // node as this method recurses back up the expression tree. |
| // --------------------------------------------------------------------- |
| return new HEAP DisjunctArray(new HEAP ValueIdSet(getValueId())); |
| |
| } // VEGPredicate::mdamTreeWalk() |
| // MDAMR |
| |
| // *********************************************************************** |
| // $$$$ ValueIdUnion |
| // member functions for class ValueIdUnion |
| // *********************************************************************** |
| ItemExpr * ValueIdUnion::normalizeNode(NormWA & /* normWARef */) |
| { |
| if (nodeIsNormalized()) |
| return this; |
| markAsNormalized(); |
| // --------------------------------------------------------------------- |
| // A ValueIdUnion is not replaced with a VEGReference, if at all such |
| // a replacement is possible, during normalization. It is retained as-is. |
| // During predicate pushdown the normalizer replaces the ValueIdUnion |
| // with an expression that is appropriate in the context of the pushdown. |
| // Otherwise, the operator represents the result of the union in the |
| // dataflow tree for the query. |
| // --------------------------------------------------------------------- |
| return this; // return unchanged do not replace with a VEGReference. |
| } // ValueIdUnion::normalizeNode() |
| |
| ItemExpr * ValueIdUnion::normalizeSpecificChild(NormWA & normWARef, Lng32 childIndex) |
| { |
| |
| CMPASSERT(childIndex < (Lng32)entries()); |
| |
| sources_[childIndex] = |
| ((ItemExpr *)(sources_[childIndex].getItemExpr()-> |
| normalizeNode(normWARef)))->getValueId(); |
| |
| // If the result is different from this ValueIdUnion, normalize it too |
| if (result_ != getValueId()) |
| result_ = ((ItemExpr *)(result_.getItemExpr()->normalizeNode(normWARef))) |
| ->getValueId(); |
| return this; |
| } // ValueIdUnion::normalizeSpecificChild() |
| |
| |
| // No transformation for this node. |
| void ZZZBinderFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| ABORT("ZZZBinderFunction should never reach here."); |
| |
| return; |
| } |
| // *********************************************************************** |
| // $$$$ ItmSequenceFunction |
| // member functions for class ItmSequenceFunction |
| // *********************************************************************** |
| |
| // |
| // Redefinition of virtual function to check for nested THIS function. |
| // A THIS function can occur within a ROWS SINCE, but not within |
| // any other sequence function. |
| // |
| NABoolean ItmSequenceFunction::containsTHISFunction() |
| { |
| Int32 arity = getArity(); |
| NABoolean result = FALSE; |
| |
| for (Int32 i = 0; i < arity; i++) |
| { |
| if (child(i)->containsTHISFunction()) |
| { |
| result = TRUE; |
| if (getOperatorType() != ITM_ROWS_SINCE) |
| { |
| CMPASSERT("Invalid nested THIS function in Normalizer."); |
| } |
| } |
| else // child does not contain THIS |
| { |
| if (getOperatorType() == ITM_THIS) // I am a THIS, return TRUE. |
| result = TRUE; |
| } |
| } |
| return result; |
| } // ItmSequenceFunction::containsTHISFunction() |
| |
| // *********************************************************************** |
| // $$$$ ItmSeqOffset |
| // member functions for class ItmSeqOffset |
| // *********************************************************************** |
| // |
| // Transform optional third argument into ScalarMin of second and third args. |
| // |
| void ItmSeqOffset::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the DIFF1 |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| if (getArity() == 3) |
| { |
| ItemExpr *newMin = new HEAP ItmScalarMinMax (ITM_SCALAR_MIN, child(1), child(2)); |
| newMin->synthTypeAndValueId(TRUE); |
| child (1) = newMin; |
| child (2) = NULL; |
| } |
| |
| } // ItmSeqOffset::transformNode() |
| |
| void ItmLagOlapFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| } // ItmSeqOffset::transformNode() |
| |
| void ItmLeadOlapFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| } // ItmLeadOlapFunction::transformNode() |
| |
| // *********************************************************************** |
| // $$$$ ItmSeqDiff1 |
| // member functions for class ItmSeqDiff1 |
| // *********************************************************************** |
| // |
| // Transform Diff1(exp) into ((exp) - OFFSET (exp, 1)); |
| // Transform Diff1 (x,y) into (x-OFFSET(x,1)) / (y-OFFSET(y,1)) |
| // |
| ItemExpr * ItmSeqDiff1::transformDiff1() |
| { |
| // --------------------------------------------------------------------- |
| // Replace Diff1(exp) with exp - OFFSET(exp, 1) |
| // ^ |
| // | |
| // child1 |
| // |
| // |
| // Replace Diff1 (x,y) with (x-OFFSET(x,1)) / (y-OFFSET(y,1)) |
| // --------------------------------------------------------------------- |
| |
| ItemExpr *offsetExpr = new HEAP ItmSeqOffset ( child(0), 1); // new OFFSET expression |
| ((ItmSeqOffset *)offsetExpr)->setIsOLAP(isOLAP()); |
| ItemExpr *tfm = new HEAP BiArith(ITM_MINUS, |
| child(0).getPtr(), // child(0) is still child 0 |
| offsetExpr); |
| |
| // |
| // Generate DIFF1 (y) == y-OFFSEY(y,1) |
| // |
| if (getArity() == 2) |
| { |
| ItemExpr *tfm1 = tfm; |
| ItemExpr *offsetExpr2 = new HEAP ItmSeqOffset (child(1), 1); // new OFFSET expression |
| ((ItmSeqOffset *)offsetExpr2)->setIsOLAP(isOLAP()); |
| ItemExpr *tfm2 = new HEAP BiArith(ITM_MINUS, // y - OFFSET (y, 1) |
| child(1).getPtr(), // child(1) is child (0) |
| offsetExpr2) ; |
| // |
| // The following is a kludge to allow DIFFn (x,y) of dates and intervals; otherwise, |
| // the divisor, that is 'y - OFFSET(y,1)', cannot be type INTERVAL. |
| // |
| NABuiltInTypeEnum opType = child(1)->getValueId().getType().getTypeQualifier(); |
| |
| if (opType == NA_INTERVAL_TYPE || opType == NA_DATETIME_TYPE) |
| { |
| ItemExpr *castExpr = new HEAP Cast (tfm2, |
| new HEAP |
| SQLLargeInt(HEAP, TRUE, TRUE)); // (must be) signed; nulls allowed |
| tfm2 = castExpr; |
| } |
| |
| tfm = new HEAP BiArith(ITM_DIVIDE, |
| tfm1, |
| tfm2); |
| } // end getArity == 2 |
| CMPASSERT(tfm); |
| return tfm; |
| } |
| |
| void ItmSeqDiff1::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the DIFF1 |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| ItemExpr *tfm = transformDiff1(); |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // ItmSeqDiff1::transformNode() |
| |
| // *********************************************************************** |
| // $$$$ ItmSeqDiff2 |
| // member functions for class ItmSeqDiff2 |
| // *********************************************************************** |
| // |
| // Transform Diff2(exp) into (DIFF1(exp) - OFFSET (DIFF1(exp), 1)) |
| // Transform Diff2(x,y) into DIFF2(x) / DIFF1(y) |
| // or (DIFF1(x) - OFFSET (DIFF1(x), 1)) / DIFF1(y) |
| // |
| // Transform node will recursively transform the children. |
| // |
| ItemExpr * ItmSeqDiff2::transformDiff2() |
| { |
| ItemExpr *newDiff1 = new HEAP ItmSeqDiff1 (child(0)); // new Diff1 expression |
| ItemExpr *offsetExpr = new HEAP ItmSeqOffset (newDiff1, 1); // new OFFSET expression: OFFSET(DIFF1(x), 1) |
| ((ItmSeqOffset *)offsetExpr)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *tfm = new HEAP BiArith(ITM_MINUS, // new MINUS expression |
| newDiff1, // newDiff1 is child 0 |
| offsetExpr); |
| if (getArity() == 2) |
| { |
| ItemExpr *tfm1 = tfm; // save DIFF2 expression |
| ItemExpr *tfm2 = new HEAP ItmSeqDiff1 (child(1)); // new Diff1 expression |
| // |
| // The following is a kludge to allow DIFF2(x, y) where y is dates or intervals; otherwise, |
| // the divisor, that is 'DIFF1(Y)', cannot be type INTERVAL. |
| // |
| NABuiltInTypeEnum opType = child(1)->getValueId().getType().getTypeQualifier(); |
| |
| if (opType == NA_INTERVAL_TYPE || opType == NA_DATETIME_TYPE) |
| { |
| ItemExpr *castExpr = new HEAP Cast (tfm2, |
| new HEAP |
| SQLLargeInt(HEAP, TRUE, TRUE)); // (must be) signed; nulls allowed |
| tfm2 = castExpr; |
| } |
| |
| tfm = new HEAP BiArith(ITM_DIVIDE, |
| tfm1, |
| tfm2); |
| |
| } // end getArity == 2 |
| CMPASSERT(tfm); |
| return tfm; |
| } |
| |
| void ItmSeqDiff2::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the DIFF2 |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| ItemExpr *tfm = transformDiff2(); |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| } // ItmSeqDiff2::transformNode() |
| |
| // *********************************************************************** |
| // $$$$ ItmSeqRunningFunction |
| // member functions for class ItmSeqRunningFunction |
| // *********************************************************************** |
| void ItmSeqRunningFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| ValueId cacheEquivTransSeqId; |
| ItemExpr * thisItem = locationOfPointerToMe.getPtr(); |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){ |
| if (normWARef.findEquivalentInSeqFunctionsCache( thisItem, cacheEquivTransSeqId)) { |
| locationOfPointerToMe = cacheEquivTransSeqId; |
| return; |
| } |
| } |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the Running Function |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| OperatorTypeEnum op = getOperatorType(); |
| |
| if (op == ITM_RUNNING_SDEV || |
| op == ITM_RUNNING_VARIANCE || |
| op == ITM_RUNNING_AVG || |
| op == ITM_RUNNING_RANK || |
| op == ITM_RUNNING_DRANK) |
| { |
| ItemExpr *tfm = NULL; |
| |
| switch (getOperatorType()) |
| { |
| case ITM_RUNNING_VARIANCE: |
| case ITM_RUNNING_SDEV: |
| tfm = transformRunningVariance(); |
| break; |
| case ITM_RUNNING_AVG: |
| tfm = transformRunningAvg(); |
| break; |
| case ITM_RUNNING_RANK: |
| tfm = transformRunningRank(); |
| break; |
| case ITM_RUNNING_DRANK: |
| { |
| ItemExpr *change |
| = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, |
| child(0)); |
| ((ItmSeqRunningFunction *)change)->setIsOLAP(isOLAP()); |
| ItemExpr *pred = new HEAP BiRelat(ITM_EQUAL, |
| change, |
| new HEAP SystemLiteral(1)); |
| |
| tfm = new HEAP |
| IfThenElse(pred, |
| new HEAP SystemLiteral(1), |
| new HEAP SystemLiteral(0)); |
| |
| tfm = new HEAP Case(NULL, tfm); |
| |
| tfm = new HEAP ItmSeqRunningFunction(ITM_RUNNING_SUM, tfm); |
| ((ItmSeqRunningFunction *)tfm)->setIsOLAP(isOLAP()); |
| tfm = new HEAP Cast(tfm, &getValueId().getType()); |
| |
| break; |
| } |
| default: |
| break; |
| } |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){ |
| normWARef.insertIntoSeqFunctionsCache(thisItem, locationOfPointerToMe.getValueId()); |
| } |
| |
| } // end SDEV, VARIANCE or AVG |
| |
| } // ItmSeqRunningFunction::transformNode() |
| |
| |
| //------------------------------------------------------------------------------------ |
| // transformRunningVariance() |
| // |
| // Transforms RUNNINGVARIANCE(x) or RUNNINGSDEV(x) into ScalarVariance: |
| // RSUM ( (x - AVG(x))2) / (RUNNINGCOUNT(*) - 1) |
| // == (RUNNING_SUM(Xi*Xi) - RUNNING_AVG(Xi)*RUNNING_SUM(Xi)) / (N-1) |
| // == (RUNNING_SUM (x * x) - (RUNNING_AVG(x) * RUNNING_SUM(x)) / RUNNINGCOUNT(*) -1 |
| // |
| // Thus, terms needed for Scalar Variance are: |
| // 1. RUNNING_SUM (x * x) ==> "sumOfValSquared" |
| // 2. RUNNING_SUM(x) ==> "sumOfVal" |
| // 3. RUNNING_COUNT(*) == countofVal |
| // |
| // All other terms can be derived from these. |
| // |
| // In ExFunctionSVariance::eval, the following calculations are performed (if VARIANCE): |
| // avgOfVal = sumOfVal/countOfVal; |
| // |
| // result = ( sumOfValSquared - (2 * avgOfVal * sumOfVal) + (sumOfVal * avgOfVal)) / (countOfVal - 1); |
| // |
| // In ExFunctionSVariance::eval (if STDDEV) |
| // result = sqrt ( sumOfValSquared - (2 * avgOfVal * sumOfVal) + (sumOfVal * avgOfVal)) / (countOfVal - 1)); |
| //-------------------------------------------------------------------------------------- |
| ItemExpr * ItmSeqRunningFunction::transformRunningVariance() |
| { |
| const NAType *desiredType = new HEAP SQLDoublePrecision(HEAP, TRUE); |
| ItemExpr *childDouble = new HEAP Cast(child(0), desiredType); |
| ItemExpr *childDoubleSquared = new HEAP BiArith(ITM_TIMES, // x * x |
| childDouble, |
| childDouble); |
| |
| ItemExpr *sumOfValSquared = new HEAP ItmSeqRunningFunction // RUNNINGSUM (x * x) |
| (ITM_RUNNING_SUM, childDoubleSquared); |
| ((ItmSeqRunningFunction *)sumOfValSquared)->setIsOLAP(isOLAP()); |
| ItemExpr *sumOfVal = new HEAP ItmSeqRunningFunction // RUNNINGSUM (x) |
| (ITM_RUNNING_SUM, childDouble); |
| ((ItmSeqRunningFunction *)sumOfVal)->setIsOLAP(isOLAP()); |
| ItemExpr *const1 = new HEAP ConstValue(1); // constant value 1 |
| |
| ItemExpr *countOfVal = new HEAP ItmSeqRunningFunction // RUNNINGCOUNT (*) |
| (ITM_RUNNING_SUM, const1); |
| ((ItmSeqRunningFunction *)countOfVal)->setIsOLAP(isOLAP()); |
| ItemExpr *castDouble = new HEAP Cast(countOfVal, desiredType); |
| |
| OperatorTypeEnum newOp; |
| |
| if (getOperatorType() == ITM_RUNNING_VARIANCE) |
| { |
| newOp = ITM_VARIANCE; |
| } |
| else |
| { |
| CMPASSERT (getOperatorType() == ITM_RUNNING_SDEV); |
| newOp = ITM_STDDEV; |
| } |
| ItemExpr *result = new HEAP |
| ScalarVariance(newOp, |
| sumOfValSquared, |
| sumOfVal, |
| castDouble) ; |
| |
| return result; |
| } |
| // transformRunningAvg |
| // |
| // transforms RUNNINGAVG (x) into |
| // RUNNINGSUM(x) / RUNNINGCOUNT(x) |
| // |
| ItemExpr * ItmSeqRunningFunction::transformRunningAvg() |
| { |
| ItemExpr *newSum = new HEAP ItmSeqRunningFunction (ITM_RUNNING_SUM, child(0)); // new RUNNINGSUM(x) |
| ((ItmSeqRunningFunction *)newSum)->setIsOLAP(isOLAP()); |
| ItemExpr *newCount = new HEAP ItmSeqRunningFunction (ITM_RUNNING_COUNT, child(0));// new RUNNINGCOUNT(x) |
| ((ItmSeqRunningFunction *)newCount)->setIsOLAP(isOLAP()); |
| ItemExpr *tfm = new HEAP BiArith(ITM_DIVIDE, // RUNNINGSUM(x) / RUNNINGCOUNT(x) |
| newSum , |
| newCount); |
| |
| return tfm; |
| } |
| |
| ItemExpr * ItmSeqRunningFunction::transformRunningRank() |
| { |
| ItemExpr *tfm = NULL; |
| |
| NABoolean inv = FALSE; |
| child(0) = child(0)->removeInverseFromExprTree(inv); |
| |
| if (inv && !(isTDFunction() || isOLAP())) |
| {//Using ASC/DESC with sequence functions is not supported |
| *CmpCommon::diags() << DgSqlCode(-4362); |
| return this; |
| } |
| |
| ItemExpr *rcStar = new HEAP ItmSeqRunningFunction(ITM_RUNNING_COUNT, |
| new HEAP SystemLiteral(1)); |
| ((ItmSeqRunningFunction *)rcStar)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *change = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, |
| child(0)); |
| ((ItmSeqRunningFunction *)change)->setIsOLAP(isOLAP()); |
| |
| tfm = new HEAP BiArith(ITM_MINUS, rcStar, change); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| |
| //set type to original type after if has been changed to BigNum above |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new HEAP BiArith(ITM_PLUS, tfm, new HEAP SystemLiteral(1)); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| |
| //set type to original type after if has been changed to BigNum above |
| tfm->getValueId().changeType(&getValueId().getType()); |
| tfm = new HEAP Cast(tfm, &getValueId().getType()); |
| |
| if( !isOLAP() ) |
| { |
| //applying NotCovered to rank only for now to allow predicates to be pushed down |
| //in the case of a semi join |
| //Gen web Case ID : 10-080219-4401 |
| tfm = new HEAP NotCovered(tfm); |
| tfm->synthTypeAndValueId(); |
| } |
| |
| return tfm; |
| } |
| |
| void ItmSeqOlapFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| ValueId cacheEquivTransSeqId; |
| ItemExpr * thisItem = locationOfPointerToMe.getPtr(); |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){ |
| if (normWARef.findEquivalentInSeqFunctionsCache( thisItem, cacheEquivTransSeqId)) { |
| locationOfPointerToMe = cacheEquivTransSeqId; |
| return; |
| } |
| } |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the Running Function |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| OperatorTypeEnum op = getOperatorType(); |
| |
| ItemExpr *tfm = NULL; |
| |
| switch (getOperatorType()) |
| { |
| case ITM_OLAP_VARIANCE: |
| case ITM_OLAP_SDEV: |
| tfm = transformOlapVariance(normWARef.wHeap()); |
| break; |
| case ITM_OLAP_AVG: |
| tfm = transformOlapAvg(normWARef.wHeap()); |
| break; |
| default: |
| break; |
| } |
| |
| if (tfm) |
| { |
| getValueId().replaceItemExpr(tfm); |
| tfm->synthTypeAndValueId(TRUE); |
| locationOfPointerToMe = tfm; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON) |
| { |
| normWARef.insertIntoSeqFunctionsCache(thisItem, locationOfPointerToMe.getValueId()); |
| } |
| } |
| |
| |
| } // ItmSeqOlapFunction::transformNode() |
| |
| |
| |
| ///---------------- |
| |
| ItemExpr * ItmSeqOlapFunction::transformOlapVariance(CollHeap *wHeap) |
| { |
| const NAType *desiredType = new (wHeap) SQLDoublePrecision(wHeap, TRUE); |
| ItemExpr *childDouble = new (wHeap) Cast(child(0), desiredType); |
| ItemExpr *childDoubleSquared = new (wHeap) BiArith(ITM_TIMES, // x * x |
| childDouble, |
| childDouble); |
| |
| ItemExpr *sumOfValSquared = new (wHeap) ItmSeqOlapFunction |
| (ITM_OLAP_SUM, childDoubleSquared); |
| ((ItmSeqOlapFunction *)sumOfValSquared)->setIsOLAP(isOLAP()); |
| ((ItmSeqOlapFunction *)sumOfValSquared)->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| ((ItmSeqOlapFunction *)sumOfValSquared)->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| ItemExpr *sumOfVal = new (wHeap) ItmSeqOlapFunction // RUNNINGSUM (x) |
| (ITM_OLAP_SUM, childDouble); |
| ((ItmSeqOlapFunction *)sumOfVal)->setIsOLAP(isOLAP()); |
| ((ItmSeqOlapFunction *)sumOfVal)->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| ((ItmSeqOlapFunction *)sumOfVal)->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| ItemExpr *const1 = new (wHeap) ConstValue(1); // constant value 1 |
| |
| ItemExpr *countOfVal = new (wHeap) ItmSeqOlapFunction // |
| (ITM_OLAP_SUM, const1); |
| ((ItmSeqOlapFunction *)countOfVal)->setIsOLAP(isOLAP()); |
| ((ItmSeqOlapFunction *)countOfVal)->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| ((ItmSeqOlapFunction *)countOfVal)->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| ItemExpr *castDouble = new (wHeap) Cast(countOfVal, desiredType); |
| |
| OperatorTypeEnum newOp; |
| |
| if (getOperatorType() == ITM_OLAP_VARIANCE) |
| { |
| newOp = ITM_VARIANCE; |
| } |
| else |
| { |
| CMPASSERT (getOperatorType() == ITM_OLAP_SDEV); |
| newOp = ITM_STDDEV; |
| } |
| ItemExpr *result = new (wHeap) |
| ScalarVariance(newOp, |
| sumOfValSquared, |
| sumOfVal, |
| castDouble) ; |
| |
| return result; |
| } |
| |
| ItemExpr * ItmSeqOlapFunction::transformOlapAvg(CollHeap *wHeap) |
| { |
| ItemExpr *newSum = new (wHeap) ItmSeqOlapFunction (ITM_OLAP_SUM, child(0)); |
| ((ItmSeqOlapFunction *)newSum)->setIsOLAP(isOLAP());/// may need to chnage this behavior |
| ((ItmSeqOlapFunction *)newSum)->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| ((ItmSeqOlapFunction *)newSum)->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| ItemExpr *newCount = new (wHeap) ItmSeqOlapFunction (ITM_OLAP_COUNT, child(0)); |
| ((ItmSeqOlapFunction *)newCount)->setIsOLAP(isOLAP()); |
| ((ItmSeqOlapFunction *)newCount)->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| ((ItmSeqOlapFunction *)newCount)->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| ItemExpr *tfm = new (wHeap) BiArith(ITM_DIVIDE, |
| newSum , |
| newCount); |
| tfm = new (wHeap) Cast(tfm, &getValueId().getType()); |
| |
| return tfm; |
| } |
| // transform olap rank function -- |
| // function called at precode gen time |
| ItemExpr * ItmSeqOlapFunction::transformOlapRank(CollHeap *wHeap) |
| { |
| ItemExpr *tfm = NULL; |
| |
| ItemExpr *rcStar = new (wHeap) ItmSeqRunningFunction(ITM_RUNNING_COUNT, |
| new (wHeap) SystemLiteral(1)); |
| ((ItmSeqRunningFunction *)rcStar)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *change = new (wHeap) ItmSeqRunningFunction(ITM_RUNNING_CHANGE, |
| child(0)); |
| ((ItmSeqRunningFunction *)change)->setIsOLAP(isOLAP()); |
| |
| tfm = new (wHeap) BiArith(ITM_MINUS, rcStar, change); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| |
| //set type to original type after if has been changed to BigNum above |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new (wHeap) BiArith(ITM_PLUS, tfm, new (wHeap) SystemLiteral(1)); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| |
| //set type to original type after if has been changed to BigNum above |
| tfm->getValueId().changeType(getValueId().getType().newCopy(wHeap)); |
| tfm = new (wHeap) Cast(tfm, getValueId().getType().newCopy(wHeap)); |
| |
| return tfm; |
| } |
| |
| ItemExpr * ItmSeqOlapFunction::transformOlapDRank(CollHeap *wHeap) |
| { |
| ItemExpr *change |
| = new (wHeap) ItmSeqRunningFunction(ITM_RUNNING_CHANGE, |
| child(0)); |
| ((ItmSeqRunningFunction *)change)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *pred = new (wHeap) BiRelat(ITM_EQUAL, |
| change, |
| new (wHeap) SystemLiteral(1)); |
| |
| ItemExpr *tfm = new (wHeap) |
| IfThenElse( pred, |
| new (wHeap) SystemLiteral(1), |
| new (wHeap) SystemLiteral(0)); |
| |
| tfm = new (wHeap) Case(NULL, tfm); |
| |
| tfm = new (wHeap) ItmSeqRunningFunction(ITM_RUNNING_SUM, tfm); |
| ((ItmSeqRunningFunction *)tfm)->setIsOLAP(isOLAP()); |
| tfm = new (wHeap) Cast(tfm, getValueId().getType().newCopy(wHeap)); |
| |
| return tfm; |
| } |
| |
| |
| ItemExpr *ItmSeqOlapFunction::transformOlapFunction(CollHeap *heap) |
| { |
| |
| // KB -- later -order by and partition maybe need to remove them from itsSequenceFunction |
| // and put them in ItmSeqOlapFunction?? |
| |
| if (getOperatorType() == ITM_OLAP_RANK) |
| { |
| return transformOlapRank(heap); |
| } |
| else |
| if (getOperatorType() == ITM_OLAP_DRANK) |
| { |
| return transformOlapDRank(heap); |
| } |
| |
| |
| ItemExpr * tfm = NULL; |
| |
| //-- KB -- ALL WINDOW FRAMES |
| //-- ROWS BETWEEN UNBOUNDED PRECEDING AND BOUNDED PRECEDING |
| //-- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
| //-- ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN UNBOUNDED PRECEDING AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN CURRENT ROW AND CURRENT ROW |
| //-- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN CURRENT ROW AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND BOUNDED PRECEDING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND CURRENT ROW |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED FOLLOWING AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED FOLLOWING AND BOUNDED FOLLOWING |
| |
| if (isFrameStartUnboundedPreceding() && //frameStart_ == -INT_MAX && |
| !isFrameEndUnboundedPreceding() && //frameEnd_ != -INT_MAX && |
| !isFrameEndUnboundedFollowing()) //frameEnd_ != INT_MAX) |
| { |
| // BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
| // BETWEEN UNBOUNDED PRECEDING AND BOUNDED PRECEDING |
| // BETWEEN UNBOUNDED PRECEDING AND BOUNDED FOLLOWING |
| // |
| OperatorTypeEnum op = mapOperTypeToRunning(); |
| |
| ItmSequenceFunction * seqFunc = new (heap) |
| ItmSeqRunningFunction(op, child(0)); |
| seqFunc->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| |
| if (frameEnd_ !=0) |
| { |
| seqFunc = new (heap) |
| ItmSeqOffset(seqFunc, -frameEnd_,FALSE,TRUE,INT_MAX); |
| |
| seqFunc->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| } |
| |
| |
| tfm = seqFunc; |
| |
| } |
| else if (frameStart_ <= frameEnd_ && |
| !isFrameStartUnboundedPreceding() && //frameStart_ != -INT_MAX && |
| !isFrameEndUnboundedPreceding()) //frameEnd_ != -INT_MAX) |
| { |
| //-- ROWS BETWEEN CURRENT ROW AND CURRENT ROW |
| //-- ROWS BETWEEN CURRENT ROW AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND BOUNDED PRECEDING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND CURRENT ROW |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED FOLLOWING AND BOUNDED FOLLOWING |
| //-- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
| //-- ROWS BETWEEN BOUNDED FOLLOWING AND UNBOUNDED FOLLOWING |
| // SUM(A) OVER(order by B ROWS BETWEEN UNBOUNDED PRECEDING |
| // AND UNBOUNDED FOLLOWING) |
| // SUM(A) OVER(order by B ROWS BETWEEN 5 PRECEDING |
| // AND 10 FOLLOWING) |
| |
| //MIN/MAX handled in generator |
| if (getOperatorType() == ITM_OLAP_MIN || |
| getOperatorType() == ITM_OLAP_MAX) |
| { |
| if (frameStart_ < 0 && frameEnd_>0) |
| { |
| OperatorTypeEnum op; |
| if(getOperatorType() == ITM_OLAP_MIN) |
| { |
| op = ITM_SCALAR_MIN; |
| } |
| else |
| { |
| op = ITM_SCALAR_MAX; |
| } |
| |
| ItmSeqOlapFunction *precFunc = new (heap) |
| ItmSeqOlapFunction(getOperatorType(), child(0)); |
| |
| precFunc->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| precFunc->setOlapWindowFrame(frameStart_, 0); |
| |
| ItmSeqOlapFunction *follFunc = new (heap) |
| ItmSeqOlapFunction(getOperatorType(), child(0)); //off); |
| |
| follFunc->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| follFunc->setOlapWindowFrame(0 , frameEnd_); |
| |
| ItemExpr * minmax = new(heap) |
| ItmScalarMinMax(op, precFunc, follFunc); |
| return minmax; |
| } |
| else |
| { |
| return this; |
| } |
| } |
| |
| OperatorTypeEnum op = mapOperTypeToRunning(); |
| |
| Lng32 wSize= INT_MAX; |
| |
| if (!isFrameEndUnboundedFollowing()) //(frameEnd_ != INT_MAX) |
| { |
| wSize = frameEnd_ - frameStart_ + 1; |
| } |
| |
| ItmSequenceFunction *newSeq = new (heap) |
| ItmSeqRunningFunction (op, child(0)); |
| |
| ItmSequenceFunction *newOffset1 = newSeq; |
| |
| if((-frameStart_ + 1) != 0) { |
| newOffset1 = new (heap) |
| ItmSeqOffset (newSeq, -frameStart_ + 1, FALSE, FALSE, wSize); |
| } |
| |
| ItmSequenceFunction *newOffset2 = newSeq; |
| |
| if(-frameEnd_ != 0) { |
| newOffset2 = new (heap) |
| ItmSeqOffset (newSeq, -frameEnd_, FALSE, TRUE, wSize); |
| } |
| |
| tfm = new (heap) |
| BiArith(ITM_MINUS, |
| newOffset2, |
| newOffset1); |
| |
| newSeq->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| newOffset1->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| newOffset2->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| // change type to the sum to the original type of the olap function |
| // so that we don't promote the sum to a bignum type which may hurt |
| // performance |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| } else if (isFrameStartUnboundedPreceding() && //frameStart_ == -INT_MAX && |
| isFrameEndUnboundedFollowing()) //frameEnd_ == INT_MAX) |
| { |
| //-- ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
| // SUM(A) OVER(order by B ROWS BETWEEN UNBOUNDED PRECEDING |
| // AND UNBOUNDED FOLLOWING) |
| |
| OperatorTypeEnum op = mapOperTypeToRunning(); |
| |
| ItmSequenceFunction *seqFunc = new (heap) |
| ItmSeqRunningFunction(op, child(0)); |
| |
| ItmSequenceFunction *seqOff = new (heap) |
| ItmSeqOffset(seqFunc, -frameEnd_/*+1*/,FALSE,TRUE, INT_MAX); |
| |
| seqOff->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| seqFunc->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| |
| tfm = seqOff; |
| |
| } |
| |
| if (tfm) |
| { |
| |
| if (getOperatorType()== ITM_OLAP_COUNT) |
| { |
| ItemExpr *constZero = new (heap) ConstValue(0); |
| ItemExpr *isnull = new (heap) UnLogic(ITM_IS_NULL, tfm); |
| ItemExpr *newITEExpr = new (heap) IfThenElse(isnull, constZero, tfm); |
| tfm = new (heap) Case(NULL, newITEExpr); |
| |
| } |
| |
| if (getOperatorType()== ITM_OLAP_SUM) |
| { |
| ItmSeqOlapFunction * olapCount = new (heap) ItmSeqOlapFunction(ITM_OLAP_COUNT, child(0)); |
| olapCount->setOLAPInfo(getOlapPartitionBy(), getOlapOrderBy()); |
| olapCount->setOlapWindowFrame(frameStart_, frameEnd_); |
| |
| olapCount->synthTypeAndValueId(TRUE); // valueId/type will be used later when we further transfornm this olap count function |
| ItemExpr * itmExpr = olapCount->transformOlapFunction(heap); |
| |
| ItemExpr *nullConst = new (heap) ConstValue(); |
| ItemExpr *constZero = new (heap) ConstValue(0); |
| ItemExpr *newEqual = new (heap) BiRelat(ITM_EQUAL, itmExpr, constZero); |
| ItemExpr *newITEExpr = new (heap) IfThenElse(newEqual, nullConst, tfm); |
| tfm = new (heap) Case(NULL, newITEExpr); |
| |
| } |
| |
| return tfm; |
| } |
| |
| return NULL; |
| } |
| |
| |
| |
| ///--------------- |
| void ItmSeqRowsSince::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| DBGSETDBG( "TRANSFORM_DEBUG" ); |
| DBGIF( |
| unp = ""; |
| unparse(unp); |
| cerr << (Int32)getOperatorType() << " " |
| << (Int32)getValueId() << " " |
| << (void *)this << " " |
| << unp << endl; |
| ); |
| |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // -------------------------------------------------------------------- |
| // Traverse the tree in order to locate all THIS and NOT THIS branches. |
| // -------------------------------------------------------------------- |
| transformNotTHISFunction(); |
| |
| // Replace the search condition with the search boolean |
| // |
| child(0) = new HEAP UnLogic(ITM_IS_TRUE, child(0)); |
| child(0)->synthTypeAndValueId(TRUE); |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the Moving Function |
| // --------------------------------------------------------------------- |
| BuiltinFunction::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| } // ItmSeqRowsSince::transformNode() |
| |
| // *********************************************************************** |
| // $$$$ ItmSeqMovingFunction |
| // member functions for class ItmSeqMovingFunction |
| // *********************************************************************** |
| void ItmSeqMovingFunction::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| ValueId cacheEquivTransSeqId; |
| ItemExpr * thisItem = locationOfPointerToMe.getPtr(); |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){ |
| if (normWARef.findEquivalentInSeqFunctionsCache( thisItem, cacheEquivTransSeqId)) { |
| locationOfPointerToMe = cacheEquivTransSeqId; |
| return; |
| } |
| } |
| |
| if (nodeIsTransformed()) |
| { |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Normalize the operands of the Moving Function |
| // --------------------------------------------------------------------- |
| ItemExpr::transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // |
| // For all moving functions: replace children 1 and 2 with SCALAR_MIN (child(1), child(2)) |
| // |
| // Start: ItmSeqMovingFunction End: ItmSeqMovingFunction |
| // / | \ / \ |
| // x y z x SCALAR_MIN (y, z) |
| // |
| if (getArity() == 3) |
| { |
| ItemExpr *newMin = new HEAP ItmScalarMinMax (ITM_SCALAR_MIN, child(1), child(2)); |
| newMin->synthTypeAndValueId(TRUE); |
| child (1) = newMin; |
| child (2) = NULL; |
| } |
| |
| OperatorTypeEnum op = getOperatorType(); |
| ItemExpr *tfm = 0; |
| |
| switch (op) { |
| case ITM_MOVING_VARIANCE: |
| case ITM_MOVING_SDEV: |
| tfm = transformMovingVariance(); |
| break; |
| case ITM_MOVING_AVG: |
| tfm = transformMovingAvg(); |
| break; |
| case ITM_MOVING_SUM: |
| tfm = transformMovingSum(); |
| break; |
| case ITM_MOVING_COUNT: |
| tfm = transformMovingCount(); |
| break; |
| case ITM_MOVING_MIN: |
| case ITM_MOVING_MAX: |
| if (getSkipMovingMinMaxTransformation() == FALSE) |
| { |
| tfm = transformMovingMinMax(); |
| } |
| break; |
| case ITM_MOVING_RANK: |
| { |
| tfm = transformMovingRank(); |
| } |
| break; |
| case ITM_MOVING_DRANK: |
| { |
| ItemExpr *constOne = new HEAP SystemLiteral(1); |
| ItemExpr *constZero = new HEAP SystemLiteral(0); |
| |
| ItemExpr *change |
| = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, |
| child(0)); |
| ((ItmSeqRunningFunction *)change)->setIsOLAP(isOLAP()); |
| ItemExpr *pred = new HEAP BiRelat(ITM_EQUAL, |
| change, |
| constOne); |
| |
| ItemExpr *rowChanged = new HEAP |
| IfThenElse(pred, constOne, constZero); |
| |
| rowChanged = new HEAP Case(NULL, rowChanged); |
| |
| ItemExpr *rsum = new HEAP ItmSeqRunningFunction(ITM_RUNNING_SUM, |
| rowChanged); |
| ((ItmSeqRunningFunction *)rsum)->setIsOLAP(isOLAP()); |
| |
| // Win is one less than the window size. It is used to access |
| // the first row in the window, not the last row in the |
| // previous window as do many other moving funcions. |
| // |
| ItemExpr *win = new HEAP BiArith(ITM_MINUS, |
| child(1), |
| constOne); |
| |
| win->synthTypeAndValueId(TRUE); |
| win->getValueId().changeType(&getValueId().getType()); |
| |
| ItemExpr *offRsum = new HEAP ItmSeqOffset(rsum, win, NULL, TRUE); |
| ((ItmSeqOffset *)offRsum)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *diff = new HEAP BiArith(ITM_MINUS, |
| rsum, |
| offRsum); |
| |
| diff->synthTypeAndValueId(TRUE); |
| diff->getValueId().changeType(&getValueId().getType()); |
| |
| diff = new HEAP BiArith(ITM_PLUS, |
| diff, |
| constOne); |
| |
| diff->synthTypeAndValueId(TRUE); |
| diff->getValueId().changeType(&getValueId().getType()); |
| |
| // If Win is zero, then this is a special case of a window |
| // size of one (see construction of win above) in which case |
| // M_DRANK is one |
| pred = new HEAP BiRelat(ITM_LESS, |
| win, |
| constOne); |
| |
| tfm = new HEAP IfThenElse(pred, constOne, diff); |
| |
| tfm= new HEAP Case(NULL, tfm); |
| |
| tfm = new HEAP Cast(tfm, &getValueId().getType()); |
| |
| break; |
| } |
| default: |
| CMPASSERT(FALSE); |
| break; |
| } |
| |
| if (tfm) |
| { |
| tfm->synthTypeAndValueId(TRUE); |
| // revert to original synthesized type |
| ItemExpr *newCast = new HEAP Cast(tfm, &getValueId().getType()); |
| if( !isOLAP() ) |
| { |
| //applying NotCovered to rank only for now to allow predicates to be pushed down |
| //in the case of a semi join |
| //Gen web Case ID : 10-080219-4401 |
| if (op == ITM_MOVING_RANK) |
| { |
| newCast = new HEAP NotCovered(newCast); |
| newCast->synthTypeAndValueId(); |
| } |
| } |
| |
| getValueId().replaceItemExpr(newCast); |
| newCast->synthTypeAndValueId(TRUE); |
| |
| locationOfPointerToMe = newCast; |
| |
| // Make sure the new nodes are transformed |
| locationOfPointerToMe->transformNode(normWARef, locationOfPointerToMe, |
| introduceSemiJoinHere, externalInputs); |
| |
| // Set the replacement expression |
| setReplacementExpr(locationOfPointerToMe); |
| |
| if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){ |
| normWARef.insertIntoSeqFunctionsCache(thisItem, locationOfPointerToMe.getValueId()); |
| } |
| } |
| |
| } // ItmSeqMovingFunction::transformNode() |
| |
| //------------------------------------------------------------------------------ |
| // transformMovingMinMax |
| // |
| // Transforms MOVINGMIN/MAX (x, y) into |
| // MOVINGMINMAX(x, CASE WHEN (y < 0 OR y IS NULL) THEN DEF_MAX_HISTORY_ROWS ELSE y END) |
| // |
| //------------------------------------------------------------------------------ |
| ItemExpr * ItmSeqMovingFunction::transformMovingMinMax() |
| |
| { |
| OperatorTypeEnum op = getOperatorType(); |
| |
| CMPASSERT (op == ITM_MOVING_MIN || op == ITM_MOVING_MAX); |
| |
| OperatorTypeEnum runningOp = |
| ((op == ITM_MOVING_MIN) ? ITM_RUNNING_MIN : ITM_RUNNING_MAX); |
| |
| // new RUNNINGMIN/MAX(x) |
| ItemExpr *newRunning = new HEAP ItmSeqRunningFunction (runningOp, child(0)); |
| ((ItmSeqRunningFunction *)newRunning)->setIsOLAP(isOLAP()); |
| ItemExpr *constZero = new HEAP ConstValue(0); |
| // new (y is NULL) |
| ItemExpr *newIsNull = new HEAP UnLogic (ITM_IS_NULL, child(1)); |
| // new ( y < 0) |
| ItemExpr *newLessThan = new HEAP BiRelat(ITM_LESS, child(1), constZero); |
| // new (y < 0 OR y IS NULL) |
| ItemExpr *newOr = new HEAP BiLogic(ITM_OR, newIsNull, newLessThan); |
| |
| ItmSeqMovingFunction *newMovingMinMax = new HEAP ItmSeqMovingFunction(op, child(0), child(1)); |
| ((ItmSeqMovingFunction *)newMovingMinMax)->setIsOLAP(isOLAP()); |
| newMovingMinMax->setSkipMovingMinMaxTransformation(); |
| |
| // new if (y < 0 OR y IS NULL) THEN RUNNINGMIN/MAX(x) ELSE MOVINGMIN/MAX(x, y) |
| ItemExpr *newITEExpr = new HEAP IfThenElse(newOr, newRunning, newMovingMinMax); |
| |
| ItemExpr *tfm = new HEAP Case(NULL, newITEExpr); |
| ItemExpr *off = new HEAP ItmSeqOffset(child(0), 1); |
| |
| tfm = new HEAP ItmBlockFunction(off, tfm); |
| |
| return tfm; |
| } |
| |
| //------------------------------------------------------------------------------------ |
| // transformMovingVariance |
| // |
| // Transforms MOVINGVARIANCE(x) or MOVINGSDEV(x) into ScalarVariance: |
| // == (MOVING_SUM(Xi*Xi) - MOVING_AVG(Xi)*MOVING_SUM(Xi)) / MOVINGSUM(1) |
| // == (MOVING_SUM (x * x) - (MOVING_AVG(x) * MOVING_SUM(x)) / MOVINGSUM(1) |
| // |
| // Thus, terms needed for Scalar Variance are: |
| // 1. MOVING_SUM (x * x) ==> "sumOfValSquared" |
| // 2. MOVING_SUM(x) ==> "sumOfVal" |
| // 3. MOVING_COUNT(*) == countofVal |
| // |
| // All other terms can be derived from these. |
| // |
| // In ExFunctionSVariance::eval, the following calculations are performed (if VARIANCE): |
| // avgOfVal = sumOfVal/countOfVal; |
| // |
| // result = ( sumOfValSquared - (2 * avgOfVal * sumOfVal) + (sumOfVal * avgOfVal)) / (countOfVal - 1); |
| // |
| // In ExFunctionSVariance::eval (if STDDEV) |
| // result = sqrt ( sumOfValSquared - (2 * avgOfVal * sumOfVal) + (sumOfVal * avgOfVal)) / (countOfVal - 1)); |
| //-------------------------------------------------------------------------------------- |
| // |
| ItemExpr * ItmSeqMovingFunction::transformMovingVariance() |
| { |
| const NAType *desiredType = new HEAP SQLDoublePrecision(HEAP, TRUE); |
| ItemExpr *childDouble = new HEAP Cast(child(0), desiredType); |
| ItemExpr *childDoubleSquared = new HEAP BiArith(ITM_TIMES, // x * x |
| childDouble, |
| childDouble); |
| |
| ItemExpr *sumOfValSquared = new HEAP ItmSeqMovingFunction // MOVINGSUM ((x * x), size) |
| (ITM_MOVING_SUM, childDoubleSquared, child(1)); |
| ((ItmSeqMovingFunction *)sumOfValSquared)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *sumOfVal = new HEAP ItmSeqMovingFunction // MOVINGSUM (x, size) |
| (ITM_MOVING_SUM, childDouble, child(1)); |
| ((ItmSeqMovingFunction *)sumOfVal)->setIsOLAP(isOLAP()); |
| ItemExpr *const1 = new HEAP ConstValue(1); // constant value 1 |
| ItemExpr *countOfVal = new HEAP ItmSeqMovingFunction // MOVINGCOUNT (*, size) |
| (ITM_MOVING_SUM, const1, child(1)); |
| ((ItmSeqMovingFunction *)countOfVal)->setIsOLAP(isOLAP()); |
| |
| ItemExpr *castDouble = new HEAP Cast(countOfVal, desiredType); |
| |
| OperatorTypeEnum newOp; |
| |
| if (getOperatorType() == ITM_MOVING_VARIANCE) |
| { |
| newOp = ITM_VARIANCE; |
| } |
| else |
| { |
| CMPASSERT (getOperatorType() == ITM_MOVING_SDEV); |
| newOp = ITM_STDDEV; |
| } |
| ItemExpr *result = new HEAP |
| ScalarVariance(newOp, |
| sumOfValSquared, |
| sumOfVal, |
| castDouble) ; |
| return result; |
| } |
| //------------------------------------------------------------------------------------ |
| // transformMovingAvg |
| // |
| // transforms MOVINGAVG (x, y) into |
| // MOVINGSUM(x) / MOVINGCOUNT(x) |
| //------------------------------------------------------------------------------------ |
| // |
| ItemExpr * ItmSeqMovingFunction::transformMovingAvg() |
| { |
| ItemExpr *newSum = new HEAP ItmSeqMovingFunction(ITM_MOVING_SUM, child(0), child(1));// new MOVINGSUM(x, size); |
| ((ItmSeqMovingFunction *)newSum)->setIsOLAP(isOLAP()); |
| ItemExpr *newCount = new HEAP ItmSeqMovingFunction(ITM_MOVING_COUNT, child(0), child(1)); // new MOVINGCOUNT(x, size); |
| ((ItmSeqMovingFunction *)newCount)->setIsOLAP(isOLAP()); |
| ItemExpr *tfm = new HEAP BiArith(ITM_DIVIDE, // MOVINGSUM(x) / MOVINGCOUNT(x) |
| newSum , |
| newCount); |
| |
| return tfm; |
| } |
| //------------------------------------------------------------------------------------ |
| // transformMovingSum |
| // |
| // Transforms MOVINGSUM (x, y) into |
| // RUNNINGSUM(x) - OFFSET(RUNNINGSUM(x), y) |
| // |
| // Unlike MOVINGCOUNT, the MOVINGSUM should return NULL, not 0, if |
| // (1) all the rows it processed were NULL or |
| // (2) the logical window size was effectively 0 or |
| // (3) both (1) and (2) |
| // Therefore, the above expression (newMinus) must be further transformed into: |
| // CASE (MOVINGCOUNT(x,y)) WHEN 0 THEN NULL ELSE newMinus END |
| //------------------------------------------------------------------------------------ |
| ItemExpr * ItmSeqMovingFunction::transformMovingSum() |
| { |
| ItemExpr *newSum = new HEAP ItmSeqRunningFunction (ITM_RUNNING_SUM, child(0)); |
| ((ItmSeqRunningFunction *)newSum)->setIsOLAP(isOLAP()); |
| ItemExpr *newOffset = new HEAP ItmSeqOffset (newSum, child(1), NULL, TRUE); |
| ((ItmSeqOffset *)newOffset)->setIsOLAP(isOLAP()); |
| ItemExpr *tfm = new HEAP BiArith(ITM_MINUS, |
| newSum, |
| newOffset); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| ItemExpr *newMovingCount = new HEAP ItmSeqMovingFunction(ITM_MOVING_COUNT, child(0), child(1)); |
| ((ItmSeqMovingFunction *)newMovingCount)->setIsOLAP(isOLAP()); |
| ItemExpr *nullConst = new HEAP ConstValue(); |
| ItemExpr *constZero = new HEAP ConstValue(0); |
| ItemExpr *newEqual = new HEAP BiRelat(ITM_EQUAL, newMovingCount, constZero); |
| ItemExpr *newITEExpr = new HEAP IfThenElse(newEqual, nullConst, tfm); |
| tfm = new HEAP Case(NULL, newITEExpr); |
| |
| return tfm; |
| } |
| //------------------------------------------------------------------------------------ |
| // transformMovingCount() |
| // |
| // Transforms MOVINGCOUNT as follows: |
| // MOVINGCOUNT(x, y) = RUNNINGCOUNT(x) - REPLACENULL(OFFSET(RUNNGINGCOUNT(x),y)) |
| //------------------------------------------------------------------------------------ |
| ItemExpr *ItmSeqMovingFunction::transformMovingCount() |
| { |
| ItemExpr *tfm ; |
| ItemExpr *newCount = new HEAP ItmSeqRunningFunction (ITM_RUNNING_COUNT,child(0)); // new RUNNINGCOUNT(x) |
| ((ItmSeqRunningFunction *)newCount)->setIsOLAP(isOLAP()); |
| ItemExpr *newOffset = new HEAP ItmSeqOffset (newCount, child(1), NULL, TRUE); // new OFFSET(RUNNINGCOUNT(x), y) |
| ((ItmSeqOffset *)newOffset)->setIsOLAP(isOLAP()); |
| tfm = new HEAP BiArith(ITM_MINUS , // RUNNINGCOUNT(x) - OFFSET(RUNNINGCOUNT(x), y) |
| newCount , |
| newOffset); |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new HEAP Cast(tfm, &getValueId().getType()); |
| |
| return tfm; |
| } // ItmSeqMovingFunction::transformMovingCount() |
| |
| ItemExpr *ItmSeqMovingFunction::transformMovingRank() |
| { |
| ItemExpr *tfm = NULL; |
| |
| NABoolean inv = FALSE; |
| child(0) = child(0)->removeInverseFromExprTree(inv); |
| if (inv && !(isTDFunction() || isOLAP())) |
| { |
| *CmpCommon::diags() << DgSqlCode(-4362); |
| return this; |
| } |
| |
| if( !isOLAP() ) |
| { |
| ItemExpr * partOrdExpr = NULL; |
| ItemExpr * partExpr = NULL; |
| |
| ItemExprList partExprList( child(1)->child(0), HEAP); |
| ItemExprList ordExprList( child(0) , HEAP ); |
| |
| CollIndex nc =(CollIndex) partExprList.entries(); |
| |
| partOrdExpr = partExprList.usedEntry(nc-1); |
| |
| for (CollIndex i = nc-1 ; i > 0 ;i--) |
| { |
| partOrdExpr = new(HEAP) ItemList( partExprList.usedEntry(i-1 ), partOrdExpr); |
| partOrdExpr->synthTypeAndValueId(TRUE); |
| } |
| |
| partExpr = partOrdExpr->copyTree(HEAP); |
| partExpr->synthTypeAndValueId(TRUE); |
| |
| for (CollIndex i = 0; (i < (CollIndex) ordExprList.entries());i++) |
| { |
| partOrdExpr = new(HEAP) ItemList(ordExprList.usedEntry(i), partOrdExpr); |
| partOrdExpr->synthTypeAndValueId(TRUE); |
| } |
| |
| ItemExpr *partOrdChange = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, partOrdExpr); |
| |
| ItemExpr *partChange = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, partExpr); |
| |
| tfm = new HEAP BiArith(ITM_MINUS, partChange, partOrdChange); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new HEAP BiArith(ITM_PLUS, tfm, new HEAP SystemLiteral(1)); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| } |
| else |
| { |
| //RANK() OVER (PARTITION BY a ORDER BY b) is transformed into |
| //ROWS SINCE CHANGED(b) - ROWS SINCE CHANGED(a,b) |
| ItemExpr * partOrdExpr = child(0); |
| |
| ExprValueId treePtr = child(1)->child(0); |
| |
| ItemExprTreeAsList changeValues(&treePtr, ITM_ITEM_LIST, RIGHT_LINEAR_TREE); |
| |
| CollIndex nc = changeValues.entries(); |
| |
| for (CollIndex i = nc; i > 0; i--) |
| { |
| partOrdExpr = new(HEAP) ItemList(changeValues[i-1], partOrdExpr); |
| partOrdExpr->synthTypeAndValueId(TRUE); |
| } |
| |
| ItemExpr *partOrdChange |
| = new HEAP ItmSeqRunningFunction(ITM_RUNNING_CHANGE, partOrdExpr); |
| |
| ((ItmSeqRunningFunction *)partOrdChange)->setIsOLAP(isOLAP()); |
| |
| tfm = new HEAP BiArith(ITM_MINUS, child(1), partOrdChange); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new HEAP BiArith(ITM_PLUS, tfm, new HEAP SystemLiteral(1)); |
| |
| tfm->synthTypeAndValueId(TRUE); |
| tfm->getValueId().changeType(&getValueId().getType()); |
| |
| tfm = new HEAP Cast(tfm, &getValueId().getType()); |
| } |
| |
| return tfm; |
| } // ItmSeqMovingFunction::transformMovingRank() |
| |
| |
| |
| |
| //**************************************************************************** |
| // The next three routines deal with OneRow aggregate transformation. |
| // See comments at ItemExpr::transformOneRowAggregate() |
| //**************************************************************************** |
| static ItemExpr * createCastNodesonLeaves3( ItemExpr *tfm, NormWA & normref ) |
| { |
| if (tfm->getOperatorType() != ITM_ITEM_LIST) |
| { |
| CMPASSERT(CmpCommon::getDefault(COMP_BOOL_137) == DF_ON) ; |
| NAType *outType = tfm->getValueId().getType().newCopy(normref.wHeap()); |
| outType->setNullable(TRUE); |
| InstantiateNull * inst = |
| new(normref.wHeap()) InstantiateNull(tfm , outType); |
| inst->NoCheckforLeftToInnerJoin = TRUE; |
| inst->synthTypeAndValueId(TRUE); |
| return inst; |
| } |
| else |
| { |
| for (Int32 i=0; i<tfm->getArity(); i++) |
| { |
| tfm->child(i) = createCastNodesonLeaves3( |
| tfm->child(i)->castToItemExpr(), normref ); |
| } |
| return tfm; |
| } |
| } |
| |
| static ItemExpr *createCastNodesonLeaves2( ItemExpr *tfm, NormWA & normref ) |
| { |
| if (tfm->getOperatorType() == ITM_ONE_ROW) |
| { |
| CMPASSERT( tfm->getArity() == 1); // either a cast or a list operator |
| CMPASSERT( tfm->nodeIsBound() ); // this is not bound?? |
| |
| ValueId exprId = tfm->getValueId(); |
| Aggregate *agr = (Aggregate *)(tfm->getReplacementExpr()); |
| |
| // if this has been transformed before do not redo it. |
| |
| if (! agr->isOneRowTransformed_ ) |
| agr->child(0) = |
| createCastNodesonLeaves3(tfm->child(0)->castToItemExpr(), normref); |
| |
| agr->synthTypeAndValueId(TRUE); |
| exprId.replaceItemExpr( agr); |
| agr->isOneRowTransformed_ = TRUE; |
| return agr; |
| } |
| /*else if ( tfm->getOperatorType() == ITM_BASECOLUMN ) // veg problem |
| { |
| Cast *castNode = new(normref.wHeap()) Cast(tfm, &(tfm->getValueId().getType())); |
| castNode->synthTypeAndValueId(TRUE); |
| return castNode; |
| }*/ |
| else |
| { |
| for (Int32 i=0; i<tfm->getArity(); i++) |
| { |
| tfm->child(i) = createCastNodesonLeaves2( |
| tfm->child(i)->castToItemExpr(), normref ); |
| } |
| return tfm; |
| } |
| } // static createCastNodesonLeaves2() |
| |
| |
| // This routine handles OneRow Transformation. It goes through the ItemExpr tree and |
| // looks for OneRow AggregateNodes and inserts an InstantiateNull node on top of |
| // the column(s) the OneRow aggregate is outputting. It uses static functions shown |
| // above to achieve this purpose. |
| ItemExpr* ItemExpr::transformOneRowAggregate( NormWA & normref ) |
| { |
| ItemExpr *tfm = this; |
| tfm = createCastNodesonLeaves2( tfm, normref ); |
| return tfm; |
| |
| } // ItemExpr::transformOneRowAggregate() |
| |
| // This routine removes OneRow aggregate nodes from the Itemexpression tree. |
| // The following transformation takes place: |
| // For some nodes A, B: A->OneRowNode()->B ===> A->B |
| ItemExpr* ItemExpr::removeOneRowAggregate( ItemExpr *tfm, NormWA & normref ) |
| { |
| if ( ( tfm->getOperatorType() == ITM_IS_NULL || |
| tfm->getOperatorType() == ITM_IS_NOT_NULL ) && |
| ( tfm->child(0)->getOperatorType() == ITM_ONE_ROW )) |
| { |
| ////////////////////////////////////////////////////////////////////////// |
| // currently this logic handles the case of IS[NOT]NULL of OneRow(A LIST): |
| // it replaces that with a conjuct of leaf nodes of one row aggregate. |
| // This is experimental; I borrowed the code from UnLogic::transformIsNull() |
| // This would be rewritten so as to reduce duplication of code. |
| // - (11/25/98) |
| // |
| // (a,b) IS NULL ==> a IS NULL AND b IS NULL |
| // (a,b) IS NOT NULL ==> a IS NOT NULL AND b IS NOT NULL |
| ////////////////////////////////////////////////////////////////////////// |
| |
| CMPASSERT( tfm->child(0)->child(0).getPtr() ); |
| // ITM_ONE_ROW has at least one child |
| |
| // Convert the child subtree into list WITHOUT FLATTENING SUBQUERIES, |
| // so they will be transformed to ONE_ROW aggregates. |
| ItemExprList childList(tfm->child(0)->castToItemExpr()->child(0).getPtr(), |
| normref.wHeap(), |
| ITM_ITEM_LIST, |
| FALSE); |
| |
| ItemExpr * rootPtr = NULL; |
| for (CollIndex i = 0; i < childList.entries(); i++) |
| { |
| ItemExpr * unLogPtr = new HEAP |
| UnLogic(tfm->getOperatorType(), childList[i]); |
| if (!rootPtr) |
| rootPtr = unLogPtr; |
| else |
| rootPtr = new HEAP BiLogic(ITM_AND, rootPtr, unLogPtr); |
| } |
| |
| // replace the definition of this valueId |
| tfm->getValueId().replaceItemExpr(rootPtr); |
| |
| // Synthesize its type |
| rootPtr->synthTypeAndValueId(TRUE); |
| return rootPtr; |
| } |
| else |
| if (tfm->getOperatorType() == ITM_ONE_ROW) |
| { |
| CMPASSERT( tfm->getArity() == 1); // as of 11/25/98 |
| // this can only handle only one child |
| |
| if (tfm->child(0)->getOperatorType() != ITM_ITEM_LIST) |
| tfm = tfm->child(0)->castToItemExpr(); |
| |
| return tfm; |
| } |
| else |
| { |
| for (Int32 i=0; i<tfm->getArity(); i++) |
| { |
| tfm->child(i) = removeOneRowAggregate( |
| tfm->child(i)->castToItemExpr(), normref ); |
| } |
| return tfm; |
| } |
| |
| } // ItemExpr::removeOneRowAggregate() |
| |
| void BiRelat::translateListOfComparisonsIntoValueIds() |
| { |
| if(!listOfComparisonExprs_) |
| return; |
| |
| for (CollIndex i = 0; i < listOfComparisonExprs_->entries(); i++) |
| { |
| CMPASSERT((*listOfComparisonExprs_)[i]->nodeIsBound()); |
| ValueId comparisonValueId = (*listOfComparisonExprs_)[i]->getValueId(); |
| listOfComparisonExprIds_.insert(comparisonValueId); |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Each operator supports a (virtual) method for transforming a |
| // scalar expression to a canonical form |
| // ----------------------------------------------------------------------- |
| void ValueIdProxy::transformNode(NormWA & normWARef, |
| ExprValueId & locationOfPointerToMe, |
| ExprGroupId & introduceSemiJoinHere, |
| const ValueIdSet & externalInputs) |
| { |
| if (nodeIsTransformed()) |
| { |
| // Return the address of the expression that was used for replacing |
| // this subquery in an earlier call. |
| locationOfPointerToMe = getReplacementExpr(); |
| return; |
| } |
| |
| // If we are proxying for the initial source for this ValueIdProxy, typically |
| // a MVF, or a subquery with degree > 1, make sure we transfrom it too |
| if (transformDerivedFromValueId_ == TRUE) |
| { |
| ItemExpr *expr = derivedFrom_.getItemExpr(); |
| |
| NABoolean origInValueIdProxyFlag = normWARef.inValueIdProxy(); |
| normWARef.setInValueIdProxy(TRUE); |
| expr->transformNode(normWARef, |
| locationOfPointerToMe, |
| introduceSemiJoinHere, |
| externalInputs); |
| |
| normWARef.setInValueIdProxy(origInValueIdProxyFlag); |
| } |
| |
| // Set return value, effectively getting rid of the ValueIdproxy node. |
| // this replaces the ValueIdproxy with the output it represents. |
| // Even though locationOfPointerToMe got set above in the transform case |
| // it will be contain the outputValueId_.getItemExpr(). So we make the |
| // two cases common. |
| |
| locationOfPointerToMe = outputValueId_.getItemExpr(); |
| |
| // Set the replacement expression |
| // if the transformDerivedFromValueId_ flag was set, the transform above |
| // initializes locationOfPointerToMe to be the itemExpr |
| // of the first output/element in the select list of the UDF/Subquery. |
| // otherwise, we just replaces itself with the output it represents. |
| |
| // Note that not ALL ValueIdProxy in a query will go away after transformation |
| // Some will be there until the Normalization phase is complete. This since |
| // not all ItemExprs transforms its children. ValueIdUnion is one such |
| // ItemExpr. |
| setReplacementExpr(locationOfPointerToMe); |
| markAsTransformed(); |
| return; |
| |
| |
| } // ValueIdProxy::transformNode() |