blob: 96ecf2a2b6abc48b9f9664bc8fcdd7372494e9e0 [file] [log] [blame]
// **********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
// **********************************************************************
// ***********************************************************************
//
// File: MatchOutput.cpp
// Description:
//
//
//
//
// Created: 08/28/08
// ***********************************************************************
#include "QmsMVDetails.h"
#include "QmsMVCandidate.h"
#include "QmsMatchTest.h"
#include "Range.h"
//========================================================================
// Class RewriteInstructionsItem
//========================================================================
RewriteInstructionsItem::RewriteInstructionsItem(const QRElementPtr queryElement,
const QRElementPtr mvElement,
ResultCode resultCode,
ResultStatus status,
ADD_MEMCHECK_ARGS_DEF(CollHeap* heap))
: NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap))
,queryElement_(queryElement)
,mvElement_(mvElement)
,resultCode_(resultCode)
,secondaryResultCode_(RC_INVALID_RESULT)
,status_(status)
,subElements_(new (heap) compositeMatchingResults(ADD_MEMCHECK_ARGS_PASS(heap)))
,extraHubTables_(heap)
,backJoinTables_(heap)
{
}
// ***************************************************************************
// ***************************************************************************
RewriteInstructionsItem::RewriteInstructionsItem(const RewriteInstructionsItem& other,
ADD_MEMCHECK_ARGS_DEF(CollHeap* heap))
: NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap))
,queryElement_(other.queryElement_)
,mvElement_(other.mvElement_)
,resultCode_(other.resultCode_)
,secondaryResultCode_(other.secondaryResultCode_)
,status_(other.status_)
,subElements_(new (heap) compositeMatchingResults(ADD_MEMCHECK_ARGS_PASS(heap)))
,extraHubTables_(heap)
,backJoinTables_(heap)
{
extraHubTables_.insert(other.extraHubTables_);
backJoinTables_.insert(other.backJoinTables_);
addSubElements(other.subElements_);
}
// ***************************************************************************
// ***************************************************************************
RewriteInstructionsItem::~RewriteInstructionsItem()
{
deletePtr(subElements_);
}
// ***************************************************************************
// ***************************************************************************
void RewriteInstructionsItem::addSubElement(RewriteInstructionsItemPtr subElement)
{
// Collect the special tables at the top.
extraHubTables_.insert(subElement->getExtraHubTables());
backJoinTables_.insert(subElement->getBackJoinTables());
// Add the rewrite instructions item to the list.
subElements_->insert(subElement);
}
// ***************************************************************************
// ***************************************************************************
void RewriteInstructionsItem::addSubElements(const RewriteInstructionsItemList& subElements,
NABoolean isSpecial)
{
if (subElements.entries() == 0)
return;
if (isSpecial)
{
// There are backjoin or extra hub tables involved - insert one by one.
CollIndex maxEntries = subElements.entries();
for (CollIndex i=0; i<maxEntries; i++)
addSubElement(subElements[i]);
}
else
{
// No special tables involved - just insert the list.
subElements_->insert(subElements);
}
}
// ***************************************************************************
// ***************************************************************************
void RewriteInstructionsItem::addSubElements(compositeMatchingResultsPtr other)
{
addSubElements(other->providedInputs_, FALSE);
addSubElements(other->notProvidedInputs_, FALSE);
addSubElements(other->outsideInputs_, FALSE);
addSubElements(other->indirectInputs_, FALSE);
addSubElements(other->extrahubInputs_, TRUE);
addSubElements(other->backJoinInputs_, TRUE);
}
// ***************************************************************************
// ***************************************************************************
const char* RewriteInstructionsItem::getResultString(ResultCode rc)
{
switch (rc)
{
case RC_OUTSIDE : return "Outside";
case RC_PROVIDED : return "Provided";
case RC_NOTPROVIDED : return "NotProvided";
case RC_REJECT : return "Reject";
case RC_CONTINUE : return "Continue";
case RC_EXTRAHUB : return "ExtraHub";
case RC_BACKJOIN : return "BackJoin";
case RC_INDIRECT : return "Indirect";
case RC_INPUTS_PROVIDED : return "InputsProvided";
case RC_EXPR_REWRITE : return "Expression Rewrite";
case RC_MATCH_RANGE : return "RangeSpec comparison";
case RC_SKIP_ME : return "Skip me";
case RC_ROLLUP : return "Rollup";
case RC_ROLLUP_EXPR : return "Rollup expression";
case RC_AGGR_EXPR : return "Aggregate expression";
case RC_INVALID_RESULT : return "Invalid result";
default : return "Invalid result code";
}
}
const char* RewriteInstructionsItem::getResultString()
{
return getResultString(getResultCode());
}
//========================================================================
// Class compositeMatchingResults
//========================================================================
// ***************************************************************************
// ***************************************************************************
compositeMatchingResults::~compositeMatchingResults()
{
for (CollIndex i=0; i<providedInputs_.entries(); i++)
deletePtr(providedInputs_[i]);
providedInputs_.clear();
for (CollIndex i=0; i<notProvidedInputs_.entries(); i++)
deletePtr(notProvidedInputs_[i]);
notProvidedInputs_.clear();
for (CollIndex i=0; i<outsideInputs_.entries(); i++)
deletePtr(outsideInputs_[i]);
outsideInputs_.clear();
for (CollIndex i=0; i<extrahubInputs_.entries(); i++)
deletePtr(extrahubInputs_[i]);
extrahubInputs_.clear();
for (CollIndex i=0; i<backJoinInputs_.entries(); i++)
deletePtr(backJoinInputs_[i]);
backJoinInputs_.clear();
for (CollIndex i=0; i<indirectInputs_.entries(); i++)
deletePtr(indirectInputs_[i]);
indirectInputs_.clear();
}
// ***************************************************************************
// ***************************************************************************
void compositeMatchingResults::insert(RewriteInstructionsItemPtr item)
{
switch(item->getResultCode())
{
case RC_PROVIDED:
providedInputs_.insert(item);
break;
case RC_NOTPROVIDED:
notProvidedInputs_.insert(item);
break;
case RC_OUTSIDE:
outsideInputs_.insert(item);
break;
case RC_EXTRAHUB:
extrahubInputs_.insert(item);
break;
case RC_BACKJOIN:
backJoinInputs_.insert(item);
break;
case RC_INDIRECT:
indirectInputs_.insert(item);
break;
case RC_SKIP_ME:
break;
default:
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Unexpected return code.");
} // switch
}
// ***************************************************************************
// ***************************************************************************
void compositeMatchingResults::insert(const RewriteInstructionsItemList& itemList)
{
switch(itemList[0]->getResultCode())
{
case RC_PROVIDED:
providedInputs_.insert(itemList);
break;
case RC_NOTPROVIDED:
notProvidedInputs_.insert(itemList);
break;
case RC_OUTSIDE:
outsideInputs_.insert(itemList);
break;
case RC_EXTRAHUB:
extrahubInputs_.insert(itemList);
break;
case RC_BACKJOIN:
backJoinInputs_.insert(itemList);
break;
case RC_INDIRECT:
indirectInputs_.insert(itemList);
break;
default:
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Unexpected return code.");
} // switch
}
//========================================================================
// Class MatchTest
//========================================================================
// ***************************************************************************
// Destructor - delete the rewrite instructionS LISTS.
// ***************************************************************************
MatchTest::~MatchTest()
{
CollIndex maxEntries = listOfFinalInstructions_.entries();
for (CollIndex i=0; i<maxEntries; i++)
deletePtr(listOfFinalInstructions_[i]);
listOfFinalInstructions_.clear();
maxEntries = listOfPendingWork_.entries();
for (CollIndex j=0; j<maxEntries; j++)
deletePtr(listOfPendingWork_[j]);
listOfPendingWork_.clear();
}
// ***************************************************************************
// Add an item of rewrite instructions to the collection.
// ***************************************************************************
void MatchTest::addRewriteInstructions(RewriteInstructionsItemPtr rewrite)
{
ResultCode rc = rewrite->getResultCode();
if (rewrite->hasSpecialSubElement())
{
candidate_->addExtraHubAndBackJoinTables(rewrite);
}
if (rewrite->isFinal())
listOfFinalInstructions_.insert(rewrite);
else
listOfPendingWork_.insert(rewrite);
}
// ***************************************************************************
// ***************************************************************************
NABoolean MatchTest::matchPass2()
{
// Is there any pending work from Pass 1?
if (isFinal())
{
// No - good, than we are done.
return TRUE;
}
else
{
// Yes - then lets check them out.
RewriteInstructionsItemList& pendingList = getListOfPendingWork();
Int32 maxEntries = pendingList.entries();
for (Int32 i=maxEntries-1; i>=0; i--)
{
RewriteInstructionsItemPtr pending = pendingList[i];
// Remove the item from the pending list.
// matchPass2OnElement() will add any needed items to the final list.
pendingList.removeAt(i);
// First verify that all the back-join and extra-hub columns are Final.
if (verifyExtraHubAndBackJoinColumns(pending) == FALSE)
{
// Cannot be provided - disqualify the MV.
deletePtr(pending);
return FALSE;
}
// Are the resulting rewrite instructions final?
if (pending->isFinal())
{
// Yes - add to the Final list.
addRewriteInstructions(pending);
}
else
{
// Call the sub-class specific method to handle the real work.
if (matchPass2OnElement(pending) == FALSE)
{
deletePtr(pending);
return FALSE;
}
}
//deletePtr(pending);
}
}
// All the pending tests passed.
return TRUE;
} // MatchTest::matchPass2()
// ***************************************************************************
// ***************************************************************************
NABoolean MatchTest::verifyExtraHubAndBackJoinColumns(RewriteInstructionsItemPtr pending)
{
ResultCode rc = pending->getResultCode();
if (rc != RC_BACKJOIN &&
rc != RC_EXTRAHUB &&
pending->getSubElements()->backJoinInputs_.entries() == 0 &&
pending->getSubElements()->extrahubInputs_.entries() == 0 )
{
// Nothing to do here.
return TRUE;
}
if (rc == RC_BACKJOIN)
{
// Nothing much to check about Back-Joins.
pending->setResultCode(RC_NOTPROVIDED);
pending->setResultStatus(RS_FINAL);
}
if (rc == RC_EXTRAHUB)
{
if (verifyExtraHubColumn(pending) == FALSE)
{
return FALSE;
}
pending->setResultCode(RC_PROVIDED);
pending->setResultStatus(RS_FINAL);
}
else if (pending->getActualQueryElement()->getElementType() == ET_Expr)
{
// Check the input columns for back joins.
RewriteInstructionsItemList& backJoinList = pending->getSubElements()->backJoinInputs_;
for (Int32 i=backJoinList.entries()-1; i>=0; i--)
{
RewriteInstructionsItemPtr rewrite = backJoinList[i];
rewrite->setResultCode(RC_NOTPROVIDED);
rewrite->setResultStatus(RS_FINAL);
}
// Check the input columns for extra hubs.
RewriteInstructionsItemList& extraHubList = pending->getSubElements()->extrahubInputs_;
RewriteInstructionsItemList& providedList = pending->getSubElements()->providedInputs_;
for (Int32 j=extraHubList.entries()-1; j>=0; j--)
{
RewriteInstructionsItemPtr rewrite = extraHubList[j];
extraHubList.removeAt(j);
if (verifyExtraHubColumn(rewrite) == FALSE)
{
// We have removed rewrite from its list, so we need to delete it explicitly.
deletePtr(rewrite);
return FALSE;
}
rewrite->setResultCode(RC_PROVIDED);
rewrite->setResultStatus(RS_FINAL);
providedList.insert(rewrite);
}
if (pending->getSubElements()->indirectInputs_.entries() == 0 &&
pending->getSecondaryResultCode() != RC_ROLLUP)
{
// We handled all the easy cases - the expression is now Final.
pending->setResultStatus(RS_FINAL);
}
}
else if (pending->getActualQueryElement()->getElementType() == ET_JoinPred)
{
// Check the input columns for extra-hubs.
const compositeMatchingResultsPtr subElements = pending->getSubElements();
NABoolean foundGoodOne = FALSE;
// If we have any extra-hub columns, use oine of them.
if (subElements->extrahubInputs_.entries() > 0)
{
RewriteInstructionsItemList& extraHubList = subElements->extrahubInputs_;
for (Int32 k=extraHubList.entries()-1; k>=0; k--)
{
RewriteInstructionsItemPtr rewrite = extraHubList[k];
extraHubList.removeAt(k);
if (foundGoodOne || verifyExtraHubColumn(rewrite) == FALSE)
{
// We have removed rewrite from its list, so we need to delete it explicitly.
deletePtr(rewrite);
}
else
{
foundGoodOne = TRUE;
pending->setResultCode(RC_PROVIDED);
pending->setResultStatus(RS_FINAL);
pending->setMvElement(rewrite->getMvElement());
}
} // for k
} // if extrahub
// Check the input columns for back joins.
RewriteInstructionsItemList& backJoinList = pending->getSubElements()->backJoinInputs_;
NABoolean hasBackJoins = backJoinList.entries() > 0;
//// Delete the sub-elements in the back-join list.
//for (int l=backJoinList.entries()-1; l>=0; l--)
//{
// RewriteInstructionsItemPtr rewrite = backJoinList[l];
// backJoinList.removeAt(l);
// deletePtr(rewrite);
//}
if (!foundGoodOne)
if (!hasBackJoins)
return FALSE;
else
{
pending->setResultCode(RC_NOTPROVIDED);
pending->setSecondaryResultCode(RC_BACKJOIN);
pending->setResultStatus(RS_FINAL);
}
}
return TRUE;
} // verifyExtraHubAndBackJoinColumns()
// ***************************************************************************
// For extra hub tables, we now know the table is matched OK,
// but we need to verify the column is Provided.
// ***************************************************************************
NABoolean MatchTest::verifyExtraHubColumn(RewriteInstructionsItemPtr pending)
{
QROutputPtr mvCol = NULL;
const QRColumnPtr queryCol = pending->getActualQueryElement()->downCastToQRColumn();
if (isProvidedColumn(queryCol, mvCol) == FALSE)
{
// The column is not Provided, so disqualify the MV.
const NAString& colName = queryCol->getFullyQualifiedColumnName();
candidate_->logMVWasDisqualified1("extra-hub column %s cannot be provided.", colName);
return FALSE;
}
pending->setResultCode(RC_PROVIDED);
pending->setMvElement(mvCol);
pending->setResultStatus(RS_FINAL);
return TRUE;
} // MatchTest::verifyExtraHubColumn()
// ***************************************************************************
// A generic method for generating the result descriptor elements.
// The specific details are implemented by sub-classes.
// ***************************************************************************
NABoolean MatchTest::generateDescriptor(QRCandidatePtr resultDesc)
{
RewriteInstructionsItemList& rewriteList = getListOfFinalInstructions();
for (CollIndex i=0; i<rewriteList.entries(); i++)
{
RewriteInstructionsItemPtr rewrite = rewriteList[i];
switch (rewrite->getResultCode())
{
case RC_PROVIDED:
if (generateProvidedElement(resultDesc, rewrite) == FALSE)
return FALSE;
break;
case RC_NOTPROVIDED:
if (generateNotProvidedElement(resultDesc, rewrite) == FALSE)
return FALSE;
break;
case RC_OUTSIDE:
// No need to specify Outside elements in the result descriptor.
//if (generateOutsideElement(resultDesc, rewrite) == FALSE)
// return FALSE;
break;
case RC_SKIP_ME:
break;
default:
// These are the only result codes allowed for final rewrite instructions.
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Unexpected result code for a final element.");
}
}
return TRUE;
} // MatchTest::generateDescriptor()
// ***************************************************************************
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::matchColumn(const QRColumnPtr colElem,
NABoolean fromExpr,
NABoolean isAnEqualitySet)
{
if (isAnEqualitySet)
return matchSingleColumn(colElem, fromExpr); // Avoid recursion.
if (!candidate_->getQueryDetails()->isFromJoin(colElem))
return matchSingleColumn(colElem, fromExpr);
else
{
const QRJoinPredPtr jp = candidate_->getQueryDetails()->getJoinPred(colElem);
RewriteInstructionsItemPtr rewrite = matchEqualitySet(jp);
if (rewrite)
{
// The rewrite instructions should reference the column, not the join pred.
rewrite->setQueryElement(colElem);
}
return rewrite;
}
}
// ***************************************************************************
// Match a single, full (not referencing) column (Pass 1)
// The options are:
// - Outside : The column's table is not covered by the MV.
// - Extra-hub : The column's table is in the MV's extra-hub. Verify in Pass 2.
// - Provided : The column is provided by the MV
// - BackJoinable : The column's table is covered by the MV, but the column
// itself is not provided by the MV, however, the table's
// key columns are provided, so we can get the needed column
// by using a back-join.
// - Indirect : This column is used as an input column to one of the MV's
// output expressions.
// - NULL : None of the above, the column cannot be provided by the
// MV in any way. Disqualify the MV.
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::matchSingleColumn(const QRColumnPtr colElem,
NABoolean fromExpr)
{
RewriteInstructionsItemPtr rewrite = NULL;
const NAString* extraHubID = NULL;
if(isOutsideColumn(colElem, extraHubID))
{
rewrite = new(heap_)
RewriteInstructionsItem(colElem, NULL, RC_OUTSIDE, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
return rewrite;
}
else if (extraHubID != NULL)
{
rewrite = new(heap_)
RewriteInstructionsItem(colElem, NULL, RC_EXTRAHUB, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
rewrite->addExtraHubTable(extraHubID);
return rewrite;
}
// If we get here, the column's table is in the MV hub.
QROutputPtr mvCol = NULL;
if (isProvidedColumn(colElem, mvCol))
{
rewrite = new(heap_)
RewriteInstructionsItem(colElem, mvCol, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
return rewrite;
}
if (isBackJoinableColumn(colElem))
{
rewrite = new(heap_)
RewriteInstructionsItem(colElem, NULL, RC_BACKJOIN, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
rewrite->addBackJoinTable(&colElem->getTableID());
rewrite->setSecondaryResultCode(RC_BACKJOIN);
return rewrite;
}
if (fromExpr)
{
rewrite = isIndirectColumn(colElem);
if (rewrite != NULL)
return rewrite;
// If not Indirect, fall through to failure.
}
// The column cannot be provided by the MV, so disqualify the MV.
const NAString& colName = colElem->getFullyQualifiedColumnName();
candidate_->logMVWasDisqualified1("column %s cannot be provided.", colName);
return NULL;
} // MatchTest::matchColumn()
// ***************************************************************************
// Check if this column of the query descriptor is an "Outside" column,
// which means that its a column of a table that is not covered by the MV.
// 1. If the column's table is covered by the MVMemo subgraph, its in the MV
// hub, so its definitly not an Outside column.
// 2. If the column's table is not covered by the entire MV, its definitly Outside.
// 3. Otherwise, it is covered by the MV's extra-hub tables, and we don't know
// yet if we can use those (maybe the join preds don't match). So return
// TRUE for now, but also return the ID of the extra hub table. This will be
// checked later, and if it does not match, the MV candidate will be disqualified.
// The results are cached in the MVCandidate object.
// ***************************************************************************
NABoolean MatchTest::isOutsideColumn(const QRColumnPtr colElem, const NAString*& extraHubID)
{
const DescriptorDetailsPtr queryDetails = candidate_->getQueryDetails();
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
QRJoinSubGraphPtr subGraph = candidate_->getJbbSubset()->getSubGraph();
QRJoinGraphPtr joinGraph = subGraph->getParentGraph();
const NAString& tableID = colElem->getTableID();
NABoolean isOutside = FALSE;
// Check if we have already cached the result for this table.
if (candidate_->probeCacheForTableID(&tableID, isOutside, extraHubID))
return isOutside;
// Both the column element and the join graph are from the query descriptor
// So we can directly use the ID mapping.
JoinGraphTablePtr tableNode = joinGraph->getTableByID(tableID);
// If the table is covered by the common subgraph, it is definitly not Outside.
if (tableNode != NULL && subGraph->contains(tableNode))
{
candidate_->cacheTableID(&tableID, FALSE, NULL);
return FALSE;
}
// What if its covered by the MV extra-hub tables?
// If so, its a different case - and should be put in the pendingWork queue
// until we check the join preds to those tables.
const NAString& tableName = queryDetails->getTableNameFromColumn(colElem);
if (tableName == "")
{
// This table is in a JBB that was ommitted from the query descriptor,
// because it has no MVs on it. So its definetly Outside.
candidate_->cacheTableID(&tableID, TRUE, NULL);
return TRUE;
}
// This is one of the few places we can lookup a table by its name,
// because we are not looking for a specific table instance, just existance,
// so self-joins are not a problem.
const QRTablePtr mvTable = mvDetails->getTableFromName(tableName);
// If the table is not used by the MV at all - its RC_OUTSIDE.
if (mvTable == NULL)
{
candidate_->cacheTableID(&tableID, TRUE, NULL);
return TRUE;
}
// We get here when table table is not part of the matched subGraph, which
// is the MV Hub, but it is used by the MV, so it must be in the MV extra-hub.
// Some more Pass 2 matching is needed to verify that its useful.
extraHubID = &colElem->getTableID();
candidate_->cacheTableID(&tableID, FALSE, extraHubID);
return FALSE;
} // MatchTest::isOutsideColumn()
// ***************************************************************************
// Is this column provided by the MV?
// ***************************************************************************
NABoolean MatchTest::isProvidedColumn(const QRColumnPtr colElem,
QROutputPtr& mvCol)
{
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
// Get the MV base table object from the query table ID.
const BaseTableDetailsPtr baseTable =
candidate_->getMvTableForQueryID(colElem->getTableID());
// Look it up in the MV hash table.
mvCol = mvDetails->getOutputByColumnName(baseTable->getTableElement()->getID(),
colElem->getColumnName(), heap_);
// The column is Provided if its an output column of the MV.
return (mvCol != NULL);
} // MatchTest::isProvidedColumn()
// ***************************************************************************
// Can the column be provided by using a back-join?
// ***************************************************************************
NABoolean MatchTest::isBackJoinableColumn(const QRColumnPtr colElem)
{
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
const DescriptorDetailsPtr queryDetails = candidate_->getQueryDetails();
// Get the MV base table object from the query table ID.
const BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(colElem->getTableID());
// Now check if the table is back-joinable (if its key columns are
// visible through the MV).
return baseTable->getTableElement()->isKeyCovered();
} // MatchTest::isBackJoinableColumn()
// *****************************************************************************
// This method is called only for columns that are inputs of query
// output expressions. It checks if this column is an input of an
// MV output expression, so there is a chance that the MV provides
// a sub-expression of the needed query expression.
// *****************************************************************************
RewriteInstructionsItemPtr MatchTest::isIndirectColumn(const QRColumnPtr colElem)
{
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
// Get the MV base table object from the query table ID.
const BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(colElem->getTableID());
// Now check if this column is used in the MV descriptor as an input to
// one of the output expressions
const QROutputPtr mvOutput =
mvDetails->getOutputByInputColumns(baseTable->getTableElement()->getID(),
colElem->getColumnName(), heap_);
if (mvOutput == NULL)
return NULL;
else
{
// Keep the MV output expression for the Pass 2 algorithm.
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(colElem, mvOutput, RC_INDIRECT, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
return rewrite;
}
} // MatchTest::isIndirectColumn()
// ***************************************************************************
// Find if the list of input columns from a query expression are provided
// by the MV:
// For all the input columns, call MatchOutputColumn()
// If ANY of the input columns was blocked
// Return NULL
// If ALL the input columns are Outside
// Return Outside
// If ALL the inputs columns are Provided
// Return a final NotProvided with the rewrite instructions.
// Otherwise
// Return an intermediate NotProvided with all the intermediate results.
// Pass 2 will sort things out.
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::findExpressionInputs(const ElementPtrList& queryColList,
const QRElementPtr queryElement)
{
RewriteInstructionsItemPtr rewrite = NULL;
if (queryColList.entries() == 0)
{
// An expression with no inputs is NotProvided.
rewrite = new(heap_)
RewriteInstructionsItem(queryElement, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
rewrite->setSecondaryResultCode(RC_NO_INPUTS);
return rewrite;
}
// Analyze the expresion inputs
compositeMatchingResultsPtr intermediateResults =
new(heap_) compositeMatchingResults(ADD_MEMCHECK_ARGS(heap_));
MatchInputColumnList(queryColList, intermediateResults, FALSE);
// Now check the analysis results
if (intermediateResults->wasBlockedColumn_)
{
// At least one of the expression inputs cannot be provided by the MV.
// Disqualify the MV.
candidate_->logMVWasDisqualified1("Expression %s cannot be provided.", queryElement->getID());
deletePtr(intermediateResults);
return NULL;
}
else if (intermediateResults->outsideInputs_.entries() == queryColList.entries())
{
// All the columns are RC_OUTSIDE
// Pick the first one for the rewrite.
rewrite = intermediateResults->outsideInputs_[0];
rewrite->setQueryElement(queryElement);
intermediateResults->outsideInputs_.removeAt(0);
}
else if (intermediateResults->providedInputs_.entries() == queryColList.entries())
{
// All the expression inputs are directly provided by the MV.
rewrite = new(heap_)
RewriteInstructionsItem(queryElement, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
rewrite->setSecondaryResultCode(RC_INPUTS_PROVIDED);
// Copy the provided members to the result
rewrite->addSubElements(intermediateResults->providedInputs_, FALSE);
intermediateResults->providedInputs_.clear();
}
else
{
// The expression is NotProvided, which means it can probably be computed using the MV columns.
// Give all the intermediate data to the Pass 2 algorithm.
rewrite = new(heap_)
RewriteInstructionsItem(queryElement, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
rewrite->setSecondaryResultCode(RC_INPUTS_PROVIDED);
rewrite->addSubElements(intermediateResults);
intermediateResults->clear();
}
deletePtr(intermediateResults);
return rewrite;
} // MatchTest::findExpressionInputs()
// ***************************************************************************
// Match an equality set to an MV's output list.
// The rules are:
// 1. First match all the equality set columns
// 2. If ALL the the equality set members are Outside
// Then the entire equality set is a final Outside.
// Pick the first member for the rewrite instructions.
// 3. If ANY of the members is a final Provided
// Then the entire equality set is Provided.
// Pick the first Provided member for the rewrite instructions.
// 4. If any of the members is a back-join or an extra-hub
// The the equality set is an intermediate NotProvided.
// 5. Otherwise
// Disquality the MV.
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::matchEqualitySet(const QRJoinPredPtr joinPred)
{
const ElementPtrList& equalitySet = joinPred->getEqualityList();
compositeMatchingResultsPtr intermediateResults =
new(heap_) compositeMatchingResults(ADD_MEMCHECK_ARGS(heap_));
RewriteInstructionsItemPtr rewrite = NULL;
// Analyze the equality set members
MatchInputColumnList(equalitySet, intermediateResults, TRUE);
// Check Outside first, because it had ALL semantics
if (intermediateResults->outsideInputs_.entries() == equalitySet.entries())
{
// All the columns are RC_OUTSIDE
// Pick the first one for the rewrite.
rewrite = intermediateResults->outsideInputs_[0];
rewrite->setQueryElement(joinPred);
intermediateResults->outsideInputs_.removeAt(0);
}
// At least one of the columns in the equality set was not an Outside, so
// this is not an Outside result. Check the other options, this time using
// ANY semantics.
else if (intermediateResults->providedInputs_.entries() > 0)
{
// We have at least one member that is Provided.
// Pick the first one for the rewrite.
rewrite = intermediateResults->providedInputs_[0];
rewrite->setQueryElement(joinPred);
intermediateResults->providedInputs_.removeAt(0);
}
else if (intermediateResults->backJoinInputs_.entries() > 0 ||
intermediateResults->extrahubInputs_.entries() > 0)
{
// Problematic scenario TBD:
// This equality set has no Provided members, but it does have back-join
// and/or extra-hub members. When we defer the decision to Pass 2, and an
// extra-hub table cannot be verified, it will disqualify the MV, even
// though there may be other alternatives still open.
// A possible solution may be to have a separate list of non-critical
// extra-hub tables, that do not disqualify the MV when fail to verify.
// We have at least one member that is either Backjoinable or RC_EXTRAHUB.
// Keep all the data for Pass 2 processing.
rewrite = new(heap_)
RewriteInstructionsItem(joinPred, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
// Copy the backjoinable members to the result
rewrite->addSubElements(intermediateResults->backJoinInputs_, TRUE);
// Clear them from the intermediate results so they don't get deleted at the end of this method.
intermediateResults->backJoinInputs_.clear();
// Copy the extrahub members to the result
rewrite->addSubElements(intermediateResults->extrahubInputs_, TRUE);
// Clear them from the intermediate results so they don't get deleted at the end of this method.
intermediateResults->extrahubInputs_.clear();
}
else
{
// None of the equality set members is covered by the MV.
candidate_->logMVWasDisqualified1("output equality set %s cannot be provided.", joinPred->getID());
rewrite = NULL;
}
deletePtr(intermediateResults);
return rewrite;
} // MatchTest::matchEqualitySet()
// ***************************************************************************
// For a list of columns that can be either the list of exprerssion inputs,
// or an equality set list of members, match every member of the list, and
// arrange the results according to the result code in separate lists.
// ***************************************************************************
void MatchTest::MatchInputColumnList(const ElementPtrList& queryColList,
compositeMatchingResultsPtr matchingResults,
NABoolean isAnEqualitySet)
{
// These lists are for collecting results from individual inputs.
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
const DescriptorDetailsPtr queryDetails = candidate_->getQueryDetails();
CollIndex numInputs = queryColList.entries();
for (CollIndex i=0; i<numInputs; i++)
{
RewriteInstructionsItemPtr rewrite = NULL;
QRElementPtr queryElem = queryColList[i]->getReferencedElement();
switch(queryElem->getElementType())
{
case ET_Column:
{
QRColumnPtr queryInputCol = queryElem->downCastToQRColumn();
if (isAnEqualitySet && queryDetails->isColumnFromLojTable(queryInputCol))
{
// This is a column of an LOJ table within a JoinPred.
// Skip it in search of the other column.
continue;
}
// Call the single column matching method.
rewrite = matchColumn(queryInputCol, !isAnEqualitySet, isAnEqualitySet);
break;
}
case ET_JoinPred:
{
// The input column is an equality set.
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
!isAnEqualitySet, QRLogicException,
"Equality sets should not reference other equality sets.");
const QRJoinPredPtr joinPred = queryElem->downCastToQRJoinPred();
rewrite = matchEqualitySet(joinPred);
break;
}
case ET_Expr:
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
isAnEqualitySet, QRLogicException,
"Expressions should not list other expressions as inputs.");
// TBD - expressions as inputs of equality sets
break;
}
default:
{
// Not expecting elements other than those above.
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Unexpected input column list member.");
}
} // case on element type
if (rewrite == NULL)
{
// This input column is not provided by the MV.
// So the query output expression cannot be computed from this MV.
matchingResults->wasBlockedColumn_ = TRUE;
// If this is an expression input list - quit now because the expression cannot be provided.
// If this is an equality set - continue because we can use some other set member.
if (!isAnEqualitySet)
return;
else
{
QRLogger::log(CAT_MATCHTST_MVDETAILS, LL_INFO,
"Ignore last disqualification message.");
continue;
}
}
matchingResults->insert(rewrite);
// If this is an equality set, one provided column is all we need.
if (rewrite->getResultCode() == RC_PROVIDED && isAnEqualitySet)
return;
} // for loop on input columns.
} // MatchTest::MatchInputColumnList()
// ***************************************************************************
// Generate the QRMVColumn element for a provided column.
// ***************************************************************************
QRMVColumnPtr MatchTest::generateProvidedMvColumn(RewriteInstructionsItemPtr rewrite)
{
// Construct the MVColumn element
QRMVColumnPtr mvColumn = new (heap_) QRMVColumn(ADD_MEMCHECK_ARGS(heap_));
// Get the name of the MV column from the MV output element.
const QRElementPtr mvElem = rewrite->getMvElement();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
mvElem != NULL, QRLogicException,
"Expecting an MV element for a Provided output column.");
const QROutputPtr mvCol = mvElem->downCastToQROutput();
mvColumn->setMVColName(mvCol->getName());
// If the query element is an Output element, reference the ID of the internal
// column/expression/join-pred.
QRElementPtr queryElem = rewrite->getActualQueryElement();
mvColumn->setRef(queryElem->getID());
// Leave the MV attribute for now. Hopefully we will not need it.
return mvColumn;
}
// ***************************************************************************
// Generate the QRColumn element for a NotProvided column.
// ***************************************************************************
QRColumnPtr MatchTest::generateNotProvidedColumn(RewriteInstructionsItemPtr rewrite)
{
// Construct the MVColumn element
QRColumnPtr resultColumn = new (heap_) QRColumn(ADD_MEMCHECK_ARGS(heap_));
// Get the name of the column from the query element.
const QRElementPtr queryElem = rewrite->getActualQueryElement();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
queryElem != NULL, QRLogicException,
"Expecting a query element for a NotProvided output column.");
resultColumn->setRef(queryElem->getID());
if (queryElem->getElementType() == ET_Column)
{
const QRColumnPtr queryCol = queryElem->downCastToQRColumn();
resultColumn->setFullyQualifiedColumnName(queryCol->getFullyQualifiedColumnName());
resultColumn->setTableID(queryCol->getTableID());
}
else
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
queryElem->getElementType() == ET_JoinPred, QRLogicException,
"Expecting a either a Column or JoinPred here.");
RewriteInstructionsItemPtr subRewrite = rewrite->getSubElements()->backJoinInputs_[0];
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
subRewrite, QRLogicException,
"Expecting a back-join sub-element.");
const QRElementPtr querySubElem = subRewrite->getQueryElement();
const QRColumnPtr queryCol = querySubElem->downCastToQRColumn();
resultColumn->setFullyQualifiedColumnName(queryCol->getFullyQualifiedColumnName());
}
// Leave the MV attribute for now. Hopefully we will not need it.
return resultColumn;
}
// ***************************************************************************
// Match an expression (Pass 1). Here is the algorithm from
// the IS document:
// 1. If the expression text is provided by the MV, and the expression's
// input columns match as well
// Return Provided.
// 2. If all the input columns are provided by the MV
// Return NotProvided.
// 3. Otherwise, return NULL.
//
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::matchExpression (const QRExprPtr expr)
{
RewriteInstructionsItemPtr rewrite = NULL;
// 1. Check if the MV has an output expression with matching text
// Skip this test for rollup aggregate expressions.
if (candidate_->getAggregateMatchingType() != AMT_MAV_AQ_DG ||
expr->getExprRoot()->containsAnAggregate() == FALSE)
{
rewrite = findMatchingMvExpression(expr->getExprRoot());
if (rewrite != NULL)
{
rewrite->setQueryElement(expr);
return rewrite;
}
}
// 2. A matching MV output expression was not found.
// Check if at least the input columns are Provided.
const ElementPtrList& queryInputs = expr->getInputColumns(heap_);
if (queryInputs.entries() == 0)
{
// Sometimes expressions have no column inputs, like COUNT(*).
switch (candidate_->getAggregateMatchingType())
{
case AMT_MAV_AQ_MG:
// If this is a matching MAV, fail because it should have matched a MAV aggregate expression.
candidate_->logMVWasDisqualified1("output expression %s is not Provided.",
expr->getID().data());
return NULL;
case AMT_MJV_AQ:
// If its on an MJV, its a Final Outside match, because it can be computed on the MJV.
rewrite = new(heap_)
RewriteInstructionsItem(expr, NULL, RC_OUTSIDE, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
break;
case AMT_MAV_AQ_DG:
// If this is a rollup matching case, defer to Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(expr, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
if (expr->getExprRoot()->containsAnAggregate())
rewrite->setSecondaryResultCode(RC_ROLLUP);
break;
case AMT_MJV_JQ:
candidate_->logMVWasDisqualified1("output expression %s is not Provided.",
expr->getID().data());
break;
}
}
else
{
// The expression has inputs. Is it an aggregate expression on a MAV?
if (expr->getExprRoot()->containsAnAggregate() &&
candidate_->getAggregateMatchingType() != AMT_MJV_AQ)
{
// Yes, its an aggregate expression. Deal with it in Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(expr, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG)
rewrite->setSecondaryResultCode(RC_ROLLUP);
}
else
{
// No. Try to find its input columns.
rewrite = findExpressionInputs(queryInputs, expr);
}
}
return rewrite;
} // MatchTest::matchExpression()
// ***************************************************************************
// Check if the MV provides a matching output expression, including its
// input columns.
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::findMatchingMvExpression(const QRExplicitExprPtr expr)
{
// Check if the MV has an output expression with matching text
const MVDetailsPtr mvDetails = candidate_->getMvDetails();
const DescriptorDetailsPtr queryDetails = candidate_->getQueryDetails();
NAString exprText(heap_);
expr->unparse(exprText, FALSE);
const QROutputPtr mvOutput1 = mvDetails->getOutputByExprText(exprText);
if (mvOutput1 == NULL)
return NULL;
// MV output expression found. Now lets match its input columns.
ElementPtrList queryColList(heap_);
expr->getInputColumns(queryColList, heap_);
// The MV may have several output expressions with the same normalized
// expression text, but different input columns. We need to loop on them,
// so if one is disqualified, we can check the others.
const QRElementHash& outputByExprText = mvDetails->getOutputByExprTextHash();
QRElementHashIterator iterator(outputByExprText, &exprText);
// Loop on the iterator on all the output expressions with the same expression text.
const NAString* key; // we don't really use this - it will always be equal to exprText.
QRElementPtr mvOutputElem;
for ( CollIndex j=0; j<iterator.entries(); j++)
{
iterator.getNext (key, mvOutputElem);
const QROutputPtr mvOutput = mvOutputElem->downCastToQROutput();
// Get to the actual MV expression
QRExprPtr mvExpr = mvOutput->getOutputItem()->getReferencedElement()->downCastToQRExpr();
// Now match the expression's input lists
if (matchExpressionInputs(queryColList, mvExpr->getInputColumns(heap_)))
{
// This expression matched just fine.
RewriteInstructionsItemPtr rewrite = NULL;
// Let's see if we can also find rewrite instructions for the individual input columns
if (queryColList.entries() == 0)
{
// This expression has no input columns (maybe a COUNT(*)? )
rewrite = new(heap_)
RewriteInstructionsItem(expr, mvOutput, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
}
else
{
// Find if the input columns are Provided.
rewrite = findExpressionInputs(queryColList, expr);
if (rewrite != NULL)
{
// Verify that all the input columns are Provided.
compositeMatchingResultsPtr subElements = rewrite->getSubElements();
if (subElements->entries() != subElements->providedInputs_.entries())
{
// Otherwise ignore the input columns (and use only the entire expression).
deletePtr(rewrite);
rewrite = NULL;
}
}
if (rewrite == NULL)
{
// No - Not all input columns are Provided. No big deal, just return
// the rewrite instructions for the entire expression.
// Undo the disqualification of the MV when finding expression inputs.
candidate_->reQualify();
rewrite = new(heap_)
RewriteInstructionsItem(expr, mvOutput, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
}
else
{
// Yes, Combine the two.
rewrite->setMvElement(mvOutput);
rewrite->setResultCode(RC_PROVIDED);
rewrite->setResultStatus(RS_FINAL);
}
}
return rewrite;
}
// This expression failed to match on the input list. continue to next expression.
} // for on iterator.
// All the expressions with the matching text failed on their inputs.
return NULL;
} // MatchOutput::findMatchingMvExpression()
// ***************************************************************************
// This method accepts two expression input lists as inputs: one from the query
// and one from the MV. Both are from expressions with the same normalized predicate
// expression text. This method checks if the list of input columns match as well.
// This method returns TRUE when a match is found, anf FALSE otherwise.
// ***************************************************************************
NABoolean MatchTest::matchExpressionInputs(const ElementPtrList& queryInputColumns,
const ElementPtrList& mvInputColumns)
{
// The input lists must have the same number of entries to match.
if (queryInputColumns.entries() != mvInputColumns.entries())
return FALSE;
CollIndex maxEntries = mvInputColumns.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
// The MV and query input lists must be ordered by their appearance in the predicate expression.
// The matchByName() method is only implemented for the QRColumn and QRJoinPred classes.
QRColumnPtr queryColumn = queryInputColumns[i]->getReferencedElement()->downCastToQRColumn();
QRJoinPredPtr query_jp = candidate_->getQueryDetails()->getJoinPred(queryColumn);
QRColumnPtr mvColumn = mvInputColumns[i]->getReferencedElement()->downCastToQRColumn();
QRJoinPredPtr mv_jp = candidate_->getQueryDetails()->getJoinPred(mvColumn);
if (query_jp == NULL)
{
// If the query input has no equi-join preds on it, the MV input
// must not have any either.
// So if the MV input column is a join pred, it must be a different expression.
if (mv_jp != NULL)
return FALSE;
if (matchColumnsByNameAndID(queryColumn, mvColumn) == FALSE)
return FALSE;
}
else if (query_jp)
{
// Lets check the MV input.
if (mv_jp != NULL)
{
// Its a join pred. Since in case of a match, the query join pred
// must be a superset of the MV join pred, we can just pick the first
// column in the join pred, and match it to any column in the query equality set.
const ElementPtrList& mvEqualityList = mv_jp->getEqualityList();
mvColumn = mvEqualityList[0]->downCastToQRColumn();
}
// OK, we have the MV input column, now match it against the columns
// in the query equality set.
const ElementPtrList& queryEqualityList = query_jp->getEqualityList();
CollIndex maxEntries = queryEqualityList.entries();
NABoolean matchFound = FALSE;
for (CollIndex j=0; j<maxEntries; j++)
{
const QRColumnPtr queryColumn = queryEqualityList[j]->getReferencedElement()->downCastToQRColumn();
if (matchColumnsByNameAndID(queryColumn, mvColumn))
{
// Found a match for this input. Break from this loop, and continue
// with the next input.
matchFound = TRUE;
break;
}
}
// Match not found for this input.
if (matchFound == FALSE)
return FALSE;
}
else
{
return FALSE;
}
} // for
return TRUE;
} // MatchTest::matchExpressionInputs()
// ***************************************************************************
// Match an MV column to a query column by table ID and column name.
// Since the two columns are from different descriptors, the query table ID
// must first be translated to the corresponding table ID from the MV descriptor.
// ***************************************************************************
NABoolean MatchTest::matchColumnsByNameAndID(const QRColumnPtr queryColumn, const QRColumnPtr mvColumn)
{
// Match the column name first.
if (queryColumn->getColumnName() != mvColumn->getColumnName())
return FALSE;
// Now check the table IDs.
const NAString& queryTableID = queryColumn->getTableID();
const NAString& mvTableID = mvColumn->getTableID();
// Translate the query tableID to the corresponding ID from the MV descriptor.
const BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(queryTableID, FALSE);
if (baseTable == NULL)
return FALSE;
// Are they the same?
return (baseTable->getID() == mvTableID);
} // MatchTest::matchColumnsByNameAndID()
// ***************************************************************************
// Add the Provided and BackJoin inputs as outputs of the MV candidate.
// In case the optimizer will not be able to use the Provided expression,
// it will try to use the input columns.
// ***************************************************************************
void MatchTest::addInputsToOutputList(RewriteInstructionsItemPtr rewrite)
{
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG)
return;
RewriteInstructionsItemList& providedList = rewrite->getSubElements()->providedInputs_;
CollIndex maxEntries = providedList.entries();
RewriteInstructionsItemPtr columnRewrite = NULL;
for (CollIndex i=0; i<maxEntries; i++)
{
candidate_->addOutputColumn(providedList[i]);
}
if (rewrite->getResultCode() == RC_PROVIDED)
return;
RewriteInstructionsItemList& backJoinList = rewrite->getSubElements()->backJoinInputs_;
maxEntries = backJoinList.entries();
for (CollIndex j=0; j<maxEntries; j++)
{
candidate_->addOutputColumn(backJoinList[j]);
}
RewriteInstructionsItemList& extraHubList = rewrite->getSubElements()->extrahubInputs_;
maxEntries = extraHubList.entries();
for (CollIndex k=0; k<maxEntries; k++)
{
candidate_->addOutputColumn(extraHubList[k]);
}
// If the expression itself is not Provided, we may be able to skip it
// and leave only the input columns.
// We can do that if some of its inputs are Outside, or if its an
// aggregate expression on top of an MJV.
QRExprPtr expr = NULL;
QRElementPtr queryElem = rewrite->getQueryElement();
if (queryElem->getElementType()== ET_RangePred)
{
QRRangePredPtr pred = static_cast<QRRangePredPtr>(queryElem);
expr = pred->getRangeItem()->getReferencedElement()->downCastToQRExpr();
}
else
{
expr = rewrite->getActualQueryElement()->downCastToQRExpr();
}
if (rewrite->getSubElements()->outsideInputs_.entries() > 0 ||
( expr->getExprRoot()->containsAnAggregate() &&
candidate_->getAggregateMatchingType() == AMT_MJV_AQ) )
{
rewrite->setResultCode(RC_SKIP_ME);
}
} // addInputsToOutputList()
// ***************************************************************************
// Use a visitor to find aggregate functions, and solve any that are found.
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::findInternalAggregateFunctions(QRExplicitExprPtr treeExpr)
{
AggregateCollectorVisitorPtr visitor = new(heap_)
AggregateCollectorVisitor(ADD_MEMCHECK_ARGS(heap_));
treeExpr->treeWalk(visitor);
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(treeExpr, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_MG)
{
rewrite->setSecondaryResultCode(RC_AGGR_EXPR);
}
else
{
rewrite->setSecondaryResultCode(RC_ROLLUP_EXPR);
}
// Try to find all the aggregate functions used by the expression.
FunctionNodesList& aggrFunctions = visitor->getAggregateFunctionList();
CollIndex maxEntries = aggrFunctions.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
// Look for the corresponding aggregate function as an MV output.
RewriteInstructionsItemPtr funcRewrite = findAggregateFunction(aggrFunctions[i]);
if (funcRewrite == NULL)
{
deletePtr(visitor);
deletePtr(rewrite);
return NULL;
}
// Add it to the expression rewrite instructions.
rewrite->addSubElement(funcRewrite);
} // for
// Check for any columns used in the expressions outside the aggregate functions.
ColumnNodesList& simpleCols = visitor->getSimpleColumnList();
maxEntries = simpleCols.entries();
for (CollIndex j=0; j<maxEntries; j++)
{
QRColumnPtr col = simpleCols[j]->getReferencedElement()->downCastToQRColumn();
RewriteInstructionsItemPtr colRewrite = matchColumn(col, FALSE);
if (colRewrite != NULL && !colRewrite->isFinal())
{
deletePtr(colRewrite);
colRewrite = NULL;
}
if (colRewrite == NULL)
{
deletePtr(visitor);
deletePtr(rewrite);
return NULL;
}
// Add it to the expression rewrite instructions.
rewrite->addSubElement(colRewrite);
}
// Check for any join preds used in the expressions outside the aggregate functions.
JoinPredNodesList& joinedCols = visitor->getJoinedColumnList();
maxEntries = joinedCols.entries();
for (CollIndex k=0; k<maxEntries; k++)
{
QRJoinPredPtr col = joinedCols[k]->getReferencedElement()->downCastToQRJoinPred();
RewriteInstructionsItemPtr colRewrite = matchEqualitySet(col);
if (colRewrite != NULL && !colRewrite->isFinal())
{
deletePtr(colRewrite);
colRewrite = NULL;
}
if (colRewrite == NULL)
{
deletePtr(visitor);
deletePtr(rewrite);
return NULL;
}
// Add it to the expression rewrite instructions.
rewrite->addSubElement(colRewrite);
}
deletePtr(visitor);
return rewrite;
}
// ***************************************************************************
// ***************************************************************************
RewriteInstructionsItemPtr MatchTest::findAggregateFunction(QRFunctionPtr func)
{
RewriteInstructionsItemPtr rewrite = NULL;
// Collect the function's input columns.
ElementPtrList inputList(heap_);
QRExplicitExprPtr funcArgument = func->getArguments()[0];
funcArgument->getInputColumns(inputList, heap_, TRUE);
// Is this a rollup case?
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG)
{
// Check for COUNT(*) as a special case
if (func->getAggregateFunc() == QRFunction::AFT_COUNTSTAR)
{
rewrite = new(heap_)
RewriteInstructionsItem(func, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
rewrite->setSecondaryResultCode(RC_ROLLUP_ON_GROUPING);
return rewrite;
}
// Maybe its a rollup on a grouping column?
if (areAllInputsGroupingColumns(func, inputList))
{
// All the input columns are MV grouping columns.
// Now check that they are Provided by the MV.
rewrite = findExpressionInputs(inputList, func);
if (rewrite == NULL)
return NULL;
if (!rewrite->isFinal())
{
deletePtr(rewrite);
return NULL;
}
rewrite->setSecondaryResultCode(RC_ROLLUP_ON_GROUPING);
return rewrite;
}
else
{
// Rollup, but not on MV grouping columns.
// Do not allow distinct aggregate functions to match.
if (func->isDistinctAggregate() )
{
candidate_->logMVWasDisqualified1("DISTINCT aggregate function %s cannot be rolled up.",
func->getID().data());
return NULL;
}
}
} // if (rollup)
// We get here if its not a rollup case, or if its a rollup case but
// not on MV grouping columns.
// Look for the corresponding aggregate function as an MV output.
rewrite = findMatchingMvExpression(func);
if (rewrite != NULL)
{
if (rewrite->isFinal())
return rewrite;
else
deletePtr(rewrite);
}
// Last try - maybe this is a COUNT(col) on a NOT NULL column,
// so we can use COUNT(*) instead?
QROutputPtr countStarOutput = candidate_->getMvDetails()->getCountStar();
if (func->getAggregateFunc() == QRFunction::AFT_COUNT && countStarOutput != NULL)
{
// OK, this is a COUNT() function, and the MV has a COUNT(*) output.
// Check that all the input columns are not nullable.
if (func->isCountStarEquivalent(heap_))
{
// Yes! - we can use COUNT(*) instead.
rewrite = new(heap_)
RewriteInstructionsItem(func, countStarOutput, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
return rewrite;
}
}
// Give up on this aggregate function.
candidate_->logMVWasDisqualified1("aggregate function %s cannot be matched.",
func->getID().data());
return NULL;
}
// ***************************************************************************
// The extra grouping columns are the ones being rolled up – columns that
// are in the MVÂ’s grouping list, but not in the queryÂ’s grouping list. An
// aggregate function is on an MV grouping column when its inputs are in this list.
// ***************************************************************************
NABoolean MatchTest::areAllInputsGroupingColumns(QRFunctionPtr func, ElementPtrList& inputList)
{
// Are all the inputs MV grouping columns?
for (CollIndex i=0; i<inputList.entries(); i++)
{
const QRColumnPtr inputCol = inputList[i]->getFirstColumn();
if (!isExtraGroupingColumn(inputCol))
return FALSE;
}
return TRUE;
}
// ***************************************************************************
// Generate a result QRExpr element for an expression involving aggregate functions.
// The rewrite instructions for the individual aggregate functions have already
// been prepared as Provided subElements, by the Pass 2 code.
// First we go over the list of subElements, creating an MVColumn element for
// each aggregate function used in the expression. Each MVColumn element is
// inserted into a hash table, with the ID of the corresponding query sub-expression
// as the key. Then we make a deep copy of the query expression tree, and in
// the process, switch every sub-expression whose ID we find in the hash table
// with the corresponding MVColumn element.
// Finaly, the output expression itself is created, referencing the query one.
// ***************************************************************************
QRExprPtr MatchTest::generateAggregateExpressionOutput(RewriteInstructionsItemPtr rewrite)
{
// Init the hash table.
subExpressionRewriteHash subExprHash(hashKey, INIT_HASH_SIZE_SMALL, FALSE, heap_);
compositeMatchingResultsPtr subExprs = rewrite->getSubElements();
// Verify all the subElements are Provided.
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
subExprs->entries() == subExprs->providedInputs_.entries()
+ subExprs->notProvidedInputs_.entries()
+ subExprs->backJoinInputs_.entries(),
QRLogicException,
"Expecting all subexpressions to be Provided, NotProvided, or back join.");
RewriteInstructionsItemList rewriteList(heap_);
rewriteList.insert(subExprs->providedInputs_);
rewriteList.insert(subExprs->notProvidedInputs_);
rewriteList.insert(subExprs->backJoinInputs_);
// For each subElement
CollIndex maxEntries = subExprs->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
RewriteInstructionsItemPtr subExprRewrite = rewriteList[i];
const NAString& key = subExprRewrite->getQueryElement()->getID();
QRExplicitExprPtr exprRewrite = NULL;
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG)
{
if (subExprRewrite->getQueryElement()->getElementType() == ET_Function)
{
// This is a rolled-up aggregate function.
if (subExprRewrite->getSecondaryResultCode() != RC_ROLLUP_ON_GROUPING)
{
// This is a normal Rollup case. Additional aggregation is needed above the MVColumn.
exprRewrite = generateRollupAggregateFunction(subExprRewrite);
}
else
{
// This rollup over a grouping column.
exprRewrite = generateRollupOverGroupingAggregateFunction(subExprRewrite);
}
}
else
{
// This is a grouping column used in the expression.
exprRewrite = generateProvidedMvColumn(subExprRewrite);
}
}
else
{
// This is a matching grouping list. Every matched sub-expression
// is a Provided MVColumn.
if (subExprRewrite->getSecondaryResultCode() == RC_BACKJOIN)
exprRewrite = generateNotProvidedColumn(subExprRewrite);
else
exprRewrite = generateProvidedMvColumn(subExprRewrite);
}
if (exprRewrite == NULL)
{
cleanupSubExprHash(subExprHash);
return NULL;
}
subExprHash.insert(&key, exprRewrite);
}
// Create the result expression tree from the query expression tree, with
// the needed sub-expressions switched to MVColumn elements.
QRExprPtr queryExpr = rewrite->getActualQueryElement()->getReferencedElement()->downCastToQRExpr();
QRExplicitExprPtr resultTreeExpr = NULL;
try
{
resultTreeExpr = queryExpr->getExprRoot()->deepCopyAndSwitch(subExprHash, heap_);
}
catch(QRDescriptorException ex)
{
// We get here if we cannot properly rewrite all the query column elements.
// Release objects stored in subExprHash before returning.
cleanupSubExprHash(subExprHash);
return NULL;
}
// Create the expression on top of it.
QRExprPtr expr = new(heap_) QRExpr(FALSE, ADD_MEMCHECK_ARGS(heap_));
expr->setExprRoot(resultTreeExpr);
expr->setRef(queryExpr->getID());
return expr;
} // generateAggregateExpressionOutput()
/****************************************************************************/
/****************************************************************************/
void MatchTest::cleanupSubExprHash(subExpressionRewriteHash& subExprHash)
{
subExpressionRewriteHashIterator iter(subExprHash);
const NAString* key;
QRExplicitExprPtr value;
for (CollIndex x = 0; x < iter.entries(); x++)
{
// Get the next sub expression.
iter.getNext(key, value);
deletePtr(value);
}
}
/****************************************************************************/
/****************************************************************************/
NABoolean MatchTest::isExtraGroupingColumn(QRColumnPtr col)
{
GroupingList* extras = candidate_->getExtraGroupingColumns();
CollIndex maxEntries = extras->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
QRElementPtr elem = (*extras)[i]->getReferencedElement();
// Handle simple columns.
if (elem->getElementType() == ET_Column)
{
QRColumnPtr groupingCol = elem->downCastToQRColumn();
if (matchColumnsByNameAndID(col, groupingCol))
return TRUE;
}
// Handle equality sets - check every element in the set.
if (elem->getElementType() == ET_JoinPred)
{
const QRJoinPredPtr mvJoinPred = elem->downCastToQRJoinPred();
const ElementPtrList& mvEqualityList = mvJoinPred->getEqualityList();
CollIndex eqSetSize = mvEqualityList.entries();
for (CollIndex j=0; j<eqSetSize; j++)
{
const QRElementPtr eqElement = mvEqualityList[j];
if (eqElement->getElementType() != ET_Column)
continue;
const QRColumnPtr eqCol = eqElement->downCastToQRColumn();
if (matchColumnsByNameAndID(col, eqCol))
return TRUE;
}
}
}
return FALSE;
}
// ***************************************************************************
// ***************************************************************************
QRFunctionPtr MatchTest::generateRollupAggregateFunction(RewriteInstructionsItemPtr rewrite)
{
// This is the query aggregate function.
QRElementPtr queryElem = rewrite->getActualQueryElement();
QRFunctionPtr queryFunction;
if (queryElem->getElementType() == ET_Expr)
{
queryFunction = queryElem->downCastToQRExpr()->getExprRoot()->downCastToQRFunction();
}
else
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
queryElem->getElementType() == ET_Function, QRLogicException,
"Expecting either a QRExpr or QRFunction.");
queryFunction = queryElem->downCastToQRFunction();
}
NAString aggrName(heap_);
QRFunction::AggregateFunctionType aggrType = QRFunction::AFT_NONE;
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
!queryFunction->isDistinctAggregate(), QRLogicException,
"DISTINCT aggregate fucttions cannot be rolled up.");
switch (queryFunction->getAggregateFunc())
{
case QRFunction::AFT_SUM:
case QRFunction::AFT_MIN:
case QRFunction::AFT_MAX:
case QRFunction::AFT_ONEROW:
case QRFunction::AFT_ANY_TRUE:
// SUM, MIN and MAX, as well as ONEROW and ANY_TRUE
// use the same aggregate function in the rollup.
aggrName = queryFunction->getFunctionName();
aggrType = queryFunction->getAggregateFunc();
break;
case QRFunction::AFT_COUNT:
case QRFunction::AFT_COUNTSTAR:
// COUNT needs SUM to be calculated in the rollup.
aggrName = "sum";
aggrType = QRFunction::AFT_SUM;
break;
case QRFunction::AFT_ONE_TRUE:
// ONE_TRUE needs ANY_TRUE to be calculated in the rollup.
aggrName = "any_true";
aggrType = QRFunction::AFT_ANY_TRUE;
break;
case QRFunction::AFT_ONE_ROW:
// ONE_ROW is the old implementation of ONEROW, and cannot be rolled up.
assertLogAndThrow1(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Aggregate function %s cannot be rolled up.", queryFunction->getFunctionName().data());
break;
}
// Create the new aggregate function.
QRFunctionPtr rollupFunction = new(heap_) QRFunction(ADD_MEMCHECK_ARGS(heap_));
rollupFunction->setFunctionName(aggrName);
rollupFunction->setAggregateFunc(aggrType);
// Use the MV column as its argument.
QRMVColumnPtr mvColumn = generateProvidedMvColumn(rewrite);
rollupFunction->addArgument(mvColumn);
return rollupFunction;
} // generateRollupAggregateFunction()
// ***************************************************************************
// ***************************************************************************
QRFunctionPtr MatchTest::generateRollupOverGroupingAggregateFunction(RewriteInstructionsItemPtr rewrite)
{
// This is the query aggregate function.
QRElementPtr queryElem = rewrite->getActualQueryElement();
QRFunctionPtr queryFunction;
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
queryElem->getElementType() == ET_Function, QRLogicException,
"Expecting a QRFunction in generateRollupOverGroupingAggregateFunction().");
queryFunction = queryElem->downCastToQRFunction();
// Which aggregate should we use?
NAString aggrName(heap_);
QRFunction::AggregateFunctionType aggrType = QRFunction::AFT_NONE;
// Create the new aggregate function.
QRFunctionPtr rollupFunction = new(heap_) QRFunction(ADD_MEMCHECK_ARGS(heap_));
NABoolean needCountStarArgument = FALSE;
NABoolean needActualArgument = TRUE;
switch (queryFunction->getAggregateFunc())
{
case QRFunction::AFT_COUNTSTAR:
// COUNT(*) translates into SUM(count_star)
aggrType = QRFunction::AFT_SUM;
aggrName = "sum";
needCountStarArgument = TRUE;
needActualArgument = FALSE;
break;
case QRFunction::AFT_COUNT:
// COUNT(g) translates into COUNT( CASE WHEN g IS NULL THEN 0 ELSE count_star END )
// Let the analyzer construct this expression, just instruct it to do it
// by using this special aggregate type.
aggrType = QRFunction::AFT_COUNT_ON_GROUPING;
aggrName = "count on grouping";
needCountStarArgument = TRUE;
break;
case QRFunction::AFT_SUM:
// SUM(g) translates into SUM(g*count_star)
// Let the analyzer construct this expression, just instruct it to do it
// by using this special aggregate type.
aggrType = QRFunction::AFT_SUM_ON_GROUPING;
aggrName = "sum on grouping";
needCountStarArgument = TRUE;
break;
case QRFunction::AFT_MIN:
case QRFunction::AFT_MAX:
// MIN(g) and MAX(g) are regular MIN/MAX.
case QRFunction::AFT_COUNT_DISTINCT:
// COUNT(DISTINCT g) can be rolled up, and stays the same.
case QRFunction::AFT_SUM_DISTINCT:
// SUM(DISTINCT g) can be rolled up, and stays the same.
aggrName = queryFunction->getFunctionName();
aggrType = queryFunction->getAggregateFunc();
break;
}
rollupFunction->setFunctionName(aggrName);
rollupFunction->setAggregateFunc(aggrType);
if (needActualArgument)
{
QRElementPtr queryArg = queryFunction->getArguments()[0]->getReferencedElement();
if (queryArg->getElementType() == ET_Column ||
queryArg->getElementType() == ET_JoinPred )
{
// The argument to the aggregate function is a simple column.
// Use the MV column as its argument.
RewriteInstructionsItemPtr colRewrite = rewrite->getSubElements()->providedInputs_[0];
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
colRewrite != NULL, QRLogicException,
"Expecting a rewrite of the aggregate function argument.");
QRMVColumnPtr mvColumn = generateProvidedMvColumn(colRewrite);
rollupFunction->addArgument(mvColumn);
}
else
{
candidate_->logMVWasDisqualified("Can't handle aggr(expr(g)) yet, for rollup over grouping column.");
deletePtr(rollupFunction);
return NULL;
}
}
if (needCountStarArgument)
{
// Find the MV COUNT(*) column.
QROutputPtr countStarOutput = candidate_->getMvDetails()->getCountStar();
if (countStarOutput == NULL)
{
candidate_->logMVWasDisqualified("the MV does not have a COUNT(*) column.");
deletePtr(rollupFunction);
return NULL;
}
// Construct the COUNT(*) MVColumn element
QRMVColumnPtr countStarMVCol = new (heap_) QRMVColumn(ADD_MEMCHECK_ARGS(heap_));
// Get the name of the MV column from the MV output element.
countStarMVCol->setMVColName(countStarOutput->getName());
// Add it as the first argument.
rollupFunction->addArgument(countStarMVCol);
}
return rollupFunction;
} // generateRollupOverGroupingAggregateFunction()
//========================================================================
// Class MatchOutput
//========================================================================
// ***************************************************************************
// Do the initial Pass 1 analysis of all output columns/expressions.
// For each output element, this method finds the actual column or expression
// And calls the next methods to match it.
// An output element can fall into one of the following options:
// 1. A QRColumn element, or a reference to one.
// Call matchColumn().
// 2. An expression (QRExpr) or a reference to one.
// Call matchExpression().
// 3. A reference to an equality set.
// Call matchEqualitySet().
// When any of these methods rturn FALSE, it means that the output element
// cannot be provided using this MV, so the MV is disqualified, and its
// matching is aborted.
// ***************************************************************************
NABoolean MatchOutput::matchPass1()
{
// Loop over the outputs in the Query descriptor
const DescriptorDetailsPtr queryDetails = candidate_->getQueryDetails();
const QRJBBPtr queryJbb = candidate_->getQueryJbb();
const QROutputListPtr outputList = queryJbb->getOutputList();
CollIndex outputListEntries = (outputList ? outputList->entries() : 0);
for (CollIndex i=0; i<outputListEntries; i++)
{
RewriteInstructionsItemPtr rewrite = NULL;
const QROutputPtr output = (*outputList)[i];
QRElementPtr element = output->getOutputItem()->getReferencedElement();
// Now check what is this element that we need to match.
switch (element->getElementType())
{
case ET_Column:
{
// Case 1: A QRColumn element.
const QRColumnPtr col = element->downCastToQRColumn();
rewrite = matchColumn(col, FALSE);
break;
}
case ET_Expr:
{
// Case 2: A QRExpr element.
const QRExprPtr expr = element->downCastToQRExpr();
rewrite = matchExpression(expr);
if (rewrite != NULL) // && rewrite->isFinal())
{
// For output expressions, add the input columns to the output list as well.
addInputsToOutputList(rewrite);
}
break;
}
case ET_JoinPred:
{
// Case 3 - A join predicate (Equality set).
const QRJoinPredPtr joinPredElement = element->downCastToQRJoinPred();
rewrite = matchEqualitySet(joinPredElement);
break;
}
default:
{
// Output elements can only be QRColumn, QRExpr or referencing QRJoinPred.
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Unexpected output list element.");
return FALSE;
}
} // switch on element type.
// Lets check the matching results
if (rewrite == NULL)
{
// Reject the MV.
return FALSE;
}
else
{
// Matching successful. Add the rewrite instructions to the list.
rewrite->setQueryElement(output);
addRewriteInstructions(rewrite);
}
} // for loop on query outputs
return TRUE;
} // MatchOutput::matchPass1()
// ***************************************************************************
// ***************************************************************************
NABoolean MatchOutput::matchPass2OnElement(RewriteInstructionsItemPtr pending)
{
if (pending->getActualQueryElement()->getElementType() != ET_Expr)
{
// simple column outputs are not supposed to get here.
// TBD: Change to assertLogAndThrow()
candidate_->logMVWasDisqualified1("Column output %s got to Pass 2.",
pending->getQueryElement()->getID());
return FALSE;
}
if (pending->getActualQueryElement()->downCastToQRExpr()->getExprRoot()->containsAnAggregate())
{
// Handle rollup aggregates
return matchAggregateExpressions(pending);
}
else
{
candidate_->logMVWasDisqualified1("Output expression %s cannot be Provided.",
pending->getQueryElement()->getID());
return FALSE;
}
}
// ***************************************************************************
// Rollup expressions means that both the MV and the query are aggregates,
// and the query grouping list is a subset of the MV's grouping list.
// In this case output expressions are never Provided, because even if the
// two expressions look identical (for example SUM(a) and SUM(a)), they really
// are not.
// 1. Check if the expression is a supported aggregate function:
// COUNT(*), COUNT(a), SUM(a), AVG(a), MIN(a), MAX(a), as well as
// STDDEV(a), STDDEV(a,b) and VARIANCE(a).
// 2. If so - check if the MV Provides the needed aggregates to calculate
// the required aggregates result. Rewrite using the MV aggregate column.
// 3. Maybe this expression is using supported aggregate functions, so its
// more complex but still rewriteable.
// ***************************************************************************
NABoolean MatchOutput::matchAggregateExpressions(RewriteInstructionsItemPtr pending)
{
QROutputPtr queryOutputExpr = pending->getQueryElement()->downCastToQROutput();
QRExprPtr queryExpr = pending->getActualQueryElement()->downCastToQRExpr();
QRExplicitExprPtr treeExpr = queryExpr->getExprRoot();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
treeExpr->containsAnAggregate(), QRLogicException,
"Expecting Rollup aggreate functions.");
RewriteInstructionsItemPtr rewrite = NULL;
// 1. Is the top level an aggregate function?
if (treeExpr->getElementType() == ET_Function &&
treeExpr->downCastToQRFunction()->isAnAggregate())
{
// Look for the corresponding aggregate function as an MV output.
RewriteInstructionsItemPtr funcRewrite =
findAggregateFunction(queryExpr->getExprRoot()->downCastToQRFunction());
if (funcRewrite == NULL)
return FALSE;
// The result code is set to NOTPROVIDED. Set it to PROVIDED as a sub-element.
funcRewrite->setResultCode(RC_PROVIDED);
// We got a final rewrite. However, its not really provided, because
// of the rollup. Change it to a NotProvided.
rewrite = new(heap_)
RewriteInstructionsItem(queryOutputExpr, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG, QRLogicException,
"Simple aggreate functions don't get here - they are Provided.");
rewrite->addSubElement(funcRewrite);
rewrite->setSecondaryResultCode(RC_ROLLUP_EXPR);
}
else
{
// 2. Use a visitor to find aggregate functions, and solve any that are found.
rewrite = findInternalAggregateFunctions(treeExpr);
if (rewrite == NULL)
return FALSE;
rewrite->setQueryElement(queryOutputExpr);
}
// And Put it in the Final list.
addRewriteInstructions(rewrite);
deletePtr(pending);
return TRUE;
} // matchAggregateExpressions()
// ***************************************************************************
// Create a new QROutput element for the result descriptor.
// ***************************************************************************
QROutputPtr MatchOutput::createNewResultElement(RewriteInstructionsItemPtr rewrite)
{
// Construct the new element
QROutputPtr outputElement = new(heap_) QROutput(ADD_MEMCHECK_ARGS(heap_));
// Set the result attribute.
outputElement->setResult(rewrite->getDescriptorResultCode());
// Set the ref attribute.
outputElement->setRef(rewrite->getQueryElement()->getID());
return outputElement;
}
// ***************************************************************************
// Create a new QROutput element for the result descriptor, set to Provided.
// ***************************************************************************
NABoolean MatchOutput::generateProvidedElement(QRCandidatePtr resultDesc,
RewriteInstructionsItemPtr rewrite)
{
// Create the new Output element, Add the MVColumn to it.
QROutputPtr outputElement = createNewResultElement(rewrite);
QRMVColumnPtr mvColumn = generateProvidedMvColumn(rewrite);
outputElement->setOutputItem(mvColumn);
// Add it to the result descriptor.
resultDesc->getOutputList()->addItem(outputElement);
return TRUE;
}
// ***************************************************************************
// Create a new QROutput element for the result descriptor, set to NotProvided.
// ***************************************************************************
NABoolean MatchOutput::generateNotProvidedElement(QRCandidatePtr resultDesc, RewriteInstructionsItemPtr rewrite)
{
ResultCode secondaryRC = rewrite->getSecondaryResultCode();
QROutputPtr outputElement = NULL;
switch (secondaryRC)
{
case RC_BACKJOIN:
{
const NAString& id = rewrite->getQueryElement()->getID();
QRColumnPtr rColumn = generateNotProvidedColumn(rewrite);
outputElement = new(heap_) QROutput(ADD_MEMCHECK_ARGS(heap_));
outputElement->setOutputItem(rColumn);
outputElement->setRef(id);
outputElement->setResult(QRElement::NotProvided);
}
break;
case RC_ROLLUP:
{
outputElement = generateRollupAggregateOutput(rewrite);
if (outputElement == NULL)
{
candidate_->logMVWasDisqualified1("Output expression %s cannot be Provided.",
rewrite->getQueryElement()->getID().data());
return FALSE;
}
}
break;
case RC_INPUTS_PROVIDED:
case RC_ROLLUP_EXPR:
case RC_AGGR_EXPR:
{
QRExprPtr exprElement = generateAggregateExpressionOutput(rewrite);
if (exprElement == NULL)
{
candidate_->logMVWasDisqualified1("Output expression %s cannot be Provided.",
rewrite->getQueryElement()->getID().data());
return FALSE;
}
else
{
outputElement = createNewResultElement(rewrite);
outputElement->setOutputItem(exprElement);
}
}
break;
default:
// Not implemented yet.
candidate_->logMVWasDisqualified1("NotProvided output list is not fully implemented for %s.",
rewrite->getResultString(secondaryRC));
return FALSE;
}
// Add the new Output element to the result descriptor.
resultDesc->getOutputList()->addItem(outputElement);
return TRUE;
} // MatchOutput::generateNotProvidedElement()
// ***************************************************************************
// ***************************************************************************
QROutputPtr MatchOutput::generateRollupAggregateOutput(RewriteInstructionsItemPtr rewrite)
{
// Generate the correct aggregate function to rollup over the MV aggregate function.
QRFunctionPtr rollupFunction = generateRollupAggregateFunction(rewrite);
// Create the expression on top of it.
QRExprPtr expr = new(heap_) QRExpr(FALSE, ADD_MEMCHECK_ARGS(heap_));
expr->setExprRoot(rollupFunction);
expr->setRef(rewrite->getActualQueryElement()->getID());
QROutputPtr outputElement = createNewResultElement(rewrite);
outputElement->setOutputItem(expr);
return outputElement;
} // generateRollupAggregateOutput()
// ***************************************************************************
// Are the rewrite instructions for an Outside column.
// ***************************************************************************
NABoolean MatchOutput::isFromOutside(RewriteInstructionsItemPtr rewrite)
{
const QRElementPtr queryElem = rewrite->getActualQueryElement()->getReferencedElement();
if (queryElem->getElementType() == ET_Column)
{
const QRColumnPtr queryCol = queryElem->downCastToQRColumn();
const NAString& queryTableID = queryCol->getTableID();
const NAString* extraHubID;
if (isOutsideColumn(queryCol, extraHubID))
return TRUE;
}
return FALSE;
}
// ***************************************************************************
// Remove firstRewrite from the list of final rewrite instructions, and
// replace it with newRewrite.
// ***************************************************************************
void MatchOutput::switchRewrites(RewriteInstructionsItemPtr newRewrite,
RewriteInstructionsItemPtr firstRewrite,
const NAString& MVColName)
{
const NAString& id = newRewrite->getActualQueryElement()->getID();
const NAString& firstID = firstRewrite->getActualQueryElement()->getID();
OutputRewriteInstructionsByID_.remove(&firstID);
OutputRewriteInstructionsByID_.insert(&id, newRewrite);
if (MVColName != "")
{
OutputRewriteInstructionsByMvColName_.remove(&MVColName);
OutputRewriteInstructionsByMvColName_.insert(&MVColName, newRewrite);
}
getListOfFinalInstructions().remove(firstRewrite);
getListOfFinalInstructions().insert(newRewrite);
}
// ***************************************************************************
// Override the default behaviour in order to avoid duplicates.
// ***************************************************************************
void MatchOutput::addRewriteInstructions(RewriteInstructionsItemPtr rewrite)
{
if (rewrite->isFinal() == FALSE)
{
// Don't bother with intermediate results.
MatchTest::addRewriteInstructions(rewrite);
}
else
{
// For final results, check if the ID of the query element is already
// stored in the hash table.
const NAString& id = rewrite->getActualQueryElement()->getID();
const QRElementPtr mvOutput = rewrite->getMvElement();
QRElementPtr mvElem = NULL;
static NAString empty = "";
NAString& MVColName = empty;
if (mvOutput)
{
mvElem = mvOutput->downCastToQROutput()->getOutputItem()->getReferencedElement();
if (mvElem->getElementType() == ET_Column)
MVColName = mvElem->downCastToQRColumn()->getColumnName();
}
// If we need to avoid this ID, ignore it.
if (OutputsToAvoidByID_.contains(&id) == TRUE)
return;
if (OutputRewriteInstructionsByID_.contains(&id) == FALSE)
{
// Its a new entry
if (mvOutput && mvOutput->getElementType() == ET_Output)
{
if (OutputRewriteInstructionsByMvColName_.contains(&MVColName))
{
// There already is an Output element for this MV column.
// If the new rewrite is for an Outside column, ignore it and leave the existing one.
if (isFromOutside(rewrite))
{
deletePtr(rewrite);
return;
}
// Find the existing entry
RewriteInstructionsItemPtr firstRewrite =
OutputRewriteInstructionsByMvColName_.getFirstValue(&MVColName);
// If the existing rewrite is on an Outside column, and the new one isn't,
// Switch between them.
if (isFromOutside(firstRewrite))
{
switchRewrites(rewrite, firstRewrite, MVColName);
deletePtr(firstRewrite);
return;
}
}
OutputRewriteInstructionsByMvColName_.insert(&MVColName, rewrite);
}
// Insert it to the hash table, and keep it.
OutputRewriteInstructionsByID_.insert(&id, rewrite);
MatchTest::addRewriteInstructions(rewrite);
}
else
{
// Its a duplicate (by query ID)
RewriteInstructionsItemPtr firstRewrite = OutputRewriteInstructionsByID_.getFirstValue(&id);
// Prefer to reference an output query element.
const NAString& firstID = firstRewrite->getQueryElement()->getID();
const NAString& secondID = rewrite->getQueryElement()->getID();
if (firstID.data()[0] != 'O' && secondID.data()[0] == 'O')
{
// Switch between them.
switchRewrites(rewrite, firstRewrite, MVColName);
deletePtr(firstRewrite);
}
else
{
deletePtr(rewrite);
}
}
}
} // MatchOutput::addRewriteInstructions()
// ***************************************************************************
// Override the default behaviour in order to avoid duplicates.
// ***************************************************************************
void MatchOutput::addOutputToAvoid(const NAString& id)
{
// Add it to the list of IDs to avoid.
OutputsToAvoidByID_.insert(&id, &id);
// Check if we already have it in the output list.
if (OutputRewriteInstructionsByID_.contains(&id) == TRUE)
{
// Yes, its in there. Remove it.
RewriteInstructionsItemPtr existingRewrite = OutputRewriteInstructionsByID_.getFirstValue(&id);
getListOfFinalInstructions().remove(existingRewrite);
OutputRewriteInstructionsByID_.remove(&id);
deletePtr(existingRewrite);
}
}
//========================================================================
// Class MatchRangePredicates
//========================================================================
// ***************************************************************************
// Run the Pass 1 algorithm of range predicate test.
// 1. First do the bitmap test, to see if we can quickly disqualify the MV.
// 2. Check the predicates one by one.
// For each query range predicate
// If the range predicate is on an expression
//
// Else (the range pred is on a single column or equality set)
// Get the column
// Find the MV predicates on this column through the BaseTableDetails.
// If no MV predicates - this column PASSED as a final NotProvided.
// If MV predicates exist - Mark for Pass 2.
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchRangePredicates::matchPass1()
{
if (matchPredicateBitmaps() == FALSE)
return FALSE;
const JBBDetailsPtr mvJbbDetails = candidate_->getMvDetails()->getJbbDetails();
const QRRangePredListPtr queryRangePredList = candidate_->getQueryJbb()->getHub()->getRangePredList();
if (queryRangePredList==NULL || queryRangePredList->getList().isEmpty())
{
if (!mvJbbDetails->hasNoRangePredicates())
{
candidate_->logMVWasDisqualified("the MV has range predicates, and the query does not.");
return FALSE;
}
else
{
// No range predicates in both the MV and the query.
// Nothing to do.
return TRUE;
}
}
// Copy the list of MV range predicates.
// Any matched predicate will be removed from this list, so we can check
// that no MV preds were left out.
if (!mvJbbDetails->hasNoRangePredicates())
mvFullPredList_.insert(mvJbbDetails->getJbbDescriptor()->getHub()->getRangePredList()->getList());
// The query has predicates, so we need to check them one-by-one.
const RangePredPtrList& queryRangePreds = queryRangePredList->getList();
for (CollIndex i=0; i<queryRangePreds.entries(); i++)
{
//RewriteInstructionsItemPtr rewrite = NULL;
const QRRangePredPtr queryRangePred = queryRangePreds[i];
QRElementPtr rangeItem = queryRangePred->getRangeItem()->getReferencedElement();
ElementType rangeElemType = rangeItem->getElementType();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
( rangeElemType == ET_Column ||
rangeElemType == ET_JoinPred ||
rangeElemType == ET_Expr ),
QRLogicException,
"Expecting range items to be columns, equality sets or expressions.");
if (rangeElemType == ET_Expr)
{
// Do the matching work for a range pred on an expression.
if (matchPredOnExpr(queryRangePred) == FALSE)
return FALSE;
}
else if (rangeElemType == ET_Column)
{
const QRColumnPtr rangeColumn = rangeItem->downCastToQRColumn();
// Do the matching work for a range pred on a Column.
if (matchPredOnColumn(queryRangePred, rangeColumn) == FALSE)
return FALSE;
}
else
{
// Range item must be a join pred.
QRJoinPredPtr joinPred = rangeItem->downCastToQRJoinPred();
if (matchPredOnEqualitySet(queryRangePred, joinPred) == FALSE)
return FALSE;
} // Range pred is on join predicate.
} // for on range predicates.
if (mvFullPredList_.entries() > 0)
{
candidate_->logMVWasDisqualified1("MV range predicate %s was not matched by the query.",
mvFullPredList_[0]->getID());
return FALSE;
}
return TRUE;
} // MatchRangePredicates::matchPass1()
// ***************************************************************************
// Match a range predicate on an equality set.
// The algorithm is:
// 1. For each column in the equality set:
// 1.1 If its an Outside column, ignore it (continue with the loop).
// 1.2 Otherwise call matchPredOnColumn() on it, return TRUE if it was matched.
// 2. If all the columns were Outside - return TRUE.
// ***************************************************************************
NABoolean MatchRangePredicates::matchPredOnEqualitySet(const QRRangePredPtr queryRangePred, const QRJoinPredPtr joinPred)
{
const ElementPtrList& equalitySet = joinPred->getEqualityList();
CollIndex maxEntries = equalitySet.entries();
NABoolean insideColFound = FALSE;
for (CollIndex i=0; i<maxEntries; i++)
{
QRElementPtr eqMember = equalitySet[i]->getReferencedElement();
if (eqMember->getElementType() != ET_Column)
continue;
QRColumnPtr eqColumn = eqMember->downCastToQRColumn();
const NAString* extraHubID = NULL;
if (isOutsideColumn(eqColumn, extraHubID))
continue;
insideColFound = TRUE;
if (matchPredOnColumn(queryRangePred, eqColumn))
return TRUE;
}
// If all eqality set columns are Outside, this range predicate can be ignored.
return !insideColFound;
}
// ***************************************************************************
// ***************************************************************************
NABoolean MatchRangePredicates::matchPredOnExpr(const QRRangePredPtr queryRangePred)
{
QRElementPtr rangeItem = queryRangePred->getRangeItem()->getReferencedElement();
const QRExprPtr queryRangeExpr = rangeItem->downCastToQRExpr();
const ElementPtrList& queryInputColumns = queryRangeExpr->getInputColumns(heap_);
RewriteInstructionsItemPtr rewrite = NULL;
// If this is an aggregate expression in a rollup query
// don't bother matching the expression text.
// Go streight to Pass 2.
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG &&
queryRangeExpr->getExprRoot()->containsAnAggregate() )
{
rewrite = new(heap_)
RewriteInstructionsItem(queryRangePred, NULL, RC_ROLLUP_EXPR, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
}
else
{
const NAString& predText = queryRangeExpr->getExprText();
RangePredPtrList* mvPredsOnExpr = candidate_->getMvDetails()->getJbbDetails()->getRangePredsOnExpression(predText);
if (mvPredsOnExpr != NULL)
{
// Both MV and query have a range pred using this text. Now match the input columns.
for (CollIndex j = 0; j<mvPredsOnExpr->entries(); j++)
{
QRRangePredPtr mvRangePred = (*mvPredsOnExpr)[j];
const QRExprPtr mvRangeExpr = mvRangePred->getRangeItem()->downCastToQRExpr();
if (matchExpressionInputs(queryInputColumns, mvRangeExpr->getInputColumns(heap_)) == FALSE)
continue; // Try next MV range pred with same text but different input columns.
else
{
// Input columns match. Now we need to match the range itself. Do that in Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(queryRangePred, mvRangePred, RC_MATCH_RANGE, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
mvFullPredList_.remove(mvRangePred);
break; // We found a match, we can stop looking.
}
}
}
}
if (rewrite == NULL)
{
// The MV has no range predicate using this text, so we need to provide rewrite instructions.
// We need to verify that the pred input columns are available as an MV output.
if ( candidate_->getAggregateMatchingType() == AMT_MAV_AQ_MG &&
queryRangeExpr->getExprRoot()->containsAnAggregate() )
{
// This is an aggregate query on a matching MAV,
// Try to match aggregate functions, in Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(queryRangePred, NULL, RC_AGGR_EXPR, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
}
else
{
// This is on an MJV. Try to find the input columns.
rewrite = findExpressionInputs(queryInputColumns, queryRangePred);
if (rewrite &&
rewrite->isFinal() &&
rewrite->getSecondaryResultCode() == RC_INPUTS_PROVIDED &&
queryRangeExpr->getExprRoot()->containsAnAggregate())
{
// A HAVING predicate on an MJV
// Add the expression inputs to the output list.
addInputsToOutputList(rewrite);
// And then throw away the Range predicate itself.
deletePtr(rewrite);
return TRUE;
}
}
}
if (rewrite == NULL)
{
candidate_->logMVWasDisqualified1("Query range pred %s cannot be provided.", queryRangePred->getID().data());
return FALSE; // No match found for this residual pred. Disqualify the MV candidate.
}
addRewriteInstructions(rewrite);
return TRUE;
} // MatchRangePredicates::matchPredOnExpr()
// ***************************************************************************
// ***************************************************************************
NABoolean MatchRangePredicates::matchPredOnColumn(const QRRangePredPtr queryRangePred,
const QRColumnPtr rangeColumn)
{
QRRangePredPtr mvRangePred = NULL;
RewriteInstructionsItemPtr rewrite = NULL;
// Find the corresponding MV range predicate (if any).
if (candidate_->getMvDetails()->getJbbDetails()->hasNoRangePredicates() == FALSE)
{
// Find the corresponding MV table (if any)
BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(rangeColumn->getTableID(), FALSE);
if (baseTable == NULL)
{
// This is an Outside table.
// We can ignore this range predicate.
return TRUE;
}
// Get the MV range pred on this column.
mvRangePred = baseTable->getRangeColumnPredicatesFor(rangeColumn->getColIndex());
}
if (mvRangePred == NULL)
{
// The MV has no predicate on this column, so we need to provide rewrite instructions.
// We need to verify that the range column is available as an MV output.
RewriteInstructionsItemPtr columnRewrite = matchColumn(rangeColumn, FALSE);
// The range column is not available - disqualify the MV.
if (columnRewrite == NULL)
return FALSE;
// The primary result code is NotPrvided.
// Its final if the column rewrite is Final.
rewrite = new(heap_)
RewriteInstructionsItem(queryRangePred, NULL, RC_NOTPROVIDED, columnRewrite->getResultStatus(), ADD_MEMCHECK_ARGS(heap_));
rewrite->addSubElement(columnRewrite);
}
else
{
// Both MV and query have a range pred on this column. We will match them in Pass 2.
mvFullPredList_.remove(mvRangePred);
rewrite = new(heap_)
RewriteInstructionsItem(queryRangePred, mvRangePred, RC_MATCH_RANGE, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
}
addRewriteInstructions(rewrite);
return TRUE;
} // MatchRangePredicates::matchPredOnColumn()
// ***************************************************************************
// Check if the query predicate bitmaps are a superset of the MV predicate bitmaps.
// This method checks the bitmaps for both range and residual predicates,
// because the algorithm is the same for both, and it doesn't make sense to
// repeat the entire loop for the residual predicates in the MatchResidualPredicates
// class for the sake of organized code.
// If the query range predicate bitmap is not a superset of the MV bitmap for
// all the involved tables, then the MV has predicates that are not covered by
// the query, and is very quickly disqualified.
// Return TRUE if the MV passed, and FALSE if it is disqualified.
// ***************************************************************************
NABoolean MatchRangePredicates::matchPredicateBitmaps()
{
JBBDetailsPtr mvJbbDetails = candidate_->getMvDetails()->getJbbDetails();
// If the MV has no range and residual preds, there is no need to check the bitmaps.
if (mvJbbDetails->hasNoRangePredicates() && mvJbbDetails->hasNoResidualPredicates())
return TRUE;
// Iterate on the tables in the join sub-graph, which correspond to the MV hub tables.
QRJoinSubGraphPtr subGraph = candidate_->getJbbSubset()->getSubGraph();
for( subGraph->reset(); subGraph->hasNext(); subGraph->advance() )
{
// Get the table query descriptor ID, and from it the QRTable pointer.
const JoinGraphTablePtr queryJoinGraphTable = subGraph->getCurrent();
const NAString& queryTableID = queryJoinGraphTable->getID();
const QRTablePtr queryTable = candidate_->getQueryDetails()->getElementForID(queryTableID)->downCastToQRTable();
// Get the query predicate bitmaps
const XMLBitmap& queryRangeBits = queryTable->getRangeBits();
const XMLBitmap& queryResidualBits = queryTable->getResidualBits();
// Find the MV table ID using the MVMemo mapping.
BaseTableDetailsPtr mvTableDetails = candidate_->getMvTableForQueryID(queryTableID);
// Get the MV predicate bitmaps
const XMLBitmap& mvRangeBits = mvTableDetails->getRangeBits();
const XMLBitmap& mvResidualBits = mvTableDetails->getResidualBits();
// Perform the actual test.
// Any failure disqualifies the MV.
if (queryRangeBits.contains(mvRangeBits) == FALSE)
{
candidate_->logMVWasDisqualified1("Table %s failed on range bitmap.", queryTable->getTableName().data());
return FALSE;
}
if (queryResidualBits.contains(mvResidualBits) == FALSE)
{
candidate_->logMVWasDisqualified1("Table %s failed on residual bitmap.", queryTable->getTableName().data());
return FALSE;
}
}
return TRUE;
} // MatchRangePredicates::matchPredicateBitmaps()
// ***************************************************************************
// Run the Pass 2 algorithm of range predicate test.
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchRangePredicates::matchPass2OnElement(RewriteInstructionsItemPtr pending)
{
const QRRangePredPtr queryPred = pending->getQueryElement()->downCastToQRRangePred();
if (pending->getResultCode() == RC_AGGR_EXPR ||
pending->getResultCode() == RC_ROLLUP_EXPR)
{
QRElementPtr treeElem =
queryPred->getRangeItem()->getReferencedElement()->downCastToQRExpr()
->getExprRoot()->getReferencedElement();
QRExplicitExprPtr treeExpr = static_cast<QRExplicitExprPtr>(treeElem);
// Use a visitor to find aggregate functions, and solve any that are found.
RewriteInstructionsItemPtr exprRewrite = findInternalAggregateFunctions(treeExpr);
if (exprRewrite == NULL)
{
candidate_->logMVWasDisqualified1("Range HAVING predicate %s is not Provided by the MV.",
queryPred->getID().data());
return FALSE;
}
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(queryPred->getRangeItem(), NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
exprRewrite->setQueryElement(queryPred->getRangeItem());
rewrite->addSubElement(exprRewrite);
rewrite->setQueryElement(queryPred);
rewrite->setSecondaryResultCode(pending->getResultCode());
addRewriteInstructions(rewrite);
deletePtr(pending);
return TRUE;
}
if (pending->getResultCode() == RC_NOTPROVIDED)
{
if (pending->getSubElements()->indirectInputs_.entries() > 0)
{
if (queryPred->getRangeItem()->getElementType() == ET_Expr)
{
// The range expression may be an output of the MV,
QRExprPtr rangeExpr = queryPred->getRangeItem()->downCastToQRExpr();
RewriteInstructionsItemPtr exprRewrite = findMatchingMvExpression(rangeExpr->getExprRoot());
if (exprRewrite == NULL)
{
// The range expression cannot be provided. Disqualify the MV.
// logMVWasDisqualified1() was already called by matchColumn();
return NULL;
}
else if (exprRewrite->getResultStatus() == RS_FINAL &&
exprRewrite->getResultCode() == RC_PROVIDED)
{
// The range expression is an output of the MV.
// Clear the indirect sub-elements first.
RewriteInstructionsItemList& indirect = pending->getSubElements()->indirectInputs_;
for (CollIndex i=0; i<indirect.entries(); i++)
deletePtr(indirect[i]);
indirect.clear();
// Now add the new rewrite instructions.
exprRewrite->setQueryElement(rangeExpr);
pending->addSubElement(exprRewrite);
}
else
{
candidate_->logMVWasDisqualified1("Range expression %s is not Provided by the MV.",
queryPred->getID().data());
deletePtr(exprRewrite);
return NULL;
}
}
}
// This must have been a back join or extra-hub table.
// If we got here, it means its OK.
pending->setResultStatus(RS_FINAL);
addRewriteInstructions(pending);
return TRUE;
}
assertLogAndThrow1(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
pending->getResultCode() == RC_MATCH_RANGE, QRLogicException,
"Unexpected Pass 2 range pred result code: %s.", pending->getResultString());
// Get the RangeSpec object from the Query descriptor
const RangeSpec* queryRangeSpec = queryPred->getRangeSpec(heap_);
// Get the RangeSpec object from the MV descriptor
const QRRangePredPtr mvPred = pending->getMvElement()->downCastToQRRangePred();
const RangeSpec* mvRangeSpec = mvPred->getRangeSpec(heap_);
if (queryRangeSpec->isEqual(*mvRangeSpec))
{
// The range predicates are identical - return Provided.
pending->setResultCode(RC_PROVIDED);
}
else if (mvPred->getMustMatch() || queryPred->getMustMatch())
{
// The predicate must match exactly, but they don't
candidate_->logMVWasDisqualified1("The range predicate %s does not exactly match any MV predicate.",
queryPred->getID().data());
return FALSE;
}
else
{
// The predicates are not identical, but they don't need to be.
if (mvRangeSpec->subsumes(queryRangeSpec))
{
// The query predicate subsumes the MV predicate - NotProvided.
pending->setResultCode(RC_NOTPROVIDED);
// Last thing to check - is the range column provided as an output?
// Otherwise we can't use the range pred on the MV...
QRElementPtr rangeItem = queryPred->getRangeItem()->getReferencedElement();
RewriteInstructionsItemPtr columnRewrite = NULL;
NABoolean wasAdded = FALSE;
switch(rangeItem->getElementType())
{
case ET_Column:
{
QRColumnPtr rangeCol = rangeItem->downCastToQRColumn();
columnRewrite = matchColumn(rangeCol, FALSE);
}
break;
case ET_JoinPred:
{
QRJoinPredPtr rangeJoin = rangeItem->downCastToQRJoinPred();
columnRewrite = matchEqualitySet(rangeJoin);
}
break;
case ET_Expr:
{
// It must be a range predicate on an expression.
QRExprPtr rangeExpr = queryPred->getRangeItem()->downCastToQRExpr();
columnRewrite = matchExpression(rangeExpr);
if (columnRewrite != NULL)
{
// We can only use a Final rewrite using an MV output expression.
if (columnRewrite->isFinal() == FALSE)
{
deletePtr(columnRewrite);
columnRewrite = NULL;
candidate_->logMVWasDisqualified1("range predicate %s cannot be Provided.",
queryPred->getID().data());
return FALSE;
}
columnRewrite->setQueryElement(rangeExpr);
// Is the entire range expression Provided by the MV.
if (columnRewrite->getMvElement() == NULL)
{
// No - Only the input columns are Provided
pending->addSubElements(columnRewrite->getSubElements());
columnRewrite->getSubElements()->clear();
deletePtr(columnRewrite);
columnRewrite = NULL;
wasAdded = TRUE;
}
}
}
break;
} // end switch()
if (columnRewrite == NULL)
{
// The range column cannot be provided. Disqualify the MV.
// logMVWasDisqualified1() was already called by matchColumn();
if (!wasAdded)
return NULL;
}
else
{
pending->addSubElement(columnRewrite);
}
}
else
{
// The MV predicate does not subsume the query predicate - disqualify!
candidate_->logMVWasDisqualified1("The query range predicate %s is not subsumed by the corresponding MV predicate.",
queryPred->getID().data());
return FALSE;
}
}
// Insert the rewrite instruction to the final list.
pending->setResultStatus(RS_FINAL);
addRewriteInstructions(pending);
return TRUE;
} // MatchRangePredicates::matchPass2OnElement()
// ***************************************************************************
// Create a new QRRangePred element for the result descriptor.
// ***************************************************************************
QRRangePredPtr MatchRangePredicates::createNewResultElement(RewriteInstructionsItemPtr rewrite)
{
// Construct the new element
QRRangePredPtr rangeElement = new(heap_) QRRangePred(ADD_MEMCHECK_ARGS(heap_));
// Set the result attribute.
rangeElement->setResult(rewrite->getDescriptorResultCode());
// Set the ref attribute.
rangeElement->setRef(rewrite->getQueryElement()->getID());
return rangeElement;
}
// ***************************************************************************
// A Provided range predicate needs only the reference to the matched
// query element.
// ***************************************************************************
NABoolean MatchRangePredicates::generateProvidedElement(QRCandidatePtr resultDesc,
RewriteInstructionsItemPtr rewrite)
{
// Create the new Range element, mark it as Provided.
QRRangePredPtr rangeElement = createNewResultElement(rewrite);
// Add it to the result descriptor.
resultDesc->getRangePredList()->addItem(rangeElement);
return TRUE;
}
// ***************************************************************************
// A NotProvided range predicate includes the actual range predicate with
// rewrite instructions for the input columns.
// ***************************************************************************
NABoolean MatchRangePredicates::generateNotProvidedElement(QRCandidatePtr resultDesc,
RewriteInstructionsItemPtr rewrite)
{
// Create the new Output element, mark it as NotProvided, and ref the query range pred.
QRRangePredPtr rangeElement = createNewResultElement(rewrite);
QRElementPtr rangeItem = NULL;
// Get the rewrite for the range column.
if (rewrite->getSecondaryResultCode() == RC_AGGR_EXPR ||
rewrite->getSecondaryResultCode() == RC_ROLLUP_EXPR)
{
RewriteInstructionsItemPtr exprRewrite = rewrite->getSubElements()->notProvidedInputs_[0];
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
exprRewrite, QRLogicException,
"Expected a single NotProvided sub-element for aggregate range predicates.");
rangeItem = generateAggregateExpressionOutput(exprRewrite);
if (rangeItem == NULL)
{
candidate_->logMVWasDisqualified1("Range HAVING predicate %s cannot be Provided.",
rewrite->getQueryElement()->getID().data());
return FALSE;
}
else
{
// Set the result attribute.
rangeElement->setResult(rewrite->getDescriptorResultCode());
// Set the ref attribute.
rangeElement->setRef(rewrite->getQueryElement()->getID());
}
}
else
{
compositeMatchingResultsPtr subElements = rewrite->getSubElements();
if (rewrite->getSecondaryResultCode() == RC_NO_INPUTS)
{
// The range item is an expression with no input columns, such as (COUNT(*)
// Create a duplicate range item for it in the result descriptor.
subExpressionRewriteHash subExprHash(hashKey, INIT_HASH_SIZE_SMALL, FALSE, heap_);
QRRangePredPtr rangePred = rewrite->getQueryElement()->downCastToQRRangePred();
QRExprPtr rangeExpr = rangePred->getRangeItem()->downCastToQRExpr();
QRExplicitExprPtr ExprRoot = rangeExpr->getExprRoot()->deepCopyAndSwitch(subExprHash, heap_);
QRExprPtr resultExpr = new(heap_) QRExpr(FALSE, ADD_MEMCHECK_ARGS(heap_));
resultExpr->setRef(rangeExpr->getID());
resultExpr->setExprRoot(ExprRoot);
rangeItem = resultExpr;
}
else
{
if (subElements->providedInputs_.entries() == 1)
{
QRMVColumnPtr mvColumn = generateProvidedMvColumn(subElements->providedInputs_[0]);
rangeItem = mvColumn;
}
else if (subElements->notProvidedInputs_.entries() == 1)
{
QRColumnPtr backJoinColumn = generateNotProvidedColumn(subElements->notProvidedInputs_[0]);
rangeItem = backJoinColumn;
}
else if (subElements->backJoinInputs_.entries() == 1)
{
QRColumnPtr backJoinColumn = generateNotProvidedColumn(subElements->backJoinInputs_[0]);
rangeItem = backJoinColumn;
}
else if (subElements->outsideInputs_.entries() == 1)
{
QRColumnPtr outsideColumn = generateNotProvidedColumn(subElements->outsideInputs_[0]);
rangeItem = outsideColumn;
}
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
rangeItem != NULL, QRLogicException,
"Expecting either Provided, Outside or BackJoin inputs.");
}
}
// Add it to the result descriptor.
rangeElement->setRangeItem(rangeItem);
resultDesc->getRangePredList()->addItem(rangeElement);
return TRUE;
} // MatchRangePredicates::generateNotProvidedElement()
//***************************************************************************
// Used by workload analysis.
//***************************************************************************
QRRangePredPtr MatchRangePredicates::checkPredicate(QRRangePredPtr querySidePred)
{
QRRangePredPtr mvRangePred = NULL;
QRElementPtr rangeItem = querySidePred->getRangeItem()->getReferencedElement();
ElementType rangeElemType = rangeItem->getElementType();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
( rangeElemType == ET_Column ||
rangeElemType == ET_JoinPred ||
rangeElemType == ET_Expr ),
QRLogicException,
"Expecting range items to be columns, equality sets or expressions.");
if (rangeElemType == ET_Expr)
{
// Find all the MV range preds using the same expression text
const QRExprPtr queryRangeExpr = rangeItem->downCastToQRExpr();
const NAString& predText = queryRangeExpr->getExprText();
RangePredPtrList* mvPredsOnExpr = candidate_->getMvDetails()->getJbbDetails()->getRangePredsOnExpression(predText);
if (mvPredsOnExpr == NULL)
return NULL;
// Both MV and query have a range pred using this text. Now match the input columns.
const ElementPtrList& queryInputColumns = queryRangeExpr->getInputColumns(heap_);
NABoolean foundMatch = FALSE;
for (CollIndex j = 0; j<mvPredsOnExpr->entries(); j++)
{
mvRangePred = (*mvPredsOnExpr)[j];
const QRExprPtr mvRangeExpr = mvRangePred->getRangeItem()->downCastToQRExpr();
if (matchExpressionInputs(queryInputColumns, mvRangeExpr->getInputColumns(heap_)))
{
foundMatch = TRUE;
break; // Found a match
}
}
// Did not find a matching range pred on the same expression text and input columns.
if (!foundMatch)
return NULL;
}
else
{
// If its a join pred, get the first column.
QRColumnPtr rangeColumn = rangeItem->getFirstColumn();
// Find the corresponding MV range predicate (if any).
if (candidate_->getMvDetails()->getJbbDetails()->hasNoRangePredicates() == FALSE)
{
// Find the corresponding MV table (if any)
BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(rangeColumn->getTableID(), FALSE);
if (baseTable)
{
// Get the MV range pred on this column.
mvRangePred = baseTable->getRangeColumnPredicatesFor(rangeColumn->getColIndex());
}
}
} // if not expreesion.
// Is there a matching range predicate on the same column?
if (mvRangePred == NULL)
return NULL;
// OK, we get here when we found an MV side range predicate with a matching
// range item (column or expression).
// Now we need to match the range spec.
// For now, we only check for an exact match.
const RangeSpec* mvRangeSpec = mvRangePred->getRangeSpec(heap_);
const RangeSpec* queryRangeSpec = querySidePred->getRangeSpec(heap_);
if (queryRangeSpec->isEqual(*mvRangeSpec))
return mvRangePred;
else
return NULL;
}
//========================================================================
// Class MatchResidualPredicates
//========================================================================
//***************************************************************************
// Run the Pass 1 algorithm of residual predicate test.
// The bitmap test was already done by the range predicate code, so now
// check the predicates one by one.
// For each query residual predicate
// Using the predicate text, find any corresponding MV predicates.
// Match the input columns of those predicates.
// If a matching MV predicate was found
// Its a final Provided.
// Otherwise
// Check if the predicate input columns are provided by the MV.
// If they are blocked
// Disqualify the MV.
// If the are all Provided
// The predicate is a final NotProvided.
// Otherwise
// The predicate is an intermediate NotProvided, postpone to Pass 2.
//
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchResidualPredicates::matchPass1()
{
const JBBDetailsPtr mvJbbDetails = candidate_->getMvDetails()->getJbbDetails();
const QRResidualPredListPtr queryResidualPredList =
candidate_->getQueryJbb()->getHub()->getResidualPredList();
if (queryResidualPredList==NULL || queryResidualPredList->getList().isEmpty())
{
if (!mvJbbDetails->hasNoResidualPredicates())
{
candidate_->logMVWasDisqualified("it has residual predicates and the Query does not.");
return FALSE;
}
else
{
// No residual predicates in both the MV and the query.
// Nothing to do.
return TRUE;
}
}
ResidualPredPtrList usedDuplicates;
const ResidualPredPtrList& queryResidualPreds = queryResidualPredList->getList();
// Copy the list of MV residual predicates.
// Any matched predicate will be removed from this list, so we can check
// that no MV preds were left out.
if (!mvJbbDetails->hasNoResidualPredicates())
mvFullPredList_.insert(mvJbbDetails->getJbbDescriptor()->getHub()->getResidualPredList()->getList());
for (CollIndex i=0; i<queryResidualPreds.entries(); i++)
{
const QRExprPtr queryResidualPred = queryResidualPreds[i];
RewriteInstructionsItemPtr rewrite = matchResidualPredicate(queryResidualPred,
usedDuplicates);
if (rewrite == NULL)
{
candidate_->logMVWasDisqualified1("Query residual predicate %s was not matched by the query.",
queryResidualPred->getID());
return FALSE; // No match found for this residual pred. Disqualify the MV candidate.
}
else
{
if (rewrite->getResultCode() == RC_SKIP_ME)
deletePtr(rewrite);
else
addRewriteInstructions(rewrite); // We found a match, save the rewrite instructions.
}
} // for
if (mvFullPredList_.entries() > 0)
{
candidate_->logMVWasDisqualified1("MV residual predicate %s was not matched by the query.",
mvFullPredList_[0]->getID());
return FALSE;
}
return TRUE;
} // MatchResidualPredicates::matchPass1()
// ***************************************************************************
// ***************************************************************************
RewriteInstructionsItemPtr MatchResidualPredicates::matchResidualPredicate(const QRExprPtr queryResidualPred,
ResidualPredPtrList& usedDupMvPreds)
{
RewriteInstructionsItemPtr rewrite = NULL;
NABoolean isAggregateExpr = queryResidualPred->getExprRoot()->containsAnAggregate();
// If this is an aggregate expression in a rollup query
// don't bother matching the expression text.
// Go straight to Pass 2.
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_DG &&
isAggregateExpr )
{
// This is an aggregate query in a rollup query.,
// Try to match aggregate functions, in Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(queryResidualPred, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
}
const NAString& predText = queryResidualPred->getExprText();
// Get the list of MV residual predicates using the same expression text.
ResidualPredPtrList* mvPredList = candidate_->getMvDetails()->getJbbDetails()->getResidualPreds(predText);
if (mvPredList != NULL)
{
CollIndex mvPredCount = mvPredList->entries();
// Both MV and query have a residual pred using this text. Now match the input columns.
for (CollIndex j = 0; j<mvPredList->entries(); j++)
{
QRExprPtr mvResidualPred = (*mvPredList)[j];
if (matchExpressionInputs(queryResidualPred->getInputColumns(heap_),
mvResidualPred->getInputColumns(heap_)) == FALSE)
continue; // Try next MV residual pred with same text but different input columns.
else
{
// A residual pred on a join operand will appear in each node of an
// equality set. In some cases, this pred will be duplicated with a
// different value id, and we must avoid matching the same (with respect
// to text) mv pred to two distinct (but also with same text) query
// resid preds, or else one will be left unmatched in mvFullPredList_
// and result in a spurious disqualification for an unmatched pred.
if (mvPredCount > 1) // Only an issue when text matches multiple preds
{
if (usedDupMvPreds.contains(mvResidualPred))
// already matched this one, try another
continue;
else
// make sure we don't match this one again
usedDupMvPreds.insert(mvResidualPred);
}
mvFullPredList_.remove(mvResidualPred);
rewrite = new(heap_)
RewriteInstructionsItem(queryResidualPred, mvResidualPred, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
break; // We found a match, we can stop looking.
}
}
}
if (rewrite == NULL)
{
// The MV has no residual predicate using this text, so we need to provide rewrite instructions.
// We need to verify that the pred input columns are available as an MV output.
if ( candidate_->getAggregateMatchingType() == AMT_MAV_AQ_MG &&
isAggregateExpr )
{
// This is an aggregate query on a matching MAV,
// Try to match aggregate functions, in Pass 2.
rewrite = new(heap_)
RewriteInstructionsItem(queryResidualPred, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
}
else
{
// This is on an MJV. Try to find the input columns.
const ElementPtrList& queryInputElements =
queryResidualPred->getInputColumns(heap_);
if (queryInputElements.entries() > 0)
{
rewrite = findExpressionInputs(queryInputElements, queryResidualPred);
if (rewrite &&
rewrite->isFinal() &&
rewrite->getSecondaryResultCode() == RC_INPUTS_PROVIDED &&
isAggregateExpr)
{
// A HAVING predicate on an MJV
// Add the expression inputs to the output list.
addInputsToOutputList(rewrite);
// The residual predicate itself will be ignored
return rewrite;
}
}
else
{
// This predicate has no inputs.
// Mark it as NotProvided.
rewrite = new(heap_)
RewriteInstructionsItem(queryResidualPred, NULL, RC_NOTPROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
rewrite->setSecondaryResultCode(RC_NO_INPUTS);
}
}
}
return rewrite;
} // MatchResidualPredicates::matchResidualPredicate()
// ***************************************************************************
// Run the Pass 2 algorithm of residual predicate test.
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchResidualPredicates::matchPass2OnElement(RewriteInstructionsItemPtr pending)
{
if (pending->getResultCode() == RC_NOTPROVIDED)
{
// The residual expression may be a HAVING predicate.
QRExprPtr residExpr = pending->getQueryElement()->downCastToQRExpr();
QRExplicitExprPtr treeExpr = residExpr->getExprRoot();
// Use a visitor to find aggregate functions, and solve any that are found.
RewriteInstructionsItemPtr rewrite = findInternalAggregateFunctions(treeExpr);
if (rewrite == NULL)
{
candidate_->logMVWasDisqualified1("Residual expression %s is not Provided by the MV.",
residExpr->getID().data());
return FALSE;
}
rewrite->setQueryElement(residExpr);
addRewriteInstructions(rewrite);
deletePtr(pending);
return TRUE;
}
else
{
const char* reason = pending->getResultString();
candidate_->logMVWasDisqualified1("Residual preds got to Pass 2 on %s.", reason);
return FALSE;
}
}
// ***************************************************************************
// Create a new residual pred element (<Expr>) for the result descriptor.
// ***************************************************************************
QRExprPtr MatchResidualPredicates::createNewResultElement(RewriteInstructionsItemPtr rewrite)
{
// Construct the new element
QRExprPtr residualElement = new(heap_) QRExpr(TRUE, ADD_MEMCHECK_ARGS(heap_));
// Set the result attribute.
residualElement->setResult(rewrite->getDescriptorResultCode());
// Set the ref attribute.
residualElement->setRef(rewrite->getQueryElement()->getID());
return residualElement;
}
// ***************************************************************************
// A Provided residual predicate requires no additional work.
// ***************************************************************************
NABoolean MatchResidualPredicates::generateProvidedElement(QRCandidatePtr resultDesc,
RewriteInstructionsItemPtr rewrite)
{
// Create the new Output element, mark it as Provided.
QRExprPtr residualElement = createNewResultElement(rewrite);
// Add it to the result descriptor.
resultDesc->getResidualPredList()->addItem(residualElement);
return TRUE;
}
// ***************************************************************************
// A NotProvided residual predicate includes the actual residual predicate
// with rewrite instructions for the input columns.
// ***************************************************************************
NABoolean MatchResidualPredicates::generateNotProvidedElement(QRCandidatePtr resultDesc,
RewriteInstructionsItemPtr rewrite)
{
QRExprPtr residualElement = NULL;
if (rewrite->getSecondaryResultCode() == RC_AGGR_EXPR ||
rewrite->getSecondaryResultCode() == RC_ROLLUP_EXPR)
{
residualElement = generateAggregateExpressionOutput(rewrite);
if (residualElement == NULL)
{
candidate_->logMVWasDisqualified1("Output expression %s cannot be Provided.",
rewrite->getQueryElement()->getID().data());
return FALSE;
}
else
{
residualElement->setResidual(TRUE);
// Set the result attribute.
residualElement->setResult(rewrite->getDescriptorResultCode());
// Set the ref attribute.
residualElement->setRef(rewrite->getQueryElement()->getID());
}
}
else
{
// Create the new NotProvided Residual element, referencing the query
// residual pred.
residualElement = createNewResultElement(rewrite);
//Get the rewrite instructions for the subexpressions.
compositeMatchingResultsPtr subExprs = rewrite->getSubElements();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
subExprs->entries() == subExprs->providedInputs_.entries() + subExprs->backJoinInputs_.entries(),
QRLogicException, "Expecting all subexpressions to be Provided or back joins.");
// Create a hash table to map query column ids to the mvcols that replace them
// in the expression.
subExpressionRewriteHash subExprHash(hashKey, INIT_HASH_SIZE_SMALL, FALSE, heap_);
// For each subElement, get the id of the query element and the corresponding
// mv column, and put them in the hash table.
// Start with Provided inputs.
RewriteInstructionsItemList& rewriteList = subExprs->providedInputs_;
for (CollIndex i=0; i<rewriteList.entries(); i++)
{
RewriteInstructionsItemPtr subExprRewrite = rewriteList[i];
const NAString& key = subExprRewrite->getQueryElement()->getID();
QRMVColumnPtr mvColumn = generateProvidedMvColumn(subExprRewrite);
subExprHash.insert(&key, mvColumn);
}
// Now do back join columns.
RewriteInstructionsItemList& backJoinList = subExprs->backJoinInputs_;
for (CollIndex i=0; i<backJoinList.entries(); i++)
{
RewriteInstructionsItemPtr subExprRewrite = backJoinList[i];
const NAString& key = subExprRewrite->getQueryElement()->getID();
QRColumnPtr bjColumn = generateNotProvidedColumn(subExprRewrite);
subExprHash.insert(&key, bjColumn);
}
// Create the result expression tree from the query expression tree, with
// the needed sub-expressions switched to MVColumn elements.
QRExprPtr queryExpr = rewrite->getActualQueryElement()->downCastToQRExpr();
QRExplicitExprPtr resultTreeExpr = NULL;
try
{
resultTreeExpr = queryExpr->getExprRoot()->deepCopyAndSwitch(subExprHash, heap_);
}
catch(QRDescriptorException ex)
{
// We get here if we cannot properly rewrite all the query column elements.
// Currently we are leaking the memory of the elements stored in subExprHash.
candidate_->logMVWasDisqualified1("Residual predicate %s cannot be rewritten.",
queryExpr->getID().data());
return NULL;
}
residualElement->setExprRoot(resultTreeExpr);
}
// Add the residual predicate to the result descriptor.
resultDesc->getResidualPredList()->addItem(residualElement);
return TRUE;
}
//***************************************************************************
// Used by workload analysis.
//***************************************************************************
QRExprPtr MatchResidualPredicates::checkPredicate(QRExprPtr querySidePred)
{
const NAString& predText = querySidePred->getExprText();
// Get the list of MV residual predicates using the same expression text.
ResidualPredPtrList* mvPredList = candidate_->getMvDetails()->getJbbDetails()->getResidualPreds(predText);
if (mvPredList != NULL)
{
CollIndex mvPredCount = mvPredList->entries();
// Both MV and query have a residual pred using this text. Now match the input columns.
for (CollIndex j = 0; j<mvPredList->entries(); j++)
{
QRExprPtr mvResidualPred = (*mvPredList)[j];
if (matchExpressionInputs(querySidePred->getInputColumns(heap_),
mvResidualPred->getInputColumns(heap_)))
return mvResidualPred;
}
}
return NULL;
}
//========================================================================
// Class MatchGroupingColumns
//========================================================================
//***************************************************************************
//***************************************************************************
NABoolean MatchGroupingColumns::matchPass1()
{
QRGroupByPtr groupBy = NULL;
if (candidate_->getJbbSubset()->isIndirectGroupBy())
{
if (VerifyGroupingColumns() == FALSE)
return FALSE;
}
else
{
switch(candidate_->getAggregateMatchingType())
{
case AMT_MJV_JQ:
// No Groupby involved.
break;
case AMT_MJV_AQ:
// Aggregate query on MJV.
// No GROUPBY element is needed in the result descriptor,
// but we do need to add rewrite instructions for the grouping columns
// to the output list.
// Return without GroupBy element in the result descriptor.
return VerifyGroupingColumns();
case AMT_MAV_AQ_MG:
// No extra grouping is needed - the MV provided it all.
break;
case AMT_MAV_AQ_DG:
// The MV did not provide all the grouping columns - handle the rewrite instructions.
return VerifyGroupingColumns();
}
}
return TRUE;
} // MatchGroupingColumns::matchPass1()
/*****************************************************************************/
NABoolean MatchGroupingColumns::VerifyGroupingColumns()
{
// Do primary grouping list only, for now.
// TODO: Should do grouping columns from all the tables covered by the MV.
QRGroupByPtr queryGroupBy = candidate_->getQueryJbb()->getGroupBy();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
queryGroupBy != NULL, QRLogicException,
"Expecting a query grouping list.");
// An empty GroupBy means its an aggregate query with no GROUP BY.
// Its OK, and there are no grouping columns to verify.
if (queryGroupBy->isEmpty())
return TRUE;
const GroupingList& groupingList = queryGroupBy->getPrimaryList()->getList();
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
!groupingList.isEmpty(), QRLogicException,
"Query has Group By, but no grouping list.");
// The list of grouping columns is taken from the query descriptor,
// So we cannot use the IDs to find the MV Output column names.
// We have to find them by name.
for (CollIndex i=0; i<groupingList.entries(); i++)
{
const QRElementPtr groupingElem = groupingList[i]->getReferencedElement();
if (verifyGroupingElement(groupingElem) == FALSE)
return FALSE;
}
// Return without GroupBy element in the result descriptor.
return TRUE;
}
/*****************************************************************************/
NABoolean MatchGroupingColumns::verifyGroupingElement(const QRElementPtr groupingElem, NABoolean isRecursive)
{
RewriteInstructionsItemPtr rewrite = NULL;
if (groupingElem->getElementType() == ET_Expr)
{
const QRExprPtr groupingExpr = groupingElem->getReferencedElement()->downCastToQRExpr();
rewrite = findMatchingMvExpression(groupingExpr->getExprRoot());
if (rewrite == NULL || !rewrite->isFinal())
{
candidate_->logMVWasDisqualified1("grouping expression %s is not provided.",
groupingExpr->getID().data());
return FALSE;
}
}
else
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
groupingElem->getElementType() == ET_Column, QRLogicException,
"Expecting a QRColumn element.");
const QRColumnPtr groupingCol = groupingElem->downCastToQRColumn();
if (candidate_->getQueryDetails()->isFromJoin(groupingCol) && !isRecursive)
{
// This grouping column is part of an equality set.
// Check each element of the equality set to see if any of them is Provided by the MV.
const QRJoinPredPtr jp = candidate_->getQueryDetails()->getJoinPred(groupingCol);
const ElementPtrList& eqList = jp->getEqualityList();
CollIndex maxEntries = eqList.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRElementPtr eqElem = eqList[i]->getReferencedElement();
if (eqElem->getElementType() == ET_Column &&
verifyGroupingElement(eqElem, TRUE) )
return TRUE;
} // End of for loop.
// If we got here, none of the eqSet's columns is Provided by the MV.
candidate_->logMVWasDisqualified1("The needed grouping column %s is not Provided.",
groupingElem->getID().data());
return FALSE;
}
// Get the MV base table object from the query table ID.
const BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(groupingCol->getTableID(), FALSE);
if (baseTable == NULL)
{
if (isRecursive)
return FALSE;
else
rewrite = new(heap_)
RewriteInstructionsItem(groupingElem, NULL, RC_OUTSIDE, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
}
else
{
const QROutputPtr mvOutput =
candidate_->getMvDetails()->getOutputByColumnName(baseTable->getTableElement()->getID(),
groupingCol->getColumnName(), heap_);
if (mvOutput == NULL)
{
// This column has a range predicate on it , where it is equal to a
// constant value, so it does not really need to be provided by the MV.
if (candidate_->getQueryJbbDetails()->isConstColumn(&groupingCol->getID()))
return TRUE;
}
// Check if the column is back-joinable only when this is not a recursive call
// that checks for columns of an equality set.
NABoolean isBackJoinable = FALSE;
if (!isRecursive)
isBackJoinable = isBackJoinableColumn(groupingCol);
if (mvOutput == NULL && !isBackJoinable)
{
if (!isRecursive)
{
// The grouping column needed for rewrite instructions is not
// an output of the MV.
// Disqualify the MV.
candidate_->logMVWasDisqualified1("The needed grouping column %s is not Provided.",
groupingCol->getFullyQualifiedColumnName().data());
}
return FALSE;
}
// Create rewrite instructions for the grouping column.
rewrite = new(heap_)
RewriteInstructionsItem(groupingCol, mvOutput, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
if (mvOutput == NULL && isBackJoinable)
{
rewrite->setResultStatus(RS_INTERMEDIATE);
rewrite->addBackJoinTable(&groupingCol->getTableID());
rewrite->setResultCode(RC_BACKJOIN);
rewrite->setSecondaryResultCode(RC_BACKJOIN);
}
// For MJVs, add it to the output list.
if (candidate_->getAggregateMatchingType() == AMT_MJV_AQ)
{
candidate_->addOutputColumn(rewrite);
}
} // else found baseTable
} // else QRColumn
addRewriteInstructions(rewrite);
return TRUE;
}
// ***************************************************************************
// A generic method for generating the result descriptor elements.
// The specific details are implemented by sub-classes.
// ***************************************************************************
NABoolean MatchGroupingColumns::generateDescriptor(QRCandidatePtr resultDesc)
{
// No GroupBy element to generate for MJVs.
if (candidate_->getAggregateMatchingType() == AMT_MJV_AQ ||
candidate_->getAggregateMatchingType() == AMT_MJV_JQ)
return TRUE;
QRGroupByPtr groupBy = new(heap_) QRGroupBy(ADD_MEMCHECK_ARGS(heap_));
// Add a reference to the query NodeID of the GroupBy node.
groupBy->setRef(candidate_->getQueryJbb()->getGroupBy()->getID());
if (candidate_->getAggregateMatchingType() == AMT_MAV_AQ_MG)
{
groupBy->setResult(QRElement::Provided);
}
else
{
groupBy->setResult(QRElement::NotProvided);
RewriteInstructionsItemList& rewriteList = getListOfFinalInstructions();
for (CollIndex i=0; i<rewriteList.entries(); i++)
{
RewriteInstructionsItemPtr rewrite = rewriteList[i];
if (rewrite->getResultCode() == RC_PROVIDED)
{
QRMVColumnPtr mvColumn = generateProvidedMvColumn(rewrite);
groupBy->getPrimaryList()->addElement(mvColumn);
}
else
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
rewrite->getResultCode() == RC_OUTSIDE, QRLogicException,
"Expecting only Outside and Provided grouping Columns.");
QRColumnPtr column = generateNotProvidedColumn(rewrite);
groupBy->getPrimaryList()->addElement(column);
}
}
}
resultDesc->setGroupBy(groupBy);
return TRUE;
} // MatchGroupingColumns::generateDescriptor()
//========================================================================
// Class MatchJoinPreds
//========================================================================
// ***************************************************************************
// Run the Pass 1 algorithm of join predicate test.
// The goal is to find join predicates between tables covered by the MV, and
// tables not covered by the MV, and verify that the needed columns from
// the MV tables are Provided by the MV.
// Join predicates on tables in the extra hub table list are kept for PASS 2.
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchJoinPreds::matchPass1()
{
const QRJoinPredListPtr queryHubJoinPreds = candidate_->getQueryJbb()->getHub()->getJoinPredList();
const QRJoinPredListPtr queryExtraHubJoinPreds = candidate_->getQueryJbb()->getExtraHub()->getJoinPredList();
// For each join pred in the hub
if (queryHubJoinPreds != NULL)
{
CollIndex maxEntries = queryHubJoinPreds->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRJoinPredPtr joinPred = (*queryHubJoinPreds)[i];
if (analyzeJoinPredicate(joinPred, TRUE) == FALSE)
return FALSE;
}
}
// And in the extra-hub
if (queryExtraHubJoinPreds != NULL)
{
CollIndex maxEntries = queryExtraHubJoinPreds->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRJoinPredPtr joinPred = (*queryExtraHubJoinPreds)[i];
if (analyzeJoinPredicate(joinPred, TRUE) == FALSE)
return FALSE;
}
}
return TRUE;
} // MatchJoinPreds::matchPass1()
// ***************************************************************************
// Analyze a single join predicate to verify the needed columns are Provided
// by the MV.
// ***************************************************************************
NABoolean MatchJoinPreds::analyzeJoinPredicate(const QRJoinPredPtr joinPred, NABoolean isPass1)
{
// For this join pred, are the equality set members from the MV hub?
Int32 mvTables = 0; // how many are from the MV hub?
Int32 ehTables = 0; // how many are from the MV extra-hub?
Int32 outTables = 0; // how many are from Outside tables?
ColumnNodesList mvColumns(heap_); // Columns from the MV hub.
NAPtrList<QRExprPtr> mvExprs(heap_); // Expressions from the MV hub.
const ElementPtrList& eqList = joinPred->getList();
QRJoinPredPtr referencedHubPred = NULL;
for (CollIndex i=0; i<eqList.entries(); i++)
{
QRElementPtr elem = eqList[i];
// An extra-hub join pred may reference a hub join pred.
if (elem->getElementType() == ET_JoinPred)
referencedHubPred = elem->downCastToQRJoinPred();
else
analyzeEQMember(elem, mvTables, ehTables, outTables, mvColumns, mvExprs, isPass1);
}
if (referencedHubPred != NULL)
{
const ElementPtrList& eqList2 = referencedHubPred->getList();
for (CollIndex i=0; i<eqList2.entries(); i++)
{
QRElementPtr elem = eqList2[i];
analyzeEQMember(elem, mvTables, ehTables, outTables, mvColumns, mvExprs, isPass1);
}
}
// We went through all the elements in the equality set.
// Now lets check what we found.
if (ehTables > 0)
{
// This join pred uses MV extra-hub tables.
// Postpose decision to PASS 2, when we know which are really used.
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(joinPred, NULL, RC_NOTPROVIDED, RS_INTERMEDIATE, ADD_MEMCHECK_ARGS(heap_));
addRewriteInstructions(rewrite);
return TRUE;
}
else if (mvTables == 0)
{
// No extra-hub and no hub tables:
// This join pred is only on tables that are outside the MV.
// We can ignore it.
return TRUE;
}
else if (outTables == 0)
{
// No extra-hub and no Outside tables:
// This join pred is only on MV hub tables.
// It must have been checked by MVMemo.
// We can ignore it here.
return TRUE;
}
else
{
// This join pred equates MV hub tables with outside tables with no extrahubs.
// We need to verify that at least one of the columns used from the hub
// is provided by the MV.
QROutputPtr mvCol = NULL;
NABoolean foundProvided = FALSE;
QRColumnPtr hubCol = NULL;
for (CollIndex k=0; k<mvColumns.entries(); k++)
{
hubCol = mvColumns[k];
if (isProvidedColumn(hubCol, mvCol))
{
// We found a Provided MV Hub column.
// Make sure its added to the Output list.
// Use the ID of the column instead of the ID of the join predicate.
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(hubCol, mvCol, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
candidate_->addOutputColumn(rewrite, FALSE);
// Avoid adding an Output element with the ID of the join predicate.
candidate_->addOutputToAvoid(joinPred->getID());
foundProvided = TRUE;
break;
}
else
{
// This MV hub column is not Provided.
// continue to the next one.
continue;
}
} // for (k)
if (!foundProvided && mvExprs.entries() > 0)
{
// None of the simple columns are Provided, need to check expressions.
// Here we need all the input columns to be Provided.
for (CollIndex m=0; m<mvExprs.entries(); m++)
{
if (analyzeProvidedExpression(mvExprs[m]))
foundProvided = TRUE;
}
}
if (!foundProvided)
{
if (hubCol != NULL)
{
// None of the MV Hub column are Provided by the MV.
// This candidate must be disqualified.
const NAString& colName = hubCol->getFullyQualifiedColumnName();
candidate_->logMVWasDisqualified1("column %s cannot be provided.", colName);
}
else
{
candidate_->logMVWasDisqualified1("join predicate %s cannot be provided.", joinPred->getID().data());
}
return FALSE;
}
} // MV hub and outside tables, no extrahub.
return TRUE;
} // MatchJoinPreds::checkJoinPredicate()
// ***************************************************************************
// Check if this equality set member is on an MV hub, extrahub table or
// on an Outside table.
// ***************************************************************************
void MatchJoinPreds::analyzeEQMember(QRElementPtr elem,
Int32& mvTables,
Int32& ehTables,
Int32& outTables,
ColumnNodesList& mvColumns,
NAPtrList<QRExprPtr>& mvExprs,
NABoolean isPass1)
{
// Get a simple column from this equality set member.
QRColumnPtr col = elem->getFirstColumn();
const NAString* extraHubID = NULL;
NABoolean isOutside = isOutsideColumn(col, extraHubID);
if (isPass1 == FALSE &&
extraHubID != NULL &&
candidate_->isAUsedExtraHubTable(extraHubID))
{
// We are in Pass2, and this is an MV extra-hub table that is
// used by the query.
// Consider it as an MV hub table.
extraHubID = NULL;
}
if (isOutside)
{
// This table is Outside the MV.
outTables++;
}
else if (extraHubID != NULL)
{
// This is an MV extra-hub table, and we are in Pass1.
// Check if its in the used extra hub tables list.
if (candidate_->isAUsedExtraHubTable(extraHubID))
ehTables++;
else
{
// If its in the MV extrahub, but not needed by the query,
// its outside.
outTables++;
}
}
else
{
// This is an MV hub table.
mvTables++;
if (elem->getElementType() == ET_Column)
mvColumns.insert(col);
else
{
assertLogAndThrow(CAT_MATCHTST_MVDETAILS, LL_MVQR_FAIL,
elem->getElementType() == ET_Expr, QRLogicException,
"Expecting only QRExpr elements here.");
// All the expression's input columns must be Provided.
// Need to add as a group.
mvExprs.insert(elem->downCastToQRExpr());
}
}
}
// ***************************************************************************
// Analyze an expression on an MV hub table, to check if all its input
// columns are Provided by the MV.
// ***************************************************************************
NABoolean MatchJoinPreds::analyzeProvidedExpression(const QRExprPtr expr)
{
RewriteInstructionsItemPtr rewrite = matchExpression(expr);
if (rewrite == NULL || !rewrite->isFinal())
{
// The expression cannot be Provided by the MV.
if (rewrite)
deletePtr(rewrite);
return FALSE;
}
// add the input columns to the output list.
addInputsToOutputList(rewrite);
// Provided means that the expression itself is Provided,
// so add it to the Output list as well.
if (rewrite->getResultCode()==RC_PROVIDED)
candidate_->addOutputColumn(rewrite, FALSE);
return TRUE;
}
// ***************************************************************************
// Run the Pass 2 algorithm of join predicates test.
// Returns TRUE if the MV passed this test, FALSE if it was disqualified.
// ***************************************************************************
NABoolean MatchJoinPreds::matchPass2OnElement(RewriteInstructionsItemPtr pending)
{
QRJoinPredPtr joinPred = pending->getQueryElement()->downCastToQRJoinPred();
NABoolean result = analyzeJoinPredicate(joinPred, FALSE);
return result;
} // MatchJoinPreds::matchPass2OnElement()
// ***************************************************************************
// ***************************************************************************
NABoolean MatchJoinPreds::generateDescriptor(QRCandidatePtr resultDesc)
{
IDSet& backJoinTables = candidate_->getBackJoinTables();
if (backJoinTables.entries() == 0)
return TRUE;
// For each back-join table
for (CollIndex i=0; i<backJoinTables.entries(); i++)
{
const NAString* tableID = backJoinTables[i];
const QRTablePtr table = candidate_->getQueryDetails()->getElementForID(*tableID)->downCastToQRTable();
// Add the join predicate to the back-join table.
if (addBackJoinPredsForTable(table, resultDesc) == FALSE)
{
// if the creation of the join pred was not successful,
// Disqualify the MV.
candidate_->logMVWasDisqualified1("table %s cannot be back-joined.",
table->getTableName().data());
return FALSE;
}
// Add the Table element to the TableList.
QRTablePtr newTableElement = new (heap_) QRTable(ADD_MEMCHECK_ARGS(heap_));
newTableElement->setTableName(table->getTableName());
newTableElement->setRef(table->getID());
resultDesc->addBackJoinTable(newTableElement);
}
return TRUE;
}
// ***************************************************************************
// ***************************************************************************
NABoolean MatchJoinPreds::addBackJoinPredsForTable(const QRTablePtr table,
QRCandidatePtr resultDesc)
{
const QRKeyPtr theKey = table->getKey();
if (theKey == NULL)
return FALSE;
const BaseTableDetailsPtr baseTable = candidate_->getMvTableForQueryID(table->getID());
const QRTablePtr mvTable = baseTable->getTableElement();
CollIndex maxEntries = theKey->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
// Construct the new JoinPred element
QRJoinPredPtr joinPred = new(heap_) QRJoinPred(ADD_MEMCHECK_ARGS(heap_));
QRColumnPtr keyCol = theKey->getElement(i)->getReferencedElement()->downCastToQRColumn();
// Create the table column side of the join pred.
QRColumnPtr tableColumn = new (heap_) QRColumn(ADD_MEMCHECK_ARGS(heap_));
tableColumn->setFullyQualifiedColumnName(keyCol->getFullyQualifiedColumnName());
tableColumn->setRef(keyCol->getID());
tableColumn->setTableID(keyCol->getTableID());
joinPred->addElement(tableColumn);
// Create the MV column side of the join pred.
QROutputPtr mvOutput = NULL;
if (!isProvidedColumn(keyCol, mvOutput))
return FALSE;
QRMVColumnPtr mvColumn = new (heap_) QRMVColumn(ADD_MEMCHECK_ARGS(heap_));
mvColumn->setMVColName(mvOutput->getName());
joinPred->addElement(mvColumn);
// Add the new JoinPred to the result descriptor.
resultDesc->addJoinPred(joinPred);
}
return TRUE;
}
//========================================================================
// Class MatchOuterJoins
//========================================================================
// ***************************************************************************
// Go over every table in the hub and extrahub, in both query and MV,
// and check if they are the inner child of a left join.
// Possibilities are:
// Query MV Case Result
// 1 No No Normal inner join Do nothing.
// 2 Yes No LOJ query on inner join MV Disqualify.
// 3 No Yes Inner join query on LOJ MV Add NOT NULL predicate.
// 4 Yes No Both query and MV use LOJ Do Nothing.
// ***************************************************************************
NABoolean MatchOuterJoins::matchPass1()
{
// Quick shortcut check
if (!candidate_->getMvDetails()->getJbbDetails()->hasLOJs() &&
!candidate_->getQueryJbbDetails()->hasLOJs() )
{
// Neither the query nor the MV have any LOJs.
return TRUE;
}
const QRJBBCListPtr queryHubTables = candidate_->getQueryJbb()->getHub()->getJbbcList();
const QRTableListPtr queryExtraHubTables = candidate_->getQueryJbb()->getExtraHub()->getTableList();
// Prepare a list of all hub tables and used extrahub tables.
NAPtrList<QRTablePtr> allTables(heap_);
CollIndex maxEntries = queryHubTables->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRElementPtr queryJBBC = queryHubTables->getList()[i];
if (queryJBBC->getElementType() != ET_Table)
continue;
const QRTablePtr queryTable = queryJBBC->downCastToQRTable();
allTables.insert(queryTable);
}
if (queryExtraHubTables)
{
maxEntries = queryExtraHubTables->entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRTablePtr queryTable = queryExtraHubTables->getList()[i];
if (candidate_->isAUsedExtraHubTable(&queryTable->getID()))
allTables.insert(queryTable);
}
}
// Now go over the tables in the list
maxEntries = allTables.entries();
for (CollIndex j=0; j<maxEntries; j++)
{
// Get the LOJ flag
const QRTablePtr queryTable = allTables[j];
NABoolean queryTableIsLOJ = queryTable->hasLOJParent();
const BaseTableDetailsPtr mvTableDetails = candidate_->getMvTableForQueryID(queryTable->getID(), FALSE);
if (mvTableDetails == NULL)
continue; // Its an Outside table (not used by the MV).
const QRTablePtr mvTable = mvTableDetails->getTableElement();
NABoolean mvTableIsLOJ = mvTable->hasLOJParent();
// Check the Result
if (queryTableIsLOJ && !mvTableIsLOJ)
{
candidate_->logMVWasDisqualified1("Table %s is an inner table of an outer join in the query but not in the MV.",
queryTable->getTableName().data());
return FALSE;
}
if (!queryTableIsLOJ && mvTableIsLOJ)
{
return addTheNotNullPred(queryTable, mvTableDetails);
}
}
return TRUE;
}
// ***************************************************************************
// Take the query table, get the key columns, and iterate over them to find
// one that is not nullable, and also provided by the MV.
// If found - add the NOT NOLL predicate on it.
// Otherwise - disqualify the MV.
// ***************************************************************************
NABoolean MatchOuterJoins::addTheNotNullPred(const QRTablePtr queryTable, const BaseTableDetailsPtr mvTableDetails)
{
const ElementPtrList& keyColumns = queryTable->getKey()->getList();
CollIndex maxEntries = keyColumns.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const QRColumnPtr keyCol = keyColumns[i]->getReferencedElement()->downCastToQRColumn();
if (keyCol->isNullable()) // Just making sure...
continue;
// Maybe there already is a range predicate that does not allow nulls?
const QRRangePredPtr rangePred = mvTableDetails->getRangeColumnPredicatesFor(keyCol->getColIndex());
if (rangePred != NULL)
{
const RangeSpec* rangeSpec = rangePred->getRangeSpec(heap_);
if (!rangeSpec->isNullIncluded())
{
// The query already has a range predicate on this column that
// does not allow nulls, so there is no need to add it.
return TRUE;
}
}
// No such range predicate found. We need to add the NOT NULL predicate.
// Is the column Provided by the MV?
QROutputPtr mvCol = NULL;
if (!isProvidedColumn(keyCol, mvCol))
continue;
RewriteInstructionsItemPtr rewrite = new(heap_)
RewriteInstructionsItem(keyCol, mvCol, RC_PROVIDED, RS_FINAL, ADD_MEMCHECK_ARGS(heap_));
addRewriteInstructions(rewrite);
return TRUE;
}
candidate_->logMVWasDisqualified1("table %s key columns are not provided for NOT NULL predicate.",
queryTable->getTableName().data());
return FALSE; // No eligible key column found.
}
// ***************************************************************************
// Not used.
// ***************************************************************************
NABoolean MatchOuterJoins::matchPass2OnElement(RewriteInstructionsItemPtr pending)
{
return TRUE;
}
// ***************************************************************************
// For each column in the rewrite instructions, add a NOT NULL range predicate.
// ***************************************************************************
NABoolean MatchOuterJoins::generateDescriptor(QRCandidatePtr resultDesc)
{
QRRangePredListPtr rangePredicates = resultDesc->getRangePredList();
RewriteInstructionsItemList& rewriteList = getListOfFinalInstructions();
for (CollIndex i=0; i<rewriteList.entries(); i++)
{
RewriteInstructionsItemPtr rewrite = rewriteList[i];
// Construct the new Range element
QRRangePredPtr rangeElement = new(heap_) QRRangePred(ADD_MEMCHECK_ARGS(heap_));
// Set the result attribute.
rangeElement->setResult(QRElement::NotProvided);
// Do the range item - the MV column.
QRMVColumnPtr rangeItem = generateProvidedMvColumn(rewrite);
rangeElement->setRangeItem(rangeItem);
// Don't add a RangeSpec - an empty range means NOT NULL.
// Add it to the result descriptor.
rangePredicates->addItem(rangeElement);
}
return TRUE;
}
//========================================================================
// Class AggregateCollectorVisitor
//========================================================================
// ***************************************************************************
// ***************************************************************************
Visitor::VisitResult AggregateCollectorVisitor::visit(QRElementPtr caller)
{
switch(caller->getElementType())
{
case ET_Function:
{
QRFunctionPtr func = caller->downCastToQRFunction();
if (func->isAnAggregate())
{
aggregateFunctions_.insert(func);
return VR_Skip;
}
}
break;
case ET_Column:
simpleColumns_.insert(caller->downCastToQRColumn());
return VR_Skip;
break;
case ET_JoinPred:
joinedColumns_.insert(caller->downCastToQRJoinPred());
return VR_Skip;
break;
}
return VR_Continue;
}