blob: 7265ae52065ff47ee6734232941b46b9b933a757 [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: QRDescGenerator.cpp
* Description: Construction of query descriptor from QueryAnalysis
* Created: 01/24/2008
* Language: C++
*
**************************************************************************
*/
#include "Analyzer.h"
#include "QRDescGenerator.h"
#include "QRSharedPtr.h"
#include "RelGrby.h"
#include "NumericType.h"
#include "ItemLog.h"
#include "QRLogger.h"
#include "QRExprElement.h"
#include "RelUpdate.h"
const UInt32 QRDescGenerator::GENERATED_JBBID_START = 10000;
ULng32 hashString(const NAString& str) { return str.hash(); }
ULng32 hashValueId(const QRValueId& vid) { return vid; }
Visitor::VisitResult SetRefVisitor::visit(QRElementPtr caller)
{
NAString ref = caller->getRef();
if (ref == "")
return VR_Continue;
// Skip if not column, table, or joinpred
if (!strchr("CTJ", *ref.data()))
return VR_Continue;
QRElementPtr elem = idHash_.getFirstValue(&ref);
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
elem != NULL, QRLogicException,
"Referenced element %s does not exist.", ref.toCharStar());
caller->setReferencedElement(elem);
return VR_Continue;
}
//
// XmlValidatorVisitor
//
Visitor::VisitResult XmlValidatorVisitor::visit(QRElementPtr caller)
{
if (caller->getElementType() == ET_StringVal)
{
QRStringValPtr stringVal = static_cast<QRStringValPtr>(caller);
const NAString& value = stringVal->getValue();
if (value.contains('&') ||
value.contains('%') )
{
foundProblem_ = TRUE;
return VR_Stop;
}
}
else if (caller->getElementType() == ET_WStringVal)
{
QRWStringValPtr stringVal = static_cast<QRWStringValPtr>(caller);
const NAWString& value = stringVal->getWideValue();
if (na_wcschr(value, NAWCHR('&')) ||
na_wcschr(value, NAWCHR('%')) )
{
foundProblem_ = TRUE;
return VR_Stop;
}
}
return VR_Continue;
}
void EqualitySet::determineType()
{
QRTRACER("EqualitySet::determineType()");
if (isEmpty())
{
// I don't think this should happen.
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
"determineType() called for empty EqualitySet.");
return;
}
NABoolean isExact = FALSE, wasExact, isSigned = FALSE, isNullable = FALSE;
Lng32 magnitude = 0, otherMagnitude, scale = 0;
// Go through each member of the equality set, getting the parameters for the
// narrowest type common to them all (excluding a constant, if any).
NABoolean typeInitialized = FALSE;
for (CollIndex i=0; i<entries(); i++)
{
// Don't let the constant's type influence the eqset type.
if (at(i)->getOperatorType() == ITM_CONSTANT)
continue;
const NAType& t = at(i)->getValueId().getType();
if (t.getTypeQualifier() != NA_NUMERIC_TYPE)
{
// Eventually we should synthesize non-numeric types as well, but for
// now, just make sure the type is not null.
type_ = t.newCopy(heap_);
return;
}
const NumericType& nt = static_cast<const NumericType&>(t);
if (!typeInitialized)
{
// Initalize the type parameters to those of the first member.
// Regarding the computation of magnitude for float types, see the
// sad tale of precision for those types in the comment for
// NumericType::getTruePrecision(), and also note that getMagnitude()
// uses getPrecision() (which apparently can return 0 for floating
// point types) instead of getTruePrecision(). We add 1 to the
// calculated magnitude so it can be easily distinguished from
// decimal-precision exact numerics when we decide what type to
// create at the end of this function.
typeInitialized = TRUE;
isExact = nt.isExact();
isSigned = nt.isSigned();
isNullable = nt.supportsSQLnull();
magnitude = isExact ? nt.getMagnitude()
: nt.getTruePrecision() * 10 + 1;
scale = nt.getScale();
}
else
{
// Modify any type parameters that are more restrictive than those
// of this member.
wasExact = isExact;
if (nt.isExact())
isExact = TRUE;
if (!nt.isSigned())
isSigned = FALSE;
if (!nt.supportsSQLnull())
isNullable = FALSE;
otherMagnitude = nt.isExact() ? nt.getMagnitude()
: nt.getTruePrecision() * 10 + 1;
if (magnitude > otherMagnitude)
magnitude = otherMagnitude;
if (scale > nt.getScale() || !wasExact)
scale = nt.getScale();
}
}
// Create the type that supports a range of values common to all members of
// the equality set. Magnitude is divisible by 10 for decimal precision exact
// numerics.
if (magnitude % 10 > 0)
{
// Binary precision smallint, int, largeint, real, double)
if (magnitude < 50)
type_ = new(heap_) SQLSmall(heap_,isSigned, isNullable);
else if (magnitude < 100)
type_ = new(heap_) SQLInt(heap_, isSigned, isNullable);
else if (magnitude < 200)
type_ = new(heap_) SQLLargeInt(heap_, isSigned, isNullable);
else if (magnitude < 500)
type_ = new(heap_) SQLReal(heap_, isNullable);
else
type_ = new(heap_) SQLDoublePrecision(heap_, isNullable);
}
else
{
// @ZX need to amend this (and elsewhere) for SQLBigNum.
// Numeric or Decimal -- type will be generated as Numeric
const Int16 DisAmbiguate = 0;
type_ = new(heap_) SQLNumeric(heap_, isSigned, (magnitude / 10) + scale, scale,
DisAmbiguate, // added for 64bit proj.
isNullable);
}
} // determineType()
QRDescGenerator::~QRDescGenerator()
{
QRTRACER("QRDescGenerator::~QRDescGenerator()");
for (CollIndex i=0; i<allEqualitySets_.entries(); i++)
delete allEqualitySets_[i];
}
NABoolean QRDescGenerator::typeSupported(const NAType* type)
{
QRTRACER("QRDescGenerator::typeSupported()");
switch (type->getTypeQualifier())
{
case NA_NUMERIC_TYPE:
{
const NumericType* numType = static_cast<const NumericType*>(type);
// All approx numerics are ok.
if (!numType->isExact())
return TRUE;
// Don't handle software-supported types yet. This is of type BigNum only
if (numType->isBigNum())
return FALSE;
else
return TRUE;
}
case NA_CHARACTER_TYPE:
{
if (CmpCommon::getDefault(MODE_SPECIAL_1) == DF_ON)
return FALSE;
return ((CharType*)type)->getCollation() == CharInfo::DefaultCollation;
}
case NA_INTERVAL_TYPE:
{
// For interval types, we have to take into account that we represent
// values in terms of the lowest possible field for the interval
// category (year-month or day-time). Thus, we can't support the max
// leading field precision for specific interval types even when they
// don't include those lower fields.
const IntervalType* intvType = static_cast<const IntervalType*>(type);
switch (intvType->getStartField())
{
case REC_DATE_YEAR:
return (SQLInterval::MAX_LEADING_PRECISION >=
IntervalType::getPrecision(intvType->getStartField(),
intvType->getLeadingPrecision(),
REC_DATE_MONTH,
0));
case REC_DATE_MONTH:
return TRUE;
default:
// All day-time interval values are expressed in terms of microseconds.
// If fractional precision is greater than
// microsecs, disable rangespec transformation.
if (type->getScale() > SQLInterval::MAX_FRACTION_PRECISION_USEC)
return FALSE;
return (SQLInterval::MAX_LEADING_PRECISION >=
IntervalType::getPrecision(intvType->getStartField(),
intvType->getLeadingPrecision(),
REC_DATE_SECOND,
SQLInterval::MAX_FRACTION_PRECISION_USEC));
}
}
case NA_DATETIME_TYPE:
// Remaining types are not yet supported.
//case NA_BOOLEAN_TYPE
//case NA_UNKNOWN_TYPE:
//case NA_USER_SUPPLIED_TYPE:
//case NA_RECORD_TYPE:
//case NA_ROWSET_TYPE:
// datetime values are currently converted to Int64 microseconds value
// for rangespec constants. If fractional precision is greater than
// microsecs, disable rangespec transformation.
if (type->getScale() > DatetimeType::MAX_FRACTION_PRECISION_USEC)
return FALSE;
return TRUE;
default:
return FALSE;
}
}
// -----------------------------------------------------------------------
NABoolean
QRDescGenerator::getTableId(ValueId vid,
CANodeId& nodeID, // OUT
ValueId& cvid, // OUT
ValueId& vegrefVid, // OUT
NAString& baseColName, // OUT
NABoolean& isExtraHub, // OUT
Int32& colIndex) // OUT
{
QRTRACER("QRDescGenerator::getTableId()");
ItemExpr *pExpr = vid.getItemExpr();
BaseColumn *pBC = 0;
cvid = vid;
vegrefVid = NULL_VALUE_ID;
if (pExpr->getOperatorType() == ITM_VEG_REFERENCE)
{
vegrefVid = vid;
const ValueIdSet &vegMembers = static_cast<VEGReference*>(pExpr)
->getVEG()->getAllValues();
// Search the veg members for a base column, so we can get its table id.
for (ValueId someMemberId=vegMembers.init();
!pBC && vegMembers.next(someMemberId);
vegMembers.advance(someMemberId))
{
if (someMemberId.getItemExpr()->getOperatorType() == ITM_BASECOLUMN)
{
cvid = someMemberId;
pBC = static_cast<BaseColumn*>(someMemberId.getItemExpr());
}
}
}
else if (pExpr->getOperatorType() == ITM_BASECOLUMN)
pBC = static_cast<BaseColumn*>(pExpr);
else if (pExpr->getOperatorType() == ITM_INDEXCOLUMN)
{
cvid = static_cast<IndexColumn*>(pExpr)->getDefinition();
pBC = static_cast<BaseColumn*>(cvid.getItemExpr());
}
if (pBC)
{
baseColName = pBC->getNAColumn()->getTableName()->getQualifiedNameAsAnsiString()
+ "." + pBC->getColName();
colIndex = pBC->getColNumber();
TableDesc *pTD = pBC->getTableDesc();
if (pTD)
{
if (vegrefVid == NULL_VALUE_ID)
{
ValueIdList baseCols;
ValueIdList vegCols;
baseCols.insert(pBC->getValueId());
pTD->getEquivVEGCols(baseCols, vegCols);
ItemExpr* ie = vegCols[0].getItemExpr();
if (ie->getOperatorType() == ITM_VEG_REFERENCE)
vegrefVid = ie->getValueId();
}
const TableAnalysis *pTA = pTD->getTableAnalysis();
if (pTA)
{
NodeAnalysis *pNA = pTA->getNodeAnalysis();
if (pNA)
{
nodeID = pNA->getId();
isExtraHub = pNA->isExtraHub();
return TRUE;
}
}
}
}
nodeID = NULL_CA_ID;
return FALSE;
} // getTableId()
CANodeId QRDescGenerator::getNodeId(ValueId vid)
{
QRTRACER("QRDescGenerator::getNodeId()");
// Required arguments to getTableId(). All we want here is the node id.
CANodeId nodeId;
ValueId dummyVid;
ValueId dummyVid2;
NAString dummyString;
NABoolean dummyBool;
Int32 colIndex;
getTableId(vid, nodeId, dummyVid, dummyVid2, dummyString, dummyBool, colIndex);
return nodeId;
}
// -----------------------------------------------------------------------
QRColumnPtr QRDescGenerator::genQRColumn(ValueId vid,
UInt32 joinPredId /*= 0*/,
NABoolean markAsUsed /*= TRUE*/)
{
QRTRACER("QRDescGenerator::genQRColumn()");
NABoolean isExtraHub;
CANodeId nodeID;
ValueId col_vid;
ValueId vegref_vid;
NAString baseColName;
Int32 colIndex;
NABoolean gotNodeID = getTableId(vid, nodeID, col_vid, vegref_vid,
baseColName, isExtraHub, colIndex);
if (!gotNodeID)
{
// This should be a vegref to an instantiate_null for a LOJ.
ItemExpr* itemExpr = vid.getItemExpr();
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
isInstNull(itemExpr), QRDescriptorException,
"genQRColumn called for ValueID that is neither column nor "
"instantiate_null function");
return skipInstNull(itemExpr);
}
QRColumnPtr columnElement = new(mvqrHeap_) QRColumn(ADD_MEMCHECK_ARGS(mvqrHeap_));
if (bGenColumnRefs_)
{
if (mColumnsUsed_.contains(col_vid))
{
// Note the early return if we reference an existing column,
// which avoids setting other column properties below.
columnElement->setRefFromInt(col_vid, FALSE);
columnElement->setReferencedElement(getElementForValueID('C', col_vid));
if (mExtraHubColumnsUsed_.contains(col_vid))
columnElement->setExtraHub(TRUE);
return columnElement;
}
else if (markAsUsed) // not when called by setPredBitmap()
{
mColumnsUsed_ += col_vid;
if (isExtraHub)
mExtraHubColumnsUsed_ += col_vid;
}
}
columnElement->setFullyQualifiedColumnName(baseColName);
columnElement->setAndRegisterID(col_vid, colTblIdHash_);
columnElement->setTableID((Int32)nodeID);
columnElement->setExtraHub(isExtraHub);
columnElement->setVegrefId(vegref_vid);
columnElement->setColIndex(colIndex);
columnElement->setNullable(vid.getType().supportsSQLnull());
// If col_vid is the value id of a column used in a join pred, it should
// reference the id of the join pred.
ValueId mappedVid, vegVid;
colToJoinPredMap_.mapValueIdUp(mappedVid, col_vid);
if (mappedVid != col_vid) // returns same value if not mapped
{
ItemExpr* cvie = col_vid.getItemExpr();
if (cvie->getOperatorType() == ITM_BASECOLUMN)
{
BaseColumn* bc = static_cast<BaseColumn*>(cvie);
vegVid = bc->getTableDesc()->getColumnVEGList()[bc->getColNumber()];
}
else
vegVid = mappedVid;
if (mappedVid == vegVid)
{
columnElement->setVegrefId(vegref_vid);
}
}
return columnElement;
} // genQRColumn()
NABoolean QRDescGenerator::isInstNull(ItemExpr* itemExpr)
{
QRTRACER("isInstNull()");
if (itemExpr->getOperatorType() == ITM_INSTANTIATE_NULL)
return TRUE;
if (itemExpr->getOperatorType() != ITM_VEG_REFERENCE)
return FALSE;
const ValueIdSet& vegMembers =
((VEGReference*)itemExpr)->getVEG()->getAllValues();
ValueId firstMemberId = vegMembers.init();
vegMembers.next(firstMemberId); // 1st call to next positions it on 1st member
ItemExpr* firstMemberExpr = firstMemberId.getItemExpr();
return (firstMemberExpr->getOperatorType() == ITM_INSTANTIATE_NULL);
}
QRColumnPtr QRDescGenerator::skipInstNull(ItemExpr* itemExpr)
{
QRTRACER("QRDescGenerator::skipInstNull()");
QRColumnPtr result = NULL;
OperatorTypeEnum opType = itemExpr->getOperatorType();
if (opType == ITM_VEG_REFERENCE)
{
const ValueIdSet& vegMembers =
((VEGReference*)itemExpr)->getVEG()->getAllValues();
ValueId firstMemberId = vegMembers.init();
vegMembers.next(firstMemberId); // 1st call to next positions it on 1st member
itemExpr = firstMemberId.getItemExpr();
opType = itemExpr->getOperatorType();
}
if (opType == ITM_INSTANTIATE_NULL)
{
// The instantiated null column is part of a join pred.
// Do not reference that join pred, and create a full QRColumn element instead.
setGenColumnRefs(FALSE);
result = genQRColumn(itemExpr->child(0));
setGenColumnRefs(TRUE);
NAString* idstr = new(mvqrHeap_) NAString(result->getID());
result->setAndRegisterID(itemExpr->getValueId(), colTblIdHash_); // Set the ID of the InstantiateNull function.
colTblIdHash_.insert(idstr, result);
}
return result;
}
// Static nonmember helper function, called by QRDescGenerator::getExprTree()
// to add extra parameters to certain functions.
static void addFunctionParameters(ItemExpr* ie,
NAMemory* heap,
QRFunctionPtr function)
{
QRTRACER("addFunctionParameters()");
QRParameterPtr param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
switch (ie->getOperatorType())
{
case ITM_EXTRACT:
case ITM_EXTRACT_ODBC:
{
Extract* extractFn = static_cast<Extract*>(ie);
param->setName("extractField");
param->setValue((Int32)extractFn->getExtractField());
function->addHiddenParam(param);
}
break;
case ITM_TRIM:
{
Trim* trimFn = static_cast<Trim*>(ie);
param->setName("mode");
param->setValue((Int32)trimFn->getTrimMode());
function->addHiddenParam(param);
}
break;
case ITM_TRANSLATE:
{
Translate* translateFn = static_cast<Translate*>(ie);
param->setName("mapTableId");
param->setValue((Int32)translateFn->getTranslateMapTableId());
function->addHiddenParam(param);
}
break;
case ITM_DATEFORMAT:
{
DateFormat* dateFormatFn = static_cast<DateFormat*>(ie);
Int32 dateFormat = dateFormatFn->getExpDatetimeFormat();
param->setName("dateFormat");
param->setValue(dateFormat);
function->addHiddenParam(param);
}
break;
case ITM_COMP_ENCODE:
case ITM_COMP_DECODE:
{
CompEncode* encodeFn = static_cast<CompEncode*>(ie);
param->setName("descFlag");
param->setValue(encodeFn->getDescFlag());
function->addHiddenParam(param);
param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
param->setName("caseInsensitiveEncode");
param->setValue(encodeFn->getCaseInsensitiveEncode());
function->addHiddenParam(param);
param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
param->setName("encodedCollation");
param->setValue(encodeFn->getEncodedCollation());
function->addHiddenParam(param);
param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
param->setName("collationType");
param->setValue(encodeFn->getCollationType());
function->addHiddenParam(param);
}
break;
case ITM_FORMAT:
{
Format* formatFn = static_cast<Format*>(ie);
param->setName("formatStr");
param->setValue(formatFn->getFormatStr());
function->addHiddenParam(param);
param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
param->setName("formatType");
param->setValue(formatFn->getFormatType());
function->addHiddenParam(param);
param = new(heap) QRParameter(ADD_MEMCHECK_ARGS(heap));
param->setName("formatCharToDate");
param->setValue(formatFn->getFormatCharToDate());
function->addHiddenParam(param);
}
break;
// We will hit this for OLAP and sequence functions for the time being.
// These are not handled by MVQR yet, and the hidden parameters for these
// functions have not been addressed.
default:
deletePtr(param);
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"Unhandled function type in addFunctionParameters(): %d",
ie->getOperatorType());
break;
}
}
QRExplicitExprPtr QRDescGenerator::getExprTree(ItemExpr* itemExpr)
{
QRTRACER("QRDescGenerator::getExprTree()");
NABoolean valueWasSet=FALSE;
if (itemExpr->getOperatorType() == ITM_RANGE_SPEC_FUNC)
{
RangeSpecRef* range = static_cast<RangeSpecRef*>(itemExpr);
itemExpr = range->getRangeObject()->getRangeItemExpr();
}
else if (itemExpr->getOperatorType() == ITM_INSTANTIATE_NULL)
{
return getExprTree(itemExpr->child(0));
}
switch (itemExpr->getQRExprElem())
{
case QR::QRFunctionElem:
case QR::QRFunctionWithParameters:
{
QRFunctionPtr function =
new(mvqrHeap_) QRFunction(ADD_MEMCHECK_ARGS(mvqrHeap_));
function->setID(itemExpr->getValueId());
function->setFunctionName(itemExpr->getText());
if (itemExpr->isAnAggregate())
{
Aggregate* aggrFunc = static_cast<Aggregate*>(itemExpr);
function->setAggregateFunc(itemExpr->getOperatorType(), aggrFunc->isDistinct());
}
for (Int32 argInx=0; argInx<itemExpr->getArity(); argInx++)
function->addArgument(getExprTree(itemExpr->child(argInx)));
// Extra work for functions like ExtractOdbc that take non-ItemExpr
// parameters.
if (itemExpr->getQRExprElem() == QR::QRFunctionWithParameters)
addFunctionParameters(itemExpr, mvqrHeap_, function);
return function;
}
break;
case QR::QRBinaryOperElem:
{
QRBinaryOperPtr binaryOper =
new(mvqrHeap_) QRBinaryOper(ADD_MEMCHECK_ARGS(mvqrHeap_));
binaryOper->setID(itemExpr->getValueId());
binaryOper->setOperator(itemExpr->getText());
binaryOper->setFirstOperand(getExprTree(itemExpr->child(0)));
binaryOper->setSecondOperand(getExprTree(itemExpr->child(1)));
return binaryOper;
}
break;
case QR::QRUnaryOperElem:
{
QRUnaryOperPtr unaryOper =
new(mvqrHeap_) QRUnaryOper(ADD_MEMCHECK_ARGS(mvqrHeap_));
unaryOper->setID(itemExpr->getValueId());
unaryOper->setOperator(itemExpr->getText());
unaryOper->setOperand(getExprTree(itemExpr->child(0)));
return unaryOper;
}
break;
case QR::QRColumnElem:
{
QRElementPtr elem = NULL;
if (isInstNull(itemExpr))
return skipInstNull(itemExpr);
// VEGReference will return QRColumnElem. If this is a vegref, use a
// constant member of the veg if there is one. An expr with more than
// one column won't be accepted by qms as a range pred.
if (itemExpr->getOperatorType() == ITM_VEG_REFERENCE)
{
ValueId constVid =
static_cast<VEGReference*>(itemExpr)->getVEG()->getAConstant(TRUE);
if (constVid != NULL_VALUE_ID)
return getExprTree(constVid.getItemExpr());
}
// If a vegref, it may have an instantiate_null as the first veg
// member if outer joins are involved.
elem = genQRColumn(itemExpr->getValueId());
ElementType elemType = elem->getElementType();
if (elemType == ET_Column)
return elem->downCastToQRColumn();
else if (elemType == ET_Function)
return elem->downCastToQRFunction();
else
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"Unhandled element type returned from genQRColumn() in getExprTree(): %d",
elemType);
}
break;
case QR::QRScalarValueElem:
{
ConstValue* constVal;
OperatorTypeEnum op = itemExpr->getOperatorType();
if (op == ITM_CACHE_PARAM)
constVal = (static_cast<ConstantParameter*>(itemExpr))->getConstVal();
else
{
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
op == ITM_CONSTANT, QRDescriptorException,
"In getExprTree(), expected ITM_CONSTANT but got %d",
op);
constVal = static_cast<ConstValue*>(itemExpr);
}
if (constVal->isNull())
return new (mvqrHeap_) QRNullVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
const NAType* type = constVal->getType();
QRScalarValuePtr scalar = NULL;
switch (type->getTypeQualifier())
{
case NA_NUMERIC_TYPE:
if (((NumericType*)type)->isExact())
{
scalar = new (mvqrHeap_)
QRNumericVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
((QRNumericValPtr)scalar)->setScale(istring(type->getScale()));
}
else
scalar = new (mvqrHeap_) QRFloatVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
break;
case NA_DATETIME_TYPE:
case NA_INTERVAL_TYPE:
scalar = new (mvqrHeap_)
QRNumericVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
((QRNumericValPtr)scalar)->setScale(istring(type->getScale()));
break;
case NA_CHARACTER_TYPE:
if (((CharType*)type)->getBytesPerChar() == 1)
scalar = new (mvqrHeap_) QRStringVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
else
{
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
((CharType*)type)->getBytesPerChar() == 2,
QRDescriptorException,
"Unhandled bytes-per-char: %d",
((CharType*)type)->getBytesPerChar());
scalar->setValue(constVal->getConstStr(FALSE));
valueWasSet = TRUE;
}
break;
default:
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
"Unhandled data type: %d (%s)",
type->getTypeQualifier(), type->getTypeName().toCharStar());
scalar = new (mvqrHeap_) QRStringVal(ADD_MEMCHECK_ARGS(mvqrHeap_));
break;
}
if (scalar)
{
if (!valueWasSet)
scalar->setValue(constVal->getText());
scalar->setID(itemExpr->getValueId());
if (isDumpMvMode())
{
// Add the "official" unparsed text of the expression as a sub-element.
NAString unparsedText;
itemExpr->unparse(unparsedText, OPTIMIZER_PHASE, QUERY_FORMAT);
scalar->setSql(unparsedText);
}
}
return scalar;
}
break;
case QR::QRNoElem:
default:
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"Unhandled ExprElement enum value: %d",
itemExpr->getQRExprElem());
return NULL;
}
// make the compiler happy
return NULL;
} // getExprTree()
// -----------------------------------------------------------------------
QRExprPtr
QRDescGenerator::genQRExpr(ItemExpr* pExpr,
NABoolean isResidual,
UInt32 joinPredId)
{
QRExprPtr exprElement;
QRTRACER("QRDescGenerator::genQRExpr()");
NABoolean isNotProvided = FALSE;
// Was this expression already generated?
QRElementPtr existingElem = getElementForValueID('X', pExpr->getValueId());
if (existingElem)
{
// Yes, just reference the other element.
exprElement = new(mvqrHeap_) QRExpr(isResidual, ADD_MEMCHECK_ARGS(mvqrHeap_));
exprElement->setRef(existingElem->getID());
exprElement->setReferencedElement(existingElem);
return exprElement;
}
Lng32 treeDepth = 0;
Lng32 exprSize = pExpr->getTreeSize(treeDepth, 0);
if (exprSize > maxExprSize_ || treeDepth > maxExprDepth_)
{
if (isQueryMode() || isDumpMvMode())
isNotProvided = TRUE;
else
throw QRDescriptorException("Expression too complex");
}
// If the expression is a member of an equality set, it consists of a ref
// attribute to the join pred for that equality set.
if (joinPredId)
{
exprElement = new(mvqrHeap_) QRExpr(isResidual, ADD_MEMCHECK_ARGS(mvqrHeap_));
exprElement->setRefFromInt(joinPredId, TRUE);
return exprElement;
}
exprElement = new(mvqrHeap_) QRExpr(isResidual, ADD_MEMCHECK_ARGS(mvqrHeap_));
exprElement->setAndRegisterID(pExpr->getValueId(), colTblIdHash_);
if (isDumpMvMode())
{
// Add the "official" unparsed text of the expression as a sub-element.
NAString unparsedText;
pExpr->unparse(unparsedText, OPTIMIZER_PHASE, QUERY_FORMAT);
if (unparsedText.first('%') != NA_NPOS ||
unparsedText.first('&') != NA_NPOS )
{
// Can't handle special characters that cause XML parsing problems
// or sprintf() problems.
isNotProvided = TRUE;
}
if (!isNotProvided)
{
QRInfoPtr info = new QRInfo(ADD_MEMCHECK_ARGS(mvqrHeap_));
info->setText(unparsedText);
exprElement->setInfo(info);
}
}
if (isNotProvided)
{
// This expression is either too big or using the '%' or '&' characters,
// which can cause problems during XML parsing or SQL generation, so we
// skip it and mark it as NotProvided for QMS. We just need to provide
// the expression's input columns.
exprElement->setResult(QRElement::NotProvided);
// Use a fake function element to hold the list of input columns.
QRFunctionPtr fakeFunction = new(mvqrHeap_) QRFunction(ADD_MEMCHECK_ARGS(mvqrHeap_));
fakeFunction->setID(pExpr->getValueId());
fakeFunction->setFunctionName("fake function");
ValueIdSet vegrefsInExpr;
pExpr->findAll(ITM_VEG_REFERENCE, vegrefsInExpr, FALSE, FALSE);
for (ValueId vegrefVid=vegrefsInExpr.init();
vegrefsInExpr.next(vegrefVid);
vegrefsInExpr.advance(vegrefVid))
{
ItemExpr* itemExpr = vegrefVid.getItemExpr();
QRColumnPtr col = genQRColumn(itemExpr->getValueId());
fakeFunction->addArgument(col);
}
exprElement->setExprRoot(fakeFunction);
}
else
{
// Generate the tree-structured representation of the expression.
QRExplicitExprPtr treeExpr = getExprTree(pExpr);
exprElement->setExprRoot(treeExpr);
}
return exprElement;
} // genQRExpr()
// -----------------------------------------------------------------------
NABoolean
QRDescGenerator::normalizeColumnInExpression(NAString& pExprText,
ValueId colvid,
short paramIndex)
{
QRTRACER("QRDescGenerator::normalizeColumnInExpression()");
NABoolean bColFound = FALSE;
NABoolean bFirst = TRUE;
NAString sReplacement = "@A";
NAString sFQColName;
sFQColName = ((BaseColumn *) colvid.getItemExpr())->getText();
size_t dPos = 0;
size_t dIndex = 0;
NABoolean bDone = FALSE;
while (!bDone)
{
dIndex = pExprText.index(sFQColName, dPos, NAString::exact);
if (dIndex != NA_NPOS)
{
if (bFirst)
{
sReplacement += istring(paramIndex);
bColFound = TRUE;
bFirst = FALSE;
}
pExprText.replace(dIndex, sFQColName.length(), sReplacement);
dPos += sReplacement.length();
}
else
{
bDone = TRUE;
}
}
return bColFound;
} // normalizeColumnInExpression()
void QRDescGenerator::markColumnsAsResidual(ValueIdSet& vegrefsInExpr)
{
QRTRACER("QRDescGenerator::markColumnsAsResidual()");
ValueIdSet baseColsInExpr;
// Iterate over vegrefs in the expression. For each of those, find all the base
// columns in its veg. Mark them all as being used in a residual predicate.
// Avoid including base columns that are equated to a constant.
for (ValueId vegrefVid=vegrefsInExpr.init();
vegrefsInExpr.next(vegrefVid);
vegrefsInExpr.advance(vegrefVid))
{
ItemExpr* itemExpr = vegrefVid.getItemExpr();
if (itemExpr->getOperatorType() == ITM_VEG_REFERENCE &&
static_cast<VEGReference*>(itemExpr)->getVEG()->getAConstant()
== NULL_VALUE_ID)
vegrefVid.getItemExpr()->findAll(ITM_BASECOLUMN, baseColsInExpr, TRUE, TRUE);
else if (itemExpr->getOperatorType() == ITM_BASECOLUMN)
baseColsInExpr.addElement(vegrefVid);
}
// Have all the columns used in the residual predicate, now set their bits in
// the residual pred bitmap.
for (ValueId colVid=baseColsInExpr.init();
baseColsInExpr.next(colVid);
baseColsInExpr.advance(colVid))
{
setPredBitmap(colVid, ET_ResidualPred); // mark use of resid pred on col
}
}
// -----------------------------------------------------------------------
void QRDescGenerator::processResidualPredicate(ValueId predVid,
QRJBBPtr jbbElement)
{
QRTRACER("QRDescGenerator::processResidualPredicate()");
ItemExpr* predItemExpr = predVid.getItemExpr();
QRExprPtr residualPredExpr = genQRExpr(predItemExpr, TRUE);
// Set bits in residual predicate bitmap for all columns referenced in the
// expression.
ValueIdSet vegrefsInExpr;
predItemExpr->findAll(ITM_VEG_REFERENCE, vegrefsInExpr, FALSE, FALSE);
markColumnsAsResidual(vegrefsInExpr);
// Residual predicates can only be on Hub tables.
jbbElement->getHub()->getResidualPredList()->addItem(residualPredExpr);
}
// -----------------------------------------------------------------------
void QRDescGenerator::processGroupBy(RelExpr* groupByNode,
CANodeId gbID,
QRJBBPtr jbbElement)
{
if (groupByNode->getOperator() != REL_GROUPBY)
return;
QRTRACER("QRDescGenerator::processGroupBy()");
GroupByAgg* groupBy = (GroupByAgg *)groupByNode;
QRGroupByPtr groupByElement = new(mvqrHeap_) QRGroupBy(ADD_MEMCHECK_ARGS(mvqrHeap_));
jbbElement->setGroupBy(groupByElement);
QRElementPtr groupItem = NULL;
ValueIdSet gbvis = groupBy->groupExpr();
// Both the hub and extra-hub GroupBy can have the same ID since they are both
// based on the same RelExpr node with the same NodeID. In QMS, the ID from the
// hub GroupBy element will be the only one actually used.
groupByElement->setID(gbID);
for (ValueId gbvid = gbvis.init();
gbvis.next(gbvid);
gbvis.advance(gbvid))
{
ItemExpr *pExpr = gbvid.getItemExpr();
if (isInstNull(pExpr))
groupItem = skipInstNull(pExpr);
else if (pExpr->getOperatorType() == ITM_VEG_REFERENCE)
groupItem = genQRColumn(gbvid);
else
{
// This is an expression.
groupItem = genQRExpr(pExpr, FALSE);
}
groupByElement->getPrimaryList()->addElement(groupItem);
}
} // processGroupBy()
// -----------------------------------------------------------------------
void
QRDescGenerator::processOutputList(const ValueIdSet& normOutputs,
QRJBBPtr jbbElement)
{
QRTRACER("QRDescGenerator::processOutputList()");
QROutputListPtr outputListElement = jbbElement->getOutputList();
const ColumnDescList* mvColList = (isMvMode()
? relExpr_->getRETDesc()->getColumnList()
: NULL);
CollIndex mvNumCols = (mvColList ? mvColList->entries() : 0);
CollIndex outInx, cdInx;
ValueId cvid;
for (cvid = normOutputs.init(), outInx = 0;
normOutputs.next(cvid);
normOutputs.advance(cvid), outInx++)
{
ItemExpr *cvExpr = cvid.getItemExpr();
QROutputPtr qro_p = new(mvqrHeap_) QROutput(ADD_MEMCHECK_ARGS(mvqrHeap_));
if (mvColList)
{
NABoolean found = FALSE;
const OperatorTypeEnum cvExprOp = cvExpr->getOperatorType();
for (cdInx=0; cdInx<mvNumCols && !found; cdInx++)
{
ValueId mvColVid = mvColList->at(cdInx)->getValueId();
if (cvid == mvColVid)
found = TRUE;
// Note that we can't just break out of the for loop if 'found'
// becomes true, because code after the loop makes assumptions
// about the loop index value.
if (!found)
{
ItemExpr* mvExpr = mvColVid.getItemExpr();
OperatorTypeEnum op = mvExpr->getOperatorType();
while (op == ITM_CAST || op == ITM_INSTANTIATE_NULL)
{
mvExpr = mvExpr->child(0);
op = mvExpr->getOperatorType();
}
mvColVid = mvExpr->getValueId();
if (cvid == mvColVid)
found = TRUE;
}
if (!found && cvExprOp == ITM_VEG_REFERENCE)
{
const ValueIdSet& cvVegVids =
((VEGReference*)cvExpr)->getVEG()->getAllValues();
found = cvVegVids.contains(mvColVid);
}
}
// If we did not find the ordinal number of the column, throw an
// exception now, so that an MV descriptor will not be created.
Int32 cvid_i = cvid;
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
found, QRDescriptorException,
"No matching MV column found for output item with "
"ValueId %d", cvid_i);
// Set the ordinal position of the output item within the MV's select
// list, so we can match it to the right column name once the column
// names are known for sure.
qro_p->setColPos((Int32)cdInx-1);
}
if (isInstNull(cvExpr))
{
QRColumnPtr col = skipInstNull(cvExpr);
qro_p->setOutputItem(col);
}
else if (cvExpr->getOperatorType() != ITM_VEG_REFERENCE)
{
QRExprPtr exprElement = genQRExpr(cvExpr, FALSE);
qro_p->setOutputItem(exprElement);
}
else
{
QRElementPtr element = genQRColumn(cvid);
qro_p->setOutputItem(element);
}
qro_p->setID(cvid);
outputListElement->addItem(qro_p);
}
} // processOutputList()
// Nonmember function to write the graphviz specification of the join order
// predecessor requirements to the log.
static void logJBBCPredecessorGraph(CANodeIdSet& jbbcs)
{
NAString graphString(STMTHEAP);
graphString += "\n#STARTJOINORDER\n"
"digraph \"\"\n"
"{\n"
" label=\"Predecessor Relationships\"\n";
char buf[200];
for (CANodeId nodeId = jbbcs.init(); jbbcs.next(nodeId); jbbcs.advance(nodeId))
{
NodeAnalysis* nodeAnalysis = nodeId.getNodeAnalysis();
if (!nodeAnalysis)
continue;
JBBC* jbbc = nodeAnalysis->getJBBC();
if (!jbbc)
continue;
TableAnalysis* tableAnalysis = nodeAnalysis->getTableAnalysis();
if (!tableAnalysis)
continue;
CorrName& corrName = tableAnalysis->getTableDesc()->getCorrNameObj();
NAString nodeLabel = corrName.getCorrNameAsString();
if (nodeLabel.length() == 0)
nodeLabel = corrName.getQualifiedNameAsString();
sprintf(buf, " n%d [label=\"%s\"];\n", (CollIndex)nodeId, nodeLabel.data());
graphString += buf;
const CANodeIdSet& predecessors = jbbc->getPredecessorJBBCs();
for (CANodeId predNodeId = predecessors.init();
predecessors.next(predNodeId);
predecessors.advance(predNodeId))
{
sprintf(buf, " n%d->n%d;\n", (CollIndex)predNodeId, (CollIndex)nodeId);
graphString += buf;
}
}
graphString += "}\n"
"#ENDJOINORDER\n";
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG, graphString.data());
}
// Nonmember helper function for processJBBCList; returns array of join order
// group numbers indexed by node number.
static Int32* getJoinOrderInfo(const CANodeIdSet* const const_jbbcs, size_t& arrSize)
{
CANodeIdSet jbbcs(*const_jbbcs); // Need a copy we can change
CANodeIdSet reached;
CANodeId nodeId, predNodeId;
Int32 currGroupOrderNum = 1;
NABoolean foundOne = FALSE;
Int32 numSkipped;
// Find needed size for array.
CollIndex maxNodeId = NULL_CA_ID;
for (nodeId = jbbcs.init(); jbbcs.next(nodeId); jbbcs.advance(nodeId))
{
if (nodeId > maxNodeId)
maxNodeId = nodeId;
}
// Allocate array of join order group numbers we will return.
arrSize = maxNodeId + 1; // assign output param
Int32* joinOrderGroupNumbers = new(STMTHEAP) Int32[arrSize];
if (QRLogger::isCategoryInDebug(CAT_SQL_COMP_QR_DESC_GEN))
logJBBCPredecessorGraph(jbbcs);
while (jbbcs.entries() > 0)
{
numSkipped = 0;
for (nodeId = jbbcs.init(); jbbcs.next(nodeId); jbbcs.advance(nodeId))
{
NodeAnalysis* nodeAnalysis = nodeId.getNodeAnalysis();
if (!nodeAnalysis)
{
numSkipped++;
continue;
}
JBBC* jbbc = nodeAnalysis->getJBBC();
if (!jbbc)
{
numSkipped++;
continue;
}
NABoolean reachable = TRUE;
const CANodeIdSet& predecessors = jbbc->getPredecessorJBBCs();
for (predNodeId = predecessors.init();
reachable && predecessors.next(predNodeId);
predecessors.advance(predNodeId))
{
if (reached.containsThisId(predNodeId))
continue;
reachable = FALSE; // haven't reached all its predecessors yet
} // each predecessor
if (reachable)
{
// Set the node's join order group number and add it to set of
// nodes we have processed. It also needs to be removed from the
// list of jbbcs, but we can't do that while we're iterating
// over it. After the current pass, the set of reached nodes will
// be removed from the jbbcs set, prior to the next loop over the
// jbbcs.
joinOrderGroupNumbers[nodeId] = currGroupOrderNum;
reached += nodeId;
foundOne = TRUE;
}
} // each jbbc
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
foundOne || numSkipped == jbbcs.entries(),
QRDescriptorException,
"One or more nodes not reachable via required predecessor nodes");
foundOne = FALSE;
currGroupOrderNum++;
jbbcs -= reached;
} // while
return joinOrderGroupNumbers;
}
// -----------------------------------------------------------------------
void QRDescGenerator::processJBBCList(CANodeIdSet* jbbcNodeIds,
QRJBBPtr jbbElement,
ValueIdSet &jbbOutputs,
CANodeId& groupJbbcNodeId)
{
QRTRACER("QRDescGenerator::processJBBCList()");
QRTablePtr *qrtableArray = 0;
qrtableArray = (QRTablePtr *)calloc(jbbcNodeIds->entries(),
sizeof(QRTablePtr));
short numTables = 0;
// Get array of join order groups, indexed by node number. This array is
// allocated in the function called, and we have to delete it.
size_t joinOrderArrSize;
Int32* joinOrders = getJoinOrderInfo(jbbcNodeIds, joinOrderArrSize);
groupJbbcNodeId = NULL_CA_ID; // Unless we find one
// Create an array of JBBCs
for (CANodeId nodeId = jbbcNodeIds->init();
jbbcNodeIds->next(nodeId);
jbbcNodeIds->advance(nodeId))
{
NodeAnalysis* nodeAnalysis = nodeId.getNodeAnalysis();
if (!nodeAnalysis) // It is possible there is no node analysis, but I'm not
continue; // sure why that is the case
TableAnalysis* tableAnalysis = nodeAnalysis->getTableAnalysis();
if (tableAnalysis)
{
// Table
const NATable* tbl = tableAnalysis->getTableDesc()->getNATable();
NAString tblName = tbl->getTableName().getQualifiedNameAsAnsiString();
QRTablePtr tableElement = new(mvqrHeap_)
QRTable(ADD_MEMCHECK_ARGS(mvqrHeap_));
tableElement->setAndRegisterID(nodeId, colTblIdHash_);
tableElement->setTableName(tblName);
tableElement->setExtraHub(nodeAnalysis->isExtraHub());
char sRedeftime[30];
memset(sRedeftime, 0, sizeof(sRedeftime));
convertInt64ToAscii(tbl->getRedefTime(), sRedeftime);
tableElement->setTimestamp(NAString(sRedeftime));
tableElement->setIsAnMV(tbl->isAnMV());
tableElement->setNumCols(tbl->getNAColumnArray().entries());
tableElement->setJoinOrder(joinOrders[nodeId]);
if (nodeAnalysis->getJBBC()->parentIsLeftJoin())
tableElement->setLOJParent(TRUE);
if (isDumpMvMode())
{
const NAString& exposedName = tableAnalysis->getTableDesc()->getCorrNameObj().getExposedNameAsAnsiString();
const NAString& corrName = strchr(exposedName, '.') ?
tableAnalysis->getTableDesc()->getCorrNameObj().getQualifiedNameObj().getObjectName() :
exposedName;
tableElement->setCorrelationName(corrName);
}
processKeys(tableAnalysis, tableElement, jbbOutputs);
qrtableArray[numTables++] = tableElement;
}
else
{
// Handle linked JBBs here.
QRJBBPtr groupJbb = new(mvqrHeap_) QRJBB(genJBBid(),
(CollIndex)NULL_CA_ID,
ADD_MEMCHECK_ARGS(mvqrHeap_));
const JBBSubset* jbbSubset = nodeAnalysis->getOriginalExpr()
->getGroupAnalysis()->getLocalJBBView();
if (jbbSubset)
{
groupJbb->setRefFromInt(jbbSubset->getJBB()->getJBBId());
jbbElement->getHub()->getJbbcList()->addElement(groupJbb);
}
else if (nodeAnalysis->getOriginalExpr()->getOperatorType() == REL_GROUPBY)
{
groupJbbcNodeId = nodeId;
}
else
{
deletePtr(groupJbb);
Int32 nodeIdVal = nodeId;
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"Unsupported operator: %s",
nodeAnalysis->getOriginalExpr()->getText().data());
}
}
}
// Finished with join order array.
NADELETEARRAY(joinOrders, joinOrderArrSize, Int32, STMTHEAP);
// Sort the array by table name.
if (numTables > 0)
{
opt_qsort(qrtableArray, numTables, sizeof(QRTablePtr),
//QRDescGenerator::cmpQRTableName);
QRElement::cmpQRElement);
}
// Add the sorted tables to the descriptor.
for (short iTableIndex = 0; iTableIndex < numTables; iTableIndex++)
{
QRTablePtr tableElement = qrtableArray[iTableIndex];
// Is this a hub or extra-hub table?
if (tableElement->isExtraHub())
jbbElement->getExtraHub()->addTable(tableElement);
else
jbbElement->getHub()->getJbbcList()->addElement(tableElement);
}
free(qrtableArray);
} // processJBBCList()
// -----------------------------------------------------------------------
void QRDescGenerator::processKeys(TableAnalysis* tableAnalysis,
QRTablePtr tableElement,
ValueIdSet& jbbOutputs)
{
QRTRACER("QRDescGenerator::processKeys()");
// Generate the content of the <Key> element using the clustering index.
// The primary key serves this purpose if one is available, otherwise SYSKEY.
ValueIdSet keyCols(tableAnalysis->getTableDesc()->getClusteringIndex()
->getClusteringKeyCols());
QRKeyPtr keyElement = NULL;
if (isQueryMode())
keyElement = new(mvqrHeap_) QRKey(ADD_MEMCHECK_ARGS(mvqrHeap_));
//check if the key columns are covered by the jbb outputs
NABoolean keyCovered = TRUE;
for (ValueId vid=keyCols.init();
keyCols.next(vid);
keyCols.advance(vid))
{
if (isQueryMode())
{
QRElementPtr keyColumn = genQRColumn(vid);
keyElement->addElement(keyColumn);
}
if (!jbbOutputs.containsTheGivenValue(vid))
keyCovered = FALSE;
}
if (isQueryMode())
tableElement->setKey(keyElement);
if (keyCovered && (!isQueryMode()))
tableElement->setIsKeyCovered(TRUE);
}
// -----------------------------------------------------------------------
void QRDescGenerator::getSingleTableJBBs(QRDescriptorPtr desc, RelExpr* expr)
{
QRTRACER("QRDescGenerator::getSingleTableJBBs()");
RelExpr* subtree;
Scan* scanExpr;
NodeAnalysis* nodeAnalysis;
const NATable* naTable;
// Traverse query tree, looking for table scans that are not part of a JBB.
// We will treat these as single-JBBC JBBs for the purpose of query rewrite.
char sRedeftime[30];
Int32 arity = expr->getArity();
for (Int32 i = 0; i < arity; i++)
{
subtree = expr->child(i).getPtr();
OperatorType op = subtree->getOperator();
// If the node is a GroupBy that is not part of a JBB (its GBAnalyis is
// null), skip over it and look for a Scan node below. This can happen when
// the GB is on a primary key and will eventually be discarded as unnecessary.
// Use a loop in case of cascaded groupbys as in the case of "select distinct"
// with a grouping query.
while (op.match(REL_GROUPBY) &&
static_cast<GroupByAgg*>(subtree)->getGBAnalysis() == NULL)
{
subtree = subtree->child(0).getPtr();
op = subtree->getOperator();
}
if (op.match(REL_SCAN) &&
(desc->getElementType() == ET_MVDescriptor || // always for MV desc
qrNeededForTable(expr, subtree)))
{
scanExpr = static_cast<Scan*>(subtree);
nodeAnalysis = scanExpr->getGroupAnalysis()->getNodeAnalysis();
QRJBBPtr jbb = new(mvqrHeap_) QRJBB(genJBBid(),
(CollIndex)(nodeAnalysis->getId()),
ADD_MEMCHECK_ARGS(mvqrHeap_));
desc->addJBB(jbb);
QRTablePtr tableElement = new(mvqrHeap_) QRTable(ADD_MEMCHECK_ARGS(mvqrHeap_));
naTable = scanExpr->getTableDesc()->getNATable();
tableElement->setAndRegisterID(nodeAnalysis->getId(), colTblIdHash_);
tableElement->setTableName(naTable->getTableName().getQualifiedNameAsAnsiString());
tableElement->setExtraHub(nodeAnalysis->isExtraHub());
memset(sRedeftime, 0, sizeof(sRedeftime));
convertInt64ToAscii(naTable->getRedefTime(), sRedeftime);
tableElement->setTimestamp(NAString(sRedeftime));
tableElement->setIsAnMV(naTable->isAnMV());
tableElement->setNumCols(naTable->getNAColumnArray().entries());
if (isDumpMvMode())
{
const NAString& exposedName = scanExpr->getTableDesc()->getCorrNameObj().getExposedNameAsAnsiString();
const NAString& corrName = strchr(exposedName, '.') ?
scanExpr->getTableDesc()->getCorrNameObj().getQualifiedNameObj().getObjectName() :
exposedName;
tableElement->setCorrelationName(corrName);
}
ValueIdSet jbbOutputs = nodeAnalysis->
getOriginalExpr()->
getGroupAttr()->
getCharacteristicOutputs();
processKeys(nodeAnalysis->getTableAnalysis(), tableElement, jbbOutputs);
if (tableElement->isExtraHub())
jbb->getExtraHub()->addTable(tableElement);
else
jbb->getHub()->getJbbcList()->addElement(tableElement);
if (expr->getOperator() == REL_GROUPBY)
processGroupBy(expr, 0, jbb);
// Use characteristic outputs of the parent if it is a groupby node.
if (expr->getOperator().match(REL_GROUPBY))
processOutputList(expr->getGroupAttr()->getCharacteristicOutputs(), jbb);
else
processOutputList(scanExpr->getGroupAttr()->getCharacteristicOutputs(), jbb);
}
else if (op.match(REL_UNARY_UPDATE))
{
// Need to create a qrtable for the update node and add its id to the hash
// table to prevent an assertion failure when placing its preds in range
// or residual bitmaps.
QRTablePtr tableElement = new(mvqrHeap_) QRTable(ADD_MEMCHECK_ARGS(mvqrHeap_));
Update* updExpr = static_cast<Update*>(subtree);
CANodeId id = updExpr->getTableDesc()->getTableAnalysis()
->getNodeAnalysis()->getId();
tableElement->setAndRegisterID(id, colTblIdHash_);
}
// cut off recursion at root of JBB
// assume outer joins are part of JBB
//else if (!op.match(REL_ANY_JOIN) && !op.match(REL_MULTI_JOIN))
else if (!op.match(REL_ANY_JOIN) && !op.match(REL_MULTI_JOIN) && !op.match(REL_GROUPBY))
{
getSingleTableJBBs(desc, subtree);
}
}
} // getSingleTableJBBs
QRJBBPtr QRDescGenerator::createJbb(JBB* jbb)
{
QRTRACER("QRDescGenerator::createJbb()");
QRJBBPtr jbbElement = new(mvqrHeap_) QRJBB(jbb->getJBBId(), jbb,
ADD_MEMCHECK_ARGS(mvqrHeap_));
CANodeIdSet jbbcs = jbb->getJBBCs();
ValueIdSet jbbOutputs;
if (jbb->getGBAnalysis())
jbbOutputs = jbb->getNormOutputs();
else if (jbb->getJBBCs().entries() == 1)
jbbOutputs = jbb->getJBBCs().getFirst().getNodeAnalysis()->
getOriginalExpr()->getGroupAttr()->
getCharacteristicOutputs();
else
jbbOutputs = jbb->getNormOutputs();
// JBBC List
CANodeId groupJbbcNodeId = NULL_CA_ID;
processJBBCList(&jbbcs, jbbElement, jbbOutputs, groupJbbcNodeId);
// Predicates. GB node needed to find having pred on count(*), which is not
// pushed down to a scan node.
GBAnalysis* gbAnalysis = jbb->getGBAnalysis();
// Group By
// For some nested queries with GB that constitute a separate JBB, the JBB
// does not have a GBAnalysis object. The GB original expr must be
// obtained from the NodeAnalysis of the grouping node itself.
// However the GroupByAgg* is derived, it must be saved in the QRJBB object
// for the eventual call to processReferencingPreds() for the jbb.
if (gbAnalysis != NULL)
{
processGroupBy(gbAnalysis->getOriginalGBExpr(),
gbAnalysis->getGroupingNodeId(),
jbbElement);
jbbElement->setGbExpr(gbAnalysis->getOriginalGBExpr());
}
else if (groupJbbcNodeId != NULL_CA_ID)
{
RelExpr* relExpr = groupJbbcNodeId.getNodeAnalysis()->getOriginalExpr();
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_ERROR,
relExpr->getOperatorType() == REL_GROUPBY,
QRDescriptorException,
"Expected grouping JBBC, but op type was %d.",
relExpr->getOperatorType());
processGroupBy(static_cast<GroupByAgg*>(relExpr),
groupJbbcNodeId, jbbElement);
jbbElement->setGbExpr(static_cast<GroupByAgg*>(relExpr));
}
// Output List
processOutputList(jbb->getNormOutputs(), jbbElement);
return jbbElement;
} // createJbb()
NABoolean QRDescGenerator::hasRewriteEnabledMVs(TableDesc* tableDesc)
{
QRTRACER("QRDescGenerator::hasRewriteEnabledMVs()");
const UsingMvInfoList& mvList = tableDesc->getNATable()->getMvsUsingMe();
for (CollIndex i=0; i<mvList.entries(); i++)
{
if (mvList[i]->isRewriteEnabled())
return TRUE;
}
return FALSE;
}
NABoolean QRDescGenerator::qrNeededForTable(RelExpr* parent, RelExpr* scanExpr)
{
QRTRACER("QRDescGenerator::qrNeededForTable()");
if (putAllJBBsInQD_)
return TRUE;
Scan* scan = static_cast<Scan*>(scanExpr);
// If there are no MVs on the table, no rewrite is possible.
if (!hasRewriteEnabledMVs(scan->getTableDesc()))
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"Omitting single table JBB %s from query descriptor -- no "
"rewrite-enabled MVs are defined on it.",
scan->getTableDesc()->getNATable()->getTableName().getObjectName().data());
return FALSE;
}
return TRUE;
}
NABoolean QRDescGenerator::qrNeededForJBB(JBB* jbb)
{
QRTRACER("QRDescGenerator::qrNeededForJBB()");
if (putAllJBBsInQD_)
return TRUE;
NodeAnalysis* nodeAnalysis;
TableAnalysis* tableAnalysis;
CANodeIdSet jbbcs = jbb->getJBBCs();
// Count the JBBCs that have MVs defined on them. We need at least two, or
// one plus a Group By.
for (CANodeId jbbc = jbbcs.init();
jbbcs.next(jbbc);
jbbcs.advance(jbbc))
{
nodeAnalysis = jbbc.getNodeAnalysis();
if (!nodeAnalysis->isExtraHub()) // Only check hub tables.
{
tableAnalysis = nodeAnalysis->getTableAnalysis();
if (tableAnalysis && hasRewriteEnabledMVs(tableAnalysis->getTableDesc()))
return TRUE; // found a base table that uses an mv
}
}
return FALSE;
}
// See if a jbbsubset uses any semijoins or TSJs. This supports a temporary
// restriction that is checked in processJBBs() below. Since the need for this
// check is temporary, we make it through this local nonmember function instead
// of further complicating the JBBSubsetAnalysis class with a member function.
static NABoolean noSemiOrTSJ(JBBSubsetAnalysis* jbbSubsetAnalysis)
{
CANodeId node;
const CANodeIdSet& jbbcs = jbbSubsetAnalysis->getJBBCs();
for (node = jbbcs.init(); jbbcs.next(node); jbbcs.advance(node))
{
Join* parentJoin = node.getNodeAnalysis()->getJBBC()->getOriginalParentJoin();
if (parentJoin)
{
OperatorType op = parentJoin->getOperator();
if (op.match(REL_ANY_SEMIJOIN) || op.match(REL_ANY_TSJ))
return FALSE;
}
}
return TRUE; // no semijoins or TSJs
}
NABoolean QRDescGenerator::processJBBs(QRDescriptorPtr descPtr,
QueryAnalysis* qa)
{
QRTRACER("QRDescGenerator::processJBBs()");
// This is a reference to the descriptor's list of JBBs, to which will
// first be added the pseudo-JBBs we create for table scans that are not
// part of any JBB, and later in this function, the actual Analyzer JBBs.
const NAPtrList<QRJBBPtr>& qrJbbList = descPtr->getJbbList();
if (isDumpMvMode())
{
// Don't bother with single table queries in WA mode.
const CANodeIdSet& allTables = qa->getTables();
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
allTables.entries() > 1,
QRDescriptorException,
"This query has a single table.");
}
// Look for table scans that are not part of a JBB, and create pseudo-JBBs
// for them. Note that these will be reflected in the contents of qrJbbList
// upon return.
getSingleTableJBBs(descPtr, relExpr_);
// Initialize return value to false if previous step created no JBBs.
NABoolean jbbElemsWereCreated = !qrJbbList.isEmpty();
// Check if any single-table (pseudo) JBBs use an MV. If so, we can skip this
// step for any actual JBBs later in this function.
// If the cqd MVQR_ALL_JBBS_IN_QD is ON (putAllJBBsInQD_), we don't need to
// check this, the descriptor is created regardless of MV usage.
NABoolean usesEnabledMVs = FALSE;
CollIndex i;
if (jbbElemsWereCreated && !putAllJBBsInQD_)
for (i = 0; !usesEnabledMVs && i < qrJbbList.entries(); i++)
{
// getNodeId() returns a CollIndex, so we have to cast it to CANodeId.
NodeAnalysis* nodeAnalysis =
static_cast<CANodeId>(qrJbbList[i]->getNodeId()).getNodeAnalysis();
if (!nodeAnalysis->isExtraHub() &&
hasRewriteEnabledMVs(nodeAnalysis->getTableAnalysis()->getTableDesc()))
{
usesEnabledMVs = TRUE; // table is used by an MV
}
}
const ARRAY(JBB*)& jbbArray = qa->getJBBs();
CollIndex remainingJBBs = jbbArray.entries();
if (isDumpMvMode())
{
Int32 totalJBBs = remainingJBBs + descPtr->getJbbList().entries();
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
totalJBBs == 1,
QRDescriptorException,
"This query has %d JBBs.", totalJBBs);
}
// For a query descriptor, make sure some table involved is used by an MV
// before going further. We used to leave out individual JBBs on this basis,
// but there are typically inter-JBB column references that can result in an
// exception being thrown if some JBBs are not processed (see bug 2502).
// If the cqd MVQR_ALL_JBBS_IN_QD is ON (putAllJBBsInQD_), we don't need to
// check this, the descriptor is created regardless of MV usage.
if (!usesEnabledMVs && !putAllJBBsInQD_ && isQueryMode())
{
for (i = 0; !usesEnabledMVs && remainingJBBs > 0; i++)
{
if (jbbArray.used(i))
{
remainingJBBs--;
currentJBB_ = jbbArray[i];
usesEnabledMVs = qrNeededForJBB(currentJBB_);
}
}
if (!usesEnabledMVs)
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"Query descriptor not generated: no JBBC of any JBB is used in "
"a rewrite-enabled MV.");
return FALSE;
}
}
// Now revisit each JBB and create a descriptor element for it.
remainingJBBs = jbbArray.entries();
for (i = 0; remainingJBBs > 0; i++)
{
if (jbbArray.used(i))
{
remainingJBBs--;
currentJBB_ = jbbArray[i];
// Semijoins and TSJs not handled yet.
JBBSubsetAnalysis* jbbSubsetAnalysis =
currentJBB_->getMainJBBSubset().getJBBSubsetAnalysis();
if (noSemiOrTSJ(jbbSubsetAnalysis))
{
QRJBBPtr jbb = createJbb(currentJBB_);
jbbElemsWereCreated = TRUE;
descPtr->addJBB(jbb);
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"Omitting JBB #%d from descriptor because it contains "
"semijoins, anti-semijoins, or TSJs", currentJBB_->getJBBId());
if (isDumpMvMode())
{
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE,
QRDescriptorException,
"SemiJoins are not supported yet.");
}
}
}
}
// The processing of predicates is deferred until after the descriptor
// elements for all JBBs have been created. This avoids the problem where
// a predicate refers to a column for which the table hasn't been processed,
// which leads to a failure in getting the table's range or residual pred
// bitmap because it hasn't been set up yet.
QRJBBPtr qrJbbPtr = NULL;
for (CollIndex qrJbbInx=0; qrJbbInx<qrJbbList.entries(); qrJbbInx++)
{
qrJbbPtr = qrJbbList[qrJbbInx];
currentJBB_ = qrJbbPtr->getJBB();
if (currentJBB_)
{
CANodeIdSet jbbcs = currentJBB_->getJBBCs();
processReferencingPreds(&jbbcs,
qrJbbPtr->getGbExpr(),
qrJbbPtr);
}
else
{
CANodeId nodeId = (CANodeId)(qrJbbPtr->getNodeId());
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
nodeId != NULL_CA_ID,
QRDescriptorException,
"QRJBB at position %d in list had neither a JBB* nor a node id",
qrJbbInx);
CANodeIdSet nodes;
nodes.addElement(nodeId);
RelExpr* expr = nodeId.getNodeAnalysis()->getOriginalExpr();
processReferencingPreds(&nodes,
expr->getOperator() == REL_GROUPBY ? expr : NULL,
qrJbbPtr);
}
}
currentJBB_ = NULL; // No longer processing a specific JBB
// Now that all predicates have been processed, the range predicates we have
// kept in a hash table are guaranteed to be complete, and can be added to
// the appropriate range predicate list.
if (jbbElemsWereCreated)
addRangePredicates();
// Return true if one or more JBB elements were found. Returning false
// prevents a descriptor from being generated.
return jbbElemsWereCreated;
}
void QRDescGenerator::logColumnBitmap(QRTablePtr table,
const XMLBitmap& bitmap,
ElementType predType)
{
// Exit immediately if logging is not in DEBUG level.
if (!QRLogger::isCategoryInDebug(CAT_SQL_COMP_QR_DESC_GEN))
return;
QRTRACER("QRDescGenerator::logColumnBitmap()");
// Log which bitmap this is.
if (predType == ET_RangePred)
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"For table %s, the following columns have range predicates:",
table->getTableName().data());
}
else if (predType == ET_ResidualPred)
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"For table %s, the following columns have residual predicates:",
table->getTableName().data());
}
else
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
"Table %s has these columns set in bitmap of element type %d:",
table->getTableName().data(), predType);
}
// If nothing set, log a message noting that and return.
if (bitmap.isEmpty())
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG, " <none>");
return;
}
// At least one column is marked in the bitmap. List the ones that are.
CANodeId tblNodeId = table->getIDNum();
const NAColumnArray& colsInTable =
tblNodeId.getNodeAnalysis()->getTableAnalysis()->getTableDesc()
->getNATable()->getNAColumnArray();
assertLogAndThrow2(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
table->getNumCols() == colsInTable.entries(),
QRDescriptorException,
"logColumnBitmap() expected %d columns, but found %d in list",
table->getNumCols(), colsInTable.entries());
for (CollIndex i=0; i<colsInTable.entries(); i++)
{
if (bitmap.testBit(i))
{
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_DEBUG,
" %s", colsInTable[i]->getColName().data());
}
}
} // logColumnBitmap()
QRQueryDescriptorPtr QRDescGenerator::createQueryDescriptor(QueryAnalysis* qa,
RelExpr* expr)
{
QRTRACER("QRDescGenerator::createQueryDescriptor()");
QRQueryDescriptorPtr queryDesc;
queryDesc = new (mvqrHeap_) QRQueryDescriptor(ADD_MEMCHECK_ARGS(mvqrHeap_));
descriptorType_ = ET_QueryDescriptor;
relExpr_ = expr;
// Initialize the return value to null, and leave it null if no JBBs are
// present.
if (!processJBBs(queryDesc, qa))
{
deletePtr(queryDesc);
queryDesc = NULL;
return NULL;
}
queryDesc->setVersion(createVersionElement());
NAString mvAgeValue;
CmpCommon::getDefault(MV_AGE, mvAgeValue, 0);
queryDesc->getMisc()->setMVAge(mvAgeValue);
Int32 level = CmpCommon::getDefaultLong(MVQR_REWRITE_LEVEL);
queryDesc->getMisc()->setRewriteLevel((MvqrRewriteLevel)level);
return queryDesc;
} // createQueryDescriptor()
QRMVDescriptorPtr QRDescGenerator::createMvDescriptor(QueryAnalysis* qa,
RelExpr* expr)
{
QRTRACER("QRDescGenerator::createMvDescriptor()");
QRMVDescriptorPtr mvDesc = new(mvqrHeap_) QRMVDescriptor(ADD_MEMCHECK_ARGS(mvqrHeap_));
descriptorType_ = ET_MVDescriptor;
relExpr_ = expr;
if (processJBBs(mvDesc, qa))
{
mvDesc->setVersion(createVersionElement());
return mvDesc;
}
else
{
deletePtr(mvDesc);
return NULL;
}
} // createMvDescriptor()
QRVersionPtr QRDescGenerator::createVersionElement()
{
QRTRACER("QRDescGenerator::createVersionElement()");
NAString versionString(QR::CURRENT_VERSION);
QRVersionPtr descVersion = new (mvqrHeap_) QRVersion(ADD_MEMCHECK_ARGS(mvqrHeap_));
descVersion->setVersionString(versionString);
return descVersion;
}
XMLString* QRDescGenerator::createXmlText(QRElementPtr desc)
{
QRTRACER("QRDescGenerator::createXmlText()");
XMLString* xmlText = NULL;
// Formatted XML takes much more space, especially when very large
// expressions are used.
// if (isDumpMvMode())
// bFormatted_ = FALSE;
if (bFormatted_)
xmlText = (XMLFormattedString *) new(mvqrHeap_) XMLFormattedString(mvqrHeap_);
else
xmlText = new (mvqrHeap_) XMLString(mvqrHeap_);
desc->toXML(*xmlText);
return xmlText;
}
// Sort the join columns, distinguish between hub vs. extra-hub, and add to
// result.
void QRDescGenerator::addJoinPred(QRJBBPtr jbbElem,
QRElementPtr* qrElemArray,
Int32* idArray,
Int32 eqCount,
UInt32& hubJoinPredId)
{
QRTRACER("QRDescGenerator::addJoinPred()");
QRJoinPredPtr hubJoinPred = NULL;
QRJoinPredPtr extraHubJoinPred = NULL;
// Sort the list of columns and expressions.
if (bSortJoinPredicateCols_)
opt_qsort(qrElemArray, eqCount, sizeof(QRElementPtr),
QRElement::cmpQRElement);
// Assign each element in the sorted array to either a hub or extrahub join
// pred. The id of the join pred is derived from that of the first element
// added to it.
for (Int32 i=0; i<eqCount; i++)
{
if (qrElemArray[i]->isExtraHub())
{
if (!extraHubJoinPred)
{
extraHubJoinPred =
new (mvqrHeap_) QRJoinPred(ADD_MEMCHECK_ARGS(mvqrHeap_));
QRValueId id(idArray[i]);
EqualitySet* eqSet = vegsUsedHash_.getFirstValue(&id);
if (eqSet && eqSet->getJoinPredId())
extraHubJoinPred->setAndRegisterID(eqSet->getJoinPredId(),
colTblIdHash_);
else
extraHubJoinPred->setAndRegisterID(idArray[i], colTblIdHash_);
}
extraHubJoinPred->addElement(qrElemArray[i]);
colToJoinPredMap_.addMapEntry(idArray[i],
qrElemArray[i]->getReferencedElement()->getIDNum());
}
else
{
if (!hubJoinPred)
{
// hubJoinPredId will have been set to the value id of a veg pred
// if the equality set was derived from one.
if (hubJoinPredId == NULL_VALUE_ID)
hubJoinPredId = idArray[i]; // returned to caller
hubJoinPred =
new (mvqrHeap_) QRJoinPred(ADD_MEMCHECK_ARGS(mvqrHeap_));
hubJoinPred->setAndRegisterID(hubJoinPredId, colTblIdHash_);
}
hubJoinPred->addElement(qrElemArray[i]);
colToJoinPredMap_.addMapEntry(hubJoinPredId,
qrElemArray[i]->getReferencedElement()->getIDNum());
}
}
// Now lets look at the result and figure out how to insert it.
QRJoinPredListPtr hubJoinPredList = jbbElem->getHub()->getJoinPredList();
QRJoinPredListPtr extraHubJoinPredList = jbbElem->getExtraHub()->getJoinPredList();
if (hubJoinPred == NULL)
{
// Insert the extra-hub join pred to the JBB.
if (!extraHubJoinPred->isRedundant())
extraHubJoinPredList->addItem(extraHubJoinPred);
else
deletePtr(extraHubJoinPred);
}
else if (hubJoinPred->entries() < 2)
{
// Since we have only a single hub member and we know there are two or more
// members, there must be at least one extra-hub member.
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
extraHubJoinPred, QRDescriptorException,
"In addJoinPred(): single hub member and no extra-hub");
// When there is only one entry in the hub list, we don't add the list to
// hubJoinPredList; instead we add the single hub member to extraHubJoinPred.
const QRElementPtr hubItem = hubJoinPred->getEqualityListElement(0);
extraHubJoinPred->addElement(hubItem); // @ZX -- needs to be in order
hubJoinPred->removeItem(hubItem);
deletePtr(hubJoinPred);
// Insert the extra-hub join pred to the JBB.
if (!extraHubJoinPred->isRedundant())
extraHubJoinPredList->addItem(extraHubJoinPred);
else
deletePtr(extraHubJoinPred);
}
else
{
// The hub list has at least 2 columns - insert it.
if (!hubJoinPred->isRedundant())
hubJoinPredList->addItem(hubJoinPred);
else
deletePtr(hubJoinPred);
// extraHubJoinPred is only NULL if there are no extra-hub tables among the
// tables represented in the join pred.
if (extraHubJoinPred)
{
// The extra-hub list has at least 1 column in it.
// Insert it too, after adding a ref to the hub join pred.
QRJoinPredPtr hubJoinPredRef =
new(mvqrHeap_) QRJoinPred(ADD_MEMCHECK_ARGS(mvqrHeap_));
hubJoinPredRef->setRefFromInt(hubJoinPred->getIDNum());
extraHubJoinPred->addElement(hubJoinPredRef);
if (!extraHubJoinPred->isRedundant())
extraHubJoinPredList->addItem(extraHubJoinPred);
else
deletePtr(extraHubJoinPred);
}
}
} // addJoinPred()
// This function is part of the temporary restriction on a range predicate that
// disallows referring to the same column more than once in an expression that
// is the subject of a range predicate.
static void getLeafValueIds(ItemExpr* itemExpr, ValueIdList &lv)
{
QRTRACER("getLeafValueIds()");
Int32 nc = itemExpr->getArity();
// if this is a leaf node, add its value id
if (nc == 0)
{
lv.insertSet(itemExpr->getValueId());
}
else
{
// else add the leaf value ids of all the children
for (Lng32 i = 0; i < (Lng32)nc; i++)
{
getLeafValueIds(itemExpr->child(i), lv);
}
}
}
// This function is temporary, and will be used as long as the restriction
// disallowing >1 reference to the same column in a range predicate persists.
// When this restriction is lifted, remove this function and uncomment the one
// with the same name immediately below it. The present solution uses a list
// instead of a set so that use of the same column more than once can be detected.
// The static function getLeafValueIds() defined immediately above here can be
// removed at the same time this function is.
CANodeId QRDescGenerator::getExprNode(ItemExpr* itemExpr)
{
QRTRACER("QRDescGenerator::getExprNode()");
// Find and return the expression's containing node, or NULL_CA_ID if it is
// a multi-node expression.
//
// There is currently a restriction that keeps a single node expression from
// being used with a range predicate if it references more than one column,
// or even the same column more than once. So for now, NULL_CA_ID is returned
// in this case even if there is only a single node.
ValueIdList vids;
NABoolean isMultiNode = FALSE;
CANodeId exprNodeId = NULL_CA_ID;
CANodeId itemNodeId;
ItemExpr* leafItemExpr;
ValueId vid;
Int32 inputCount = 0;
getLeafValueIds(itemExpr, vids);
for (CollIndex i=0; i<vids.entries(); i++)
{
vid = vids[i];
leafItemExpr = vid.getItemExpr();
switch (leafItemExpr->getOperatorType())
{
case ITM_VEG_REFERENCE:
// If a veg contains a constant, it will be used in the range expr,
// so only count it if it contains no constants.
if (static_cast<VEGReference*>(leafItemExpr)->getVEG()->getAConstant(TRUE)
== NULL_VALUE_ID)
inputCount++;
break;
//case ITM_CONSTANT:
case ITM_REFERENCE:
case ITM_BASECOLUMN:
case ITM_INDEXCOLUMN:
//case ITM_HOSTVAR:
//case ITM_DYN_PARAM:
case ITM_SEL_INDEX:
case ITM_VALUEIDREF:
case ITM_VALUEIDUNION:
case ITM_VEG:
case ITM_VEG_PREDICATE:
//case ITM_DEFAULT_SPECIFICATION:
//case ITM_SAMPLE_VALUE:
//case ITM_CACHE_PARAM:
inputCount++;
break;
default:
break;
}
itemNodeId = getNodeId(vid);
if (itemNodeId != NULL_CA_ID)
{
if (exprNodeId == NULL_CA_ID)
exprNodeId = itemNodeId;
else if (itemNodeId != exprNodeId)
isMultiNode = TRUE;
}
}
if (exprNodeId)
return (isMultiNode || inputCount != 1)
? NULL_CA_ID
: exprNodeId;
else if (itemExpr->containsAnAggregate())
{
// exprNodeId will be NULL_CA_ID if only count(*) is used.
if (!currentJBB_)
// Called from Normalizer. Analysis not done yet, have to settle for
// residual pred.
return NULL_CA_ID;
else
{
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
currentJBB_->getGBAnalysis(), QRDescriptorException,
"No GBAnalysis for JBB %d, although an aggfn was used",
currentJBB_->getJBBId());
return currentJBB_->getGBAnalysis()->getGroupingNodeId();
}
}
else
{
NAString predText;
itemExpr->unparse(predText);
QRLogger::log(CAT_SQL_COMP_QR_DESC_GEN, LL_WARN,
"Predicate encountered with no columns and no count(*), will treat "
"as residual predicate -- %s", predText.data());
return NULL_CA_ID;
}
}
// DON'T REMOVE THIS CODE -- it will be restored when the temporary restriction
// described at the top of the previous function is
// eliminated.
//CANodeId QRDescGenerator::getExprNode(ItemExpr* itemExpr)
//{
// QRTRACER("getExprNode");
//
// // Find and return the expression's containing node, or NULL_CA_ID if it is
// // a multi-node expression.
// //
// // There is currently a restriction that keeps a single node expression from
// // being used with a range predicate if it references more than one column,
// // So for now, NULL_CA_ID is returned in this case even if there is only a
// // single node.
// ValueIdSet vids;
// NABoolean isMultiNode = FALSE;
// CANodeId exprNodeId = NULL_CA_ID;
// CANodeId itemNodeId;
// ItemExpr* leafItemExpr;
// int inputCount = 0;
//
// itemExpr->getLeafValueIds(vids);
//
// for (ValueId vid=vids.init();
// !isMultiNode && inputCount < 2 && vids.next(vid);
// vids.advance(vid))
// {
// leafItemExpr = vid.getItemExpr();
// switch (leafItemExpr->getOperatorType())
// {
// //case ITM_CONSTANT:
// case ITM_REFERENCE:
// case ITM_BASECOLUMN:
// case ITM_INDEXCOLUMN:
// //case ITM_HOSTVAR:
// //case ITM_DYN_PARAM:
// case ITM_SEL_INDEX:
// case ITM_VALUEIDREF:
// case ITM_VALUEIDUNION:
// case ITM_VEG:
// case ITM_VEG_PREDICATE:
// case ITM_VEG_REFERENCE:
// //case ITM_DEFAULT_SPECIFICATION:
// //case ITM_SAMPLE_VALUE:
// //case ITM_CACHE_PARAM:
// inputCount++;
// break;
// default:
// break;
// }
// itemNodeId = getNodeId(vid);
// if (itemNodeId != NULL_CA_ID)
// {
// if (exprNodeId == NULL_CA_ID)
// exprNodeId = itemNodeId;
// else if (itemNodeId != exprNodeId)
// isMultiNode = TRUE;
// }
// }
//
// return (isMultiNode || inputCount != 1)
// ? NULL_CA_ID
// : exprNodeId;
//}
void QRDescGenerator::processEqualitySet(QRJBBPtr jbbElem,
EqualitySet& eqSet)
{
QRTRACER("QRDescGenerator::processEqualitySet()");
CollIndex i;
ItemExpr* itemExpr;
OperatorTypeEnum op;
CANodeId itemNodeId;
// Keep track of the CA nodes represented by the join pred members. Only one
// col/expr per node is placed in the join pred.
CANodeIdSet joinPredNodes;
// Retain a ptr to the first item expression added to the list of join pred
// members. If it turns out to be the only one, there is no join and we add
// it to the list of residual pred operands.
ItemExpr* firstJoinPredItem = NULL;
// List to store members of the equality set that are not part of a JoinPred.
// This includes expressions involving more than a single node, cols/exprs
// other than the first from each represented node, dynamic params, etc.
// When a join pred is derived from a subset of the members of the equality
// set, each remaining equality set member will appear in the query descriptor
// either as
// 1) a residual predicate equated to a reference to the join pred created
// from the equality set. This is the case when
// a) there is no constant in the equality set
// b) there is a constant, but the given eq set member is a multinode
// expression
// 2) a range predicate equated to the constant member of the eq set, except
// for a multinode expression
// If no join pred comes out of the equality set and there is no constant, we
// will form a chain of equality residual predicates involving all members of
// the eq set.
NAList<ItemExpr*> nonJoinPredOperands(mvqrHeap_);
// This keeps track of which members can NOT be used in a range predicate
// (multi-node expressions).
NABitVector rangeIneligible(mvqrHeap_);
// Stores the constant value, if any (max of 1 is possible), from the equality
// set. Used to create a range pred on the join columns.
ConstValue* constItem = NULL;
CollIndex joinPredElemCount = 0;
CollIndex eqCount = eqSet.entries();
NAString exprText;
// Array to put elements in so they can be sorted, and array of corresponding
// ids that can be used for the created join preds (for hub and extrahub).
QRElementPtr* qrElemArray = new(mvqrHeap_) QRElementPtr[eqCount];
Int32* idArray = new(mvqrHeap_) Int32[eqCount];
for (i=0; i<eqCount; i++)
{
itemExpr = eqSet[i];
op = itemExpr->getOperatorType();
if (op == ITM_BASECOLUMN)
{
const TableAnalysis* tableAnalysis =
((BaseColumn*)itemExpr)->getTableDesc()->getTableAnalysis();
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
tableAnalysis, QRDescriptorException,
"TableDesc has null table analysis");
itemNodeId = tableAnalysis->getNodeAnalysis()->getId();
//itemNodeId = ((BaseColumn*)itemExpr)->getTableDesc()->getTableAnalysis()
// ->getNodeAnalysis()->getId();
if (!joinPredNodes.containsThisId(itemNodeId))
{
joinPredNodes.insert(itemNodeId);
if (joinPredElemCount == 0)
firstJoinPredItem = itemExpr;
else
{
// Avoid call to genQRColumn until we're sure there is more than
// one join pred element. Otherwise we'll have to retract it,
// and the created col elem will not be used, but will be
// referenced by subsequent occurrences of the column.
if (joinPredElemCount == 1)
{
if (isInstNull(firstJoinPredItem))
qrElemArray[0] = skipInstNull(firstJoinPredItem);
else if (firstJoinPredItem->getOperatorType() == ITM_BASECOLUMN)
qrElemArray[0] =
genQRColumn(firstJoinPredItem->getValueId());
else
qrElemArray[0] = genQRExpr(firstJoinPredItem, FALSE);
idArray[0] = firstJoinPredItem->getValueId();
}
qrElemArray[joinPredElemCount] =
genQRColumn(itemExpr->getValueId());
idArray[joinPredElemCount] = itemExpr->getValueId();
}
joinPredElemCount++;
}
else
nonJoinPredOperands.insert(itemExpr);
}
else if (op == ITM_CONSTANT)
constItem = static_cast<ConstValue*>(itemExpr);
else if (op == ITM_CACHE_PARAM)
{
// Substitute an itemexpr representing the constant value underlying
// the parameter.
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
!constItem, QRDescriptorException,
"Equality set contains more than one constant");
constItem = (static_cast<ConstantParameter*>(itemExpr))->getConstVal();
}
else if (itemExpr->doesExprEvaluateToConstant(FALSE, FALSE))
nonJoinPredOperands.insert(itemExpr);
else if (op != ITM_INDEXCOLUMN)
{
// If more than one node is involved in an expression, it is treated
// as a residual pred of the form ref(J1)=expr.
// First, see if the expression is multi-node, and find the single
// node if it is not.
CANodeId exprNodeId = getExprNode(itemExpr);
// The expression will NOT be part of a join pred if any of the
// following hold true:
// 1) the expression references more than one node
// 2) the single referenced node is already in the join pred list
// 3) the expression contains an aggregate function
// Upon failing the join pred test, the expression can be part of a
// range pred if it only references a single node. Otherwise, it can
// only be part of a residual pred.
if (exprNodeId == NULL_CA_ID
|| joinPredNodes.containsThisId(exprNodeId)
|| itemExpr->containsAnAggregate())
{
if (exprNodeId == NULL_CA_ID)
rangeIneligible += nonJoinPredOperands.entries();
nonJoinPredOperands.insert(itemExpr);
}
else
{
joinPredNodes.insert(exprNodeId);
if (joinPredElemCount == 0)
firstJoinPredItem = itemExpr;
else
{
// Avoid call to genQRExpr until we're sure there is more than
// one join pred element. Otherwise we'll have to retract it,
// and the created col elems will not be used, but will be
// referenced by subsequent occurrences of those columns.
if (joinPredElemCount == 1)
{
if (firstJoinPredItem->getOperatorType() == ITM_BASECOLUMN)
qrElemArray[0] =
genQRColumn(firstJoinPredItem->getValueId());
else
qrElemArray[0] = genQRExpr(firstJoinPredItem, FALSE);
idArray[0] = firstJoinPredItem->getValueId();
}
qrElemArray[joinPredElemCount] = genQRExpr(itemExpr, FALSE);
idArray[joinPredElemCount] = itemExpr->getValueId();
}
joinPredElemCount++;
}
}
} // for each member of equality set
// Create a JoinPred object if there is more than 1 table represented. If only
// one, add it to the residual preds operand list.
UInt32 hubJoinPredId = 0;
if (joinPredElemCount > 1)
{
hubJoinPredId = eqSet.getJoinPredId();
addJoinPred(jbbElem, qrElemArray, idArray, joinPredElemCount,
hubJoinPredId); // will be set only if it has no value
}
else if (joinPredElemCount == 1)
nonJoinPredOperands.insert(firstJoinPredItem);
// If a hub join pred was created from the equality set, save its id. Range
// and residual preds that use a column of the equality set will reference the
// equality set using this id.
eqSet.setJoinPredId(hubJoinPredId);
NABoolean useConstItem = FALSE;
if (constItem)
{
if (typeSupported(constItem->getType()))
useConstItem = TRUE;
else
nonJoinPredOperands.insert(constItem);
}
// If a constant was part of the equality set, create a range predicate for
// each eligible (i.e., single-node) non-joinpred member of the set, and a
// residual pred for the ineligible ones. If there is a join pred, create a
// range predicate for it.
if (useConstItem)
{
for (CollIndex i=0; i<nonJoinPredOperands.entries(); i++)
{
if (rangeIneligible.testBit(i))
addEqualityResidPred(jbbElem, nonJoinPredOperands[i], constItem);
else
addEqualityRangePred(nonJoinPredOperands[i], eqSet.getType(),
jbbElem, constItem);
}
if (hubJoinPredId)
addEqualityRangePred(hubJoinPredId, eqSet.getType(), jbbElem, constItem);
}
else if (hubJoinPredId)
{
// Create residual preds that link each nonjoin operand to the hub join pred.
for (CollIndex i=0; i<nonJoinPredOperands.entries(); i++)
addEqualityResidPred(jbbElem, nonJoinPredOperands[i], NULL,
hubJoinPredId);
}
else
{
// No hub join pred to reference. Just produce a series of equality
// residual predicates relating the equality set members.
for (CollIndex i=0; i<nonJoinPredOperands.entries()-1; i++)
addEqualityResidPred(jbbElem, nonJoinPredOperands[i],
nonJoinPredOperands[i+1]);
}
// Don't delete the element array, because the elements are used in JoinPreds
// as well as in the array. However, we can delete the parallel id array.
NADELETEBASIC(idArray, mvqrHeap_);
} // processEqualitySet()
VEGPredicate* QRDescGenerator::getVegPredicate(UInt32 hubJoinPredId)
{
QRTRACER("QRDescGenerator::getVegPredicate()");
ValueId vegRefVid = (ValueId)hubJoinPredId;
ItemExpr* ie = vegRefVid.getItemExpr();
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
ie->getOperatorType() == ITM_VEG_REFERENCE,
QRDescriptorException,
"Hub join pred id is not ValueId of vegref -- %d",
hubJoinPredId);
return (static_cast<VEGReference*>(ie))->getVEG()->getVEGPredicate();
}
void QRDescGenerator::addEqualityRangePred(ItemExpr* rangeItemExpr,
const NAType* type,
QRJBBPtr jbbElem,
ConstValue* constItem)
{
QRTRACER("QRDescGenerator::addEqualityRangePred()");
OptRangeSpec* range = new (mvqrHeap_) OptRangeSpec(this, mvqrHeap_);
if (rangeItemExpr->getOperatorType() == ITM_BASECOLUMN)
range->setRangeColValueId(rangeItemExpr->getValueId());
else
range->setRangeExpr(rangeItemExpr);
range->setType(&rangeItemExpr->getValueId().getType());
range->addSubrange(constItem, constItem, TRUE, TRUE);
ItemExpr* eqItemExpr = new(mvqrHeap_) BiRelat(ITM_EQUAL, rangeItemExpr, constItem);
eqItemExpr->synthTypeAndValueId(TRUE);
range->setID(eqItemExpr->getValueId());
range->log();
storeRangeInfo(range, jbbElem);
}
void QRDescGenerator::addEqualityRangePred(UInt32 hubJoinPredId,
const NAType* type,
QRJBBPtr jbbElem,
ConstValue* constItem)
{
QRTRACER("QRDescGenerator::addEqualityRangePred()");
OptRangeSpec* range = new (mvqrHeap_) OptRangeSpec(this, mvqrHeap_);
range->setRangeJoinPredId(hubJoinPredId);
range->setID(getVegPredicate(hubJoinPredId)->getValueId());
range->setType(type);
range->addSubrange(constItem, constItem, TRUE, TRUE);
range->log();
storeRangeInfo(range, jbbElem);
}
void QRDescGenerator::addEqualityResidPred(QRJBBPtr jbbElem,
ItemExpr* op1,
ItemExpr* op2,
ValueId hubJoinPredId)
{
QRTRACER("QRDescGenerator::addEqualityResidPred()");
ItemExpr* residExpr;
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
op1, QRDescriptorException,
"op1 of equality resid pred is null");
if (op2)
residExpr = new(mvqrHeap_) BiRelat(ITM_EQUAL, op1, op2);
else
{
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
hubJoinPredId, QRDescriptorException,
"Neither op2 nor hubJoinPredId is given");
residExpr = new(mvqrHeap_) BiRelat(ITM_EQUAL,
op1, hubJoinPredId.getItemExpr());
}
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
relExpr_,
QRDescriptorException,
"No RelExpr* stored for QRDescGenerator");
residExpr->bindNode(relExpr_->getRETDesc()->getBindWA());
processResidualPredicate(residExpr->getValueId(), jbbElem);
}
void QRDescGenerator::processReferencingPreds(CANodeIdSet* nodeSet,
RelExpr* gbNode,
QRJBBPtr jbbElem)
{
QRTRACER("QRDescGenerator::processReferencingPreds()");
ValueIdSet preds;
NodeAnalysis* nodeAnalysis;
TableAnalysis* tableAnalysis;
// Gather all the predicates referencing a member of the nodeset.
for (CANodeId node = nodeSet->init();
nodeSet->next(node);
nodeSet->advance(node))
{
nodeAnalysis = node.getNodeAnalysis();
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
nodeAnalysis, QRDescriptorException,
"No NodeAnalysis found for CA node id %ud", node.toUInt32());
tableAnalysis = nodeAnalysis->getTableAnalysis();
if (tableAnalysis)
{
preds.addSet(tableAnalysis->getReferencingPreds());
// Constant preds, like return_false (for a combination of preds that
// can't be satisfied), or a pred on current_date, etc., appear only
// in localPreds, so add those.
preds.addSet(tableAnalysis->getLocalPreds());
}
}
// Add any predicates attached to the Group By node. Count(*) will not have
// been pushed down to one of the scan nodes.
if (gbNode)
preds.addSet(gbNode->getSelectionPredicates());
// Identify equality sets implied by vegpreds and equality conditions, and
// translate these to joinpred/range/residual predicates in the descriptor.
NAList<EqualitySet*> equalitySets(mvqrHeap_);
formEqualitySets(preds, equalitySets);
for (CollIndex i=0; i<equalitySets.entries(); i++)
processEqualitySet(jbbElem, *equalitySets[i]);
// Iterate over the remaining preds and generate range or residual predicates
// as needed. The preds involved in equality sets have been removed by
// formEqualitySets().
for (ValueId predVid=preds.init(); preds.next(predVid); preds.advance(predVid))
{
OptRangeSpec* range = OptRangeSpec::createRangeSpec(this,
predVid.getItemExpr(),
mvqrHeap_);
if (range)
storeRangeInfo(range, jbbElem);
else // residual predicate
processResidualPredicate(predVid, jbbElem);
}
// Delete the equality sets and clean up the hash tables that reference them.
discardEqualitySets(equalitySets);
// Move eqset ptrs to QRDescGenerator member var before exiting scope. They
// may be used later to rewrite a vegpred.
for (CollIndex i=0; i<equalitySets.entries(); i++)
allEqualitySets_.insert(equalitySets[i]);
}
void QRDescGenerator::discardEqualitySets(NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::discardEqualitySets()");
// Iterate over all keys in the vid->eqset hash table and delete them.
NAHashDictionaryIterator<QRValueId, EqualitySet> vegsHashIter(vegsUsedHash_);
QRValueId* vidKey;
EqualitySet* value;
for (CollIndex i=0; i<vegsHashIter.entries(); i++)
{
vegsHashIter.getNext(vidKey, value);
delete vidKey;
}
// Now delete the keys in the exprtext->eqset hash table.
NAHashDictionaryIterator<const NAString, EqualitySet>
exprsHashIter(exprsUsedHash_);
const NAString* exprKey;
for (CollIndex i=0; i<exprsHashIter.entries(); i++)
{
exprsHashIter.getNext(exprKey, value);
delete exprKey;
}
// Empty the hash tables so they will be clean for the next JBB processed.
// Can't let them delete their contents, because the key to value relationship
// is many to one, so some of the values (EqualitySets) would be deleted more
// than once. You can't request that only keys be deleted, so we have to
// delete both the keys (above) and values (below, by iterating over the list
// of equality sets) ourselves.
vegsUsedHash_.clear(FALSE);
exprsUsedHash_.clear(FALSE);
// Delete all the equality sets, which were dynamically allocated so a
// persistent pointer could be used as the value referenced in the hash
// tables.
//@ZXeqset
//for (CollIndex i=0; i<equalitySets.entries(); i++)
// delete equalitySets[i];
}
void QRDescGenerator::combineEqSets(EqualitySet* source,
EqualitySet* destination,
NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::combineEqSets()");
// Combine the two lists.
destination->insert(*source);
delete source;
equalitySets.remove(source);
// Change ptrs in hash tables from source to destination list.
NAHashDictionaryIterator<QRValueId, EqualitySet>
vegsHashIter(vegsUsedHash_);
NAHashDictionaryIterator<const NAString, EqualitySet>
exprsHashIter(exprsUsedHash_);
// Iterate over all key/value pairs in the hash table (iterator doesn't allow
// iterating over a selected value alone), selecting those keys that have the
// source equality set as a value. I'm not sure how manipulating the hash
// table in mid-iteration would affect the iteration, so we keep the keys in
// a list, and delete/reinsert them with the new equality set after completing
// the iteration.
QRValueId* vidKey;
EqualitySet* ptrListValue;
NAList<QRValueId*> vidsToRemove(mvqrHeap_);
CollIndex i;
for (i=0; i<vegsHashIter.entries(); i++)
{
vegsHashIter.getNext(vidKey, ptrListValue); // get key and value
if (ptrListValue == source)
vidsToRemove.insert(vidKey);
}
// Remove and reinsert keys referencing the old (source) equality set.
for (i=0; i<vidsToRemove.entries(); i++)
{
vegsUsedHash_.remove(vidsToRemove[i]);
vegsUsedHash_.insert(vidsToRemove[i], destination);
}
// Now iterate over the hash table that uses expression text as a key, the
// same as with the ValueId keys above.
const NAString* exprKey;
NAList<const NAString*> exprsToRemove(mvqrHeap_);
for (i=0; i<exprsHashIter.entries(); i++)
{
exprsHashIter.getNext(exprKey, ptrListValue);
if (ptrListValue == source)
exprsToRemove.insert(exprKey);
}
// Remove and reinsert expression text keys referencing the old (source)
// equality set.
for (i=0; i<exprsToRemove.entries(); i++)
{
exprsUsedHash_.remove(exprKey);
exprsUsedHash_.insert(exprKey, destination);
}
} // combineEqSets()
// If no members were found in any other list, create a new equality list,
// populate it with the members of the vegpred, and add it to the list of lists.
// Also add the ValueId or expression text of each member to the appropriate
// hash table, with the address of the containing list as the value. The ValueId
// of the veg itself is also added as a key, so an equality operator that uses
// a vegref will find it. We have to create heap-allocated copies of the hash
// keys, because they need to persist beyond this function.
//
// Takes ValueIdSet and veg vid instead of VEG because we may remove one or more
// members from a local copy of a veg member list before making this call.
void QRDescGenerator::putVegMembersInEqualitySet(
ItemExpr* vegPred, // NULL if called for equality pred
const ValueId& vegVid,
const ValueIdSet& vegVals,
EqualitySet*& eqSet, // will be set if NULL
NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::putVegMembersInEqualitySet()");
// Make sure they weren't all removed because they were already members of
// other separate-but-equal lists.
if (vegVals.isEmpty())
return;
// Create list if one was not passed in, which is the case when an existing
// compatible equality set is not found.
if (!eqSet)
{
eqSet = new(mvqrHeap_) EqualitySet(mvqrHeap_);
equalitySets.insert(eqSet);
}
// If this fn was called for a vegpred instead of a simple equality pred, use
// the vegpred's value id as the joinpred id. If no vegpreds contribute to this
// equality set, the value id of one of the members of the equality set will
// be used.
if (vegPred)
eqSet->setJoinPredId(((VEGPredicate*)vegPred)->getVEG()->getVEGReference()
->getValueId());
// Put the value id of this veg in the hash table. The ValueIds of the
// individual members will be added as well. NAHashDictionary is pointer-based,
// so we have to allocate a copy of the ValueId on the heap, so it will
// persist beyond this function. Note that ValueId is not an NABasicObject,
// so we use the system heap.
//
QRValueId* vegVidPtr = new (mvqrHeap_) QRValueId(vegVid);
vegsUsedHash_.insert(vegVidPtr, eqSet);
// Put the veg members in the list and in the hash tables.
ItemExpr* itemExpr;
OperatorTypeEnum op;
QRValueId* vidPtr;
NAString* exprTextPtr;
for (ValueId vid=vegVals.init(); vegVals.next(vid); vegVals.advance(vid))
{
itemExpr = vid.getItemExpr();
op = itemExpr->getOperatorType();
if (op == ITM_BASECOLUMN)
{
eqSet->insert(itemExpr);
vidPtr = new (mvqrHeap_) QRValueId(vid);
vegsUsedHash_.insert(vidPtr, eqSet);
}
else if (op != ITM_INDEXCOLUMN)
{
// Constants are lumped in with general expressions, because when
// query caching is enabled they are converted to params with
// distinct ValueIds for each occurrence.
eqSet->insert(itemExpr);
exprTextPtr = new(mvqrHeap_) NAString(mvqrHeap_);
itemExpr->unparse(*exprTextPtr, OPTIMIZER_PHASE, MVINFO_FORMAT);
exprsUsedHash_.insert(exprTextPtr, eqSet);
}
}
} // putVegMembersInEqualitySet()
void QRDescGenerator::addVegPredToEqualitySets(
VEGPredicate* vegPred,
NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::addVegPredToEqualitySets()");
ValueIdSet vegVals;
vegPred->getVEG()->getAndExpandAllValues(vegVals);
EqualitySet* eqSet;
EqualitySet* targetEqSet = NULL;
ItemExpr* itemExpr;
NAString exprText;
OperatorTypeEnum op;
QRValueId vidNum;
for (ValueId vid=vegVals.init(); vegVals.next(vid); vegVals.advance(vid))
{
itemExpr = vid.getItemExpr();
op = itemExpr->getOperatorType();
if (op == ITM_BASECOLUMN)
{
// Probe with base col vid. These are in hash table as well as veg
// vids, to handle all cases.
vidNum = vid;
eqSet = vegsUsedHash_.getFirstValue(&vidNum);
}
else if (op != ITM_INDEXCOLUMN)
{
// Constants are lumped in with general expressions, because when
// query caching is enabled they are converted to params with
// distinct ValueIds for each occurrence.
// @ZX -- maybe add case for caching turned off
exprText = "";
itemExpr->unparse(exprText, OPTIMIZER_PHASE, MVINFO_FORMAT);
eqSet = exprsUsedHash_.getFirstValue(&exprText);
}
else
eqSet = NULL;
if (eqSet)
{
// Remove the item that was found in the hash table, because it is
// already in an existing list that the members of this vegpred will
// be merged with.
vegVals.subtractElement(vid);
// If this is not the first list we've found, merge it into the target
// list (the one the members of this vegpred will be added to).
if (targetEqSet)
{
if (eqSet != targetEqSet)
combineEqSets(eqSet, targetEqSet, equalitySets);
}
else
targetEqSet = eqSet;
}
}
// Add the members to targetEqSet, or if it is null, create a new list.
putVegMembersInEqualitySet(vegPred, vegPred->getVEG()->getValueId(), vegVals,
targetEqSet, equalitySets);
} // addVegPredToEqualitySets()
void QRDescGenerator::addEqPredToEqualitySets(
ItemExpr* pred,
NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::addEqPredToEqualitySets()");
EqualitySet* eqSet;
EqualitySet* targetEqSet = NULL;
ItemExpr* eqOperand;
OperatorTypeEnum op;
NAString exprText;
QRValueId vid;
NABoolean moveToTarget[] = {TRUE, TRUE};
assertLogAndThrow(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
pred->getOperatorType() == ITM_EQUAL,
QRDescriptorException,
"addEqPredToEqualitySets() called for non-equality predicate");
for (Int32 i=0; i<2; i++)
{
eqOperand = pred->child(i);
op = eqOperand->getOperatorType();
if (op == ITM_VEG_REFERENCE)
{
vid = ((VEGReference*)eqOperand)->getVEG()->getValueId();
eqSet = vegsUsedHash_.getFirstValue(&vid);
}
else
{
exprText = "";
eqOperand->unparse(exprText, OPTIMIZER_PHASE, MVINFO_FORMAT);
eqSet = exprsUsedHash_.getFirstValue(&exprText);
}
if (eqSet)
{
moveToTarget[i] = FALSE; // already there
// If this is not the first list we've found (the one we will add the
// eq operands to), merge it into the target list.
if (targetEqSet)
{
if (eqSet != targetEqSet)
combineEqSets(eqSet, targetEqSet, equalitySets);
}
else
targetEqSet = eqSet;
}
}
// Put the equality operands in a list (either new or created) and hash
// tables, unless they were already members of a separate-but-equal list.
for (Int32 i=0; i<2; i++)
{
if (!moveToTarget[i])
continue; // already in target list
eqOperand = pred->child(i);
op = eqOperand->getOperatorType();
if (op == ITM_VEG_REFERENCE)
{
VEG* veg = ((VEGReference*)eqOperand)->getVEG();
// This will create targetEqSet if it is NULL.
putVegMembersInEqualitySet(NULL,
veg->getValueId(), veg->getAllValues(),
targetEqSet, equalitySets);
}
else
{
if (!targetEqSet)
{
targetEqSet = new(mvqrHeap_) EqualitySet(mvqrHeap_);
equalitySets.insert(targetEqSet);
}
targetEqSet->insert(eqOperand);
NAString* exprTextPtr = new(mvqrHeap_) NAString(mvqrHeap_);
eqOperand->unparse(*exprTextPtr, OPTIMIZER_PHASE, MVINFO_FORMAT);
exprsUsedHash_.insert(exprTextPtr, targetEqSet);
}
}
} // addEqPredToEqualitySets()
void QRDescGenerator::formEqualitySets(ValueIdSet& preds, // processed preds removed
NAList<EqualitySet*>& equalitySets)
{
QRTRACER("QRDescGenerator::formEqualitySets()");
ValueId predVid;
ItemExpr* predExpr;
OperatorTypeEnum op;
// To form equality groups, we look at vegpreds and equality operators (and
// remove them from the passed set of pred ValueIds after processing them).
// The operands of each will either constitute a new equality set, or be
// merged with an existing one. In some cases, 2 or more operands may
// correspond to members of different equality sets, in which case they
// will all be merged.
for (predVid=preds.init(); preds.next(predVid); preds.advance(predVid))
{
predExpr = predVid.getItemExpr();
op = predExpr->getOperatorType();
// If it's a range spec operator, sub in the actual predicate from the
// right subtree.
if (op == ITM_RANGE_SPEC_FUNC)
{
predExpr = predExpr->child(1);
op = predExpr->getOperatorType();
}
if (op == ITM_VEG_PREDICATE)
{
addVegPredToEqualitySets((VEGPredicate*)predExpr, equalitySets);
preds.subtractElement(predVid);
}
else if (op == ITM_EQUAL)
{
addEqPredToEqualitySets(predExpr, equalitySets);
preds.subtractElement(predVid);
}
}
} // formEqualitySets()
void QRDescGenerator::setPredBitmap(QRValueId colVid, ElementType elemType)
{
QRTRACER("QRDescGenerator::setPredBitmap()");
QRElementPtr elem = getElementForValueID('C', colVid);
if (!elem)
{
ValueId vid = colVid;
OperatorTypeEnum opType = vid.getItemExpr()->getOperatorType();
if (opType == ITM_BASECOLUMN)
// Pass FALSE to prevent adding the col to the list of used cols. This
// col element is created to find its table's id and position in the
// table; it is not added to the descriptor.
elem = genQRColumn(vid, 0, FALSE);
else if (opType == ITM_VEG_REFERENCE)
{
// For a range pred on a member of a vegref, we need to set the bitmap
// entry for each column in the veg. Make recursive calls to this fn
// for each column in the veg, then exit.
ValueIdSet vegMembers =
static_cast<VEGReference*>(vid.getItemExpr())->getVEG()->getAllValues();
for (ValueId id=vegMembers.init();
vegMembers.next(id);
vegMembers.advance(id))
{
if (id.getItemExpr()->getOperatorType() == ITM_BASECOLUMN)
setPredBitmap(id, elemType);
}
return;
}
else
{
Int32 vidInt = vid;
assertLogAndThrow2(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"ValueId %d is not a base col or veg ref -- op type = %d",
vidInt, opType);
}
}
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
elem, QRDescriptorException,
"Column id not found in hash table -- %d", (UInt32)colVid);
QRColumnPtr col = elem->getReferencedElement()->downCastToQRColumn();
const NAString& tblID = col->getTableID();
elem = colTblIdHash_.getFirstValue(&tblID);
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
elem, QRDescriptorException,
"Table id not found in hash table -- %s", tblID.data());
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
elem->getElementType() == ET_Table, QRDescriptorException,
"Expected a Table element type, not %d",
elem->getElementType());
// Set the column's bit in the appropriate bitmap, using its ordinal position
// in the table.
if (elemType == ET_RangePred)
elem->downCastToQRTable()->setRangeBit(col->getColIndex());
else if (elemType == ET_ResidualPred)
elem->downCastToQRTable()->setResidualBit(col->getColIndex());
else
assertLogAndThrow1(CAT_SQL_COMP_QR_DESC_GEN, LL_MVQR_FAIL,
FALSE, QRDescriptorException,
"Wrong element type sent to setPredBitmap() -- %d",
elemType);
}
void QRDescGenerator::storeRangeInfo(OptRangeSpec* range, QRJBBPtr jbbElem)
{
QRTRACER("QRDescGenerator::storeRangeInfo()");
RangeInfo* rangeInfo = NULL;
NAString* exprText = NULL;
QRValueId* key = new (mvqrHeap_) QRValueId(range->getRangeJoinPredId());
if (*key != NULL_VALUE_ID)
rangeInfo = rangeColHash_.getFirstValue(key);
else if (range->getRangeColValueId() != NULL_VALUE_ID)
{
*key = range->getRangeColValueId();
rangeInfo = rangeColHash_.getFirstValue(key);
}
else
{
exprText = new (mvqrHeap_) NAString(range->getRangeExprText(), mvqrHeap_);
rangeInfo = rangeExprHash_.getFirstValue(exprText);
}
// If a range was already created for another predicate on this column or
// expression, intersect it with this range.
if (rangeInfo)
{
rangeInfo->getRangeSpec()->intersectRange(range);
// Don't need these since new hash entry was not created.
delete key;
delete exprText;
delete range;
range = NULL; // makes it easier to debug use after deletion
}
else if (*key != NULL_VALUE_ID)
rangeColHash_.insert(key, new(mvqrHeap_) RangeInfo(range, jbbElem));
else
{
rangeExprHash_.insert(exprText, new(mvqrHeap_) RangeInfo(range, jbbElem));
delete key;
}
}
NABoolean QRDescGenerator::isRangeSupported(QRRangePredPtr rangePred)
{
Int32 rangeExprSize = rangePred->getSize();
if (rangeExprSize > maxExprSize_)
return FALSE;
return TRUE;
}
void QRDescGenerator::addRangePredicates()
{
QRTRACER("QRDescGenerator::addRangePredicates()");
QRValueId* vid;
const NAString* exprText;
RangeInfo* rangeInfo;
OptRangeSpec* rangeSpec;
QRRangePredPtr rangePred;
NAHashDictionaryIterator<QRValueId, RangeInfo> vidIter(rangeColHash_);
for (CollIndex i=0; i<vidIter.entries(); i++)
{
vidIter.getNext(vid, rangeInfo);
rangeSpec = rangeInfo->getRangeSpec();
rangeSpec->addColumnsUsed(this);
rangeSpec->addConstraints(this);
if (rangeSpec->isFalse())
{
// Unsatisfiable condition; generate FALSE as a residual predicate.
BindWA bindWA(ActiveSchemaDB(), CmpCommon::context());
ItemExpr* expr = new(mvqrHeap_) BoolVal(ITM_RETURN_FALSE);
expr->bindNode(&bindWA);
processResidualPredicate(expr->getValueId(), rangeInfo->getJbbElem());
}
else
{
rangePred = rangeSpec->createRangeElem();
// The OptRangeSpec may involve a tautology (e.g., a>20 OR a<25 OR
// a is null), which is equivalent to no range restriction at all.
if (rangePred)
{
if ((isQueryMode() || isDumpMvMode()) &&
!isRangeSupported(rangePred))
{
// This range predicate is too big, or unsuitable for a descriptor
// for some other reason. Replace it with an empty range pred
// marked as NotProvided.
QRRangePredPtr notProvidedRangePred = new(mvqrHeap_)
QRRangePred(ADD_MEMCHECK_ARGS(mvqrHeap_));
QRElementPtr rangeItem = rangePred->getRangeItem();
rangePred->setRangeItem(NULL);
notProvidedRangePred->setRangeItem(rangeItem);
notProvidedRangePred->setID(rangePred->getIDNum());
notProvidedRangePred->setResult(QRElement::NotProvided);
// Now delete the full range pred and use the empty one instead.
deletePtr(rangePred);
rangePred = notProvidedRangePred;
}
// Add the RangePred to the appropriate RangePredList, and set the
// column's bit in the range predicate bitmap.
rangeInfo->getOwningList()->addItemOrdered(rangePred);
setPredBitmap(*vid, ET_RangePred);
}
}
}
rangeColHash_.clear(TRUE);
NAHashDictionaryIterator<const NAString, RangeInfo> exprIter(rangeExprHash_);
for (CollIndex i=0; i<exprIter.entries(); i++)
{
exprIter.getNext(exprText, rangeInfo);
rangePred = rangeInfo->getRangeSpec()->createRangeElem();
// The OptRangeSpec may involve a tautology (e.g., a>20 or a<25), which is
// equivalent to no range restriction at all.
if (rangePred)
rangeInfo->getOwningList()->addItemOrdered(rangePred);
}
rangeExprHash_.clear(TRUE);
}
QRElementPtr QRDescGenerator::getElementForValueID(char firstChar, UInt32 id) const
{
QRTRACER("QRDescGenerator::getElementForValueID()");
char buff[12];
QRValueId vid(id);
buff[0] = firstChar;
str_itoa(vid, buff+1);
NAString idStr(buff);
return colTblIdHash_.getFirstValue(&idStr);
}
void QRDescGenerator::mergeDescGenerator(const QRDescGenerator* other)
{
mColumnsUsed_ += other->mColumnsUsed_;
mExtraHubColumnsUsed_ += other->mExtraHubColumnsUsed_;
const NAString* key;
QRElementPtr value;
QRElementHashIterator otherIter(other->colTblIdHash_);
for (CollIndex i=0; i<otherIter.entries(); i++)
{
otherIter.getNext(key, value);
colTblIdHash_.insert(key, value);
}
}