blob: 920d2f4ba85c6289674b920aeb88d47b68ae2f86 [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: ItemExpr.C
* Description: Item expressions (both physical and logical operators)
* Created: 5/17/94
* Language: C++
*
*
*
*
******************************************************************************
*/
#define SQLPARSERGLOBALS_NADEFAULTS
#include "SqlParserGlobals.h"
#include "Platform.h"
#include "Sqlcomp.h"
#include "GroupAttr.h"
#include "AllItemExpr.h"
#include "PartFunc.h"
#include "wstr.h"
#include "NLSConversion.h"
#include "Cost.h" /* for lookups in the defaults table */
#include "Stats.h"
#include "exp_function.h" // for calling ExHDPHash::hash(data, len)
#include "ItemFuncUDF.h"
#include "CmpStatement.h"
#include "exp_datetime.h"
#include "OptRange.h"
# include <limits.h>
#include <string.h> // memcmp
// A constant to be used for allocating a buffer for getText()
#define TEXT_DISPLAY_LENGTH 1001
static NABoolean isQuantifiedComp(const ItemExpr *ie);
#define _strcmpi strcasecmp
// Initialize static members.
//THREAD_P OperatorTypeEnum ItemExpr::origOpTypeBeingBound_ = NO_OPERATOR_TYPE;
//THREAD_P Int32 ItemExpr::origOpTypeCounter_ = 0;
void ItemExpr::cleanupPerStatement()
{
if (CURRENTSTMT)
CURRENTSTMT->setItemExprOrigOpTypeBeingBound(NO_OPERATOR_TYPE);
}
// -----------------------------------------------------------------------
// methods for class ExprValueId
// -----------------------------------------------------------------------
// Ane ExprValueId can hold either an ItemExpr * or a ValueId.
// If it holds ValueId then the ItemExpr * is NULL.
ExprValueId::ExprValueId()
{
exprMode_ = STANDALONE;
exprPtr_ = NULL;
exprId_ = NULL_VALUE_ID;
}
ExprValueId::ExprValueId(const ExprValueId &other)
{
exprMode_ = other.exprMode_;
exprId_ = other.exprId_;
exprPtr_ = other.exprPtr_;
if (exprId_ != NULL_VALUE_ID)
exprPtr_ = NULL;
}
ExprValueId::ExprValueId(ItemExpr * exprPtr)
{
exprMode_ = STANDALONE;
exprPtr_ = exprPtr;
if (exprPtr_ != NULL)
exprId_ = exprPtr->getValueId();
else
exprId_ = NULL_VALUE_ID;
if (exprId_ != NULL_VALUE_ID)
exprPtr_ = NULL;
}
ExprValueId::ExprValueId(const ValueId & exprId)
{
exprMode_ = STANDALONE;
exprPtr_ = NULL;
exprId_ = exprId;
}
ExprValueId & ExprValueId::operator= (const ExprValueId & other)
{
CMPASSERT(exprMode_ != MEMOIZED);
exprMode_ = other.exprMode_;
exprPtr_ = other.exprPtr_;
exprId_ = other.exprId_;
if (exprId_ != NULL_VALUE_ID)
exprPtr_ = NULL;
return *this;
}
ExprValueId & ExprValueId::operator= (ItemExpr * other)
{
CMPASSERT(exprMode_ != MEMOIZED);
exprPtr_ = other;
// update the value id as well (may be set to a NULL value id, though)
if (exprPtr_ != NULL)
exprId_ = exprPtr_->getValueId();
else
exprId_ = NULL_VALUE_ID;
if (exprId_ != NULL_VALUE_ID)
exprPtr_ = NULL;
return *this;
}
ExprValueId & ExprValueId::operator= (const ValueId & other)
{
CMPASSERT(exprMode_ != MEMOIZED);
exprId_ = other;
exprPtr_ = NULL;
return *this;
}
NABoolean ExprValueId::operator== (const ExprValueId &other) const
{
return (getPtr() == other.getPtr()); // ptrs must match
}
NABoolean ExprValueId::operator== (const ItemExpr *other) const
{
return getPtr() == other;
}
NABoolean ExprValueId::operator== (const ValueId &other) const
{
return (getValueId() == other);
}
ValueId ExprValueId::getValueId() const
{
// make sure we return the value id that belongs to exprPtr_
// (we might have initialized it with an ItemExpr * and later
// added the value id to the ItemExpr without updating this object)
if (exprId_ == NULL_VALUE_ID AND exprPtr_ != NULL)
return exprPtr_->getValueId();
else
return exprId_;
}
void ExprValueId::convertToMemoized()
{
// set this mode to prevent updates to it: a MEMOIZED object cannot
// be assigned a new value
exprMode_ = MEMOIZED;
}
void ExprValueId::convertToStandalone()
{
exprMode_ = STANDALONE;
}
ItemExpr * ExprValueId::getPtr() const
{
ValueId temp = exprId_; // NSK platform needs to save this...?
exprPtr_->checkInvalidObject(this);
if (temp == NULL_VALUE_ID)
{
if (exprPtr_ == NULL || exprPtr_->getValueId() == NULL_VALUE_ID)
return exprPtr_;
return exprPtr_->getValueId().getItemExpr();
}
return exprId_.getItemExpr();
}
// -----------------------------------------------------------------------
// common member functions
// -----------------------------------------------------------------------
THREAD_P ObjectCounter (*ItemExpr::counter_)(0);
ItemExpr::ItemExpr(OperatorTypeEnum otype,
ItemExpr *child0,
ItemExpr *child1)
: ExprNode(otype),
// origOpType_(origOpTypeBeingBound() != NO_OPERATOR_TYPE ?
// origOpTypeBeingBound() : otype),
currChildNo_(0),
clause_(NULL),
collateClause_(NULL),
previousHostVar_(FALSE),
resolveIncompleteType_(FALSE),
preCodeGenNATypeChange_(FALSE),
selectivityFactor_(-1),
flags_(0)
{
child(0) = child0;
child(1) = child1;
replacementExpr_ = this;
(*counter_).incrementCounter();
origOpType_ = otype;
CmpStatement* currStmt = CmpCommon::statement();
if ( currStmt ) {
OperatorTypeEnum x = currStmt->getItemExprOrigOpTypeBeingBound();
if (x != NO_OPERATOR_TYPE)
origOpType_ = x;
}
}
ItemExpr::ItemExpr(const ItemExpr& s)
: ExprNode(s)
, valId_(s.valId_)
, currChildNo_(s.currChildNo_)
, collateClause_(s.collateClause_)
, replacementExpr_(s.replacementExpr_)
, clause_(s.clause_)
, origOpType_(s.origOpType_)
, previousHostVar_(s.previousHostVar_)
, resolveIncompleteType_(s.resolveIncompleteType_)
, previousName_(s.previousName_)
, preCodeGenNATypeChange_(s.preCodeGenNATypeChange_)
, selectivityFactor_(s.selectivityFactor_)
, flags_(s.flags_)
{
inputs_[0] = s.inputs_[0];
inputs_[1] = s.inputs_[1];
inputs_[2] = s.inputs_[2];
}
ItemExpr::~ItemExpr()
{
// recursively delete all the children
for (Lng32 i = 0; i < MAX_ITM_ARITY; i++)
delete inputs_[i].getPtr();
(*counter_).decrementCounter();
}
void ItemExpr::transformToRelExpr(NormWA & normWARef,
ExprValueId & locationOfPointerToMe,
ExprGroupId & introduceSemiJoinHere,
const ValueIdSet & externalInputs)
{
// Do nothing
locationOfPointerToMe = this;
}
// operator[] is used to access the children of a tree
ExprValueId & ItemExpr::operator[] (Lng32 index)
{
CMPASSERT(index >= 0 AND index < MAX_ITM_ARITY);
return inputs_[index];
}
const ExprValueId & ItemExpr::operator[] (Lng32 index) const
{
CMPASSERT(index >= 0 AND index < MAX_ITM_ARITY);
return inputs_[index];
}
NABoolean ItemExpr::operator== (const ItemExpr& other) const // virtual meth
{
return (getValueId() == other.getValueId());
}
void ItemExpr::deleteInstance()
{
Int32 nc = getArity();
for (Lng32 i = 0; i < (Lng32)nc; i++)
inputs_[i] = NULL;
delete this;
} // ItemExpr::deleteInstance()
void ItemExpr::setChild(Lng32 index, ExprNode * newChild)
{
if (newChild)
{
CMPASSERT(newChild->castToItemExpr());
child(index) = newChild->castToItemExpr();
}
else
child(index) = (ItemExpr *)NULL;
} // ItemExpr::setChild()
void ItemExpr::allocValueId()
{
ValueDesc *vdesc = new (CmpCommon::statementHeap()) ValueDesc(this);
setValueId(vdesc->getValueId());
} // ItemExpr::allocValueId()
// Check whether the given ValueId is referenced in this ItemExpr.
NABoolean ItemExpr::referencesTheGivenValue(const ValueId & vid,
NABoolean doNotDigInsideVegRefs ,
NABoolean doNotDigInsideInstNulls) const
{
// In really peculiar cases, a VEGRef might contain another VEGRef within
// its VEG which recursively self-references the VEGRef. In order to prevent
// running into an infinite loop there, we remember those VEG we've already
// already seen by setting a flag in those VEG. If we run into that again,
// there is no need to check the members of the VEG into it anymore since
// those have already been or will be checked.
//
NABoolean retVal = FALSE;
if (getValueId() == vid)
return TRUE;
ValueId exprId;
switch (getOperatorType())
{
case ITM_VEG_PREDICATE:
{
VEG *veg = ((VEGPredicate *)this)->getVEG();
if (veg->getVEGReference()->getValueId() == vid)
return TRUE;
else
{
// Already seen. Return FALSE. In VEGRef_x(VEG{VEGRef_x(..),y,z}),
// the call to the inner VEGRef_x returns FALSE. However, if the
// vid is VEGRef_x itself, when the function is called on the outer
// VEGRef_x, it has return TRUE already. The previous "if" clause
// ensures that. If vid is one of the other values in the VEG such
// as y or z, ValueIdSet::referencesTheGivenValue() is going to call
// us again on other members of the VEG. We will return TRUE with
// the matching member; and ValueIdSet::referencesTheGivenValue()
// is also going to return TRUE when we return TRUE on any one of
// the members of the VEG.
//
if (veg->seenBefore())
return FALSE;
else
{
// Mark this VEG as already seen before going over its members,
// so that if one of its members is a VEGRef to this VEG, we
// don't have to traverse the VEG anymore.
//
veg->markAsSeenBefore();
retVal = veg->getAllValues().referencesTheGivenValue(vid,exprId);
veg->markAsNotSeenBefore();
}
}
break;
}
case ITM_VEG_REFERENCE:
{
if (doNotDigInsideVegRefs)
return FALSE;
VEG *veg = ((VEGReference *)this)->getVEG();
// Already seen. Return FALSE. In VEGRef_x(VEG{VEGRef_x(..),y,z}),
// the call to the inner VEGRef_x returns FALSE. However, if the
// vid is VEGRef_x itself, when the function is called on the outer
// VEGRef_x, it has return TRUE already. The previous "if" clause
// ensures that. If vid is one of the other values in the VEG such
// as y or z, ValueIdSet::referencesTheGivenValue() is going to call
// us again on other members of the VEG. We will return TRUE with
// the matching member; and ValueIdSet::referencesTheGivenValue()
// is also going to return TRUE when we return TRUE on any one of
// the members of the VEG.
//
if (veg->seenBefore())
{
return FALSE;
}
else
{
// Mark this VEG as already seen before going over its members,
// so that if one of its members is a VEGRef to this VEG, we
// don't have to traverse the VEG anymore.
//
veg->markAsSeenBefore();
retVal = veg->getAllValues().referencesTheGivenValue(vid,exprId);
veg->markAsNotSeenBefore();
}
break;
}
case ITM_INSTANTIATE_NULL:
{
ValueId nvid = getValueId();
const ItemExpr * nie = nvid.getItemExpr();
InstantiateNull *inst = (InstantiateNull *)nie->castToItemExpr();
if (doNotDigInsideInstNulls)
return FALSE;
else
{
// Need to dig underneath the instantiate null to check if
// the value we are looking for is the immediate child of
// the instantiate null or contained in a veg reference
// that is the child of the instantiate null.
const ValueId & childVid = this->child(0)->getValueId();
const ItemExpr * childExpr = childVid.getItemExpr();
retVal = childExpr->referencesTheGivenValue(vid);
}
break;
} // end case instantiate null
case ITM_ROWSETARRAY_SCAN:
{
retVal = FALSE;
break;
}
default:
{
Lng32 nc = getArity();
for (Lng32 i = 0; i < nc; i++)
{
if (child(i).getPtr())
{
if (child(i)->referencesTheGivenValue(vid))
{
retVal = TRUE;
break;
}
}
} // endfor
// if this is a subquery, check any outer references of the subquery as
// well as the children.
//
if ((NOT retVal) AND isASubquery())
{
// check if the given value is referenced in the required inputs of
// the subquery
//
const ValueIdSet inputs = ((Subquery *)this)->getSubquery()->
getGroupAttr()->getCharacteristicInputs();
// Must not call internal function here, since we are checking on a
// different set (inputs), not a subexpr within this node.
//
retVal = inputs.referencesTheGivenValue(vid,exprId);
}
break;
} // default
} // switch
return retVal;
}
// Check whether this ItemExpr references any one value
// belonging to the given ValueidSet
NABoolean ItemExpr::referencesOneValueFrom(const ValueIdSet &vs) const
{
for (ValueId id = vs.init(); vs.next(id); vs.advance(id))
{
//check if the item expr is a non-strict constant
//a strict constant is somethine like cos(1)
//where as cos(?p) can be considered a constant
//in the non-strict definition since it remains
//constant for a given execution of a query
if (!id.getItemExpr()->doesExprEvaluateToConstant(FALSE) )
if (referencesTheGivenValue(id))
return TRUE;
}
return FALSE;
} // ItemExpr::referencesOneValueFrom()
// Check whether the given ValueId is contained in a VEGRef
NABoolean ItemExpr::containsTheGivenValue(const ValueId & vid) const
{
// In really peculiar cases, a VEGRef might contain another VEGRef within
// its VEG which recursively self-references the VEGRef. In order to prevent
// running into an infinite loop there, we remember those VEG we've already
// already seen by setting a flag in those VEG. If we run into that again,
// there is no need to check the members of the VEG into it anymore since
// those have already been or will be checked.
//
NABoolean retVal = FALSE;
if (getValueId() == vid)
return TRUE;
switch (getOperatorType())
{
case ITM_VEG_PREDICATE:
{
VEG *veg = ((VEGPredicate *)this)->getVEG();
if (veg->getVEGReference()->getValueId() == vid)
return TRUE;
else
{
// Already seen. Return FALSE. In VEGRef_x(VEG{VEGRef_x(..),y,z}),
// the call to the inner VEGRef_x returns FALSE. However, if the
// vid is VEGRef_x itself, when the function is called on the outer
// VEGRef_x, it has return TRUE already. The previous "if" clause
// ensures that. If vid is one of the other values in the VEG such
// as y or z, ValueIdSet::containsTheGivenValue() is going to call
// us again on other members of the VEG. We will return TRUE with
// the matching member; and ValueIdSet::containsTheGivenValue()
// is also going to return TRUE when we return TRUE on any one of
// the members of the VEG.
//
if (veg->seenBefore())
return FALSE;
else
{
// Mark this VEG as already seen before going over its members,
// so that if one of its members is a VEGRef to this VEG, we
// don't have to traverse the VEG anymore.
//
veg->markAsSeenBefore();
retVal = veg->getAllValues().containsTheGivenValue(vid);
veg->markAsNotSeenBefore();
}
}
break;
}
case ITM_VEG_REFERENCE:
{
VEG *veg = ((VEGReference *)this)->getVEG();
// Already seen. Return FALSE. In VEGRef_x(VEG{VEGRef_x(..),y,z}),
// the call to the inner VEGRef_x returns FALSE. However, if the
// vid is VEGRef_x itself, when the function is called on the outer
// VEGRef_x, it has return TRUE already. The previous "if" clause
// ensures that. If vid is one of the other values in the VEG such
// as y or z, ValueIdSet::containsTheGivenValue() is going to call
// us again on other members of the VEG. We will return TRUE with
// the matching member; and ValueIdSet::containsTheGivenValue()
// is also going to return TRUE when we return TRUE on any one of
// the members of the VEG.
//
if (veg->seenBefore())
{
return FALSE;
}
else
{
// Mark this VEG as already seen before going over its members,
// so that if one of its members is a VEGRef to this VEG, we
// don't have to traverse the VEG anymore.
//
veg->markAsSeenBefore();
retVal = veg->getAllValues().containsTheGivenValue(vid);
veg->markAsNotSeenBefore();
}
break;
}
case ITM_INSTANTIATE_NULL:
{
ValueId nvid = getValueId();
const ItemExpr * nie = nvid.getItemExpr();
InstantiateNull *inst = (InstantiateNull *)nie->castToItemExpr();
// Special code to handle instantiate nulls for subqueries.
// If the instantiate null was introduced for a subquery, then the
// "NoCheckForLeftToInnerJoin" flag was set to TRUE. If set, this
// flag indicates that we don't need to check to see if we need to
// transform the left join to an inner join, and thus possibly
// remove the instantiate null, because the instantiate_null isn't
// for a left join. If the flag is FALSE, however, it means the
// instantiate null is for left join. In this case, we don't need
// to execute this special code, so just break out now.
if (NOT inst->NoCheckforLeftToInnerJoin)
{
break;
}
else
{
// Need to dig underneath the instantiate null to check if
// the value we are looking for is the immediate child of
// the instantiate null or contained in a veg reference
// that is the child of the instantiate null.
const ValueId & childVid = this->child(0)->getValueId();
const ItemExpr * childExpr = childVid.getItemExpr();
return childExpr->containsTheGivenValue(vid);
}
} // end case instantiate null
default:
{
break;
}
} // switch
return retVal;
} // ItemExpr::containsTheGivenValue()
// return the folded expression (if can be done). Return NULL otherwise
ItemExpr* ItemExpr::constFold()
{
ItemExpr* expr = NULL;
try {
expr = copyTree(STMTHEAP);
}
catch (...) {
return NULL;
}
expr->synthTypeAndValueId(TRUE, TRUE);
expr = expr->simplifyBeforeConstFolding();
//expr = simplifyBeforeConstFolding();
if ( expr == NULL )
expr = this;
ValueIdList dummy; ValueId dummyId;
ItemExpr* outExpr = NULL;
Int32 error = dummy.evaluateExpr(dummyId, expr->getValueId(), -1,
FALSE, //don't simplify expr
TRUE, // eval all consts
&outExpr);
if ( error == TRUE && outExpr )
return outExpr;
else
return NULL;
}
ItemExpr* ItemExpr::simplifyBeforeConstFolding()
{
Lng32 nc = getArity();
if ( nc == 0 ) {
return getConstantInVEG();
}
for (Int32 i = 0; i < nc; i++)
{
ValueId id = child(i)->getValueId();
ItemExpr* cExpr = id.getItemExpr()->simplifyBeforeConstFolding();
if ( cExpr )
setChild(i, cExpr);
}
return this;
}
ItemExpr* ItemExpr::getConstantInVEG()
{
switch (getOperatorType())
{
case ITM_VEG_REFERENCE:
{
const VEG * exprVEG = ((VEGReference*)this)->getVEG();
const ValueIdSet & VEGGroup = exprVEG->getAllValues();
ItemExpr *c = NULL;
Lng32 cnt = 0;
for (ValueId id = VEGGroup.init(); VEGGroup.next(id); VEGGroup.advance(id))
{
const ItemExpr * expr = id.getItemExpr();
if (expr->getOperatorType() == ITM_CONSTANT)
{
c = (ConstValue*)(id.getItemExpr());
cnt ++;
}
}
return (cnt == 1) ? c : NULL;
}
default:
break;
}
return NULL;
}
ItemExpr * ItemExpr::createMirrorPred(ItemExpr *compColPtr,
ItemExpr * compColExprPtr,
const ValueIdSet &underlyingCols)
{
CMPASSERT(compColPtr->getOperatorType() == ITM_BASECOLUMN);
BaseColumn *bcol = static_cast<BaseColumn *>(compColPtr);
// use the basecolumn Veg, using the basecolumn by itself can cause issues
// during codegen downstream
ValueId egVid = bcol->getTableDesc()->getColumnVEGList()[bcol->getColNumber()];
ItemExpr *compColVEGRefPtr = egVid.getItemExpr();
switch (getOperatorType())
{
case ITM_VEG_PREDICATE:
{
ValueId refColId ;
// XXX this should be a for loop to collect the each sub
// expression containing each referenced column, mirror those
// sub expressions and then final assembly of the complete
// complete expression. The colde below works for simple expressions
// like A=2 and B=4 when the computed col expr is POWER(A+B,2)
// because of the veges formed.
underlyingCols.getFirst(refColId);
const VEG * predVEG = ((VEGPredicate *) this)->getVEG();
const ValueIdSet & VEGGroup = predVEG->getAllValues();
if (VEGGroup.containsTheGivenValue(refColId) &&
underlyingCols.entries() == 1)
{
ItemExpr * keyColExpr = predVEG->getVEGReference();
ItemExpr * keyColRef = refColId.getItemExpr();
ValueIdMap compExprRewriteMap;
ValueId newCompExpr;
compExprRewriteMap.addMapEntry(keyColExpr->getValueId(), keyColRef->getValueId());
newCompExpr = compColExprPtr->mapAndRewrite(compExprRewriteMap);
// Now try to const folding the newCompExpr by evaluating
// the expression. If successful, we use the computed result
// instead of newCompExpr. The simplification is necessary for
// the scan optimizer to figure out the actual number of rows
// returned per probe.
ItemExpr* foldedExpr = newCompExpr.getItemExpr()->constFold();
ItemExpr * compPred = new(CmpCommon::statementHeap())
BiRelat(ITM_EQUAL,
compColVEGRefPtr,
(foldedExpr==NULL) ? newCompExpr.getItemExpr() : foldedExpr);
compPred->synthTypeAndValueId(TRUE);
return compPred;
}
return NULL;
break;
}
case ITM_EQUAL:
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
{
ItemExpr *keyColRef;
ItemExpr *keyColExpr;
ValueId refColId;
// XXX figure out what it means when we have more than one
// say CC expr is POWER(T.A+T.B,2)
// If the keyPred is: T.A =2 AND T.B=4
// we need to make sure we substitue both T.A and T.B in the power
// expression above.
// The current code does the correct thing for simple veg expressions
// with multiple cols even though it
// only process the first of the underlying cols, due to the veges
// formed. Should be a for loop instead
// Also, at the moment have restricted the key predicate candidates
// to mirror only to be equipreds when we have multiple columns
// referenced in the computedColumn expression.
underlyingCols.getFirst(refColId);
NABoolean keyColOnLeft = TRUE;
if (child(0)->referencesTheGivenValue(refColId) &&
underlyingCols.entries() == 1)
{
keyColRef = child(0);
keyColExpr = child(1);
}
else if (child(1)->referencesTheGivenValue(refColId) &&
underlyingCols.entries() == 1)
{
keyColRef = child(1);
keyColExpr = child(0);
keyColOnLeft = FALSE;
}
else
{ // XXX print warning for now.
#ifdef DEBUG
fprintf(stderr, "ItemExpr::createMirrorPred(): Didn't find any references to valueId: %d\n", tempV);
#endif
return NULL;
}
// now we want to replace the reference to the key column in the compExpr with the keyColExpr
ValueIdMap compExprRewriteMap;
ValueId newCompExpr;
compExprRewriteMap.addMapEntry(keyColExpr->getValueId(), keyColRef->getValueId());
newCompExpr = compColExprPtr->mapAndRewrite(compExprRewriteMap);
ItemExpr* foldedExpr = newCompExpr.getItemExpr()->constFold();
ItemExpr* mirroredExpr =
(foldedExpr==NULL) ? newCompExpr.getItemExpr() : foldedExpr;
ItemExpr * compPred;
if (keyColOnLeft == TRUE)
compPred = new(CmpCommon::statementHeap())
BiRelat(((BiRelat *) this)->getRelaxedComparisonOpType(),
compColVEGRefPtr,
mirroredExpr //newCompExpr.getItemExpr()
);
else
compPred = new(CmpCommon::statementHeap())
BiRelat(((BiRelat *) this)->getRelaxedComparisonOpType(),
mirroredExpr, //newCompExpr.getItemExpr(),
compColVEGRefPtr);
compPred->synthTypeAndValueId(TRUE);
return compPred;
break;
}
default:
return NULL;
}
return NULL;
}
//Does 'this' ItemExpr evaluate to a constant?
//e.g.
//Sin(Cos(1)+1) will return TRUE.
//Sin(?p) will return FALSE.
NABoolean ItemExpr::doesExprEvaluateToConstant(NABoolean strict,
NABoolean considerVEG) const
{
NABoolean result = TRUE;
Lng32 nc = getArity();
//check if I am a leaf node
if(!nc){
//I am a leaf node
//therefore check if I can be considered
//a constant
switch(getOperatorType()){
case ITM_CONSTANT:
case ITM_DEFAULT_SPECIFICATION:
case ITM_PI:
//I am a strict constant, so return TRUE
return TRUE;
case ITM_HOSTVAR:
{
HostVar* hv = (HostVar*)this;
if ( hv->isSystemGeneratedOutputHV() )
return TRUE;
}
case ITM_DYN_PARAM:
case ITM_CACHE_PARAM:
case ITM_CURRENT_USER:
case ITM_SESSION_USER:
case ITM_CURRENT_TIMESTAMP:
case ITM_UNIX_TIMESTAMP:
case ITM_GET_TRIGGERS_STATUS:
case ITM_UNIQUE_EXECUTE_ID:
case ITM_CURR_TRANSID:
if(strict)
//I am a non-strict constant, so return FALSE
return FALSE;
//non-strict definition of constant
//therefore return TRUE
return TRUE;
case ITM_VEG_REFERENCE:
if(considerVEG)
{
const VEG * refVEG = ((VEGReference *)this)->getVEG();
ValueId literal = NULL_VALUE_ID;
if (strict)
{
literal = refVEG->getAConstant();
}
else{
literal = refVEG->getAConstantHostVarOrParameter();
}
if (literal == NULL_VALUE_ID)
return FALSE;
return TRUE;
}
return FALSE;
default:
return FALSE;
}
}
//I am not a leaf node
//check if I the random method
//I will return different values
//for the same parameter, so
//I will never return a constant
//value
if(getOperatorType() == ITM_RANDOMNUM)
return FALSE;
// An aggrgate, sequence function and instantiateNull itemExprs require state information (i.e. other rows)
// to determine their value. Therefore they cannot be a constant. Also a rowset array scan
// denotes an array of values and is therefore not a constant.
if(isAnAggregate()|| isASequenceFunction () ||
(getOperatorType() == ITM_INSTANTIATE_NULL) ||
(getOperatorType() == ITM_ROWSETARRAY_SCAN) )
{
return FALSE;
}
//I am not constant if anyone of my children
//is not constant
//check if all my children can be considered constants
for (Lng32 i = 0; i < nc; i++)
{
result = child(i)->doesExprEvaluateToConstant(strict,considerVEG);
//check if this child is not a constant
if(!result){
//this child is not a constant, so return FALSE
return result;
}
}
//none of my children is a non-constant, so return TRUE
return result;
}//ItemExpr::doesExprEvaluateToConstant()
NABoolean ItemExpr::referencesAHostVar() const
{
NABoolean result = FALSE;
Lng32 nc = getArity();
//check if I am a leaf node
if(!nc)
{
//I am a leaf node
//therefore check if I can be considered
//a constant
switch(getOperatorType())
{
case ITM_HOSTVAR:
case ITM_DYN_PARAM:
case ITM_CURRENT_USER:
case ITM_SESSION_USER:
case ITM_CURRENT_TIMESTAMP:
case ITM_UNIX_TIMESTAMP:
case ITM_GET_TRIGGERS_STATUS:
case ITM_UNIQUE_EXECUTE_ID:
case ITM_CURR_TRANSID:
return TRUE;
case ITM_VEG_PREDICATE:
{
VEG * predVEG = ((VEGPredicate *)this)->getVEG();
// Now, get all members of the VEG group, only if this VEG has not
// been seen before. This is to avoid infinite looping for cases
// where a VEG references itself
if (predVEG->seenBefore())
return FALSE;
// get all members of the VEG
const ValueIdSet & VEGGroup = predVEG->getAllValues();
for (ValueId id = VEGGroup.init(); VEGGroup.next(id); VEGGroup.advance(id))
{
predVEG->markAsSeenBefore();
NABoolean isAHostVar = id.getItemExpr()->referencesAHostVar();
predVEG->markAsNotSeenBefore();
if (isAHostVar)
return TRUE;
}
return FALSE;
} // end Case ITM_VEG_PREDICATE
} // end switch
} // end nc == 0
for (Lng32 i = 0; i < nc; i++)
{
if(this->child(i)->referencesAHostVar())
return TRUE;
}
return FALSE;
}
// This method returns TRUE or FALSE depending whether the optimizer
// should use stats to compute selectivity or use default selectivity
// Return TRUE if the expression is a VEG reference, base column,
// index column, column with CAST / UPPER / LOWER / UCASE /
// LCASE / UPSHIFT / TRIM / SUBSTR (sometimes)
//
// Note that in the case of functions such as CAST and UPPER,
// the stats returned are those of the argument. For a right TRIM
// where we are trimming blanks, this is correct but for the
// others the resulting stats will be incorrect in some way.
// For example, UPPER maps characters to upper case, so the UECs
// of the result should often be lower than the original, and
// certain intervals (namely those encompassing values that begin
// with a lower case letter) should be empty. For another
// example, CAST often is a 1-to-1 transformation so the UECs
// are right, but the order might change (e.g. when casting
// numerics to characters; 99 < 100, but '99' > '100'). Even
// so, the statistics of the argument are still a better reflection
// of the result of the function than the default distribution,
// particularly from the standpoint of skew. So, we use them.
NABoolean ItemExpr::useStatsForPred()
{
OperatorTypeEnum myType = getOperatorType();
if (myType == ITM_VEG_REFERENCE OR
myType == ITM_BASECOLUMN OR
myType == ITM_VEG_PREDICATE OR
myType == ITM_INDEXCOLUMN OR
myType == ITM_VALUEIDUNION OR
myType == ITM_CAST OR
myType == ITM_INSTANTIATE_NULL OR
myType == ITM_UNPACKCOL OR
myType == ITM_UPPER OR
myType == ITM_LOWER OR
myType == ITM_TRIM OR
myType == ITM_CONVERT)
return TRUE;
else if (myType == ITM_SUBSTR)
{
// if the substring is known to be a prefix of the string,
// then use the stats
ItemExpr * startPosition = child(1);
if (startPosition->getOperatorType() == ITM_CONSTANT)
{
NABoolean negate = FALSE;
ConstValue * c = startPosition->castToConstValue(negate);
if (c->canGetExactNumericValue() &&
(c->getExactNumericValue() == 1))
return TRUE;
}
return FALSE;
}
else
return FALSE;
}
void ItemExpr::getLeafValueIds(ValueIdSet &lv) const
{
Int32 nc = getArity();
// if this is a leaf node, add its value id
// UDFunction uses the getArity() function to return the number
// of inputs. It is really a leaf value...
if (nc == 0 || getOperatorType() == ITM_USER_DEF_FUNCTION)
{
lv += getValueId();
}
else
{
// else add the leaf value ids of all the children
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
child(i)->getLeafValueIds(lv);
}
}
}
void ItemExpr::getLeafValueIds(ValueIdList &lv) const
{
Int32 nc = getArity();
// if this is a leaf node, add its value id
// UDFunction uses the getArity() function to return the number
// of inputs. It is really a leaf value...
if (nc == 0 || getOperatorType() == ITM_USER_DEF_FUNCTION)
{
lv.insert(getValueId());
}
else
{
// else add the leaf value ids of all the children
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
child(i)->getLeafValueIds(lv);
}
}
}
void ItemExpr::getLeafValueIdsForCaseExpr(ValueIdSet &lv)
{
Int32 nc = getArity();
// if this is a leaf node, add its value id
if (nc == 0)
{
if (doesExprEvaluateToConstant(FALSE))
lv += getValueId();
else
{
ValueIdSet baseColSet;
findAll(ITM_BASECOLUMN, baseColSet, TRUE, TRUE);
lv.addSet(baseColSet);
}
}
else
{
// elseadd the leaf value ids of all the children
if (getOperatorType() == ITM_IF_THEN_ELSE)
{
// for if_then_else statement ignore the first parameter which is the condition
child(1)->getLeafValueIdsForCaseExpr(lv);
// else part of Case can be a NULL pointer if it is created by Generator
if (child(2))
child(2)->getLeafValueIdsForCaseExpr(lv);
}
else // for Case get the child, which would be IF_THEN_ELSE
{
if (getOperatorType() == ITM_CASE)
child(0)->getLeafValueIdsForCaseExpr(lv);
else // for all other expressions, collect the base columns
{
ValueIdSet baseColSet;
findAll(ITM_BASECOLUMN, baseColSet, TRUE, TRUE);
lv.addSet(baseColSet);
}
}
}
}
// get leaf expression if the cardinality for this expression can be
// estimated using histograms
ItemExpr * ItemExpr::getLeafValueIfUseStats(NABoolean digIntoInstantiateNull)
{
ItemExpr * expr = this;
if (useStatsForPred())
{
if (digIntoInstantiateNull == FALSE && expr->getOperatorType() == ITM_INSTANTIATE_NULL)
return expr;
if(expr->getArity() > 0)
{
if (expr->getOperatorType() != ITM_TRIM)
expr = expr->child(0)->getLeafValueIfUseStats(digIntoInstantiateNull);
else
expr = expr->child(1)->getLeafValueIfUseStats(digIntoInstantiateNull);
}
}
return expr;
}
ItemExpr * ItemExpr::castToItemExpr()
{
return this;
} // ItemExpr::castToItemExpr()
const ItemExpr * ItemExpr::castToItemExpr() const
{
return this;
} // ItemExpr::castToItemExpr()
ConstValue * ItemExpr::castToConstValue(NABoolean & negate_it)
{
negate_it = FALSE;
return NULL;
}
SimpleHashValue ItemExpr::hash()
{
// this method is just defined to have a hash method in ExprNode
// without referencing class HashValue (which is too complicated
// for the common code directory)
return treeHash().getValue();
}
HashValue ItemExpr::topHash()
{
HashValue result = 0;
// hash on item properties???
return result;
}
// this method is not virtual, since combining the hash values of the
// top node and its inputs should be independent of the actual node
HashValue ItemExpr::treeHash()
{
HashValue result = topHash();
Int32 maxc = getArity();
for (Lng32 i = 0; i < (Lng32)maxc; i++)
{
// call this method recursively for the inputs
result ^= child(i)->treeHash();
}
return result;
}
NABoolean ItemExpr::duplicateMatch(const ItemExpr & other) const
{
// duplicateMatch works a little different in ItemExpr objects than it
// does in RelExpr. In classes derived from RelExpr we strictly require
// the duplicateMatch() method to be defined correctly. In classes
// derived from ItemExpr this has not been done. Therefore, the
// default method returns FALSE to be conservative (except
// when the two expressions are the same).
// The generic processing of duplicateMatch() for every derived class
// is done in a separate method, genericDuplicateMatch().
return (this == &other);
}
NABoolean ItemExpr::genericDuplicateMatch(const ItemExpr & other) const
{
DCMPASSERT(other.castToItemExpr());
if ((getValueId() == other.getValueId()) &&
(getValueId() != NULL_VALUE_ID))
return TRUE;
if (getOperatorType() != other.getOperatorType())
return FALSE;
Int32 arity = getArity();
if (arity != other.getArity())
return FALSE;
for (Lng32 i=0; i < (Lng32) arity; i++)
{
if (NOT (child(i)->duplicateMatch(*(other.child(i).getPtr()))))
return FALSE;
}
return TRUE;
}
ItemExpr * ItemExpr::copyTopNode(ItemExpr *derivedNode,
CollHeap* )
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
ABORT("encountered an instantiation of an ItemExpr object");
}
else
result = derivedNode;
// copy this's (the source's) original setting to result (the target)
result->origOpType_ = origOpType_;
result->selectivityFactor_ = selectivityFactor_;
#ifndef NDEBUG
CMPASSERT(!collateClause()); // else, Binder fell down on the job
#endif // (wrong overload of synthType() used)
// copy/synthesize other item properties???
return result;
}
// this method is not virtual, since combining the copies of the
// top node and its inputs should be independent of the actual node
ItemExpr * ItemExpr::copyTree(CollHeap* outHeap)
{
ItemExpr * result = copyTopNode(NULL, outHeap);
Int32 arity = getArity();
for (Int32 i = 0; i < arity; i++) {
if (child(i)) {
result->child(i) = child(i)->copyTree(outHeap);
}
}
return result;
}
NABoolean ItemExpr::containsSubquery() // virtual method
{
Int32 arity = getArity();
for (Int32 i = 0; i < arity; i++)
if (child(i) && child(i)->containsSubquery())
return TRUE;
return FALSE;
}
ItemExpr *ItemExpr::containsUDF() // virtual method
{
Int32 arity = getArity();
for (Int32 i = 0; i < arity; i++)
if (child(i) && child(i)->containsUDF())
return child(i)->containsUDF();
return 0;
}
NABoolean ItemExpr::containsIsolatedUDFunction() // virtual method
{
Int32 arity = getArity();
for (Int32 i = 0; i < arity; i++)
if (child(i) && child(i)->containsIsolatedUDFunction())
return TRUE;
return FALSE;
}
NABoolean ItemExpr::containsValueIdProxySibling(const ValueIdSet &siblings)
// virtual method
{
Int32 arity = getArity();
for (Int32 i = 0; i < arity; i++)
if (child(i) && child(i)->containsValueIdProxySibling(siblings))
return TRUE;
return FALSE;
}
NABoolean ItemExpr::isASubquery() const { return FALSE; } // virtual method
NABoolean ItemExpr::isAnAggregate() const { return FALSE; } // virtual method
NABoolean ItemExpr::isAPredicate() const { return FALSE; } // virtual method
NABoolean ItemExpr::isASequenceFunction() const { return FALSE; } // virtual method
NABoolean ItemExpr::isOlapFunction() const { return FALSE; } // virtual method
NABoolean ItemExpr::isAUserSuppliedInput() const { return FALSE; } // virtual
ItemExpr * ItemExpr::transformMultiValuePredicate( // virtual method
NABoolean /*flattenSubqueries*/,
ChildCondition /*tfmIf*/)
{ return NULL; }
NABoolean ItemExpr::containsAnAggregate() const
{
for (Int32 i=0; i<getArity(); i++)
{
if (child(i)->containsAnAggregate())
return TRUE;
}
return FALSE;
}
// ----------------------------------------------------------------------
// Walk through an ItemExpr tree and gather the ValueIds of those
// expressions that behave as if they are "leaves" for the sake of
// the coverage test, e.g., expressions that have no children, or
// aggregate functions, or instantiate null. These are usually values
// that are produced in one "scope" and referenced above that "scope"
// in the dataflow tree for the query.
// ----------------------------------------------------------------------
void ItemExpr::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
Lng32 nc = getArity();
if ((nc == 0)
|| coveringGA.isCharacteristicOutput(getValueId())
|| newExternalInputs.contains(getValueId()))
{
leafValues += getValueId();
}
else
{
for (Lng32 i = 0; i < nc; i++) {
if (coveringGA.isCharacteristicOutput(child(i)->getValueId()) ||
newExternalInputs.contains(child(i)->getValueId()))
leafValues += child(i)->getValueId();
else
child(i)->getLeafValuesForCoverTest(leafValues, coveringGA, newExternalInputs);
}
}
} // ItemExpr::getLeafValuesForCoverTest()
// This is a recursive
// function that returns the leaf predicates of the tree
// whose root is the ItemExpr into the ValueIdSet&.
void ItemExpr::getLeafPredicates(ValueIdSet& leafPredicates)
{
if (getOperatorType() != ITM_AND &&
getOperatorType() != ITM_OR)
leafPredicates.insert(getValueId());
else {
for (Lng32 i=0; i < (Lng32)getArity(); i++)
child(i)->getLeafPredicates(leafPredicates);
}
}// getLeafPredicates()
// -----------------------------------------------------------------------
// temp. method to collect basecolumns, used in FileScanRule
// -----------------------------------------------------------------------
template <class Result>
void ItemExpr::findAllT(OperatorTypeEnum wantedType,
Result& result,
NABoolean visitVEGMembers,
NABoolean visitIndexColDefs)
{
OperatorTypeEnum myType = getOperatorType();
if (myType == wantedType)
result.addMember(this);
// for VEGReferences and VEGPredicates (both having arity 0),
// look at all the VEG members as well, if requested by the caller
if (visitVEGMembers AND
(myType == ITM_VEG_REFERENCE OR myType == ITM_VEG_PREDICATE))
{
VEG *veg;
ValueId vegM;
if (myType == ITM_VEG_REFERENCE)
veg = ((VEGReference *) this)->getVEG();
else
veg = ((VEGPredicate *) this)->getVEG();
if(! veg->seenBefore())
{
veg->markAsSeenBefore();
for (ValueId x = veg->getAllValues().init();
veg->getAllValues().next(x);
veg->getAllValues().advance(x))
x.getItemExpr()->findAllT(wantedType,
result,
visitVEGMembers,
visitIndexColDefs);
veg->markAsNotSeenBefore();
}
}
// for indexcolumns, look at the corresponding base column
// or expression from the base table, if requested
if (visitIndexColDefs AND myType == ITM_INDEXCOLUMN)
{
((IndexColumn *) this)->getDefinition().getItemExpr()->
findAllT(wantedType,
result,
visitVEGMembers,
visitIndexColDefs);
}
// recurse
Int32 nc = getArity();
for (Lng32 i = 0; i < (Lng32)nc; i++)
child(i)->findAllT(wantedType,
result,
visitVEGMembers,
visitIndexColDefs);
if ( nc == 0 )
switch ( myType )
{
case ITM_VALUEIDUNION:
{
// NB: ValueIdUnion objects can have more than 2 sources!
ValueIdUnion * tempUnion = (ValueIdUnion*) this;
for (Lng32 i = 0; i < (Lng32)tempUnion->entries(); i++)
{
// guard against loops in the references
// (can happen with common subexpressions, for example)
if (!tempUnion->getSource(i).getItemExpr()->
referencesTheGivenValue(getValueId()))
tempUnion->getSource(i).getItemExpr()->
findAllT(wantedType,
result,
visitVEGMembers,
visitIndexColDefs);
}
break;
}
default:
break;
}
} // ItemExpr::findAllT, Template version
void ItemExpr::findAll(OperatorTypeEnum wantedType,
ValueIdSet & result,
NABoolean visitVEGMembers,
NABoolean visitIndexColDefs)
{
findAllT(wantedType, result, visitVEGMembers, visitIndexColDefs);
}
void ItemExpr::findAll(OperatorTypeEnum wantedType,
ItemExprList& result,
NABoolean visitVEGMembers,
NABoolean visitIndexColDefs)
{
// Collect all ItemExprs into a list.
findAllT(wantedType, result, visitVEGMembers, visitIndexColDefs);
} // ItemExpr::findAll
Lng32 ItemExpr::getTreeSize(Lng32& maxDepth, NABoolean giveUpThreshold)
{
Lng32 currentSize=1;
Int32 nc = getArity();
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
Lng32 thisDepth = 0;
currentSize += child(i)->getTreeSize(thisDepth, giveUpThreshold);
if (giveUpThreshold > 0 && currentSize >= giveUpThreshold)
break;
if (thisDepth > maxDepth)
maxDepth = thisDepth;
}
maxDepth++;
return currentSize;
}
// Find all eqaulity columns in an item expression tree.
void ItemExpr::findEqualityCols(ValueIdSet& result)
{
OperatorTypeEnum myType = getOperatorType();
ValueId myVid = getValueId();
// If it is a base column or an index column, add to the list
if (myType == ITM_BASECOLUMN || myType == ITM_INDEXCOLUMN)
result += myVid;
// If it belongs to VEG, add all the memebers of that VEG
if (myType == ITM_VEG_REFERENCE || myType == ITM_VEG_PREDICATE)
{
VEG *veg;
if (myType == ITM_VEG_REFERENCE)
veg = ((VEGReference *) myVid.getItemExpr())->getVEG();
else
// myType == ITM_VEG_PREDICATE
veg = ((VEGPredicate *) myVid.getItemExpr())->getVEG();
for (ValueId vid = veg->getAllValues().init();
veg->getAllValues().next(vid);
veg->getAllValues().advance(vid))
{
result += vid;
}
}
// recurse
if (myType == ITM_EQUAL)
{
child(0)->findEqualityCols(result);
child(1)->findEqualityCols(result);
}
}
ItemExpr * ItemExpr::treeWalk(ItemTreeWalkFunc f,
CollHeap *outHeap,
enum ItemTreeWalkSeq sequence,
void *context)
{
ItemExpr *transformedChildrenArr[4];
ItemExpr **transformedChildren = transformedChildrenArr;
Int32 arity = -1;
NABoolean needToCopy = FALSE;
ItemExpr *result = this;
if (sequence == ITM_PREFIX_WALK)
result = f(result, outHeap, context);
arity = result->getArity();
if (arity > 4)
// allocated from stmt heap since it is used locally only
// and is not deallocated below
transformedChildren = new(CmpCommon::statementHeap()) ItemExpr *[getArity()];
for (int i=0; i<arity; i++)
{
transformedChildren[i] =
result->child(i)->treeWalk(f,
outHeap,
sequence,
context);
if (transformedChildren[i] != child(i))
needToCopy = TRUE;
}
if (needToCopy)
{
// one of the children changed, since we can't
// change an ItemExpr once we assigned a ValueId to
// it, make a copy with new children
result = result->copyTopNode(NULL, outHeap);
for (int j=0; j<arity; j++)
result->setChild(j, transformedChildren[j]);
}
if (sequence == ITM_POSTFIX_WALK)
result = f(result, outHeap, context);
return result;
}
ValueId ItemExpr::mapAndRewrite(ValueIdMap &map,
NABoolean mapDownwards)
{
ValueId result;
// Look in the map for a match
if (mapDownwards)
map.mapValueIdDown(getValueId(),result);
else
map.mapValueIdUp(result,getValueId());
// if this expression can be mapped directly, then return the
// mapped value id
if (result != getValueId())
return result;
return mapAndRewriteCommon(map, mapDownwards);
} // ItemExpr::mapAndRewrite
ValueId Aggregate::mapAndRewrite(ValueIdMap &map,
NABoolean mapDownwards)
{
// Need to make sure we map and rewrite the distinctId as well.
if (isDistinct())
distinctId_ = distinctId_.getItemExpr()->mapAndRewrite(map, mapDownwards);
return ItemExpr::mapAndRewrite(map, mapDownwards);
} // Aggregate::mapAndRewrite
ValueId ItemExpr::mapAndRewriteWithIndx(ValueIdMap &map,
CollIndex ind)
{
ValueId result;
map.mapValueIdUpWithIndex(result,getValueId(), ind);
// if this expression can be mapped directly, then return the
// mapped value id
if (result != getValueId())
return result;
return mapAndRewriteCommon(map, FALSE);
} // ItemExpr::mapAndRewriteWithIndex
// This method has the code that is common to
// mapAndRewrite() and mapAndRewriteWithIndex().
ValueId ItemExpr::mapAndRewriteCommon(ValueIdMap &map, NABoolean mapDownwards)
{
ValueId result;
ItemExpr *copyOfMe = NULL;
Lng32 nc = getArity();
// if the children of this expression changed after mapping, then
// return the value id of a duplicate of myself with the new
// children attached to it
for (Lng32 i = 0; i < nc; i++)
{
ValueId c = child(i)->mapAndRewrite(map,mapDownwards);
if (c != child(i)->getValueId())
{
// This child was mapped to a different value id
if (copyOfMe == NULL)
{
// This was the first child to map to a different value
// make a copy of me and initialize its children with
// the same pointers that I have
copyOfMe = copyTopNode(NULL, CmpCommon::statementHeap());
// copy the previous children into the new expression
for (Lng32 j = 0; j < nc; j++)
copyOfMe->child(j) = child(j).getPtr();
}
copyOfMe->child(i) = c.getItemExpr();
}
}
// If the expr had not child then copyOfMe is NULL
if (copyOfMe)
{
// A new expression was created as the mapped value
copyOfMe->synthTypeAndValueId();
result = copyOfMe->getValueId();
// Add to the map table the newly formed expression
if (mapDownwards)
map.addMapEntry(getValueId(),result);
else
map.addMapEntry(result,getValueId());
}
else
// otherwise just return my value id
result = getValueId();
return result;
} // ItemExpr::mapAndRewriteCommon
ItemExpr * ItemExpr::foldConstants(ComDiagsArea *diagsArea,
NABoolean newTypeSynthesis)
{
ItemExpr *result = this;
Lng32 nc = getArity();
// if the children of this expression changed after constant folding, then
// return a duplicate of myself with the new children attached to it
for (Lng32 i = 0; i < nc; i++)
{
ItemExpr *c = child(i)->foldConstants(diagsArea,newTypeSynthesis);
if (c != child(i))
{
// This child was transformed into a different ItemExpr
if (result == this)
{
// This was the first child to be transformed;
// make a copy of me and initialize its children with
// the same pointers that I have
result = copyTopNode(NULL, CmpCommon::statementHeap());
// copy the previous children into the new expression
for (Lng32 j = 0; j < nc; j++)
result->child(j) = child(j).getPtr();
}
result->child(i) = c;
}
}
// make sure we return a tree with a correct type
if (result != this)
result->synthTypeAndValueId(newTypeSynthesis);
return result;
}
ItemExpr * ItemExpr::applyInverseDistributivityLaw(
OperatorTypeEnum backboneType,
OperatorTypeEnum innerType)
{
// operators other than OR will require some more code, see CMPASSERTs
DCMPASSERT(backboneType == ITM_OR);
// we must have a backbone of the backboneType for this to work
if (getOperatorType() != backboneType)
return this;
// a default can switch this code off if required (should be needed
// only if bugs are found in the code)
if (CmpCommon::getDefault(FIND_COMMON_SUBEXPRS_IN_OR) != DF_ON)
return this;
ItemExpr *result = NULL;
// NOTE: we'll use the words "disjuncts" and "conjuncts" that make
// sense with the default operator types, but may not be a great choice
// if the caller uses different types (sorry, but this might help
// understand the code better)
ValueIdSet disjuncts;
ValueIdSet commonSubexprs;
CollIndex numCommonSubexprs;
ValueId c;
// get a ValueIdSet with all the "disjuncts".
convertToValueIdSet(disjuncts,NULL,backboneType,FALSE);
// prime the common subexpressions with all of the conjuncts
// of the first disjunct
disjuncts.getFirst(c);
c.getItemExpr()->convertToValueIdSet(commonSubexprs,NULL,innerType,FALSE);
disjuncts.advance(c);
// in a first loop, find the common subexpressions among all of the disjuncts
for (/* c already initialized */; disjuncts.next(c); disjuncts.advance(c))
{
ValueIdSet conjuncts;
c.getItemExpr()->convertToValueIdSet(conjuncts,NULL,innerType,FALSE);
// find common subexpressions between the
// previous and the current disjunct
commonSubexprs.findCommonSubexpressions(conjuncts);
// stop if we have no common subexpressions
if (commonSubexprs.isEmpty())
break;
}
numCommonSubexprs = commonSubexprs.entries();
if (numCommonSubexprs > 0)
{
// Now that we know the real common subexpressions do the same loop
// again, but this time modify the conjuncts by removing the
// common parts from them, forming new conjuncts, and connecting
// them to form a new backbone of disjuncts.
for (ValueId c2 = disjuncts.init();
disjuncts.next(c2);
disjuncts.advance(c2))
{
ValueIdSet conjuncts;
// one child of the backbone, eliminate common subexpressions
// from it
c2.getItemExpr()->convertToValueIdSet(conjuncts,
NULL,
innerType,
FALSE);
commonSubexprs.findCommonSubexpressions(conjuncts, TRUE);
// make sure the common subexpressions haven't changed
CMPASSERT(numCommonSubexprs == commonSubexprs.entries());
// are there any non-common conjuncts left?
if (conjuncts.entries() == 0)
{
// No, one of the disjuncts consists of only the common
// subexpressions. This requires logic specific to the
// operator type. For example, (a=10 and b=11) or (a=10)
// is (a=10) and (b=11 or TRUE) which is (a=10).
CMPASSERT(backboneType == ITM_OR);
result = NULL;
// leave the inner loop with a NULL result, meaning
// that only the common subexpressions should survive
break;
}
// rebuild the ItemExpr tree for the conjuncts
ValueIdList conjunctList(conjuncts);
ItemExpr *newConjuncts = conjunctList.rebuildExprTree(innerType);
// rebuild the backbone
if (result == NULL)
result = newConjuncts;
else
result = connect2(backboneType,result,newConjuncts);
}
// create a tree with the common subexpressions
ValueIdList commonSubexprsList(commonSubexprs);
ItemExpr *commonSubexprsAsItem =
commonSubexprsList.rebuildExprTree(innerType);
// hook up common part and the non-common expressions (if any)
if (result)
result = connect2(innerType,commonSubexprsAsItem,result);
else
result = commonSubexprsAsItem;
result->synthTypeAndValueId();
}
else
{
// sorry, no common subexpressions found, return original expression
result = this;
}
return result;
}
// little helper for the above method
ItemExpr * ItemExpr::connect2(OperatorTypeEnum op,
ItemExpr *op1,
ItemExpr *op2)
{
switch (op)
{
case ITM_AND:
case ITM_OR:
return new(CmpCommon::statementHeap()) BiLogic(op, op1, op2);
default:
CMPASSERT("Operator type not supported by connect2" == 0);
return NULL;
}
}
// -----------------------------------------------------------------------
// ItemExpr::synthTypeAndValueId()
// -----------------------------------------------------------------------
void ItemExpr::synthTypeAndValueId(NABoolean redriveTypeSynthesisFlag, NABoolean redriveChildTypeSynthesis)
{
// If the redriveTypeSynthesisFlag is set, it synthesises iself.
// If the redriveChildTypeSynthesis is set, it synthesizes the
// types for the entire subtree, from the leaves upto this operator,
// once again.
if (nodeIsBound() AND NOT redriveTypeSynthesisFlag) return;
Int32 nc = getArity();
// do it recursively on the children
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
if (child(i))
{
// If it is an Item Expressions
child(i)->synthTypeAndValueId(redriveChildTypeSynthesis, redriveChildTypeSynthesis);
// refer to the child by its valueid instead of its pointer
child(i) = child(i)->getValueId();
}
// else leave the RelExpr alone
}
// Now do the non-recursive part
synthTypeAndValueId2( redriveTypeSynthesisFlag, redriveChildTypeSynthesis );
} // ItemExpr::synthTypeAndValueId()
//
// ItemExpr::synthTypeAndValueId2() - a helper routine for synthTypeAndValueId()
// NOTE: The code for this routine came from the previous version of
// ItemExpr::synthTypeAndValueId(). It was pulled out as a separate
// routine so that the C++ compiler would generate code for
// ItemExpr::synthTypeAndValueId() that used significantly less stack space.
//
void ItemExpr::synthTypeAndValueId2(NABoolean redriveTypeSynthesisFlag, NABoolean redriveChildTypeSynthesis)
{
Int32 nc = getArity();
ValueDesc *vdesc;
DomainDesc *ddesc;
// make sure the expression has a value id
if (valId_ == NULL_VALUE_ID)
{
vdesc = new (CmpCommon::statementHeap()) ValueDesc(this);
// remember the value id and the type in the node
setValueId(vdesc->getValueId());
}
else
{
vdesc = valId_.getValueDesc();
}
// if the type hasn't been set or if type synthesis is to be
// redriven, set the type to the synthesized value
ddesc = vdesc->getDomainDesc();
if (ddesc == NULL OR (nc > 0 AND redriveTypeSynthesisFlag))
{
const NAType *t = synthesizeType();
// changed from CMPASSERT to CMPABORT, because it could be null
// for several reasons. In most cases it is because of User error
// where, the diagnostics would be filled with the error number.
// In such cases it is wrong to raise an unnecessary alarm. If the
// diagnostics buffer is not filled, then raise an exception
if (!t)
CMPABORT;
// Fix for "BR0094.txt", here and in Case::bindNode().
// Special handling is required for CASE because its operand
// is not one of its children (because the operand can be a subquery,
// which the normalizer needs to move). And when that operand IS a subq,
// then the CASE can return NULL if the subq produces zero rows.
if (getOperatorType() == ITM_CASE &&
((Case *)this)->caseOperandWasNullable())
t = t->synthesizeNullableType(CmpCommon::statementHeap());
if (ddesc == NULL)
{
ddesc = new (CmpCommon::statementHeap())
DomainDesc(ActiveSchemaDB(), *t);
vdesc->setDomainDesc(ddesc);
}
else
{
getValueId().changeType(t);
}
}
// mark me as bound
markAsBound();
} // ItemExpr::synthTypeAndValueId2()
// -----------------------------------------------------------------------
// ItemExpr::isCovered()
// -----------------------------------------------------------------------
NABoolean ItemExpr::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
NABoolean exprIsCovered;
switch (getArity())
{
case 0:
exprIsCovered = FALSE;
break;
default:
{
exprIsCovered = TRUE;
NABoolean coverFlag;
Lng32 index, nc = getArity();
for (index = 0; index < nc; index++)
{
coverFlag = coveringGA.covers(child(index)->getValueId(),
newExternalInputs,
referencedInputs,
&coveredSubExpr,
&unCoveredExpr);
if (coverFlag)
coveredSubExpr += ((ItemExpr *)child(index))->getValueId();
exprIsCovered &= coverFlag;
} // for i'th child of expr
break;
} // default
} // switch(getArity())
return exprIsCovered;
} // ItemExpr::isCovered()
OrderComparison ItemExpr::sameOrder(ItemExpr *other,
NABoolean askOther)
{
// first, check whether the item expressions are identical
if (this == other)
return SAME_ORDER;
// second, try to simplify both item expressions
OrderComparison order1;
OrderComparison order2;
OrderComparison result;
Int32 inversions = 0;
ItemExpr *simpThis = simplifyOrderExpr(&order1);
ItemExpr *simpOther = other->simplifyOrderExpr(&order2);
// check whether the expressions actually got simplified, return
// DIFFERENT order if that was not the case
if (this == simpThis AND other == simpOther)
return DIFFERENT_ORDER;
// Recursively call sameOrder with the simplified expressions
// If it is a VEG reference, need to do a cast of "simpOther" to type
// VEGReference so that the implementation of sameOrder that accepts
// a VEGReference "other" parameter will be called.
if (simpOther->getOperatorType() == ITM_VEG_REFERENCE)
result = simpThis->sameOrder((VEGReference *)simpOther,askOther);
else
result = simpThis->sameOrder(simpOther,askOther);
if (result != DIFFERENT_ORDER)
{
// if the order is not different, add up all the inversions
// we encountered, either from simplifying expressions or
// by comparing them
if (order1 == INVERSE_ORDER)
inversions++;
if (order2 == INVERSE_ORDER)
inversions++;
if (result == INVERSE_ORDER)
inversions++;
// if we inverted an even number of times then the two expressions
// have the same order
if (inversions == 0 OR inversions == 2)
result = SAME_ORDER;
else
result = INVERSE_ORDER;
}
return result;
}
OrderComparison ItemExpr::sameOrder(VEGReference *other,
NABoolean askOther)
{
// First, check whether the "this" item expression is identical
// to the "other" VegReference
if (this == other)
return SAME_ORDER;
// Second, go through the "other" VegReference members one at a
// time, and call sameOrder recursively to see if one of the member
// ItemExpr's is identical to "this".
const ValueIdSet &equivValues = other->getVEG()->getAllValues();
OrderComparison result = DIFFERENT_ORDER;
for (ValueId x = equivValues.init();
equivValues.next(x) AND result == DIFFERENT_ORDER;
equivValues.advance(x))
{
result = sameOrder(x.getItemExpr(),askOther);
}
return result;
}
ItemExpr * ItemExpr::simplifyOrderExpr(OrderComparison *newOrder)
{
// The default implementation assumes that it is not possible
// to simplify an expression.
if (newOrder)
*newOrder = SAME_ORDER;
return this;
}
ItemExpr * ItemExpr::removeInverseOrder()
{
// The default implementation just returns a pointer to itself,
// since obviously there is no inverse order operator to remove.
return this;
}
OperatorTypeEnum ItemExpr::getInverseOpType() const
{
switch (getOperatorType())
{
case ITM_EQUAL:
return ITM_NOT_EQUAL;
case ITM_NOT_EQUAL:
return ITM_EQUAL;
case ITM_LESS:
return ITM_GREATER;
case ITM_LESS_EQ:
return ITM_GREATER_EQ;
case ITM_GREATER:
return ITM_LESS;
case ITM_GREATER_EQ:
return ITM_LESS_EQ;
case ITM_IS_TRUE:
return ITM_IS_FALSE;
case ITM_IS_FALSE:
return ITM_IS_TRUE;
case ITM_IS_NULL:
return ITM_IS_NOT_NULL;
case ITM_IS_NOT_NULL:
return ITM_IS_NULL;
case ITM_IS_UNKNOWN:
return ITM_IS_NOT_UNKNOWN;
case ITM_IS_NOT_UNKNOWN:
return ITM_IS_UNKNOWN;
case ITM_RETURN_FALSE:
return ITM_RETURN_TRUE;
case ITM_RETURN_TRUE:
return ITM_RETURN_FALSE;
default:
return (getOperatorType());
}
}
NABoolean ItemExpr::isAColumnReference( )
{ return FALSE; }
// return true iff maxSelectivity(this) == selectivity(this)
NABoolean ItemExpr::maxSelectivitySameAsSelectivity() const
{
switch (getOperatorType())
{
case ITM_EQUAL:
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_GREATER:
case ITM_GREATER_EQ:
if (getArity() > 1)
{
// maxSelectivity(any op constant) == selectivity(any op constant)
// maxSelectivity(constant op any) == selectivity(constant op any)
// when op is oneof: =, <, <=, >, >=
NABoolean negate;
ItemExpr * rhs = child(1);
ItemExpr * lhs = child(0);
if (rhs->getOperatorType() == ITM_CACHE_PARAM)
rhs = ((ConstantParameter *)rhs)->getConstVal();
else
rhs = rhs->castToConstValue( negate );
if (lhs->getOperatorType() == ITM_CACHE_PARAM)
lhs = ((ConstantParameter *)lhs)->getConstVal();
else
lhs = lhs->castToConstValue( negate );
return ((lhs != NULL) || (rhs != NULL));
}
break;
// maxSelectivity(any <> constant) is 1.0
case ITM_IS_NULL:
case ITM_IS_UNKNOWN:
case ITM_IS_NOT_NULL:
case ITM_IS_NOT_UNKNOWN:
// maxSelectivity(any is null) == selectivity(any is null)
// maxSelectivity(any is not null) == selectivity(any is not null)
return TRUE;
default:
return FALSE;
}
return FALSE;
}
void ItemExpr::print(FILE * f,
const char * prefix,
const char * suffix) const
{
#ifndef NDEBUG
ExprNode::print(f,prefix,suffix);
#ifndef NDEBUG
fprintf(f,"%sItem Expression (Value Id %d): ",prefix, (CollIndex) valId_);
if ( valId_ != NULL_VALUE_ID && &(valId_.getType()) != NULL )
fprintf( f, "%s", valId_.getType().getTypeSQLname().data() );
fprintf( f, "\n" );
#else
fprintf(f,"%sItem Expression (Value Id %d):\n",prefix, (CollIndex) valId_);
#endif
// print children
Int32 nc = getArity();
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
fprintf(f,"%sExpression input %d:\n",prefix,i);
if (child(i).getPtr())
child(i)->print(f,CONCAT(prefix," "));
else
fprintf(f,"%snonexistent child\n",prefix);
}
#endif
}
void ItemExpr::display()
{
NAString result;
unparse(result, PARSER_PHASE, USER_FORMAT_DELUXE);
fprintf(stdout, "%s\n", result.data());
}
//
// computeKwdAndFlags() - a helper routine for ItemExpr::unparse()
//
// NOTE: The code in this routine came from the previous version of
// ItemExpr::unparse(). 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::unparse() routine.
//
void ItemExpr::computeKwdAndFlags( NAString &kwd,
NABoolean &prefixFns,
NABoolean &specialPrefixFns,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId ) const
{
OperatorTypeEnum operatorType = getOperatorType();
if ( operatorType == ITM_BASECOLUMN)
{
if (form == QUERY_FORMAT)
{
if (CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) != DF_DUMP_MV)
kwd = ((BaseColumn *)this)->getTextForQuery();
else
kwd = getText();
}
else if ((form == COMPUTED_COLUMN_FORMAT) ||
(form == HIVE_MD_FORMAT))
kwd = ToAnsiIdentifier(((BaseColumn *)this)->getColName());
else
kwd = getText();
}
else if ( operatorType == ITM_RENAME_COL)
{
if (form == HIVE_MD_FORMAT)
kwd = ((RenameCol *)this)->getNewColRefName()->getColName();
else
kwd = getText();
}
else if (operatorType == ITM_NAMED_TYPE_TO_ITEM)
{
kwd = ToAnsiIdentifier(((NamedTypeToItem *)this)->getText());
}
else if (( operatorType == ITM_CACHE_PARAM) &&
(form == QUERY_FORMAT) )
((ConstantParameter *)this)->getConstVal()->unparse(kwd, phase, QUERY_FORMAT, tabId);
else
kwd = getText();
if (form == USER_FORMAT_DELUXE)
// Do not upcase if string literal or delimited ident.
if (!strchr(kwd, '\"') && !strchr(kwd, '\'')) //"
{
TrimNAStringSpace(kwd);
kwd.toUpper();
}
switch (operatorType)
{
case ITM_BITAND:
case ITM_CAST:
case ITM_CHAR_LENGTH:
case ITM_EXTRACT:
case ITM_LEFT:
case ITM_MOD:
case ITM_MOVING_SDEV:
case ITM_MOVING_VARIANCE:
case ITM_OFFSET:
case ITM_POSITION:
case ITM_POWER:
case ITM_STDDEV:
case ITM_SUBSTR:
case ITM_VARIANCE:
prefixFns = TRUE;
break;
case ITM_DATEDIFF_YEAR:
case ITM_DATEDIFF_QUARTER:
case ITM_DATEDIFF_MONTH:
case ITM_DATEDIFF_WEEK:
case ITM_YEARWEEK:
case ITM_YEARWEEKD:
specialPrefixFns = TRUE;
break;
case ITM_PLUS:
case ITM_MINUS:
if (((BiArith *) this)->isDateMathFunction() &&
(form == QUERY_FORMAT ||
form == COMPUTED_COLUMN_FORMAT))
{
// this is not a regular addition or subtractions, it's
// a datetime function with special handling of the last
// day of the month
specialPrefixFns = TRUE;
}
}
}
//
// computeKwdAndPostfix() - a helper routine for ItemExpr::unparse()
//
// NOTE: Either computes or augments kwd, depending on the item expr involved.
//
// NOTE: The code in this routine came from the previous version of
// ItemExpr::unparse(). 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::unparse() routine.
//
void ItemExpr::computeKwdAndPostfix( NAString &kwd,
NAString &postfix,
UnparseFormatEnum form ) const
{
// print function syntax of the form <kwd> <children> <postfix>
// <fname>(<prefix-args,> <children> <,postfix-args>)
// <--------kwd---------> <----postfix--->
// handle special prefix argument cases by adding to 'kwd' and 'postfix'
OperatorTypeEnum operatorType = getOperatorType();
switch (operatorType)
{
case ITM_CAST:
{
if (form == QUERY_FORMAT || form == COMPUTED_COLUMN_FORMAT)
{
kwd += "(";
postfix = " AS ";
// Get the Data type after the Cast
const NAType *naType = ((Cast *) this)->getType();
// ignore NULLs description in getTypeSQLName, since it
// does not return valid SQL syntax
postfix += naType->getTypeSQLname(TRUE);
if (!naType->supportsSQLnull())
postfix += " NOT NULL";
postfix += ")";
}
else
kwd += "(";
}
break;
case ITM_EXTRACT:
case ITM_EXTRACT_ODBC:
switch(((Extract*) this)->getExtractField())
{
case REC_DATE_YEAR:
kwd = "YEAR(";
break;
case REC_DATE_MONTH:
kwd = "MONTH(";
break;
case REC_DATE_DAY:
kwd = "DAY(";
break;
case REC_DATE_HOUR:
kwd = "HOUR(";
break;
case REC_DATE_MINUTE:
kwd = "MINUTE(";
break;
case REC_DATE_SECOND:
kwd = "SECOND(";
break;
case REC_DATE_YEARQUARTER_EXTRACT:
kwd = "DATE_PART('YEARQUARTER',";
break;
case REC_DATE_YEARMONTH_EXTRACT:
kwd = "DATE_PART('YEARMONTH',";
break;
case REC_DATE_YEARQUARTER_D_EXTRACT:
kwd = "DATE_PART('YEARQUARTERD',";
break;
case REC_DATE_YEARMONTH_D_EXTRACT:
kwd = "DATE_PART('YEARMONTHD',";
break;
// YEARWEEK gets transformed into ITM_YEARWEEK, see below
default:
kwd += "(";
break;
}
break;
case ITM_DATE_TRUNC_YEAR:
kwd = "DATE_TRUNC('YEAR',";
break;
case ITM_DATE_TRUNC_MONTH:
kwd = "DATE_TRUNC('MONTH',";
break;
case ITM_DATE_TRUNC_DAY:
kwd = "DATE_TRUNC('DAY',";
break;
case ITM_DATE_TRUNC_HOUR:
kwd = "DATE_TRUNC('HOUR',";
break;
case ITM_DATE_TRUNC_MINUTE:
kwd = "DATE_TRUNC('MINUTE',";
break;
case ITM_DATE_TRUNC_SECOND:
kwd = "DATE_TRUNC('SECOND',";
break;
case ITM_DATE_TRUNC_CENTURY:
kwd = "DATE_TRUNC('CENTURY',";
break;
case ITM_DATE_TRUNC_DECADE:
kwd = "DATE_TRUNC('DECADE',";
break;
case ITM_DATEDIFF_YEAR:
kwd = "DATEDIFF(YEAR,";
break;
case ITM_DATEDIFF_QUARTER:
kwd = "DATEDIFF(QUARTER,";
break;
case ITM_DATEDIFF_MONTH:
kwd = "DATEDIFF(MONTH,";
break;
case ITM_DATEDIFF_WEEK:
kwd = "DATEDIFF(WEEK,";
break;
case ITM_YEARWEEK:
kwd = "DATE_PART('YEARWEEK',";
break;
case ITM_YEARWEEKD:
kwd = "DATE_PART('YEARWEEKD',";
break;
case ITM_PLUS:
case ITM_MINUS:
// we come here for special datetime functions that
// get represented by a + or - operator with
// isStandardNormalization() set
if (((BiArith *) this)->isStandardNormalization())
{
// Here are the original expressions and their transformations,
// (+) and (-) indicate the +/- operators with the standard
// normalization flag set:
//
// ADD_MONTHS(<datetime_expr>, <num_expr> [, 0]) ==>
// <datetime_expr> (+) CAST(<num_expr> AS INTERVAL MONTHS)
// DATE_ADD(<datetime_expr>, <interval_expr>) ==>
// <datetime_expr> (+) <interval_expr>
// DATE_SUB(<datetime_expr>, <interval_expr>) ==>
// <datetime_expr> (-) <interval_expr>
// DATEADD(<keyword>, <num_expr>, <datetime_expr>) ==>
// <datetime_expr> (+) CAST(<num_expr> AS ...)
// TIMESTAMPADD(<keyword>, <num_expr>, <datetime_expr>) ==>
// <datetime_expr> (+) CAST(<num_expr> AS ...)
//
// We unparse all those as the equivalent DATE_ADD and DATE_SUB
// functions:
if (operatorType == ITM_PLUS)
kwd = "DATE_ADD(";
else
kwd = "DATE_SUB(";
}
else if (((BiArith *) this)->isKeepLastDay() && operatorType == ITM_PLUS)
{
// Here are the original expressions and their transformations,
// (+) and (-) indicate the +/- operators with the "keep last day"
// normalization flag set:
//
// ADD_MONTHS(<datetime_expr>, <num_expr>, 1) ==>
// <datetime_expr> (+) CAST(<num_expr> AS INTERVAL MONTHS)
CMPASSERT(child(1)->getValueId().getType().getTypeQualifier() == NA_INTERVAL_TYPE &&
child(1)->getValueId().getType().getFSDatatype() == REC_INT_MONTH);
kwd = "ADD_MONTHS(";
postfix = ", 1" + postfix;
}
else
DCMPASSERT(FALSE); // the above should have covered all datetime math expressions
break;
default:
kwd += "(";
break;
}
}
void ItemExpr::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// Avoid creating large strings which will only be truncated later
// by EXPLAIN.
if (form == EXPLAIN_FORMAT && (result.length() > 4096))
{
return;
}
// Allocate 3 procedure local variables and initialize them here.
// They may get changed by the call to ItemExpr::computeKwdAndFlags()
// which follows, but we initialize them here so source analysis
// tools won't complain about unitialized variables.
//
NAString kwd(CmpCommon::statementHeap());
NABoolean prefixFns = FALSE; // prefix function, some with infix, arity 2
NABoolean specialPrefixFns = FALSE; // require special prefix argument, arity 1 or 2
computeKwdAndFlags( kwd, prefixFns, specialPrefixFns, phase, form, tabId );
OperatorTypeEnum operatorType = getOperatorType();
Int32 arity = getArity();
if (operatorType != origOpType() &&
form == QUERY_FORMAT ||
form == COMPUTED_COLUMN_FORMAT)
{
// handle some cases where the original function was rewritten
// in the binder, using a ZZZBinderFunction
switch (origOpType())
{
case ITM_DATE_TRUNC_MINUTE:
case ITM_DATE_TRUNC_SECOND:
case ITM_DATE_TRUNC_MONTH:
case ITM_DATE_TRUNC_HOUR:
case ITM_DATE_TRUNC_CENTURY:
case ITM_DATE_TRUNC_DECADE:
case ITM_DATE_TRUNC_YEAR:
case ITM_DATE_TRUNC_DAY:
case ITM_DATEDIFF_YEAR:
case ITM_DATEDIFF_QUARTER:
case ITM_DATEDIFF_MONTH:
case ITM_DATEDIFF_WEEK:
case ITM_YEARWEEK:
case ITM_YEARWEEKD:
{
ItemExpr *unboundExpr =
ZZZBinderFunction::tryToUndoBindTransformation((ItemExpr *) this);
if (unboundExpr)
{
// we were able to undo this transformation, unparse
// the ZZZBinderFunction instead
unboundExpr->unparse(result,
phase,
form,
tabId);
return;
}
}
break;
default:
break;
}
}
switch (arity)
{
case 0:
// simply print the text out for a leaf operator
result += kwd;
break;
case 2:
if (form != FILE_FORMAT && !specialPrefixFns)
{
if( ((form == MV_SHOWDDL_FORMAT) ||
(form == QUERY_FORMAT) ||
(form == COMPUTED_COLUMN_FORMAT)) &&
prefixFns )
{
result += kwd;
result += "(";
if (child(0))
child(0)->unparse(result, phase,form, tabId);
else
result += "NULL";
if (operatorType == ITM_POSITION)
result += " IN ";
else
result += ", ";
if (child(1))
child(1)->unparse(result, phase, form, tabId);
else
result += "NULL";
result += ")";
break;
} // STDDEV, MOD, VARIANCE
else
if ( (operatorType == ITM_TRIM) &&
((form == QUERY_FORMAT) ||
(form == COMPUTED_COLUMN_FORMAT)))
{
result += kwd;
result += "(";
child(1)->unparse(result,phase,form,tabId);
result += ")";
}
else
if((operatorType == ITM_ITEM_LIST ||
operatorType == ITM_AND) &&
(form != MV_SHOWDDL_FORMAT) &&
(form != QUERY_FORMAT) )
{
if (child(0))
child(0)->unparse(result,phase,form, tabId);
else
result += "NULL";
if (operatorType != ITM_ITEM_LIST)
result += " ";
result += kwd;
result += " ";
if (child(1))
child(1)->unparse(result,phase,form, tabId);
else
result += "NULL";
}
else
{
// assume this is an infix operator (<child0>) op (<child1>)
result += "(";
NABoolean list0 = FALSE, list1 = FALSE;
if (operatorType != ITM_ITEM_LIST)
{
list0 = child(0)->getOperatorType() == ITM_ITEM_LIST;
list1 = child(1)->getOperatorType() == ITM_ITEM_LIST;
}
if (list0) result += "(";
child(0)->unparse(result,phase,form, tabId);
if (list0) result += ")";
result += " ";
result += kwd;
result += " ";
if (list1) result += "(";
child(1)->unparse(result,phase,form, tabId);
if (list1) result += ")";
result += ")";
} // infix operator
break; // for now, break here
}
// otherwise fall through to next case
default:
{
// usually, that's arity 1, but can be >1 as well
if (form == USER_FORMAT_DELUXE && isQuantifiedComp(this))
{
child(0)->unparse(result,phase,form, tabId);
result += " ";
result += kwd;
result += " (_subquery_)";
} // QuantifiedComp
else if (arity == 1 && isAPredicate() && operatorType != ITM_NOT)
{
child(0)->unparse(result,phase,form,tabId);
result += " ";
result += kwd;
} // predicate and != ITM_NOT
else if (form == MV_SHOWDDL_FORMAT && operatorType == ITM_RENAME_COL)
{
child(0)->unparse(result, phase, form, tabId);
}
else if (form == MV_SHOWDDL_FORMAT && operatorType == ITM_COUNT_NONULL)
{
result += "count(";
child(0)->unparse(result, phase, form, tabId);
result += ")";
}
else if (form == MV_SHOWDDL_FORMAT && operatorType == ITM_COUNT)
{
result += "count(*)";
}
else
{
NAString postfix = ")";
computeKwdAndPostfix( kwd, postfix, form );
// function name, open parenthesis, initial arguments
result += kwd;
// unparse all the children as arguments
for (Lng32 i = 0; i < (Lng32)arity; i++)
{
if (i > 0) result += ", ";
child(i)->unparse(result,phase,form,tabId);
}
// add any postfix and closing parenthesis
result += postfix;
} // else (print function syntax)
} // default of switch(arity)
} // end switch (arity)
} // ItemExpr::unparse
// MDAMR
DisjunctArray * ItemExpr::mdamTreeWalk()
{
DisjunctArray * rc = new STMTHEAP DisjunctArray(new STMTHEAP ValueIdSet(getValueId()));
return rc;
}
//MDAMR
NABoolean ItemExpr::equatesToAConstant() const
{
if (getOperatorType() != ITM_EQUAL)
return FALSE;
ItemExpr *lhs = child(0);
lhs = lhs->getLeafValueIfUseStats();
if(lhs->getOperatorType() != ITM_VEG_REFERENCE)
return FALSE;
ItemExpr *rhs = child(1);
rhs = rhs->getLeafValueIfUseStats();
if (rhs->getOperatorType() == ITM_CACHE_PARAM)
return TRUE;
if(!rhs->doesExprEvaluateToConstant(TRUE, TRUE))
return FALSE;
return TRUE;
}
// The check performed to determine if a column
// exists works only till binder phase. For use
// in post-Binder phase, the logic in the method
// will have to be changed.
NABoolean ItemExpr::containsColumn()
{
OperatorTypeEnum op = getOperatorType();
// This is a base column.
if (op == ITM_BASECOLUMN)
return TRUE;
// This is a leaf item other than a base column.
if (op >= ITM_CONSTANT && op <= ITM_VEG_REFERENCE)
return FALSE;
// Iterate through this item's inputs looking for a base column.
for (Int32 i = 0; i < getArity(); i++) {
ItemExpr *expr = child(i);
if (!expr)
return FALSE;
if (expr->containsColumn())
return TRUE;
}
return FALSE;
}
//++MV
// -----------------------------------------------------------------------
// Return the default selectivity for this predicate
// -----------------------------------------------------------------------
double ItemExpr::defaultSel()
{
if (selectivityFactor_ < 0)
return getOperatorType() == ITM_RETURN_FALSE ? 0.0 : 1.0;
else
return selectivityFactor_;
}
//--MV
ItemExpr * ItemExpr::containsRightmost(const ItemExpr *ie)
{
if (this == ie) return this;
if (!this || !ie) return NULL;
for (Int32 arity = getArity(); arity-- > 0; )
if (child(arity))
return (child(arity) == ie) ? this : child(arity)->containsRightmost(ie);
return NULL;
}
NABoolean ItemExpr::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL )
return FALSE;
CMPASSERT(other->getValueId() != NULL_VALUE_ID && getValueId() != NULL_VALUE_ID);
//defaults behavior is return false
return FALSE;
}
NABoolean ItemExpr::hasBaseEquivalence(ItemExpr * other)
{
if (other == NULL)
return FALSE;
CMPASSERT(other->getValueId() != NULL_VALUE_ID && this->getValueId()!= NULL_VALUE_ID);
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
if (getValueId()== other->getValueId() ||
(getOperatorType() == ITM_CONSTANT && ((ConstValue*)this)->isNull() && ((ConstValue*)other)->isNull()))
return TRUE;
else
if (getArity()==0)
return FALSE;
for (Int32 i= 0 ; i < getArity(); i++)
{
ItemExpr * chld= child(i);
if (!chld->hasBaseEquivalence(other->child(i)) || !chld->hasEquivalentProperties(other->child(i)) )
return FALSE;
}
return TRUE;
}//ItemExpr::hasBaseEquivalence(ValueId otherId)
ItemExpr * ItemExpr::changeDefaultOrderToDesc()
{
if (getOperatorType() == ITM_ITEM_LIST )
{
for(Int32 i=0; i<getArity(); i++)
{
child(i) = child(i)->changeDefaultOrderToDesc();
}
return this;
}
else
if (getOperatorType()== ITM_INVERSE)
{
if (child(0)->getOperatorType() == ITM_INVERSE)
return child(0)->child(0);
else
return this;
}
else
return new (CmpCommon::statementHeap()) InverseOrder(this);
}//ItemExpr::changeDefaultOrderToDesc()
ValueId ItemExpr::removeInverseFromExprTree( NABoolean & invExists)
{
if (getOperatorType() == ITM_ITEM_LIST )
{
for(Int32 i=0; i<getArity(); i++)
child(i) = child(i)->removeInverseFromExprTree(invExists);
return getValueId();
}
else
if (getOperatorType() == ITM_INVERSE)
invExists = TRUE;
return removeInverseOrder()->removeInverseOrder()->getValueId();
}//ItemExpr::removeInverseFromExprTree()
///
void ItemExpr::transformOlapFunctions(CollHeap *wHeap)
{
for (Int32 i= 0 ; i < getArity(); i++)
{
ItemExpr * mychild= child(i);
if (mychild->isOlapFunction())
{
ItmSeqOlapFunction * olap = (ItmSeqOlapFunction*) mychild;
ItemExpr * itmExpr = olap->transformOlapFunction(wHeap);
itmExpr->synthTypeAndValueId(TRUE);
child(i) = itmExpr;
}
//else //olap function can t have another olap as descendant ????
// {
// child(i)->transformOlapFunctions(wHeap);
//}
child(i)->transformOlapFunctions(wHeap);
}
}//void ItemExpr::transformOlapFunctions(CollHeap *wHeap)
// -----------------------------------------------------------------------
// removeInverseFromExprTree
//
// overloading above method for special purpose use
// onlyDouble parameter is used when the Inverse should not be removed
// unless two Inverse exprs are found in a row
// -----------------------------------------------------------------------
ItemExpr*
ItemExpr::removeInverseFromExprTree( NABoolean & invExists,
const NABoolean onlyDouble )
{
if (getOperatorType() == ITM_ITEM_LIST )
{
for(Int32 i=0; i<getArity(); i++)
child(i) = child(i)->removeInverseFromExprTree(invExists,
onlyDouble);
return this;
}
else
{
if (getOperatorType() == ITM_INVERSE)
invExists = TRUE;
// if the onlyDouble flag is set to TRUE, then we
// must find two Inverse exprs in a row to remove
if( (!onlyDouble) ||
getOperator().match(ITM_INVERSE) &&
child(0)->getOperator().match(ITM_INVERSE) )
{
return removeInverseOrder()->removeInverseOrder();
}
else
{
return this;
}
}
}//ItemExpr::removeInverseFromExprTree()
//
void ItemExpr::removeNotCoveredFromExprTree(ItemExpr * &ie ,
const ValueIdSet &seqColumns,
NABoolean rootNode)
{
if (rootNode)
{
while (ie->getOperatorType() == ITM_NOTCOVERED &&
seqColumns.contains(ie->getValueId()))
{
ie= ie->child(0);
}
}
for(Int32 i=0; i<ie->getArity(); i++)
{
while (ie->child(i)->getOperatorType() == ITM_NOTCOVERED &&
seqColumns.contains(ie->child(i)->getValueId()))
{
ie->child(i) = ie->child(i)->child(0);
}
ItemExpr * tmp = ie->child(i);
removeNotCoveredFromExprTree(tmp,seqColumns, FALSE);
}
}//ItemExpr::removeNotCoveredFromExprTree()
// -----------------------------------------------------------------------
// member functions for class ZZZBinderFunction
// -----------------------------------------------------------------------
ItemExpr *ZZZBinderFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap *outHeap)
{
ItemExpr *result=0;
if (derivedNode == NULL)
{
switch (getArity())
{
case 1:
result = new (outHeap) ZZZBinderFunction(getOperatorType(),
child(0));
break;
case 2:
result = new (outHeap) ZZZBinderFunction(getOperatorType(),
child(0), child(1));
break;
case 3:
result = new (outHeap) ZZZBinderFunction(getOperatorType(),
child(0), child(1), child(2));
break;
case 4:
result = new (outHeap) ZZZBinderFunction(getOperatorType(),
child(0), child(1), child(2), child(3));
break;
case 5:
result = new (outHeap) ZZZBinderFunction(getOperatorType(),
child(0), child(1), child(2), child(3), child(4));
break;
} // switch
}
else
{
result = derivedNode;
}
return BuiltinFunction::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class BiArith
// -----------------------------------------------------------------------
Int32 BiArith::getArity() const { return 2;}
const NAString BiArith::getText() const
{
switch (getOperatorType())
{
case ITM_PLUS:
return "+";
case ITM_MINUS:
return "-";
case ITM_TIMES:
return "*";
case ITM_DIVIDE:
return "/";
case ITM_EXPONENT:
return "**";
default:
return "unknown BiArith";
} // switch
} // BiArith::getText()
NABoolean BiArith::duplicateMatch(const ItemExpr& other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
const BiArith &o = (BiArith &) other;
if (unaryNegate_ != o.unaryNegate_)
return FALSE;
if (normalizeFlags_ != o.normalizeFlags_ )
return FALSE;
if (intervalQualifier_ OR o.intervalQualifier_)
{
if (intervalQualifier_ == NULL OR
o.intervalQualifier_ == NULL OR
NOT (*intervalQualifier_ == *(o.intervalQualifier_)))
return FALSE;
}
return TRUE;
}
ItemExpr * BiArith::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
BiArith *result;
BiArith *result2;
if (derivedNode == NULL)
result = new (outHeap) BiArith(getOperatorType());
else
result = (BiArith*)derivedNode;
result->setRoundingMode(getRoundingMode());
if ( ignoreSpecialRounding() ) result->setIgnoreSpecialRounding();
result->setDivToDownscale(getDivToDownscale());
result2 = (BiArith *)ItemExpr::copyTopNode(result, outHeap);
result2->normalizeFlags_ = normalizeFlags_ ;
return result2;
}
ItemExpr * BiArithSum::copyTopNode(ItemExpr *derivedNode, CollHeap *outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) BiArithSum(getOperatorType());
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
ConstValue * BiArith::castToConstValue (NABoolean & negate_it)
{
OperatorTypeEnum op = getOperatorType();
if ((op == ITM_MINUS) || (op == ITM_PLUS))
{
NABoolean neg0 = FALSE;
NABoolean neg1 = FALSE;
ConstValue * lhs = child(0)->castToConstValue(neg0);
ConstValue * rhs = child(1)->castToConstValue(neg1);
if (lhs && rhs)
{
Lng32 scale0 = 0;
if (lhs->canGetExactNumericValue() &&
lhs->getExactNumericValue(scale0) == 0)
{
if (op == ITM_MINUS)
negate_it = (!neg1);
else
negate_it = neg1;
return (rhs);
}
else
{
Lng32 scale1 = 0;
if (rhs->canGetExactNumericValue() &&
rhs->getExactNumericValue(scale1) == 0)
{
negate_it = neg0;
return (lhs);
}
}
}
}
return NULL;
}
/*--------------------------------------------------------------
* this routine is mainly called by complifyAndRemoveUncoveredSuffix(),
* sameOrder() and shortCutGroupBy::topMatch() to find out if a
* simplified expression still has the same order or if it has inverse
* order. Based on this we decide to do a inverse scan or a forward
* scan to satisfy order by or min/max aggregate.
*-----------------------------------------------------------------*/
ItemExpr * BiArith::simplifyOrderExpr(OrderComparison *newOrder)
{
ItemExpr *result = this;
OrderComparison myOrder = SAME_ORDER;
OrderComparison childOrder = SAME_ORDER;
// - the following expressions are simplified and
// has the same order as "a"
// a + const
// const + a
// a - const
// a * positive const
// positive const * a
// a / positive const
// - the following expressions have the inverse order of "a"
// and are siplified
// const - a
// a * negative const
// negative const * a
// a / negative const
// - the following expressions are not simplified because
// the order is not consistent
// positive const / a
// negative const / a
//
// example:
//
// values for a : -2 , -1, 1, 2, 3
//
// if we say that 1/a is inverse order of a
// then a order by of 1/a results in
//
// (ordering of a 1/a but the correct order
// if we simplify) would be
// 3 .33 -1 -1
// 2 .5 -.5 -2
// 1 1 .33 3
// -1 -1 .5 2
// -2 -.5 1 1
//
//Note: as long as 'a' is an unsigned integer we can simplify but
// it is to rare an occasion that a user does 1/a or -1/a so
// it is not supported.
if (child(0)->getOperatorType() == ITM_CONSTANT)
{
switch (getOperatorType())
{
case ITM_PLUS:
result = child(1)->simplifyOrderExpr(&childOrder);
myOrder = SAME_ORDER;
break;
case ITM_MINUS:
result = child(1)->simplifyOrderExpr(&childOrder);
myOrder = INVERSE_ORDER;
break;
case ITM_TIMES:
result = child(1)->simplifyOrderExpr(&childOrder);
break;
case ITM_DIVIDE:
break;
default:
break;
}
}
else if (child(1)->getOperatorType() == ITM_CONSTANT)
{
switch (getOperatorType())
{
case ITM_PLUS:
case ITM_MINUS:
result = child(0)->simplifyOrderExpr(&childOrder);
myOrder = SAME_ORDER;
break;
case ITM_TIMES:
result = child(0)->simplifyOrderExpr(&childOrder);
break;
case ITM_DIVIDE:
// part of fix to genesis case 10-070416-0218, soln 10-070416-4141
// mxcmp must consider ORDER BY "v/const" incompatible with GROUP BY "v"
// when "v/const" can lose precision relative to "v"
if (getRoundingMode() != 0 &&
CmpCommon::getDefault(COMP_BOOL_176) == DF_OFF)
{
// do NOT simplify. leave expr as is.
break;
}
else
{
result = child(0)->simplifyOrderExpr(&childOrder);
}
break;
default:
break;
}
}
else if (child(0)->getOperatorType() == ITM_MINUS AND
child(0)->child(0)->getOperatorType() == ITM_CONSTANT
AND child(0)->child(1)->getOperatorType() == ITM_CONSTANT
AND *(short *)((ConstValue *)((ItemExpr*)(child(0)->child(0))))->getConstValue()==0
)
{
ItemExpr * constExpr = child(0)->child(1);
switch(getOperatorType())
{
case ITM_TIMES:
result = child(1)->simplifyOrderExpr(&childOrder);
myOrder = INVERSE_ORDER;
break;
case ITM_DIVIDE:
break;
}
}
else if (child(1)->getOperatorType() == ITM_MINUS AND
child(1)->child(0)->getOperatorType() == ITM_CONSTANT
AND child(1)->child(1)->getOperatorType() == ITM_CONSTANT
AND *(short *)((ConstValue *)((ItemExpr*)(child(1)->child(0))))->getConstValue()==0
)
{
switch(getOperatorType())
{
case ITM_TIMES:
result = child(0)->simplifyOrderExpr(&childOrder);
myOrder = INVERSE_ORDER;
break;
case ITM_DIVIDE:
// part of fix to genesis case 10-070416-0218, soln 10-070416-4141
// mxcmp must consider ORDER BY "v/-const" incompatible
// with GROUP BY "v"
// when "v/-const" can lose precision relative to "v"
if (getRoundingMode() != 0 &&
CmpCommon::getDefault(COMP_BOOL_176) == DF_OFF)
{
// do NOT simplify. leave expr as is.
break;
}
else
{
result = child(0)->simplifyOrderExpr(&childOrder);
myOrder = INVERSE_ORDER;
}
break;
}
}
// return results
if (newOrder)
{
// if both this and the child inverse or leave the order the
// net result is the same order, otherwise we inverse the order once
if (myOrder == childOrder)
*newOrder = SAME_ORDER;
else
*newOrder = INVERSE_ORDER;
}
return result;
}
ItemExpr * BiArith::foldConstants(ComDiagsArea *diagsArea,
NABoolean newTypeSynthesis)
{
ItemExpr *result = this;
// attempt to do constant arithmetic if both operands are constants
// and if we are allowed to pick the result type
if (child(0)->getOperatorType() == ITM_CONSTANT AND
child(1)->getOperatorType() == ITM_CONSTANT AND
newTypeSynthesis)
{
// get the two operands into two Int64 variables if possible
Int64 ops[2] = {0,0};
Lng32 scales[2] = {0,0};
Int64 numResult = 0;
NABoolean canDoIt = TRUE; // give up once it becomes too difficult
scales[0] = 0; scales[1] = 0;
ops[0] = 0; ops[1] = 0;
for (Int32 i = 0; i < 2 AND canDoIt; i++)
{
NABoolean negate;
ConstValue *cv = child(i)->castToConstValue(negate);
if (cv AND cv->canGetExactNumericValue())
{
CMPASSERT(NOT negate);
ops[i] = cv->getExactNumericValue(scales[i]);
}
else
{
// can't do it yet if this isn't represented as a signed
// or unsigned binary number without any additional fluff
canDoIt = FALSE;
}
}
// -----------------------------------------------------------------
// At this point, ops contains two 64 bit numbers that represent the
// exact numeric operands. scales contains the scales of the operands.
// Now do the arithmetic, but try not to cause overflow traps.
// -----------------------------------------------------------------
// for now, give up on any values with a scale != 0
if (scales[0] != 0 OR scales[1] != 0)
canDoIt = FALSE;
if (canDoIt)
{
// adjust scales, if necessary
switch (getOperatorType())
{
case ITM_PLUS:
case ITM_MINUS:
if (scales[0] != scales[1])
// sorry, keep it simple for now
canDoIt = FALSE;
break;
default:
break;
}
}
NABoolean overflow = FALSE;
NABoolean zeroDivide = FALSE;
// check for overflow
// for now, just do it for reasonably small constants
if (ops[0] < -10000000 OR ops[0] > 10000000 OR
ops[1] < -10000000 OR ops[1] > 10000000)
canDoIt = FALSE;
// -----------------------------------------------------------------
// ok, now it's time to do the real thing
// -----------------------------------------------------------------
if (canDoIt AND NOT overflow)
{
switch (getOperatorType())
{
case ITM_PLUS:
numResult = ops[0] + ops[1];
break;
case ITM_MINUS:
numResult = ops[0] - ops[1];
break;
case ITM_TIMES:
numResult = ops[0] * ops[1];
break;
case ITM_DIVIDE:
if (ops[1] == 0)
zeroDivide = TRUE;
else
numResult = ops[0] / ops[1];
break;
}
}
// error handling
if (canDoIt AND (overflow OR zeroDivide))
{
NAString up(CmpCommon::statementHeap());
unparse(up);
*diagsArea << DgSqlCode(zeroDivide ? -4075 : -4076) << DgString0(up);
canDoIt = FALSE;
}
if (canDoIt)
{
// now we're ready to do the switch
if (numResult >= INT_MIN AND numResult <= INT_MAX)
{
// result can fit into a long, use the ConstValue(long)
// constructor
Lng32 num;
num = (Lng32) numResult;
result = new(CmpCommon::statementHeap()) SystemLiteral(num);
}
else
{
// the result becomes an 8 byte integer, use the ConstValue
// constructor with a type, the binary image, and a text string
char numstr[TEXT_DISPLAY_LENGTH];
convertInt64ToAscii(numResult,numstr);
NAString runningOutOfNames(numstr, CmpCommon::statementHeap());
result = new(CmpCommon::statementHeap()) SystemLiteral(
new(CmpCommon::statementHeap()) SQLLargeInt(CmpCommon::statementHeap(), TRUE,FALSE),
(void *) &numResult,
sizeof(numResult),
&runningOutOfNames);
}
}
}
// perform the generic tasks (recursion, type synthesis) on the
// result, regardless of whether we transformed it or not
return result->ItemExpr::foldConstants(diagsArea,newTypeSynthesis);
}
NABoolean BiArith::isEquivalentForCodeGeneration(const ItemExpr * other)
{
NABoolean rc = FALSE; // assume failure
if (hasBaseEquivalenceForCodeGeneration(other))
{
// we know that other is a BiArith, its operator type is the same,
// and that the children are equivalent
BiArith * otherBiArith = (BiArith *)other;
if ( (unaryNegate_ == otherBiArith->unaryNegate_) &&
(normalizeFlags_ == otherBiArith->normalizeFlags_ ) )
{
// make sure both have the same interval qualifier (if any)
NABoolean sameIntervalQualifier = FALSE; // assume failure
if (intervalQualifier_)
{
if (otherBiArith->intervalQualifier_)
{
const NAType & otherIntervalQualifier = *(otherBiArith->intervalQualifier_);
if (intervalQualifier_->operator==(otherIntervalQualifier))
sameIntervalQualifier = TRUE;
}
}
else if (otherBiArith->intervalQualifier_ == 0)
sameIntervalQualifier = TRUE;
if (sameIntervalQualifier)
{
// the next test distinguishes between BiArith and its special-case
// inheriting classes, BiArithSum and BiArithCount
if (getText() == otherBiArith->getText()) // same class?
rc = TRUE; // the two values can be produced by the same generated expr
}
}
}
return rc;
}
QR::ExprElement BiArith::getQRExprElem() const
{
return QR::QRBinaryOperElem;
}
NABoolean BiArith::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
BiArith * otherBiArith = (BiArith *) other;
return
(this->unaryNegate_ == otherBiArith->unaryNegate_) &&
(this->normalizeFlags_ == otherBiArith->normalizeFlags_) &&
(this->intervalQualifier_ == otherBiArith->intervalQualifier_) &&
(this->divToDownscale_ == otherBiArith->divToDownscale_);
}
ItemExpr * UnArith::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
UnArith *result;
if (derivedNode == NULL)
result = new (outHeap) UnArith();
else
result = (UnArith*)derivedNode;
return result;
}
// -----------------------------------------------------------------------
// member functions for class ColReference
// -----------------------------------------------------------------------
Int32 ColReference::getArity() const { return 0; }
const NAString ColReference::getText() const
{
NAColumn *nacol = getValueId() != NULL_VALUE_ID ?
getValueId().getNAColumn() :
NULL;
return nacol ?
nacol->getFullColRefNameAsAnsiString() :
colRefName_->getColRefAsAnsiString();
}
HashValue ColReference::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= colRefName_->getColRefAsString();
return result;
}
NABoolean ColReference::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
ColReference &o = (ColReference &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (NOT (*colRefName_ == *(o.colRefName_)))
return FALSE;
return TRUE;
}
ItemExpr * ColReference::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
ColRefName *colName;
if (colRefName_->isStar())
{
colName = new (outHeap) ColRefName(1);
}
else
{
colName = new (outHeap)
ColRefName(colRefName_->getColName(),
colRefName_->getCorrNameObj(),
outHeap);
}
ColReference *newColRef = new (outHeap) ColReference(colName);
newColRef->setTargetColumnClass(getTargetColumnClass());
result = newColRef;
}
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class HostVar
// -----------------------------------------------------------------------
HostVar::HostVar(const HostVar &hv)
: ItemExpr(hv.getOperatorType()),
varName_(hv.varName_, CmpCommon::statementHeap()),
indicatorName_(hv.indicatorName_, CmpCommon::statementHeap()),
prototypeValue_(hv.prototypeValue_, CmpCommon::statementHeap()),
prototypeType_(hv.prototypeType_),
isEnvVar_(hv.isEnvVar_), isDefine_(hv.isDefine_),
isSystemGenerated_(hv.isSystemGenerated_),
paramMode_ (hv.paramMode_),
ordinalPosition_ (hv.ordinalPosition_),
hvIndex_ (hv.hvIndex_),
rowsetInfo_(hv.rowsetInfo_),
heading_(hv.heading_, CmpCommon::statementHeap()),
tablename_(hv.tablename_,CmpCommon::statementHeap())
{
if (hv.varType_ == NULL)
varType_ = NULL;
else
varType_ = hv.varType_->newCopy(CmpCommon::statementHeap());
}
Int32 HostVar::getArity() const { return 0; }
NABoolean HostVar::isAUserSuppliedInput() const { return TRUE; }
const NAString HostVar::getText() const
{
NAString punc(CmpCommon::statementHeap());
if (isSystemGenerated_) punc = "\\:"; // backslash, colon
if (!indicatorName_.isNull())
return punc + varName_ + " " + punc + indicatorName_;
else
return punc + varName_;
}
HashValue HostVar::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= (CollIndex) getValueId();
return result;
}
NABoolean HostVar::duplicateMatch(const ItemExpr & other) const
{
// rely on value id comparison since the binder should not
// assign different value ids for the same hostvar
return genericDuplicateMatch(other);
}
ItemExpr * HostVar::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HostVar(*this);
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
NABoolean HostVar::isCharTypeMatchRulesRelaxable()
{
if ( getType()->getTypeQualifier() == NA_CHARACTER_TYPE &&
((CharType*)getType())->getCharSet() == CharInfo::UNICODE
)
return TRUE;
else
return FALSE;
}
ItemExpr * DefaultSpecification::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) DefaultSpecification();
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
NABoolean RowsetArrayScan::isCharTypeMatchRulesRelaxable()
{
if ( getOperatorType() == ITM_ROWSETARRAY_SCAN &&
getType()->getTypeQualifier() == NA_CHARACTER_TYPE &&
((CharType*)getType())->getCharSet() == CharInfo::UNICODE
)
return TRUE;
else
return FALSE;
}
// -----------------------------------------------------------------------
// member functions for class ConstantParameter
// -----------------------------------------------------------------------
ConstantParameter::ConstantParameter(const ConstValue& v, NAMemory *h,
NABoolean quantizeLen, UInt32 p)
: val_((ConstValue*)&v), type_(CONST_CAST(NAType*, v.getType()))
, Parameter(ITM_CACHE_PARAM)
{
if (type_->getTypeQualifier() != NA_CHARACTER_TYPE) {
type_ = type_->newCopy(h);
}
else {
// convert char type to equivalent varchar type
type_ = ((CharType*)type_)->equivalentVarCharType(h, quantizeLen);
}
posns_ = new(STMTHEAP) ClusteredBitmap(STMTHEAP);
*posns_ += p;
}
ConstantParameter::ConstantParameter(ConstValue* v, NAMemory *h,
const NAType* typ, UInt32 p)
: val_(v), type_(typ->newCopy(h)), Parameter(ITM_CACHE_PARAM)
{
// non-null ConstantParameters are non-nullable
type_->setNullable(v->isNull());
if (v->getType()->getTypeQualifier() == NA_CHARACTER_TYPE &&
type_->getTypeQualifier() == NA_CHARACTER_TYPE) {
// this is part of a fix to genesis case: 10-010618-3484.
// ConstantParameter's type must take v's (not typ's) upshift.
CharType *vt = (CharType*)v->getType();
CharType *t = (CharType*)type_;
t->setUpshifted(vt->isUpshifted());
}
posns_ = new(STMTHEAP) ClusteredBitmap(STMTHEAP);
*posns_ += p;
}
ConstantParameter::ConstantParameter(const ConstantParameter& cp, NAHeap *h)
: val_(new(h) ConstValue(*cp.val_,h)), type_(cp.type_->newCopy(h))
, posns_(new(h) ClusteredBitmap(*cp.posns_,h))
, Parameter(cp)
{
}
ConstantParameter::~ConstantParameter()
{
// we don't own type_; the statementHeap does.
// so, we leave it up to statementHeap to free it.
}
ItemExpr *ConstantParameter::copyTopNode
(ItemExpr *derivedNode, CollHeap* outHeap)
{
CMPASSERT(derivedNode == NULL);
ItemExpr *result = new (outHeap) ConstantParameter(*this, (NAHeap*)outHeap);
return result;
}
NABoolean ConstantParameter::duplicateMatch(const ItemExpr& other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
CMPASSERT(val_ != NULL);
const ConstantParameter &o = (ConstantParameter &) other;
return val_->duplicateMatch(*(o.val_));
// no comparison of type_ since it should be the same if the values are
}
const NAString ConstantParameter::getText() const
{
if (val_)
return NAString("%(", CmpCommon::statementHeap()) + val_->getText() + ")";
else
return NAString("%", CmpCommon::statementHeap());
}
const NAType* ConstantParameter::synthesizeType()
{
return getType();
}
HashValue ConstantParameter::topHash()
{
return val_ ? val_->topHash() : ItemExpr::topHash();
}
// return true if val matches this ConstantParameter
NABoolean ConstantParameter::matches(ConstValue *val) const
{
if (!val_ || !val)
return FALSE;
else
return *val_ == *val;
}
// -----------------------------------------------------------------------
// member functions for class DynamicParam
// -----------------------------------------------------------------------
const NAString DynamicParam::getText() const
{
if (!indicatorName_.isNull())
return NAString("?") + paramName_ +
NAString(" ?") + indicatorName_ ;
else
return NAString("?") + paramName_ ;
}
HashValue DynamicParam::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= (CollIndex) getValueId();
return result;
}
NABoolean DynamicParam::duplicateMatch(const ItemExpr & other) const
{
// rely on value id comparison since the binder should not
// assign different value ids for the same parameter
return genericDuplicateMatch(other);
}
ItemExpr * Parameter::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
ABORT("copyTopNode() can only be called for a derived class of Parameter");
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
ItemExpr * DynamicParam::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
result = new (outHeap) DynamicParam(paramName_, indicatorName_, outHeap);
((DynamicParam *) result)->setRowsetSize(rowsetSize_);
((DynamicParam *) result)->setRowsetInfo(rowsetInfo_);
((DynamicParam *) result)->setParamHeading(heading_);
((DynamicParam *) result)->setParamTablename(tablename_);
// we remember our original dynamic parameter because we
// must use their valueid at dynamicparam::codegen time
((DynamicParam *) result)->setOriginal(this);
}
else
result = derivedNode;
return Parameter::copyTopNode(result, outHeap);
}
const NAType * DynamicParam::pushDownType(NAType& desiredType,
enum NABuiltInTypeEnum defaultQualifier)
{
const NAType * currentType = &getValueId().getType();
if ( desiredType.getTypeQualifier() == NA_CHARACTER_TYPE &&
currentType-> getTypeQualifier() == NA_CHARACTER_TYPE
)
{
CharType &desiredCT = (CharType&)desiredType;
enum CharInfo::CharSet desiredCS = desiredCT.getCharSet();
CharType* ct = (CharType*)currentType;
if ( ct ->getCharSet() != desiredCS )
{
ct->setCharSet(desiredCS);
ct->setCollation(desiredCT.getCollation());
ct->setCoercibility(desiredCT.getCoercibility());
ct->setCaseinsensitive(desiredCT.isCaseinsensitive());
}
return currentType;
}
return &desiredType;
}
// -----------------------------------------------------------------------
// member functions for class RoutineParam
// -----------------------------------------------------------------------
const NAString RoutineParam::getText() const
{
return NAString("?") + paramName_ ;
}
HashValue RoutineParam::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= paramName_;
result ^= optionalParam_;
result ^= paramMode_;
result ^= ordinalPosition_;
result ^= rdesc_;
result ^= (CollIndex) getValueId();
return result;
}
NABoolean RoutineParam::duplicateMatch(const ItemExpr & other) const
{
// rely on value id comparison since the binder should not
// assign different value ids for the same parameter
return genericDuplicateMatch(other);
}
ItemExpr * RoutineParam::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
RoutineParam *result;
if (derivedNode == NULL) {
// Does not copy the rdesc_
result = new (outHeap) RoutineParam(paramName_, paramType_,
ordinalPosition_,paramMode_, NULL, outHeap);
}
else
result = (RoutineParam *) derivedNode;
if (derivedNode != NULL)
{
result->paramName_ = paramName_;
if (paramType_ != NULL)
result->paramType_ = paramType_->newCopy (outHeap);
result->paramMode_ = paramMode_;
result->ordinalPosition_ = ordinalPosition_;
result->rdesc_ = NULL; // Do not want to copy the RoutineDesc
// nor does it make any sense to just
// point to the one from the clone since
// multiple routineParams typically will
// point to the same routineDesc. If we
// just make a deep copy here we will end
// up with N new copies of the same info..
//
}
result->optionalParam_ = optionalParam_;
result->isCacheable_ = isCacheable_;
memcpy(result->argumentType_, argumentType_, sizeof(argumentType_));
return Parameter::copyTopNode((ItemExpr *)result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class BaseColumn
// -----------------------------------------------------------------------
BaseColumn::~BaseColumn()
{}
// copy constructor
BaseColumn::BaseColumn(const BaseColumn &column) :
ItemExpr(column),
tableDesc_(column.tableDesc_),
colNumber_(column.colNumber_),
equivalentIndexCols_(column.equivalentIndexCols_),
computedColumnExpr_(column.computedColumnExpr_)
{}
NAColumn *BaseColumn::getNAColumn() const
{
return getTableDesc()->getNATable()->getNAColumnArray()[colNumber_];
}
const NAString& BaseColumn::getColName() const
{
return getNAColumn()->getColName();
}
const NAType& BaseColumn::getType() const
{
return *getNAColumn()->getType();
}
Int32 BaseColumn::getArity() const { return 0; }
const NAString BaseColumn::getText() const
{
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_BASECOL_DISPLAY") || getenv("SIMPLE_DISPLAY"))
{
return
NAString(getTableDesc()->getNATable()->getTableName().getObjectName()) +
NAString(".") +
NAString(getTableDesc()->getNATable()->getNAColumnArray()[colNumber_]->
getColName());
}
#endif
if (CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) == DF_DUMP_MV)
{
// If a correlation name exists, use it.
// Otherwise, just use the base column name.
const CorrName& corr = getTableDesc()->getCorrNameObj();
NAString result;
if (corr.getCorrNameAsString() != "")
{
result += corr.getCorrNameAsString();
result += ".";
}
result += getTableDesc()->getNATable()->getNAColumnArray()[colNumber_]->getColName();
return result;
}
// return the table in the format "table.col"
ColRefName name(
getTableDesc()->getNATable()->getNAColumnArray()[colNumber_]->getColName(),
getTableDesc()->getCorrNameObj(), CmpCommon::statementHeap());
return name.getColRefAsAnsiString(FALSE, TRUE);
}
const NAString BaseColumn::getTextForQuery() const {
// return the table in the format "table.col" where
// table is the physical table name and not the corr
// table name as in getText() version
return
NAString(getTableDesc()->getNATable()->getTableName().getCatalogName()) +
NAString(".") +
NAString(getTableDesc()->getNATable()->getTableName().getSchemaName()) +
NAString(".") +
NAString(getTableDesc()->getNATable()->getTableName().getObjectName()) +
NAString(".") +
NAString(getTableDesc()->getNATable()->getNAColumnArray()[colNumber_]->
getColName());
}
// return the set of KeyColumns referenced in the Computed Column Expression
void BaseColumn::getUnderlyingColumnsForCC(ValueIdSet &underlyingCols)
{
if (getNAColumn()->isComputedColumn())
{
ValueIdSet keyCols = getTableDesc()->getClusteringIndex()->getIndexKey();
keyCols = keyCols.convertToBaseIds();
ValueIdSet compExprSet = getComputedColumnExpr();
underlyingCols.accumulateReferencedValues(keyCols, compExprSet);
}
}
HashValue BaseColumn::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= (void *) getTableDesc();
result ^= colNumber_;
return result;
}
NABoolean BaseColumn::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
BaseColumn &o = (BaseColumn &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (getTableDesc() != o.getTableDesc() OR
colNumber_ != o.colNumber_)
return FALSE;
return TRUE;
}
ItemExpr * BaseColumn::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
ItemExpr *oldBC;
if (derivedNode == NULL)
oldBC = new (outHeap) BaseColumn(*this);
else
oldBC = derivedNode;
result = ItemExpr::copyTopNode(oldBC, outHeap);
if (derivedNode != NULL)
((BaseColumn *)result)->computedColumnExpr_ =
((BaseColumn *)derivedNode)->computedColumnExpr_;
return result;
}
NABoolean BaseColumn::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& /*coveredSubExpr*/,
ValueIdSet& /*unCoveredExpr*/) const
{
// ---------------------------------------------------------------------
// A base column is also covered if any equivalent index column is
// available in the group attributes or in the new inputs. Note that
// the inverse of this is not true: the ValueId of an IndexColumn item
// expression is NOT covered by group attributes that contain the value
// id of the corresponding BaseColumn.
// ---------------------------------------------------------------------
ValueId x = equivalentIndexCols_.init();
for (;
equivalentIndexCols_.next(x);
equivalentIndexCols_.advance(x))
{
if (newRelExprAnchorGA.isCharacteristicOutput(x) OR
newRelExprAnchorGA.isCharacteristicInput(x))
return TRUE;
if (newExternalInputs.contains(x))
{
referencedInputs += x;
return TRUE;
}
}
return FALSE;
}
OrderComparison BaseColumn::sameOrder(ItemExpr *other,
NABoolean askOther)
{
OrderComparison result =
ItemExpr::sameOrder(other,askOther);
if (result != DIFFERENT_ORDER)
return result;
// try all equivalent index columns, but don't call sameOrder recursively
// for them, rather do a simple look-up
// 10-031114-1317: replaced a for loop that traverses the ValueIdSet
// with the contains method that is more efficient
if (equivalentIndexCols_.contains(other->getValueId()))
{
result = SAME_ORDER;
}
return result;
}
ValueIdList *BaseColumn::getClusteringKeyCols() const
{
return (ValueIdList*)
&(tableDesc_->getClusteringIndex()->getClusteringKeyCols());
}
QR::ExprElement BaseColumn::getQRExprElem() const
{
return QR::QRColumnElem;
}
// -----------------------------------------------------------------------
// member functions for class IndexColumn
// -----------------------------------------------------------------------
IndexColumn::IndexColumn(const NAFileSet *indexPtr,
Lng32 indexColumnNumber,
const ValueId &colDefinition)
: ItemExpr(ITM_INDEXCOLUMN),index_(indexPtr),
indexColNumber_(indexColumnNumber),indexColDefinition_(colDefinition)
{}
IndexColumn::~IndexColumn()
{}
NAColumn * IndexColumn::getNAColumn() const
{
return index_->getAllColumns()[indexColNumber_];
}
const NAType& IndexColumn::getType() const
{
return *getNAColumn()->getType();
}
Int32 IndexColumn::getArity() const { return 0; }
const NAString IndexColumn::getText() const
{
// As an indexcol is not an Ansi construct, we need not make an effort to
// unparse it following Ansi rules; this quick-and-dirty method is fine
// (should only be seen in gui-display tool):
return ColRefName::getColRefAsString(
getNAColumn()->getColName(),
index_->getFileSetName().getQualifiedNameAsString());
}
void IndexColumn::unparse(NAString &result,
PhaseEnum /*phase*/,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// don't print the NSK name in Explain
if (form == EXPLAIN_FORMAT)
{
// result += "indexcol(";
/* if (index_->isVolatile())
{
// only output the object part of the external name
// and exclude cat/sch names as these are internal IDs.
ComObjectName con(index_->getExtFileSetName());
result += ColRefName::getColRefAsString(
getNAColumn()->getColName(),
con.getObjectNamePartAsAnsiString());
}
else
{
*/
result += ColRefName::getColRefAsString(
getNAColumn()->getColName(),
index_->getExtFileSetName());
// result += ")";
}
else
{
// in all other cases do print the NSK name?
result += getText();
}
}
HashValue IndexColumn::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any data members of the derived class
// (the definition is functionally dependent on the index and
// column number, so don't hash it)
result ^= (void *) index_;
result ^= indexColNumber_;
return result;
}
NABoolean IndexColumn::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
IndexColumn &o = (IndexColumn &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (index_ != o.index_ OR
indexColNumber_ != o.indexColNumber_ OR
NOT (indexColDefinition_ == o.indexColDefinition_))
return FALSE;
return TRUE;
}
ItemExpr * IndexColumn::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap)
IndexColumn(index_,indexColNumber_,indexColDefinition_);
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
OrderComparison IndexColumn::sameOrder(ItemExpr *other,
NABoolean askOther)
{
OrderComparison result =
ItemExpr::sameOrder(other,askOther);
if (result != DIFFERENT_ORDER)
return result;
// try it with the definition of the index column
return indexColDefinition_.getItemExpr()->sameOrder(other,TRUE);
}
ValueId IndexColumn::mapAndRewrite(ValueIdMap &map,
NABoolean mapDownwards)
{
// try the standard method first
ValueId result = ItemExpr::mapAndRewrite(map,mapDownwards);
// we're done if the map contained a mapping for this column
if (result != getValueId())
return result;
// try to replace the index column with its base column and then map
result = indexColDefinition_.getItemExpr()->mapAndRewrite(map,mapDownwards);
// return the result if it could be mapped
// (no need to cache the index col. mapping since no new expr is created)
if (result != indexColDefinition_)
return result;
// if all failed, just return the value id unchanged
return getValueId();
}
Lng32 IndexColumn::getOffset() const
{
return index_->getIndexKeyColumns().getOffset((short) indexColNumber_);
}
// -----------------------------------------------------------------------
// member functions for class SelIndex
// -----------------------------------------------------------------------
ItemExpr * SelIndex::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
CMPASSERT(derivedNode == NULL);
ItemExpr *result = new (outHeap)
SelIndex(getSelIndex(), getExprInGrbyClause());
((SelIndex *)result)->setRenamedColNameInGrbyClause
(renamedColNameInGrbyClause());
return ItemExpr::copyTopNode(result, outHeap);
}
Int32 SelIndex::getArity() const { return 0; }
const NAString SelIndex::getText() const
{
char cp[TEXT_DISPLAY_LENGTH];
sprintf(cp, "%d", getSelIndex());
return cp;
}
// -----------------------------------------------------------------------
// member functions for class ValueIdRef
// -----------------------------------------------------------------------
Int32 ValueIdRef::getArity() const { return 0; }
NABoolean ValueIdRef::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& /*unCoveredExpr*/) const
{
// ---------------------------------------------------------------------
// The ValueIdRef isCovered if the expression that it derives from
// is covered.
// ---------------------------------------------------------------------
return coveringGA.covers(isDerivedFrom(), newExternalInputs,
referencedInputs, &coveredSubExpr);
} // ValueIdRef::isCovered()
const NAString ValueIdRef::getText() const
{
char cp[TEXT_DISPLAY_LENGTH];
sprintf(cp,"ValueId (%d)",CollIndex(isDerivedFrom()));
return cp;
} // ValueIdRef::getText()
HashValue ValueIdRef::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= derivedFrom_;
return result;
}
NABoolean ValueIdRef::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
ValueIdRef &o = (ValueIdRef &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (NOT (derivedFrom_ == o.derivedFrom_))
return FALSE;
return TRUE;
}
ItemExpr * ValueIdRef::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ValueIdRef(derivedFrom_);
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
OrderComparison ValueIdRef::sameOrder(ItemExpr *other,
NABoolean askOther)
{
OrderComparison result =
ItemExpr::sameOrder(other,askOther);
if (result != DIFFERENT_ORDER)
return result;
// check with the original expression
return isDerivedFrom().getItemExpr()->sameOrder(other,TRUE);
}
// -----------------------------------------------------------------------
// member functions for class ValueIdProxy
// -----------------------------------------------------------------------
NABoolean ValueIdProxy::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// The ValueIdProxy isCovered if the expression that it derives from
// is covered.
// ---------------------------------------------------------------------
return coveringGA.covers(isDerivedFrom(), newExternalInputs,
referencedInputs, &coveredSubExpr, &unCoveredExpr);
} // ValueIdProxy::isCovered()
const NAString ValueIdProxy::getText() const
{
char cp[TEXT_DISPLAY_LENGTH];
sprintf(cp,"ValueIdProxy (%d:%d->%d)",CollIndex(isDerivedFrom()),
CollIndex(getOutputNum()),
CollIndex(getOutputId()));
return cp;
} // ValueIdProxy::getText()
HashValue ValueIdProxy::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= derivedFrom_;
result ^= outputOrdinalNumber_;
result ^= outputValueId_;
return result;
}
NABoolean ValueIdProxy::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
ValueIdProxy &o = (ValueIdProxy &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if ((NOT (derivedFrom_ == o.derivedFrom_)) AND
(NOT (outputValueId_ == o.outputValueId_)) AND
(NOT (outputOrdinalNumber_ == o.outputOrdinalNumber_)))
return FALSE;
return TRUE;
}
ItemExpr * ValueIdProxy::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ValueIdProxy(derivedFrom_,
outputValueId_,
outputOrdinalNumber_);
else
{
result = derivedNode;
}
ValueIdProxy * tmpNode = (ValueIdProxy *) result;
tmpNode->derivedFrom_ = derivedFrom_;
tmpNode->outputValueId_ = outputValueId_;
tmpNode->outputOrdinalNumber_ = outputOrdinalNumber_;
tmpNode->transformDerivedFromValueId_ = transformDerivedFromValueId_;
return ItemExpr::copyTopNode(result, outHeap);
}
ItemExpr *ValueIdProxy::containsUDF()
{
// The transfromChild_ means that this ValueIdProxy represents the
// the subquery or UDF, thus return true..
if ((transformDerivedFromValueId_ == TRUE) &&
derivedFrom_.getItemExpr()->containsUDF())
return this;
else
return 0;
} // ValueIdProxy::containsUDF
NABoolean ValueIdProxy::containsIsolatedUDFunction()
{
// The transfromChild_ means that this ValueIdProxy represents the
// the subquery or UDF, thus return true..
if ((transformDerivedFromValueId_ == TRUE) &&
derivedFrom_.getItemExpr()->containsUDF())
{
UDFunction *udf = (UDFunction *) derivedFrom_.getItemExpr()->containsUDF();
const RoutineDesc *rdesc = udf->getRoutineDesc();
if (rdesc == NULL || rdesc->getEffectiveNARoutine() == NULL ) return FALSE;
return ( rdesc->getEffectiveNARoutine()->isIsolate() ? TRUE : FALSE ) ;
}
else
return FALSE;
} // ValueIdProxy::containsIsolatedUDFunction
NABoolean ValueIdProxy::containsSubquery() // virtual method
{
// The transfromChild_ means that this ValueIdProxy represents the
// the subquery or UDF, thus return true..
if ((transformDerivedFromValueId_ == TRUE) &&
derivedFrom_.getItemExpr()->containsSubquery())
return TRUE;
else
return FALSE;
} // ValueIdProxy::containsSubquery
NABoolean ValueIdProxy::containsValueIdProxySibling(const ValueIdSet &siblings)
// virtual method
{
// This method is used to make sure we extract all valueIds associated with
// a subquery or UDF from a predicate or list.
for (ValueId s = siblings.init(); siblings.next(s); siblings.advance(s))
{
if (s.getItemExpr()->getOperatorType() == ITM_VALUEID_PROXY)
{
ValueIdProxy *proxy = (ValueIdProxy *) (s.getItemExpr());
if ( derivedFrom_ == proxy->isDerivedFrom() ) return TRUE;
}
}
return FALSE;
} // ValueIdProxy::containsValueIdProxySibling
// -----------------------------------------------------------------------
// member functions for class ValueIdUnion
// -----------------------------------------------------------------------
void ValueIdUnion::setSource(Lng32 index, ValueId v)
{
if((Lng32)sources_.entries() <= index) {
sources_.insertAt(index, v);
} else {
sources_[index] = v;
}
}
Int32 ValueIdUnion::getArity() const { return 0; }
NABoolean ValueIdUnion::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& /*unCoveredExpr*/ ) const
{
// ---------------------------------------------------------------------
// Check which of all the operands of the ValueIdUnion isCovered().
// Return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
// $$$ Question: why not return all of the operands of the ValueIdUnion which
// $$$ are covered -- why do we break after we find the first, in the loop below?
ValueIdSet localSubExpr;
for(CollIndex i = 0; i < entries(); i++)
{
localSubExpr.clear();
if (coveringGA.covers(getSource(i), newExternalInputs,
referencedInputs, &localSubExpr) )
{
coveredSubExpr += getSource(i);
break;
}
}
// ---------------------------------------------------------------------
// An aggregate function is coerced to fail the coverage test even when
// its operand isCovered(). This is because the computation of the
// aggregate function requires "raw" values produced by its operand to
// be grouped. The aggregated values cannot be treated in the same
// manner as the constitutent raw values.
// ---------------------------------------------------------------------
// Is something similar to the above, copied from
// Aggregate::isCovered(), also true for ValueIdUnion? If this is so
// (which sort of makes sense to me), then that explains why this
// function is hardcoded to always return FALSE.
// ---------------------------------------------------------------------
return FALSE; // the ValueIdUnion is not covered by its child's coveringGA
} // ValueIdUnion::isCovered()
void ValueIdUnion::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// for now, not sure if this method is really necessary -- default to
// the base class method:
ItemExpr::getLeafValuesForCoverTest (leafValues, coveringGA, newExternalInputs) ;
// leafValues += getValueId(); // this MIGHT be the right definition of this
// // class -- hard to say at this point ...
} // ValueIdUnion::getLeafValuesForCoverTest()
const NAString ValueIdUnion::getText(UnparseFormatEnum form) const
{
NAString result(CmpCommon::statementHeap());
const char *delim;
if (form == USER_FORMAT_DELUXE)
delim = " UNION "; // keyword format
else
{
delim = ", "; // standard list format
result = "ValueIdUnion(";
}
for (CollIndex i = 0; i < entries(); i++)
{
if (i > 0)
result += delim;
// guard against loops in the references
// (can happen with common subexpressions, for example)
if (!getSource(i).getItemExpr()->referencesTheGivenValue(getValueId()))
getSource(i).getItemExpr()->unparse(result);
else
result += "...";
}
if (form == USER_FORMAT_DELUXE)
result.toUpper();
else
result += ")";
return result;
} // ValueIdUnion::getText(), the NON-virtual method
const NAString ValueIdUnion::getText() const
{
return getText(USER_FORMAT/*standard list format*/);
} // ValueIdUnion::getText(), the virtual method
HashValue ValueIdUnion::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
for(CollIndex i = 0; i < entries(); i++)
{
result ^= getSource(i);
}
result ^= result_;
result ^= flags_;
return result;
}
NABoolean ValueIdUnion::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
ValueIdUnion &o = (ValueIdUnion &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
for(CollIndex i = 0; i < entries(); i++)
{
if (getSource(i) != o.getSource(i))
return FALSE;
}
if (result_ != o.result_ || flags_ != o.flags_)
return FALSE;
return TRUE;
}
ItemExpr * ValueIdUnion::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ValueIdUnion *result;
if (derivedNode == NULL)
result = new (outHeap) ValueIdUnion(sources_, result_, flags_);
else
result = (ValueIdUnion*)derivedNode;
result->otherFlags_ = otherFlags_;
return ItemExpr::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class VEG
// -----------------------------------------------------------------------
VEG::VEG()
: ItemExpr(ITM_VEG), done_(FALSE), userInputs_(0), seenBefore_(FALSE),
specialNulls_(FALSE)
{
allocValueId();
// Allocate a VegReference for representing any member of the VEG.
vegRef_ = new (CmpCommon::statementHeap()) VEGReference(getValueId());
// Allocate a VegPredicate for replacing the = predicate
vegPred_ = new (CmpCommon::statementHeap()) VEGPredicate(getValueId());
} // VEG::VEG()
VEG::VEG(const ValueIdSet & vegSet)
: ItemExpr(ITM_VEG), eqGroup_(vegSet), done_(FALSE), userInputs_(0),
seenBefore_(FALSE), specialNulls_(FALSE)
{
allocValueId();
// Allocate a VegReference for representing any member of the VEG.
vegRef_ = new (CmpCommon::statementHeap()) VEGReference(getValueId());
// Allocate a VegPredicate for replacing the = predicate
vegPred_ = new (CmpCommon::statementHeap()) VEGPredicate(getValueId());
} // VEG::VEG()
VEG::~VEG()
{
delete vegRef_;
delete vegPred_;
} // VEG::~VEG()
void VEG::insert(const ValueId & newValue)
{
if (NOT eqGroup_.contains(newValue))
{
eqGroup_ += newValue;
if (newValue.getItemExpr()->isAUserSuppliedInput())
userInputs_++;
}
} // VEG::insert()
void VEG::insert(const ValueIdSet & newValues)
{
for (ValueId exprId = newValues.init();
newValues.next(exprId);
newValues.advance(exprId))
{
if (NOT eqGroup_.contains(exprId))
{
eqGroup_ += exprId;
if (exprId.getItemExpr()->isAUserSuppliedInput())
userInputs_++;
}
}
} // VEG::insert()
void VEG::merge(const VEG& other)
{
// Add all members of the other VEG to my own
eqGroup_ += other.eqGroup_;
// Fixup the VEGReference and VEGPredicate of the
// other VEG to reference me instead.
other.getVEGReference()->replaceVEG(getValueId());
other.getVEGPredicate()->replaceVEG(getValueId());
userInputs_ += other.userInputs_;
if (specialNulls_ && !other.getSpecialNulls())
specialNulls_ = FALSE;
} // VEG::merge()
Int32 VEG::getArity() const { return 0; }
const NAString VEG::getText() const
{
if (seenBefore())
{
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY"))
return NAString("[...]");
#endif
return NAString("...");
}
markAsSeenBefore();
NAString result("(",CmpCommon::statementHeap());
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY")) result = "[";
#endif
NABoolean first = TRUE;
//char cstrId[TEXT_DISPLAY_LENGTH];
const ValueIdSet & eqGroup_ = getAllValues();
for (ValueId x = eqGroup_.init(); eqGroup_.next(x); eqGroup_.advance(x))
{
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("NO_INDEXCOL_DISPLAY") || getenv("SIMPLE_DISPLAY"))
if (x.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN) continue;
#endif
if (x.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN) continue;
if (NOT first)
{
result += " = ";
}
first = FALSE;
x.getItemExpr()->unparse(result);
// add valueId's after pred. name
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if ( (getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY")) &&
x.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE ) continue;
#endif
}
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY"))
result += "]";
#endif
result += ")";
markAsNotSeenBefore();
return result;
}
HashValue VEG::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= (ValueIdSet &) *this;
return result;
}
NABoolean VEG::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
if (getVEGReference()->getVEG() !=
((VEG &)other).getVEGReference()->getVEG())
return FALSE;
return TRUE;
}
ItemExpr * VEG::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
ABORT("No methods for copying this object yet");
if (derivedNode == NULL)
result = new (outHeap) VEG();
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
void
VEG::getAndExpandAllValues(ValueIdSet& expandedValues) const
{
// Get all members of VEGRef:
const ValueIdSet& vegGroup = getAllValues();
for (ValueId vid = vegGroup.init();
vegGroup.next(vid);
vegGroup.advance(vid))
{
if (vid.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
{
expandedValues.insert(vid);
continue;
}
if ( (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) &&
(! expandedValues.contains(vid))
)
{
// In some cases, a VEGRef might contain another VEGRef within
// its VEG which recursively self-references the VEGRef.
// In order to prevent running into an infinite loop there, we
// insert the vid into the set and remove it after returning
// from recursive call. (case 10-001201-9972)
expandedValues.insert(vid);
((VEGReference *) (vid.getItemExpr())) -> getVEG() ->
getAndExpandAllValues(expandedValues);
expandedValues -=vid;
}
}
} // VEG::getAndExpandAllValues(ValueIdSet& expandedValues) const
// return a Constant, hostvar or parameter from this VEG
ValueId VEG::getAConstantHostVarOrParameter() const
{
for (ValueId id = eqGroup_.init(); eqGroup_.next(id); eqGroup_.advance(id))
{
ItemExpr *ie = id.getItemExpr();
OperatorTypeEnum oper = ie->getOperatorType();
if ((oper == ITM_CONSTANT) ||
(oper == ITM_HOSTVAR) ||
(oper == ITM_DYN_PARAM) ||
(oper == ITM_CACHE_PARAM))
return id;
}
return NULL_VALUE_ID;
}
// return a Constant from this VEG. If includeCacheParam is TRUE, also accept
// the value of a constant parameter.
ValueId VEG::getAConstant(NABoolean includeCacheParam) const
{
for (ValueId id = eqGroup_.init(); eqGroup_.next(id); eqGroup_.advance(id))
{
ItemExpr *ie = id.getItemExpr();
OperatorTypeEnum oper = ie->getOperatorType();
if (oper == ITM_CONSTANT)
return id;
else if (includeCacheParam && oper == ITM_CACHE_PARAM)
return (static_cast<ConstantParameter*>(ie))->getConstVal()->getValueId();
}
return NULL_VALUE_ID;
}
// -----------------------------------------------------------------------
// member functions for class VEGPredicate
// -----------------------------------------------------------------------
// default selectivity for VEGPredicates (same as equality preds)
double VEGPredicate::defaultSel()
{
return (1.0/(CURRSTMT_OPTDEFAULTS->defNoStatsUec()) );
}
VEGPredicate::VEGPredicate(const ValueId & ofVEG)
: ItemExpr(ITM_VEG_PREDICATE)
, veg_(ofVEG)
, specialNulls_(FALSE) // ++MV - Irena
{
CMPASSERT (ofVEG.getItemExpr()->getOperatorType() == ITM_VEG);
synthTypeAndValueId();
}
VEGPredicate::VEGPredicate(const VEGPredicate& vp)
: ItemExpr(ITM_VEG_PREDICATE)
, veg_(vp.veg_)
, specialNulls_(vp.specialNulls_)
, predsWithSelectivities_(vp.predsWithSelectivities_)
{
synthTypeAndValueId();
}
VEGPredicate::~VEGPredicate() {}
void VEGPredicate::replaceVEG(const ValueId& vegId)
{
CMPASSERT(vegId.getItemExpr()->getOperatorType() == ITM_VEG);
veg_ = vegId;
} // VEGPredicate::replaceVEG()
Int32 VEGPredicate::getArity() const { return 0; }
NABoolean VEGPredicate::isAPredicate() const { return TRUE; }
// ----------------------------------------------------------------------
// Walk through an ItemExpr tree and gather the ValueIds of those
// expressions that behave as if they are "leaves" for the sake of
// the coverage test, e.g., expressions that have no children, or
// aggregate functions, or instantiate null. These are usually values
// that are produced in one "scope" and referenced above that "scope"
// in the dataflow tree for the query.
// ----------------------------------------------------------------------
void VEGPredicate::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
leafValues += ((VEGPredicate *)this)->getVEG()->getVEGReference()->getValueId();
} // VEGPredicate::getLeafValuesForCoverTest()
NABoolean VEGPredicate::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& /* coveredSubExpr*/,
ValueIdSet& /* unCoveredExpr*/ ) const
{
ValueId VEGRefId = getVEG()->getVEGReference()->getValueId();
ValueIdSet vegMembers = getVEG()->getAllValues();
NABoolean covered = FALSE;
// A VEGPredicate is covered if the child can supply the VEGRefId.
// If the VEGRefId is only supplied by the inputs we want to return false.
// The VEGRefId will be part of the child's required outputs since
// the parent needed that to evaluate the VEGPredicate.
// So simply check if the VEGRefId is part of the required outputs.
// However, when FileScanRule::nextSubstitute is trying to determine
// if a index is useful, the required output simply contains the
// list of indexcol valueId's, and has not been remapped to the
// VEGRefId's that correspond in that region. So check to see if
// the indexcol intersects with the VEG members.
// We cannot call coveringGA.covers() in this case because it will be fooled
// by the presences of one of the members of the VEG in the required
// inputs or newExternalInputs.
if (coveringGA.isCharacteristicOutput(VEGRefId))
{
covered = TRUE;
}
else
{
// Check if a member of the VEG is in the characteristic outputs
ValueIdSet outputs = coveringGA.getCharacteristicOutputs();
outputs.intersectSet(vegMembers);
if (NOT outputs.isEmpty())
covered = TRUE;
}
// If the VEGPredicate is covered we want to include in the
// characteristic inputs the VEGRefId for the VEG if
// available
if (covered)
{
// If the VEGPredicate is covered without supplying any
// new external inputs, indicate which of the given
// external inputs can help for evaluating this
// VEGPredicate here.
if (newExternalInputs.contains(VEGRefId))
referencedInputs += VEGRefId;
else
{
const NABoolean doNotLookInsideVegReferences = FALSE ;
const NABoolean doNotLookInsideInstantiateNulls = FALSE ;
//
// It is likely that some member of the VEG appears in
// the external inputs. For example, consider
// t1 left join t2 on t1.x = t2.y
// The VEG will contain (t2.y, ixcol t2.y, VEGRef(t1.x ..))
//
// The nested loop join transformation will supply
// VEGRef(t1.x ..), which belongs to an outer scope, as an
// external input. Clearly, the latter new external input does
// not contain the VEGReference for the former VEG. In fact,
// the reverse situation is true. The case that is sketched
// in these comments is detected by the code that appears below:
referencedInputs.accumulateReferencedValues
(newExternalInputs,
vegMembers,
doNotLookInsideVegReferences,
doNotLookInsideInstantiateNulls);
}
} // if (covered)
// The idea is to push the predicate down only when the tree can
// produce a member of the VEG. However, we are having too many
// problems with the above policy. We did not want to push a
// VEGPredicate down the tree only because the VEG references a
// constant that is needed in the tree. However, predicates of the
// form :hv = 10 and other predicates that only reference correlated
// values need to be pushed to some scan.
// $$$$ Comments (Dec. 19, 1996)
// Check whether the predicate should be pushed down by looking at
// the number of user supplied inputs that are members of the
// VEG. If the VEGPredicate is not covered because the tree cannot
// produce a member of the VEG, push it down iff there are at least
// two user supplied inputs that are members of the VEG. Indeed,
// this is a kludge that is meant to permit the optimization
// i.e., evaluating predicates such as :hv =
// 10 on some scan. However, it has the advantage of eliminating the
// really dumb behaviour of pushing a predicate down on the strength
// of supplying a single constant value as an input.
// The kludge added does not work. Predicates of the form
// :hv = 10 are not stored in a VEGPred unless there also is a
// predicate of the form col = :hv. So, all that needs to be handled
// here are VEGPreds that reference correlated column values but
// do not reference values supplied by the child's char outputs.
// For example, 31 = T0.i1, and T0 is not a child.
else
{
// Check if all value ids in the vegMembers set are either
// user supplied inputs or correlated references (an embedded
// vegRef must be a correlated reference or it's in an left join
// ON clause). If it's in an left join ON clause, this is ok,
// it just means we may push down the pred to the right child
// of the left join if the join is a TSJ and the pred only
// refers to the left child and user inputs. For example,
// select t0.a from t0 left join t1 on t0.a = 10. The pred
// t0.a = 10 will get pushed to the right child (t1) if the
// join is a TSJ. This would not happen without this fix but
// this should not be a problem as it is legal to push an
// ON clause pred to the right child of a left join.
NABoolean allVEGMembersAreInputs = TRUE;
NABoolean outerReferencesSeen = FALSE;
NABoolean userSuppliedInputsSeen = FALSE;
ValueIdSet outerReferences;
ValueIdSet userSuppliedInputs;
for (ValueId vid = vegMembers.init();
vegMembers.next(vid);
vegMembers.advance(vid))
{
// Check if the veg member is not a user supplied input or
// a correlated reference. It is a correlated reference if it
// is an embedded vegRef or if it is an instantiate null whose
// child is an embedded vegRef. A instantiate null with an
// embedded vegRef child represents a correlated reference to
// the right child output of a left join.
if (NOT (vid.getItemExpr()->isAUserSuppliedInput() OR
(vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE) OR
((vid.getItemExpr()->getOperatorType() ==
ITM_INSTANTIATE_NULL) AND
(vid.getItemExpr()->child(0)->getOperatorType() ==
ITM_VEG_REFERENCE))
)
)
{
// If this column was a host var before binding, we consider it as such
if (vid.getItemExpr()->previousHostVar())
{
allVEGMembersAreInputs = TRUE;
break;
}
allVEGMembersAreInputs = FALSE;
}
// check for outer-references in form of embedded veg-references
if (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE)
{
outerReferencesSeen = TRUE;
outerReferences +=vid;
}
else if (vid.getItemExpr()->isAUserSuppliedInput())
{
userSuppliedInputs +=vid;
userSuppliedInputsSeen = TRUE;
}
} // for
if (outerReferencesSeen &&
NOT allVEGMembersAreInputs)
{
// This VEGPredicate has an outer reference in the VEG Members set.
// In addition the set also has a NON-UserSuppliedInput such
// as a base column or an index column (extra columns);
// We already know that these extra columns are not part of the output
// (A check is made at the beginning of this routine)
// Check if these are part of input and set covered to TRUE only when
// they are NOT part of characteristic input set
// CR 10-010314-1732
if ( newExternalInputs.contains(VEGRefId) &&
userSuppliedInputsSeen
)
{
// VegMembers contain an user Supplied input and an outer Reference
ValueIdSet inputset = vegMembers;
inputset -= outerReferences;
inputset -= userSuppliedInputs;
inputset.intersectSet(newExternalInputs);
if (inputset.isEmpty())
{
covered = TRUE;
referencedInputs += VEGRefId;
}
}
}
// Allow the predicate to be pushed down if all vegMembers are
// user supplied inputs or correlated references, and the VEGRef
// or at least one of it's constiuent parts are covered by the
// char. inputs.
if (allVEGMembersAreInputs)
{
if (coveringGA.isCharacteristicInput(VEGRefId))
{
covered = TRUE;
}
if (newExternalInputs.contains(VEGRefId))
{
covered = TRUE;
referencedInputs += VEGRefId;
}
if (NOT covered)
{
vegMembers.intersectSet(newExternalInputs);
if (NOT vegMembers.isEmpty())
{
covered = TRUE;
referencedInputs += vegMembers;
}
}
} // end if all veg members are inputs
} // end if not covered by outputs
return covered;
} // VEGPredicate::isCovered()
const NAString VEGPredicate::getText() const
{
char cp[TEXT_DISPLAY_LENGTH];
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY"))
{
sprintf(cp,"VP%d",CollIndex(getValueId()));
return NAString(cp) + veg_.getItemExpr()->getText();
}
#endif
//sprintf(cp,"VEGPred_%d(",CollIndex(getValueId()));
cp[0] = 0;
return NAString(cp) + veg_.getItemExpr()->getText(); // + ")";
} // VEGPredicate::getText()
// MVs --
void VEGPredicate::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
if ((form != MVINFO_FORMAT) &&
(form != QUERY_FORMAT))
{
ItemExpr::unparse(result, phase, form, tabId);
return;
}
const ValueIdList vegMembers(getVEG()->getAllValues());
CMPASSERT(vegMembers.entries() >=2);
ValueIdList copyList;
CollIndex startIndex;
if (form == QUERY_FORMAT || form == MVINFO_FORMAT)
startIndex = 0;
else
startIndex = 1;
ValueId nextMemberId;
for (CollIndex i=startIndex; i<vegMembers.entries(); i++)
{
nextMemberId = vegMembers[i];
ItemExpr *nextExpr = nextMemberId.getItemExpr();
if (nextExpr->getOperatorType() == ITM_INDEXCOLUMN)
continue;
if ((form == QUERY_FORMAT) &&
(nextExpr->getOperatorType() == ITM_BASECOLUMN))
{
BaseColumn * bs = (BaseColumn *)nextExpr;
if (bs->getTableDesc() != tabId && form != MVINFO_FORMAT)
continue;
}
copyList.insert(vegMembers[i]);
}
if (copyList.entries() < 2)
return;
ValueId firstMemberId = copyList[0];
NAString firstMemberText;
firstMemberId.getItemExpr()->unparse(firstMemberText, phase, form, tabId);
NAString nextMemberText("empty");
for (CollIndex i=1; i<copyList.entries(); i++)
{
nextMemberId = copyList[i];
// If this is not the first predicate, "AND" between predicates.
if (nextMemberText != "empty")
result += " AND ";
nextMemberText = "";
nextMemberId.getItemExpr()->unparse(nextMemberText, phase, form, tabId);
result += firstMemberText;
result += " = ";
result += nextMemberText;
}
}
HashValue VEGPredicate::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= getVEG()->topHash();
return result;
} // VEGPredicate::topHash()
NABoolean VEGPredicate::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (veg_ != ((VEGPredicate &)other).veg_)
return FALSE;
return TRUE;
} // VEGPredicate::duplicateMatch()
ItemExpr * VEGPredicate::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
//ABORT("No methods for copying this object yet");
result = new VEGPredicate(*this);
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
} // VEGPredicate::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class VEGReference
// -----------------------------------------------------------------------
VEGReference::VEGReference(const ValueId & ofVEG)
: ItemExpr(ITM_VEG_REFERENCE), veg_(ofVEG)
{
CMPASSERT (ofVEG.getItemExpr()->getOperatorType() == ITM_VEG);
allocValueId();
}
VEGReference::~VEGReference() {}
Int32 VEGReference::getArity() const { return 0; }
void VEGReference::replaceVEG(const ValueId& vegId)
{
CMPASSERT(vegId.getItemExpr()->getOperatorType() == ITM_VEG);
veg_ = vegId;
} // VEGReference::replaceVEG()
NABoolean VEGReference::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr ) const
{
// If we arrive here to check whether a VEGReference isCovered(),
// it means that this VEGReference does not belong to the
// Characteristic Inputs and Outputs. (Otherwise, the test in
// covers() would have detected its presence.)
// Walk through all the members of the set in order to return an
// indication of which values are referenced inputs, which values
// are covered sub expressions and which of them are uncovered expr.
NABoolean retVal = FALSE;
VEG *veg = getVEG();
if (veg->seenBefore())
return FALSE;
// Remember valueId of the VEGReference that we've already seen.
veg->markAsSeenBefore();
for (ValueId x = veg->getAllValues().init();
veg->getAllValues().next(x);
veg->getAllValues().advance(x))
{
// I am covered if ANY ONE of my candidate values is covered
if (coveringGA.covers(x, newExternalInputs, referencedInputs,
&coveredSubExpr, &unCoveredExpr))
{
retVal = TRUE;
break;
}
}
veg->markAsNotSeenBefore();
return retVal;
} // VEGReference::isCovered()
OrderComparison VEGReference::sameOrder(ItemExpr * other,
NABoolean /*askOther*/)
{
// first, check whether the item expressions are identical
if (this == other)
return SAME_ORDER;
// We can have circular VegRefs like :
// VEGRef_285(TAB4.SSTUTKYOKI = VEGRef_310(TAB9.TKYOKI = VEGRef_285(..)))
// In this case, sameOrder()will go into infinite loop (soln 10-110911-7326).
// Not sure why we ended up constructing circular VegRefs in the first place,
// but ItemExpr::referencesTheGivenValue() code indicates it's possible
// and handles by marking Vegrefs as seen before and back out if VegRef is
// being seen second time. Same logic is also used here as explained below:
// 1. if VegRef has not seen before, mark it as seen before, also remember it.
// 2. for every veg member who is of type vegreference and seen before,
// do not call sameOrder() again.
// 3. if this method has called markAsSeenBefore(), then call
// markAsNotSeenBefore()
NABoolean callNotSeen = FALSE;
VEG *veg = getVEG();
// step 1
if (!veg->seenBefore())
{
veg->markAsSeenBefore();
callNotSeen = TRUE;
}
// VEGReferences can be used for ordering comparison under the
// assumption that all comparison predicates that can be applied
// actually have been applied. For example, if we want "order by t1.x"
// and t1.x is in a VEG with t2.y, then we can use an existing
// order t2.y if we can assume that t1.x has already been compared
// with t2.y. Note that we want order t1.x we should have t1.x
// available at that time.
const ValueIdSet &equivValues = getVEG()->getAllValues();
OrderComparison result = DIFFERENT_ORDER;
for (ValueId x = equivValues.init();
equivValues.next(x) AND result == DIFFERENT_ORDER;
equivValues.advance(x))
{
if((x.getItemExpr()->getOperatorType()) == ITM_VEG_REFERENCE)
{
// step 2
VEG *innerVeg = ((VEGReference *)x.getItemExpr())->getVEG();
if (!innerVeg->seenBefore())
result = x.getItemExpr()->sameOrder(other,TRUE);
}
else
result = x.getItemExpr()->sameOrder(other,TRUE);
}
// step 3
if (callNotSeen)
veg->markAsNotSeenBefore();
return result;
}
// ----------------------------------------------------------------------
// Walk through members of VegReference and verify if any member of this
// VegReference, which is of TYPE ITM_VEG_REFERENCE contains passed
// argument. Also dig deep inside all ITM_VEG_REFERENCE objects to avoid
// circular vegRefences like :
// VEGRef_285((T4.SSTUTKYOKI = VEGRef_310((T9.TKYOKI = VEGRef_285(...))))
// This method gets called from VEGRegion::replaceVEGMember() and fixes
// bug QC_1348
// ----------------------------------------------------------------------
NABoolean VEGReference::referencesVegRefValue(ValueId& ofVegRef)
{
// Get all members of VEGRef:
const ValueIdSet &vegGroup = getVEG()->getAllValues();
VEG *oldVEGPtr = ((VEGReference *)ofVegRef.getItemExpr())->getVEG();
for (ValueId vid = vegGroup.init(); vegGroup.next(vid); vegGroup.advance(vid))
{
if (vid.getItemExpr()->getOperatorType() == ITM_VEG_REFERENCE)
{
VEG *newVEGPtr = ((VEGReference *) (vid.getItemExpr()))->getVEG();
if (newVEGPtr == oldVEGPtr)
return TRUE;
else
return ((VEGReference *) (vid.getItemExpr()))->referencesVegRefValue(ofVegRef);
}
}
return FALSE;
}
const NAString VEGReference::getText() const
{
char cp[TEXT_DISPLAY_LENGTH];
sprintf(cp,"VEGRef_%d(",CollIndex(getValueId()));
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
NABoolean simpleDisplay = FALSE;
simpleDisplay = getenv("SIMPLE_VEG_DISPLAY") || getenv("SIMPLE_DISPLAY");
if (simpleDisplay)
sprintf(cp,"VR%d",CollIndex(getValueId()));
#endif
NAString x(NAString(cp, CmpCommon::statementHeap())+
veg_.getItemExpr()->getText(),
CmpCommon::statementHeap());
x += ")";
#ifndef NDEBUG
if (simpleDisplay)
x.remove(x.length()-1);
#endif
return x;
} // VEGReference::getText()
void VEGReference::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
if ((form == EXPLAIN_FORMAT) || (form == MVINFO_FORMAT) ||
(form == QUERY_FORMAT))
{
// End users won't know what a VegReference is, and
// most items in EXPLAIN are already rewritten w/o VEGies.
// Generate equiv(col) where col is the unparsed text
// of some VEG member.
/*if (form == EXPLAIN_FORMAT)
result += "equiv(";
*/
const ValueIdSet &vegMembers =
((VEG *) veg_.getItemExpr())->getAllValues();
for (ValueId someMemberId = vegMembers.init();
vegMembers.next(someMemberId);
vegMembers.advance(someMemberId))
{
ItemExpr * someMemberExpr = someMemberId.getItemExpr();
if (form == QUERY_FORMAT)
{
// for QUERY_FORMAT, unparse the VEG member that belongs to the
// given tableDesc
if ((someMemberExpr->getOperatorType() == ITM_BASECOLUMN) &&
(((BaseColumn *)someMemberExpr)->getTableDesc() == tabId) ||
(CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) == DF_DUMP_MV))
{
someMemberExpr->unparse(result, phase, form, tabId);
break;
}
else
continue;
}
else
{
// if not QUERY_FORMAT, unparse the first veg member
someMemberExpr->unparse(result,phase,form);
break;
}
}
/*
if (form == EXPLAIN_FORMAT)
result += ") ";
*/
}
else
{
result += getText();
}
}
HashValue VEGReference::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= getVEG()->topHash();
return result;
} // VEGReference::topHash()
NABoolean VEGReference::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (veg_ != ((VEGReference &)other).veg_)
return FALSE;
return TRUE;
} // VEGReference::duplicateMatch()
ItemExpr * VEGReference::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
//ABORT("No methods for copying this object yet");
result = new (outHeap) VEGReference(getVEG()->getValueId());
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
} // VEGReference::copyTopNode()
QR::ExprElement VEGReference::getQRExprElem() const
{
return QR::QRColumnElem;
}
// -----------------------------------------------------------------------
// member functions for class CheckConstraint
// -----------------------------------------------------------------------
Int32 CheckConstraint::getArity() const { return 0;}
ItemExpr * CheckConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CheckConstraint(*this, outHeap);
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
const NAString CheckConstraint::getText() const
{
return "CheckConstraint";
}
void CheckConstraint::unparse(NAString &result,
PhaseEnum /* phase */,
UnparseFormatEnum /* form */,
TableDesc * tabId) const
{
result += getText();
result += "(";
result += getConstraintName().getQualifiedNameAsAnsiString();
result += ")";
}
// -----------------------------------------------------------------------
// member functions for class OptConstraint
// -----------------------------------------------------------------------
ItemExpr * OptConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
return ItemExpr::copyTopNode(derivedNode, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class CardConstraint
// -----------------------------------------------------------------------
Int32 CardConstraint::getArity() const { return 0;}
ItemExpr * CardConstraint::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CardConstraint(lowerBound_,upperBound_);
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
const NAString CardConstraint::getText() const
{
return "CardConstraint";
}
void CardConstraint::unparse(NAString &result,
PhaseEnum /* phase */,
UnparseFormatEnum /* form */,
TableDesc * tabId) const
{
char ascii[TEXT_DISPLAY_LENGTH];
sprintf(ascii,"CardConstraint(%g,%g)",lowerBound_,upperBound_);
result += ascii;
}
// -----------------------------------------------------------------------
// member functions for class UniqueOptConstraint
// -----------------------------------------------------------------------
Int32 UniqueOptConstraint::getArity() const { return 0;}
ItemExpr * UniqueOptConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) UniqueOptConstraint(uniqueCols_);
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
const NAString UniqueOptConstraint::getText() const
{
return "UniqueOptConstraint";
}
void UniqueOptConstraint::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
result += "UniqueOptConstraint";
uniqueCols_.unparse(result,phase,form);
}
// -----------------------------------------------------------------------
// member functions for class FuncDependencyConstraint
// -----------------------------------------------------------------------
Int32 FuncDependencyConstraint::getArity() const
{
return 0;
}
ItemExpr * FuncDependencyConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) FuncDependencyConstraint(determiningCols_,
dependentCols_);
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
void FuncDependencyConstraint::synthFunctionalDependenciesFromChild(
GroupAttributes &ga,
const RelExpr *child,
NABoolean createNewDependencies)
{
const ValueIdSet & childConstraints =
child->getGroupAttr()->getConstraints();
ValueIdSet vegColsFromChild(child->getGroupAttr()->
getCharacteristicOutputs());
// For now we want to maintain only VEGReferences as the dependent
// columns. This is mainly done to avoid unusual situations
// (monkeys at the keyboard) that might lead to errors. Restricting
// functional dependencies to VEGRefs should still cover the majority
// of all interesting situations.
if (createNewDependencies)
{
for (ValueId co = vegColsFromChild.init();
vegColsFromChild.next(co);
vegColsFromChild.advance(co))
{
if (co.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
vegColsFromChild -= co;
}
}
// walk through the child constraints
for (ValueId cx = childConstraints.init();
childConstraints.next(cx);
childConstraints.advance(cx))
{
ItemExpr *c = cx.getItemExpr();
OperatorTypeEnum optype = c->getOperatorType();
if (optype == ITM_UNIQUE_OPT_CONSTRAINT AND
createNewDependencies AND
NOT vegColsFromChild.isEmpty())
{
UniqueOptConstraint *uc = ((UniqueOptConstraint *)c);
// Try to find unique columns in the child that are no longer
// unique in the parent. For each such uniqueness constraint,
// add a functional dependency constraint if the unique columns
// are visible to the parent.
// NOTE: At some later point we should consider taking out
// the check on the characteristic outputs, since it may
// suppress some interesting constraints (we don't check
// uniqueness constraints for being covered by char. outputs either)
// Example: select a from t1 join t2 may get
// different uniqueness constraints (and cardinality estimates)
// than select * from t1 join t2.
if (NOT ga.isUnique(uc->uniqueCols()) &&
child->getGroupAttr()->getCharacteristicOutputs().contains(
uc->uniqueCols()))
{
// yes, found unique columns that are visible to the parent,
// now check whether all unique columns are VEGRefs
// (sorry, we only consider those for now)
NABoolean NonVEGFound = FALSE;
for (ValueId ux=uc->uniqueCols().init();
uc->uniqueCols().next(ux);
uc->uniqueCols().advance(ux))
if (ux.getItemExpr()->getOperatorType() != ITM_VEG_REFERENCE)
NonVEGFound = TRUE;
if (NOT NonVEGFound)
{
ValueIdSet myDependentCols(vegColsFromChild);
// remove the unique columns from the dependent columns
// (don't want functional dependencies like A --> A)
myDependentCols -= uc->uniqueCols();
if (NOT myDependentCols.isEmpty())
{
FuncDependencyConstraint *nc =
new(CmpCommon::statementHeap())
FuncDependencyConstraint(uc->uniqueCols(),
myDependentCols);
ga.addConstraint(nc);
}
}
}
}
else if (optype == ITM_FUNC_DEPEND_CONSTRAINT)
{
// Found a functional dependency in the child constraints.
// Copy it if the determining columns are visible in the
// parent node.
FuncDependencyConstraint *fd = (FuncDependencyConstraint *) c;
// NOTE: At some later point we should consider taking out
// the check on the characteristic outputs (same as above).
if (child->getGroupAttr()->getCharacteristicOutputs().contains(
fd->getDeterminingCols()))
{
ga.addConstraint(c);
}
}
}
}
void FuncDependencyConstraint::minimizeUniqueCols(ValueIdSet &uniqueCols)
{
if (uniqueCols.contains(determiningCols_))
{
// We know now that the uniqueCols determine our "dependentCols_".
// As an example, let's assume that uniqueCols is (a,b,c,d),
// determiningCols_ is (a,b), and dependentCols_ is (c).
// If columns (a,b) determine (c), then it is true that if
// (a,b,c) is unique then (a,b) is unique as well. This is
// because the definition of functional dependency says that
// there will never be two rows with the same (a,b) values that
// have different (c) values.
// The extra column (d) in the example does not invalidate this
// argument.
// at this point we only use simple ValueId operations, no
// cover test methods for checks like "does (a,b) determine (a+c+2)?"
// Could assert here that the intersection of determiningCols_ and
// dependentCols_ is empty (more precisely: that the dependent columns
// don't cover any of the unique columns, but we don't have the
// necessary information to check this here). See method
// FuncDependencyConstraint::createFunctionalDependenciesFromChild()
// above for where this constraint is enforced.
uniqueCols.subtractSet(dependentCols_);
}
}
const NAString FuncDependencyConstraint::getText() const
{
return "FuncDependencyConstraint";
}
void FuncDependencyConstraint::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// use EXPLAIN format since we only talk about VEGRefs and
// one member should be sufficient to represent the VEG
result += "FuncDependencyConstraint(";
determiningCols_.unparse(result,phase,EXPLAIN_FORMAT);
result += " ---> ";
dependentCols_.unparse(result,phase,EXPLAIN_FORMAT);
result += ")";
}
// -----------------------------------------------------------------------
// member functions for class CheckOptConstraint
// -----------------------------------------------------------------------
CheckOptConstraint::~CheckOptConstraint() {}
Int32 CheckOptConstraint::getArity() const { return 0; }
ItemExpr * CheckOptConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CheckOptConstraint(checkPreds_);
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
const NAString CheckOptConstraint::getText() const
{
return "CheckOptConstraint";
}
void CheckOptConstraint::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
result += "CheckOptConstraint";
checkPreds_.unparse(result,phase,form);
}
// -----------------------------------------------------------------------
// member functions for class RefOptConstraint
// -----------------------------------------------------------------------
Int32 RefOptConstraint::getArity() const { return 0;}
ItemExpr * RefOptConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) RefOptConstraint(fkCols_, uniqueConstraintName_);
((RefOptConstraint*)result)->isMatched_ = isMatched_ ;
}
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
const NAString RefOptConstraint::getText() const
{
return "RefOptConstraint";
}
void RefOptConstraint::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
result += "RefOptConstraint";
fkCols_.unparse(result,phase,form);
result += "(";
result += uniqueConstraintName().getQualifiedNameAsAnsiString();
result += ")";
}
// -----------------------------------------------------------------------
// member functions for class ComplementaryRefOptConstraint
// -----------------------------------------------------------------------
Int32 ComplementaryRefOptConstraint::getArity() const { return 0;}
ItemExpr * ComplementaryRefOptConstraint::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ComplementaryRefOptConstraint(ucCols_,
constraintName_, tabPtr_, tabDesc_, NULL, isMatchedForElimination_);
else
result = derivedNode;
return OptConstraint::copyTopNode(result, outHeap);
}
const NAString ComplementaryRefOptConstraint::getText() const
{
return "ComplementaryRefOptConstraint";
}
void ComplementaryRefOptConstraint::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
result += "ComplementaryRefOptConstraint";
ucCols_.unparse(result,phase,form);
result += "(";
result += constraintName().getQualifiedNameAsAnsiString();
result += ")";
}
// -----------------------------------------------------------------------
// member functions for class Aggregate
// -----------------------------------------------------------------------
Aggregate::~Aggregate() {}
NABoolean Aggregate::isAnAggregate() const { return TRUE; }
NABoolean Aggregate::containsAnAggregate() const { return TRUE; }
Int32 Aggregate::getArity() const
{
// ##Should be same as Function::getNumChildren(), except added CMPASSERT here
// (aggregates must have at least one child; stddev/variance can have more).
if (!child(1)) return 1; // most aggs have 1 child
CMPASSERT(!child(2)); // no aggs have 3+ children
return 2; // STDDEV/VARIANCE may have 1 or 2
}
NABoolean Aggregate::operator== (const ItemExpr& other) const // virtual meth
{
if (ItemExpr::operator==(other)) return TRUE;
if (getOperatorType() != other.getOperatorType()) return FALSE;
const Aggregate &otherAgg = (const Aggregate &)other;
if (getOperatorType() == ITM_MIN || getOperatorType() == ITM_MAX)
{
// Min/Max are order-sensitive
ItemExprList arglist0(child(0), NULL);
ItemExprList arglist1(otherAgg.child(0), NULL);
if (arglist0.entries() != arglist1.entries()) return FALSE;
for (CollIndex i = arglist0.entries(); i--; )
if (arglist0[i] != arglist1[i]) return FALSE;
}
else
{
ValueIdSet argset0, argset1;
child(0)->convertToValueIdSet(argset0, NULL, ITM_ITEM_LIST);
otherAgg.child(0)->convertToValueIdSet(argset1, NULL, ITM_ITEM_LIST);
if (argset0 != argset1) return FALSE;
}
return TRUE;
}
NABoolean Aggregate::duplicateMatch(const ItemExpr& other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
const Aggregate &ag = (Aggregate &)other;
if (isDistinct_ != ag.isDistinct_)
return FALSE;
if (NOT isSensitiveToDuplicates())
return FALSE;
return TRUE;
}
NABoolean Aggregate::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& /*unCoveredExpr*/ ) const
{
// ---------------------------------------------------------------------
// If the operand of the aggregate isCovered(), then return its ValueId
// in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for (Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if ( coveringGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr) )
{
coveredSubExpr += child(i)->getValueId();
}
}
// ---------------------------------------------------------------------
// An aggregate function is coerced to fail the coverage test even
// when its operand isCovered(). This is because the computation of
// the aggregate function requires "raw" values produced by its
// operand to be grouped. The aggregated values cannot be treated
// in the same manner as the constituent raw values.
// ---------------------------------------------------------------------
return FALSE; // sorry, i am a special case
} // Aggregate::isCovered()
// ----------------------------------------------------------------------
// Walk through an ItemExpr tree and gather the ValueIds of those
// expressions that behave as if they are "leaves" for the sake of
// the coverage test, e.g., expressions that have no children, or
// aggregate functions, or instantiate null. These are usually values
// that are produced in one "scope" and referenced above that "scope"
// in the dataflow tree for the query.
// ----------------------------------------------------------------------
void Aggregate::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
leafValues += getValueId();
} // Aggregate::getLeafValuesForCoverTest()
const NAString Aggregate::getText() const
{
NAString result(CmpCommon::statementHeap());
NABoolean dumpMvMode = (CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) == DF_DUMP_MV);
switch (getOperatorType())
{
case ITM_AVG:
result = "avg";
break;
case ITM_MAX:
result = "max";
break;
case ITM_MIN:
result = "min";
break;
case ITM_SUM:
result = "sum";
break;
case ITM_COUNT:
result = "count";
break;
case ITM_COUNT_NONULL:
if (dumpMvMode)
result = "count";
else
result = "count_nonull";
break;
case ITM_GROUPING:
result = "grouping";
break;
case ITM_ONE_ROW:
result = "one_Row";
break;
case ITM_ONE_TRUE:
result = "oneTrue";
break;
case ITM_ANY_TRUE:
result = "anytrue";
break;
case ITM_ANY_TRUE_MAX:
result = "anytruemax";
break;
case ITM_STDDEV:
result = "stddev";
break;
case ITM_VARIANCE:
result = "variance";
break;
case ITM_ONEROW:
result = "oneRow";
break;
case ITM_PIVOT_GROUP:
result = "pivot_group";
break;
default:
result = "unknown aggr";
break;
} // switch
if (isDistinct_ && !dumpMvMode) {
result += " distinct";
if (distinctId_ != NULL_VALUE_ID) {
char buf[13]; // CollIndex is UInt32 with a max value of 4,294,967,295
// We need 10 + 2 + 1=13 bytes to store all possible
// values.
snprintf(buf, sizeof(buf), "(%u)", (CollIndex)distinctId_);
result += buf;
// ## Perhaps a better way of displaying this would instead be:
// result += "(";
// result += distinctId_.getItemExpr()->getText();
// result += ")";
}
}
return result;
} // Aggregate::getText()
void Aggregate::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
if (form == USER_FORMAT_DELUXE)
{
Aggregate *ncThis = (Aggregate *)this; // cast away constness!
OperatorTypeEnum saveType = getOperatorType(); // save
ItemExpr *saveChild = child(0);
ValueId saveVid = getOriginalChild()->getValueId();
getOriginalChild()->setValueId(NULL_VALUE_ID); // BEFORE setChild()!
ncThis->setOperatorType(origOpType()); // set to "orig"
ncThis->setChild(0, getOriginalChild());
ItemExpr::unparse(result,phase, form, tabId); // invoke superclass
getOriginalChild()->setValueId(saveVid); // BEFORE setChild()!
ncThis->setOperatorType(saveType); // restore
ncThis->setChild(0, saveChild);
}
else
{
if ((CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) == DF_DUMP_MV) && isDistinct_)
{
// Fix DISTINCT to be parsable.
result += getText();
result += "( DISTINCT ";
child(0)->unparse(result, phase, form, tabId);
result += ")";
}
else
ItemExpr::unparse(result, phase, form, tabId); // invoke our superclass method
}
} // Aggregate::unparse()
ItemExpr * Aggregate::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
Aggregate *result;
if (derivedNode == NULL)
result = new (outHeap) Aggregate(getOperatorType());
else
result = (Aggregate *) derivedNode;
if (inScalarGroupBy())
result->setInScalarGroupBy();
if (topPartOfAggr())
result->setTopPartOfAggr();
result->origChild_ = origChild_;
result->isDistinct_ = isDistinct_;
result->inScalarGroupBy_ = inScalarGroupBy_;
result->distinctId_ = distinctId_;
// Copy OLAP Window Function information.
//
result->isOLAP_ = isOLAP_;
result->olapPartitionBy_ = (olapPartitionBy_ ?
olapPartitionBy_->copyTree(outHeap) :
NULL);
result->olapOrderBy_ = (olapOrderBy_ ?
olapOrderBy_->copyTree(outHeap) :
NULL);
result->frameStart_ = frameStart_;
result->frameEnd_ = frameEnd_;
result->rollupGroupIndex_ = rollupGroupIndex_;
return ItemExpr::copyTopNode(result, outHeap);
}
// One heck of an implementation for a virtual function!
NABoolean Aggregate::isSensitiveToDuplicates() const
{
switch (getOperatorType())
{
case ITM_MAX:
case ITM_MIN:
case ITM_ANY_TRUE:
case ITM_ONE_TRUE:
case ITM_GROUPING:
return FALSE;
case ITM_SUM:
case ITM_COUNT:
case ITM_COUNT_NONULL:
case ITM_ONE_ROW:
case ITM_ONEROW:
case ITM_AVG:
default:
return TRUE;
} // switch
}
// Another hack of an implementation for a virtual function!!
NABoolean Aggregate::evaluationCanBeStaged() const
{
switch (getOperatorType())
{
case ITM_ONE_ROW:
// See Aggregate::rewriteForStagedEvaluation()
return FALSE;
default:
return TRUE;
} // switch
}
ItemExpr * Aggregate::rewriteForElimination()
{
// rewrite the aggregate function to return the result it would have
// returned if it were called on a group with exactly one member
// NOTE: this method doesn't delete the existing aggregate node, but
// it may reuse some of the children of the original expression.
ItemExpr *result = NULL;
switch (getOperatorType())
{
case ITM_AVG:
case ITM_MAX:
case ITM_MIN:
case ITM_SUM:
case ITM_ONEROW:
case ITM_ANY_TRUE:
case ITM_PIVOT_GROUP:
// return the value of the aggregate function's argument
result = child(0);
break;
case ITM_COUNT:
// the count of a single value is always 1
result = new (CmpCommon::statementHeap()) SystemLiteral(1);
break;
case ITM_COUNT_NONULL:
if (child(0)->getValueId().getType().supportsSQLnullLogical())
{
// generate a CASE expression: CASE WHEN arg IS NOT NULL THEN 1 ELSE 0
result = new (CmpCommon::statementHeap())
Case(NULL,
new (CmpCommon::statementHeap())
IfThenElse(new (CmpCommon::statementHeap())
UnLogic(ITM_IS_NOT_NULL,
child(0)),
new (CmpCommon::statementHeap()) SystemLiteral(1),
new (CmpCommon::statementHeap()) SystemLiteral(0)));
}
else
{
result = new (CmpCommon::statementHeap()) SystemLiteral(1);
}
break;
case ITM_ONE_TRUE:
// return TRUE only if the argument is TRUE, FALSE otherwise
result = new (CmpCommon::statementHeap()) UnLogic(ITM_IS_TRUE,child(0));
break;
default:
ABORT("unknown aggregate function encountered");
}
result->synthTypeAndValueId();
return result;
}
ItemExpr * Aggregate::rewriteForStagedEvaluation(ValueIdList &initialAggrs,
ValueIdList &finalAggrs,
NABoolean sameFormat)
{
// Split the aggregate function into three parts: the initial part is
// executed in multiple groupby nodes whose results are sent to a
// single, "final" groupby node that executes the final aggregate expression.
// More than one aggregate function may be evaluated in the initial and/or
// final nodes, therefore a set parameter is used. The return value is
// an item expression equivalent to the original expression "this". The
// return value is not necessarily an aggregate function, while
// initialAggr and finalAggr contain only aggregate functions.
// NOTE: this method doesn't delete the existing aggregate node, and it
// produces new value ids for all three parts returned.
ItemExpr *result = NULL;
Aggregate *partial;
Aggregate *sumOfCounts;
Aggregate *sumOfSums;
switch (getOperatorType())
{
case ITM_AVG:
assert(NOT sameFormat);
// divide the sum of sums by the sum of counts, but return NULL
// if the sum of counts is zero
// in pseudo-SQL, that sounds like transforming avg(x) into:
// CASE WHEN SUM(COUNT(x)) > 0 THEN SUM(SUM(x)) / SUM(COUNT(x))
// ELSE NULL
sumOfCounts = new (CmpCommon::statementHeap())
Aggregate(ITM_SUM,
new (CmpCommon::statementHeap()) Aggregate(ITM_COUNT_NONULL,
child(0)));
partial = new (CmpCommon::statementHeap())
Aggregate(ITM_SUM, child(0));
sumOfSums = new (CmpCommon::statementHeap())
Aggregate(ITM_SUM, partial);
if (inScalarGroupBy())
{
partial->setInScalarGroupBy();
sumOfSums->setInScalarGroupBy();
sumOfCounts->setInScalarGroupBy();
((Aggregate *)(sumOfCounts->child(0).getPtr()))->setInScalarGroupBy();
}
// The sum of the counts cannot be zero if the binder typed the
// avg as non-nullable.
// An AVG on a non scalar group by on a non-nullable
// expression is always non-nullable, the sum(sum(x)) is always
// non-nullable and the sum(count(x)) is always > 0
if (getValueId().getType().supportsSQLnullLogical())
{
result = new (CmpCommon::statementHeap())
Case(NULL,
new (CmpCommon::statementHeap())
IfThenElse(
new (CmpCommon::statementHeap())
BiRelat(ITM_GREATER,
sumOfCounts,
new (CmpCommon::statementHeap()) SystemLiteral(0)),
new (CmpCommon::statementHeap())
BiArith(ITM_DIVIDE,sumOfSums,sumOfCounts),
new (CmpCommon::statementHeap()) SystemLiteral()));
}
else
{
result = new (CmpCommon::statementHeap())
BiArith(ITM_DIVIDE,sumOfSums,sumOfCounts);
}
result->synthTypeAndValueId();
// execute the initial sum and count in the initial set
initialAggrs.insert(sumOfCounts->child(0)->getValueId());
initialAggrs.insert(sumOfSums->child(0)->getValueId());
finalAggrs.insert(sumOfCounts->getValueId());
finalAggrs.insert(sumOfSums->getValueId());
break;
case ITM_COUNT:
case ITM_COUNT_NONULL:
// compute the sum of the counts
partial = new (CmpCommon::statementHeap())
Aggregate(getOperatorType(),child(0));
sumOfCounts = new (CmpCommon::statementHeap())
Aggregate(ITM_SUM, partial);
if (inScalarGroupBy())
{
partial->setInScalarGroupBy();
sumOfCounts->setInScalarGroupBy();
}
sumOfCounts->setTreatAsACount();
sumOfCounts->setTopPartOfAggr();
result = sumOfCounts;
result->synthTypeAndValueId();
// If we have to force the same type for the initial and final
// aggregate expressions, then force the type of the sum to
// be the same as the type of the count.
if (sameFormat)
result->getValueId().changeType(&(result->child(0)->
getValueId().getType()));
// execute the nested aggregate function in the initial set
initialAggrs.insert(result->child(0)->getValueId());
finalAggrs.insert(result->getValueId());
break;
case ITM_MAX:
case ITM_MIN:
case ITM_SUM:
case ITM_ANY_TRUE:
case ITM_ONEROW:
case ITM_GROUPING:
// in these cases, just do the same aggregate function twice
partial = new (CmpCommon::statementHeap())
Aggregate(getOperatorType(), child(0));
result = new (CmpCommon::statementHeap())
Aggregate(getOperatorType(), partial);
if (getOperatorType() == ITM_GROUPING)
{
((Aggregate *)partial)->setRollupGroupIndex(getRollupGroupIndex());
((Aggregate *)result)->setRollupGroupIndex(getRollupGroupIndex());
}
if (inScalarGroupBy())
{
partial->setInScalarGroupBy();
((Aggregate *)result)->setInScalarGroupBy();
}
((Aggregate *)result)->setTopPartOfAggr();
// fix case 10-081203-5622, soln 10-081203-7701 by preserving this'
// "treatAsACount_ & amTopPartOfAggr_" attribute settings.
if (treatAsACount()) {
partial->setTreatAsACount();
((Aggregate*)result)->setTreatAsACount();
}
if (topPartOfAggr()) {
partial->setTopPartOfAggr();
}
result->synthTypeAndValueId();
// If we have to force the same type for the initial and final
// aggregate expressions, then force the type of the sum to
// be the same as the type of the sum/min/max etc...
if (sameFormat)
result->getValueId().changeType(&(result->child(0)->
getValueId().getType()));
// execute the nested aggregate function in the initial set
initialAggrs.insert(result->child(0)->getValueId());
finalAggrs.insert(result->getValueId());
break;
case ITM_ONE_TRUE:
partial = new (CmpCommon::statementHeap())
Aggregate(getOperatorType(), child(0));
result = new (CmpCommon::statementHeap())
Aggregate(ITM_ANY_TRUE, partial);
if (inScalarGroupBy())
{
partial->setInScalarGroupBy();
((Aggregate *)result)->setInScalarGroupBy();
}
result->synthTypeAndValueId();
// If we have to force the same type for the initial and final
// aggregate expressions, then force the type of the sum to
// be the same as the type of the sum/min/max etc...
if (sameFormat)
result->getValueId().changeType(&(result->child(0)->
getValueId().getType()));
// execute the nested aggregate function in the initial set
initialAggrs.insert(result->child(0)->getValueId());
finalAggrs.insert(result->getValueId());
break;
case ITM_ONE_ROW:
// need another aggregate function that handles the case where
// some servers return NULL rows
ABORT("sorry, one row aggregates cannot be implemented in a staged fashion");
break;
default:
ABORT("unknown aggregate function encountered");
}
return result;
}
ItemExpr * PivotGroup::rewriteForStagedEvaluation(ValueIdList &initialAggrs,
ValueIdList &finalAggrs,
NABoolean sameFormat)
{
// Split the aggregate function into three parts: the initial part is
// executed in multiple groupby nodes whose results are sent to a
// single, "final" groupby node that executes the final aggregate expression.
// More than one aggregate function may be evaluated in the initial and/or
// final nodes, therefore a set parameter is used. The return value is
// an item expression equivalent to the original expression "this". The
// return value is not necessarily an aggregate function, while
// initialAggr and finalAggr contain only aggregate functions.
// NOTE: this method doesn't delete the existing aggregate node, and it
// produces new value ids for all three parts returned.
ItemExpr *result = NULL;
Aggregate *partial;
partial = new (CmpCommon::statementHeap())
PivotGroup(getOperatorType(), child(0), pivotOptionsList_, isDistinct());
result = new (CmpCommon::statementHeap())
PivotGroup(ITM_PIVOT_GROUP, partial, NULL);
if (inScalarGroupBy())
{
partial->setInScalarGroupBy();
((Aggregate *)result)->setInScalarGroupBy();
}
result->synthTypeAndValueId();
// If we have to force the same type for the initial and final
// aggregate expressions, then force the type of the sum to
// be the same as the type of the sum/min/max etc...
if (sameFormat)
result->getValueId().changeType(&(result->child(0)->
getValueId().getType()));
// execute the nested aggregate function in the initial set
initialAggrs.insert(result->child(0)->getValueId());
finalAggrs.insert(result->getValueId());
return result;
}
// Aggregate::isEquivalentForBinding Determine if these two
// ItemExprs are equivalent. If so, one may be eliminated and
// replaced with the other.
// Inputs:
// ItemExpr *other - the ItemExpr being compared to 'this'
//
// Outputs: return value: Returns TRUE if the 'other' ItemExpr is
// equivalent to 'this'. In order to be equivalent, they must:
// - Have equivalent children
// (determined by 'hasBaseEquivalenceForCodeGeneration()
// - Be the same aggregate. (same operatorType)
// (determined by 'hasBaseEquivalenceForCodeGeneration()
// - have the same distinct settings and if distinct, must both
// be distinct with respect to the immediate child. This could
// prevent otherwise equivalent distinct STDDEV and VARIANCE aggregates
// from being eliminated.
//
NABoolean Aggregate::isEquivalentForBinding(const ItemExpr * other)
{
// Make sure that the children are equivalent and that this and
// other are the same operator
//
if (hasBaseEquivalenceForCodeGeneration(other))
{
// we know that other is an Aggregate, its operator type is the same,
// and that the children are equivalent
Aggregate * otherAggregate = (Aggregate *)other;
// Both must have the same distinct settings.
//
if(isDistinct() != otherAggregate->isDistinct()) {
return FALSE;
}
// Both must have the same inScalarGroupBy settings.
//
if(inScalarGroupBy() != otherAggregate->inScalarGroupBy()) {
return FALSE;
}
// If both are distinct (sufficient to check one since we know
// both are the same).
//
if(isDistinct()) {
// If they are distinct, both must be distinct on the
// immediate child. VARIANCE and STDDEV are transformed into
// a more complicated expression and the distinct valueid will
// not be the immediate child. So this means that the
// distinct versions of VARIANCE and STDDEV will not be
// considered for elimination even though they may qualify.
// Furthermore, the 'this' version of a distinct VARIANCE or
// STDDEV will not have its distinctID set properly until
// after this routine is called (but the 'other' one will).
//
if(getDistinctValueId() != child(0)->getValueId()) {
return FALSE;
}
if(otherAggregate->getDistinctValueId() !=
otherAggregate->child(0)->getValueId()) {
return FALSE;
}
}
// the two values are equivalent.
return TRUE;
}
return FALSE;
}
// Return the equivalent running sequence function operator type
//
OperatorTypeEnum Aggregate::mapOperTypeToRunning() const
{
switch(getOperatorType()) {
case ITM_AVG:
return ITM_RUNNING_AVG;
break;
case ITM_COUNT:
return ITM_RUNNING_COUNT;
break;
case ITM_COUNT_NONULL:
return ITM_RUNNING_COUNT;
break;
case ITM_MAX:
return ITM_RUNNING_MAX;
break;
case ITM_MIN:
return ITM_RUNNING_MIN;
break;
case ITM_STDDEV:
return ITM_RUNNING_SDEV;
break;
case ITM_SUM:
return ITM_RUNNING_SUM;
break;
case ITM_VARIANCE:
return ITM_RUNNING_VARIANCE;
break;
case ITM_RUNNING_RANK:
return ITM_RUNNING_RANK;
break;
case ITM_RUNNING_DRANK:
return ITM_RUNNING_DRANK;
break;
default:
return INVALID_OPERATOR_TYPE;
}
}
OperatorTypeEnum Aggregate::mapOperTypeToOlap() const
{
switch(getOperatorType()) {
case ITM_AVG:
return ITM_OLAP_AVG;
break;
case ITM_COUNT:
return ITM_OLAP_COUNT;
break;
case ITM_COUNT_NONULL:
return ITM_OLAP_COUNT;
break;
case ITM_MAX:
return ITM_OLAP_MAX;
break;
case ITM_MIN:
return ITM_OLAP_MIN;
break;
case ITM_STDDEV:
return ITM_OLAP_SDEV;
break;
case ITM_SUM:
return ITM_OLAP_SUM;
break;
case ITM_VARIANCE:
return ITM_OLAP_VARIANCE;
break;
case ITM_RUNNING_RANK:
return ITM_OLAP_RANK;
break;
case ITM_RUNNING_DRANK:
return ITM_OLAP_DRANK;
break;
default:
return INVALID_OPERATOR_TYPE;
}
}
// Return the equivalent moving sequence function operator type
//
OperatorTypeEnum Aggregate::mapOperTypeToMoving() const
{
switch(getOperatorType()) {
case ITM_AVG:
return ITM_MOVING_AVG;
break;
case ITM_COUNT:
return ITM_MOVING_COUNT;
break;
case ITM_COUNT_NONULL:
return ITM_MOVING_COUNT;
break;
case ITM_MAX:
return ITM_MOVING_MAX;
break;
case ITM_MIN:
return ITM_MOVING_MIN;
break;
case ITM_STDDEV:
return ITM_MOVING_SDEV;
break;
case ITM_SUM:
return ITM_MOVING_SUM;
break;
case ITM_VARIANCE:
return ITM_MOVING_VARIANCE;
break;
case ITM_RUNNING_RANK:
return ITM_MOVING_RANK;
break;
case ITM_RUNNING_DRANK:
return ITM_MOVING_DRANK;
break;
default:
return INVALID_OPERATOR_TYPE;
}
}
OperatorTypeEnum ItmSeqOlapFunction::mapOperTypeToRunning() const
{
switch(getOperatorType()) {
case ITM_OLAP_AVG:
return ITM_RUNNING_AVG;
break;
case ITM_OLAP_COUNT:
return ITM_RUNNING_COUNT;
break;
case ITM_OLAP_MAX:
return ITM_RUNNING_MAX;
break;
case ITM_OLAP_MIN:
return ITM_RUNNING_MIN;
break;
case ITM_OLAP_SDEV:
return ITM_RUNNING_SDEV;
break;
case ITM_OLAP_SUM:
return ITM_RUNNING_SUM;
break;
case ITM_OLAP_VARIANCE:
return ITM_RUNNING_VARIANCE;
break;
case ITM_OLAP_RANK:
return ITM_RUNNING_RANK;
break;
case ITM_OLAP_DRANK:
return ITM_RUNNING_DRANK;
break;
default:
return INVALID_OPERATOR_TYPE;
}
}
OperatorTypeEnum ItmSeqOlapFunction::mapOperTypeToMoving() const
{
switch(getOperatorType()) {
case ITM_OLAP_AVG:
return ITM_MOVING_AVG;
break;
case ITM_OLAP_COUNT:
return ITM_MOVING_COUNT;
break;
case ITM_OLAP_MAX:
return ITM_MOVING_MAX;
break;
case ITM_OLAP_MIN:
return ITM_MOVING_MIN;
break;
case ITM_OLAP_SDEV:
return ITM_MOVING_SDEV;
break;
case ITM_OLAP_SUM:
return ITM_MOVING_SUM;
break;
case ITM_OLAP_VARIANCE:
return ITM_MOVING_VARIANCE;
break;
case ITM_OLAP_RANK:
return ITM_MOVING_RANK;
break;
case ITM_OLAP_DRANK:
return ITM_MOVING_DRANK;
break;
default:
return INVALID_OPERATOR_TYPE;
}
}
// transformOlapFunction.
// Transform OLAP Window aggregate functions into their equivalent
// sequence functions.
//
// Also verifies that all OLAP Window function are using the same Window
// specification (partition by and order by). If not, an error message
// is put into the diags area.
//
// Inputs - this - the Aggregate with OLAP information
// (partition by and order by)
//
// bindWA - Used to get current scope for error handling
// Returns - transformed expression.
// - NULL on error
//
ItemExpr *Aggregate::transformOlapFunction(BindWA *bindWA)
{
if(NOT isOLAP_) {
// Not an OLAP aggregate.
return this;
}
// If this is an illegal Frame Specification, then issue an error.
//
if ((frameStart_ > frameEnd_) ||
(isFrameStartUnboundedFollowing()) || // frameStart_ == INT_MAX) ||
(isFrameEndUnboundedPreceding())) //frameEnd_ == -INT_MAX ))
{
*CmpCommon::diags() << DgSqlCode(-4342);
bindWA->setErrStatus();
return NULL;
}
if (!olapOrderBy_ && ( getOperatorType() == ITM_RUNNING_RANK || getOperatorType() == ITM_RUNNING_DRANK))
{//The use of RANK or DENSE_RANK window functions without a window ORDER BY clause is not supported.
*CmpCommon::diags() << DgSqlCode(-4344);
bindWA->setErrStatus();
return NULL;
}
// Distinct is not supported for Window Functions.
//
if (this->isDistinct_)
{
*CmpCommon::diags() << DgSqlCode(-4341);
bindWA->setErrStatus();
return NULL;
}
BindScope *currScope = bindWA->getCurrentScope();
// OLAP Window functions can only be in the select list
//
if (! currScope->context()->inSelectList())
{
*CmpCommon::diags() << DgSqlCode(-4346);
bindWA->setErrStatus();
return NULL;
}
if (currScope->context()->inAggregate())
{
*CmpCommon::diags() << DgSqlCode(-4375) << DgString0(getTextUpper());
bindWA->setErrStatus();
return NULL;
}
ValueIdList partition_vil, order_vil;
ItemExpr * tmpItemExpr = NULL;
// Verify that all Window Functions within this scope, use the
// same Window Specification (partition by and order by)
//
// The Sequence functions are bound in the environment (RETDesc) of
// the child of the OLAP Sequence if one exists
//
RelExpr *sequenceNode = currScope->getSequenceNode();
RETDesc *currentRETDesc = currScope->getRETDesc();
currScope->setRETDesc(sequenceNode->child(0)->getRETDesc());
if (olapPartitionBy_)
{
currScope ->context()->inOrderBy() = TRUE;
currScope ->context()->inOlapPartitionBy() = TRUE;
olapPartitionBy_->convertToValueIdList(partition_vil, bindWA, ITM_ITEM_LIST);
currScope ->context()->inOlapPartitionBy() = FALSE;
currScope ->context()->inOrderBy() = FALSE;
if (bindWA->errStatus())
return NULL;
}
if (olapOrderBy_)
{
currScope->context()->inOtherSequenceFunction() = TRUE;
currScope ->context()->inOrderBy() = TRUE;
currScope ->context()->inOlapOrderBy() = TRUE;
olapOrderBy_->convertToValueIdList(order_vil, bindWA, ITM_ITEM_LIST);
currScope ->context()->inOlapOrderBy() = FALSE;
currScope ->context()->inOrderBy() = FALSE;
currScope->context()->inOtherSequenceFunction() = FALSE;
if (bindWA->errStatus())
return NULL;
}
currScope->setRETDesc(currentRETDesc);
// If this is the first Window Function to be bound in this scope,
// remember the partition and order by lists.
//
if ( currScope->getIsFirstOlapWindowSpec() )
{
currScope->setOlapPartition( partition_vil );
currScope->setOlapOrder( order_vil );
currScope->setIsFirstOlapWindowSpec ( FALSE );
}
else
{
// Check to see if the partition by and order by for this Window
// Function is the same as all the others we have bound in this
// scope so far.
//
NABoolean olap = TRUE;
if (partition_vil.entries() != currScope->getOlapPartition().entries() ||
order_vil.entries() != currScope->getOlapOrder().entries())
{
olap = FALSE;
}
for(CollIndex i = 0; olap && i < partition_vil.entries(); i++)
{
ItemExpr *ie = currScope->getOlapPartition()[i].getItemExpr();
ItemExpr *otherIe = partition_vil[i].getItemExpr();
if(!ie->hasBaseEquivalence(otherIe))
{
olap = FALSE;
}
}
for(CollIndex i = 0; olap && i < order_vil.entries(); i++)
{
ValueId vid1, vid2;
vid1 = currScope->getOlapOrder()[i];
vid2 = order_vil[i];
if (order_vil[i].getItemExpr()->getOperatorType() == ITM_INVERSE &&
currScope->getOlapOrder()[i].getItemExpr()->getOperatorType() == ITM_INVERSE)
{
vid1 = currScope->getOlapOrder()[i].getItemExpr()->child(0).getValueId();
vid2 = order_vil[i].getItemExpr()->child(0).getValueId();
}
ItemExpr *ie = vid1.getItemExpr();
ItemExpr *otherIe = vid2.getItemExpr();
if( !ie->hasBaseEquivalence(otherIe))
{
olap = FALSE;
}
}
if (! olap)
{
// The Window Specification for this Window Function is not
// the same as the others we have bound so far in this scope.
//
*CmpCommon::diags() << DgSqlCode(-4340);
bindWA->setErrStatus();
return NULL;
}
}
CollHeap *heap = CmpCommon::statementHeap();
OperatorTypeEnum op = mapOperTypeToOlap();
ItmSeqOlapFunction *seqFunc = new (heap)
ItmSeqOlapFunction(op, child(0));
seqFunc->setOLAPInfo(olapPartitionBy_, olapOrderBy_);
seqFunc->setOlapWindowFrame(frameStart_, frameEnd_);
return seqFunc;
}
QR::ExprElement Aggregate::getQRExprElem() const
{
return QR::QRFunctionElem;
}
// -----------------------------------------------------------------------
// member functions for class Variance
// -----------------------------------------------------------------------
Variance::~Variance() {}
ItemExpr * Variance::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
Variance *result;
if (derivedNode == NULL)
result = new (outHeap) Variance(getOperatorType(), NULL, NULL, isDistinct());
else
result = (Variance *) derivedNode;
return Aggregate::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class PivotGroup
// -----------------------------------------------------------------------
PivotGroup::PivotGroup(OperatorTypeEnum otype,
ItemExpr *child0,
NAList<PivotOption*> * pivotOptionsList,
NABoolean isDistinct)
: Aggregate(otype, child0, NULL, isDistinct),
pivotOptionsList_(pivotOptionsList),
maxLen_(DEFAULT_MAX_LEN),
delim_(","),
orderBy_(FALSE)
{
if (pivotOptionsList)
{
for (CollIndex i = 0; i < pivotOptionsList->entries(); i++)
{
PivotOption * po = (*pivotOptionsList)[i];
switch (po->option_)
{
case DELIMITER_:
{
delim_ = *po->stringVal_;
}
break;
case MAX_LENGTH_:
{
maxLen_ = po->numericVal_;
}
break;
case ORDER_BY_:
{
orderBy_ = TRUE;
// optionNode_ contains the ItemExpr
orgReqOrder_ = (ItemExpr *)po->optionNode_;
}
break;
}
}
}
}
PivotGroup::~PivotGroup() {}
ItemExpr * PivotGroup::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
PivotGroup *result;
if (derivedNode == NULL)
result = new (outHeap) PivotGroup(getOperatorType(), NULL, NULL, isDistinct());
else
result = (PivotGroup *) derivedNode;
result->pivotOptionsList_ = pivotOptionsList_;
result->delim_ = delim_;
result->orderBy_ = orderBy_;
result->reqdOrder_ = reqdOrder_;
result->orgReqOrder_ = orgReqOrder_;
result->maxLen_ = maxLen_;
return Aggregate::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class Function
// -----------------------------------------------------------------------
Function::Function(OperatorTypeEnum otype,
NAMemory *h,
Lng32 argumentCount,
ItemExpr *child0,
ItemExpr *child1,
ItemExpr *child2,
ItemExpr *child3,
ItemExpr *child4,
ItemExpr *child5)
: ItemExpr(otype),
children_(h,argumentCount),
allowsSQLnullArg_(TRUE)
{
Lng32 lastInserted = -1;
ItemExpr *childx;
for (Lng32 i = 0; i < (Lng32)argumentCount; i++)
{
childx = IFX i==0
THENX child0
ELSEX IFX i==1
THENX child1
ELSEX IFX i==2
THENX child2
ELSEX IFX i==3
THENX child3
ELSEX IFX i==4
THENX child4
ELSEX IFX i==5
THENX child5
ELSEX (ItemExpr *) NULL;
CMPASSERT((Lng32)children_.entries() == i);
children_.insertAt(i, childx);
} // end for
}
Function::Function(OperatorTypeEnum otype, const LIST(ItemExpr *) &children,
CollHeap *h)
: ItemExpr(otype),
children_(h)
{
Lng32 ne = children.entries();
for (Lng32 i = 0; i < ne; i++)
{
children_.insertAt(i,children[i]);
}
}
Function::~Function() {}
Lng32 Function::getNumChildren() const
{
Lng32 count = children_.entries();
// $$$$ Skip all the NULL children at the tail end.
// $$$$ Assumes children that are missing in the middle
// $$$$ should figure in the count, e.g., F(a, NULL, b, NULL, NULL)
while ( (count > 0) AND (children_[count-1].getPtr() == NULL) )
count--;
return count;
}
ItemExpr * Function::copyTopNode(ItemExpr * derivedNode, CollHeap* outHeap)
{
Function *result = NULL;
if (derivedNode == NULL)
ABORT("copyTopNode() can only be called for a derived class of Function");
else
result = (Function *)derivedNode;
result->allowsSQLnullArg() = allowsSQLnullArg();
// Make sure we copy the kids as well.
Lng32 ne = children_.entries();
for (Lng32 i = 0; i < ne; i++)
result->children_.insertAt(i, children_[i]);
return ItemExpr::copyTopNode(result, outHeap);
}
ExprValueId & Function::operator[] (Lng32 index)
{
CMPASSERT( (index >= 0) AND (index < (Lng32)children_.entries()) );
return children_[index];
}
const ExprValueId & Function::operator[] (Lng32 index) const
{
CMPASSERT( (index >= 0) AND (index < (Lng32)children_.entries()) );
return children_[index];
}
// -----------------------------------------------------------------------
// member functions for class BuiltinFunction
// -----------------------------------------------------------------------
BuiltinFunction::BuiltinFunction(OperatorTypeEnum otype,
NAMemory *h,
Lng32 argumentCount,
ItemExpr *child0,
ItemExpr *child1,
ItemExpr *child2,
ItemExpr *child3,
ItemExpr *child4,
ItemExpr *child5)
: Function(otype,h,argumentCount,child0,child1,child2,child3,child4,child5)
{
switch (getOperatorType())
{
case ITM_NULLIFZERO:
case ITM_QUERYID_EXTRACT:
case ITM_TOKENSTR:
case ITM_REVERSE: {
allowsSQLnullArg() = FALSE;
}
break;
default:
{
}
break;
}
}
BuiltinFunction::~BuiltinFunction() {}
Int32 BuiltinFunction::getArity() const
{
return getNumChildren();
}
// -----------------------------------------------------------------------
// BuiltinFunction::isCovered()
// -----------------------------------------------------------------------
NABoolean BuiltinFunction::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ITM_CURRENT_USER function should appear as an input characteristic
// of the root for it to be evaluated by the executor. And hence it
// needs to be treated differently from other BuiltinFunctions. For
// it to be propogated to the root, it should not be covered.
// Case 10-010419-2366
if (isAUserSuppliedInput()) // for user(x), current_timestamp, extract (fix)
return FALSE;
// A BuiltinFunction with no arguments (children)
// is like a constant. It is always covered.
Lng32 nc = getNumChildren();
if (nc == 0)
return TRUE;
// ---------------------------------------------------------------------
// A BuiltinFunction can contain values that are produced by
// different sources. The BuiltinFunction is covered if each
// operand is covered.
// The coverage test insists on a complete coverage of all the
// children.
// ---------------------------------------------------------------------
ValueIdSet childValues;
for (Int32 i = 0; i < nc; i++)
childValues += child(i).getValueId();
return childValues.isCovered(newExternalInputs,
coveringGA,
referencedInputs,
coveredSubExpr,
unCoveredExpr);
} // BuiltinFunction::isCovered()
NABoolean BuiltinFunction::isCacheableExpr(CacheWA& cwa)
{
switch (getOperatorType())
{
case ITM_NULLIFZERO:
case ITM_QUERYID_EXTRACT:
case ITM_TOKENSTR:
{
return ItemExpr::isCacheableExpr(cwa);
}
break;
case ITM_NVL:
case ITM_REVERSE:
{
return FALSE;
}
break;
case ITM_JSONOBJECTFIELDTEXT:
{
return FALSE;
}
break;
default:
{
return Function::isCacheableExpr(cwa);
}
break;
}
}
const NAString BuiltinFunction::getText() const
{
NABoolean dumpMvMode = (CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) == DF_DUMP_MV);
switch (getOperatorType())
{
case ITM_ABS:
return "abs";
case ITM_ASCII:
return "ascii";
case ITM_AUTHNAME:
return "authname";
case ITM_AUTHTYPE:
return "authtype";
case ITM_BETWEEN:
return "between";
case ITM_BLOCK:
return "block";
case ITM_BOOL_RESULT:
return "bool_result";
case ITM_CASE:
return "case";
case ITM_CAST:
return "cast";
case ITM_CAST_CONVERT:
return "cast_convert";
case ITM_CAST_TYPE:
return "typecast";
case ITM_CHAR:
return "char";
case ITM_CHAR_LENGTH:
return "char_length";
case ITM_COALESCE:
return "coalesce";
case ITM_COMP_ENCODE:
return "comp_encode";
case ITM_COMP_DECODE:
return "comp_decode";
case ITM_CONCAT:
return "||"; // "concat";
case ITM_CONVERTFROMHEX:
return "CONVERTFROMHEX";
case ITM_CONVERTTOHEX:
return "CONVERTTOHEX";
case ITM_CONVERTTOBITS:
return "CONVERTTOBITS";
case ITM_CONVERTTIMESTAMP:
return "converttimestamp";
case ITM_SLEEP:
return "sleep";
case ITM_UNIX_TIMESTAMP:
return "unix_timestamp";
case ITM_CURRENT_TIMESTAMP:
return "current_timestamp";
case ITM_CURRENT_TIMESTAMP_RUNNING:
return "current_timestamp_running";
case ITM_CURRENT_USER:
return "current_user";
case ITM_DATEFORMAT:
return "dateformat";
case ITM_DAYOFMONTH:
return "dayofmonth";
case ITM_DAYOFWEEK:
return "dayofweek";
case ITM_DO_WHILE:
return "do while";
case ITM_WHILE:
return "while";
case ITM_EXPLODE_VARCHAR:
return "explodevarchar";
case ITM_EXTRACT:
return "extract";
case ITM_EXTRACT_ODBC:
return "extract_odbc";
case ITM_GREATEST:
return "greatest";
case ITM_LEAST:
return "least";
case ITM_IN:
return "in";
case ITM_INSTANTIATE_NULL:
// getenv() calls are costly. avoid especially in release code.
#ifndef NDEBUG
if (getenv("SIMPLE_DISPLAY")) return "iNull";
#endif
return "instantiate_null";
case ITM_JULIANTIMESTAMP:
return "juliantimestamp";
case ITM_EXEC_COUNT:
return "execution_count";
case ITM_CURR_TRANSID:
return "current_transid";
case ITM_LIKE:
case ITM_LIKE_DOUBLEBYTE:
return "like";
case ITM_REGEXP:
return "regexp";
case ITM_LOWER:
case ITM_LOWER_UNICODE:
return "lower";
case ITM_NARROW:
return "narrow";
case ITM_NULLIFZERO:
return "nullifzero";
case ITM_NVL:
return "nvl";
case ITM_JSONOBJECTFIELDTEXT:
return "json_object_field_text";
case ITM_QUERYID_EXTRACT:
return "queryid_extract";
case ITM_UPPER:
case ITM_UPPER_UNICODE:
return "upper";
case ITM_UNICODE_CHAR:
return "unicode_char";
case ITM_NO_OP:
return "no_op";
case ITM_POSITION:
return "position";
case ITM_REPEAT:
return "repeat";
case ITM_REPLACE:
return "replace";
case ITM_REPLACE_NULL:
return "replace null";
case ITM_RETURN_TRUE:
if (dumpMvMode)
return "1";
else
return "return_true";
case ITM_RETURN_FALSE:
if (dumpMvMode)
return "0";
else
return "return_false";
case ITM_RETURN_NULL:
if (dumpMvMode)
return "null";
else
return "return_unknown";
case ITM_SESSION_USER:
return "session_user";
case ITM_OCTET_LENGTH:
return "octet_length";
case ITM_HASH:
return "hash";
case ITM_HASH2_DISTRIB:
return "hash2_distrib";
case ITM_MOD:
return "mod";
case ITM_INVERSE:
return "inverse";
case ITM_SUBSTR:
case ITM_SUBSTR_DOUBLEBYTE:
return "substring";
case ITM_TRANSLATE:
return "translate";
case ITM_TRIM:
case ITM_TRIM_DOUBLEBYTE:
return "trim";
case ITM_IF_THEN_ELSE:
return "if_then_else";
case ITM_LESS_OR_LE:
return "< or <=";
case ITM_GREATER_OR_GE:
return "> or >=";
case ITM_RANGE_LOOKUP:
return "range_lookup";
case ITM_RANDOMNUM:
return "randomNum";
case ITM_RAND_SELECTION:
return "randomSelection";
case ITM_ROUND_ROBIN:
return "Round_Robin";
case ITM_PACK_FUNC:
return "pack";
case ITM_SAMPLE_VALUE:
return "sample_size";
case ITM_UNIQUE_SHORT_ID:
return "unique_short_id";
case ITM_UNIQUE_ID:
return "unique_id";
case ITM_HBASE_COLUMN_LOOKUP:
return "hbase_column_lookup";
case ITM_HBASE_COLUMNS_DISPLAY:
return "hbase_columns_display";
case ITM_HBASE_COLUMN_CREATE:
return "hbase_column_create";
case ITM_SEQUENCE_VALUE:
return "seqnum";
case ITM_ROWNUM:
return "rownum";
case ITM_USER:
return "user";
case ITM_UNIQUE_EXECUTE_ID:
return "unique_execute_id";
case ITM_GET_TRIGGERS_STATUS:
return "get_triggers_status";
case ITM_GET_BIT_VALUE_AT:
return "get_bit_value_at";
case ITM_IS_BITWISE_AND_TRUE:
return "is_bitwise_and_true";
case ITM_USERID:
return "os_userid";
case ITM_CURRENTEPOCH:
return "current_epoch";
case ITM_VSBBROWTYPE:
return "vsbb_row_type";
case ITM_VSBBROWCOUNT:
return "vsbb_row_count";
case ITM_INTERNALTIMESTAMP:
return "internal_timestamp";
case ITM_SCALAR_MIN:
return "scalar_min";
case ITM_SCALAR_MAX:
return "scalar_max";
case ITM_TOKENSTR:
return "TOKENSTR";
case ITM_REVERSE:
return "REVERSE";
// ZZZBinderFunction classes (for error messages only)
case ITM_DATE_TRUNC_YEAR:
case ITM_DATE_TRUNC_MONTH:
case ITM_DATE_TRUNC_DAY:
case ITM_DATE_TRUNC_HOUR:
case ITM_DATE_TRUNC_MINUTE:
case ITM_DATE_TRUNC_SECOND:
case ITM_DATE_TRUNC_CENTURY:
case ITM_DATE_TRUNC_DECADE:
return "date_trunc";
case ITM_DATEDIFF_YEAR:
case ITM_DATEDIFF_MONTH:
case ITM_DATEDIFF_DAY:
case ITM_DATEDIFF_HOUR:
case ITM_DATEDIFF_MINUTE:
case ITM_DATEDIFF_SECOND:
case ITM_DATEDIFF_QUARTER:
case ITM_DATEDIFF_WEEK:
return "datediff";
case ITM_TSI_YEAR:
case ITM_TSI_MONTH:
case ITM_TSI_DAY:
case ITM_TSI_HOUR:
case ITM_TSI_MINUTE:
case ITM_TSI_SECOND:
case ITM_TSI_QUARTER:
case ITM_TSI_WEEK:
return "timestampdiff";
case ITM_DAYNAME:
return "dayname";
case ITM_DAYOFYEAR:
return "dayofyear";
case ITM_DECODE:
return "decode";
case ITM_FIRSTDAYOFYEAR:
return "firstdayofyear";
case ITM_LAST_DAY:
return "last_day";
case ITM_NEXT_DAY:
return "next_day";
case ITM_INSERT_STR:
return "insert";
case ITM_LEFT:
return "left";
case ITM_LPAD:
return "lpad";
case ITM_MONTHNAME:
return "monthname";
case ITM_NULLIF:
return "nullif";
case ITM_ODBC_LENGTH:
return "LENGTH";
case ITM_QUARTER:
return "quarter";
case ITM_RIGHT:
return "right";
case ITM_RPAD:
return "rpad";
case ITM_SIGN:
return "sign";
case ITM_SPACE:
return "space";
case ITM_CODE_VALUE:
case ITM_UNICODE_CODE_VALUE:
case ITM_NCHAR_MP_CODE_VALUE:
return "code_value";
case ITM_WEEK:
return "week";
case ITM_ZEROIFNULL:
return "zeroifnull";
case ITM_LOBINSERT:
return "lobinsert";
case ITM_LOBSELECT:
return "lobselect";
case ITM_LOBDELETE:
return "lobdelete";
case ITM_LOBUPDATE:
return "lobupdate";
case ITM_LOBCONVERTHANDLE:
return "lobconverthandle";
case ITM_LOBCONVERT:
return "lobconvert";
case ITM_LOBLOAD:
return "lobload";
case ITM_AGGR_GROUPING_FUNC:
return "aggr_grouping";
case ITM_TO_TIMESTAMP:
return "to_timestamp";
default:
return "unknown func";
} // switch
} // BuiltinFunction::getText()
//## Yuk -- embedded English text -- this is not per I18N standards!
const NAString BuiltinFunction::getTextForError() const
{
switch (getOperatorType())
{
case ITM_CHAR_LENGTH:
return "CHARACTER_LENGTH, CHAR_LENGTH, or LENGTH";
case ITM_LOWER:
return "LOWER or LCASE";
case ITM_POSITION:
return "POSITION or LOCATE";
case ITM_TRIM:
case ITM_TRIM_DOUBLEBYTE:
return "TRIM, LTRIM, or RTRIM";
case ITM_UPPER:
return "UPPER, UPSHIFT, or UCASE";
default: return getTextUpper();
} // switch
} // BuiltinFunction::getTextForError()
ItemExpr * BuiltinFunction::copyTopNode(ItemExpr * derivedNode,
CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
switch (getOperatorType())
{
case ITM_NULLIFZERO:
case ITM_ISIPV4:
case ITM_ISIPV6:
case ITM_MD5:
case ITM_CRC32:
case ITM_SOUNDEX:
case ITM_REVERSE:
{
result = new (outHeap) BuiltinFunction(getOperatorType(),
outHeap, 1, child(0));
}
break;
case ITM_NVL:
case ITM_QUERYID_EXTRACT:
case ITM_TOKENSTR:
{
result = new (outHeap) BuiltinFunction(getOperatorType(),
outHeap, 2, child(0), child(1));
}
break;
case ITM_JSONOBJECTFIELDTEXT:
{
result = new (outHeap) BuiltinFunction(getOperatorType(),
outHeap, 2, child(0), child(1));
}
break;
default:
{
ABORT("copyTopNode() can only be called for a derived class of BuiltinFunction");
}
break;
}
}
else
result = derivedNode;
return Function::copyTopNode(result, outHeap);
}
ItemExpr * HashCommon::copyTopNode(ItemExpr * derivedNode,
CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
ABORT("copyTopNode() can only be called for a derived class of Function");
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
//++Triggers,
// -----------------------------------------------------------------------
// member functions for class EvaluateOnceBuiltinFunction
// -----------------------------------------------------------------------
EvaluateOnceBuiltinFunction::~EvaluateOnceBuiltinFunction() {}
NABoolean EvaluateOnceBuiltinFunction::isAUserSuppliedInput() const { return TRUE; }
NABoolean EvaluateOnceBuiltinFunction::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
return FALSE;
}
//--Triggers,
// -----------------------------------------------------------------------
// member functions for builtin functions
// -----------------------------------------------------------------------
Between::~Between()
{
// NOTE: never destroy pDirectionVector_.
// this is a pointer passed on to other objects as is.
// if it is destroyed this may affect other items.
}
Concat::~Concat() {}
Case::~Case() {}
IfThenElse::~IfThenElse() {}
InstantiateNull::~InstantiateNull() {}
Hash::~Hash() {}
ReplaceNull::~ReplaceNull() {}
// -----------------------------------------------------------------------
// member functions for BoolVal
// -----------------------------------------------------------------------
BoolVal::~BoolVal() {}
NABoolean BoolVal::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr ) const
{
// A BoolVal that returns a TRUE/FALSE/NULL unconditionally, i.e.,
// its evaluation does not depend upon the boolean outcome of its
// child subtree, is like a constant. It is always covered.
if (getNumChildren() == 0)
return TRUE;
else
// BoolVal is Covered if its operand is covered
return coveringGA.covers(((ItemExpr *)child(0))->getValueId(),
newExternalInputs, referencedInputs,
&coveredSubExpr, &unCoveredExpr);
} // BoolVal::isCovered()
// -----------------------------------------------------------------------
// member functions for InverseOrder
// -----------------------------------------------------------------------
InverseOrder::~InverseOrder() {}
ItemExpr * InverseOrder::simplifyOrderExpr(OrderComparison *newOrder)
{
OrderComparison resultOrder;
ItemExpr *result = child(0)->simplifyOrderExpr(&resultOrder);
// reverse the order of the simplified child
if (resultOrder == SAME_ORDER)
resultOrder = INVERSE_ORDER;
else
resultOrder = SAME_ORDER;
if (newOrder)
*newOrder = resultOrder;
return result;
}
ItemExpr * InverseOrder::removeInverseOrder()
{
return child(0);
}
// -----------------------------------------------------------------------
// member functions for PatternMatchingFunction.
// -----------------------------------------------------------------------
PatternMatchingFunction::~PatternMatchingFunction() {}
// -----------------------------------------------------------------------
// member functions for Like
// -----------------------------------------------------------------------
Like::~Like() {}
Regexp::~Regexp() {}
ItemExpr * Regexp::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
result = new (outHeap) Regexp(NULL, NULL,
numberOfNonWildcardChars_,
bytesInNonWildcardChars_,
patternAStringLiteral_,
oldDefaultSelForLikeWildCardUsed_,
beginEndKeysApplied_);
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Regexp::copyTopNode()
ItemExpr * Like::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
switch (getArity())
{
case 2:
result = new (outHeap) Like(child(0), child(1),
numberOfNonWildcardChars_,
bytesInNonWildcardChars_,
patternAStringLiteral_,
oldDefaultSelForLikeWildCardUsed_,
beginEndKeysApplied_);
break;
case 3:
result = new (outHeap) Like(child(0), child(1), child(2),
numberOfNonWildcardChars_,
bytesInNonWildcardChars_,
patternAStringLiteral_,
oldDefaultSelForLikeWildCardUsed_,
beginEndKeysApplied_);
break;
default:
CMPASSERT(0 == 1);
break;
}
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Like::copyTopNode()
double PatternMatchingFunction::defaultSel()
{
// if begin and end keys have been applied to this expression, then this means
// that the original LIKE predicate was something like a%b. This was transformed
// to >=a and < b and like %b. Here we would have already applied selectivity
// to the first range predicate. We do not want to apply selectivity to the other
// portions of the predicate. Hence return 1.0 for the last portion of the
// predicate
if ( beginEndKeysApplied( CmpCommon::statementHeap() ) )
return 1.0;
if (oldDefaultSelForLikeWildCardUsed_)
{
return ActiveSchemaDB()->getDefaults().getAsDouble(HIST_DEFAULT_SEL_FOR_LIKE_WILDCARD);
}
// For all other cases compute selectivity based on the number of non-wildcard
// characters
return computeSelForNonWildcardChars(); ;
}
void PatternMatchingFunction::setNumberOfNonWildcardChars(const LikePatternString &pattern)
{
Int32 count = 0;
Int32 byteCnt = 0;
CharInfo::CharSet cs = pattern.getPatternCharSet();
LikePatternStringIterator i(pattern);
while (i != LikePatternStringIterator::END_OF_PATTERN)
{
const char *currentChar = i.getCurrentChar();
UInt16 number_bytes = Attributes::getFirstCharLength(currentChar, 8, cs);
if (i == LikePatternStringIterator::NON_WILDCARD)
{
count++;
byteCnt += number_bytes;
}
i += number_bytes + ((cs == CharInfo::UCS2) ? 1 : 0); // For UCS2, number_bytes is always 1
i.determineCharType();
}
numberOfNonWildcardChars_ = count;
bytesInNonWildcardChars_ = byteCnt ;
}
double PatternMatchingFunction::computeSelForNonWildcardChars()
{
// get the default selectivity for like predicate
double defaultSelectivity = CURRSTMT_OPTDEFAULTS->defSelForWildCard();
// get the number of non_wildcard characters after the first wildcard char
Int32 cnt = getNoOfNonWildcardChars();
// Retuen default selectivity for the following cases:
// 1. For some special cases, example when there is a dynamic parameter in the
// LIKE predicate, the LIKE expression is not optimized. In all such cases
// cnt is less than 0. For these, return the default selectivity
// 2. if there are no non_wildcard characters in the like clause, then if the column
// is not nullable, we have already transformed the predicate to TRUE.
// For a nullable column, we apply default selectivity.
// 3. If there is only one non_wildcard character, we go by the selectivity
// defined by HIST_DEFAULT_BASE_SEL_FOR_LIKE_WILDCARD,
// 4. HIST_DEFAULT_SEL_FOR_LIKE_NO_WILDCARD is nearing 0 (e-16). We use default
// selectivity in that case.
double reductionFactor = CURRSTMT_OPTDEFAULTS->defSelForNoWildCard();
if ( (cnt <= 1) OR
(reductionFactor <= MIN_SELECTIVITY) )
return defaultSelectivity;
// We compute selectivity as: if number of non-wild characters is 1, selectivity
// is equal to default selectivity (which is 1/10 right now. If the number of
// non-wild characters is 2, selectivity is 1/100. For number of non-wild characters
// 3 or more, it is 1/1000. Multiplication factor each non-wildcard character is
// defined by the CQD HIST_DEFAULT_SEL_FOR_LIKE_NO_WILDCARD
Int32 nonWildCardChar = ((cnt > 4) ? 4 : cnt);
defaultSelectivity = defaultSelectivity * pow(defaultSelectivity / reductionFactor, nonWildCardChar - 1);
// Bind the selectivity between MIN_SELECTIVITY and 1.0
if (defaultSelectivity < MIN_SELECTIVITY)
return MIN_SELECTIVITY;
if (defaultSelectivity > 1.0)
return 1.0;
return defaultSelectivity;
}
NABoolean PatternMatchingFunction::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Like * tmp = (Like *) other;
return
(this->numberOfNonWildcardChars_ == tmp->numberOfNonWildcardChars_) &&
(this->bytesInNonWildcardChars_ == tmp->bytesInNonWildcardChars_) &&
(this->patternAStringLiteral_ == tmp->patternAStringLiteral_ ) &&
(this->oldDefaultSelForLikeWildCardUsed_ == tmp->oldDefaultSelForLikeWildCardUsed_) &&
(this->beginEndKeysApplied_ == tmp->beginEndKeysApplied_ );
}
void PatternMatchingFunction::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc* tabId) const
{
if (getArity() == 2)
return CacheableBuiltinFunction::unparse(result, phase, form, tabId);
else
{
result += "(";
child(0)->unparse(result, phase, form, tabId);
result += " like ";
child(1)->unparse(result, phase, form, tabId);
result += " escape ";
child(2)->unparse(result, phase, form, tabId);
result += ")";
}
}
// -----------------------------------------------------------------------
// member functions for class ConvertHex
// -----------------------------------------------------------------------
ConvertHex::~ConvertHex() {}
ItemExpr * ConvertHex::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ConvertHex(getOperatorType(), child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // ConvertHex::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class CharLength
// -----------------------------------------------------------------------
CharLength::~CharLength() {}
ItemExpr * CharLength::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CharLength(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // CharLength::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class OctetLength
// -----------------------------------------------------------------------
OctetLength::~OctetLength() {}
ItemExpr * OctetLength::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) OctetLength(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // OctetLength::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class PositionFunc
// -----------------------------------------------------------------------
PositionFunc::~PositionFunc() {}
ItemExpr * PositionFunc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) PositionFunc(child(0), child(1), child(2), child(3));
else
result = derivedNode;
((PositionFunc*)result)->collation_ = collation_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // PositionFunc::copyTopNode()
// -----------------------------------------------------------------------
// member functions for Substring
// -----------------------------------------------------------------------
Substring::~Substring() {}
ItemExpr * Substring::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
switch (getArity())
{
case 2:
result = new (outHeap) Substring(child(0), child(1));
break;
case 3:
result = new (outHeap) Substring(child(0), child(1), child(2));
break;
default:
CMPASSERT(0 == 1);
break;
}
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Substring::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class ConvertTimestamp
// -----------------------------------------------------------------------
ConvertTimestamp::~ConvertTimestamp() {}
ItemExpr * ConvertTimestamp::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ConvertTimestamp(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // ConvertTimestamp::copyTopNode()
SleepFunction::~SleepFunction() {}
ItemExpr * SleepFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) SleepFunction(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // SleepFunction::copyTopNode()
NABoolean SleepFunction::isAUserSuppliedInput() const { return TRUE; }
UnixTimestamp::~UnixTimestamp() {}
ItemExpr * UnixTimestamp::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) UnixTimestamp();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // UnixTimestamp::copyTopNode()
NABoolean UnixTimestamp::isAUserSuppliedInput() const { return TRUE; }
// -----------------------------------------------------------------------
// member functions for class CurrentTimestamp
// -----------------------------------------------------------------------
CurrentTimestamp::~CurrentTimestamp() {}
ItemExpr * CurrentTimestamp::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CurrentTimestamp(dtCode_, fractPrec_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // CurrentTimestamp::copyTopNode()
NABoolean CurrentTimestamp::isAUserSuppliedInput() const { return TRUE; }
ItemExpr * CurrentTimestamp::construct
(CollHeap * heap,
DatetimeType::Subtype dtCode ,
Lng32 fractPrec)
{
ItemExpr * ie = new(heap) CurrentTimestamp(dtCode, fractPrec);
if ((fractPrec != SQLTimestamp::DEFAULT_FRACTION_PRECISION) ||
(dtCode != DatetimeType::SUBTYPE_SQLTimestamp))
{
if (dtCode == DatetimeType::SUBTYPE_SQLDate)
ie = new (heap)
Cast(ie, new (heap) SQLDate(heap, FALSE));
else if (dtCode == DatetimeType::SUBTYPE_SQLTime)
ie = new (heap)
Cast(ie, new (heap) SQLTime(heap, FALSE, fractPrec));
else
ie = new (heap)
Cast(ie, new (heap) SQLTimestamp(heap, FALSE, fractPrec));
}
return ie;
}
// -----------------------------------------------------------------------
// member functions for class CurrentTimestampRunning
// -----------------------------------------------------------------------
CurrentTimestampRunning::~CurrentTimestampRunning() {}
ItemExpr * CurrentTimestampRunning::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CurrentTimestampRunning();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // CurrentTimestampRunning::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class DateFormat
// -----------------------------------------------------------------------
DateFormat::~DateFormat() {}
ItemExpr * DateFormat::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
DateFormat *result;
if (derivedNode == NULL)
result = new (outHeap) DateFormat(child(0),
formatStr_, formatType_,
wasDateformat_);
else
result = (DateFormat*)derivedNode;
result->frmt_ = frmt_;
result->dateFormat_ = dateFormat_;
return BuiltinFunction::copyTopNode(result,outHeap);
} // DateFormat::copyTopNode()
NABoolean DateFormat::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
DateFormat * df = (DateFormat *) other;
return
(this->dateFormat_ == df->dateFormat_);
}
void DateFormat::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
if (wasDateformat_)
result += "DATEFORMAT(";
else if (formatType_ == FORMAT_TO_DATE)
result += "TO_DATE(";
else if (formatType_ == FORMAT_TO_CHAR)
result += "TO_CHAR(";
else
result += "unknown(";
child(0)->unparse(result, phase, form, tabId);
result += ", ";
if (wasDateformat_)
{
if (frmt_ == ExpDatetime::DATETIME_FORMAT_DEFAULT)
result += "DEFAULT";
else if (frmt_ == ExpDatetime::DATETIME_FORMAT_USA)
result += "USA";
else if (frmt_ == ExpDatetime::DATETIME_FORMAT_EUROPEAN)
result += "EUROPEAN";
else
result += "unknown";
}
else
{
result += "'";
result += formatStr_;
result += "'";
}
result += ")";
}
// -----------------------------------------------------------------------
// member functions for class DayOfWeek
// -----------------------------------------------------------------------
DayOfWeek::~DayOfWeek() {}
ItemExpr * DayOfWeek::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) DayOfWeek(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // DayOfWeek::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class ExplodeVarchar
// -----------------------------------------------------------------------
ExplodeVarchar::~ExplodeVarchar() {}
ItemExpr * ExplodeVarchar::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ExplodeVarchar(child(0), type_, forInsert_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // ExplodeVarchar::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Extract
// -----------------------------------------------------------------------
Extract::~Extract() {}
NABoolean Extract::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
Extract &o = (Extract &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (extractField_ != o.extractField_)
return FALSE;
return TRUE;
}
ItemExpr * Extract::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Extract(getExtractField(), child(0), getFieldFunction());
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Extract::copyTopNode()
ItemExpr * ExtractOdbc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ExtractOdbc(getExtractField(), child(0), getFieldFunction());
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Extract::copyTopNode()
NABoolean Extract::isAUserSuppliedInput() const
{
ItemExpr * extractChild = NULL;
if ( child(0) && (child(0)->getOperatorType() == ITM_CAST) &&
child(0)->child(0) )
extractChild = child(0)->child(0)->castToItemExpr();
else if (child(0))
extractChild = child(0)->castToItemExpr();
if ((CmpCommon::getDefault(COMP_BOOL_185) == DF_ON) &&
(extractChild && extractChild->isAUserSuppliedInput()) &&
(NOT(extractChild->doesExprEvaluateToConstant(FALSE))))
return TRUE;
else
return FALSE;
} // Extract::isAUserSuppliedInput()
// ----------------------------------------------------------------------
// Walk through an ItemExpr tree and gather the ValueIds of those
// expressions that behave as if they are "leaves" for the sake of
// the coverage test. In this case it is expressions of the type
// extract(cast(timestamp))
// ----------------------------------------------------------------------
void Extract::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
if (isAUserSuppliedInput())
leafValues += getValueId();
else
BuiltinFunction::getLeafValuesForCoverTest(leafValues, coveringGA, newExternalInputs);
}
NABoolean Extract::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Extract * tmp = (Extract *) other;
return
(this->extractField_ == tmp->extractField_ ) &&
(this->fieldFunction_ == tmp->fieldFunction_);
}
QR::ExprElement Extract::getQRExprElem() const
{
return QR::QRFunctionWithParameters;
}
// -----------------------------------------------------------------------
// member functions for class JulianTimestamp
// -----------------------------------------------------------------------
JulianTimestamp::~JulianTimestamp() {}
ItemExpr * JulianTimestamp::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) JulianTimestamp(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // JulianTimestamp::copyTopNode()
// Fix for cr 10-010718-3967
// We dont need this method, the reason this method was added was to return
// true, the ItemExpr method already returns false, so we don't need this
// method. So basically we want FALSE to be returned for JulianTimestamp all
// the time.
// NABoolean JulianTimestamp::isAUserSuppliedInput() const { return TRUE; }
// -----------------------------------------------------------------------
// member functions for class StatementExecutionCount
// -----------------------------------------------------------------------
NABoolean StatementExecutionCount::isAUserSuppliedInput() const { return TRUE; }
ItemExpr * StatementExecutionCount::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) StatementExecutionCount();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
}
// -----------------------------------------------------------------------
// member functions for class CurrentTransId
// -----------------------------------------------------------------------
NABoolean CurrentTransId::isAUserSuppliedInput() const { return TRUE; }
ItemExpr * CurrentTransId::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CurrentTransId();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
}
// -----------------------------------------------------------------------
// member functions for class Upper
// -----------------------------------------------------------------------
Upper::~Upper() {}
ItemExpr * Upper::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Upper(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Upper::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Lower
// -----------------------------------------------------------------------
Lower::~Lower() {}
ItemExpr * Lower::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Lower(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Lower::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Trim
// -----------------------------------------------------------------------
Trim::~Trim() {}
ItemExpr * Trim::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Trim(getTrimMode(), child(0), child(1));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Trim::copyTopNode()
NABoolean Trim::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Trim * t = (Trim *) other;
return
(mode_ == t->mode_);
}
// -----------------------------------------------------------------------
// member functions for class Increment
// -----------------------------------------------------------------------
Increment::~Increment() {}
ItemExpr * Increment::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Increment(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Increment::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Decrement
// -----------------------------------------------------------------------
Decrement::~Decrement() {}
ItemExpr * Decrement::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Decrement(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Decrement::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class TriRelational
// -----------------------------------------------------------------------
TriRelational::TriRelational(OperatorTypeEnum optype,
ItemExpr *val1Ptr,
ItemExpr *val2Ptr,
ItemExpr *val3Ptr)
: BuiltinFunction(optype, CmpCommon::statementHeap(),
3, val1Ptr, val2Ptr, val3Ptr)
{
CMPASSERT(optype == ITM_LESS_OR_LE OR
optype == ITM_GREATER_OR_GE);
}
TriRelational::~TriRelational() {}
ItemExpr * TriRelational::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) TriRelational(getOperatorType(),NULL,NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for ScalarVariance
// -----------------------------------------------------------------------
ScalarVariance::~ScalarVariance() {}
ItemExpr *
ScalarVariance::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
CMPASSERT(getArity() == 3);
result = new (outHeap)
ScalarVariance(getOperatorType(), child(0), child(1), child(2));
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // ScalarVariance::copyTopNode()
// -----------------------------------------------------------------------
// member functions for UnPackCol
// -----------------------------------------------------------------------
//
UnPackCol::~UnPackCol() {}
ItemExpr *
UnPackCol::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
CMPASSERT(getArity() == 2);
result = new (outHeap)
UnPackCol(child(0),
child(1),
width_,
base_,
nullsPresent_,
type_->newCopy(outHeap));
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // UnPackCol::copyTopNode()
NABoolean UnPackCol::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// If the operand is covered, then return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for(Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if(newRelExprAnchorGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr))
{
coveredSubExpr += child(i)->getValueId();
}
}
// ---------------------------------------------------------------------
// The UnPackCol function is coerced to fail the coverage test even when
// its operands isCovered(). This is because only the UnPackCol node can
// evaluate the function. The function is associated with a UnPackCol
// node at the very beginning and we don't allow it to be pushed down
// even if the function's operands are covered at the node's child.
// ---------------------------------------------------------------------
return FALSE;
}
void UnPackCol::getLeafValuesForCoverTest(ValueIdSet& leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// UnPackCol is considered a leaf operator for cover test.
leafValues += getValueId();
}
// -----------------------------------------------------------------------
// member functions for RowsetArrayScan
// -----------------------------------------------------------------------
//
RowsetArrayScan::~RowsetArrayScan() {}
ItemExpr *
RowsetArrayScan::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
CMPASSERT(getArity() == 2);
result = new (outHeap)
RowsetArrayScan(child(0),
child(1),
maxNumElem_,
elemSize_,
elemNullInd_,
elemType_->newCopy(outHeap),
getOperatorType());
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
NABoolean RowsetArrayScan::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// If the operand is covered, then return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for (Lng32 i = 0; i < (Lng32)getArity(); i++) {
if (newRelExprAnchorGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr)) {
coveredSubExpr += child(i)->getValueId();
}
coveredSubExpr += localSubExpr;
}
// ---------------------------------------------------------------------
// The RowsetArrayScan function is coerced to fail the coverage test even
// when its operands isCovered(). This is because only the RowsetArrayScan
// node can evaluate the function. The function is associated with a
// RowsetArrayScan node at the very beginning and we don't allow it to be
// pushed down even if the function's operands are covered at the node's
// child.
// ---------------------------------------------------------------------
return FALSE;
}
void RowsetArrayScan::getLeafValuesForCoverTest(ValueIdSet& leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// RowsetArrayScan is considered a leaf operator for cover test.
leafValues += getValueId();
}
// -----------------------------------------------------------------------
// member functions for RowsetArrayInto
// -----------------------------------------------------------------------
//
RowsetArrayInto::~RowsetArrayInto() {}
ItemExpr *
RowsetArrayInto::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
CMPASSERT(getArity() == 2);
result = new (outHeap)
RowsetArrayInto(child(0),
child(1),
maxNumElem_,
elemSize_,
elemNullInd_,
hostVarType_->newCopy(outHeap),
getOperatorType());
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
NABoolean RowsetArrayInto::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// If the operand is covered, then return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for (Lng32 i = 0; i < (Lng32)getArity(); i++) {
if (newRelExprAnchorGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr)) {
coveredSubExpr += child(i)->getValueId();
}
coveredSubExpr += localSubExpr;
}
// ---------------------------------------------------------------------
// The RowsetArrayInto function is coerced to fail the coverage test even
// when its operands isCovered(). This is because only the RowsetArrayInto
// node can evaluate the function. The function is associated with a
// RowsetArrayInto node at the very beginning and we don't allow it to be
// pushed down even if the function's operands are covered at the node's
// child.
// ---------------------------------------------------------------------
return FALSE;
}
void RowsetArrayInto::getLeafValuesForCoverTest(ValueIdSet& leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// RowsetArrayScan is considered a leaf operator for cover test.
leafValues += getValueId();
}
// -----------------------------------------------------------------------
// member functions for class RangeLookup
// -----------------------------------------------------------------------
RangeLookup::RangeLookup(ItemExpr *val1Ptr,
const RangePartitioningFunction *partFunc) :
BuiltinFunction(ITM_RANGE_LOOKUP, CmpCommon::statementHeap(), 1, val1Ptr),
partFunc_(partFunc)
{
}
RangeLookup::~RangeLookup() {}
ItemExpr * RangeLookup::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) RangeLookup(NULL,partFunc_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
Lng32 RangeLookup::splitKeysLen()
{
// we assume a dense array of character strings, with #parts + 1 entries
// (no NULL terminators, no fillers)
return (partFunc_->getCountOfPartitions() + 1) *
partFunc_->getRangePartitionBoundaries()->getEncodedBoundaryKeyLength();
}
void RangeLookup::copySplitKeys(char *tgt, Lng32 tgtLen)
{
CMPASSERT(tgtLen = splitKeysLen());
const RangePartitionBoundaries * b =
partFunc_->getRangePartitionBoundaries();
Lng32 numEntries = partFunc_->getCountOfPartitions() + 1;
Lng32 entryLen = b->getEncodedBoundaryKeyLength();
Lng32 offset = 0;
for (Lng32 i = 0; i < numEntries; i++)
{
str_cpy_all(&tgt[offset],
b->getBinaryBoundaryValue(i),
entryLen);
offset += entryLen;
}
}
Lng32 RangeLookup::getNumOfPartitions()
{
return partFunc_->getCountOfPartitions();
}
Lng32 RangeLookup::getEncodedBoundaryKeyLength()
{
return partFunc_->getRangePartitionBoundaries()->
getEncodedBoundaryKeyLength();
}
// -----------------------------------------------------------------------
// member functions for class PackFunc
// -----------------------------------------------------------------------
PackFunc::PackFunc(ItemExpr* val1Ptr,
ItemExpr* pf,
Lng32 base,
Lng32 width,
NABoolean nullsPresent)
: BuiltinFunction(ITM_PACK_FUNC,
CmpCommon::statementHeap(),
2,val1Ptr,pf),
isFormatInfoValid_(TRUE),
base_(base),
width_(width),
nullsPresent_(nullsPresent),
type_(NULL)
{
deriveTypeFromFormatInfo();
}
PackFunc::PackFunc(ItemExpr* val1Ptr,
ItemExpr* pf,
const NAType* unpackType)
: BuiltinFunction(ITM_PACK_FUNC,
CmpCommon::statementHeap(),
2,val1Ptr,pf),
isFormatInfoValid_(FALSE)
{
deriveFormatInfoFromUnpackType(unpackType);
}
void PackFunc::deriveTypeFromFormatInfo()
{
CMPASSERT(isFormatInfoValid_);
// The packing factor must be stored as a constant.
NABoolean negateIt;
ConstValue* pfconst = child(1)->castToConstValue(negateIt);
CMPASSERT(pfconst AND negateIt == FALSE);
// The packing factor must be stored as a long in the constant.
CMPASSERT(pfconst->getStorageSize() == sizeof(Lng32));
CMPASSERT(pfconst->getType()->getTypeQualifier() == NA_NUMERIC_TYPE);
Lng32 pf;
memcpy(&pf,pfconst->getConstValue(),sizeof(Lng32));
Lng32 dataSizeInBytes = (width_ < 0 ? (-width_-1)/8+1 : width_);
Lng32 packedRowSizeInBytes = (base_ + dataSizeInBytes);
type_ = new(CmpCommon::statementHeap()) SQLChar(CmpCommon::statementHeap(), packedRowSizeInBytes,FALSE);
}
void PackFunc::deriveFormatInfoFromUnpackType(const NAType* unpackType)
{
CMPASSERT(unpackType);
// Packing of varying length column not (yet?) supported.
// It should have been enforced when the packed table is created.
CMPASSERT(NOT DFS2REC::isAnyVarChar(unpackType->getFSDatatype()));
// Save four byte for the packing factor to be stored.
Lng32 pfSizeInBytes = sizeof(Int32);
// The packing factor must be stored as a constant.
NABoolean negateIt;
ConstValue* pfconst = child(1)->castToConstValue(negateIt);
CMPASSERT(pfconst AND negateIt == FALSE);
// The packing factor must be stored as a long in the constant.
CMPASSERT(pfconst->getStorageSize() == sizeof(Lng32));
CMPASSERT(pfconst->getType()->getTypeQualifier() == NA_NUMERIC_TYPE);
Lng32 pf;
memcpy(&pf,pfconst->getConstValue(),sizeof(Lng32));
// No of bits reserved for null indicators.
nullsPresent_ = unpackType->supportsSQLnullPhysical();
Lng32 nullIndLenInBytes = (nullsPresent_ ? (pf-1)/8+1 : 0);
base_ = pfSizeInBytes + nullIndLenInBytes;
// Storage size for the actual packed column values in bytes.
Lng32 dataSizeInBytes;
// For bit precision integers, width needs to be in negative no of bits.
if(unpackType->getFSDatatype() == REC_BPINT_UNSIGNED)
{
width_ = ((SQLBPInt*)unpackType)->getDeclaredLength();
dataSizeInBytes = (width_*pf-1)/8+1;
width_ = -width_;
}
else
{
width_ = unpackType->getNominalSize();
dataSizeInBytes = width_ * pf;
}
// Now, the length of the contents of a packed record in bytes.
Lng32 packedRowSizeInBytes = (base_ + dataSizeInBytes);
// Synthesize type of the packed column.
type_ = new(CmpCommon::statementHeap()) SQLChar(CmpCommon::statementHeap(), packedRowSizeInBytes,FALSE);
isFormatInfoValid_ = TRUE;
}
PackFunc::~PackFunc() {}
NABoolean PackFunc::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// If the operand is covered, then return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for(Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if(newRelExprAnchorGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr))
{
coveredSubExpr += child(i)->getValueId();
}
}
// ---------------------------------------------------------------------
// The packing function is coerced to fail the coverage test even when
// its operands isCovered(). This is because only the Pack node can
// evaluate a packing function. The function is associated with a Pack
// node at the very beginning and we don't allow it to be pushed down
// even if the function's operands are covered at the Pack node's child.
// ---------------------------------------------------------------------
return FALSE;
}
void PackFunc::getLeafValuesForCoverTest(ValueIdSet& leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// see note in isCovered().
leafValues += getValueId();
}
ItemExpr* PackFunc::copyTopNode(ItemExpr* derivedNode, CollHeap* outHeap)
{
ItemExpr* result;
if(derivedNode == NULL)
result = new (outHeap) PackFunc (child(0),child(1));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
}
UDFunction::~UDFunction()
{
delete udfDesc_;
}
Int32 UDFunction::getArity() const
{
return getNumChildren(); // Function class member function.
}
const NAString UDFunction::getText() const
{
return functionName_.getExternalName();
}
HashValue UDFunction::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= functionName_.getExternalName() + actionName_;
return result;
}
NABoolean Function::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
return TRUE;
}
NABoolean UDFunction::duplicateMatch(const ItemExpr & other) const
{
// Check to see if the arguments are the same, among other things.
if (NOT genericDuplicateMatch(other))
return FALSE;
UDFunction &o = (UDFunction &) other;
// compare all local data members of the derived class
// and return FALSE if they don't match
if (functionName_.getExternalName() != o.functionName_.getExternalName() ||
actionName_ != o.actionName_ ||
hasSubquery_ != o.hasSubquery_ ||
inputVars_ != o.inputVars_ ||
outputVars_ != o.outputVars_)
return FALSE;
if (udfDesc_ && o.udfDesc_) // RoutineDesc pointers
if (!(*udfDesc_ == *o.udfDesc_))
return FALSE;
if (udfDesc_ != NULL || o.udfDesc_ != NULL) // Both must be NULL to proceed.
return FALSE;
return TRUE;
}
ItemExpr *UDFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
UDFunction *result;
if (derivedNode == NULL)
result = new (outHeap) UDFunction(functionName_, getArity(), outHeap);
else
result = (UDFunction *) derivedNode;
result->heap_ = outHeap;
result->functionName_= functionName_;
result->functionNamePos_= functionNamePos_;
result->actionName_ = actionName_ ;
result->udfDesc_ = udfDesc_ != NULL ?
new (outHeap) RoutineDesc(*udfDesc_, outHeap) : NULL;
result->hasSubquery_ = hasSubquery_ ;
result->inputVars_ = inputVars_ ;
result->outputVars_ = outputVars_ ;
return Function::copyTopNode(result, outHeap);
}
void UDFunction::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
NAString kwd(getText(), CmpCommon::statementHeap());
if (form == USER_FORMAT_DELUXE) kwd.toUpper();
result += kwd;
result += "(";
CollIndex lastComma = getNumChildren() -1;
for (CollIndex i=0; i<(CollIndex ) getNumChildren(); i++)
{
child(i)->unparse(result,phase,form,tabId);
if (i < lastComma) result += ",";
}
result += ")";
}
ExprValueId &UDFunction::operator[] (Lng32 index)
{
return Function::operator[](index);
}
const ExprValueId &UDFunction::operator[] (Lng32 index) const
{
return Function::operator[](index);
}
ItemExpr *UDFunction::containsUDF()
{
return this;
}
NABoolean UDFunction::containsIsolatedUDFunction()
{
NABoolean containsIsolatedUDF(FALSE);
for (CollIndex i=0; i < (CollIndex) getNumChildren(); i++)
{
if (child(i)->containsIsolatedUDFunction() == TRUE)
containsIsolatedUDF = TRUE;
}
if (containsIsolatedUDF == TRUE)
return TRUE;
// We didn't have one as an input parameter, check to see if we
// are isolated..
const RoutineDesc *rdesc = getRoutineDesc();
if (rdesc == NULL || rdesc->getEffectiveNARoutine() == NULL ) return FALSE;
return ( rdesc->getEffectiveNARoutine()->isIsolate() ? TRUE : FALSE ) ;
} // UDFunction::containsIsolatedUDFunction
// Get the output degree of this function. If it is >1, getOutputItem
// should be used to get the ItemExpr of each output.
int UDFunction::getOutputDegree()
{
CCMPASSERT(getRoutineDesc() != NULL);
if (!getRoutineDesc()) return -1; // Routine desc not set. Error.
else
return getRoutineDesc()->getOutputColumnList().entries();
}
// Get i'th output argument of UDF as ItemExpr.
ItemExpr *UDFunction::getOutputItem(unsigned int i)
{
CCMPASSERT(getRoutineDesc() != NULL);
if (!getRoutineDesc()) return NULL; // Routine desc not set. Error.
else
{
if (i < getRoutineDesc()->getOutputColumnList().entries())
return getRoutineDesc()->getOutputColumnList()[i].getItemExpr();
else return NULL; // 'i' out of bounds. Error.
}
}
ItemExpr * NotIn::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
NotIn *result;
if (derivedNode == NULL)
result = new (outHeap) NotIn();
else
result = (NotIn *) derivedNode;
//copy the data members
result->equivEquiPredicate_ = equivEquiPredicate_;
result->equivNonEquiPredicate_ = equivNonEquiPredicate_;
result->isOneInnerBroadcastRequired_ = isOneInnerBroadcastRequired_;
return BiRelat::copyTopNode(result, outHeap);
}
ValueId NotIn::createEquivEquiPredicate() const
{
CMPASSERT ( child(0)->getOperatorType() != ITM_ITEM_LIST &&
child(1)->getOperatorType() != ITM_ITEM_LIST)
ItemExpr * newPred = new (CmpCommon::statementHeap())
BiRelat(ITM_EQUAL,
child(0),
child(1));
newPred->synthTypeAndValueId(TRUE);
return newPred->getValueId();
}
ValueId NotIn::createEquivNonEquiPredicate() const
{
CMPASSERT ( child(0)->getOperatorType() != ITM_ITEM_LIST &&
child(1)->getOperatorType() != ITM_ITEM_LIST)
ItemExpr * newPred = new (CmpCommon::statementHeap())
BiRelat(ITM_NOT_EQUAL,
child(0),
child(1));
newPred = new (CmpCommon::statementHeap())
UnLogic(ITM_IS_TRUE, newPred);
newPred = new (CmpCommon::statementHeap())
UnLogic(ITM_NOT, newPred);
newPred->synthTypeAndValueId(TRUE);
return newPred->getValueId();
}
void NotIn::cacheEquivEquiPredicate()
{
if (getEquivEquiPredicate() == NULL_VALUE_ID)
{
ValueId vid = createEquivEquiPredicate();
ItemExpr * itm = vid.getItemExpr();
((BiRelat *)itm)->setIsNotInPredTransform(TRUE);
((BiRelat *)itm)->setOuterNullFilteringDetected(
getOuterNullFilteringDetected());
((BiRelat *)itm)->setInnerNullFilteringDetected(
getInnerNullFilteringDetected());
setEquivEquiPredicate(vid);
}
}
void NotIn::cacheEquivNonEquiPredicate()
{
if (getEquivNonEquiPredicate() == NULL_VALUE_ID )
{
setEquivNonEquiPredicate(
createEquivNonEquiPredicate());
}
}
void NotIn::cacheIsOneInnerBroadcastRequired()
{
if (isOneInnerBroadcastRequired_ == NotIn::NOT_SET)
{
isOneInnerBroadcastRequired_ = NotIn::NOT_REQUIRED;
ItemExpr * child0 = child(0);
const NAType &outerType = child0->getValueId().getType();
if (outerType.supportsSQLnull() &&
!getOuterNullFilteringDetected())
{
isOneInnerBroadcastRequired_ = NotIn::REQUIRED;
}
}
}//NotIn::cacheIsOneInnerBroadcastRequired()
// -----------------------------------------------------------------------
// member functions for class BiLogic
// -----------------------------------------------------------------------
Int32 BiLogic::getArity() const { return 2; }
NABoolean BiLogic::isAPredicate() const { return TRUE; }
// return true iff we're an expansion of a LIKE predicate
NABoolean BiLogic::isLike() const
{
switch (getOperatorType()) {
case ITM_AND:
case ITM_OR:
{
Int32 arity = getArity();
for (Int32 x = 0; x < arity; x++) {
switch (child(x)->getOperatorType()) {
case ITM_GREATER_EQ:
case ITM_LESS:
if (!((BiRelat*)child(x).getPtr())->derivativeOfLike())
return FALSE; // we're not an expansion of a LIKE predicate
else // we may be part of an expansion of a LIKE predicate
continue; // keep looking
break;
default:
return FALSE; // we're not an expansion of a LIKE predicate
break;
}
}
return TRUE; // yes, we are an expansion of a LIKE predicate
}
break;
default:
return FALSE; // we're not an expansion of a LIKE predicate
}
}
// -----------------------------------------------------------------------
// BiLogic::isCovered()
// -----------------------------------------------------------------------
NABoolean BiLogic::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
if (getOperatorType() == ITM_OR)
{
// ----------------------------------------------------------------
// The subtree that is rooted in an OR can contain values that
// are produced by different sources. The entire expression tree
// including the OR at the root is covered, if the subtree that
// is rooted in the OR is covered. Otherwise it is not covered.
// The coverage test insists on a complete coverage of all the
// expressions at the leaves.
// ----------------------------------------------------------------
ValueIdSet leafValues;
getLeafValuesForCoverTest(leafValues, coveringGA, newExternalInputs);
return leafValues.isCovered(newExternalInputs,
coveringGA,
referencedInputs,
coveredSubExpr,
unCoveredExpr);
}
else
return ItemExpr::isCovered(newExternalInputs,
coveringGA,
referencedInputs,
coveredSubExpr,
unCoveredExpr);
} // BiLogic::isCovered()
NABoolean BiLogic::duplicateMatch(const ItemExpr& other) const
{
return genericDuplicateMatch(other);
}
ItemExpr * BiLogic::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
BiLogic *result;
if (derivedNode == NULL)
result = new (outHeap) BiLogic(getOperatorType());
else
result = (BiLogic*)derivedNode;
result->setNumLeaves(getNumLeaves());
result->createdFromINlist_ = createdFromINlist_;
return ItemExpr::copyTopNode(result, outHeap);
}
ItemExpr* BiLogic::getINlhs()
{
if (createdFromINlist() && child(1)->getOperatorType() == ITM_EQUAL)
return child(1)->child(0);
else
return NULL;
}
const NAString BiLogic::getText() const
{
switch (getOperatorType())
{
case ITM_AND:
return "and";
case ITM_OR:
return "or";
default:
return "unknown BiLogic";
} // switch
} // BiLogic::getText()
QR::ExprElement BiLogic::getQRExprElem() const
{
return QR::QRBinaryOperElem;
}
// -----------------------------------------------------------------------
// member functions for class UnLogic
// -----------------------------------------------------------------------
Int32 UnLogic::getArity() const { return 1; }
NABoolean UnLogic::isAPredicate() const { return TRUE; }
const NAString UnLogic::getText() const
{
switch (getOperatorType())
{
case ITM_NOT:
return "not";
case ITM_IS_TRUE:
return "is true";
case ITM_IS_FALSE:
return "is false";
case ITM_IS_NULL:
return "is null";
case ITM_IS_NOT_NULL:
return "is not null";
case ITM_IS_UNKNOWN:
return "is unknown";
case ITM_IS_NOT_UNKNOWN:
return "is not unknown";
default:
return "unknown UnLogic??";
} // switch
} // UnLogic::getText()
NABoolean UnLogic::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
return TRUE;
}
ItemExpr * UnLogic::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) UnLogic(getOperatorType());
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
QR::ExprElement UnLogic::getQRExprElem() const
{
return QR::QRUnaryOperElem;
}
// -----------------------------------------------------------------------
// member functions for class BiRelat
// -----------------------------------------------------------------------
NABoolean BiRelat::derivativeOfLike()
{
if (originalLikeExprId() != NULL_VALUE_ID)
return TRUE;
else
return FALSE;
}
void BiRelat::adjustRowcountAndUecForLike(const ColStatDescSharedPtr& colStatDesc,
CostScalar rowCountBeforePreds,
CostScalar totalUecBeforePreds,
CostScalar baseUecBeforePreds)
{
// This range predicate is a derivative of Like predicate.
// Hence we cannot use the usual selectivity obtained after
// applying range predicates. Here we use a portion of the
// default selectivity for like predicates to obtain the
// resultant rowcount. We still continue to use the histograms
// obtained after applying range predicates the usual way
// The selectivity would be applied to the inital rowcount
// before any predicates were applied.
ColStatsSharedPtr colStats = colStatDesc->getColStatsToModify() ;
CostScalar nrc = MIN_ONE_CS(rowCountBeforePreds * getLikeSelectivity());
CostScalar nuec = MIN_ONE_CS(totalUecBeforePreds * getLikeSelectivity());
CostScalar baseuec = MIN_ONE_CS(baseUecBeforePreds * getLikeSelectivity());
if ( colStats->isUnique() )
{
// If this is a UNIQUE column, UEC == rowcount
nuec = nrc;
}
else
{
nuec = MINOF(nrc, nuec);
}
HistogramSharedPtr hist = colStats->getHistogramToModify() ;
hist->condenseToSingleInterval();
colStats->setIsCompressed(TRUE);
// first, set the aggregate values
colStats->setRowsAndUec ( nrc, nuec );
colStats->setRedFactor ( csOne );
colStats->setUecRedFactor( csOne );
colStats->setBaseUec(baseuec);
// Set first interval's rowcount and uec.
hist->getFirstInterval().setRowsAndUec( nrc, nuec );
// from this point forward, we're going to consider this a fake histogram
colStats->setFakeHistogram();
}
Int32 BiRelat::getArity() const { return 2; }
NABoolean BiRelat::isAPredicate() const { return TRUE; }
const NAString BiRelat::getText() const
{
switch (getOperatorType())
{
case ITM_EQUAL:
return "=";
case ITM_NOT_EQUAL:
return "<>";
case ITM_LESS:
return "<";
case ITM_LESS_EQ:
return "<=";
case ITM_GREATER:
return ">";
case ITM_GREATER_EQ:
return ">=";
default:
return "unknown BiRelat";
} // switch
} // BiRelat::getText()
NABoolean BiRelat::duplicateMatch(const ItemExpr& other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
const BiRelat &o = (BiRelat &) other;
if (specialNulls_ != o.specialNulls_ OR
specialMultiValuePredicateTransformation_ !=
o.specialMultiValuePredicateTransformation_ OR
directionVector_ != o.directionVector_ /* ptr comparison only */ OR
isaPartKeyPred_ != o.isaPartKeyPred_ OR
originalLikeExprId_ != o.originalLikeExprId_ OR
likeSelectivity_ != o.likeSelectivity_ OR
derivedFromMCRP_ != o.derivedFromMCRP_ OR
preferForSubsetScanKey_ != o.preferForSubsetScanKey_
)
return FALSE;
// do this if this a range predicate derived from a MCRP
// A MCRP (MultiColumnRangePredicate) is a predicate of the form
// (a, b, c) > (1, 2, 3). Such a predicate is transformed to
// (a >= 1) and ((a, b, c) > (1, 2, 3)). Notice the transformation
// adds predicate (a >= 1). The derivedFromMCRP flag is set to TRUE
// for predicate (a >= 1).
if ((derivedFromMCRP_ == TRUE) &&
(listOfComparisonExprs_ != o.listOfComparisonExprs_))
{
if (listOfComparisonExprIds_.entries() != o.listOfComparisonExprIds_.entries())
return FALSE;
for (CollIndex i=0; i < listOfComparisonExprIds_.entries(); i++)
{
if (listOfComparisonExprIds_[i] != o.listOfComparisonExprIds_[i])
return FALSE;
}
}
return TRUE;
}
ItemExpr * BiRelat::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
BiRelat *result;
if (derivedNode == NULL)
result = new (outHeap) BiRelat(getOperatorType());
else
result = (BiRelat *) derivedNode;
// copy the data members
result->specialNulls_ = specialNulls_;
result->specialMultiValuePredicateTransformation_ =
specialMultiValuePredicateTransformation_;
result->isaPartKeyPred_ = isaPartKeyPred_;
result->originalLikeExprId_ = originalLikeExprId_;
result->setDirectionVector(directionVector_);
result->likeSelectivity_ = likeSelectivity_;
result->derivedFromMCRP_ = derivedFromMCRP_;
result->listOfComparisonExprs_ = listOfComparisonExprs_;
result->listOfComparisonExprIds_ = listOfComparisonExprIds_;
result->leftMCRPChildList_ = leftMCRPChildList_;
result->rightMCRPChildList_ = rightMCRPChildList_;
result->preferForSubsetScanKey_ = preferForSubsetScanKey_;
result->createdFromINlist_ = createdFromINlist_;
result->collationEncodeComp_ = collationEncodeComp_;
result->isNotInPredTransform_ = isNotInPredTransform_;
result->outerNullFilteringDetected_= outerNullFilteringDetected_;
result->innerNullFilteringDetected_ = innerNullFilteringDetected_;
result->rollupColumnNum_ = rollupColumnNum_;
result->flags_ = flags_;
return ItemExpr::copyTopNode(result, outHeap);
}
void BiRelat::getLeftMCRPChildList(ValueIdList & leftMCRPChildList)
{
if ((!isDerivedFromMCRP()) ||
(!listOfComparisonExprIds_.entries()))
return;
if (leftMCRPChildList_.entries())
{
leftMCRPChildList = leftMCRPChildList_;
return;
}
// iterate over the list of comparisons
for(CollIndex i=0; i < listOfComparisonExprIds_.entries(); i++)
{
//get comparison at location i
BiRelat * comparison = (BiRelat *) listOfComparisonExprIds_[i].getItemExpr();
ItemExpr * leftChild = (ItemExpr *) comparison->child(0);
ItemExpr * rightChild = (ItemExpr *) comparison->child(1);
leftMCRPChildList_.insert(leftChild->getValueId());
rightMCRPChildList_.insert(rightChild->getValueId());
}
leftMCRPChildList = leftMCRPChildList_;
}
void BiRelat::getRightMCRPChildList(ValueIdList & rightMCRPChildList)
{
if ((!isDerivedFromMCRP()) ||
(!listOfComparisonExprIds_.entries()))
return;
if (rightMCRPChildList_.entries())
{
rightMCRPChildList = rightMCRPChildList_;
return;
}
// iterate over the list of comparisons
for(CollIndex i=0; i < listOfComparisonExprIds_.entries(); i++)
{
//get comparison at location i
BiRelat * comparison = (BiRelat *) listOfComparisonExprIds_[i].getItemExpr();
ItemExpr * leftChild = (ItemExpr *) comparison->child(0);
ItemExpr * rightChild = (ItemExpr *) comparison->child(1);
leftMCRPChildList_.insert(leftChild->getValueId());
rightMCRPChildList_.insert(rightChild->getValueId());
}
rightMCRPChildList = rightMCRPChildList_;
}
QR::ExprElement BiRelat::getQRExprElem() const
{
return QR::QRBinaryOperElem;
}
OperatorTypeEnum BiRelat::getRelaxedComparisonOpType() const
{
switch (getOperatorType())
{
case ITM_LESS:
return ITM_LESS_EQ;
case ITM_GREATER:
return ITM_GREATER_EQ;
case ITM_NOT_EQUAL:
CMPASSERT(0); // the relaxed form would be TRUE,
// but we don't handle that right now
default:
break;
}
return getOperatorType();
}
// Member functions for class KeyRangeCompare //
const NAString KeyRangeCompare::getText() const
{
NAString result("", CmpCommon::statementHeap());
switch (getOperatorType())
{
case ITM_EQUAL:
result += "=";
break;
case ITM_NOT_EQUAL:
result += "<>";
break;
case ITM_LESS:
result += "<";
break;
case ITM_LESS_EQ:
result += "<=";
break;
case ITM_GREATER:
result += ">";
break;
case ITM_GREATER_EQ:
result += ">=";
break;
default:
result += "unknown BiRelat";
return result;
} // switch
result += " (KEY_RANGE_COMPARE)";
return result;
} // KeyRangeCompare::getText()
// -----------------------------------------------------------------------
// member functions for class ConstValue
// -----------------------------------------------------------------------
// constructor for an untyped NULL constant
ConstValue::ConstValue()
: ItemExpr(ITM_CONSTANT)
, isNull_(IS_NULL)
, type_(new (CmpCommon::statementHeap()) SQLUnknown(CmpCommon::statementHeap(), TRUE))
, value_(NULL)
, storageSize_(0)
, text_(new (CmpCommon::statementHeap()) NAString("NULL", CmpCommon::statementHeap()))
, textIsValidatedSQLLiteralInUTF8_(FALSE)
, isSystemSupplied_(FALSE)
, locale_strval(0)
, locale_wstrval(0)
, isStrLitWithCharSetPrefix_(FALSE)
, rebindNeeded_(FALSE)
{
}
// constructor for a numeric constant
ConstValue::ConstValue(Lng32 intval, NAMemory * outHeap)
: ItemExpr(ITM_CONSTANT)
, isNull_(IS_NOT_NULL)
, textIsValidatedSQLLiteralInUTF8_(FALSE)
, type_(new (CmpCommon::statementHeap()) SQLInt(CmpCommon::statementHeap(), TRUE, FALSE))
, isSystemSupplied_(FALSE)
, locale_strval(0)
, locale_wstrval(0)
, isStrLitWithCharSetPrefix_(FALSE)
, rebindNeeded_(FALSE)
{
Int64 lintval = intval;
char buf[TEXT_DISPLAY_LENGTH];
char * S = buf;
Int32 len = 0;
sprintf(S,"%d %n",intval, &len);
S[len++] = '\0';
text_ = new (outHeap) NAString(S, outHeap);
storageSize_ = sizeof(Lng32);
value_ = (void *)( new (outHeap) char[storageSize_] );
// copy the bit pattern as is
memcpy(value_,(void *)(&intval),(Int32)storageSize_);
}
ConstValue::ConstValue(const NAString & strval,
enum CharInfo::CharSet charSet,
enum CharInfo::Collation collation,
enum CharInfo::Coercibility coercibility,
NAMemory * outHeap)
: ItemExpr(ITM_CONSTANT), isNull_(IS_NOT_NULL),
textIsValidatedSQLLiteralInUTF8_(FALSE), isStrLitWithCharSetPrefix_(FALSE),
isSystemSupplied_(FALSE), locale_wstrval(0), rebindNeeded_(FALSE)
{
initCharConstValue(strval, charSet, collation, coercibility, FALSE, outHeap);
locale_strval = new (outHeap) NAString
((char*)strval.data(), strval.length(), outHeap);
}
void ConstValue::initCharConstValue
(
const NAString & strval,
enum CharInfo::CharSet charSet,
enum CharInfo::Collation collation,
enum CharInfo::Coercibility coercibility,
NABoolean isCaseInSensitive,
NAMemory * outHeap
)
{
if (strval.length() == 0)
{
// create a varchar constant of length 0, in this case.
type_ = new (outHeap)
SQLVarChar(outHeap, 0, FALSE, FALSE, FALSE,
charSet, collation, coercibility);
storageSize_ = type_->getVarLenHdrSize();
value_ = (void *)( new (outHeap)
char[storageSize_] );
str_pad((char *)value_, (Int32)storageSize_, '\0');
}
else
{
// A "mini-cache" to avoid proc call, for performance,
// local to the SINGLE-byte-charset NAString ctor
// (distinct from the double-byte-charset ctor to prevent cache churn).
static THREAD_P CharInfo::CharSet cachedCS = CharInfo::UnknownCharSet;
static THREAD_P Int32 cachedBPC = 1;
if (cachedCS != charSet) {
cachedCS = charSet;
cachedBPC = CharInfo::maxBytesPerChar(charSet);
}
Int32 num_of_chars = (Int32)strval.length() / cachedBPC;
if (CharInfo::isVariableWidthMultiByteCharSet(charSet))
{
Int32 actualCharsCount = ComputeStrLenInUCS4chars
( strval.data() // const char * pStr
, (Int32) strval.length() // const Int32 strLenInBytes
, charSet // const CharInfo::CharSet cs
);
CMPASSERT(actualCharsCount >= 0); // no errors
type_ = new (outHeap) SQLChar (outHeap, CharLenInfo ( actualCharsCount
, strval.length() // str len in bytes
)
, FALSE // allowSQLnull
, FALSE // isUpShifted
, FALSE // isCaseInsensitive
, FALSE // varLenFlag
, charSet
, collation
, coercibility
, /*encoding*/charSet
);
}
else
type_ = new (outHeap)
SQLChar(outHeap, num_of_chars, FALSE, FALSE, FALSE, FALSE,
charSet, collation, coercibility);
storageSize_ = strval.length();
value_ = (void *)( new (outHeap)
char[storageSize_] );
memcpy(value_, (void *)(strval.data()), (Int32)storageSize_);
}
((CharType*)type_)->setCaseinsensitive(isCaseInSensitive);
text_ = new (outHeap)
NAString(strval,
outHeap);
textIsValidatedSQLLiteralInUTF8_ = FALSE;
}
ConstValue::ConstValue(const NAWString& wstrval,
enum CharInfo::CharSet charSet,
enum CharInfo::Collation collation,
enum CharInfo::Coercibility coercibility,
NAMemory * outHeap,
enum CharInfo::CharSet strLitPrefixCharSet
)
: ItemExpr(ITM_CONSTANT), isNull_(IS_NOT_NULL),
isSystemSupplied_(FALSE),
value_(0),
text_(0),
locale_strval(0),
locale_wstrval(0),
isStrLitWithCharSetPrefix_(FALSE),
rebindNeeded_(FALSE)
{
initCharConstValue(wstrval, charSet, collation, coercibility, FALSE, outHeap,
strLitPrefixCharSet);
locale_wstrval = new (CmpCommon::statementHeap()) NAWString
(wstrval.data(), wstrval.length(), CmpCommon::statementHeap());
}
void ConstValue::initCharConstValue(const NAWString& strval,
enum CharInfo::CharSet charSet,
enum CharInfo::Collation collation,
enum CharInfo::Coercibility coercibility,
NABoolean isCaseInSensitive,
NAMemory * outHeap,
enum CharInfo::CharSet strLitPrefixCharSet)
{
if (strval.length() == 0)
{
// create a varchar constant of length 0, in this case.
type_ = new (outHeap)
SQLVarChar(outHeap, 0, FALSE, FALSE, FALSE,
charSet, collation, coercibility);
storageSize_ = type_->getVarLenHdrSize();
value_ = (void *)( new (outHeap)
NAWchar[storageSize_] );
wc_str_pad((NAWchar *)value_, (Int32)storageSize_, '\0');
}
else
{
// A "mini-cache" to avoid proc call, for performance,
// local to the DOUBLE-byte-charset NAString ctor
// (distinct from the single-byte-charset ctor to prevent cache churn).
static CharInfo::CharSet cachedCS = CharInfo::UNICODE;
static Int32 cachedBPC = sizeof(NAWchar);
if (cachedCS != charSet) {
cachedCS = charSet;
if (cachedCS == CharInfo::UnknownCharSet) {
cachedCS = CharInfo::UNICODE;
cachedBPC = sizeof(NAWchar);
} else {
cachedBPC = CharInfo::maxBytesPerChar(cachedCS);
// make sure the incoming charset is double-byte
CMPASSERT(cachedBPC == (Int32)sizeof(NAWchar));
}
}
Int32 num_of_chars = (Int32)strval.length();
type_ = new (outHeap)
SQLChar(outHeap, num_of_chars, FALSE, FALSE, FALSE, FALSE,
charSet, collation, coercibility);
storageSize_ = cachedBPC * strval.length();
value_ = (void *)( new (outHeap)
NAWchar[storageSize_] );
memcpy(value_, (void *)(strval.data()), (Int32)storageSize_);
}
((CharType*)type_)->setCaseinsensitive(isCaseInSensitive);
text_ = new (outHeap)
NAString((char*)strval.data(), storageSize_,
outHeap);
textIsValidatedSQLLiteralInUTF8_ = FALSE;
}
ConstValue::ConstValue(NAString strval, NAWString wstrval,
enum CharInfo::Collation collation,
enum CharInfo::Coercibility coercibility,
NAMemory * outHeap)
: ItemExpr(ITM_CONSTANT), isNull_(IS_NOT_NULL),
value_(0),
text_(0),
textIsValidatedSQLLiteralInUTF8_(FALSE),
isSystemSupplied_(FALSE),
isStrLitWithCharSetPrefix_(FALSE),
rebindNeeded_(FALSE)
{
initCharConstValue(strval, CharInfo::UnknownCharSet,
collation, coercibility, FALSE, outHeap);
/*
type_ = new (CmpCommon::statementHeap())
SQLVarChar(0, FALSE, FALSE,
CharInfo::UnknownCharSet, collation, coercibility);
storageSize_ = type_->getVarLenHdrSize();
*/
locale_strval = new (CmpCommon::statementHeap()) NAString
((char*)strval.data(), strval.length(), CmpCommon::statementHeap());
locale_wstrval = new (CmpCommon::statementHeap()) NAWString
(wstrval.data(), wstrval.length(), CmpCommon::statementHeap());
}
const NAType* ConstValue::pushDownType(NAType& newType,
enum NABuiltInTypeEnum defaultQualifier)
{
if ( newType.getTypeQualifier() == NA_CHARACTER_TYPE &&
type_ -> getTypeQualifier() == NA_CHARACTER_TYPE
)
{
CharType &newCT = (CharType&)newType;
enum CharInfo::CharSet newCS = ((const CharType&)newType).getCharSet();
CharType* ct = (CharType*)type_;
if ( ct ->getCharSet() != newCS ) {
if ( newCS == CharInfo::UNICODE ) {
assert(locale_wstrval);
// fix 10-070329-5979 by passing in the case-sensitivity flag.
// Should re-implement this with collation some day.
initCharConstValue(*locale_wstrval, newCS,
ct->getCollation(),
ct->getCoercibility(),
ct->isCaseinsensitive());
} else {
if ( locale_strval == NULL ) {
assert(locale_wstrval);
// init locale_strval from locale_wstrval
Lng32 wlen = (Lng32)(locale_wstrval->length());
Lng32 bufLen = wlen * CharInfo::maxBytesPerChar(newCS) + 1; // add the NULL
char* buf = new (CmpCommon::statementHeap()) char[bufLen];
Lng32 cLen = UnicodeStringToLocale(newCS, locale_wstrval->data(), wlen,
buf, bufLen, TRUE, FALSE);
if ( cLen == 0 ) { // If conversion fails, do not change the type.
NADELETEBASIC(buf,CmpCommon::statementHeap());
return type_;
}
locale_strval = new (CmpCommon::statementHeap()) NAString
(buf, cLen, CmpCommon::statementHeap());
NADELETEBASIC(buf,CmpCommon::statementHeap());
}
// fix 10-040527-6468 (CQD infer_charset doesn't work with
// kanji character set correctly). We do not change this ConstValue
// object's attributes if the lendth of the literal is odd. The
// compatibility check code will catch the incompatible error (e.g.,
// T.KANJI_C1 = _unknown'abc').
if ( CharInfo::is_NCHAR_MP(newCS) == FALSE ||
locale_strval -> length() % SQL_DBCHAR_SIZE == 0
)
{
// fix 10-070329-5979 by passing in the case-sensitivity flag.
// Should re-implement this with collation some day.
initCharConstValue(*locale_strval, newCS,
ct->getCollation(),
ct->getCoercibility(),
ct->isCaseinsensitive());
}
}
}
return synthesizeType();
}
return &newType;
}
// this constructor creates a constant of the given type and initializes
// it with the value.
ConstValue::ConstValue(const NAType * type, void * value, Lng32 value_len,
NAString * literal, NAMemory * outHeap)
: ItemExpr(ITM_CONSTANT)
, isNull_(IS_NOT_NULL)
, type_(type)
, isSystemSupplied_(FALSE)
, locale_strval(0)
, locale_wstrval(0)
, isStrLitWithCharSetPrefix_(FALSE)
, rebindNeeded_(FALSE)
{
CMPASSERT(value_len > 0);
CMPASSERT(type_);
storageSize_ = value_len;
value_ = (void *)( new (outHeap) char[storageSize_] );
memcpy(value_,(void *)value,(Int32)storageSize_);
if(type)
type_ = type->newCopy(outHeap);
NABoolean isNull = FALSE;
// If a literal was not supplied, call a method to convert
// the value from its binary form to UTF-8 and add any necessary
// syntax modifiers like the charset and date and time qualifiers
if (literal == NULL)
{
if (type_)
textIsValidatedSQLLiteralInUTF8_ =
type_->createSQLLiteral((const char *)value,
text_,
isNull,
outHeap);
DCMPASSERT(textIsValidatedSQLLiteralInUTF8_); // for now
if (!textIsValidatedSQLLiteralInUTF8_)
text_ = new (outHeap) NAString("<unknown value>", outHeap);
}
else
{
text_ = new (outHeap) NAString(*literal, outHeap);
textIsValidatedSQLLiteralInUTF8_ = FALSE;
if (type->supportsSQLnull())
{
// call the NAType base class method
// just to check for a NULL value (sets isNull)
NAString *dummyPtr;
type->NAType::createSQLLiteral((const char *) value,
dummyPtr,
isNull,
outHeap);
}
}
if (isNull)
isNull_ = IS_NULL;
}
/*soln:10-050710-9594 begin */
ConstValue::ConstValue(const NAType * type, void * value, Lng32 value_len,
NAString *lstrval, NAWString *wstrval,
NAString * literal, NAMemory * outHeap, IsNullEnum isNull)
: ItemExpr(ITM_CONSTANT)
, isNull_(isNull)
, type_(type)
, isSystemSupplied_(FALSE)
, locale_strval(0)
, locale_wstrval(0)
, isStrLitWithCharSetPrefix_(FALSE)
, rebindNeeded_(FALSE)
{
CMPASSERT(value_len > 0);
CMPASSERT(type_);
storageSize_ = value_len;
value_ = (void *)( new (outHeap) char[storageSize_] );
memcpy(value_,(void *)value,(Int32)storageSize_);
if(type)
type_ = type->newCopy(outHeap);
// If a literal was not supplied, the text for this constant
// is the bit pattern for the given value. Note that embedded
// ascii nulls in the bit pattern are copied as is.
if (literal == NULL)
text_ = new (outHeap)
NAString((char *)value,(UInt32)value_len, CmpCommon::statementHeap());
else
text_ = new (outHeap) NAString
(*literal, outHeap);
textIsValidatedSQLLiteralInUTF8_ = FALSE;
if(lstrval)
locale_strval = new (outHeap) NAString
((char*)lstrval->data(), lstrval->length(), outHeap);
if(wstrval)
locale_wstrval = new (outHeap) NAWString
((NAWchar *)wstrval->data(), wstrval->length(), outHeap);
}
/*soln:10-050710-9594 end */
// constructor for an extremal (min or max) value of a given type
// The allowNull parameter specifies whether NULL should be considered
// when generating the value; if it is TRUE and we want the max value,
// and the type is nullable, then NULL will be returned instead of the
// max non-null value.
ConstValue::ConstValue(const NAType * type,
const NABoolean wantMinValue,
const NABoolean includeNull,
NAMemory * outHeap)
: ItemExpr(ITM_CONSTANT)
, isNull_(IsNullEnum(type->supportsSQLnull() && includeNull && !wantMinValue))
, type_(type)
, isSystemSupplied_(FALSE)
, locale_strval(0)
, locale_wstrval(0)
, isStrLitWithCharSetPrefix_(FALSE)
, rebindNeeded_(FALSE)
{
assert(type_);
storageSize_ = type->getTotalSize();
assert(storageSize_ > 0);
char *storage = new (outHeap) char[storageSize_];
value_ = (void *)storage;
// if nullable, make the null indicator bytes 0 or NULL
// depending on whether min or max value is needed.
if (type->supportsSQLnull())
{
short indicatorVal;
// the min value is non-NULL, the max value is the NULL
// value unless we want the max non-NULL value
if (wantMinValue || !includeNull)
indicatorVal = 0;
else
indicatorVal = -1;
// move the NULL indicator into the first few bytes of the value
switch (type->getSQLnullHdrSize())
{
case 2:
*(short *)value_ = indicatorVal;
break;
case 4:
*(Lng32 *)value_ = (Lng32) indicatorVal;
break;
default:
CMPASSERT(0); // unsupported type of NULL indicator
}
}
Lng32 startOfData = type->getSQLnullHdrSize();
Lng32 templen = storageSize_ - startOfData;
if (wantMinValue) // min value
{
type->minRepresentableValue(&storage[startOfData],
&templen,
NULL,
outHeap);
text_ = new (outHeap) NAString("<min>", outHeap);
}
else
{
type->maxRepresentableValue(&storage[startOfData],
&templen,
NULL,
outHeap);
text_ = new (outHeap) NAString("<max>", outHeap);
}
textIsValidatedSQLLiteralInUTF8_ = FALSE;
}
// A copy constructor used by the SampleSize derived class
//
ConstValue::ConstValue(OperatorTypeEnum otype,
ConstValue *other,
NAMemory * outHeap)
: ItemExpr(otype)
// , isNull_(other.isNull())
, type_(other->getType())
, storageSize_(other->getStorageSize())
, textIsValidatedSQLLiteralInUTF8_(other->textIsValidatedSQLLiteralInUTF8_)
, isSystemSupplied_(other->isSystemSupplied_)
, locale_strval(other->locale_strval)
, locale_wstrval(other->locale_wstrval)
, isStrLitWithCharSetPrefix_(other->isStrLitWithCharSetPrefix_)
, rebindNeeded_(other->rebindNeeded_)
{
value_ = (void *)( new (outHeap) char[storageSize_] );
memcpy(value_,(void *)other->getConstValue(),(Int32)storageSize_);
text_ = new (outHeap)
NAString(other->getText(), outHeap);
}
ConstValue::ConstValue(const ConstValue& s, NAHeap *h)
: ItemExpr(ITM_CONSTANT), isNull_(s.isNull_), type_(s.type_)
, storageSize_(s.storageSize_)
, textIsValidatedSQLLiteralInUTF8_(s.textIsValidatedSQLLiteralInUTF8_)
, isSystemSupplied_(s.isSystemSupplied_)
, isStrLitWithCharSetPrefix_(s.isStrLitWithCharSetPrefix_)
, rebindNeeded_(s.rebindNeeded_)
{
if (s.value_) {
value_ = (void *)( new (h) char[storageSize_] );
memcpy(value_,(void *)s.value_,(Int32)storageSize_);
}
if (s.text_) {
text_ = new (h) NAString(*s.text_, h);
}
}
ConstValue::ConstValue(const ConstValue& s)
: ItemExpr(ITM_CONSTANT), isNull_(s.isNull_), type_(s.type_)
, storageSize_(s.storageSize_), value_(s.value_)
, textIsValidatedSQLLiteralInUTF8_(s.textIsValidatedSQLLiteralInUTF8_)
, text_(s.text_), isSystemSupplied_(s.isSystemSupplied_)
, isStrLitWithCharSetPrefix_(s.isStrLitWithCharSetPrefix_)
, rebindNeeded_(s.rebindNeeded_)
{
}
ConstValue::~ConstValue()
{
// type_ can be shared by multiple consts --
// see ConstValue::copyTopNode for a good example --
// so we must not delete it!
// delete type_;
NADELETEBASIC((char*)value_,CmpCommon::statementHeap()); // value_ is a void*
if (text_)
NADELETEBASIC((NAString*)text_,CmpCommon::statementHeap());
}
NABoolean ConstValue::isAUserSuppliedInput() const { return TRUE; }
void ConstValue::changeStringConstant(const NAString* strval)
{
NADELETEBASIC((char*)value_,CmpCommon::statementHeap()); // value_ is a void*
if (strval -> length() == 0)
{
// create a varchar constant of length 0, in this case.
storageSize_ = type_->getVarLenHdrSize();
value_ = (void *)( new (CmpCommon::statementHeap()) char[storageSize_] );
str_pad((char *)value_, (Int32)storageSize_, '\0');
} else {
storageSize_ = strval -> length();
value_ = (void *)( new (CmpCommon::statementHeap()) char[storageSize_] );
memcpy(value_,(void *)(strval -> data()),(Int32)storageSize_);
}
*text_ = *strval;
}
NABoolean ConstValue::isAFalseConstant() const
{
NABoolean result = FALSE;
if (type_->getTypeQualifier() == NA_BOOLEAN_TYPE && !isNull())
{
CMPASSERT(storageSize_ == sizeof(Int32));
if (*(reinterpret_cast<Int32 *>(value_)) == 0)
result = TRUE; // that means the constant is FALSE!!
}
return result;
}
NABoolean ConstValue::isExactNumeric() const
{
return (type_->getTypeQualifier() == NA_NUMERIC_TYPE AND
((NumericType *)type_)->isExact());
}
// exact numeric value can only be returned for certain types
// and if the value is within max largeint range.
NABoolean ConstValue::canGetExactNumericValue() const
{
if (isExactNumeric())
{
NumericType &t = (NumericType &) *type_;
// if unsigned largeint and value greater than largeint max,
// cannot return exact numeric value.
if ((t.getFSDatatype() == REC_BIN64_UNSIGNED) &&
((*(UInt64*)value_) > LLONG_MAX))
return FALSE;
// for now we can't do it for arbitrary exact numeric types, sorry
if (NOT t.isDecimal() AND
NOT t.isComplexType() AND
NOT t.supportsSQLnull() AND
NOT t.getPrefixSize())
return TRUE;
}
return FALSE;
}
Int64 ConstValue::getExactNumericValue(Lng32 &scale) const
{
CMPASSERT(canGetExactNumericValue());
Int64 result = 0;
NumericType &t = (NumericType &) *type_;
// for now we are looking at a binary representation of a number
// that is either 2, 4, or 8 bytes.
scale = t.getScale();
CMPASSERT(t.getNominalSize() == storageSize_);
switch (storageSize_)
{
case 1:
if (t.isUnsigned())
result = *((UInt8 *) value_);
else
result = *((Int8 *) value_);
break;
case 2:
if (t.isUnsigned())
result = *((unsigned short *) value_);
else
result = *((short *) value_);
break;
case 4:
if (t.isUnsigned())
result = uint32ToInt64(*((ULng32 *) value_));
else
result = *((Lng32 *) value_);
break;
case 8:
if (t.isUnsigned())
{
if ((*(UInt64*)value_) > LLONG_MAX)
{
CMPASSERT(0);
}
else
result = *(UInt64*)value_;
}
else
result = *((Int64 *) value_);
break;
default:
CMPASSERT(0);
break;
}
return result;
}
NABoolean ConstValue::canGetApproximateNumericValue() const
{
// return TRUE if this is an approximate numeric type or if we
// can return an exact numeric value
return (type_->getTypeQualifier() == NA_NUMERIC_TYPE &&
((!((NumericType *) type_)->isExact() ||
canGetExactNumericValue())));
}
double ConstValue::getApproximateNumericValue() const
{
CMPASSERT(canGetApproximateNumericValue());
switch (type_->getFSDatatype())
{
case REC_IEEE_FLOAT32:
return *((float *) value_);
case REC_IEEE_FLOAT64:
return *((double *) value_);
default:
{
Int32 scale;
double tempResult = (double) getExactNumericValue(scale);
return tempResult * pow(10,-scale);
}
}
}
void ConstValue::getOffsetsInBuffer(int &nullIndOffset,
int &vcLenOffset,
int &dataOffset)
{
int nextOffset = 0;
// This uses the alignment method of the ConstVal, which is slightly
// different from other formats, no alignment is used.
// See also method ExpGenerator::placeConstants()
if (type_->supportsSQLnull())
{
nullIndOffset = nextOffset;
nextOffset += type_->getSQLnullHdrSize();
}
else
nullIndOffset = -1;
if (type_->isVaryingLen())
{
vcLenOffset = nextOffset;
nextOffset += type_->getVarLenHdrSize();
}
else
vcLenOffset = -1;
dataOffset = nextOffset;
}
// The implementation checks for identity rather than equality,
// that is, the following method returns TRUE when the two
// ConstValues are identical in all respects except that they
// are implemented in two different storage locations.
NABoolean ConstValue::operator== (const ItemExpr& other) const // virtual meth
{
if (ItemExpr::operator==(other)) return TRUE;
if (getOperatorType() != other.getOperatorType()) return FALSE;
const ConstValue &otherConst = (const ConstValue &)other;
// There is room for more creativity here, but it requires
// the use of type conversion functions.
if (type_ != otherConst.type_) return FALSE;
if (storageSize_ != otherConst.storageSize_) return FALSE;
// Perform a byte-by-byte comparison of the bit pattern for the value.
if (memcmp(value_, otherConst.value_, (size_t)storageSize_)) return FALSE;
return TRUE;
}
ConstValue * ConstValue::castToConstValue(NABoolean & negate_it)
{
negate_it = FALSE;
return this;
}
Int32 ConstValue::getArity() const { return 0; }
NAString ConstValue::getConstStr(NABoolean transformNeeded) const
{
if ((*text_ == "<min>") || (*text_ == "<max>"))
return *text_ ;
if (getType()->getTypeQualifier() == NA_DATETIME_TYPE &&
getType()->getPrecision() != SQLDTCODE_MPDATETIME)
{
return getType()->getSimpleTypeName() + " '" + *text_ + "'";
}
else if(getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
{
CharType* chType = (CharType*)getType();
NAString txt;
if ( transformNeeded )
txt = getText();
else
txt = getTextForQuery(QUERY_FORMAT);
// if result doesn't have a charset specifier already, add one
if (txt.index(SQLCHARSET_INTRODUCER_IN_LITERAL) == 0)
return txt;
else
return chType->getCharSetAsPrefix() + txt;
}
else
{
return *text_;
}
}
// Genesis 10-980402-1556 (see Binder)
ConstValue * ConstValue::toUpper(CollHeap *h)
{
ConstValue *cv;
if (isNull()) return this; // nothing to be done
const CharType *typ = (const CharType *)getType();
if (getType()->getTypeQualifier() != NA_CHARACTER_TYPE)
return this;
switch(typ->getCharSet())
{
case CharInfo::KSC5601_MP:
case CharInfo::KANJI_MP:
{
// MP does not allow SQL upper/lower functions
// to be applicable to KANJI/KSC5601 charsets.
return this;
}
case CharInfo::UNICODE:
{
// upshift the text and put it into wcUpshift
NAWString wcUpshift(h);
unicode_char_set::to_upper((NAWchar *)value_,
typ->getStrCharLimit(),
wcUpshift);
// create new ConstValue with the upshifted text
cv = new (h) ConstValue(wcUpshift,
typ->getCharSet(),
typ->getCollation(),
typ->getCoercibility());
cv->changeType(typ);
break;
}
default:
{
const unsigned char *c = (const unsigned char*)(text_->data());
// check if it's already upshifted
for (; *c; c++)
if (islower(*c)) break;
if (!*c) return this;
// upshift the text and return a new constant
NAString upshift(*text_);
upshift.toUpper();
cv = new (h) ConstValue(upshift,
typ->getCharSet(),
typ->getCollation(),
typ->getCoercibility());
cv->changeType(typ);
break;
}
}
CMPASSERT(*typ == *cv->getType());
return cv;
}
const NAString ConstValue::getText4CacheKey() const
{
if(getType()->getTypeQualifier() != NA_CHARACTER_TYPE) {
return getText();
}
else {
NAString result = getText();
// we want to return _charset'strfoo' instead of 'strfoo'.
// This is to fix genesis case 10-040616-0347 "NF: query cache does not
// work properly on || for certain character set". The root cause here
// was ItemExpr::generateCacheKey() incorrectly returning the same string
// keys for both
// select ?p1 || _ksc5601'arg' from ...
// select ?p1 || _kanji'arg from ...
// The solution is to make them different via charset prefixes.
if (result.index(SQLCHARSET_INTRODUCER_IN_LITERAL) != 0)
result.prepend(((CharType*)getType())->getCharSetAsPrefix());
return result;
}
}
const NAString ConstValue::getTextForQuery() const
{
if(getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
{
NAString result("\'", CmpCommon::statementHeap());
if (text_) result += *text_;
RemoveTrailingZeros(result);
result += "\'";
return result;
}
else
return *text_;
}
const NAString ConstValue::getTextForQuery(UnparseFormatEnum form) const
{
if(getType()->getTypeQualifier() != NA_CHARACTER_TYPE ||
textIsValidatedSQLLiteralInUTF8_)
return *text_;
//
// getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
//
NABoolean charSetConvFailed = FALSE;
switch (form)
{
case QUERY_FORMAT:
case MV_SHOWDDL_FORMAT:
case USER_FORMAT_DELUXE:
{
NAString result((NAMemory *)CmpCommon::statementHeap());
if (text_)
{
switch (((CharType*)getType())->getCharSet())
{
case CharInfo::UCS2:
{
// Assume that *text_ contains Unicode UCS-2/UTF-16 encoding values.
// For SeaQuest, query text should be in Unicode UTF-8 encoding format.
NAString *utf8Str = unicodeToChar ( (const NAWchar *)text_->data() // source
, text_->length/*in_bytes*/() / BYTES_PER_NAWCHAR
, CharInfo::UTF8 // target CS
, CmpCommon::statementHeap() // heap for target
);
if (utf8Str) // Translation was successful
{
result += *utf8Str;
delete utf8Str;
utf8Str = NULL;
}
else // Translation failed - Assume *text_ already in UTF-8 encoding
{
result += *text_;
charSetConvFailed = TRUE;
}
}
break;
case CharInfo::ISO88591:
{
NAString utf8String = Latin1StrToUTF8 ( *text_, CmpCommon::statementHeap() );
if (utf8String.isNull()) // Translation failed - Assume *text_ already in UTF-8 encoding
{
result += *text_;
charSetConvFailed = TRUE;
}
else // Translation was successful
result += utf8String;
}
break;
// case CharInfo::SJIS:
// {
// NAString utf8Str2 = SjisStrToUTF8 ( *text_, CmpCommon::statementHeap() );
// if (utf8Str2.isNull()) // Translation failed - Assume *text_ already in UTF-8 encoding
// {
// result += *text_;
// charSetConvFailed = TRUE;
// }
// else // Translation was successful
// result += utf8Str2;
// }
// break;
default: // Assume *text_ already in UTF-8 encoding
result += *text_;
break;
} // switch (((CharType*)getType())->getCharSet())
} // if (text_)
RemoveTrailingZeros(result);
NAString extStrLit;
if (result.isNull())
extStrLit = "''";
else if (charSetConvFailed)
{
extStrLit = "'";
extStrLit += result;
extStrLit += "'";
}
else
ToQuotedString(extStrLit/*out*/, result/*in - internalString*/, TRUE/*in - encloseInQuotes*/);
return extStrLit;
}
break;
default:
return getTextForQuery();
break;
} // switch (((CharType*)getType())->getCharSet())
return getTextForQuery(); // to keep the C++ compiler happy
}
const NAString ConstValue::getText() const
{
if(getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
{
NAString result(CmpCommon::statementHeap());
if (!textIsValidatedSQLLiteralInUTF8_)
result += "\'";
if (text_) result += *text_;
if (!textIsValidatedSQLLiteralInUTF8_)
result += "\'";
// Change imbedded NULL and \377 chars to \0 and \377
// This comes up in key values quite often.
size_t index;
while((index = result.first('\0')) != NA_NPOS
&& index != result.length())
result(index,1) = "\\0";
while((index = result.first('\377')) != NA_NPOS
&& index != result.length())
result(index,1) = "\\377";
return result;
}
else
return *text_;
}
NABoolean ConstValue::isEmptyString() const
{
if (getType()->getTypeQualifier() == NA_CHARACTER_TYPE) {
if (text_ && text_->length() == 0) { return TRUE; }
}
return FALSE;
}
void ConstValue::unparse(NAString &result,
PhaseEnum /*phase*/,
UnparseFormatEnum form,
TableDesc * tabId) const
{
if (form == EXPLAIN_FORMAT)
{
// for EXPLAIN, abbreviate very long text values:
// drop any repeated text from the end, and drop any text that
// is more than 30 characters
NAString fullText(getText(), CmpCommon::statementHeap());
const Int32 tooLong = 30; // keep strings shorter than this
const Int32 shortEnough = 10; // leave strings of this length alone
// get the full text in query text format first
fullText = getTextForQuery(QUERY_FORMAT);
// then truncate it if too long
if (fullText.length() > shortEnough)
{
NABoolean truncated = FALSE;
// the last character often is a quote and should be preserved
char lastchar = fullText[fullText.length()-1];
// more than <tooLong> characters are unlikely to convey
// a lot of extra info
if (fullText.length() > tooLong)
{
fullText.remove(tooLong);
// if there are unprintable characters in the string,
// remove entire escape sequences
if (fullText.last('\\') != NA_NPOS)
fullText.remove(fullText.last('\\'));
truncated = TRUE;
}
// try to reduce the text even further if the information is
// repetitive at the end of the string
CollIndex len = fullText.length();
// skip the last character unless we have already removed it
if (NOT truncated)
len--;
// remove repeating patterns of length 1, 2, or 4 from the end
// of the string (patterns could be ' ', '\0', '377', etc.)
while (len >= 8 AND fullText(len-4,4) == fullText(len-8,4))
len -= 4;
while (len >= 4 AND fullText(len-2,2) == fullText(len-4,2))
len -= 2;
while (len >= 2 AND fullText(len-1,1) == fullText(len-2,1))
len -= 1;
// remove all the repetitive information except 3 characters
// but only if it's worth it (we have to add 4 extra characters
// to indicate the omission and want to leave 3 repeating chars...)
if (len+7 < fullText.length())
{
fullText.remove(len+3);
truncated = TRUE;
}
if (truncated)
{
fullText += " ...";
fullText += lastchar;
}
}
result += fullText;
}
else if ((form == MVINFO_FORMAT) || (form == QUERY_FORMAT) ||
(form == MV_SHOWDDL_FORMAT) || (form == COMPUTED_COLUMN_FORMAT))
{
if (getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
{
CharType* chType = (CharType*)getType();
if (form == MVINFO_FORMAT)
result += getText();
else
{
if (!textIsValidatedSQLLiteralInUTF8_)
result += chType->getCharSetAsPrefix();
if ((form == MV_SHOWDDL_FORMAT) || (form == QUERY_FORMAT))
result += getTextForQuery(form);
else
result += getTextForQuery();
}
}
else
{
if (getType()->getTypeQualifier() == NA_DATETIME_TYPE &&
!textIsValidatedSQLLiteralInUTF8_)
{
if (getType()->getTypeName() == "DATE")
result += " DATE '";
else if (getType()->getTypeName() == "TIME")
result += " TIME '";
else if (getType()->getTypeName() == "TIMESTAMP")
result += " TIMESTAMP '";
}
result += getText();
if (getType()->getTypeQualifier() == NA_DATETIME_TYPE &&
!textIsValidatedSQLLiteralInUTF8_)
result += "'";
}
} // MVINFO_FORMAT || QUERY_FORMAT || MV_SHOWDDL_FORMAT || COMPUTED_COLUMN_FORMAT
else if ((form == USER_FORMAT_DELUXE) ||
(form == HIVE_MD_FORMAT))
{
if (getType()->getTypeQualifier() == NA_CHARACTER_TYPE)
{
// Intentionally not adding the string literal character set prefix
// to avoid updating the regression test expected results.
result += getTextForQuery(form);
}
else
result += getText();
}
else
{
// in all other cases use the getText() method
result += getText();
}
}
const NAString * ConstValue::getRawText() const
{
return text_;
}
HashValue ConstValue::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= isNull();
if (NOT isNull())
{
// hash binary representation of value, but no more than 10
// bytes (diminishing return for hashing very long strings)
Int32 maxc = MINOF(storageSize_, 10);
for (Int32 i=0; i < maxc; i++)
result ^= ((char *)value_)[i];
}
return result;
}
NABoolean ConstValue::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
ConstValue &o = (ConstValue &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (NOT (*type_ == *(o.type_)))
return FALSE;
if (isNull() != o.isNull())
return FALSE;
if (isNull())
return TRUE;
if (storageSize_ != o.storageSize_)
return FALSE;
if (str_cmp((char *) value_, (char *)o.value_, storageSize_) != 0 OR
isSystemSupplied_ != o.isSystemSupplied_)
return FALSE;
return TRUE;
}
ItemExpr * ConstValue::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ConstValue *result;
if (derivedNode == NULL)
{
// According to the constructor that initiates isNull_ to NULL the value_
// void pointer is set to NULL, however there are places in the code
// like SearchKeyBounds::::computeMissingKeyValue() that creates a
// ConstValue with isNull set to IS_NULL and Value set to NULL
// indicator. So it is not right to assume that when isNULL is set
// value will be empty. So creating an empty ConstValue only when
// isNULL() is true and value_ not set, otherwise we go to else
// and call a constructor that copies isNULL_ flag along with value_
if (isNull() && !value_)
{
//CMPASSERT(!value_);
result = new (outHeap) ConstValue; // a NULL
}
else
{
/* soln:10-050710-9594 begin */
/* Changed the constructor that is being called so that value
* of locale_strval and locale_wstrval are not lost
*/
result = new (outHeap) ConstValue(type_,value_,storageSize_,
locale_strval, locale_wstrval,
text_,outHeap, isNull_);
/* soln:10-050710-9594 end */
result->textIsValidatedSQLLiteralInUTF8_ =
textIsValidatedSQLLiteralInUTF8_;
result->setRebindNeeded(isRebindNeeded());
}
}
else
result = (ConstValue *) derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
NABoolean ConstValue::isCovered(const ValueIdSet& /* newExternalInputs */,
const GroupAttributes& /* coveringGA */,
ValueIdSet& /* referencedInputs */,
ValueIdSet& /* coveredSubExpr */,
ValueIdSet& /*unCoveredExpr*/) const
{
// ---------------------------------------------------------------------
// Constants are always covered
// ---------------------------------------------------------------------
return TRUE;
} // ConstValue::isCovered()
// ------------------------------------------------------------------------
// computeHashValues creates a hash value for a constant.
// Hash value is created only for character and numeric column types
// ------------------------------------------------------------------------
UInt32 ConstValue::computeHashValue(const NAType& columnType)
{
UInt32 hashValue = 0;
switch ( getType()->getTypeQualifier() )
{
case NA_CHARACTER_TYPE:
{
UInt32 flags = ExHDPHash::NO_FLAGS;
const CharType *cType = (CharType *)(&columnType);
if(cType->getCharSet() == CharInfo::UNICODE) {
flags = ExHDPHash::SWAP_TWO;
}
// For a case-insensitive char type, it is necessary to upper-case the
// boundary value first before compute the hash. This is to mimic the
// run-time behavior where values of such char type are always
// upper shifted before case-insensitive comparison, being hashed or
// encoded. Upshifting has already been done during encoding step.
hashValue = ExHDPHash::hash((char*)(getConstValue()), flags, getStorageSize());
}
break;
case NA_NUMERIC_TYPE :
// only handle non-big NUMERICs that require 8-or-less bytes to store
// for now.
if ( getType()->getTypeName() == LiteralNumeric AND
columnType.getTypeName() == LiteralNumeric AND
columnType.getNominalSize() <= 8
)
{
// 10-080124-0055 (Skewbuster numeric uneven distribution
// different scales).
// Convert the constant to the exact NUMERIC type of the column
// before applying the hash on it. This is necessary because for
// example 4 bytes is sufficient to represent a small scale boundary
// value such as 1.0, while the storage size of the column can be
// 8-bytes. Without converting the number of bytes of the value to
// that of the column, the hashing result can be different.
//
// There is still a chance that the skews are not busted when the
// fact table side has a scale smaller than that of the dimension
// table. For example numeric(18,2) on fact side and
// numeric(18, 4) on dimension side. In this case, the hash values
// computed for the fact table side will not match the actual
// values used for repartition or hashing because a CAST will be
// used on the fact side and repartition and hashing will use the
// output from the CAST.
const SQLNumeric& type = (const SQLNumeric&)(columnType);
Lng32 len = type.getNominalSize();
char result[8];
// Ignore errors because the boundary value pointed by cvPtr
// is read from the histogram table and should be have been cleared
// off any precision or scale problems by the update histogram
// utility.
short retCode = convDoIt((char*)(getConstValue()),
getStorageSize(),
(short)getType()->getFSDatatype(),
getType()->getPrecision(),
getType()->getScale(),
result,
len,
(short)(type.getFSDatatype()),
type.getPrecision(),
type.getScale(),
NULL /* null varchar ptr*/, 0 /* varchar len*/);
DCMPASSERT(retCode == ex_expr::EXPR_OK);
UInt32 flags = ExHDPHash::NO_FLAGS;
switch(type.getFSDatatype()) {
case REC_NUM_BIG_UNSIGNED:
case REC_NUM_BIG_SIGNED:
case REC_BIN16_SIGNED:
case REC_BIN16_UNSIGNED:
flags = ExHDPHash::SWAP_TWO;
break;
case REC_BIN32_SIGNED:
case REC_BIN32_UNSIGNED:
case REC_IEEE_FLOAT32:
flags = ExHDPHash::SWAP_FOUR;
break;
case REC_BIN64_SIGNED:
case REC_BIN64_UNSIGNED:
case REC_IEEE_FLOAT64:
flags = ExHDPHash::SWAP_EIGHT;
break;
}
hashValue = ExHDPHash::hash(result, flags, len);
}
break;
default:
break;
}
return hashValue;
}
// -----------------------------------------------------------------------
// does the value of this constant (if char) has trailing blanks
// -----------------------------------------------------------------------
NABoolean ConstValue::valueHasTrailingBlanks()
{
NABoolean hasATrailingBlanks = FALSE;
if (value_ && (getType()->getTypeQualifier() == NA_CHARACTER_TYPE))
{
const CharType *typ = (const CharType *)getType();
if (typ->getCharSet() == CharInfo::UNICODE)
{
NAWchar* nawCharVal = (NAWchar *) value_;
Int32 bytesPerChar = (CharInfo::maxBytesPerChar)(typ->getCharSet());
Int32 lastPos = (storageSize_/bytesPerChar)-1;
if ((lastPos >= 0) && (nawCharVal[lastPos] == L' '))
{
hasATrailingBlanks = TRUE;
}
}
else
{
char* charVal = (char *) value_;
Int32 lastPos = storageSize_-1;
if ((lastPos >= 0) && (charVal[lastPos] == ' '))
{
hasATrailingBlanks = TRUE;
}
}
}
return hasATrailingBlanks;
}
QR::ExprElement ConstValue::getQRExprElem() const
{
return QR::QRScalarValueElem;
}
// -----------------------------------------------------------------------
// member functions for class RenameCol
// -----------------------------------------------------------------------
Int32 RenameCol::getArity() const { return (child(0) ? 1 : 0); }
const NAString RenameCol::getText() const
{
return ("rename as " + newColRefName_->getColName());
}
HashValue RenameCol::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
result ^= newColRefName_->getColRefAsString();
return result;
}
NABoolean RenameCol::duplicateMatch(const ItemExpr & other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
RenameCol &o = (RenameCol &) other;
// compare any local data members of the derived class
// and return FALSE if they don't match
if (NOT (*newColRefName_ == *(o.newColRefName_)))
return FALSE;
return TRUE;
}
ItemExpr * RenameCol::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
RenameCol *newRename = new (outHeap)
RenameCol(NULL,
new (outHeap)
ColRefName(newColRefName_->getColName(),
newColRefName_->getCorrNameObj(),
outHeap));
newRename->setTargetColumnClass(getTargetColumnClass());
result = newRename;
}
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class Convert
// -----------------------------------------------------------------------
Int32 Convert::getArity() const { return 1; }
HashValue Convert::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
return result;
}
ItemExpr * Convert::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
Convert *result;
if (derivedNode == NULL)
result = new (outHeap) Convert(NULL);
else
result = (Convert *) derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class Assign
// -----------------------------------------------------------------------
Int32 Assign::getArity() const { return 2; }
NABoolean Assign::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr ) const
{
// ---------------------------------------------------------------------
// Compute the referenced input values, covered subexpressions and
// uncovered expressions for the Assign.
// ---------------------------------------------------------------------
// The target is never covered
if (coveringGA.covers(getSource(), newExternalInputs,
referencedInputs,
&coveredSubExpr, &unCoveredExpr))
coveredSubExpr += getSource();
// ---------------------------------------------------------------------
// Regardless of whether its operands are covered, the Assignment
// must be performed by the RelExpr with which it is associated.
// ---------------------------------------------------------------------
return FALSE; // sorry, i am a special case
} // Assign::isCovered()
ItemExpr * Assign::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
Assign *result;
if (derivedNode == NULL)
result = new (outHeap) Assign(NULL, NULL);
else
result = (Assign *) derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
HashValue Assign::topHash()
{
HashValue result = ItemExpr::topHash();
// hash any local data members of the derived class
return result;
}
// -----------------------------------------------------------------------
// member functions for class ItemList
// -----------------------------------------------------------------------
Int32 ItemList::getArity() const { return 2; }
NABoolean ItemList::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
ValueId exprId;
NABoolean exprIsCovered = TRUE;
NABoolean coverFlag;
for (Lng32 i = 0; i < (Lng32)getArity(); i++)
{
ItemExpr *thisChild = (ItemExpr *)child(i);
ItemExprList childList(thisChild,0);
for (CollIndex j = 0; j < childList.entries(); j++)
{
// Recurse, inserting leaf valueids into the cov/uncov sets
// without looking at or setting the local exprIsCovered var
exprId = childList[j]->getValueId();
coverFlag = coveringGA.covers(exprId,
newExternalInputs,
referencedInputs,
&coveredSubExpr,
&unCoveredExpr);
if (coverFlag)
coveredSubExpr += exprId;
exprIsCovered &= coverFlag;
} // for j'th item in list
} // for i'th child
return exprIsCovered;
} // ItemList::isCovered()
ItemExpr * ItemList::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemList *result;
if (derivedNode == NULL)
result = new (outHeap) ItemList(NULL,NULL);
else
result = (ItemList *) derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
NABoolean ItemList::duplicateMatch(const ItemExpr& other) const
{
if (NOT genericDuplicateMatch(other))
return FALSE;
return TRUE;
}
NABoolean ItemList::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
ItemList * otherList = (ItemList *) other;
return
(this->numOfItems() == otherList->numOfItems()) &&
(this->constChild() == otherList->constChild()) ;
}
// -----------------------------------------------------------------------
// member functions for Subquery operators
// -----------------------------------------------------------------------
Int32 Subquery::getArity() const
{
switch (getOperatorType())
{
case ITM_ROW_SUBQUERY:
case ITM_EXISTS:
case ITM_NOT_EXISTS:
return 0;
default:
return 1;
}
} // Subquery::getArity()
// Virtual method
NABoolean Subquery::isASubquery() const { return TRUE; }
NABoolean Subquery::isAnAnySubquery() const
{
switch (getOperatorType())
{
case ITM_EQUAL_ANY:
case ITM_NOT_EQUAL_ANY:
case ITM_GREATER_ANY:
case ITM_GREATER_EQ_ANY:
case ITM_LESS_EQ_ANY:
case ITM_LESS_ANY:
return TRUE;
default:
return FALSE;
}
} // Subquery::isAnAnySubquery()
NABoolean Subquery::isAnAllSubquery() const
{
switch (getOperatorType())
{
case ITM_EQUAL_ALL:
case ITM_NOT_EQUAL_ALL:
case ITM_GREATER_ALL:
case ITM_GREATER_EQ_ALL:
case ITM_LESS_EQ_ALL:
case ITM_LESS_ALL:
return TRUE;
default:
return FALSE;
}
} // Subquery::isAnAnySubquery()
const NAString Subquery::getText() const
{
return "unknown subquery";
} // Subquery::getText()
void Subquery::addLocalExpr(LIST(ExprNode *) &xlist,
LIST(NAString) &llist) const
{
xlist.insert(tableExpr_);
llist.insert("Subquery");
}
NABoolean Subquery::containsSubquery()
{
return TRUE;
} // Subquery::containsSubquery
// <aviv> BEGIN
ItemExpr *
Subquery::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
RelExpr* tableExprCopy = tableExpr_->copyTree(outHeap);
result = new (outHeap) Subquery(getOperatorType(), tableExprCopy);
}
else
result = derivedNode;
((Subquery *)result)->setAvoidHalloweenR2(getAvoidHalloweenR2());
return ItemExpr::copyTopNode(result, outHeap);
}
// output degree functions to handle multi-output subqueries.
// Returns the number of outputs and the ItemExpr for each.
Int32 Subquery::getOutputDegree()
{
if (!getRETDesc()) return -1; // Routine desc not set. Error.
else
return getRETDesc()->getColumnList()->entries();
}
ItemExpr *Subquery::getOutputItem(UInt32 i)
{
if (!getRETDesc()) return NULL; // Routine desc not set. Error.
else
{
if (i < getRETDesc()->getColumnList()->entries())
return getRETDesc()->getValueId(i).getItemExpr();
else return NULL; // 'i' out of bounds. Error.
}
}
// <aviv> END
// -----------------------------------------------------------------------
// member functions for row subquery operators
// -----------------------------------------------------------------------
const NAString RowSubquery::getText() const
{
//return "row subquery";
NAString tmp(CmpCommon::statementHeap());
if (getSubquery()->child(0))
getSubquery()->child(0)->unparse(tmp); // unparse below the subq root
else
getSubquery()->unparse(tmp); // unparse the tuple
return tmp;
}
ItemExpr *
RowSubquery::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
RelExpr* tableExprCopy = getSubquery()->copyTree(outHeap);
result = new (outHeap) RowSubquery(tableExprCopy);
}
else
result = derivedNode;
return Subquery::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for Quantified comparison subquery operators
// -----------------------------------------------------------------------
const NAString QuantifiedComp::getText() const
{
switch (getOperatorType())
{
case ITM_EQUAL_ANY:
return "= any";
case ITM_NOT_EQUAL_ANY:
return "<> any";
case ITM_GREATER_ANY:
return "> any";
case ITM_GREATER_EQ_ANY:
return ">= any";
case ITM_LESS_EQ_ANY:
return "<= any";
case ITM_LESS_ANY:
return "< any";
case ITM_EQUAL_ALL:
return "= all";
case ITM_NOT_EQUAL_ALL:
return "<> all";
case ITM_GREATER_ALL:
return "> all";
case ITM_GREATER_EQ_ALL:
return ">= all";
case ITM_LESS_EQ_ALL:
return "<= all";
case ITM_LESS_ALL:
return "< all";
default:
return "? any|all ";
}
} // QuantifiedComp::getText()
// Instead of this next function, we could add a wildcard to OperatorTypeEnum
// and augment the OperatorType::match() function in ExprNode.cpp...
static NABoolean isQuantifiedComp(const ItemExpr *ie)
{
switch (ie->getOperatorType())
{
case ITM_EQUAL_ANY:
case ITM_NOT_EQUAL_ANY:
case ITM_GREATER_ANY:
case ITM_GREATER_EQ_ANY:
case ITM_LESS_EQ_ANY:
case ITM_LESS_ANY:
case ITM_EQUAL_ALL:
case ITM_NOT_EQUAL_ALL:
case ITM_GREATER_ALL:
case ITM_GREATER_EQ_ALL:
case ITM_LESS_EQ_ALL:
case ITM_LESS_ALL:
return TRUE;
default:
return FALSE;
}
} // isQuantifiedComp()
// <aviv> BEGIN
ItemExpr *
QuantifiedComp::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
QuantifiedComp *result;
if (derivedNode == NULL) {
RelExpr* tableExprCopy = getSubquery()->copyTree(outHeap);
// NOTICE: using child(0) here is based on the Subquery and ItemExpr constructors
// This is bad programming !!! A solution could be to change the order of
// the parameters in this ctor and using defaults (NULL), yet it involves
// changes in many other places in the code <aviv>
result = new (outHeap) QuantifiedComp(getOperatorType(),
(child(0))->copyTree(outHeap),
tableExprCopy,
getAvoidHalloweenR2());
}
else
result = (QuantifiedComp*)derivedNode;
result->createdFromINlist_ = createdFromINlist_;
result->createdFromALLpred_ = createdFromALLpred_;
return Subquery::copyTopNode(result, outHeap);
}
// <aviv> END
// -----------------------------------------------------------------------
// member functions for In subquery operators
// -----------------------------------------------------------------------
const NAString InSubquery::getText() const { return "in"; }
// <aviv> BEGIN
ItemExpr *
InSubquery::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
RelExpr* tableExprCopy = getSubquery()->copyTree(outHeap);
// NOTICE: using child(0) here is based on the Subquery and ItemExpr constructors
// This is bad programming !!! A solution could be to change the order of
// the parameters in this ctor and using defaults (NULL), yet it involves
// changes in many other places in the code <aviv>
result = new (outHeap) InSubquery(getOperatorType(), (child(0))->copyTree(outHeap), tableExprCopy);
}
else
result = derivedNode;
return Subquery::copyTopNode(result, outHeap);
}
// <aviv> END
// --------------------------------------------------------------
// member functions for Abs operator
// --------------------------------------------------------------
ItemExpr * Abs::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Abs(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for CodeVal operator
// --------------------------------------------------------------
ItemExpr * CodeVal::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CodeVal(getOperatorType(), child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
const NAString CodeVal::getText() const
{
// to fix case 10-040504-4753 (Incorrect error text when using NULL as k
// CODE_VALUE function parameter). The operator type for CodeVal is
// NO_OPERATOR_TYPE until after the type is synthesized. getText() can be
// called before that (e.g., in checkForSQLnullChild()).
//
// Use the general code_value name for such case.
//
if (getOperatorType() == NO_OPERATOR_TYPE )
return "code_value";
else
return BuiltinFunction::getText();
}
// --------------------------------------------------------------
// member functions for AggrMinMax
// --------------------------------------------------------------
ItemExpr * AggrMinMax::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) AggrMinMax(NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for AggrGrouping function
// --------------------------------------------------------------
ItemExpr * AggrGrouping::copyTopNode(ItemExpr * derivedNode, CollHeap* outHeap)
{
AggrGrouping *result;
if (derivedNode == NULL)
result = new (outHeap) AggrGrouping(-1);
else
result = (AggrGrouping*)derivedNode;
result->rollupGroupIndex_ = rollupGroupIndex_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for Exists subquery operators
// -----------------------------------------------------------------------
Exists::Exists(RelExpr * tableExpr)
: Subquery(ITM_EXISTS, tableExpr) {}
Exists::~Exists() {}
const NAString Exists::getText() const
{
if (isNotExists())
return "not_exists";
else
return "exists";
}
// <aviv> BEGIN
ItemExpr *
Exists::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
RelExpr* tableExprCopy = getSubquery()->copyTree(outHeap);
result = new (outHeap) Exists(tableExprCopy);
if (isNotExists())
result->setOperatorType(getOperatorType());
}
else
result = derivedNode;
return Subquery::copyTopNode(result, outHeap);
}
// <aviv> END
// --------------------------------------------------------------
// member functions for Between operator
// --------------------------------------------------------------
ItemExpr * Between::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) Between(NULL,NULL,NULL);
((Between*)result)->leftBoundryIncluded_ = leftBoundryIncluded_;
((Between*)result)->rightBoundryIncluded_ = rightBoundryIncluded_;
// NOTICE: we are copying the pointer, not the allocating new memory
// and copying the object. because of this the vector must never be
// destroyed in any destructor
((Between*)result)->pDirectionVector_ = pDirectionVector_;
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
void Between::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
child(0)->unparse(result,phase,form,tabId);
result += " ";
NAString kwd(getText(), CmpCommon::statementHeap());
if (form == USER_FORMAT_DELUXE) kwd.toUpper();
result += kwd;
result += " ";
child(1)->unparse(result,phase,form,tabId);
kwd = " and ";
if (form == USER_FORMAT_DELUXE) kwd.toUpper();
result += kwd;
child(2)->unparse(result,phase,form,tabId);
}
NABoolean Between::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Between * tmp = (Between *) other;
return
(this->leftBoundryIncluded_ == tmp->leftBoundryIncluded_) &&
(this->rightBoundryIncluded_ == tmp->rightBoundryIncluded_ ) &&
(this->pDirectionVector_ == tmp->pDirectionVector_);
}
// --------------------------------------------------------------
// member functions for BoolResult operator
// --------------------------------------------------------------
BoolResult::~BoolResult() {}
ItemExpr * BoolResult::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) BoolResult(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for BoolVal operator
// --------------------------------------------------------------
ItemExpr * BoolVal::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
if (getArity() == 0)
result = new (outHeap) BoolVal(getOperatorType());
else
result = new (outHeap) BoolVal(getOperatorType(), NULL);
}
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Case operator
// --------------------------------------------------------------
ItemExpr * Case::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Case(NULL, NULL);
else
result = derivedNode;
//++MV bug fix
if (caseOperand_)
((Case*) result)->caseOperand_ = caseOperand_->copyTree(outHeap);
((Case*) result)->caseOperandWasNullable_ = caseOperandWasNullable_;
//--MV
return BuiltinFunction::copyTopNode(result, outHeap);
}
// This method recursively removes from coveredSubExpr,
// all IfThenElse nodes that are part of 'expr' and for those IfThenElse
// nodes, adds its children to coveredSubExpr.
// input: expr, a non-null IfThenElse node.
// Added another parameter boolean ifThenEsleExists, which would be passed
// from the parent ifThenElse to its children. This will ensure that if there
// is more than two levels of if_then_else nesting, then even the inner
// if then else is correctly fixed. Sol: 10-041025-1038
void Case::fixIfThenElse(ItemExpr * expr, ValueIdSet& coveredSubExpr, NABoolean ifThenElseExists) const
{
if ((! expr) ||
(expr->getOperatorType() != ITM_IF_THEN_ELSE))
return;
// remove expr from coveredSubExpr, if it is there
if (coveredSubExpr.contains(expr->getValueId()))
{
ifThenElseExists = TRUE;
coveredSubExpr.remove(expr->getValueId());
}
// and add all its children to it
for (Lng32 index = 0; index < expr->getArity(); index++)
{
if (expr->child(index))
{
if (expr->child(index)->getOperatorType() == ITM_IF_THEN_ELSE)
fixIfThenElse(expr->child(index), coveredSubExpr, ifThenElseExists);
else if (ifThenElseExists)
coveredSubExpr.insert(expr->child(index)->getValueId());
}
}
}
NABoolean Case::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr ) const
{
ValueIdSet coveredSubExprForCase ;
NABoolean retVal =
BuiltinFunction::isCovered(newExternalInputs, coveringGA,
referencedInputs, coveredSubExprForCase,
unCoveredExpr);
// The case expression is covered as a unit. Parts of a case
// expression must not be pushed down independently of each other
// as this cause divide by zero type errors (if the case expression
// has different branches for the zero and non-zero cases, see soln.
// 10-081107-7119). We do not column references and leaf values
// to be counted as covered if they are. We use the getLeafValuesForCoverTest
// for this purpose. Note that Case::getLeafValuesForCoverTest() returns
// itself.
const ValueIdSet emptySet;
//coveredSubExprForCase.getLeafValuesForCoverTest(coveredSubExpr, coveringGA, emptySet);
coveredSubExprForCase.getLeafValuesForCoverTest(coveredSubExpr, coveringGA, newExternalInputs);
return retVal;
}
void Case::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// Is this for MV use?
if ((form != MV_SHOWDDL_FORMAT) &&
(form != QUERY_FORMAT) &&
(form != COMPUTED_COLUMN_FORMAT) )
{
// No - sorry, use the default code.
ItemExpr::unparse(result,phase, form, tabId);
}
else
{
// Yes - avoid the parenthasis:
// Instead of "case (when....)"
// Do "case when ...
result += "case";
child(0)->unparse(result, phase, form, tabId);
}
}
void Case::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
leafValues += getValueId();
} // Case::getLeafValuesForCoverTest()
NABoolean Case::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Case * otherCase = (Case *) other;
return
(this->getCaseOperand()== otherCase->getCaseOperand()) &&
(this->caseOperandWasNullable() == otherCase->caseOperandWasNullable());
}
// --------------------------------------------------------------
// member functions for Cast operator
// --------------------------------------------------------------
Cast::Cast(ItemExpr *val1Ptr, const NAType *type, OperatorTypeEnum otype,
NABoolean checkForTrunc, NABoolean noStringTrunWarnings)
:
CacheableBuiltinFunction(otype, 1, val1Ptr), type_(type),
reverseDataErrorConversionFlag_(FALSE),
flags_(0)
{
ValueId vid = val1Ptr ? val1Ptr->getValueId() : NULL_VALUE_ID;
checkForTruncation_ = FALSE;
if (checkForTrunc)
if (vid == NULL_VALUE_ID)
checkForTruncation_ = TRUE;
else if ( type && type->getTypeQualifier() == NA_CHARACTER_TYPE &&
vid.getType().getTypeQualifier() == NA_CHARACTER_TYPE )
{
if ( type->getNominalSize() < vid.getType().getNominalSize() )
checkForTruncation_ = TRUE;
else
{ // If UTF8 and *any* chance of truncation error
// Ex: casting CHAR(8 BYTES) to CHAR(3)
if ( ((CharType *)type)->getCharSet() == CharInfo::UTF8 &&
((CharType *)type)->getStrCharLimit() < vid.getType().getNominalSize() )
checkForTruncation_ = TRUE;
}
}
setNoStringTruncationWarnings(noStringTrunWarnings);
}
Cast::Cast(ItemExpr *val1Ptr, ItemExpr *errorOutPtr, const NAType *type,
OperatorTypeEnum otype, NABoolean checkForTrunc,
NABoolean reverseDataErrorConversionFlag,
NABoolean noStringTrunWarnings)
:
CacheableBuiltinFunction(otype, 2, val1Ptr, errorOutPtr), type_(type),
reverseDataErrorConversionFlag_(reverseDataErrorConversionFlag),
flags_(0)
{
checkForTruncation_ = checkForTrunc;
setNoStringTruncationWarnings(noStringTrunWarnings);
}
Cast::~Cast() {}
ConstValue * Cast::castToConstValue(NABoolean & negate_it)
{
return child(0)->castToConstValue(negate_it);
}
ItemExpr * Cast::foldConstants(ComDiagsArea * diagsArea,
NABoolean newTypeSynthesis)
{
// Convert a cast on top of a constant into a new constant with
// the correct type. The reason for implementing this special
// case of constant folding is that the optimizer needs to generate
// the actual binary representation of constants in some cases
// (e.g. when generating the split ranges of a partitioning scheme).
ItemExpr *result = this;
if (child(0)->getOperatorType() == ITM_CONSTANT)
{
const NAType &t = child(0)->getValueId().getType();
// simple case, the cast is unnecessary because the child
// already has the correct type: eliminate this node
if (t == *type_)
return child(0);
// try one more case, mapping of numeric data types:
}
return ItemExpr::foldConstants(diagsArea,newTypeSynthesis);
}
NABoolean Cast::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
NABoolean coverFlag = TRUE;
if (coveringGA.covers(getExpr()->getValueId(),
newExternalInputs, referencedInputs,
&coveredSubExpr, &unCoveredExpr))
coveredSubExpr += getExpr()->getValueId();
else
coverFlag = FALSE;
if (getOperatorType() == ITM_INSTANTIATE_NULL)
return FALSE; //
else
return coverFlag;
} // Cast::isCovered()
// MV --
// If this is a Cast over an NATypeToItem, return this ValueId instead
// of the child's. This fixes a problem with using the TupleList for the
// MVLog command.
void Cast::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
if(ITM_NATYPE == child(0)->getOperatorType())
{
leafValues += getValueId();
}
else
{
// Otherwise - call the super class.
BuiltinFunction::getLeafValuesForCoverTest(leafValues, coveringGA, newExternalInputs);
}
}
ItemExpr * Cast::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) Cast(NULL, type_->newCopy(outHeap),
this->getOperatorType(), this->checkForTruncation_);
// Fix CR 10-010426-2464: Copy other members of the class as well
((Cast *)result)->reverseDataErrorConversionFlag_ =
this->reverseDataErrorConversionFlag_;
((Cast *)result)->flags_ = this->flags_;
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
ItemExpr * CastConvert::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CastConvert(NULL, getType()->newCopy(outHeap));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
ItemExpr * CastType::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap)
CastType(NULL,
(getType() ? getType()->newCopy(outHeap) : NULL));
else
result = derivedNode;
((CastType*)result)->makeNullable_ = makeNullable_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
NABoolean Cast::isEquivalentForCodeGeneration(const ItemExpr * other)
{
NABoolean rc = FALSE; // assume failure
if (hasBaseEquivalenceForCodeGeneration(other))
{
// we know that other is a Cast, and that the children are equivalent
Cast * otherCast = (Cast *)other;
const NAType & otherCastType = *(otherCast->type_);
if (type_->operator==(otherCastType))
if (checkForTruncation_ == otherCast->checkForTruncation_)
rc = TRUE; // the two casts can be produced by the same code
}
return rc;
}
NABoolean Cast::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Cast * tmp = (Cast *) other;
return
(this->type_->operator == (*(tmp->type_) )) &&
(this->checkForTruncation_ == tmp->checkForTruncation_ ) &&
(this->reverseDataErrorConversionFlag_ == tmp->reverseDataErrorConversionFlag_ ) &&
// (this->noStringTruncationWarnings_ == tmp->noStringTruncationWarnings_ ) &&
(this->flags_ == tmp->flags_);
}
// --------------------------------------------------------------
// member functions for CharFunc operator
// --------------------------------------------------------------
ItemExpr * CharFunc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
result = new (outHeap) CharFunc(charSet_, child(0));
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for IfThenElse operator
// --------------------------------------------------------------
ItemExpr * IfThenElse::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) IfThenElse(NULL, NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
NABoolean IfThenElse::isCharTypeMatchRulesRelaxable()
{
NABoolean ok =
child(0)->isCharTypeMatchRulesRelaxable() &&
child(1)->isCharTypeMatchRulesRelaxable();
if (child(2).getPtr() != NULL)
ok = ok && child(2)->isCharTypeMatchRulesRelaxable();
return ok;
}
void IfThenElse::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// Is this for MV use?
if ( (form != MV_SHOWDDL_FORMAT) &&
(form != QUERY_FORMAT) &&
(form != COMPUTED_COLUMN_FORMAT))
{
// No - sorry, use the default code.
ItemExpr::unparse(result, phase, form, tabId);
}
else
{
// Instead of: case( if_then_else(cond1, op1, if_then_else(cond2, op2, op3)))
// do this: CASE WHEN cond1 THEN op1 WHEN cond2 THEN op2 ELSE op3 END
result += " WHEN ";
child(0)->unparse(result, phase, form, tabId);
result += " THEN ";
child(1)->unparse(result, phase, form, tabId);
// Any more nested conditions?
if (child(2)->getOperatorType() == ITM_IF_THEN_ELSE)
{
// Yes - Recursive call to the next condition
child(2)->unparse(result, phase, form, tabId);
}
else
{
// No - Do the ELSE.
result += " ELSE ";
child(2)->unparse(result, phase, form, tabId);
result += " END ";
}
}
}
//----------------------------------------------------------------
// member functions of RaiseError operator
//----------------------------------------------------------------
RaiseError::~RaiseError() {}
const NAString RaiseError::getText() const
{
return NAString("RaiseError(") + getConstraintName() + ")";
}
ItemExpr * RaiseError::copyTopNode (ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
if (getArity() > 0) // Do we have string expressions?
result = new (outHeap) RaiseError(getSQLCODE(),
getConstraintName(),
child(0)->copyTree(outHeap),
outHeap);
else
result = new (outHeap) RaiseError(getSQLCODE(),
getConstraintName(),
getTableName(),
outHeap);
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Narrow operator
// --------------------------------------------------------------
Narrow::~Narrow() {}
// Note: We simply inherit isCovered() from Cast
ItemExpr * Narrow::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
// Begin_Fix 10-040114-2431
// 02/18/2004
// changed to copy data member matchChildNullability_
result = new (outHeap) Narrow(NULL,
NULL,
getType()->newCopy(outHeap),
ITM_NARROW,
FALSE,
matchChildNullability_);
// End_Fix 10-040114-2431
((Narrow*)result)->setMatchChildType(matchChildType());
}
else
result = derivedNode;
// $$$
// $$$ shouldn't this last line instead be something like
// $$$ Cast::copyTopNode(result, outHeap); ?!!??
return ItemExpr::copyTopNode(result, outHeap);
}
NABoolean Narrow::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
Narrow * tmp = (Narrow *) other;
return
(this->matchChildNullability_ == tmp->matchChildNullability_);
}
// -----------------------------------------------------------------------
// member functions for class InstantiateNull
// -----------------------------------------------------------------------
// ----------------------------------------------------------------------
// Walk through an ItemExpr tree and gather the ValueIds of those
// expressions that behave as if they are "leaves" for the sake of
// the coverage test, e.g., expressions that have no children, or
// aggregate functions, or instantiate null. These are usually values
// that are produced in one "scope" and referenced above that "scope"
// in the dataflow tree for the query.
// ----------------------------------------------------------------------
void InstantiateNull::getLeafValuesForCoverTest(ValueIdSet & leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
leafValues += getValueId();
} // InstantiateNull::getLeafValuesForCoverTest()
ItemExpr * InstantiateNull::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap)
InstantiateNull(NULL, getType()->newCopy(outHeap));
else
result = derivedNode;
((InstantiateNull *)result)->beginLOJTransform_ = beginLOJTransform_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
NABoolean InstantiateNull::isAColumnReference( )
{
if (child(0)) {
/* return child(0)->isAColumnReference();
commenting this line out as it causes regression tests to fail */
if ((child(0)->getOperatorType() != ITM_VALUEIDUNION) &&
(NOT child(0)->isAnAggregate()))
return TRUE;
}
return FALSE;
}
NABoolean InstantiateNull::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
InstantiateNull * tmp = (InstantiateNull *) other;
return
(this->NoCheckforLeftToInnerJoin == tmp->NoCheckforLeftToInnerJoin ) &&
(this->beginLOJTransform_ == tmp->beginLOJTransform_ );
}
// InstantiateNull is never equivalent to another instance. Even when
// two instances are similar, they cannot be considered equivalent
// since they may have come from different outer joins. The
// InstantiateNull ItemExpr operates in the context of a join, but
// this is not recorded in the data members of the class.
NABoolean InstantiateNull::isEquivalentForCodeGeneration(const ItemExpr * other)
{
return FALSE;
}
// --------------------------------------------------------------
// member functions for BitOperFunc operator
// --------------------------------------------------------------
Int32 BitOperFunc::getArity() const
{
if (child(0) == NULL)
return 0;
else if (child(1) == NULL)
return 1;
else if (child(2) == NULL)
return 2;
else
return 3;
}
ItemExpr * BitOperFunc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) BitOperFunc(getOperatorType(), NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
const NAString BitOperFunc::getText() const
{
switch (getOperatorType())
{
case ITM_BITAND: return "bitand";
case ITM_BITOR: return "bitor";
case ITM_BITXOR: return "bitxor";
case ITM_BITNOT: return "bitnot";
case ITM_BITEXTRACT: return "bitextract";
default:
return "unknown bit func";
} // switch
}
// --------------------------------------------------------------
// member functions for CompEncode operator
// --------------------------------------------------------------
CompEncode::~CompEncode() {}
ItemExpr * CompEncode::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CompEncode(NULL,
descFlag_,
length_,
collationType_,
regularNullability_,
outHeap);
else
result = derivedNode;
((CompEncode *) result)->caseinsensitiveEncode_ = caseinsensitiveEncode_;
((CompEncode *) result)->encodedCollation_ = encodedCollation_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
Lng32 CompEncode::getEncodedLength( const CharInfo::Collation collation,
const CollationInfo::CollationType ct,
const Lng32 srcLength,
const NABoolean nullable)
{
CMPASSERT(CollationInfo::isSystemCollation(collation));
Int32 nPasses= CollationInfo::getCollationNPasses(collation);
switch (ct)
{
case CollationInfo::Sort:
{
return nPasses * (srcLength + 1 ) +
(nullable? ExpTupleDesc::KEY_NULL_INDICATOR_LENGTH: 0 ) ;
}
case CollationInfo::Compare:
{
return nPasses * (srcLength + 1 ) ;
}
case CollationInfo::Search:
{
return nPasses * srcLength;
}
default:
{
CMPASSERT(0);
return NULL;
}
}
}
// --------------------------------------------------------------
// member functions for CompDecode operator
// --------------------------------------------------------------
CompDecode::~CompDecode() {}
ItemExpr * CompDecode::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) CompDecode(NULL,
unencodedType_->newCopy(outHeap),
descFlag_,
length_,
collationType_,
regularNullability_,
outHeap);
else
result = derivedNode;
return CompEncode::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for LOBoper operator
// --------------------------------------------------------------
ItemExpr * LOBoper::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
LOBoper *result;
if (derivedNode == NULL)
result = new (outHeap) LOBoper(getOperatorType(), NULL, NULL, NULL,obj_);
else
result = (LOBoper*)derivedNode;
result->lobNum() = lobNum();
result->lobStorageType() = lobStorageType();
result->lobStorageLocation() = lobStorageLocation();
return BuiltinFunction::copyTopNode(result, outHeap);
}
ItemExpr * LOBinsert::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
LOBinsert *result;
if (derivedNode == NULL)
result = new (outHeap) LOBinsert(NULL, NULL, obj_, append_);
else
result = (LOBinsert*)derivedNode;
result->insertedTableObjectUID() = insertedTableObjectUID();
result->insertedTableSchemaName() = insertedTableSchemaName();
// result->lobNum() = lobNum();
result->lobSize() = lobSize();
result->lobFsType() = lobFsType();
return LOBoper::copyTopNode(result, outHeap);
}
ItemExpr * LOBselect::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) LOBselect(NULL, NULL, obj_);
else
result = derivedNode;
return LOBoper::copyTopNode(result, outHeap);
}
ItemExpr * LOBdelete::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) LOBdelete(NULL);
else
result = derivedNode;
return LOBoper::copyTopNode(result, outHeap);
}
ItemExpr * LOBupdate::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
LOBupdate *result;
if (derivedNode == NULL)
result = new (outHeap) LOBupdate(NULL, NULL, NULL,obj_, append_);
else
result = (LOBupdate*)derivedNode;
result->updatedTableObjectUID() = updatedTableObjectUID();
result->updatedTableSchemaName() = updatedTableSchemaName();
return LOBoper::copyTopNode(result, outHeap);
}
/*
Int32 LOBupdate::getArity() const
{
if (obj_ == EMPTY_LOB_)
return 0;
else
return getNumChildren();
}
*/
ItemExpr * LOBconvert::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) LOBconvert(NULL,obj_,tgtSize_);
else
result = derivedNode;
return LOBoper::copyTopNode(result, outHeap);
}
ItemExpr * LOBconvertHandle::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) LOBconvertHandle(NULL, obj_);
else
result = derivedNode;
return LOBoper::copyTopNode(result, outHeap);
}
ItemExpr * LOBextract::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) LOBextract(NULL, tgtSize_);
else
result = derivedNode;
return LOBoper::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Concat operator
// --------------------------------------------------------------
ItemExpr * Concat::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Concat(NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Format operator
// --------------------------------------------------------------
Format::~Format() {}
ItemExpr * Format::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
Format *result;
if (derivedNode == NULL)
result = new (outHeap) Format(NULL, formatStr_, formatCharToDate_);
else
result = (Format*)derivedNode;
result->formatCharToDate_ = formatCharToDate_;
result->formatType_ = formatType_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Hash operator
// --------------------------------------------------------------
ItemExpr * Hash::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Hash(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HashComp operator
// --------------------------------------------------------------
HashComb::~HashComb() {}
ItemExpr * HashComb::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HashComb(NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HashDistPartHash operator
// Hash Function used by Hash Partitioning. This function cannot change
// once Hash Partitioning is released! Defined for all data types,
// returns a 32 bit non-nullable hash value for the data item.
//--------------------------------------------------------------
HashDistPartHash::~HashDistPartHash() {}
ItemExpr *
HashDistPartHash::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HashDistPartHash(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HashDistPartHashComp operator
// This function is used to combine two hash values to produce a new
// hash value. Used by Hash Partitioning. This function cannot change
// once Hash Partitioning is released! Defined for all data types,
// returns a 32 bit non-nullable hash value for the data item.
// --------------------------------------------------------------
HashDistPartHashComb::~HashDistPartHashComb() {}
ItemExpr *
HashDistPartHashComb::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HashDistPartHashComb(NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HiveFunc operator
// --------------------------------------------------------------
ItemExpr * HiveHash::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HiveHash(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HiveHashComb operator
// --------------------------------------------------------------
ItemExpr * HiveHashComb::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HiveHashComb(NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for MathFunc operator
// --------------------------------------------------------------
Int32 MathFunc::getArity() const
{
if (child(0) == NULL)
return 0;
else if (child(1) == NULL)
return 1;
else
return 2;
}
ItemExpr * MathFunc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) MathFunc(getOperatorType(), NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
const NAString MathFunc::getText() const
{
switch (getOperatorType())
{
case ITM_ABS: return "abs";
case ITM_ACOS: return "acos";
case ITM_ASIN: return "asin";
case ITM_ATAN: return "atan";
case ITM_ATAN2: return "atan2";
case ITM_CEIL: return "ceiling";
case ITM_COS: return "cos";
case ITM_COSH: return "cosh";
case ITM_DEGREES: return "degrees";
case ITM_EXP: return "exp";
case ITM_EXPONENT: return "'**'";
case ITM_FLOOR: return "floor";
case ITM_LOG: return "log";
case ITM_LOG10: return "log10";
case ITM_LOG2: return "log2";
case ITM_PI: return "pi";
case ITM_POWER: return "power";
case ITM_RADIANS: return "radians";
case ITM_ROUND: return "round";
case ITM_SCALE_TRUNC: return "truncate";
case ITM_SIN: return "sin";
case ITM_SINH: return "sinh";
case ITM_SQRT: return "sqrt";
case ITM_TAN: return "tan";
case ITM_TANH: return "tanh";
default:
return "unknown math func";
} // switch
}
// --------------------------------------------------------------
// member functions for Modulus operator
// --------------------------------------------------------------
Modulus::~Modulus() {}
ItemExpr * Modulus::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Modulus(NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Repeat operator
// --------------------------------------------------------------
ItemExpr * Repeat::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Repeat(NULL, NULL);
else
result = derivedNode;
((Repeat *) result)->setMaxLength(getMaxLength());
((Repeat *) result)->maxLengthWasExplicitlySet_ = maxLengthWasExplicitlySet_;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for Replace operator
// --------------------------------------------------------------
ItemExpr * Replace::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Replace(NULL, NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for ReplaceNull operator
// --------------------------------------------------------------
ItemExpr * ReplaceNull::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ReplaceNull(NULL, NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for HashDistrib operator
// --------------------------------------------------------------
HashDistrib::~HashDistrib() {}
ItemExpr * HashDistrib::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
CMPASSERT (derivedNode != NULL);
return BuiltinFunction::copyTopNode(derivedNode, outHeap);
}
// --------------------------------------------------------------
// member functions for Hash2Distrib operator
// --------------------------------------------------------------
Hash2Distrib::~Hash2Distrib() {}
ItemExpr * Hash2Distrib::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Hash2Distrib(NULL,NULL);
else
result = derivedNode;
return HashDistrib::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for ProgDistrib operator
// --------------------------------------------------------------
ProgDistrib::~ProgDistrib() {}
ItemExpr * ProgDistrib::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ProgDistrib(NULL,NULL);
else
result = derivedNode;
return HashDistrib::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for ProgDistribKey operator
// --------------------------------------------------------------
ProgDistribKey::~ProgDistribKey() {}
ItemExpr *
ProgDistribKey::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) ProgDistribKey(NULL,NULL,NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for PAGroup operator
// --------------------------------------------------------------
PAGroup::~PAGroup() {}
ItemExpr * PAGroup::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) PAGroup(NULL,NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for InverseOrder operator
// --------------------------------------------------------------
ItemExpr * InverseOrder::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) InverseOrder(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for In operator
// --------------------------------------------------------------
ItemExpr * In::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) In(NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for NATypeToItem operator
// --------------------------------------------------------------
Int32 NATypeToItem::getArity() const { return 0; }
const NAString NATypeToItem::getText() const
{ return natype_pointer->getTypeSQLname(TRUE); }
ItemExpr * NATypeToItem::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) NATypeToItem( natype_pointer );
else
result = derivedNode;
return ItemExpr::copyTopNode(result, outHeap);
}
// --------------------------------------------------------------
// member functions for NamedTypeToItem operator
// --------------------------------------------------------------
ItemExpr* NamedTypeToItem::copyTopNode(ItemExpr *derivedNode, CollHeap *outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) NamedTypeToItem(name_.data(),
natype_pointer,
outHeap);
else
result = derivedNode;
return NATypeToItem::copyTopNode(result, outHeap);
}
const NAString NamedTypeToItem::getText() const
{
return name_;
}
// --------------------------------------------------------------
// member functions for NoOp operator
// --------------------------------------------------------------
NoOp::~NoOp() {}
ItemExpr * NoOp::copyTopNode(ItemExpr * derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) NoOp(NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
}
// -----------------------------------------------------------------------
// member functions for class RandomNum
// -----------------------------------------------------------------------
RandomNum::~RandomNum() {}
ItemExpr * RandomNum::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) RandomNum(NULL, simpleRandom_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // RandomNum::copyTopNode()
// MV,
// -----------------------------------------------------------------------
// member functions for class GenericUpdateOutputFunction
// -----------------------------------------------------------------------
ItemExpr * GenericUpdateOutputFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) GenericUpdateOutputFunction(getOperatorType());
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // GenericUpdateOutputFunction::copyTopNode()
// Triggers -
// -----------------------------------------------------------------------
// member functions for class InternalTimestamp
// -----------------------------------------------------------------------
InternalTimestamp::~InternalTimestamp() {}
ItemExpr * InternalTimestamp::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) InternalTimestamp();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Timestamp::copyTopNode()
//++Triggers,
// -----------------------------------------------------------------------
// member functions for class UniqueExecuteId
// -----------------------------------------------------------------------
UniqueExecuteId::~UniqueExecuteId() {}
ItemExpr * UniqueExecuteId::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) UniqueExecuteId();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // UniqueExecuteId::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class GetTriggersStatus
// -----------------------------------------------------------------------
GetTriggersStatus::~GetTriggersStatus() {}
ItemExpr * GetTriggersStatus::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) GetTriggersStatus();
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result,outHeap);
} // GetTriggersStatus::copyTopNode()
// -----------------------------------------------------------------------
// member functions for GetBitValueAt
// -----------------------------------------------------------------------
GetBitValueAt::~GetBitValueAt() {}
ItemExpr * GetBitValueAt::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) GetBitValueAt(child(0), child(1));
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // GetBitValueAt::copyTopNode()
//--Triggers,
//++MV,
// -----------------------------------------------------------------------
// member functions for IsBitwiseAndTrue
// -----------------------------------------------------------------------
IsBitwiseAndTrue::~IsBitwiseAndTrue() {}
ItemExpr * IsBitwiseAndTrue::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) IsBitwiseAndTrue(child(0), child(1));
}
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // IsBitwiseAndTrue::copyTopNode()
//--MV,
// -----------------------------------------------------------------------
// member functions for class Mask
// -----------------------------------------------------------------------
Mask::~Mask() {}
ItemExpr * Mask::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Mask(ITM_MASK_SET, NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Mask::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Shift
// -----------------------------------------------------------------------
Shift::~Shift() {}
ItemExpr * Shift::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Shift(ITM_SHIFT_RIGHT, NULL, NULL);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Shift::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class AnsiUSERFunction
// -----------------------------------------------------------------------
AnsiUSERFunction::~AnsiUSERFunction() {}
NABoolean AnsiUSERFunction::isAUserSuppliedInput() const { return TRUE; }
ItemExpr * AnsiUSERFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) AnsiUSERFunction(getOperatorType());
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // AnsiUSERFunction::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class MonadicUSERFunction
// -----------------------------------------------------------------------
MonadicUSERFunction::~MonadicUSERFunction() {}
NABoolean MonadicUSERFunction::isAUserSuppliedInput() const { return TRUE; }
ItemExpr * MonadicUSERFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) MonadicUSERFunction(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // MonadicUSERFunction::copyTopNode()
NABoolean MonadicUSERFunction::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// If the argument of USER function is not a constant then it can be
// evaluated anywhere in the tree. So check for its coverage
ValueIdSet localSubExpr;
for (Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if ( coveringGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr) )
{
coveredSubExpr += child(i)->getValueId();
}
}
// The USER function is not pushed down.
return FALSE;
} // MonadicUSERFunction::isCovered
// -----------------------------------------------------------------------
// member functions for class MonadicUSERIDFunction
// -----------------------------------------------------------------------
MonadicUSERIDFunction::~MonadicUSERIDFunction() {}
ItemExpr * MonadicUSERIDFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) MonadicUSERIDFunction(child(0));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // MonadicUSERIDFunction::copyTopNode()
// -----------------------------------------------------------------------
// member functions for class Translate
// -----------------------------------------------------------------------
Translate::Translate(ItemExpr *valPtr, NAString* map_table_name)
: CacheableBuiltinFunction(ITM_TRANSLATE, 1, valPtr)
{
if ( _strcmpi(map_table_name->data(), "UNICODE_TO_SJIS") == 0 ||
_strcmpi(map_table_name->data(), "UTOSJ") == 0
)
map_table_id_ = Translate::UNICODE_TO_SJIS;
else
if ( _strcmpi(map_table_name->data(), "UCS2TOSJIS") == 0)
map_table_id_ = Translate::UCS2_TO_SJIS;
else
if ( _strcmpi(map_table_name->data(), "UCS2TOUTF8") == 0)
map_table_id_ = Translate::UCS2_TO_UTF8;
else
if ( _strcmpi(map_table_name->data(), "UCS2TOISO88591") == 0 )
map_table_id_ = Translate::UNICODE_TO_ISO88591;
else
if ( _strcmpi(map_table_name->data(), "SJIS_TO_UNICODE") == 0 ||
_strcmpi(map_table_name->data(), "SJTOU") == 0
)
map_table_id_ = Translate::SJIS_TO_UNICODE;
else
if ( _strcmpi(map_table_name->data(), "SJISTOUCS2") == 0)
map_table_id_ = Translate::SJIS_TO_UCS2;
else
if ( _strcmpi(map_table_name->data(), "UTF8TOUCS2") == 0 )
map_table_id_ = Translate::UTF8_TO_UCS2;
else
if ( _strcmpi(map_table_name->data(), "ISO88591TOUCS2") == 0 )
map_table_id_ = Translate::ISO88591_TO_UNICODE;
else if ( _strcmpi(map_table_name->data(), "ISO88591TOUTF8") == 0 )
map_table_id_ = Translate::ISO88591_TO_UTF8;
else if ( _strcmpi(map_table_name->data(), "UTF8TOISO88591") == 0 )
map_table_id_ = Translate::UTF8_TO_ISO88591;
else if ( _strcmpi(map_table_name->data(), "SJISTOUTF8") == 0 )
map_table_id_ = Translate::SJIS_TO_UTF8;
else if ( _strcmpi(map_table_name->data(), "UTF8TOSJIS") == 0 )
map_table_id_ = Translate::UTF8_TO_SJIS;
else if ( _strcmpi(map_table_name->data(), "GBKTOUTF8") == 0 )
map_table_id_ = Translate::GBK_TO_UTF8;
else
if ( _strcmpi(map_table_name->data(), "KANJITOISO88591") == 0 )
map_table_id_ = Translate::KANJI_MP_TO_ISO88591;
else
if ( _strcmpi(map_table_name->data(), "KSC5601TOISO88591") == 0 )
map_table_id_ = Translate::KSC5601_MP_TO_ISO88591;
else
map_table_id_ = UNKNOWN_TRANSLATION;
allowsSQLnullArg() = FALSE;
}
Translate::Translate(ItemExpr *valPtr, Int32 map_table_id)
: CacheableBuiltinFunction(ITM_TRANSLATE, 1, valPtr)
{
map_table_id_ = map_table_id;
allowsSQLnullArg() = FALSE;
}
ItemExpr * Translate::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) Translate(child(0), getTranslateMapTableId());
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // Translate::copyTopNode()
// do not know why. But if this function is put inside Translate's
// class definition, cl.exe aborts with the following info:
// cl.exe ... sooutput.cpp
//../rogue\rw/locale.h(243) : fatal error C1001: INTERNAL COMPILER ERROR
// (compiler file 'msc1.cpp', line 1188)
// Please choose the Technical Support command on the Visual C++
// Help menu, or open the Technical Support help file for more information
NABoolean Translate::isCharTypeMatchRulesRelaxable()
{
return child(0)->isCharTypeMatchRulesRelaxable();
}
void Translate::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc* tabId) const
{
if (CmpCommon::getDefault(MVQR_LOG_QUERY_DESCRIPTORS) != DF_DUMP_MV)
return CacheableBuiltinFunction::unparse(result, phase, form, tabId);
else
{
result += "translate(";
child(0)->unparse(result, phase, form, tabId);
result += " using ";
NAString translation;
switch (map_table_id_)
{
case UNICODE_TO_SJIS : translation="UCS2_TO_SJIS"; break;
case UNICODE_TO_ISO88591 : translation="UCS2TOISO88591"; break;
case ISO88591_TO_UNICODE : translation="ISO88591TOUCS2"; break;
case SJIS_TO_UNICODE : translation="SJIS_TO_UCS2"; break;
case UCS2_TO_SJIS : translation="UCS2TOSJIS"; break;
case SJIS_TO_UCS2 : translation="SJISTOUCS2"; break;
case UCS2_TO_UTF8 : translation="UCS2TOUTF8"; break;
case UTF8_TO_UCS2 : translation="UTF8TOUCS2"; break;
case UTF8_TO_SJIS : translation="UTF8TOSJIS"; break;
case SJIS_TO_UTF8 : translation="SJISTOUTF8"; break;
case UTF8_TO_ISO88591 : translation="UTF8TOISO88591"; break;
case ISO88591_TO_UTF8 : translation="ISO88591TOUTF8"; break;
case KANJI_MP_TO_ISO88591 : translation="KANJITOISO88591"; break;
case KSC5601_MP_TO_ISO88591 : translation="KSC5601TOISO88591"; break;
case UNKNOWN_TRANSLATION : translation="UNKNOWN_TRANSLATION"; break;
}
result += translation;
result += ")";
}
}
HbaseColumnLookup::~HbaseColumnLookup()
{
}
ItemExpr * HbaseColumnLookup::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseColumnLookup(child(0), hbaseCol_, naType_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseColumnLookup::copyTopNode()
HbaseColumnsDisplay::~HbaseColumnsDisplay()
{
}
ItemExpr * HbaseColumnsDisplay::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseColumnsDisplay(child(0), csl_, displayWidth_);
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseColumnsDisplay::copyTopNode()
HbaseColumnCreate::~HbaseColumnCreate()
{
}
ItemExpr * HbaseColumnCreate::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
HbaseColumnCreate *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseColumnCreate(hccol_);
else
result = (HbaseColumnCreate*)derivedNode;
result->colNameMaxLen_ = colNameMaxLen_;
result->colValMaxLen_ = colValMaxLen_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseColumnLookup::copyTopNode()
SequenceValue::~SequenceValue()
{
}
ItemExpr * SequenceValue::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
SequenceValue *result;
if (derivedNode == NULL)
result = new (outHeap) SequenceValue(seqCorrName_, currVal_, nextVal_);
else
result = (SequenceValue*)derivedNode;
result->naTable_ = naTable_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // SequenceValue::copyTopNode()
// HbaseTimestamp
HbaseTimestamp::~HbaseTimestamp()
{
}
ItemExpr * HbaseTimestamp::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
HbaseTimestamp *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseTimestamp(col_);
else
result = (HbaseTimestamp*)derivedNode;
result->colIndex_ = colIndex_;
result->colName_ = colName_;
result->tsVals_ = tsVals_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseTimestamp::copyTopNode()
// HbaseTimestampRef
HbaseTimestampRef::~HbaseTimestampRef()
{
}
ItemExpr * HbaseTimestampRef::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
HbaseTimestampRef *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseTimestampRef(col_);
else
result = (HbaseTimestampRef*)derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseTimestamp::copyTopNode()
NABoolean HbaseTimestamp::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
//return TRUE;
return FALSE;
}
// HbaseVersion
HbaseVersion::~HbaseVersion()
{
}
ItemExpr * HbaseVersion::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
HbaseVersion *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseVersion(col_);
else
result = (HbaseVersion*)derivedNode;
result->colIndex_ = colIndex_;
result->colName_ = colName_;
result->tsVals_ = tsVals_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseVersion::copyTopNode()
// HbaseVersionRef
HbaseVersionRef::~HbaseVersionRef()
{
}
ItemExpr * HbaseVersionRef::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
HbaseVersionRef *result;
if (derivedNode == NULL)
result = new (outHeap) HbaseVersionRef(col_);
else
result = (HbaseVersionRef*)derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // HbaseVersion::copyTopNode()
NABoolean HbaseVersion::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
//return TRUE;
return FALSE;
}
// RowNumFunc
RowNumFunc::~RowNumFunc()
{
}
ItemExpr * RowNumFunc::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
RowNumFunc *result;
if (derivedNode == NULL)
result = new (outHeap) RowNumFunc();
else
result = (RowNumFunc*)derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // RowNumFunc::copyTopNode()
NABoolean RowNumFunc::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// The ROWNUM function is not pushed down.
return FALSE;
} // RowNumFunc::isCovered
// -----------------------------------------------------------------------
// Member functions for ItmSequenceFunction
//------------------------------------------------------------------------
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSequenceFunction::~ItmSequenceFunction() {}
NABoolean ItmSequenceFunction::isASequenceFunction() const // virtual method
{ return TRUE; }
NABoolean ItmSequenceFunction::isCovered(const ValueIdSet& newExternalInputs,
const GroupAttributes& newRelExprAnchorGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// ---------------------------------------------------------------------
// If the operand is covered, then return its ValueId in coveredSubExpr.
// ---------------------------------------------------------------------
ValueIdSet localSubExpr;
for(Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if (child(i)->getOperatorType() == ITM_ITEM_LIST)
{
// child is a multi-valued expression, test coverage on individuals
//
ExprValueId treePtr = child(i);
ItemExprTreeAsList values(&treePtr,
ITM_ITEM_LIST,
RIGHT_LINEAR_TREE);
CollIndex nc = values.entries();
for (CollIndex m = 0; m < nc; m++)
{
if(newRelExprAnchorGA.covers(values[m]->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr))
{
coveredSubExpr += values[m]->getValueId();
}
coveredSubExpr += localSubExpr;
}
}
else
{
if(newRelExprAnchorGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr))
{
coveredSubExpr += child(i)->getValueId();
}
coveredSubExpr += localSubExpr;
}
}
// ---------------------------------------------------------------------
// The ItmSequenceFunction function is coerced to fail the coverage test even
// when its operands isCovered(). This is because only the RelSequence node
// can evaluate the function. The function is associated with a RelSequence
// node at the very beginning and we don't allow it to be pushed down
// even if the function's operands are covered at the node's child.
// ---------------------------------------------------------------------
return FALSE;
}
void ItmSequenceFunction::getLeafValuesForCoverTest(ValueIdSet& leafValues,
const GroupAttributes& coveringGA,
const ValueIdSet & newExternalInputs) const
{
// ItmSequenceFunction is considered a leaf operator for cover test.
leafValues += getValueId();
}
const NAString ItmSequenceFunction::getText() const
{
switch (getOperatorType())
{
case ITM_DIFF1:
return "diff1";
case ITM_DIFF2:
return "diff2";
case ITM_LAST_NOT_NULL:
return "lastnotnull";
case ITM_MOVING_AVG:
return "movingavg";
case ITM_MOVING_COUNT:
return "movingcount";
case ITM_MOVING_MAX:
return "movingmax";
case ITM_MOVING_MIN:
return "movingmin";
case ITM_MOVING_RANK:
return "movingrank";
case ITM_MOVING_SDEV:
return "movingsdev";
case ITM_MOVING_SUM:
return "movingsum";
case ITM_MOVING_VARIANCE:
return "movingvariance";
case ITM_RUNNING_AVG:
return "runningavg";
case ITM_RUNNING_CHANGE:
return "rows since changed";
case ITM_RUNNING_COUNT:
return "runningcount";
case ITM_RUNNING_MAX:
return "runningmax";
case ITM_RUNNING_MIN:
return "runningmin";
case ITM_RUNNING_RANK:
return "runningrank";
case ITM_RUNNING_SDEV:
return "runningsdev";
case ITM_RUNNING_SUM:
return "runningsum";
case ITM_RUNNING_VARIANCE:
return "runningvariance";
case ITM_OFFSET:
if (getArity() == 1 )// internally created offsets
{
char str[30];
str_sprintf(str, "offset[%d]",((ItmSeqOffset *)this)->getOffsetConstantValue());
return str;
}
else
{
return "offset";
}
case ITM_THIS:
return "this";
case ITM_NOT_THIS:
return "not this";
case ITM_OLAP_COUNT:
return "olap count";
case ITM_OLAP_MAX:
return "olap max";
case ITM_OLAP_MIN:
return "olap min";
case ITM_OLAP_RANK:
return "olap rank";
case ITM_OLAP_DRANK:
return "olap dense rank";
case ITM_OLAP_SDEV:
return "olap sdev";
case ITM_OLAP_SUM:
return "olap sum";
case ITM_OLAP_VARIANCE:
return "olap variance";
default:
return "unknown sequence function";
} // switch
}
ItemExpr * ItmSequenceFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItmSequenceFunction *result = NULL;
if (derivedNode == NULL)
ABORT(
"copyTopNode() can only be called for a derived class of ItmSequenceFunction"
);
else
result = (ItmSequenceFunction *)derivedNode;
// Copy OLAP Window Function information.
//
result->isOLAP_ = isOLAP_;
result->olapPartitionBy_ = olapPartitionBy_;
result->olapOrderBy_ = olapOrderBy_;
result->isTDFunction_ = isTDFunction_;
return BuiltinFunction::copyTopNode(result, outHeap);
} // ItmSequenceFunction::copyTopNode()
ItemExpr * ItmSequenceFunction::transformTDFunction(BindWA * bindWA)
{
if (getOperatorType() != ITM_RUNNING_RANK) // we are only doing td rank in this ohase
return this;
if (!isTDFunction())
return this;
if (!olapPartitionBy_)
return this;
ItmSequenceFunction *partClause = NULL;
partClause = new (bindWA->getCurrentScope()->collHeap())
ItmSeqRunningFunction(ITM_RUNNING_CHANGE,
olapPartitionBy_->copyTree(bindWA->getCurrentScope()->collHeap()) );
partClause->setIsTDFunction(TRUE);
partClause->setOlapOrderBy(olapOrderBy_->copyTree(bindWA->getCurrentScope()->collHeap()));
partClause->setOlapPartitionBy(olapPartitionBy_->copyTree(bindWA->getCurrentScope()->collHeap()));
ItmSequenceFunction *seqFunc = new (bindWA->getCurrentScope()->collHeap())
ItmSeqMovingFunction(ITM_MOVING_RANK, child(0), partClause);
seqFunc->setIsTDFunction(TRUE);
seqFunc->setOlapOrderBy(olapOrderBy_->copyTree(bindWA->getCurrentScope()->collHeap()));
seqFunc->setOlapPartitionBy(olapPartitionBy_->copyTree(bindWA->getCurrentScope()->collHeap()));
return seqFunc;
}
// -----------------------------------------------------------------------
// member functions for ItmSeqRunningFunction
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqRunningFunction::~ItmSeqRunningFunction() {}
NABoolean ItmSeqOlapFunction::isOlapFunction() const // virtual method
{ return TRUE; }
ItmSeqOlapFunction::~ItmSeqOlapFunction() {}
ItemExpr * ItmSeqRunningFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) ItmSeqRunningFunction(getOperatorType(),
child(0));
((ItmSeqRunningFunction *)result)->setIsOLAP(isOLAP());
}
else
result = derivedNode;
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqRunningFunction::copyTopNode()
ItemExpr * ItmSeqOlapFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) ItmSeqOlapFunction(getOperatorType(),
child(0));
}
else
result = derivedNode;
((ItmSeqOlapFunction *)result)->frameStart_ = frameStart_;
((ItmSeqOlapFunction *)result)->frameEnd_ = frameEnd_;
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqOlapFunction::copyTopNode()
NABoolean
ItmSeqOlapFunction::inverseOLAPOrder(CollHeap *heap)
{
//if (frameStart_ != -INT_MAX) {
if (getOlapOrderBy())
{
ItemExprList orderList(getOlapOrderBy(),0);
ItemExpr *newOrder = NULL;
for (CollIndex i = 0; i < orderList.entries(); i++) {
ItemExpr *itm = orderList[i];
if (itm->getOperatorType() == ITM_INVERSE) {
itm = itm->child(0);
} else {
itm = new (heap) InverseOrder(itm);
}
if(newOrder) {
newOrder = new(heap) ItemList(newOrder, itm);
} else {
newOrder = itm;
}
}
setOlapOrderBy(newOrder);
}
Lng32 olapRowsTemp = frameEnd_;
frameEnd_ = -frameStart_;
frameStart_ = -olapRowsTemp;
return TRUE;
//}
//return FALSE;
}
NABoolean ItmSeqOlapFunction::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
ItmSeqOlapFunction * otherOSeq = (ItmSeqOlapFunction *) other;
return
(this->frameStart_ == otherOSeq->frameStart_ &&
this->frameEnd_ == otherOSeq->frameEnd_) ;
}
NABoolean ItmSequenceFunction::isEquivalentForBinding(const ItemExpr * other)
{
if ( getOperatorType() != other->getOperatorType() || getArity() != other->getArity() )
{
return FALSE;
}
for (Lng32 i = 0; (i < getArity()); i++)
{
if (child(i)->isASequenceFunction())
{
ItmSequenceFunction * seqFunc = (ItmSequenceFunction * ) child(i).getValueId().getItemExpr();
if (!seqFunc->isEquivalentForBinding(other->child(i)))
{
return FALSE;
}
}
else
{
if (!child(i)->hasBaseEquivalenceForCodeGeneration(other->child(i)))
{
return FALSE;
}
}
}
return TRUE;
}
// -----------------------------------------------------------------------
// member functions for Offset
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqOffset::~ItmSeqOffset() {}
// -----------------------------------------------------------------------
Int32 ItmSeqOffset::getArity() const
{
if (child(1))
{
if (child(2))
return 3;
else
return 2;
}
else
return 1;
}
ItemExpr * ItmSeqOffset::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
switch (getArity())
{
case 2:
result = new (outHeap) ItmSeqOffset(child(0), child(1), NULL,
nullRowIsZero());
break;
case 3:
result = new (outHeap) ItmSeqOffset(child(0), child(1), child(2),
nullRowIsZero());
break;
default:
result = new (outHeap) ItmSeqOffset( child(0),
nullRowIsZero(),
getOffsetConstantValue(),
isLeading(),
winSize());
break;
}
((ItmSeqOffset *)result)->setIsOLAP(isOLAP());
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqOffset::copyTopNode()
NABoolean ItmSeqOffset::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
ItmSeqOffset * otherOffset = (ItmSeqOffset *) other;
return
(this->offsetConstantValue_ == otherOffset->offsetConstantValue_) &&
(this->nullRowIsZero_ == otherOffset->nullRowIsZero_) &&
(this->leading_ == otherOffset->leading_) &&
(this->winSize_ == otherOffset->winSize_);
}
// -----------------------------------------------------------------------
// member functions for DIFF1
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqDiff1::~ItmSeqDiff1() {}
// -----------------------------------------------------------------------
ItemExpr * ItmSeqDiff1::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
if (getArity() == 1)
{
result = new (outHeap) ItmSeqDiff1(child(0));
}
else
{
result = new (outHeap) ItmSeqDiff1(child(0), child(1));
}
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqDiff1::copyTopNode()
// -----------------------------------------------------------------------
// member functions for DIFF2
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqDiff2::~ItmSeqDiff2() {}
// -----------------------------------------------------------------------
ItemExpr * ItmSeqDiff2::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
if (getArity() == 1)
{
result = new (outHeap) ItmSeqDiff2(child(0));
}
else
{
result = new (outHeap) ItmSeqDiff2(child(0), child(1));
}
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqDiff2::copyTopNode()
// -----------------------------------------------------------------------
// member functions for ItmSeqRowsSince
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqRowsSince :: ~ItmSeqRowsSince() {};
ItemExpr *ItmSeqRowsSince :: copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
if (getArity() == 1)
{
result = new (outHeap) ItmSeqRowsSince(child(0), NULL, includeCurrentRow());
}
else
{
result = new (outHeap) ItmSeqRowsSince(child(0), child(1), includeCurrentRow());
}
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqRowsSince::copyTopNode()
const NAString ItmSeqRowsSince :: getText() const
{
if (includeCurrentRow())
return "rows since inclusive";
else
return "rows since";
} // ItmSeqRowsSince::getText()
// -----------------------------------------------------------------------
// member functions for ItmSeqMovingFunction
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqMovingFunction::~ItmSeqMovingFunction() {}
ItemExpr * ItmSeqMovingFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL) {
result = new (outHeap) ItmSeqMovingFunction(getOperatorType(),
child(0), child(1), child(2));
((ItmSeqMovingFunction *)result)->setIsOLAP(isOLAP());
}
else
result = derivedNode;
if (this->getSkipMovingMinMaxTransformation() == TRUE)
{
((ItmSeqMovingFunction *)result)->setSkipMovingMinMaxTransformation();
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqMovingFunction::copyTopNode()
NABoolean ItmSeqMovingFunction::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
ItmSeqMovingFunction * otherMSeq = (ItmSeqMovingFunction *) other;
return
(this->skipMovingMinMaxTransformation_ == otherMSeq->skipMovingMinMaxTransformation_) ;
}
// -----------------------------------------------------------------------
// member functions for THIS function
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqThisFunction::~ItmSeqThisFunction() {}
ItemExpr * ItmSeqThisFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) ItmSeqThisFunction (child(0));
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqThisFunction::copyTopNode()
// -----------------------------------------------------------------------
// member functions for NotTHIS function
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmSeqNotTHISFunction::~ItmSeqNotTHISFunction(){}
ItemExpr *ItmSeqNotTHISFunction::copyTopNode(ItemExpr *derivedNode,
CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
result = new (outHeap) ItmSeqNotTHISFunction (child(0));
}
else
{
result = derivedNode;
}
return ItmSequenceFunction::copyTopNode(result, outHeap);
} // ItmSeqNotTHISFunction::copyTopNode()
// -----------------------------------------------------------------------
// member functions for ItmScalarMinMax
// -----------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------
ItmScalarMinMax::~ItmScalarMinMax() {};
ItemExpr *ItmScalarMinMax::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
result = new (outHeap)ItmScalarMinMax(getOperatorType(), child(0), child(1));
else
result = derivedNode;
return BuiltinFunction::copyTopNode(result, outHeap);
} // ItmScalarMinMax::copyTopNode
NABoolean ItemExpr::containsOneRowAggregate()
{
if ( getOperatorType() == ITM_ONE_ROW )
return TRUE;
for (Int32 i=0; i < getArity(); i++)
{
if (child(i)->castToItemExpr()->containsOneRowAggregate())
return TRUE;
}
return FALSE;
} // ItemExpr::containsOneRowAggregate()
NABoolean ItemExpr::containsOpType(OperatorTypeEnum opType) const
{
if ( getOperator().match(opType) )
return TRUE;
for (Int32 i=0; i < getArity(); i++)
{
if (child(i)->castToItemExpr()->containsOpType(opType))
return TRUE;
}
return FALSE;
} // ItemExpr::containsOpType()
// If I am untransformed, I tell my parent, which in turn will mark itself
// as untransformed, and tell its parent, ..., all the way up to the
// original place where this method was called.
NABoolean ItemExpr::markPathToUnTransformedNode()
{
NABoolean retval = FALSE ;
if ( !nodeIsTransformed() )
retval = TRUE ;
for (Int32 i=0; i < getArity(); i++)
{
if ( child(i)->castToItemExpr()->markPathToUnTransformedNode() )
{
markAsUnTransformed() ;
retval = TRUE ;
// NB: Since this function is marking nodes as untransformed, we
// can't simply break after the first child which is found to
// be untransformed.
}
}
return retval;
} // ItemExpr::markPathToUnTransformedNode()
NABoolean ItemExpr::isEquivalentForCodeGeneration(const ItemExpr * other)
{
NABoolean rc = FALSE;// by default, return FALSE (suppress common subexpr elimination
if (getArity() == 0) // to limit needless recursion
{
if (hasBaseEquivalenceForCodeGeneration(other))
{
// we know other is non-null, has same operator type, same arity, and
// that its children are equivalent
OperatorTypeEnum myOp = getOperatorType();
const ItemExpr & refOther = (const ItemExpr &) *other;
// as it happens, with 0-arity operators, we know that operator==
// should return false for all cases below (because of the way the
// code in hasBaseEquivalenceForCodeGeneration() works; but I want
// to see if virtual forms of operator== are selected)
if (myOp == ITM_CONSTANT)
{
rc = (operator==(refOther));
}
else if (myOp == ITM_REFERENCE)
{
rc = (operator==(refOther));
}
else if (myOp == ITM_BASECOLUMN)
{
rc = (operator==(refOther));
}
else if (myOp == ITM_INDEXCOLUMN)
{
rc = (operator==(refOther));
}
}
}
return rc;
}
NABoolean ItemExpr::hasBaseEquivalenceForCodeGeneration(const ItemExpr * other)
{
NABoolean rc = FALSE; // assume not
if (other)
{
const ItemExpr & refOther = (const ItemExpr &) *other; // we know it exists
if (operator==(refOther)) // $$$$ will this get virtual operator==????
rc = TRUE; // equal nodes are considered equivalent
else
{
if (getOperatorType() == other->getOperatorType())
{
Lng32 arity = getArity();
if (arity == other->getArity())
{
// make sure children are equivalent
rc = TRUE; // be optimistic now; assume equivalent
for (Lng32 i = 0; (i < arity) && (rc); i++)
{
if (!(child(i)->isEquivalentForCodeGeneration(other->child(i))))
rc = FALSE; // oops -- found non-equivalent children
}
} // end if arity's are equal
} // end if oper types are equal
} // end else nodes aren't equal
} // end if other is non-null
return rc;
}
ItemExpr* ItemExpr::getParticularItemExprFromTree(NAList<Lng32>& childNum,
NAList<OperatorTypeEnum>& opType) const
{
ItemExpr * root = (ItemExpr *) this;
for (CollIndex i = 0; i < childNum.entries(); i++)
{
if ((root->getArity() > childNum[i]) &&
(root->child(childNum[i])) &&
(root->child(childNum[i])->getOperatorType() == opType[i]))
{
root = root->child(childNum[i]);
}
else
{
DCMPASSERT(FALSE);
return NULL;
}
}
return root;
}
ItemExpr* ItemExpr::removeRangeSpecItems(NormWA* normWA)
{
for (Lng32 i = 0; i < getArity(); i++)
{
child(i) = child(i)->removeRangeSpecItems(normWA);
}
return this;
};
// Raj P - 1/01
// Support for OUT parameters in Stored Procedures for Java
// Set Parameter Mode, Ordinal Position and Variable Index for a
// host variable or dynamic parameter
void
HostVar::setPMOrdPosAndIndex( ComColumnDirection paramMode,
Int32 ordinalPosition,
Int32 index )
{
paramMode_ = paramMode;
ordinalPosition_ = ordinalPosition;
hvIndex_ = index;
}
NABoolean HostVar::isSystemGeneratedOutputHV() const
{
return (isSystemGenerated() &&
getName() == "_sys_ignored_CC_convErrorFlag");
}
void
DynamicParam::setPMOrdPosAndIndex( ComColumnDirection paramMode,
Int32 ordinalPosition,
Int32 index )
{
paramMode_ = paramMode;
ordinalPosition_ = ordinalPosition;
dpIndex_ = index;
}
ComColumnDirection ItemExpr::getParamMode () const
{
CMPASSERT (0);
return COM_UNKNOWN_DIRECTION;
};
Int32 ItemExpr::getOrdinalPosition () const
{
CMPASSERT (0);
return -1;
}
Int32 ItemExpr::getHVorDPIndex () const
{
CMPASSERT (0);
return -1;
}
NABoolean ItemExpr::isARangePredicate() const
{
OperatorTypeEnum oper = getOperatorType();
if ((oper == ITM_GREATER) ||
(oper == ITM_GREATER_EQ) ||
(oper == ITM_LESS) ||
(oper == ITM_LESS_EQ))
{
return TRUE;
}
return FALSE;
}
QR::ExprElement Function::getQRExprElem() const
{
return QR::QRFunctionElem;
}
// Flipping the tree in 1 pass top->bottom
//
//ITEM_LIST (OLD TREE)
// / \
// 4 ITEM_LIST
// / \
// 3 ITEM_LIST
// / \
// 2 1
//
//ITEM_LIST (NEW TREE)
// / \
// 1 ITEM_LIST
// / \
// 2 ITEM_LIST
// / \
// 3 4
ItemExpr * ItemExpr::reverseTree()
{
ItemExpr *grammarTree = this;
ItemExpr *topNode = NULL;
// Special Case: 1, 2 & 3 columns. Base of tree
// only 1 column.
if (grammarTree->getOperatorType()!=ITM_ITEM_LIST)
return grammarTree;
if (grammarTree->child(1)->getOperatorType()==ITM_ITEM_LIST ) {
// more than 2 columns
topNode = grammarTree->child(1)->child(1);
grammarTree->child(1)->child(1) = grammarTree->child(0);
if (topNode->getOperatorType() != ITM_ITEM_LIST) {
// extactly 3 columns
grammarTree->child(0) = topNode;
return grammarTree;
}
else {
// more than 3 columns
grammarTree->child(0) = topNode->child(0);
}
}
else {
// exactly 2 columns
ItemExpr *temp = grammarTree->child(1);
grammarTree->child(1) = grammarTree->child(0);
grammarTree->child(0) = temp;
return grammarTree;
}
while (topNode) {
if (topNode->child(1)->getOperatorType()==ITM_ITEM_LIST ) {
// not bottom of tree
ItemExpr *temp = topNode->child(1);
topNode->child(1) = grammarTree;
grammarTree = topNode;
topNode = temp;
grammarTree->child(0) = topNode->child(0);
}
else { // bottom of the tree
topNode->child(0) = topNode->child(1);
topNode->child(1) = grammarTree;
grammarTree = topNode;
break;
}
}
return grammarTree;
} // end of function
QR::ExprElement ItemExpr::getQRExprElem() const
{
return QR::QRNoElem;
}
QR::ExprElement ConstantParameter::getQRExprElem() const
{
return QR::QRScalarValueElem;
}
// Constructor for the wrapper class RangeSpecRef:
RangeSpecRef::RangeSpecRef(OperatorTypeEnum otype,
OptNormRangeSpec* range,
ItemExpr *colValueId,
ItemExpr *reConsIExpr)
: ItemExpr(ITM_RANGE_SPEC_FUNC,colValueId,reConsIExpr),
range_(range)
{}
/* Get the already generated ValueId */
DisjunctArray * RangeSpecRef::mdamTreeWalk()
{
return new (CmpCommon::statementHeap()) DisjunctArray(new (CmpCommon::statementHeap()) ValueIdSet(getValueId()));
}
/* Need to get the access rightchild to print out the value , rightchild is reconstructed though */
/* This will affect explain "xx" output, this needs to be completed while we get a pointer to the right child
Item expression, then it will print something like <column> "Range in" Item expression corresponding to
<subranges> */
const NAString RangeSpecRef::getText() const
{
return "Range in";
}
short RangeSpecRef::mdamPredGen(Generator * generator,
MdamPred ** head,
MdamPred ** tail,
MdamCodeGenHelper & mdamHelper,
ItemExpr * parent)
{
// return dummy;
return 0;
}
Int32 RangeSpecRef::getArity() const
{
return 2;
}
// delete this and the range_ object associated with this, if present */
RangeSpecRef::~RangeSpecRef()
{
if(range_)
delete range_;
}
ItemExpr * RangeSpecRef::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
RangeSpecRef *result;
if (derivedNode == NULL)
// Need a Deep copy of range_ object. - Change needs to be done after
// Copy ctor is available.
// Curently range_->clone(outHeap) gives Null value, while copying the tree
// So again back to shallow copy
result = new (outHeap) RangeSpecRef(getOperatorType(),range_, NULL,NULL);
else
result = (RangeSpecRef *) derivedNode;
// copy the data members
return ItemExpr::copyTopNode(result, outHeap);
}
short RangeSpecRef::codeGen(Generator* generator)
{
child(1)->codeGen(generator);
return 0;
}
void RangeSpecRef::unparse(NAString &result,
PhaseEnum phase,
UnparseFormatEnum form,
TableDesc * tabId) const
{
// Don't include rangespec op if query format -- it won't parse.
if (form == QUERY_FORMAT || form == COMPUTED_COLUMN_FORMAT)
child(1)->unparse(result, phase, form, tabId);
else
ItemExpr::unparse(result, phase, form, tabId);
}
ItemExpr* RangeSpecRef::removeRangeSpecItems(NormWA* normWA)
{
return getRangeObject()->getRangeItemExpr(normWA);
};
void RangeSpecRef::getValueIdSetForReconsItemExpr(ValueIdSet &outvs)
{
ValueIdSet items;
ItemExpr* ie;
child(1)->convertToValueIdSet(items, NULL, ITM_ITEM_LIST);
for (ValueId vid=items.init(); items.next(vid); items.advance(vid))
{
ie = vid.getItemExpr();
if (!ie->isLike())
ie->convertToValueIdSet(outvs, NULL, ITM_AND);
else
outvs.insert(vid); // Add LIKE expansion without decomposing
}
}
ItemExpr* revertBackToOldTree(CollHeap *heap, ItemExpr* newTree)
{
if(newTree->getOperatorType() != ITM_AND &&
newTree->getOperatorType() != ITM_OR)
{
if (newTree->getOperatorType() == ITM_RANGE_SPEC_FUNC )
{
return(newTree->child(1));
}
else
{
return (newTree);
}
}
else
{
ItemExpr* newLeftNode = revertBackToOldTree(heap,newTree->child(0));
ItemExpr* newRightNode = revertBackToOldTree(heap,newTree->child(1));
assert(newLeftNode != NULL && newRightNode != NULL);
newTree->setChild(0, newLeftNode);
newTree->setChild(1, newRightNode);
return newTree;
}
}
// This method reverts back to Old Tree as valueIdSet from transformed Tree as valueIdSet.
void revertBackToOldTreeUsingValueIdSet( ValueIdSet& inputSet /* IN */, ValueIdSet& outputSet /* OUT */)
{
ValueIdSet orSet,outorSet;
ItemExpr * inputItemExprTree = NULL;
for (ValueId predId = inputSet.init();
inputSet.next(predId);
inputSet.advance(predId) ){
if( predId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC){
outputSet += predId.getItemExpr()->child(1)->castToItemExpr()->getValueId();
if(inputItemExprTree != NULL)
{
outputSet += inputItemExprTree->getValueId();
inputItemExprTree = NULL;
outorSet.clear();
}
}
else if( predId.getItemExpr()->getOperatorType() == ITM_OR){
predId.getItemExpr()->convertToValueIdSet(orSet, NULL, ITM_OR, FALSE);
for (ValueId predIdOr = orSet.init();
orSet.next(predIdOr);
orSet.advance(predIdOr) ){
if(predIdOr.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC){
outorSet += predIdOr.getItemExpr()->child(1)->castToItemExpr()->getValueId();
}
else
outorSet += predIdOr;
}
if(outorSet.entries())
inputItemExprTree = outorSet.rebuildExprTree(ITM_OR,FALSE,FALSE);
}
else
{
outputSet += predId;
if(inputItemExprTree != NULL)
{
outputSet += inputItemExprTree->getValueId();
inputItemExprTree = NULL;
outorSet.clear();
}
}
}// for (1)
if(inputItemExprTree != NULL)
{
outputSet += inputItemExprTree->getValueId();
inputItemExprTree = NULL;
outorSet.clear();
}
}
NABoolean LOBoper::isCovered
(const ValueIdSet& newExternalInputs,
const GroupAttributes& coveringGA,
ValueIdSet& referencedInputs,
ValueIdSet& coveredSubExpr,
ValueIdSet& unCoveredExpr) const
{
// If the argument is not a constant then it can be
// evaluated anywhere in the tree. So check for its coverage
ValueIdSet localSubExpr;
for (Lng32 i = 0; i < (Lng32)getArity(); i++)
{
if ( coveringGA.covers(child(i)->getValueId(),
newExternalInputs,
referencedInputs,
&localSubExpr) )
{
coveredSubExpr += child(i)->getValueId();
}
}
// cannot be pushed down. Must be evaluated in master exe.
return FALSE;
}
// Evalaute the exprssion at compile time. Assume all operands are constants.
// Return NULL if the computation fails and CmpCommon::diags() may be side-affected.
ConstValue* ItemExpr::evaluate(CollHeap* heap)
{
ValueIdList exprs;
exprs.insert(getValueId());
const NAType& dataType = getValueId().getType();
Lng32 decodedValueLen = dataType.getNominalSize() + dataType.getSQLnullHdrSize();
char staticDecodeBuf[200];
Lng32 staticDecodeBufLen = 200;
char* decodeBuf = staticDecodeBuf;
Lng32 decodeBufLen = staticDecodeBufLen;
// For character types, multiplying by 6 to deal with conversions between
// any two known character sets allowed. See CharInfo::maxBytesPerChar()
// for a list of max bytes per char for each supported character set.
Lng32 factor = (DFS2REC::isAnyCharacter(dataType.getFSDatatype())) ? 6 : 1;
if ( staticDecodeBufLen < decodedValueLen * factor) {
decodeBufLen = decodedValueLen * factor;
decodeBuf = new (STMTHEAP) char[decodeBufLen];
}
Lng32 resultLength = 0;
Lng32 resultOffset = 0;
// Produce the decoded key. Refer to
// ex_function_encode::decodeKeyValue() for the
// implementation of the decoding logic.
ex_expr::exp_return_type rc = exprs.evalAtCompileTime
(0, ExpTupleDesc::SQLARK_EXPLODED_FORMAT, decodeBuf, decodeBufLen,
&resultLength, &resultOffset, CmpCommon::diags()
);
ConstValue* result = NULL;
if ( rc == ex_expr::EXPR_OK ) {
CMPASSERT(resultOffset == dataType.getPrefixSizeWithAlignment());
// expect the decodeBuf to have this layout
// | null ind. | varchar length ind. | alignment | result |
// |<---getPrefixSizeWithAlignment-------------->|
// |<----getPrefixSize-------------->|
// The method getPrefixSizeWithAlignment(), the diagram above,
// and this code block assumes that varchar length ind. is
// 2 bytes if present. If it is 4 bytes we should fail the
// previous assert
// Next we get rid of alignment bytes by prepending the prefix
// (null ind. + varlen ind.) to the result. ConstValue constr.
// will process prefix + result. The assert above ensures that
// there are no alignment fillers at the beginning of the
// buffer. Given the previous assumption about size
// of varchar length indicator, alignment bytes will be used by
// expression evaluator only if column is of nullable type.
// For a description of how alignment is computed, please see
// ExpTupleDesc::sqlarkExplodedOffsets() in exp/exp_tuple_desc.cpp
if (dataType.getSQLnullHdrSize() > 0)
memmove(&decodeBuf[resultOffset - dataType.getPrefixSize()],
decodeBuf, dataType.getPrefixSize());
result =
new (heap)
ConstValue(&dataType,
(void *) &(decodeBuf[resultOffset -
dataType.getPrefixSize()]),
resultLength+dataType.getPrefixSize(),
NULL,
heap);
}
return result;
}
ItemExpr * ItmLagOlapFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result = NULL;
if (derivedNode == NULL)
{
switch (getArity()) {
case 2:
result = new (outHeap) ItmLagOlapFunction(child(0), child(1));
break;
case 3:
result = new (outHeap) ItmLagOlapFunction(child(0), child(1), child(2));
break;
default:
CMPASSERT(FALSE);
}
}
else
result = derivedNode;
return ItmSeqOlapFunction::copyTopNode(result, outHeap);
}
ItmLeadOlapFunction::~ItmLeadOlapFunction() {}
ItemExpr *
ItmLeadOlapFunction::copyTopNode(ItemExpr *derivedNode, CollHeap* outHeap)
{
ItemExpr *result;
if (derivedNode == NULL)
{
switch (getArity()) {
case 2:
result = new (outHeap) ItmLeadOlapFunction(child(0), child(1));
break;
case 1:
default:
result = new (outHeap) ItmLeadOlapFunction(child(0));
break;
}
}
else
result = derivedNode;
((ItmLeadOlapFunction*)result)->setOffset(getOffset());
return ItmSeqOlapFunction::copyTopNode(result, outHeap);
}
NABoolean ItmLeadOlapFunction::hasEquivalentProperties(ItemExpr * other)
{
if (other == NULL)
return FALSE;
if (getOperatorType() != other->getOperatorType() ||
getArity() != other->getArity())
return FALSE;
//return getOffsetExpr()->hasEquivalentProperties(((ItmLeadOlapFunction*)other)->getOffsetExpr());
return TRUE;
}
ItemExpr *ItmLeadOlapFunction::transformOlapFunction(CollHeap *heap)
{
return this;
}