blob: 80fada4c7d6401f7b62c4c0bc6b181761cbb14e4 [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 @@@
// **********************************************************************
#include "QmsMVMemo.h"
#include "QmsGroupLattice.h"
#include "QmsMVCandidate.h"
//
// QRGroupLattice
//
QRGroupLattice::QRGroupLattice(CollHeap* heap,
ADD_MEMCHECK_ARGS_DEF(CollIndex maxNumKeys))
: NAIntrusiveSharedPtrObject(ADD_MEMCHECK_ARGS_PASS(heap)),
lattice_(new QRLatticeIndex(heap, maxNumKeys)),
reverseKeyHash_(hashKey, INIT_HASH_SIZE_SMALL, TRUE, heap), // Pass NAString::hashKey
heap_(heap)
{
}
QRGroupLattice::~QRGroupLattice()
{
deletePtr(lattice_);
}
void QRGroupLattice::dumpLattice(const char* tag)
{
QRTRACER("QRGroupLattice::dumpLattice()");
if (QRLogger::isCategoryInDebug(CAT_GRP_LATTCE_INDX))
{
NAString graph(heap_);
lattice_->dumpLattice(graph, tag);
QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, graph.data());
}
}
/**
* This method can be used to dump detailed usage stats to the log file,
* including MVMemo hash keys and LatticeIndex graphs.
* Currently not used.
*****************************************************************************
*/
void QRGroupLattice::reportStats(NAString& text)
{
QRTRACER("QRGroupLattice::reportStats()");
text += "\nLatticeIndex Graph:\n";
lattice_->dumpLattice(text, "stats");
}
void QRGroupLattice::collectMVGroups(WorkloadAnalysisPtr workload, Int32 minQueriesPerMV, CollHeap* heap)
{
lattice_->collectMVGroups(workload, minQueriesPerMV, heap);
}
void QRGroupLattice::insert(QRJoinSubGraphMapPtr map, const QRJBBPtr jbb)
{
QRTRACER("QRGroupLattice::insert()");
LatticeKeyList keyList;
NABoolean ok = getGroupingLatticeKeys(keyList, map, map->getMVDetails(), jbb, FALSE, TRUE);
assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR,
ok, QRLogicException,
"getGroupingLatticeKeys() failed in Insert mode (Inserting a multi-JBB MV?).");
lattice_->addMV(keyList, map);
dumpLattice("1");
}
void QRGroupLattice::remove(QRJoinSubGraphMapPtr map)
{
QRTRACER("QRGroupLattice::remove()");
const QRJBBPtr jbb = map->getMVDetails()->getJbbDetails()->getJbbDescriptor();
LatticeKeyList keyList;
NABoolean ok = getGroupingLatticeKeys(keyList, map, map->getMVDetails(), jbb, FALSE, TRUE);
assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR,
ok, QRLogicException,
"getGroupingLatticeKeys() failed in Insert mode (Inserting a multi-JBB MV?).");
lattice_->removeMV(keyList, map->getMVDetails());
dumpLattice("2");
}
NABoolean QRGroupLattice::getGroupingLatticeKeys(LatticeKeyList& keyList,
QRJoinSubGraphMapPtr map,
DescriptorDetailsPtr queryDetails,
const QRJBBPtr jbb,
NABoolean primaryOnly,
NABoolean insertMode)
{
QRTRACER("QRGroupLattice::getGroupingLatticeKeys()");
// Start with the list of primary grouping columns in the Hub.
const QRGroupByPtr groupBy = jbb->getGroupBy();
// In the aggregate with no GroupBy case, return an empty list.
if (groupBy->isEmpty())
return TRUE;
const ElementPtrList& gbList = groupBy->getPrimaryList()->getList();
NABoolean ok = elementListToKeyList(gbList, keyList, map, queryDetails, insertMode);
if (!ok)
return FALSE;
if (primaryOnly)
return TRUE;
// Now check for dependent grouping columns in the extra-Hub.
QRDependentGroupByPtr depGroupBy = groupBy->getDependentList();
if (depGroupBy != NULL)
{
const ElementPtrList& depKeyList = depGroupBy->getList();
if (!depKeyList.isEmpty())
{
// Dependent grouping columns found - add them to the key list.
ok = elementListToKeyList(depKeyList, keyList, map, queryDetails, insertMode);
if (!ok)
return FALSE;
}
}
return TRUE;
}
NABoolean QRGroupLattice::elementListToKeyList(const ElementPtrList& elementList,
LatticeKeyList& keyList,
QRJoinSubGraphMapPtr map,
DescriptorDetailsPtr queryDetails,
NABoolean insertMode)
{
CollIndex maxEntries = elementList.entries();
for (CollIndex i=0; i<maxEntries; i++)
{
QRElementPtr gbElement = elementList[i];
LatticeIndexablePtr key = elementToKey(gbElement, map, queryDetails, insertMode);
if (key == NULL)
{
assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR,
!insertMode, QRLogicException,
"elementToKey() failed in Insert mode (Inserting a multi-JBB MV?).");
// Cleanup
for (CollIndex j=0; j<i; j++)
delete keyList[j];
return FALSE;
}
keyList.insert(key);
}
return TRUE;
}
MVCandidatesForJBBSubsetPtr QRGroupLattice::search(QRJBBPtr queryJbb,
MVCandidatesForJBBPtr mvCandidates,
QRJoinSubGraphMapPtr map,
ElementPtrList* minimizedGroupingList)
{
QRTRACER("QRGroupLattice::search()");
DescriptorDetailsPtr queryDetails = mvCandidates->getAllCandidates()->getQueryDetails();
LatticeKeyList keyList(heap_);
if (minimizedGroupingList)
{
if (!elementListToKeyList(*minimizedGroupingList, keyList, map, queryDetails, FALSE))
return NULL;
}
else
{
// Get the grouping columns of the passed query JBB, and create a key list
// from them. Return now if there are no grouping items -- a query without a
// Group By can't match an MV that has one.
QRGroupByPtr groupBy = queryJbb->getGroupBy();
if (!groupBy)
return NULL;
// An aggregate query with no GroupBy has an empty key list.
if (!getGroupingLatticeKeys(keyList, map, queryDetails, queryJbb, TRUE, FALSE))
return NULL;
}
// Create the search node, disallowing the addition of any new keys to the hash
// table used by the lattice; if the key isn't already used, the node can have
// no supersets.
QRLatticeIndexSearchNode searchNode(keyList, lattice_, TRUE, heap_);
if (!searchNode.isValid()) // because a key wasn't added
return NULL;
NAString keysText;
searchNode.dumpNode(keysText, *lattice_->getKeyArr());
QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG,
"Searching LatticeIndex for: %s.", keysText.data());
// Find the supersets of the query's group-by list in the MV group lattice
// index. For each lattice index node returned, create an MVCandidate
// corresponding to each MV represented by that node, and add it to the
// candidate list passed by the caller.
NAPtrList<QRLatticeIndexNodePtr> nodes;
lattice_->findSupersets(searchNode, nodes);
if (nodes.entries() == 0)
{
QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG, "No match found.");
return NULL;
}
MVCandidatesForJBBSubsetPtr jbbSubset = new(heap_)
MVCandidatesForJBBSubset(mvCandidates, ADD_MEMCHECK_ARGS(heap_));
jbbSubset->setSubGraphMap(map);
jbbSubset->setGroupBy(TRUE);
if (minimizedGroupingList != NULL)
jbbSubset->setMinimizedGroupingList(minimizedGroupingList);
for (CollIndex i=0; i<nodes.entries(); i++)
{
QRLatticeIndexNodePtr thisNode = nodes[i];
const SubGraphMapList& matchingMVs = thisNode->getMVs();
NABoolean isPreferred = (*thisNode == searchNode); // preferred if exact match
GroupingList* extraGroupingColumns = new(heap_) GroupingList(heap_);
if (!isPreferred)
{
// For preferred match modes, there are no extraGroupingColumns.
LatticeKeySubArray* diff = thisNode->computeDiff(searchNode, heap_);
for(CollIndex i = 0; diff->nextUsed(i); i++)
{
LatticeIndexablePtr key = diff->element(i);
QRElementPtr elem = keyToElement(key, map);
extraGroupingColumns->insert(elem);
}
delete diff;
}
for (CollIndex j=0; j<matchingMVs.entries(); j++)
{
MVDetailsPtr mv = matchingMVs[j]->getMVDetails();
QRLogger::log(CAT_GRP_LATTCE_INDX, LL_DEBUG,
"Found MV: %s!", mv->getMVName().data() );
jbbSubset->insert(mv, queryJbb, isPreferred, extraGroupingColumns, NULL, heap_);
}
}
return jbbSubset;
} // QRGroupLattice::search()
LatticeIndexablePtr QRGroupLattice::elementToKey(const QRElementPtr element,
QRJoinSubGraphMapPtr map,
DescriptorDetailsPtr queryDetails,
NABoolean insertMode,
NABoolean isRecursive)
{
QRElementPtr elem = element->getReferencedElement();
QRColumnPtr col = NULL;
if (elem->getElementType() == ET_Column)
{
col = elem->downCastToQRColumn();
if (queryDetails->isFromJoin(col) && !isRecursive)
{
// This groupby element is a join pred.
// During insert, this loop inserts the first column.
// During search, try each equality set element until we find one that
// appears in our array.
const QRJoinPredPtr eqSet = queryDetails->getJoinPred(col);
const ElementPtrList& equalityList = eqSet->getEqualityList();
if (insertMode)
{
// Insert mode - pick the first column of the equality set.
col = equalityList[0]->downCastToQRColumn();
}
else
{
// Search mode - pick the first equality set entry that is found in this LatticeIndex.
for (CollIndex i=0; i<equalityList.entries(); i++)
{
QRElementPtr entry = equalityList[i]->getReferencedElement();
// Call recursively for each join pred column.
LatticeIndexablePtr entryKey = elementToKey(entry, map, queryDetails, insertMode, TRUE);
if (entryKey == NULL)
continue;
if (lattice_->contains(entryKey))
{
// Found it in the lattice index.
col = entry->downCastToQRColumn();
break;
}
} // for ( )
// If none of the entries was found - give up now.
if (col == NULL)
return NULL;
} // if insert mode
} // if JoinPred
} // if Column
NAString* key = NULL;
if (col != NULL)
{
col = col->getReferencedElement()->downCastToQRColumn();
const NAString& tableID = col->getTableID();
Int32 Inx = map->getIndexForTable(tableID);
if (Inx == -1)
{
assertLogAndThrow(CAT_GRP_LATTCE_INDX, LL_ERROR,
!insertMode, QRLogicException,
"Table index not found in Insert mode (Inserting a multi-JBB MV?).");
return NULL;
}
char buffer[4096+5];
sprintf(buffer, "%d.%s", Inx, col->getColumnName().data() );
key = new(heap_) NAString(buffer, heap_);
}
else
{
// This is an expression.
// TBD We still do not handle expressions using columns from self-join tables.
key = new(heap_) NAString(element->getSortName(), heap_);
}
// The reverseKeyHash should always use the most recent MV inserted.
if (insertMode)
{
reverseKeyHash_.remove(key);
reverseKeyHash_.insert(key, element);
}
return key;
}
QRElementPtr QRGroupLattice::keyToElement(LatticeIndexablePtr key, QRJoinSubGraphMapPtr map)
{
const QRElementPtr elem = reverseKeyHash_.getFirstValue(key);
return elem;
}