blob: 7b2209bb09d447a8c789aeaf302ded07d6d5b664 [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 "QmsQms.h"
#include "QRDescriptor.h"
#include "QRLogger.h"
#include "QmsMVMemo.h"
#include "NAType.h"
#include "ComRtUtils.h"
/**
* \file
* A command-line version of the Query Matching Server (QMS). The purpose of the
* program is to provide a simplified testing tool for Query Rewrite requests,
* although it could be adapted to start either a simple executable or a server
* process, based on an additional command-line argument. Presently there are
* two arguments, an input file and an output file. The input file should contain
* a number of message specifiers (one per line), each consisting of the header
* part of the message and a reference to a file containing the associated XML
* document (if the specific message type requires one). The output file will
* contain the return codes indicating the outcome of processing each message in
* the input file, as well as result descriptors for any messages that specify a
* MATCH request.
*/
// This is needed to avoid a link error.
NABoolean NAType::isComparable(const NAType &other,
ItemExpr *parentOp,
Int32 emitErr) const
{ return FALSE; }
Qms* Qms::instance_ = NULL;
Qms::~Qms()
{
MVDetailHashIterator iterator(MVInventoryHash_);
const NAString* key;
MVDetailsPtr mvDetails;
for (CollIndex i = 0; i < iterator.entries(); i++)
{
// Get the next MV
iterator.getNext(key, mvDetails);
// For reference counting.
mvDetails = NULL;
// Drop the MV.
drop(*key);
}
}
/**
* Matches the passed query descriptor against known MVs, determining which MVs
* are candidates for rewriting the query by replacing JBB subsets with the
* already-computed results of the MV. These candidates, along with the pertinent
* rewrite instructions, are included in the result descriptor.
*
* @param qryDescPtr Pointer to the document element of the descriptor for the
* query that is to be rewritten.
* @param requestHeap The heap from which to allocate temporary objects.
*/
QRResultDescriptorPtr Qms::match(QRQueryDescriptorPtr qryDescPtr, NAMemory* requestHeap)
{
static Int32 queryNumber = 0;
try
{
DescriptorDetailsPtr queryDetails = new(requestHeap)
DescriptorDetails(qryDescPtr, FALSE, ADD_MEMCHECK_ARGS(requestHeap));
queryDetails->init(requestHeap);
MVCandidateCollectionPtr mvCandidates = new(requestHeap)
MVCandidateCollection(queryDetails, ADD_MEMCHECK_ARGS(requestHeap));
// Count this query being matched.
queryNumber++;
// For each JBB in the query descriptor, search for MV candidates.
const NAPtrList<QRJBBPtr>& jbbs = qryDescPtr->getJbbList();
for (CollIndex i = 0; i < jbbs.entries(); i++)
{
QRJBBPtr jbb = jbbs[i];
// Collection of MVCandidates for this JBB.
MVCandidatesForJBBPtr jbbCandidates = new(requestHeap)
MVCandidatesForJBB(jbb, mvCandidates, ADD_MEMCHECK_ARGS(requestHeap));
// Search MVMemo for candidate MVs for this JBB
try
{
mvMemo_.search(jbb, jbbCandidates, requestHeap);
}
catch (...)
{
// Exception occurred, skip this JBB and keep going.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"JBB %s skipped due to exception", jbb->getID().data());
deletePtr(jbbCandidates);
continue;
}
mvCandidates->insert(jbbCandidates);
}
// Continue matching Pass 1 and Pass 2 algorithms.
mvCandidates->doMatching();
// Generate the result descriptor.
QRResultDescriptorPtr resultDesc = mvCandidates->generateResultDescriptor(requestHeap);
deletePtr(mvCandidates);
//dumpInventoryHash();
return resultDesc;
}
catch (QRException e)
{
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL, "MATCH operation aborted.");
// Return an empty result descriptor.
QRResultDescriptorPtr resultDesc = MVCandidateCollection::generateEmptyResultDescriptor(requestHeap);
return resultDesc;
}
catch (...)
{
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"MATCH operation aborted because of unknown exception.");
// Return an empty result descriptor.
QRResultDescriptorPtr resultDesc = MVCandidateCollection::generateEmptyResultDescriptor(requestHeap);
return resultDesc;
}
}
/**
* Insert a new MV into the QMS matching data structures.
* @param mvDescPtr Descriptor of MV.
*/
void Qms::insert(QRMVDescriptorPtr mvDescPtr, const QRMVDefinition* mvDefPtr)
{
static Int32 mvNumber = 0;
try
{
// Count this query being matched.
mvNumber++;
MVDetailsPtr mvDetails = new(heap_)
MVDetails(mvDescPtr, ADD_MEMCHECK_ARGS(heap_));
mvDetails->init(heap_);
if (mvDefPtr)
{
mvDetails->setRedefTimestamp(mvDefPtr->redefTimeString_);
mvDetails->setRefreshTimestamp(mvDefPtr->refreshedTimeString_);
mvDetails->setIgnoreChanges(mvDefPtr->hasIgnoreChanges_);
}
// Iterate on the JBBs
const NAPtrList<QRJBBPtr>& jbbs = mvDescPtr->getJbbList();
if (jbbs.entries() > 1)
{
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"MV Insert operation aborted - Only MVs with a single JBB are supported for now.");
return;
}
for (CollIndex i = 0; i < jbbs.entries(); i++)
{
QRJBBPtr jbb = jbbs[i];
// Insert it into MVMemo
mvMemo_.insert(jbb, mvDetails);
}
MVInventoryHash_.insert(&mvDetails->getMVName(), mvDetails);
//dumpInventoryHash();
}
catch (QRException e)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"PUBLISH operation aborted, An Exception occurred: %s", e.getMessage());
}
catch (...)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"PUBLISH operation aborted, An unknown exception occurred.");
}
}
/**
* Remove an MV from the matching data structures.
* @param mvName Name of MV to remove.
*/
void Qms::drop(const NAString& mvName)
{
try
{
// If the MV is contained in QMS
MVDetailsPtr mv = getMvDetails(mvName);
if (mv != NULL)
{
// Remove it from the inventory.
MVInventoryHash_.remove(&mvName);
// Disengage it from MVMemo.
mv->disengage();
// Now delete the MVDetails object
deletePtr(mv);
}
//dumpInventoryHash();
}
catch (QRException e)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"DROP operation aborted.");
}
catch (...)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"DROP operation aborted because of unknown exception.");
}
}
/**
* Touch the redefinition timestamp of an MV.
* @param mvName Name of MV to touch.
* @param timestamp New redefinition timestamp of MV.
*/
void Qms::touch(const NAString& mvName, const NAString& timestamp)
{
MVDetailsPtr mvDetails = getMvDetails(mvName);
if (mvDetails != NULL)
mvDetails->setRedefTimestamp(timestamp);
else
QRLogger::instance().log(CAT_QMS_MAIN, LL_INFO,
"Unable to TOUCH MV %s - not found in QMS.", mvName.data());
}
/**
* Alter the IGNORE_CHANGES value of an MV.
* @param mvName Name of MV to alter.
* @param hasIgnoreChanges new IGNORE_CHANGES boolean value.
*/
void Qms::alter(const NAString& mvName, NABoolean hasIgnoreChanges)
{
MVDetailsPtr mvDetails = getMvDetails(mvName);
if (mvDetails != NULL)
{
mvDetails->setIgnoreChanges(hasIgnoreChanges);
}
else
QRLogger::instance().log(CAT_QMS_MAIN, LL_INFO,
"Unable to ALTER MV %s - not found in QMS.", mvName.data());
}
/**
* Alter the last refresh timestamp of an MV.
* @param mvName Name of MV to alter.
* @param timestamp New last refresh timestamp of MV.
*/
void Qms::refresh(const NAString& mvName, const NAString& timestamp, NABoolean isRecompute)
{
MVDetailsPtr mvDetails = getMvDetails(mvName);
if (mvDetails != NULL)
{
mvDetails->setRefreshTimestamp(timestamp);
if (isRecompute)
mvDetails->setConsistent(TRUE);
else if (mvDetails->hasIgnoreChanges())
mvDetails->setConsistent(FALSE);
}
else
QRLogger::instance().log(CAT_QMS_MAIN, LL_INFO,
"Unable to Refresh MV %s - not found in QMS.", mvName.data());
}
/**
* Rename an MV from mvName to newName.
* @param oldName The old name of the MV.
* @param newName The new name of the MV.
* @param timestamp New redefinition timestamp of MV.
* \todo Not sure this is complete. Is the MV name used anywhere else?
*/
void Qms::rename(const NAString& oldName, const NAString& newName)
{
try
{
MVDetailsPtr mvDetails = getMvDetails(oldName);
if (mvDetails != NULL)
{
// Remove it from the inventory.
MVInventoryHash_.remove(&oldName);
// Rename the MVDetails object.
mvDetails->rename(oldName, newName);
// Re-insert with the new name.
MVInventoryHash_.insert(&mvDetails->getMVName(), mvDetails);
//dumpInventoryHash();
}
else
QRLogger::instance().log(CAT_QMS_MAIN, LL_INFO,
"Unable to rename MV %s - not found in QMS.", oldName.data());
}
catch (QRException e)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL, "Rename operation aborted.");
}
catch (...)
{
// Ignore exceptions for now.
QRLogger::instance().log(CAT_QMS_MAIN, LL_MVQR_FAIL,
"Rename operation aborted because of unknown exception.");
}
}
/**
* Check if QMS includes an MV named mvName.
* @param mvName The name of the MV to find.
* @return TRUE if the MV is found, FALSE otherwise.
*/
NABoolean Qms::contains(const NAString& mvName)
{
MVDetailsPtr mvDetails = getMvDetails(mvName);
return (mvDetails != NULL);
}
/**
* Get the redefinition timestamp of an MV.
* @param mvName The name of the MV to find.
* @return The redefinition timestamp as an Int64 number, or NULL if the MV is not contained in QMS.
*/
const Int64 *Qms::getMVTimestamp(const NAString& mvName)
{
MVDetailsPtr mvDetails = getMvDetails(mvName);
if (mvDetails == NULL)
return NULL;
else
return &mvDetails->getRedefTimestamp();
}
void Qms::dumpInventoryHash()
{
QRLogger::instance().log(CAT_QMS_MAIN, LL_DEBUG, "Dumping MV Inventory:");
MVDetailHashIterator iterator(MVInventoryHash_);
const NAString* key;
MVDetailsPtr mvDetails;
for (CollIndex i = 0; i < iterator.entries(); i++)
{
iterator.getNext(key, mvDetails);
QRLogger::instance().log(CAT_QMS_MAIN, LL_DEBUG, key->data());
}
}
/**
* Perform workload analysis.
* all the workload queries have already been loaded as MV descriptors.
* Now we need to:
* 1. Collect groups of queries that share the same join graph and GroupBy list.
* 2. Perform perdicate analysis on each group.
* 3. Generate the SQL text for the CREATE MV command for the MV we ar proposing.
*/
void Qms::workloadAnalysis(ofstream& ofs, Int32 minQueriesPerMV, NAMemory* requestHeap)
{
WorkloadAnalysisPtr workload = new(requestHeap) WorkloadAnalysis(requestHeap);
// Collect the join graph + GroupBy list information
mvMemo_.collectMVGroups(workload, minQueriesPerMV, requestHeap);
// Perform predicate analysis and generate the SQL.
workload->reportResults(ofs, minQueriesPerMV);
deletePtr(workload);
}