blob: fb4996aedb583093946bddde8aabc9b1723d7128 [file] [log] [blame]
/**********************************************************************
// @@@ 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(&notVal);
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()