| /********************************************************************** |
| // @@@ 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); |
| } |
| } |