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