blob: 8c0feea96507e3c63b80b4fe9f402ab82a475674 [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: MVCandidate.cpp
// Description:
//
//
//
//
// Created: 07/20/08
// ***********************************************************************
#include "QmsMVCandidate.h"
#include "QRDescriptor.h"
#include "QRMessage.h"
#include "ComCextdecs.h" // For NA_JulianTimestamp()
using namespace QR;
//========================================================================
// Class MVCandidate
//========================================================================
/****************************************************************************/
MVCandidate::MVCandidate(const MVDetailsPtr mvDetails,
const DescriptorDetailsPtr query,
const QRJBBPtr jbb,
MVCandidatesForJBBSubsetPtr jbbSubset,
ADD_MEMCHECK_ARGS_DEF(CollHeap* heap))
: NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap))
,heap_(heap)
,mvDetails_(mvDetails)
,queryDetails_(query)
,queryJbb_(jbb)
,queryJbbDetails_(NULL)
,jbbSubset_(jbbSubset)
,mvSubGraphMap_(NULL)
,extraGroupingColumns_(NULL)
,isPreferredMatch_(FALSE)
,outputMatching_(ADD_MEMCHECK_ARGS(heap))
,rangeMatching_(ADD_MEMCHECK_ARGS(heap))
,residualMatching_(ADD_MEMCHECK_ARGS(heap))
,groupingMatching_(ADD_MEMCHECK_ARGS(heap))
,joinPredMatching_(ADD_MEMCHECK_ARGS(heap))
,lojMatching_(ADD_MEMCHECK_ARGS(heap))
,matchingType_(ANT_NO_INIT)
,extraHubTables_(heap)
,backJoinTables_(heap)
,wasDisqualified_(FALSE)
,disqualifiedReason_(NULL)
,tableIDCache_(hashKey, INIT_HASH_SIZE_SMALL, FALSE, heap)
{
if (jbbSubset)
jbbSubset->registerCandidate(this);
outputMatching_.setCandidate(this);
rangeMatching_.setCandidate(this);
residualMatching_.setCandidate(this);
groupingMatching_.setCandidate(this);
joinPredMatching_.setCandidate(this);
lojMatching_.setCandidate(this);
}
/****************************************************************************/
/****************************************************************************/
QRJoinSubGraphMapPtr MVCandidate::getMvSubGraphMap()
{
return mvSubGraphMap_;
}
/****************************************************************************/
/****************************************************************************/
QRJoinSubGraphMapPtr MVCandidate::getQuerySubGraphMap()
{
return jbbSubset_->getQuerySubGraphMap();
}
/****************************************************************************/
// Initialize internal data structures, such as the AggregateMatchingType.
/****************************************************************************/
void MVCandidate::init(NABoolean isPreferredMatch, GroupingList* groupingList)
{
isPreferredMatch_ = isPreferredMatch;
extraGroupingColumns_ = groupingList;
initAggregateMatchingType();
}
/****************************************************************************/
/****************************************************************************/
void MVCandidate::initAggregateMatchingType()
{
// Check if the query JBB has a GroupBy in it.
NABoolean QueryHasGroupBy = (queryJbb_->getGroupBy() != NULL);
if (mvDetails_->hasGroupBy())
{
// The MV is a MAV.
if (QueryHasGroupBy)
{
// Both have a GroupBy, check if it matching.
if ( (extraGroupingColumns_ == NULL || extraGroupingColumns_->entries() == 0) &&
!getJbbSubset()->isIndirectGroupBy() )
{
// No extra grouping columns, so matching grouping list.
matchingType_ = AMT_MAV_AQ_MG;
}
else
{
// Query needs a subset of the MV's grouping list.
matchingType_ = AMT_MAV_AQ_DG;
}
}
else
{
// MAV with join query JBB - not supposed to happen.
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
FALSE, QRLogicException,
"A MAV cannot match a join query.");
}
}
else
{
// The MV is an MJV
if (QueryHasGroupBy)
{
// Aggregate query with MJV.
matchingType_ = AMT_MJV_AQ;
}
else
{
// Join query with MJV.
matchingType_ = AMT_MJV_JQ;
}
}
} // MVCandidate::initAggregateMatchingType()
/****************************************************************************/
/****************************************************************************/
NABoolean MVCandidate::matchPass1()
{
if (getAggregateMatchingType() == AMT_MAV_AQ_DG &&
mvDetails_->getJbbDetails()->hasHavingPredicates() )
{
// An MV with HAVING predicates cannot be used with rollup.
logMVWasDisqualified("it has HAVING predicates, and the query needs Rollup.");
return FALSE;
}
if (rangeMatching_.matchPass1() == FALSE)
return FALSE;
if (residualMatching_.matchPass1() == FALSE)
return FALSE;
if (groupingMatching_.matchPass1() == FALSE)
return FALSE;
if (outputMatching_.matchPass1() == FALSE)
return FALSE;
// Must be last Pass1 test before checking extrahub tables.
if (joinPredMatching_.matchPass1() == FALSE)
return FALSE;
if (CheckExtraHubTables() == FALSE)
return FALSE;
if (lojMatching_.matchPass1() == FALSE)
return FALSE;
return TRUE;
} // MVCandidate::matchPass1()
/****************************************************************************/
/****************************************************************************/
NABoolean MVCandidate::matchPass2()
{
if (rangeMatching_.matchPass2() == FALSE)
return FALSE;
if (residualMatching_.matchPass2() == FALSE)
return FALSE;
if (groupingMatching_.matchPass2() == FALSE)
return FALSE;
if (outputMatching_.matchPass2() == FALSE)
return FALSE;
if (joinPredMatching_.matchPass2() == FALSE)
return FALSE;
if (lojMatching_.matchPass2() == FALSE)
return FALSE;
return TRUE;
} // MVCandidate::matchPass1()
/****************************************************************************/
// Generate the Candidate element of the result descriptor including
// the needed rewrite instructions.
/****************************************************************************/
QRCandidatePtr MVCandidate::generateDescriptor()
{
// Start with the name of the MV.
QRMVNamePtr mv = new(heap_) QRMVName(ADD_MEMCHECK_ARGS(heap_));
mv->setMVName(mvDetails_->getMVName());
mv->setTimestamp(mvDetails_->getRedefTimestampAsString());
// Generate the QRCandidate object and populate it.
QRCandidatePtr resultDesc = new(heap_) QRCandidate(ADD_MEMCHECK_ARGS(heap_));
resultDesc->setMVName(mv);
resultDesc->setIndirectGroupBy(getJbbSubset()->isIndirectGroupBy());
NABoolean allIsWell = TRUE;
// Now handle the GroupBy stuff.
if(allIsWell)
allIsWell = groupingMatching_.generateDescriptor(resultDesc);
// Add range predicates.
if(allIsWell)
allIsWell = rangeMatching_.generateDescriptor(resultDesc);
// Add range predicates.
if(allIsWell)
allIsWell = residualMatching_.generateDescriptor(resultDesc);
// Add the output list.
if(allIsWell)
allIsWell = outputMatching_.generateDescriptor(resultDesc);
// Add the back-join table list and join predicates.
if(allIsWell)
allIsWell = joinPredMatching_.generateDescriptor(resultDesc);
// Add any NOT NULL predicates for inner over outer joins.
if(allIsWell)
allIsWell = lojMatching_.generateDescriptor(resultDesc);
if (allIsWell)
{
QRLogger::log(CAT_MVCAND, LL_DEBUG,
"Added MV %s to result descriptor.", mvDetails_->getMVName().data());
return resultDesc;
}
else
{
deletePtr(resultDesc);
disqualify();
return NULL;
}
} // MVCandidate::generateDescriptor()
/*****************************************************************************/
/* Add new entries to the list of MV extra-hub or back-join tables
/* used by the query.
/*****************************************************************************/
void MVCandidate::addExtraHubAndBackJoinTables(RewriteInstructionsItemPtr rewrite)
{
extraHubTables_.insert(rewrite->getExtraHubTables());
backJoinTables_.insert(rewrite->getBackJoinTables());
}
/*****************************************************************************/
// This MV candidate has been disqualified.
/****************************************************************************/
void MVCandidate::disqualify()
{
jbbSubset_->getJbbCandidates()->addMatchInfo(disqualifiedReason_);
disqualifiedReason_ = NULL;
jbbSubset_->disqualify(this);
}
/****************************************************************************/
// Write to the log file why the MV was disqualified.
/****************************************************************************/
void MVCandidate::logMVWasDisqualified(const char* reason)
{
// Avoid duplicate messages.
if (wasDisqualified_)
return;
wasDisqualified_ = TRUE;
char buffer[1024];
const MVDetailsPtr mvDetails = getMvDetails();
sprintf(buffer, "MV %s was disqualified because %s",
mvDetails->getMVName().data(), reason);
QRLogger::log(CAT_MVCAND, LL_INFO, buffer);
disqualifiedReason_ = new (heap_) NAString(buffer, heap_);
}
/****************************************************************************/
// Write to the log file why the MV was disqualified.
/****************************************************************************/
void MVCandidate::logMVWasDisqualified1(const char* reason, const NAString& arg1)
{
char buffer[1024];
sprintf(buffer, reason, arg1.data());
logMVWasDisqualified(buffer);
}
/****************************************************************************/
// When an MV is disqualified while checking for optional information,
// mark it as qualified again.
/****************************************************************************/
void MVCandidate::reQualify()
{
wasDisqualified_ = FALSE;
delete disqualifiedReason_;
disqualifiedReason_ = NULL;
}
/****************************************************************************/
// Use the query descriptor ID of a table to find its MV data.
/****************************************************************************/
const BaseTableDetailsPtr MVCandidate::getMvTableForQueryID(const NAString& id, NABoolean assertOnFailure)
{
QRJoinSubGraphMapPtr queryMap = getQuerySubGraphMap();
if (!queryMap->contains(id))
{
if (assertOnFailure)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Matching MV table not found in JoinGraphMap.");
}
return NULL;
}
Int32 hashKeyIndex = queryMap->getIndexForTable(id);
if (hashKeyIndex == -1)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
assertOnFailure, QRLogicException,
"Matching MV table not found in JoinGraphMap.");
return NULL;
}
const JoinGraphTablePtr mvTable = getMvSubGraphMap()->getTableForIndex(hashKeyIndex);
if (mvTable == NULL)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
assertOnFailure, QRLogicException,
"Matching MV table not found in JoinGraphMap.");
return NULL;
}
return mvTable->getBaseTable();
}
/****************************************************************************/
// Use the MV descriptor ID of a table to find its Query data.
// Used only by WA
/****************************************************************************/
const BaseTableDetailsPtr MVCandidate::getQueryTableForMvID(const NAString& id, NABoolean assertOnFailure)
{
QRJoinSubGraphMapPtr mvMap = getMvSubGraphMap();
if (!mvMap->contains(id))
{
if (assertOnFailure)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
FALSE, QRLogicException,
"Matching Query table not found in JoinGraphMap.");
}
return NULL;
}
Int32 hashKeyIndex = mvMap->getIndexForTable(id);
if (hashKeyIndex == -1)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
assertOnFailure, QRLogicException,
"Matching Query table not found in JoinGraphMap.");
return NULL;
}
const JoinGraphTablePtr queryTable = getQuerySubGraphMap()->getTableForIndex(hashKeyIndex);
if (queryTable == NULL)
{
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
assertOnFailure, QRLogicException,
"Matching Query table not found in JoinGraphMap.");
return NULL;
}
return queryTable->getBaseTable();
}
/****************************************************************************/
// This method is called between Pass 1 and Pass 2.
// Pass 1 finds MV extra-hub tables that are needed for output columns or
// predicates, and adds them to the extraHubTables_ list.
// Now, we need to verify that the extra-hub table is connected to the
// rest of the sub-graph, using equi-join predicates that match the ones
// in the query. There is no need to match predicates because extra-hub
// tables have no predicates on them by definition.
// The algorithm cannot handle self-joined extra-hub tables.
/****************************************************************************/
NABoolean MVCandidate::CheckExtraHubTables()
{
if (extraHubTables_.entries() == 0)
return TRUE;
// Allocate a new subGraph and subGraphMap for the MV - the one we have is from MVMemo,
// so its considered read-only.
QRJoinSubGraphMapPtr mvSubGraphMap = new(heap_)
QRJoinSubGraphMap(*getMvSubGraphMap(), ADD_MEMCHECK_ARGS(heap_));
// The query subGraph is shared between the MVCandidates that were found
// in the same MVMemo group, so work on a copy.
QRJoinSubGraphMapPtr querySubGraphMap = new(heap_)
QRJoinSubGraphMap(*getJbbSubset()->getQuerySubGraphMap(), ADD_MEMCHECK_ARGS(heap_));
// Loop over the list, each time picking an extra-hub table that is
// directly connected to a table already in the matched sub-graph,
// until all the tables are checked.
do
{
NABoolean extraHubTableWasAdded = FALSE;
// Do the loop backwards because we remove checked extra-hub tables from the list.
for (Int32 i=(CollIndex)extraHubTables_.entries()-1; i>=0; i--)
{
if (CheckAnExtraHubTable(extraHubTables_[i],
mvSubGraphMap, // IN/OUT
querySubGraphMap, // IN/OUT
extraHubTableWasAdded) == FALSE)
return FALSE;
} // for
// Keep looping as long as there are extra-hub tables that are not connected,
// and as long as we keep adding them.
if (!extraHubTableWasAdded && extraHubTables_.entries() > 0)
{
logMVWasDisqualified1("Extra hub table %s is needed but cannot be matched.",
extraHubTables_[0]->data());
return FALSE;
}
// assertLogAndThrow(extraHubTableWasAdded, QRLogicException,
// "Extra-hub table is not connected to the hub.");
} while (extraHubTables_.entries() > 0);
// Update the MV new subGraphMap
mvSubGraphMap_ = mvSubGraphMap;
// We have added at least one extra-hub table -
// now we need to reorg the JBBSubsets accordingly.
MVCandidatesForJBBPtr jbb = getJbbSubset()->getJbbCandidates();
MVCandidatesForJBBSubsetPtr newJbbSubset = NULL;
QRJoinSubGraphPtr querySubGraph = querySubGraphMap->getSubGraph();
if (jbb->contains(querySubGraph))
{
// The JBBSubset with the new extra-hub tables already exists.
newJbbSubset = jbb->getJbbSubsetFor(querySubGraph);
}
else
{
// Need to create a new JBBSubset.
newJbbSubset = new (heap_) MVCandidatesForJBBSubset(jbb, ADD_MEMCHECK_ARGS(heap_));
newJbbSubset->setSubGraphMap(querySubGraphMap);
// Add the new JbbSubset to the JBB.
jbb->insert(newJbbSubset);
}
// Move the candidate from the current JbbSubset to the new one.
getJbbSubset()->remove(this);
newJbbSubset->insert(this);
setJbbSubset(newJbbSubset);
return TRUE;
} // CheckExtraHubTables()
//*****************************************************************************
//*****************************************************************************
NABoolean MVCandidate::CheckAnExtraHubTable(const NAString* tableID,
QRJoinSubGraphMapPtr mvSubGraphMap,
QRJoinSubGraphMapPtr querySubGraphMap,
NABoolean& extraHubTableWasAdded)
{
QRJoinSubGraphPtr mvSubGraph = mvSubGraphMap->getSubGraph();
QRJoinSubGraphPtr querySubGraph = querySubGraphMap->getSubGraph();
const QRTablePtr queryTableElement =
getQueryDetails()->getElementForID(*tableID)->downCastToQRTable();
const JoinGraphTablePtr queryGraphTable =
querySubGraph->getParentGraph()->getTableByID(queryTableElement->getID());
// If this extra-hub table is not directly connected to the rest of
// the subgraph, skip it for now.
if (queryGraphTable->isConnectedTo(querySubGraph) == FALSE)
{
// Add additional needed extra-hub tables here...
return TRUE;
}
// Find the corresponding MV table
const QRTablePtr mvTableElement = getMvDetails()->getTableFromName(queryTableElement->getTableName());
const JoinGraphTablePtr mvGraphTable =
mvSubGraph->getParentGraph()->getTableByID(mvTableElement->getID());
if (matchPredsFromTableToSubGraph(mvSubGraph,
querySubGraph,
mvGraphTable,
queryGraphTable) == FALSE)
{
// Match failed.
// Release the sandbox subgraphs and disqualify the MVCandidate.
deletePtr(mvSubGraphMap);
deletePtr(querySubGraphMap);
logMVWasDisqualified1("Extra hub table %s is needed but cannot be matched.",
queryTableElement->getTableName().data());
return FALSE;
}
// Add the table to both MV and query subgraphs.
querySubGraph->addTable(queryGraphTable);
mvSubGraph->addTable(mvGraphTable);
// To make sure that Pass 2 matching and result desxcriptor generation will
// work correctly, add the tables to the SubGraphMaps (they will have the same index).
CollIndex index = querySubGraph->entries();
querySubGraphMap->addTable(index, queryGraphTable);
mvSubGraphMap->addTable(index, mvGraphTable);
// We are done with this table, remove it from the list.
extraHubTables_.remove(tableID);
extraHubTableWasAdded = TRUE;
QRLogger::log(CAT_MVCAND, LL_DEBUG,
"ExtraHub table %s was matched for MV %s.",
queryTableElement->getTableName().data(),
mvDetails_->getMVName().data());
return TRUE;
} // MVCandidate::CheckAnExtraHubTable()
//*****************************************************************************
//*****************************************************************************
NABoolean MVCandidate::isAUsedExtraHubTable(const NAString* tableID)
{
CollIndex maxEntries = extraHubTables_.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
if (*tableID == *(extraHubTables_[i]))
return TRUE;
}
return FALSE;
}
//*****************************************************************************
// thisSubgraph and otherSubGraph are equivalent subgraphs (from the MV and
// query join graphs). This method checks if they will continue to match
// if an extra-hub table is added. It checks that ALL predicates connecting
// this table to the subgraph match those of the other table/subgraph.
// The assumption is that this is the MV table/subgraph, while the other
// is the query table/subgraph, so the query can have additional join
// predicates over those in the MV. Those extra predicates are returned as
// rewrite instructions.
//*****************************************************************************
NABoolean MVCandidate::matchPredsFromTableToSubGraph(const QRJoinSubGraphPtr mvSubGraph,
const QRJoinSubGraphPtr querySubGraph,
const JoinGraphTablePtr mvGraphTable,
const JoinGraphTablePtr queryGraphTable)
{
// For each join pred on this table
CollIndex maxEntries = mvGraphTable->getEqualitySets().entries();
for (CollIndex i=0; i<maxEntries; i++)
{
// Find the column its using
const JoinGraphEqualitySetPtr mvEqSet = mvGraphTable->getEqualitySets()[i];
const JoinGraphHalfPredicatePtr mvHalfPred = mvEqSet->findHalfPredTo(mvGraphTable);
// Get the corresponding eqSet from the other join graph
const JoinGraphEqualitySetPtr queryEqSet = queryGraphTable->getEqSetUsing(mvHalfPred);
if (queryEqSet == NULL)
{
// Corresponding eqSet not found - match failed.
return FALSE;
}
// Verify that the half-pred to the EQ set matches.
const JoinGraphHalfPredicatePtr queryHalfPred = queryEqSet->findHalfPredTo(queryGraphTable);
if (mvHalfPred->match(queryHalfPred) == FALSE)
return FALSE;
// Let the JoinPredMatching object continue with the matching from the EQ set to the subgraph.
if (matchPredsFromEQSetToSubGraph(mvSubGraph,
querySubGraph,
mvEqSet,
queryEqSet,
mvHalfPred->getID()) == FALSE)
return FALSE;
}
return TRUE;
} // matchPredsFromTableToSubGraph()
// ***************************************************************************
// ***************************************************************************
NABoolean MVCandidate::matchPredsFromEQSetToSubGraph(const QRJoinSubGraphPtr mvSubGraph,
const QRJoinSubGraphPtr querySubGraph,
const JoinGraphEqualitySetPtr mvEqSet,
const JoinGraphEqualitySetPtr queryEqSet,
const NAString& mvHalfPredID)
{
NABoolean extraHubColumnChecked = FALSE;
const HalfPredicateList& queryHalfPreds = queryEqSet->getHalfPredicates();
CollIndex maxEntries = queryHalfPreds.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
const JoinGraphHalfPredicatePtr queryHalfPred = queryHalfPreds[i];
const JoinGraphTablePtr queryTable = queryHalfPred->getTable();
if (querySubGraph->contains(queryTable))
{
// We found a join pred that's connected to the subgraph - match it.
// Find the corresponding table in the MV join graph.
// ??? Don't assert if a match is not found, because maybe the join pred
// ??? does not exist in the MV.
const BaseTableDetailsPtr mvBaseTable = getMvTableForQueryID(queryTable->getID(), FALSE);
if (mvBaseTable == NULL)
{
// Do we ever get here???
assertLogAndThrow(CAT_MVCAND, LL_MVQR_FAIL,
FALSE, QRLogicException,
"MV hub table corresponding to query hub not found.");
}
else
{
// We have the MV hub table corresponding to the query hub table.
// Now find its halfPred.
const NAString& mvID = mvBaseTable->getID();
const JoinGraphTablePtr mvTable = mvSubGraph->getParentGraph()->getTableByID(mvID);
const JoinGraphHalfPredicatePtr mvHalfPred = mvEqSet->findHalfPredTo(mvTable);
if (mvHalfPred == NULL)
{
// This query pred has no corresponding mv pred.
// Handle rewrite instructions here (Not implemented yet).
//if (addJoinPredForExtraHubTable(mvHalfPredID, queryHalfPred) == FALSE)
return FALSE;
}
else
{
// Finally we have both the MV halfPred and its corresponding query halfPred.
// Match them.
if (mvHalfPred->match(queryHalfPred) == FALSE)
return FALSE;
}
// Continue matching with the next halfPred.
continue;
}
}
}
return TRUE;
} // MVCandidate::matchPredsFromEQSetToSubGraph()
const NAString MVCandidate::trueString_ = "TRUE";
const NAString MVCandidate::falseString_ = "FALSE";
/****************************************************************************/
/****************************************************************************/
void MVCandidate::cacheTableID(const NAString* tableID,
NABoolean isOutside,
const NAString* extraHubID)
{
if (extraHubID != NULL)
{
tableIDCache_.insert(tableID, extraHubID);
}
else if (isOutside)
{
tableIDCache_.insert(tableID, &trueString_);
}
else
{
tableIDCache_.insert(tableID, &falseString_);
}
}
/****************************************************************************/
/****************************************************************************/
NABoolean MVCandidate::probeCacheForTableID(const NAString* tableID,
NABoolean& isOutside,
const NAString*& extraHubID)
{
const NAString* result = tableIDCache_.getFirstValue(tableID);
if (result == NULL)
return FALSE;
else if (*result == trueString_)
{
isOutside = TRUE;
}
else if (*result == falseString_)
{
isOutside = FALSE;
}
else
{
isOutside = FALSE;
extraHubID = result;
}
return TRUE;
}
/****************************************************************************/
/****************************************************************************/
JBBDetailsPtr MVCandidate::getQueryJbbDetails()
{
if (queryJbbDetails_)
return queryJbbDetails_;
const NAString& id = queryJbb_->getID();
JBBDetailsPtrList& jbbs = queryDetails_->getJbbDetailsList();
CollIndex maxEntries = jbbs.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
JBBDetailsPtr jbbDetails = jbbs[i];
if (jbbDetails->getJbbDescriptor()->getID() == id)
{
queryJbbDetails_ = jbbDetails;
return jbbDetails;
}
}
return NULL;
}
/****************************************************************************/
/****************************************************************************/
void MVCandidate::addOutputColumn(RewriteInstructionsItemPtr rewrite, NABoolean makeACopy)
{
RewriteInstructionsItemPtr newRewrite = rewrite;
if (makeACopy)
{
// Make a copy to avoid deleting it twice.
newRewrite = new (heap_) RewriteInstructionsItem(*rewrite, ADD_MEMCHECK_ARGS(heap_));
}
outputMatching_.addRewriteInstructions(newRewrite);
}
//========================================================================
// Class MVCandidatesForJBBSubset
//========================================================================
/****************************************************************************/
/****************************************************************************/
MVCandidatesForJBBSubset::~MVCandidatesForJBBSubset()
{
// The subGraph was not deleted with the hubIterator, because we set the
// stillUsed flag, so delete it now.
deletePtr(querySubGraphMap_);
// Delete all the MVCndidates.
for (CollIndex i=0; i<candidateList_.entries(); i++)
deletePtr(candidateList_[i]);
}
/****************************************************************************/
/****************************************************************************/
NABoolean MVCandidatesForJBBSubset::operator==(const MVCandidatesForJBBSubset& other) const
{
return getSubGraph() == other.getSubGraph();
}
/****************************************************************************/
/****************************************************************************/
const NAString* MVCandidatesForJBBSubset::getKeyObject() const
{
return getSubGraph()->getFastKey();
}
/****************************************************************************/
/****************************************************************************/
void MVCandidatesForJBBSubset::setSubGraphMap(QRJoinSubGraphMapPtr querySubGraphMap)
{
querySubGraphMap_ = querySubGraphMap;
// Mark that this subGraph is being used for rewrite instructions, so that
// it will not be deleted when the HubIterator is deleted.
querySubGraphMap->getSubGraph()->setStillUsed(TRUE);
}
/****************************************************************************/
/****************************************************************************/
QRJoinSubGraphPtr MVCandidatesForJBBSubset::getSubGraph() const
{
return querySubGraphMap_->getSubGraph();
}
/****************************************************************************/
/* Parse the MV_AGE cqd and translate it to seconds.
/* A 32 bit number should be enough for calculating seconds.
/****************************************************************************/
Int32 parseMVAge(const NAString& mvAge)
{
Int32 result = 0;
float number=0;
char textChars[20];
if (mvAge.length() < 15)
{
if (sscanf(mvAge.data(), "%f %s", &number, textChars) == 2)
{
const NAString text(textChars);
if (!text.compareTo("Seconds", NAString::ignoreCase))
{
result = (Int32)floor(number);
}
else if (!text.compareTo("Minutes", NAString::ignoreCase))
{
result = (Int32)floor(number*60);
}
else if (!text.compareTo("Hours", NAString::ignoreCase))
{
result = (Int32)floor(number*60*60);
}
else if (!text.compareTo("Days", NAString::ignoreCase))
{
result = (Int32)floor(number*60*60*24);
}
}
}
if (result == 0)
{
QRLogger::log(CAT_MVCAND, LL_ERROR,
"Invalid setting for MV_AGE default value: %s, Using only fresh MVs.",
mvAge.data());
}
return result;
}
/****************************************************************************/
// Create an MVCandidate object and insert it into the JBBSubset.
/****************************************************************************/
void MVCandidatesForJBBSubset::insert(MVDetailsPtr mv,
QRJBBPtr queryJbb,
NABoolean isPreferredMatch,
GroupingList* extraGroupingColumns,
QRJoinSubGraphMapPtr mvMap,
CollHeap* heap)
{
DescriptorDetailsPtr queryDetails = jbbCandidates_->getAllCandidates()->getQueryDetails();
NAString excuse(heap);
if (mv->isInitialized() == FALSE)
{
// Disqualify uninitialized MVs
excuse = " was skipped because it is not initialized";
}
else
{
QRDescriptorPtr desc = queryDetails->getDescriptor();
QRQueryDescriptorPtr queryDesc = static_cast<QRQueryDescriptorPtr>(desc);
QRQueryMiscPtr misc = queryDesc->getMisc();
const NAString& mvAge = misc->getMVAge();
switch (misc->getRewriteLevel())
{
case MRL_FRESH:
// Allow only ON STATEMENT MVs.
if (mv->isImmediate() == FALSE)
{
// Disqualify stale MVs.
excuse = " was skipped because it is not completey fresh";
}
break;
case MRL_STALE:
if (mv->isImmediate())
{
// Allow immediate MVs. No need to calculate MV_AGE.
break;
}
if ( mv->isUMV() )
{
// Disqualify UMVs.
excuse = " was skipped because it is user maintained";
break;
}
// Allow all MVs that were refreshed at least MV_AGE ago
if ( mvAge != "")
{
const Int32 mvAgeSeconds = parseMVAge(mvAge);
const Int64 mvRefreshTS = mv->getRefreshTimestamp()/1000000;
const Int64 nowTS = NA_JulianTimestamp()/1000000;
Int32 refreshAge = (Int32)(nowTS - mvRefreshTS);
QRLogger::log(CAT_MVCAND, LL_DEBUG,
"MV_AGE is: %d seconds, RefreshAge is: %d seconds.", mvAgeSeconds, refreshAge);
if (mvAgeSeconds < refreshAge)
{
// Disqualify stale MVs.
excuse = " was skipped because it is stale.";
}
}
break;
case MRL_OLD:
if ( mv->isUMV() )
{
// Disqualify UMVs.
excuse = " was skipped because it is user maintained";
}
break;
case MRL_UMVS:
// All MVs are allowed here.
break;
case MRL_OFF:
// MVQR is OFF. Not supposed to get a descriptor in this case.
default:
assertLogAndThrow1(CAT_MVCAND, LL_ERROR,
FALSE, QRLogicException,
"Invalid value for MVQR_REWRITE_LEVEL: %d", (Int32)misc->getRewriteLevel());
}
}
// Create a new MVCandidate object for it.
MVCandidatePtr newCandidate = new(heap)
MVCandidate(mv, queryDetails, queryJbb, this, ADD_MEMCHECK_ARGS(heap));
if (mvMap != NULL)
newCandidate->setMvSubGraphMap(mvMap);
newCandidate->init(isPreferredMatch, extraGroupingColumns);
insert(newCandidate);
if (excuse != "")
{
// The reason we insert the MVCandidate and then disqualify it is that
// we want the disqualification message to be added to the result descriptor.
NAString msg(heap);
msg = "MV ";
msg.append(mv->getMVName());
msg.append(excuse);
newCandidate->logMVWasDisqualified(msg);
newCandidate->disqualify();
}
} // MVCandidatesForJBBSubset::insert()
/****************************************************************************/
// Insert an MV candidate object into this JBBSubset.
// Keep the list ordered by MV name.
/****************************************************************************/
void MVCandidatesForJBBSubset::insert(MVCandidatePtr mv)
{
const NAString& newMVName = mv->getMvDetails()->getMVName();
CollIndex maxEntries = candidateList_.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
if (candidateList_[i]->getMvDetails()->getMVName() > newMVName)
{
candidateList_.insertAt(i, mv);
return;
}
}
candidateList_.insertAt(maxEntries, mv);
}
/****************************************************************************/
// Generate the JBBSubset element of the result descriptor including the
// rewrite instructions and all the MV candidates inside it.
/****************************************************************************/
QRJbbSubsetPtr MVCandidatesForJBBSubset::generateDescriptor(CollHeap* heap)
{
// No candidates - no work.
if (candidateList_.entries() == 0)
return NULL;
// Construct the new QRJbbSubset object.
QRJbbSubsetPtr resultDesc = new(heap) QRJbbSubset(ADD_MEMCHECK_ARGS(heap));
resultDesc->setGroupBy(hasGroupBy_);
if (hasGroupBy_)
{
// Add a reference to the query NodeID of the GroupBy node.
const NAString& groupbyID =
getJbbCandidates()->getQueryJbb()->getGroupBy()->getID();
resultDesc->setRef(groupbyID);
}
// For each table included in the subgraph
QRJoinSubGraphPtr subGraph = querySubGraphMap_->getSubGraph();
for(subGraph->reset(); subGraph->hasNext(); subGraph->advance())
{
JoinGraphTablePtr table = subGraph->getCurrent();
// Add it to the JBBSubset descriptor.
QRTablePtr tableDesc = new(heap) QRTable(heap);
tableDesc->setTableName(table->getName());
tableDesc->setRef(table->getID());
resultDesc->addTable(tableDesc);
}
// If candidates are disqualified during the loop itself,
// Don't remove them from the list.
isLoopingOnCandidates_ = TRUE;
// Now add the MVCandidates.
CollIndex maxEntries = candidateList_.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
MVCandidatePtr candidate = candidateList_[i];
if (candidate == NULL)
{
// The candidate was disqualified.
continue;
}
QRCandidatePtr candidateXml;
try
{
candidateXml = candidate->generateDescriptor();
}
catch (...)
{
candidate->logMVWasDisqualified("an exception was thrown.");
candidate->disqualify(); // Propagate the Info element to the result descriptor.
candidateXml = NULL;
}
if (candidateXml == NULL)
{
// The candidate was disqualified when generating the descriptor.
continue;
}
resultDesc->addCandidate(candidateXml);
}
if (resultDesc->getCandidateList()->entries() == 0)
{
// None of the candidates survived the result descriptor generation.
deletePtr(resultDesc);
return NULL;
}
return resultDesc;
} // MVCandidatesForJBBSubset::generateDescriptor()
/*****************************************************************************/
// Remove the MVCandidate from the list, because it has been disqualified.
// Skip the removal if we are on the final loop of generating the result descriptor
// because that will throw the loop off its index.
/****************************************************************************/
void MVCandidatesForJBBSubset::disqualify(MVCandidatePtr candidate)
{
if (!isLoopingOnCandidates_)
{
candidateList_.remove(candidate);
}
}
/*****************************************************************************/
// Add this MVCandidate into the master list of all the MVCandiates for this query.
/****************************************************************************/
void MVCandidatesForJBBSubset::registerCandidate(MVCandidatePtr candidate)
{
jbbCandidates_->registerCandidate(candidate);
}
//========================================================================
// Class MVCandidatesForJBB
//========================================================================
/****************************************************************************/
/****************************************************************************/
MVCandidatesForJBB::MVCandidatesForJBB(QRJBBPtr queryJbb,
MVCandidateCollectionPtr allCandidates,
ADD_MEMCHECK_ARGS_DEF(CollHeap* heap))
: NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap))
,jbbSubsets_(heap)
,queryJbb_(queryJbb)
,allCandidates_(allCandidates)
,infoList_(heap)
{
}
/****************************************************************************/
/****************************************************************************/
MVCandidatesForJBB::~MVCandidatesForJBB()
{
CollIndex maxEntries = jbbSubsets_.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
deletePtr(jbbSubsets_[i]);
}
jbbSubsets_.clear();
}
/****************************************************************************/
/****************************************************************************/
void MVCandidatesForJBB::insert(MVCandidatesForJBBSubsetPtr jbbSubset)
{
CollIndex pos = 0;
while (pos < jbbSubsets_.entries() && jbbSubsets_[pos]->getKeyObject() )
pos++;
jbbSubsets_.insertAt(pos, jbbSubset);
}
/****************************************************************************/
/****************************************************************************/
NABoolean MVCandidatesForJBB::contains(QRJoinSubGraphPtr subGraph) const
{
CollIndex pos = 0;
while (pos < jbbSubsets_.entries() && jbbSubsets_[pos]->getKeyObject() )
pos++;
return (pos < jbbSubsets_.entries() &&
jbbSubsets_[pos]->getKeyObject() == subGraph->getFastKey() );
}
/****************************************************************************/
/****************************************************************************/
MVCandidatesForJBBSubsetPtr MVCandidatesForJBB::getJbbSubsetFor(QRJoinSubGraphPtr subGraph) const
{
CollIndex pos = 0;
while (pos < jbbSubsets_.entries() && jbbSubsets_[pos]->getKeyObject() )
pos++;
if (pos < jbbSubsets_.entries() &&
jbbSubsets_[pos]->getKeyObject() == subGraph->getFastKey() )
return jbbSubsets_[pos];
else
return NULL;
}
/****************************************************************************/
/****************************************************************************/
void MVCandidatesForJBB::addMatchInfo(const NAString* info)
{
// Keep info lines sorted.
CollIndex maxEntries = infoList_.entries();
if (maxEntries == 0)
{
infoList_.insert(info);
}
else
{
for (CollIndex i=0; i<maxEntries; i++)
{
if (*infoList_[i] > *info)
{
infoList_.insertAt(i, info);
return;
}
}
infoList_.insertAt(maxEntries, info);
}
}
/****************************************************************************/
// Generate the result descriptor for this JBB.
/****************************************************************************/
QRJbbResultPtr MVCandidatesForJBB::generateDescriptor(CollHeap* heap)
{
// Allocate the JBBResult element.
QRJbbResultPtr resultDesc = new(heap) QRJbbResult(ADD_MEMCHECK_ARGS(heap));
// Set the reference to the JBB in the query descriptor.
resultDesc->setRef(queryJbb_->getID());
CollIndex maxEntries = jbbSubsets_.entries();
// for (CollIndex i=0; i<maxEntries; i++)
for (Int32 i=maxEntries-1; i>=0; i--)
{
MVCandidatesForJBBSubsetPtr jbbSubset = jbbSubsets_[i];
QRJbbSubsetPtr jbbSubsetResult = jbbSubset->generateDescriptor(heap);
if (jbbSubsetResult != NULL)
resultDesc->addJbbSubset(jbbSubsetResult);
}
maxEntries = infoList_.entries();
for (CollIndex j=0; j<maxEntries; j++)
{
QRInfoPtr info = new (heap) QRInfo(ADD_MEMCHECK_ARGS(heap));
info->setText(*(infoList_[j]));
resultDesc->addInfoItem(info);
}
// If all the MV candidates of all the JBBSubsets for this JBB has been
// disqualified, Don't denerate an element for it.
if (resultDesc->getJbbSubsets().entries() == 0 && maxEntries == 0)
{
deletePtr(resultDesc);
resultDesc = NULL;
}
return resultDesc;
} // MVCandidatesForJBB::generateDescriptor()
/*****************************************************************************/
// Add this MVCandidate into the master list of all the MVCandiates for this query.
/****************************************************************************/
void MVCandidatesForJBB::registerCandidate(MVCandidatePtr candidate)
{
allCandidates_->registerCandidate(candidate);
}
//========================================================================
// Class MVCandidateCollection
//========================================================================
MVCandidateCollection::~MVCandidateCollection()
{
allCandidates_.clear();
CollIndex maxEntries = jbbs_.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
MVCandidatesForJBBPtr jbb = jbbs_[i];
deletePtr(jbb);
}
deletePtr(queryDetails_);
}
/****************************************************************************/
// Generate the result descriptor for this query.
/****************************************************************************/
QRResultDescriptorPtr MVCandidateCollection::generateEmptyResultDescriptor(CollHeap* heap)
{
// Allocate the result descriptor.
QRResultDescriptorPtr resultDesc = new(heap) QRResultDescriptor(ADD_MEMCHECK_ARGS(heap));
// Init the version element.
NAString versionString(CURRENT_VERSION, heap);
QRVersionPtr descVersion = new (heap) QRVersion(ADD_MEMCHECK_ARGS(heap));
descVersion->setVersionString(versionString);
resultDesc->setVersion(descVersion);
return resultDesc;
} // MVCandidateCollection::generateEmptyResultDescriptor()
/****************************************************************************/
// Generate the result descriptor for this query.
/****************************************************************************/
QRResultDescriptorPtr MVCandidateCollection::generateResultDescriptor(CollHeap* heap)
{
// Allocate the result descriptor.
QRResultDescriptorPtr resultDesc = generateEmptyResultDescriptor(heap);
// Add all the JBB elements.
for (CollIndex i=0; i<jbbs_.entries(); i++)
{
QRJbbResultPtr jbbResult = jbbs_[i]->generateDescriptor(heap);
if (jbbResult != NULL)
resultDesc->addJbbResult(jbbResult);
}
return resultDesc;
} // MVCandidateCollection::generateResultDescriptor()
/****************************************************************************/
// Add this MVCandidate into the master list of all the MVCandiates for this query.
/*****************************************************************************/
void MVCandidateCollection::registerCandidate(MVCandidatePtr candidate)
{
allCandidates_.insert(candidate);
}
/*****************************************************************************/
// Matching failed - remove the MV candidate from all the lists, and
// rerlease its memory.
/*****************************************************************************/
void MVCandidateCollection::disqualifyCandidate(MVCandidatePtr candidate, Int32 index)
{
candidate->disqualify(); // Remove from the JBBSubset
allCandidates_.removeAt(index); // Remove from the master list
deletePtr(candidate); // Delete it.
}
/****************************************************************************/
// Run the matching algorithms on all the MVCandidate objects.
// This is a temporary method, until the MV candidate reduction algorithm
// is implemented in a separate method.
/*****************************************************************************/
void MVCandidateCollection::doMatching()
{
// For now, just go over all the MV candidates, and run the matching
// algorithms one by one.
// The loop is running backwards so we can delete MV candidates that are
// disqualified, without breaking the loop index.
// Start with running Pass 1 tests on all candidates.
NABoolean matched = FALSE;
for (Int32 i=allCandidates_.entries()-1; i>=0; i--)
{
MVCandidatePtr candidate = allCandidates_[i];
if (!candidate->wasDisqualified())
{
// Run the output list matching, Pass 1.
try
{
matched = candidate->matchPass1();
}
catch (...)
{
// Skip only this candidate on exception, and proceed with matching
// for the rest.
QRLogger::log(CAT_MVCAND, LL_MVQR_FAIL,
"Disqualifying candidate %s due to exception on pass 1",
candidate->getMvDetails()->getMVName().data());
matched = FALSE;
}
if (!matched)
{
// Matching failed - disqualify the MV candidate.
disqualifyCandidate(candidate, i);
continue;
}
}
}
// Now do Pass 2 tests on the remaining candidates.
for (Int32 i=allCandidates_.entries()-1; i>=0; i--)
{
MVCandidatePtr candidate = allCandidates_[i];
// Run the output list matching, Pass 2.
try
{
matched = candidate->matchPass2();
}
catch (...)
{
// Skip only this candidate on exception, and proceed with matching
// for the rest.
QRLogger::log(CAT_MVCAND, LL_MVQR_FAIL,
"Disqualifying candidate %s due to exception on pass 2",
candidate->getMvDetails()->getMVName().data());
matched = FALSE;
}
if (!matched)
{
// Matching failed - disqualify the MV candidate.
disqualifyCandidate(candidate, i);
continue;
}
}
}
//========================================================================
// Class MVPair
//========================================================================
/*****************************************************************************/
/*****************************************************************************/
MVPair::MVPair(const QRJoinSubGraphMapPtr firstMV,
const QRJoinSubGraphMapPtr otherMV,
ADD_MEMCHECK_ARGS_DECL(CollHeap* heap))
: MVCandidate(otherMV->getMVDetails(),
firstMV->getMVDetails(),
firstMV->getMVDetails()->getJbbDetails()->getJbbDescriptor(),
NULL,
ADD_MEMCHECK_ARGS_PASS(heap))
,querySubGraphMap_(firstMV)
,mvSubGraphMap_(otherMV)
,rangePredList_(heap)
,residualPredList_(heap)
{
QRRangePredListPtr rangePredList =
getMvDetails()->getJbbDetails()->getJbbDescriptor()->getHub()->getRangePredList();
if (rangePredList)
rangePredList_.insert(rangePredList->getList());
QRResidualPredListPtr residualPredList =
getMvDetails()->getJbbDetails()->getJbbDescriptor()->getHub()->getResidualPredList();
if (residualPredList)
residualPredList_.insert(residualPredList->getList());
}
/*****************************************************************************/
/*****************************************************************************/
NABoolean MVPair::checkRangePredicate(QRRangePredPtr querySidePred)
{
QRRangePredPtr mvSidePred = getRangeMatching().checkPredicate(querySidePred);
if (mvSidePred)
{
rangePredList_.remove(mvSidePred);
return TRUE;
}
else
return FALSE;
}
/*****************************************************************************/
/*****************************************************************************/
NABoolean MVPair::checkResidualPredicate(QRExprPtr querySidePred)
{
QRExprPtr mvSidePred = getResidualMatching().checkPredicate(querySidePred);
if (mvSidePred)
{
residualPredList_.remove(mvSidePred);
return TRUE;
}
else
return FALSE;
}
/*****************************************************************************/
/*****************************************************************************/
void MVPair::intersectColumnBitmapsForTable(const NAString& queryTableID,
XMLBitmap& rangeBitmap,
XMLBitmap& residualBitmap)
{
// Find the MV table ID using the MVMemo mapping.
BaseTableDetailsPtr mvTableDetails = getMvTableForQueryID(queryTableID);
// Get the MV predicate bitmaps
const XMLBitmap& mvRangeBits = mvTableDetails->getRangeBits();
rangeBitmap.intersectSet(mvRangeBits);
const XMLBitmap& mvResidualBits = mvTableDetails->getResidualBits();
residualBitmap.intersectSet(mvResidualBits);
}
/*****************************************************************************/
/*****************************************************************************/
NABoolean MVPair::checkForLOJ(QRTablePtr queryTable)
{
// Find the MV table ID using the MVMemo mapping.
BaseTableDetailsPtr mvTableDetails = getMvTableForQueryID(queryTable->getID());
return mvTableDetails->getTableElement()->hasLOJParent();
}