blob: f2ddd49d1b9538a4e1b2823b6008a89aec45d022 [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 @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: OptLogRelExpr.C
* Description: Optimizer methods related to Logical Expressions
*
* Created: 5/17/94
* Language: C++
*
*
*
*
*****************************************************************************
*/
// -----------------------------------------------------------------------
#include "Sqlcomp.h"
#include "GroupAttr.h"
#include "AllRelExpr.h"
#include "AllItemExpr.h"
#include "Cost.h"
#include "opt.h"
#include "PhyProp.h"
#include "OptTrigger.h"
#include "hs_read.h"
#include "Analyzer.h"
#include "AppliedStatMan.h"
#include "NormWA.h"
#include "CmpStatement.h"
#include <math.h>
// -----------------------------------------------------------------------
// member functions for class RelExpr
// -----------------------------------------------------------------------
RelExpr *
RelExpr::optimizeNode()
{
RelExpr *result;
NABoolean printStats = CURRSTMT_OPTGLOBALS->BeSilent;
//if (getenv("PRINT_OPTIMIZER_STATISTICS"))
CURRSTMT_OPTGLOBALS->BeSilent = FALSE;
// ---------------------------------------------------------------------
// optimize
// ---------------------------------------------------------------------
NABoolean useNewOptDriver =
(CmpCommon::getDefault(NEW_OPT_DRIVER) == DF_ON);
if(useNewOptDriver)
result = optimize2();
else
result = optimize();
CURRSTMT_OPTGLOBALS->BeSilent = printStats;
// release the bindings so the following phases can modify the tree
if (result != NULL)
result->releaseBindingTree();
// The search space can now be deleted.
// Since the group attributes have a reference count, they will still be
// available for the result tree. Contexts will be gone, however.
//delete memo;
CURRSTMT_OPTGLOBALS->memo = NULL;
return result;
} // RelExpr::optimizeNode()
void
RelExpr::cleanupAfterCompilation()
{
// CascadesMemo now gets deleted before the generator phase, no need for this
// method anymore
// delete memo;
// memo = NULL;
// delete secondaryMemo;
// secondaryMemo = NULL;
} // RelExpr::cleanupAfterCompilation()
// how many moves to pursue during exploration?
Int32
RelExpr::computeExploreExprCutoff(RuleWithPromise [],
Int32 numberOfMoves,
RelExpr *,
Guidance *)
{
// default implementation: use all promising moves
return numberOfMoves;
} // RelExpr::computeExploreExprCutoff()
// given an array of moves (rule applications with a given
// promise), now many of the moves should actually be performed?
Int32
RelExpr::computeOptimizeExprCutoff(RuleWithPromise [],
Int32 numberOfMoves,
Guidance *,
Context *)
{
// default implementation: use all promising moves
return numberOfMoves;
} // RelExpr::computeOptimizeExprCutoff()
// -----------------------------------------------------------------------
// This is a helper method for the purpose of synthesizing estimated
// logical properties for unary and leaf operators. This method is
// invoked by various synthEstLogProp() methods.
// -----------------------------------------------------------------------
EstLogPropSharedPtr
RelExpr::synthEstLogPropForUnaryLeafOp (const EstLogPropSharedPtr& inputEstLogProp,
const ColStatDescList & childStats,
CostScalar initialRowcount,
CostScalar *childMaxCardEst) const
{
CollIndex outerRefCount = 0;
OperatorTypeEnum opType = getOperatorType();
// ------------------------------------------------------------------------
// Special case for estimated rowcounts of SQL update operations (e.g.,
// "update t1 set a = 3 where a < 2;") for columns which have an index.
//
// If this is a REL_LEAF_[DELETE | UPDATE | INSERT] node, and there are
// not any selection predicates, then the outputestlogprop is exactly the
// same as the inputestlogprop (because we're deleting / updating /
// inserting rows that are exactly specified by the input key predicates).
switch (opType) {
case REL_LEAF_DELETE:
case REL_LEAF_UPDATE:
case REL_LEAF_INSERT: // For insert, we should take into account the initial
// tableColStats of the Insert table too
if ( getSelectionPred().entries() == 0 )
{
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(*inputEstLogProp));
GenericUpdate *updateExpr = (GenericUpdate *) this;
// do it only if the update expression is on a normal table
if (updateExpr->getTableName().getSpecialType() == ExtendedQualName::NORMAL_TABLE)
{
// create myColStats after mapping my child's columns to my columns.
ValueIdMap & updateSelectValueIdMap = updateExpr->updateToSelectMap();
myEstProps->mapOutputsForUpdate(*updateExpr, updateSelectValueIdMap);
}
return myEstProps;
}
default:
break ;
}
// ------------------------------------------------------------------------
// output est log prop -- allocate it
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(1));
// Start with the provided initial rowcount.
CostScalar newRowCount = initialRowcount;
// If input column statistics have been provided, reset the initial new
// rowcount.
if (childStats.entries() > 0)
newRowCount = childStats[0]->getColStats()->getRowcount();
//++MV
// remember the initial cardinality for later use
CostScalar initialCardinality = newRowCount;
//--MV
// Part of fix to genesis cases 10-080530-0291, 10-080530-0305,
// solution 10-080530-3538 "R2.4NF:RQ:AND predicate MIN sel not always
// chosen for Max Card calc".
// Computation of expected cardinality estimates routinely SIDE EFFECT
// histograms. For example, given a compound predicate
// "col = ? and col like pattern"
// we used to make an estimateCardinality() call that computes
// maxSelectivity of "col = ?" but that SIDE EFFECTs the histogram.
// So, when a 2nd estimateCardinality() call computes maxSelectivity of
// "col like pattern", that maxSelectivity reflects the modified histogram
// which may be good for expected cardinality estimates but bad for max
// cardinality estimates.
// One way of fixing this problem is to split estimateMaxSelectivity and
// estimateCardinality into separate methods. But, that duplicates code
// and causes code maintenance problems.
// We tried to make these two methods operate on the same ColStatDescList
// instance. But, that does not work. It breaks estimated cardinality or
// it breaks max cardinality. Sigh :-(
// So, the solution is to have two ColStatDescList instances and have one
// method, estimateCardinality, compute both max selectivity & expected
// cardinality using 2 calls with different arguments.
// maxRows is our initial max cardinality estimate.
// For some queries, eg, "select * from t10 where d not like 'one%'"
// the max cardinality estimate for the root node can be below actual
// because RelRoot::synthEstLogProp() calls
// synthEstLogPropForUnaryLeafOp(inputEstLogProp,
// child(0).outputLogProp(inputEstLogProp)->getColStats(),
// child(0).outputLogProp(inputEstLogProp)->getResultCardinality())
// Since max cardinality should never be below actual, we correct this by
// also passing the child's max cardinality estimate
// synthEstLogPropForUnaryLeafOp(inputEstLogProp,
// child(0).outputLogProp(inputEstLogProp)->getColStats(),
// child(0).outputLogProp(inputEstLogProp)->getResultCardinality(),
// child(0).outputLogProp(inputEstLogProp)->getMaxCardEst())
// which shows up here as a non-null childMaxCardEst.
CostScalar maxRows =
(childMaxCardEst ? (*childMaxCardEst) : initialCardinality);
// unless overridden by downstream code, maxSelectivity is 1.0
// this is a diffused & obscure way of computing maxCardinality and
// maxSelectivity.
// but we have to fit in without breaking existing expected cardinality
// estimation code.
CostScalar maxSelectivity = csOne;
NABoolean indexNestedJoin = FALSE;
CollIndex outerInd;
MergeType mergeMethod = INNER_JOIN_MERGE; // starting assumption
ColStatDescList columnStats(CmpCommon::statementHeap());
if (opType != REL_SCAN)
{
// make the local copy of the information provided to this 'leaf' operator
columnStats.makeDeepCopy (childStats) ;
// We want to be sure that all colStats have the same row count.
// It should assert if these are different
#define MONITOR_SAMEROWCOUNT
columnStats.verifyInternalConsistency( 0, columnStats.entries() );
#undef MONITOR_SAMEROWCOUNT
}
else
// ----------------------------------------------------------------------
// If the RelExpr being processed is a scan (tbd: other operators too?)
// there are numerous scan-specific actions to perform.
// The inputEstLogProp's associated with true outer references (i.e., not
// a host variable, or parameter), have been passed down from their source
// largely unchanged. They are now effectively cross-producted with the
// child's statistics.
// This allows parents of the scan to largely ignore the inputEstLogProps
// they are given as input; the still-interesting descriptions of outer
// references will be part of the output of their children.
//
// NOTE: This is not what run-time does; run-time doesn't provide output
// that is identical with supplied input. This treatment of input data
// (outer references) is done to simplify logical property maintenance,
// primarily in the area of histograms.
//
// The cross-producting of outer references' and child's statistics is
// not done in either of two situations:
// - The outer reference contains data from an index of the current table.
// - The input description of the outer references indicate that they are
// the result of a nested [anti-]semi-join.
//
// In the case of an index-driven nested join, the estimated RowCount of
// the inner table should be scaled down to match that reported in the
// outer table prior to executing any additional join.
//
// Further, for index-driven nested joins, the base table columns'
// statistics -- those associated with columns provided by the index
// table as outer references -- are removed. Their equivalent index
// column stats provide the result stats for the join between the index
// table and the base table.
//
// NOTE: this code assumes that all predicates that can be pushed down
// to 'identical' columns are pushed down to all copies of those
// columns.
// ----------------------------------------------------------------------
{
// -----------------------------------------------------------------
// First, determine if we seem to be in an index-driven nested join.
// And simultaneously, make a copy of only those base table column statistics
// that are not provided as outer references from the index table.
// -----------------------------------------------------------------
const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();
outerRefCount = outerColStatsList.entries();
CostScalar inputCard = inputEstLogProp->getResultCardinality();
if (!outerRefCount)
{
// no outer references, copy all base columns
columnStats.makeDeepCopy (childStats) ;
}
else
{
// copy only those columns which are not provided as outer references
CollIndex i;
for (i = 0; i < childStats.entries(); i++)
{
ColStatDescSharedPtr columnStatDesc = childStats[i];
for (outerInd = 0; (Int32)outerInd < outerRefCount; outerInd++)
{
ColStatDescSharedPtr outerStatDesc = outerColStatsList[outerInd];
if (outerStatDesc->getMergeState().contains(
columnStatDesc->getMergeState()) )
{
indexNestedJoin = TRUE;
break;
}
}
if((Int32)outerInd == outerRefCount)
{
ColStatDescSharedPtr tmpColStatDescPtr(new (HISTHEAP)
ColStatDesc(*childStats[i]), HISTHEAP);
ColStatsSharedPtr tmpColStatsPtr = tmpColStatDescPtr->getColStatsToModify();
tmpColStatsPtr->copyAndScaleHistogram(1);
columnStats.insert(tmpColStatDescPtr );
}
}
// now set the Uec list of the copy from the original colStats
columnStats.setUecList( childStats.getUecList() );
}
columnStats.setInputCard(inputCard);
// After doing all these copies, we want to be sure that all
// colStats have the same row count. It should assert if these are
// different
#define MONITOR_SAMEROWCOUNT
columnStats.verifyInternalConsistency( 0, columnStats.entries() );
#undef MONITOR_SAMEROWCOUNT
// -----------------------------------------------------------------
// Second, pre-pend the outer reference column statistics onto the
// [remaining] child's column statistics.
// -----------------------------------------------------------------
CostScalar innerScale =
(indexNestedJoin ||
inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ ?
1 : newRowCount);
// There can be cases where the outerColStatsList is empty, but there are
// still rows coming into the right child. See Sol: 10-030327-5157.
// For such cases the columnStats cardinality does not get adjusted even though
// the input cardinality > 1. Sscale histograms with the input cardinality.
if (outerRefCount > 0)
columnStats.prependDeepCopy (outerColStatsList, // source ColStatDescList
outerRefCount, // prepend limit
innerScale, // scale prepended Rowcounts
FALSE) ; // clear shapeChanged flag
else
if (inputEstLogProp->getResultCardinality() > csOne) // no colStats to prepend, still the rowcount needs to be adjusted
{
CostScalar inputRowcount = inputEstLogProp->getResultCardinality();
CollIndex colEntries = columnStats.entries();
for (CollIndex i = 0; i < colEntries; i++)
{
ColStatDescSharedPtr columnStatDesc = columnStats[i];
ColStatsSharedPtr tempColumnStats = columnStatDesc->getColStatsToModify();
tempColumnStats->copyAndScaleHistogram ( inputRowcount );
}
}
if (columnStats.entries() > 0)
newRowCount = columnStats[0]->getColStats()->getRowcount();
// -----------------------------------------------------------------
// Third, scale the original entries in the child's column stats. The
// actual determination of how this is done is based on knowing
// whether or not this is an index-driven nested join.....
//
// I.e., if it's an index-based NJ, then both RowCount and UEC of
// the 'base' table are scaled; if it's not index-based, then the
// scaling depends upon whether or not this scan is the right child
// of a [anti-]semi-TSJ. If not, scaling is done as with join
// cross-products; otherwise (it *is* the R child of a [anti-]semiTSJ), the
// right side isn't scaled.
// For case where outerrefCount = 0, but innerScale is greater than 1, the
// scaling of histograms has already been done.
// -----------------------------------------------------------------
CostScalar outerScale = 1;
if (indexNestedJoin)
{
for (CollIndex i = outerRefCount; i < columnStats.entries(); i++)
{
ColStatDescSharedPtr columnStatDesc = columnStats[i];
CostScalar oldCount = columnStatDesc->getColStats()->getRowcount();
if (oldCount != newRowCount)
columnStatDesc->synchronizeStats (oldCount, newRowCount);
}
}
else // not an indexNestedJoin
{
if (outerRefCount > 0 )
{
// Either the method for doing the join, or the scale factor of
// the remaining columns in the StatDescList, must be updated.
if ( inputEstLogProp->getInputForSemiTSJ() == EstLogProp::SEMI_TSJ )
mergeMethod = SEMI_JOIN_MERGE;
else if ( inputEstLogProp->getInputForSemiTSJ() == EstLogProp::ANTI_SEMI_TSJ )
mergeMethod = ANTI_SEMI_JOIN_MERGE;
else
outerScale = outerColStatsList[0]->getColStats()->getRowcount();
}
for (CollIndex i = outerRefCount; i < columnStats.entries(); i++)
{
ColStatDescSharedPtr columnStatDesc = columnStats[i];
ColStatsSharedPtr colStats = columnStatDesc->getColStatsToModify();
colStats->copyAndScaleHistogram( outerScale );
}
}
} // If REL_SCAN
const ValueIdSet & inputValues = getGroupAttr()->getCharacteristicInputs();
ValueIdSet outerReferences;
// Get the set of outer references. If input value is a VEGReference,then
// include this VEGReference only if the group consists of no constants.
if (inputValues.entries() > 0)
inputValues.getOuterReferences (outerReferences);
// Apply the effect of my selection predicates on columnStats,
// returning the resultant rowcount.
const SelectivityHint * selHint = NULL;
const CardinalityHint * cardHint = NULL;
NABoolean sampleUsed = FALSE;
if (opType == REL_SCAN)
{
Scan * scanExpr = (Scan *)this;
selHint = scanExpr->getSelectivityHint();
cardHint = scanExpr->getCardinalityHint();
ColStatDescList columnStatsForMaxSel(CmpCommon::statementHeap());
columnStatsForMaxSel.makeDeepCopy (columnStats) ;
// Set MC Skewed values
columnStats.setMCSkewedValueLists(childStats.getMCSkewedValueLists());
// compute maxSelectivity. toss rowcount return value.
(void) columnStatsForMaxSel.estimateCardinality
(newRowCount, getSelectionPred(), outerReferences, NULL, selHint,
cardHint, outerRefCount, myEstProps->unresolvedPreds(),
mergeMethod, opType, &maxSelectivity);
newRowCount =
columnStats.estimateCardinality (newRowCount, /*in*/
getSelectionPred(),/*in*/
outerReferences, /*in*/
NULL,
selHint, /*in*/
cardHint, /*in*/
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(), /*in/out*/
mergeMethod,
opType, /*in*/
NULL);
// if cardinality or selectivity hint is not given, then
// check to see if the computed cardinality is within
// the cardinality range obtained from executing the same
// query on a sample
if (scanExpr && !cardHint && !selHint && !columnStats.selectivityHintApplied())
{
sampleUsed = scanExpr->checkForCardRange(getSelectionPred(), newRowCount);
}
}
else
{
newRowCount =
columnStats.estimateCardinality (newRowCount, /*in*/
getSelectionPred(),/*in*/
outerReferences, /*in*/
NULL,
NULL, /*in*/
NULL,
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(), /*in/out*/
mergeMethod, /*in*/
ITM_FIRST_ITEM_OP); /*no-op*/
}
// Cardinality after applying predicates should not be more than
// the product of cardinality before applying predicate multiplied
// by the input cardinality. Sol:10-070502-4521. We do this, only
// if there are no hints defined for this table. In case of hints
// this sanity check is ignored
if (!sampleUsed && !cardHint && !selHint)
{
CostScalar inputCard = inputEstLogProp->getResultCardinality();
NABoolean rollingColumnPresent = FALSE;
if(newRowCount > (inputCard * initialCardinality))
{
for ( CollIndex i = 0; i < columnStats.entries(); i++ )
{
if((columnStats)[i]->getColStats()->isARollingColumn())
{
rollingColumnPresent = TRUE;
break;
}
}
}
if(!rollingColumnPresent)
newRowCount = MINOF(newRowCount, inputCard * initialCardinality);
}
// ++MV
if (getInliningInfo().isForceCardinality())
{
CostScalar oldCount = newRowCount;
// Override the estimated cardinality using the inlining information.
newRowCount = getInliningInfo().getNewCardinality(initialCardinality);
if (oldCount != newRowCount)
{
columnStats.synchronizeStats(newRowCount, columnStats.entries());
}
}
else
columnStats.synchronizeStats(newRowCount, columnStats.entries());
// --MV
myEstProps->setResultCardinality( newRowCount );
if (cardHint != NULL)
myEstProps->setMaxCardEst( newRowCount );
else if (selHint != NULL && selHint->getScanSelectivityFactor() > 0.0)
myEstProps->setMaxCardEst(maxRows * selHint->getScanSelectivityFactor());
else
myEstProps->setMaxCardEst(MAXOF(maxRows * maxSelectivity, newRowCount));
// -------------------------------------------------------------------
// Now, determine which of these histograms are useful based on the
// characteristic outputs for the group, and finish up.
// We need to handle some special cases:
// 1) if the expression is update then we need to generate colStats for
// the characteristics outputs of the UpdateExpression. Each of these
// should map to the corresponding colStats which are picked up based
// on the characteristic outputs of the child of Update
// 2) If there are some VEG predicates on this operator, which contain a
// reference to a column of another table, and also a constant
// then the histograms for these should be passed too, even though
// the column does not showup as a characteristics output. The reason
// for doing this is because we might need it later while doing a
// nested join between the two tables.
// -------------------------------------------------------------------
OperatorTypeEnum statOper = getOperatorType();
ValueIdSet charOutputs = getGroupAttr()->getCharacteristicOutputs();
if ( (statOper == REL_UNARY_INSERT) || (statOper == REL_LEAF_INSERT) ||
(statOper == REL_UNARY_DELETE ) || (statOper == REL_LEAF_DELETE ) ||
(statOper == REL_UNARY_UPDATE) || (statOper == REL_LEAF_UPDATE))
{
GenericUpdate *updateExpr = (GenericUpdate *) this;
// do it only if the update expression is on a normal table
if (updateExpr->getTableName().getSpecialType() == ExtendedQualName::NORMAL_TABLE)
{
myEstProps->pickOutputsForUpdate(columnStats, inputEstLogProp, (*this), charOutputs, getSelectionPred());
}
else
myEstProps->pickOutputs( columnStats, inputEstLogProp, charOutputs, getSelectionPred());
}
else
myEstProps->pickOutputs( columnStats, inputEstLogProp, charOutputs, getSelectionPred());
return (myEstProps);
} // RelExpr::synthEstLogPropForUnaryLeafOp()
void
RelExpr::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
// create a default OLP with rowcount of 1.
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(1));
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstProps);
}
void
RelExpr::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent logical expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
GroupAttributes * GA = getGroupAttr();
Int32 numChildren = getArity();
Int32 numBaseTables = 0; // add up all base tables from children
Int32 numTMUDFs = 0; // ditto for TMUDFs
// synthesize the log props for all children
for (Lng32 i = 0; i < numChildren; i++)
{
// only if the child is not a Cascadesgroup or NULL
if (child(i).getPtr() != NULL)
{
child(i)->synthLogProp(normWAPtr);
numBaseTables += child(i).getGroupAttr()->getNumBaseTables();
numTMUDFs += child(i).getGroupAttr()->getNumTMUDFs();
// QSTUFF
// synthesize logical properties STREAM and DELETE from
// children. The properties are synthesized by the BINDER for
// semantic checking and by the OPTIMIZER for rule enforcement
if (child(i).getGroupAttr()->isStream())
{
GA->setStream(TRUE);
if (child(i).getGroupAttr()->isSkipInitialScan())
getGroupAttr()->setSkipInitialScan(TRUE);
}
if (child(i).getGroupAttr()->isEmbeddedUpdateOrDelete())
GA->setEmbeddedIUD(child(i).getGroupAttr()->getEmbeddedIUD());
// QSTUFF ^^
if (child(i).getGroupAttr()->getHasNonDeterministicUDRs())
getGroupAttr()->setHasNonDeterministicUDRs(TRUE);
if (child(i).getGroupAttr()->isSeabaseMDTable())
getGroupAttr()->setSeabaseMDTable(TRUE);
}
}
// remember the number of base tables
// remember the number of base tables and TMUDFs
GA->setNumBaseTables(numBaseTables);
GA->setNumTMUDFs(numTMUDFs);
// calculate the row size from the characteristic outputs
GA->setRecordLength(GA->getCharacteristicOutputs().getRowLength());
GA->setInputVarLength(GA->getCharacteristicInputs().getRowLength());
// Associate this GA with this log expr for any further synthesis
GA->setLogExprForSynthesis (this);
//++MV
// Used by inlining mechanism for adding optimizer constraints
if (!getUniqueColumns().isEmpty())
{
GA->addConstraint(new(CmpCommon::statementHeap())
UniqueOptConstraint(getUniqueColumns()));
}
if (getCardConstraint())
{
GA->addConstraint(new(CmpCommon::statementHeap())
CardConstraint(getCardConstraint()->getLowerBound(),
getCardConstraint()->getUpperBound()));
}
//--MV
} // RelExpr::synthLogProp()
void
RelExpr::finishSynthEstLogProp()
{
GroupAttributes * GA = getGroupAttr();
Int32 numChildren = getArity();
CostScalar minRowCount = COSTSCALAR_MAX;
// finish synthesis of log props for all children
for (Lng32 i = 0; i < numChildren; i++)
{
// only if the child is not a CascadesGroup or NULL
if (child(i).getPtr() != NULL)
{
child(i)->finishSynthEstLogProp();
// set the minimum rowCount after applying local predicates for this group
CostScalar thisChildsRowCount = child(i)->getGroupAttr()->getMinChildEstRowCount();
if (thisChildsRowCount < minRowCount)
minRowCount = thisChildsRowCount;
}
}
GA->setMinChildEstRowCount(minRowCount);
} // RelExpr::finishSynthEstLogProp()
// clear logical properties of me and my children
void
RelExpr::clearAllEstLogProp()
{
Int32 numChildren = getArity();
// clear the log props for all children
for (Lng32 i = 0; i < numChildren; i++)
{
// only if the child is not a CascadesGroup or NULL
if (child(i).getPtr() != NULL)
{
child(i)->clearAllEstLogProp();
}
}
// finally clear my logical properties
getGroupAttr()->clearAllEstLogProp();
// for scan nodes clear the colStats_ from tableDesc, in order to use
// compressed histograms in future
if (getOperatorType() == REL_SCAN)
{
// for scan nodes clear the histograms compressed flag to FALSE
// so that the histograms can be compressed later
Scan * scanExpr = (Scan *)this;
scanExpr->getTableDesc()->histsCompressed(FALSE);
}
}
RelExpr *
RelExpr::checkAndReorderTree()
{
// ---------------------------------------------------------------------
// Reorder any join orderings based on estimated rowcount.
// We do not re-order if the control JOIN_ORDER_BY_USER is ON since
// this specify that we want the join order that was output from
// the normalizer, unchanged.
// ---------------------------------------------------------------------
// Here am using direct call to NADefaults instead of the much more
// efficient way of using the CURRSTMT_OPTDEFAULTS->joinOrderByUser() function.
// The reason is that OptDefaults has not been initialized yet.
// It's not a big deal since this is happening only once per statement.
//
// The path of reorderTree is used also to perform the trigger
// is not required.
NABoolean treeModified = FALSE;
NABoolean areTriggersPresent = getOperatorType() == REL_ROOT &&
((RelRoot *)this)->getTriggersList() != NULL;
NABoolean containsOnStatementMV = getOperatorType() == REL_ROOT &&
((RelRoot *)this)->containsOnStatementMV();
NABoolean doReorderJoin =
((ActiveSchemaDB()->getDefaults()).getAsULong(COMP_INT_90)!=0) ||
getGroupAttr()->reorderNeeded() ||
areTriggersPresent || containsOnStatementMV;
if (!(QueryAnalysis::Instance()->joinOrderByUser()))
{
if (doReorderJoin &&
((CURRSTMT_CQSWA == NULL) ||
CURRSTMT_CQSWA && !CURRSTMT_CQSWA->reArrangementSuccessful_)
)
{
this->reorderTree(treeModified, doReorderJoin);
this->clearAllEstLogProp();
if (treeModified)
synthLogProp();
}
}
else{
if (getGroupAttr()->reorderNeeded())
*CmpCommon::diags() << DgSqlCode(-4174);
}
// QSTUFF
/*
NABoolean doReorderJoin = FALSE;
NABoolean areTriggersPresent = getOperatorType() == REL_ROOT &&
((RelRoot *)this)->getTriggersList() != NULL;
NABoolean containsOnStatementMV = getOperatorType() == REL_ROOT &&
((RelRoot *)this)->containsOnStatementMV();
if (!(QueryAnalysis::Instance()->joinOrderByUser()) &&
((ActiveSchemaDB()->getDefaults()).getAsULong(COMP_INT_90)!=0
|| getGroupAttr()->isStream()
|| areTriggersPresent
|| containsOnStatementMV))
doReorderJoin = TRUE;
// QSTUFF
else{
if (getGroupAttr()->reorderNeeded())
*CmpCommon::diags() << DgSqlCode(-4174);
}
// QSTUFF
if ( (doReorderJoin && (CURRSTMT_CQSWA == NULL)) ||
(doReorderJoin &&
CURRSTMT_CQSWA &&
!CURRSTMT_CQSWA->reArrangementSuccessful_)
)
{
this->reorderTree(treeModified, doReorderJoin);
this->clearAllEstLogProp();
if (treeModified)
synthLogProp();
}
*/
return(this);
}
// The reorderTree() is used also for pre-optimizer pass
RelExpr *
RelExpr::reorderTree(NABoolean & treeModified, NABoolean doReorderJoin)
{
Int32 nc = getArity();
NABoolean childModified = FALSE;
treeModified = FALSE;
// reorder any join backbones in the children
for (Lng32 i = 0; i < nc; i++)
{
child(i) = child(i)->reorderTree(childModified, doReorderJoin);
if (childModified)
treeModified = TRUE;
}
// if any of my children were reordered, then we need to
// clear my logical properties for resynthesis.
if (treeModified)
getGroupAttr()->clearLogProperties();
return (this);
} // RelExpr::reorderTree()
RelExpr *
RelExpr::fixupTriggers(NABoolean & treeModified)
{
Int32 nc = getArity();
NABoolean childModified = FALSE;
NABoolean myTreeModified = FALSE;
// reorder any join backbones in the children
for (Lng32 i = 0; i < nc; i++)
{
child(i) = child(i)->fixupTriggers(childModified);
if (childModified)
myTreeModified = TRUE;
}
// if any of my children were reordered, then we need to
// clear my logical properties for resynthesis.
if (myTreeModified)
getGroupAttr()->clearLogProperties();
treeModified = treeModified || myTreeModified;
return (this);
} // RelExpr::fixupTriggers()
// The fixupTriggers() is used for pre-optimizer pass
// for triggers
// After trigger backbone transformation
RelExpr *
Union::fixupTriggers(NABoolean & treeModified)
{
RelExpr *retNode = this;
NABoolean myTreeModified = FALSE;
#ifndef NDEBUG
if (getenv("TRIGGER_TRANSFORMATION_OFF") != NULL)
return (RelExpr::fixupTriggers(treeModified));
#endif
InliningInfo &InlineInfo = getInliningInfo();
if (InlineInfo.isDrivingBeforeTriggers() ||
(InlineInfo.isDrivingTempDelete() && !InlineInfo.isBeforeTriggersExist()))
{
OptTriggersBackbone *backbone = new(CmpCommon::statementHeap())
OptTriggersBackbone(this);
backbone->init();
RelExpr *topNode = backbone->getTransformedTree();
NADELETE(backbone, OptTriggersBackbone, (CmpCommon::statementHeap()));
if (topNode != this) // top node has been removed
{
myTreeModified = TRUE;
retNode = topNode;
}
}
RelExpr * retVal = retNode->RelExpr::fixupTriggers(myTreeModified);
if(myTreeModified)
getGroupAttr()->clearLogProperties();
treeModified = treeModified || myTreeModified;
return retVal;
} // Union::fixupTriggers()
// end - for triggers
// -----------------------------------------------------------------------
// member functions for class CutOp
// -----------------------------------------------------------------------
void
CutOp::synthLogProp(NormWA * normWAPtr)
{
}
// ----------------------------------------------------------------------
// Helper method for left and full outer joins
// The method simulates the behavior of left outer joins
// ----------------------------------------------------------------------
void
Join::instantiateLeftHistsWithNULLs(ValueIdSet EqLocalPreds, /*in*/
CollIndex outerRefCount, /*in*/
ColStatDescList &joinStatDescList, /*in*/
const ColStatDescList &leftColStatsList, /*in*/
CostScalar &oJoinResultRows /*in, out*/,
CostScalar initialRowcount /*in*/,
CollIndex &rootStatIndex /*out*/,
NAList<CollIndex>& statsToMerge /*out*/)
{
// contains the rowcount of the left child before join
CostScalar baseRows;
// contains the innerJoinCardinality
CostScalar innerJoinCard = oJoinResultRows;
// Flag to indicate that the histogram from the left side that needs to
// be NULL Instantiated is found.
NABoolean foundFlag = FALSE;
// locate Histogram that participated in equiJoin to determine the number of rows
// to NULL augment. If no histogram participated in equiJoin, look for a histogram
// whose shape changed during join. This could be like join on expressions
// rootIndex contains the index of the histogram to be NULL augmented first
foundFlag = joinStatDescList.locateHistogramToNULLAugment(EqLocalPreds,
statsToMerge,
rootStatIndex,
outerRefCount);
// Use the above histogram to estimate the outer join result. This is done by
// determining the number of rows from the left / right side that did
// not participate in the inner join
// found flag indicates if an error occured while trying to compute the number
// of rows. It is an input as well as an output parameter
// if there were no histogram with join predicate found to start with, then
// there is nothing to do
if (foundFlag)
{
baseRows = joinStatDescList[rootStatIndex]->getColStats()->getRowcount();
oJoinResultRows = joinStatDescList.computeLeftOuterJoinRC(foundFlag,
leftColStatsList,
rootStatIndex);
}
// if any hist with equality predicate or any shape changing predicate was
// found, set baseRowCount as the rowcount of the histogram. This would be
// the result after an inner join.
// if foundFlag was false, that is we did not find any histogram with
// predicate, then set the baseRows equal to original left rowcount
if (!foundFlag)
{
// Exception condition, set condition so no histograms could be merged
// ------------------------------------------------------------
// If this code is hit, no histogram shape-changing predicates
// (or none that we currently know how to apply) were found.
// In this [hopefully] unusual case, set the baseRowcount as
// the initialRowCount, which is the cross product of the two
// children of Join
// Since it is an exception, we shall set the oJoinResultRows as
// the max of inner join cardinality and original leftRowCount
//
// Set up a situation where the null-augmentation logic does
// the desired thing:
// clear the statsToMerge set, and
// pick a rootStatIndex to indicate that no special root
// histogram was found.
// ------------------------------------------------------------
CCMPASSERT ("Histograms for left table in outer join missing");
baseRows = initialRowcount;
statsToMerge.clear(); // reset.
CostScalar lrc = csOne;
if (leftColStatsList.entries() > 0)
lrc = leftColStatsList[0]->getColStats()->getRowcount();
oJoinResultRows = MAXOF(innerJoinCard, lrc);
rootStatIndex = leftColStatsList.entries() + 1; // out of range; +1 overkill
}
// NULL augment the histograms that did not match the other side
// synchronize histograms from left table to all have the same rowcount
joinStatDescList.synchronizeHistsWithOJRC(statsToMerge,
0,
outerRefCount,
rootStatIndex,
leftColStatsList,
oJoinResultRows,
baseRows);
// ------------------------------------------------------------
// Next, Instantiate Nulls in histograms from the right table.
// Also, alter the discriptions the histograms have of themselves
// such that they indicate that they are null-instantiated.
// ------------------------------------------------------------
ValueIdList nulledVIds = nullInstantiatedOutput();
// At this point all joined histograms have the intervals from the
// left side have merged. Now NULL Instantiate histograms
joinStatDescList.nullInstantiateHists(outerRefCount,
joinStatDescList.entries(),
oJoinResultRows,
nulledVIds);
return;
} // instantiateLeftHistsWithNULLs
// ----------------------------------------------------------------------
// Helper method for left and full outer joins
// The method simulates the behavior of left and full outer joins
// depending on the fullOuterJoinMethod. The flag = TRUE means that
// it is the second part of full outer join, where right histograms
// are being NULL instantiated
// ----------------------------------------------------------------------
void
Join::instantiateRightHistsWithNULLs(CollIndex outerRefCount, /*in*/
ColStatDescList &innerJoinCopy, /*in/out*/
ColStatDescList &joinStatDescList, /*in/out*/
const ColStatDescList &rightColStatsList, /*in*/
CostScalar &oJoinResultRows /*in, out*/,
CostScalar initialRowcount /*in*/,
CollIndex rootStatIndex /*in*/,
NAList<CollIndex> statsToMerge /*out*/)
{
// contains the leftJoin cardinality
CostScalar leftJoinCard = oJoinResultRows;
// contains the number of right rows that did not match the left
CostScalar rightJoinCard = csMinusOne;
// contains inner join cardinality
CostScalar innerJoinCard = csMinusOne;
// Flag to indicate that the histogram from the left side that needs to
// be NULL Instantiated is found.
NABoolean foundFlag = FALSE;
// baseRowCount contains the rowcount with which we started. For full outerjoin
// we have already computed left join cardinality
CostScalar baseRows = leftJoinCard;
// Use the histogram to estimate the outer join result. This is done by
// determining the number of rows from the right side that did
// not participate in the inner join
if (statsToMerge.entries() > 0)
{
innerJoinCard = innerJoinCopy[rootStatIndex]->getColStats()->getRowcount();
rightJoinCard = innerJoinCopy.computeFullOuterJoinRC(foundFlag,
rightColStatsList,
rootStatIndex);
// Final full outer join cardinality
// = inner join card + left rows that did not match inner + right rows that did not match inner
// Since inner got accounted twice, we shall reduce one inner
// Hence fullOuterJoinCard = leftJoinCard + rightJoinCard - InnerJoinCard
oJoinResultRows = leftJoinCard + rightJoinCard - innerJoinCard;
}
// In case of an error set the baseRows equal to original left rowcount
// if there were no histogram with join predicate found to start with, then
// there is nothing to do
if (!foundFlag)
{
// Exception condition, set condition so no histograms could be merged
// ------------------------------------------------------------
// If this code is hit, no histogram shape-changing predicates
// (or none that we currently know how to apply) were found.
// In this [hopefully] unusual case, set the baseRowcount as
// the initialRowCount, which is the cross product of the two
// children of Join
// Since it is an exception, we shall set the oJoinResultRows as
// the max of left join cardinality and original rightRowCount
//
// Set up a situation where the null-augmentation logic does
// the desired thing:
// clear the statsToMerge set, and
// pick a rootStatIndex to indicate that no special root
// histogram was found.
// ------------------------------------------------------------
CCMPASSERT ("Histograms from right table in full outer join missing");
baseRows = initialRowcount;
statsToMerge.clear(); // reset.
rootStatIndex = joinStatDescList.entries() + 1; // out of range; +1 overkill
CostScalar rrc = csOne;
if (rightColStatsList.entries() > 0)
rrc = rightColStatsList[0]->getColStats()->getRowcount();
oJoinResultRows = MAXOF(leftJoinCard, rrc);
}
// NULL augment the histograms that did not match the other side
// synchronize histograms from left / right table to all have the same rowcount
// computed so far for full outer joins
joinStatDescList.synchronizeHistsWithOJRC(statsToMerge,
outerRefCount,
joinStatDescList.entries(),
rootStatIndex,
rightColStatsList,
oJoinResultRows,
baseRows);
// ------------------------------------------------------------
// Next, Instantiate Nulls in histograms from the left table.
// Also, alter the descriptions the histograms have of themselves
// such that they indicate that they are null-instantiated.
// ------------------------------------------------------------
ValueIdList nulledVIds = nullInstantiatedForRightJoinOutput();
joinStatDescList.nullInstantiateHists(0, outerRefCount, oJoinResultRows, nulledVIds);
return;
} // instantiateRightHistsWithNULLs
// -----------------------------------------------------------------------
// Join::commonSynthEst is shared logic for Join::synthEstLogPropForTSJ
// and Join::synthEstLogProp.
// -----------------------------------------------------------------------
CostScalar
Join::commonSynthEst (const EstLogPropSharedPtr& myEstProps,
const EstLogPropSharedPtr& intermedEstProps,
CollIndex & outerRefCount,
const ColStatDescList &leftColStatsList,
const ColStatDescList &rightColStatsList,
ColStatDescList & joinStatDescList,
CostScalar initialRowcount)
{
// CMPASSERT (outerRefCount == leftColStatsList.entries() ) ; // should always be true!
// Get the set of outer references. If input value is a VEGReference,
// include this VEGReference only if the group consists of no constants.
const ValueIdSet & inputValues = getGroupAttr()->getCharacteristicInputs();
ValueIdSet outerReferences;
if (inputValues.entries() > 0)
inputValues.getOuterReferences (outerReferences);
// if the join is an outer join, gather some information (to be used
// later) regarding the original join predicates.
ValueIdSet EqLocalPreds, OtherLocalPreds, EqNonLocalPreds,
OtherNonLocalPreds, BiLogicPreds, DefaultPreds;
if ( isLeftJoin() || isFullOuterJoin() )
getJoinPred().categorizePredicates (outerReferences, EqLocalPreds,
OtherLocalPreds, EqNonLocalPreds,
OtherNonLocalPreds, BiLogicPreds,
DefaultPreds);
MergeType mergeMethod = getMergeTypeToBeUsedForSynthLogProperties();
// approximate the cardinality of the join
CostScalar newRowcount = initialRowcount;
//---------------------------------------------------------------------
// Apply the local join predicate; return the resultant rowcount.
// In the case of Outer joins, this join is done as an Inner join.
//---------------------------------------------------------------------
newRowcount =
joinStatDescList.estimateCardinality (newRowcount, /*in*/
getJoinPred(), /*in*/
outerReferences, /*in*/
this,
NULL, /*for selectivityHint, which can only be given for Scan*/
NULL, /* for CardinalityHint, which is NULL for ! Scan operators */
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(), /*in/out*/
mergeMethod, /*in*/
REL_JOIN);
// make a copy of innerJoinCard result which will be used later to compute
// full outerjoin cardinality;
ColStatDescList innerJoinCopy;
if(isFullOuterJoin())
innerJoinCopy.makeDeepCopy( joinStatDescList );
//---------------------------------------------------------------------
// Save an intermediate EstLogProp for use by Physical Costing.
// As usual, this is a Deep copy to prevent inadvertent modifications
// of the data, by subsequent changes to the Histograms.
//---------------------------------------------------------------------
ColStatDescList & intermedStatsList = intermedEstProps->colStats();
intermedStatsList.makeDeepCopy (joinStatDescList) ;
intermedEstProps->unresolvedPreds() += myEstProps->getUnresolvedPreds();
intermedEstProps->setResultCardinality( newRowcount );
//---------------------------------------------------------------------
// IF this is an outer join, whether left or full, fix-up the result of
// the inner join to reflect its outer join nature:
// - recreate what left rows didn't meet the join predicate.
// - 'OR' those rows, null-augmented on right, into the join results
// newRowcount contains the left join rowcount.
//---------------------------------------------------------------------
// contains the list of indices of histograms that should be
// NULL instantiated
NAList<CollIndex> statsToMerge(CmpCommon::statementHeap());
// contains the index of the left histogram to be merged
CollIndex rootStatIndex = NULL_COLL_INDEX;
// save the inner join cardinality
CostScalar innerJoinCard = newRowcount;
if ( mergeMethod == OUTER_JOIN_MERGE )
{
instantiateLeftHistsWithNULLs(EqLocalPreds, /*in*/
outerRefCount, /*in*/
joinStatDescList, /*in*/
leftColStatsList, /*in*/
newRowcount /*in, out*/,
initialRowcount /*in*/,
rootStatIndex,
statsToMerge);
} // if (left or full outer join)
// ---------------------------------------------------------------------
// if this is a full outer join,
// - recreate what right rows didn't meet the join predicate.
// start with the inner join and find the number of rows from the right
// that did not match the left side (innerJoinCopy)
// - 'OR' those rows, null-augmented on the left, into the join results
// ---------------------------------------------------------------------
if ( isFullOuterJoin() )
{
instantiateRightHistsWithNULLs(outerRefCount, /*in*/
innerJoinCopy, /*in*/
joinStatDescList, /*in */
rightColStatsList, /*in*/
newRowcount /*in, out*/,
initialRowcount /*in*/,
rootStatIndex,
statsToMerge);
}
// We want to recompute the max frequency as the NULL interval added
// may become the most frequent value
if ( mergeMethod == OUTER_JOIN_MERGE &&
CmpCommon::getDefault(HIST_FREQ_VALS_NULL_FIX) == DF_ON )
joinStatDescList.computeMaxFreq(TRUE);
// ---------------------------------------------------------------------
// Lastly, apply the effect of my selection predicates on joinStatDescList,
// returning the resultant rowcount.
//
// If we had been doing an outer join, this where-clause predicate is done
// as an inner join.
// ---------------------------------------------------------------------
if (mergeMethod == OUTER_JOIN_MERGE)
mergeMethod = INNER_JOIN_MERGE;
newRowcount =
joinStatDescList.estimateCardinality (newRowcount, /*in*/
getSelectionPred(),/*in*/
outerReferences, /*in*/
this,
NULL, /*for selectivityHint, which can only be given for Scan*/
NULL, /* for Cardinality hint, which can only be given for Scan */
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(), /*in/out*/
mergeMethod,
REL_JOIN); /*in*/
return (newRowcount);
} // commonSynthEst
// -----------------------------------------------------------------------
// Join::synthEstLogPropForTSJ
// -----------------------------------------------------------------------
void
Join::synthEstLogPropForTSJ(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(1));
EstLogPropSharedPtr intermedEstProps(new (HISTHEAP) EstLogProp(1));
EstLogPropSharedPtr leftEstProps;
EstLogPropSharedPtr rightEstProps;
EstLogPropSharedPtr copyInputEstProp;
// if inputForSemiTSJ is set for inputEstLogProp it cannot be passed to child(0)
// however it must be passed to child(1)
// so create a new EstLogProp with the flag set off to pass to child(0)
if (inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ)
{
copyInputEstProp = EstLogPropSharedPtr(new (HISTHEAP) EstLogProp(*inputEstLogProp));
copyInputEstProp->setInputForSemiTSJ(EstLogProp::NOT_SEMI_TSJ);
}
else
{
copyInputEstProp = inputEstLogProp;
}
leftEstProps = child(0).outputLogProp(copyInputEstProp);
EstLogPropSharedPtr copyLeftEstProps(new (HISTHEAP) EstLogProp (*leftEstProps));
// from this point on, it is important to utilize the left's copy.
if ( isSemiJoin() ||
inputEstLogProp->getInputForSemiTSJ() == EstLogProp::SEMI_TSJ)
copyLeftEstProps->setInputForSemiTSJ( EstLogProp::SEMI_TSJ );
else if ( isAntiSemiJoin() ||
inputEstLogProp->getInputForSemiTSJ() == EstLogProp::ANTI_SEMI_TSJ)
copyLeftEstProps->setInputForSemiTSJ( EstLogProp::ANTI_SEMI_TSJ );
//Compute the right child output estLogProps for the given left child EstLogProp
rightEstProps = child(1).outputLogProp(copyLeftEstProps);
const ColStatDescList &leftColStatsList = copyLeftEstProps->getColStats();
const ColStatDescList &rightColStatsList = rightEstProps->getColStats();
// Initially, the unresolved predicates of a join are those that are
// unresolved by either child.
myEstProps->unresolvedPreds() += copyLeftEstProps->getUnresolvedPreds();
myEstProps->unresolvedPreds() += rightEstProps->getUnresolvedPreds();
//------------------------------------------------------------------
// Copy the Right child's ColStatDescs
//------------------------------------------------------------------
ColStatDescList joinStatDescList;
joinStatDescList.makeDeepCopy (rightColStatsList) ;
CollIndex i;
CollIndex outerRefCount = 0;
for (i = 0; i < rightColStatsList.entries(); i++)
{
ColStatDescSharedPtr joinStatDesc = joinStatDescList[i] ;
ItemExpr * statExpr = joinStatDesc->getVEGColumn().getItemExpr() ;
// -----------------------------------------------------------------
// Determine the number of entries in rightEstProps's column
// stats that match entries in the copyLeftEstProps columns.
//
// The first of these matching stats originated from the given
// inputEstLogProp column stats, and the remaining are colstats from
// the left table that are modified and returned by the right. That
// determined count will be used by the right child as the count of
// Outer reference columns
// -----------------------------------------------------------------
NABoolean goodMatch = FALSE;
for (CollIndex j = 0; j < leftColStatsList.entries() ; j++)
{
ColStatDescSharedPtr origStatDesc = leftColStatsList[j];
ItemExpr * originalExpr = origStatDesc->getVEGColumn().getItemExpr() ;
if ( statExpr == originalExpr )
{
goodMatch = TRUE;
break ; // break out of inner for-loop
}
}
if (goodMatch)
outerRefCount++;
else
break ;
}
CostScalar originalLeftRowcount = copyLeftEstProps->getResultCardinality();
CostScalar newRowcount = rightEstProps->getResultCardinality();
newRowcount = this->commonSynthEst (myEstProps,
intermedEstProps,
outerRefCount,
leftColStatsList,
rightColStatsList,
joinStatDescList,
newRowcount) ;
// synchronizeStat only if needed
NABoolean synchronizeStatNeeded = FALSE;
CostScalar oldCount = newRowcount;
// ++MV
if (getInliningInfo().isForceCardinality())
{
// Override the estimated cardinality using the inlining information.
newRowcount = getInliningInfo().getNewCardinality(newRowcount);
}
// since the histograms have been merged together. Set the scale factor of remaining
// histograms to one.
joinStatDescList.setScaleFactor(csOne);
newRowcount = doCardSanityChecks(copyInputEstProp,
joinStatDescList,
newRowcount);
CostScalar maxCardEst = estimateMaxCardinality
(copyInputEstProp, joinStatDescList);
if (oldCount != newRowcount)
joinStatDescList.synchronizeStats (newRowcount, joinStatDescList.entries()) ;
myEstProps->setResultCardinality(newRowcount);
myEstProps->setMaxCardEst(MAXOF(maxCardEst, newRowcount));
//------------------------------------------------------------------
// Now, determine which of these histograms are useful based on
// the characteristic outputs for the group
// -----------------------------------------------------------------
const ValueIdSet charOutputs = getGroupAttr()->getCharacteristicOutputs();
myEstProps->pickOutputs( joinStatDescList, inputEstLogProp, charOutputs, getSelectionPred()) ;
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstProps,
intermedEstProps);
} // Join::synthEstLogPropForTSJ()
// -----------------------------------------------------------------------
// Join::synthEstLogProp
// -----------------------------------------------------------------------
void
Join::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (isTSJ())
{
synthEstLogPropForTSJ(inputEstLogProp);
return;
}
if (getGroupAttr()->isPropSynthesized(inputEstLogProp))
return;
const NABoolean isASemiJoin = isSemiJoin() || isAntiSemiJoin();
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp());
EstLogPropSharedPtr intermedEstProps(new (HISTHEAP) EstLogProp());
EstLogPropSharedPtr leftEstProps;
EstLogPropSharedPtr rightEstProps;
EstLogPropSharedPtr copyInputEstProp;
// if inputForSemiTSJ is set for inputEstLogProp it cannot be passed below the join
// so create a new EstLogProp with the flag set off to pass to my children
if (inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ)
{
copyInputEstProp = EstLogPropSharedPtr(new (HISTHEAP)
EstLogProp(*inputEstLogProp));
copyInputEstProp->setInputForSemiTSJ(EstLogProp::NOT_SEMI_TSJ);
}
else
{
copyInputEstProp = inputEstLogProp;
}
leftEstProps = child(0).outputLogProp(copyInputEstProp);
rightEstProps = child(1).outputLogProp(copyInputEstProp);
// Initiallly, the unresolved predicates of a join are those that are
// unresolved by either child.
myEstProps->unresolvedPreds() += leftEstProps->getUnresolvedPreds();
myEstProps->unresolvedPreds() += rightEstProps->getUnresolvedPreds();
//------------------------------------------------------------------
// access the column statistics descriptor list from the left and
// right child's estimated logical properties.
//------------------------------------------------------------------
const ColStatDescList &leftColStatsList = leftEstProps->getColStats();
const ColStatDescList &rightColStatsList = rightEstProps->getColStats();
ColStatDescList outerColStatsList = inputEstLogProp->getColStats();
ColStatDescList joinStatDescList;
// set up the ueclist data member!
joinStatDescList.insertIntoUecList (leftColStatsList.getUecList()) ;
joinStatDescList.insertIntoUecList (rightColStatsList.getUecList()) ;
//--------------------------------------------------------------------
// Note, when gathering rowcounts, avoid problems caused by the
// possibility of subsequent division by zero.
//--------------------------------------------------------------------
CostScalar leftRowCount = leftEstProps->getResultCardinality().minCsOne() ;
CostScalar rightRowCount = rightEstProps->getResultCardinality().minCsOne() ;
CostScalar outerRefRowCount = inputEstLogProp->getResultCardinality().minCsOne() ;
CostScalar newRowCount = 1;
Int32 outerMatchCount = 0;
CollIndex i = 0;
CollIndex j = 0;
//--------------------------------------------------------------------
// Stage 1: If there are outer references involved (I.e., this join is
// under the 2nd child of a TSJ), determine the effect of the fact
// that a join only produces rows when, for a given probe, it produces
// rows from both its left and right child.
//
// First cross-product the matching outer references returned by both
// children.
//
// Note that getColStatsToModify's copy of the ColStats causes two
// pointers to point to the same histogram.
// i.e., copyAndScaleHistogram does the real copy.
// ( copyAndScaleHistogram also applies the current reduction factor
// to all histogram intervals )
//--------------------------------------------------------------------
// Note:
// If this join is under the second child of a Left/Anti TSJ
// the outer colRef VEG may be a different VEGRef in that region
// that it is in this region.
// E.g T1 LEFT JOIN (T2 JOIN T3 ON (T1.x = T2.x AND T2.x = T3.x)
//
// There will be a VEG for {VegRef(T1.x), T2.x, T3.x}
// The outerColRef will be VegRef(T1.x)
//--------------------------------------------------------------------
for (i=0; i < outerColStatsList.entries(); i++)
{
NABoolean foundMatch = FALSE;
CollIndex leftCol, rightCol;
for (j=0; j < leftColStatsList.entries() ; j++)
if (leftColStatsList[j]->getVEGColumn().getItemExpr()->
containsTheGivenValue(outerColStatsList[i]->getVEGColumn()) )
{
leftCol = j;
foundMatch = TRUE;
break ;
}
if (foundMatch)
{
foundMatch = FALSE;
for (j=0; j < rightColStatsList.entries() ; j++)
if (rightColStatsList[j]->getVEGColumn().getItemExpr()->
containsTheGivenValue(outerColStatsList[i]->getVEGColumn()) )
{
rightCol = j;
foundMatch = TRUE;
break ;
}
}
if (foundMatch)
{
// Normal Case: Found current def'n of outer ref for both children
// Cross-Product those two def'ns and insert both into the under-
// construction ColStatDescList for this Join.
// *** Future: if we had multi-column outer references it would
// be nice to bring them along......
joinStatDescList.insertDeepCopyAt (outerMatchCount,
leftColStatsList[leftCol],
rightRowCount); // scale
outerMatchCount++;
joinStatDescList.insertDeepCopyAt (outerMatchCount,
rightColStatsList[rightCol],
leftRowCount); // scale
outerMatchCount++;
}
} // for i
//--------------------------------------------------------------------
// Stage 2: If there are outer reference involved, and we were able to
// match the returned outer references from the left and right child,
// Inner-equi-join pairs of entries (i and i+1) in the ColStatDescList
// that represents matching outer references.
//--------------------------------------------------------------------
//++MV
// remember the initial cardinality for later use
CostScalar initialCardinality = newRowCount;
//--MV
if ( outerMatchCount > 0 )
newRowCount = joinStatDescList.mergeListPairwise();
else
{
// before doing the cross product, make sure there is no overflow
if (rightRowCount.getValue() < 1.000001)
rightRowCount = CostScalar(1.0);
if ( DBL_MAX /rightRowCount.getValue() > leftRowCount.getValue())
newRowCount = rightRowCount * leftRowCount;
else
newRowCount = CostScalar(DBL_MAX)/CostScalar(100.0);
}
//--------------------------------------------------------------------
// Stage 3: At this point, there are 3 cardinalities to deal with:
// newRowCount, if applicable, a normalized view of outer references;
// leftRowCount, rows from left child; and
// rightRowCount, rows from right child.
// Now, set up the rest of the ColStatDesc entries for other columns
// returned by children of the join, and make a first approximation
// of the cardinality of the join, prior to applying local predicates.
//
// This is complicated by differences in treatment of non-semi- and
// semi- joins. It is also tricky to estimate the output cardinality
// of the current join when it is itself under the right child of a
// TSJ.
// When under the right child of a TSJ, both this join's children
// describe themselves as producing the result of all probes done.
// To determine the rowcount involved in the crossproduct done prior
// to applying local join predicates, involves application of the
// implicit equi-join between returned outer references (done above),
// and in the case of non-semi-joins also involves down-scaling the
// newRowCount produced above by the outer reference's cardinality.
//
// If we had outer references,
// If this is NOT a semijoin, then
// all histograms are made to have newRowCount/outerRefCard rows.
// Else
// all histograms on outer leg have newRowCount/outerRefCard rows
// all histograms on inner leg keep their current cardinality
// Else
// If this is NOT a semijoin, then
// all histograms need to have leftRowCount*RightRowCount rows
// Else
// leave histograms with their current cardinality.
//
// reminder: Subsequent logic will use the fact that the Left child's
// column statistics appear first. It is significant for
// semi- and outer- joins.
//--------------------------------------------------------------------
CostScalar leftScale = 1;
CostScalar rightScale = 1;
CostScalar outerRefScale = ((CostScalar) 1) / outerRefRowCount;
// Multiply the rowCount after cross-product with the outerRefScale to
// to ensure that the input cardinality is not counted twice, once each from
// the left and the right child.
// This would be true for both cases when outerMatchCount > 0 (predicates like
// T1.a =T2.a = T3.a, where T1.a is passed down to both T2 and T3.
// Hence is counted twice.), and when outerMatchCount = 0 (predicate T1.a = T2.a + T3.a
// where T1.a is not passed down to T2 and T3, but T2 and T3 are evaluated for each probe
// coming down from T1. Once again the input Cardinality is counted twice in this case)
// While reducing the cardinality, make sure it does not go below 1
newRowCount = MIN_ONE_CS(outerRefScale * newRowCount);
// The right scale is equal to the left row count divided by
// the outer row count. This is because the outer row count
// has already been included in both the children.
rightScale = isASemiJoin ? (CostScalar) 1 : leftRowCount * outerRefScale;
leftScale = isASemiJoin ? (CostScalar) 1 : rightRowCount * outerRefScale;
// update newRowCount if it is a semi-join
if (isASemiJoin)
newRowCount = leftRowCount;
CollIndex joinEntries = joinStatDescList.entries();
if ( outerRefScale != 1 )
{
for (i = 0; i < joinEntries; i++ )
{
joinStatDescList[i]->applySel (outerRefScale);
}
}
for (i = 0; i < leftColStatsList.entries(); i++)
{
NABoolean alreadyAdded = FALSE;
for (j=0 ; j < outerColStatsList.entries() ; j++)
if (leftColStatsList[i]->getVEGColumn().getItemExpr()->
containsTheGivenValue(outerColStatsList[j]->getVEGColumn()) )
{
alreadyAdded = TRUE;
break ;
}
if (NOT alreadyAdded)
{
joinStatDescList.insertDeepCopy(leftColStatsList[i],
leftScale, // scale
FALSE); // clear shapeChanged
}
}
// remember the number of columns, and current stats of the 'outer' table
CollIndex outerRefCount = joinStatDescList.entries();
ColStatDescList leftCopy;
leftCopy.makeDeepCopy( leftColStatsList );
for (i = 0; i < rightColStatsList.entries(); i++)
{
NABoolean alreadyAdded = FALSE;
for (j=0 ; j < outerColStatsList.entries() ; j++)
if (rightColStatsList[i]->getVEGColumn().getItemExpr()->
containsTheGivenValue(outerColStatsList[j]->getVEGColumn()) )
{
alreadyAdded = TRUE;
break ;
}
if (NOT alreadyAdded)
joinStatDescList.insertDeepCopy(rightColStatsList[i],
rightScale, // scale
FALSE); // clear shapeChanged
}
// Finally, after all the setup, do the real work in the join.
newRowCount = this->commonSynthEst (myEstProps,
intermedEstProps,
outerRefCount,
leftCopy,
rightColStatsList,
joinStatDescList,
newRowCount) ;
// If the input to this join is for semijoinTSJ then force a semijoin
// merge between the result of this join and the input to this join.
if (inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ)
{
ColStatDescList newStatsList;
EstLogPropSharedPtr newIntermedEstProps(new (HISTHEAP) EstLogProp());
// To maintain uniformity, use GLOBAL_EMPTY_INPUT_LOGPROP
EstLogPropSharedPtr newLeftEstProp = child(0).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
EstLogPropSharedPtr newRightEstProp = child(1).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
leftRowCount = MIN_ONE_CS(newLeftEstProp->getResultCardinality());
rightRowCount = MIN_ONE_CS(newRightEstProp->getResultCardinality());
rightScale = isASemiJoin ? 1 : leftRowCount;
leftScale = isASemiJoin ? 1 : rightRowCount;
const ColStatDescList &leftColStatsList = newLeftEstProp->getColStats();
const ColStatDescList &rightColStatsList = newRightEstProp->getColStats();
j = 0;
newStatsList.makeDeepCopy( leftColStatsList,
leftScale, // scale RowCount,
FALSE ); // clear shapeChanged
newStatsList.appendDeepCopy( rightColStatsList,
rightColStatsList.entries(), // all
rightScale, // scale Rowcount
FALSE ); // clear shapeChanged
outerRefCount = 0 ; // at this point, there *are* no outer refs; we add them below
ColStatDescList leftCopy;
leftCopy.makeDeepCopy(leftColStatsList);
// Finally, after all the settup, do the real work in the join.
newRowCount = this->commonSynthEst (myEstProps,
newIntermedEstProps,
outerRefCount,
leftCopy,
rightColStatsList,
newStatsList,
newRowCount) ;
// now do a semi join with the input
newStatsList.prependDeepCopy( outerColStatsList, // source
outerColStatsList.entries(), // all
1, // no scale
FALSE ); // clear shapeChanged
ValueIdSet outerReferences;
outerRefCount = outerColStatsList.entries();
MergeType mergeType ;
if ( inputEstLogProp->getInputForSemiTSJ() == EstLogProp::SEMI_TSJ )
{
mergeType = SEMI_JOIN_MERGE ;
}
else
{
// We got here after checking that the merge type is not NOT_SEMI_TSJ, implying that it is
// one of the TSJs. If it is not a SEMI_TSJ, then we assume it to be ANTI_SEMI_TSJ
CCMPASSERT (inputEstLogProp->getInputForSemiTSJ() == EstLogProp::ANTI_SEMI_TSJ) ;
mergeType = ANTI_SEMI_JOIN_MERGE ;
}
newRowCount =
newStatsList.estimateCardinality (newRowCount, /*in*/
getSelectionPred(), /*in*/
outerReferences, /*in*/
this,
NULL, /*for selectivityHint, which can only be given for Scan*/
NULL, /* for Cardinality hint, which can only be given for Scan */
outerRefCount, /*in*/
myEstProps->unresolvedPreds(), /*in/out*/
mergeType, /*in*/
REL_JOIN);
joinStatDescList.synchronizeStats (newRowCount, joinStatDescList.entries()) ;
// deallocate the entries used by the 'if-local' leftCopy.
for ( i = 0; i < leftCopy.entries(); i++ )
leftCopy.removeDeepCopyAt(0);
}
// deallocate the entries used by the leftCopy
for ( i = 0 ; i < leftCopy.entries(); i++ )
leftCopy.removeDeepCopyAt(0) ;
// synchronizeStat only if needed
NABoolean synchronizeStatNeeded = FALSE;
CostScalar oldCount = newRowCount;
// ++MV
if (getInliningInfo().isForceCardinality())
{
// Override the estimated cardinality using the inlining information.
newRowCount = getInliningInfo().getNewCardinality(initialCardinality);
}
// --MV
// since the histograms have been merged together. Set the scale factor of remaining
// histograms to one.
joinStatDescList.setScaleFactor(csOne);
newRowCount = doCardSanityChecks(copyInputEstProp,
joinStatDescList,
newRowCount);
CostScalar maxCardEst = estimateMaxCardinality
(copyInputEstProp, joinStatDescList);
if (oldCount != newRowCount)
joinStatDescList.synchronizeStats (newRowCount, joinStatDescList.entries()) ;
myEstProps->setResultCardinality(newRowCount);
myEstProps->setMaxCardEst(MAXOF(maxCardEst, newRowCount));
//---------------------------------------------------------------------------
// Now, determine which of these histograms are useful based on
// the characteristic outputs and the selection predicates for the group.
// --------------------------------------------------------------------------
myEstProps->pickOutputs( joinStatDescList, inputEstLogProp, getGroupAttr()->getCharacteristicOutputs(), getSelectionPred());
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstProps,
intermedEstProps);
} // Join::synthEstLogProp()
// ------------------------------------------------------------------------
// Join::doCardSanityChecks
// Is a helper method, which does sanity checks for Join cardinality
// at the end. The following checks are used to slightly
// improve the results. These are in no way perfect, and we intend
// to come back and have a look at the whole estimateCardinality
// some day.
// (1) For joins with fake histograms, rowcount should be atleast equal
// the max of left and the right child
// (2) For join on single column, the join cardinality should be
// a minimum of maximum frequency of the two children. Example
// T1 has 10,000 rows and 1 UEC and T2 has 30,000 rows equally
// distributed amongst 2 values (UECs). Max frequency of T1 for a
// value would be 10,000 / 1 and for T2 it would be 30,000/2 = 15,000
// Join cardinality should be a minimum of 15,000
// Ofcourse since we do not assume the rows to be equally distributed
// hence frequency is calculated for each interval of the histogram
// (3) Fourth check is an optimistic sanity check also used to
// uplift the join cardinality for highly skewed data, such that values
// in one column span over say 200,000 days, while the values on other
// column span, say over 100 days. The problem happens when the joining
// column is indirectly reduced because of a local predicate on some other
// column. The fix is controlled by CQD
// HIST_OPTIMISTIC_CARD_OPTIMIZATION, and is OFF by default
// (4) For join on more than one columns, and in the absence of
// multi-column statistics, newRowcount should not be less than some
// percentage of smaller table
// (5) New row count should never be greater than the inputCardinality
// multiplied by the cardinalitiy fo the leftchild for empty input
// log prop multipled by the cardinality of the right child for empty
// inputlogprop. This will ensure that the input cardinality is not
// considered twice, thereby increasing the row counts.
//------------------------------------------------------------------
CostScalar
Join::doCardSanityChecks(const EstLogPropSharedPtr& inLP,
ColStatDescList & joinStatDescList,
CostScalar newRowCount)
{
if(joinStatDescList.selectivityHintApplied())
return newRowCount;
CostScalar adjustedRowcount = newRowCount;
CostScalar inCard = inLP->getResultCardinality();
EstLogPropSharedPtr leftEstLogProp = child(0).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
EstLogPropSharedPtr rightEstLogProp = child(1).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
CostScalar leftCard = leftEstLogProp->getResultCardinality();
CostScalar rightCard = rightEstLogProp->getResultCardinality();
if (isLeftJoin())
{
ColStatDescList &rightColStatsList = rightEstLogProp->colStats();
if(!rightColStatsList.isEmpty())
{
CostScalar nullRows = rightColStatsList[0]->getColStats()->getNullCount();
rightCard -= nullRows;
rightCard.minCsOne();
}
}
CostScalar maxCard = leftCard * rightCard * inCard;
CostScalar minCard = csOne;
ValueIdSet joinPredicates = getSelectionPred();
joinPredicates.addSet(getJoinPred());
ValueIdSet joinedCols = joinStatDescList.getJoinedCols();
NABoolean joinOnSingleCol = FALSE;
if ( (joinPredicates.entries() == 1) &&
(joinedCols.entries() > 0 ) )
joinOnSingleCol = TRUE;
NABoolean skipMinCardTest = FALSE;
NABoolean SemiOrAntiSemiJoin = ((isSemiJoin() || isAntiSemiJoin()) ? TRUE : FALSE);
OperatorTypeEnum oper = getOperatorType();
if (CmpCommon::getDefault(COMP_BOOL_44) == DF_OFF)
{
for ( CollIndex i = 0; i < joinStatDescList.entries(); i++ )
{
ColStatDescSharedPtr thisColStatDesc = joinStatDescList[i];
ValueIdSet mergeCols;
thisColStatDesc->getMergeState().findAllReferencedBaseCols(mergeCols);
// The second argument of FALSE for the method membersCoveredInSet
// indicates that we do NOT need to look below InstantiateNulls
// to check for coverage.
if (( mergeCols.membersCoveredInSet(joinedCols,FALSE) > 0) &&
(thisColStatDesc->getColStats()->isOrigFakeHist() ) )
{
if (SemiOrAntiSemiJoin)
minCard = leftCard;
else
minCard = MAXOF(leftCard, rightCard);
skipMinCardTest = TRUE;
break;
}
}
}
if ( joinOnSingleCol &&
(skipMinCardTest == FALSE) )
{
// single column join, set the min cardinality
// 1. Based on the maximum frequency
if (CmpCommon::getDefault(COMP_BOOL_46) == DF_OFF)
{
for (ValueId col = joinedCols.init();
joinedCols.next(col);
joinedCols.advance(col) )
{
CostScalar currMax = csOne;
CostScalar leftFreq = leftEstLogProp->colStats().getMaxFreq(col);
CostScalar rightFreq = csOne;
if (!SemiOrAntiSemiJoin)
// for semi_join, the cardinality is only dependent on the outer
// frequency, hence inner frequency should not be applied to
// semi-joins
rightFreq = rightEstLogProp->colStats().getMaxFreq(col);
if (leftFreq > rightFreq)
currMax = leftFreq;
else
currMax = rightFreq;
if (minCard < currMax)
{
minCard = currMax;
skipMinCardTest = TRUE;
}
}
} // comp_bool_46 == off
// The code is controlled by CQD HIST_OPTIMISTIC_CARD_OPTIMIZATION
// with following values
// 0 - disable code. .
// 1 - uplift the join cardinality only if the join cardinality is
// lower than the minimum cardinality
// 2 - overwrite the join cardinality irrespective of its original
// value. This
// could even lower the join cardinality
UInt32 upliftCardCond = CURRSTMT_OPTDEFAULTS->histOptimisticCardOpt();
if (( (upliftCardCond == 1) ||
(upliftCardCond == 2) ) &&
joinOnSingleCol &&
!SemiOrAntiSemiJoin)
{
CostScalar currMin = csOne;
// first get the joining column with minimum UEC
CostScalar minOriginalUec = joinedCols.getMinOrigUecOfJoiningCols();
// next get the UEC of the left and the right joining columns
CostScalar leftUec = leftEstLogProp->getColStats().
getUecOfJoiningCols(joinedCols);
CostScalar rightUec = rightEstLogProp->getColStats().
getUecOfJoiningCols(joinedCols);
// max of left and right UEC to compute join cardinality
// uses single interval concept and the containment assumption
CostScalar baseUec = MAXOF(rightUec, leftUec);
// Using maximum of the left and right UEC and minimum original
// UEC makes the estimate more conservative
baseUec = MAXOF(baseUec, minOriginalUec);
currMin = (rightCard * leftCard)/baseUec;
MIN_ONE_CS(currMin.round());
if ( ( (upliftCardCond == 1) && (minCard < currMin))
||
(upliftCardCond > 1) )
{
minCard = currMin;
skipMinCardTest = TRUE;
}
} // histOptimisticCardOpt > 0
} // single column join
if ( (skipMinCardTest == FALSE) &&
(joinStatDescList.isCapForLowBound()) )
{
// multi-column join
const JBBSubset * localJBBView = NULL;
if ((getGroupAnalysis()) &&
(localJBBView = getGroupAnalysis()->getLocalJBBView()))
minCard = localJBBView->getMinimumJBBCRowCount();
else
{
// we estimate cardinality for GroupAttr using empty_input_logprop
// but at this point we might have some input est logprop. So take
// inputEstLogProp into account while determining low bound
CostScalar minEstRowCount = getGroupAttr()->getMinChildEstRowCount();
if (minEstRowCount <= csZero)
minEstRowCount = computeMinEstRCForGroup();
// we estimate cardinality for GroupAttr using empty_input_logprop
// but at this point we might have some input est logprop. So take
// inputEstLogProp into account while determining low bound
minCard = inCard * minEstRowCount;
}
minCard *= (CURRSTMT_OPTDEFAULTS->joinCardLowBound());
skipMinCardTest = TRUE;
}
if (adjustedRowcount < minCard)
{
adjustedRowcount = minCard.round();
}
if (adjustedRowcount > maxCard)
{
// Synchronize all histograms of the joinStatDescList based on this
// new row count
adjustedRowcount = maxCard.round();
}
adjustedRowcount.minCsOne();
return adjustedRowcount;
}
NABoolean Join::matchRIConstraint(GroupAttributes& leftGA,
GroupAttributes& rightGA,
NormWA * normWAPtr)
{
// there can only be at most one RefOpt CompRefOpt match at a given join
// once we find that match we terminate all loops.
NABoolean riMatched = FALSE;
ValueIdSet matchingPreds;
if ((leftGA.hasRefOptConstraint() || leftGA.hasCompRefOptConstraint() ) &&
(rightGA.hasRefOptConstraint() || rightGA.hasCompRefOptConstraint()))
{
const ValueIdSet& leftConstraints = leftGA.getConstraints();
const ValueIdSet& rightConstraints = rightGA.getConstraints();
for (ValueId leftId = leftConstraints.init();
leftConstraints.next(leftId) && NOT riMatched;
leftConstraints.advance(leftId) )
{
if (leftId.getItemExpr()->getOperatorType() == ITM_REF_OPT_CONSTRAINT)
{
if (rightGA.hasCompRefOptConstraint())
{
for (ValueId ukId = rightConstraints.init();
rightConstraints.next(ukId) && NOT riMatched;
rightConstraints.advance(ukId) )
{
if (ukId.getItemExpr()->getOperatorType() == ITM_COMP_REF_OPT_CONSTRAINT)
{
riMatched = hasMatchingRIConstraint(leftId, ukId, normWAPtr);
}
} // end of loop over compRefOptConstraints
} // if rightGA has CompRefOptConstraints
} // if leftId is a RefOptConstraint
else if (leftId.getItemExpr()->getOperatorType() == ITM_COMP_REF_OPT_CONSTRAINT)
{
if (rightGA.hasRefOptConstraint())
{
for (ValueId fkId = rightConstraints.init();
rightConstraints.next(fkId) && NOT riMatched;
rightConstraints.advance(fkId) )
{
if (fkId.getItemExpr()->getOperatorType() == ITM_REF_OPT_CONSTRAINT)
{
riMatched = hasMatchingRIConstraint(fkId, leftId, normWAPtr);
}
} // end of loop over RefOptConstraints
} // if rightGA has RefOptConstraints
} // if leftId is a CompRefOptConstraint
} // end of loop over leftGAConstraints
} // leftGA has RefOpt or CompRefOptConstraints and rightGA has RefOpt or CompRefOpt
return riMatched;
}
NABoolean Join::hasMatchingRIConstraint(ValueId fkConstraintId,
ValueId ukConstraintId,
NormWA * normWAPtr)
{
RefOptConstraint * riConstraint =
(RefOptConstraint *) fkConstraintId.getItemExpr();
ComplementaryRefOptConstraint * compRIConstraint =
(ComplementaryRefOptConstraint *) ukConstraintId.getItemExpr();
ValueIdSet matchingPreds;
NABoolean riMatched = FALSE;
if (riConstraint->uniqueConstraintName() ==
compRIConstraint->constraintName())
{
if (hasRIMatchingPredicates(riConstraint->foreignKeyCols(),
compRIConstraint->uniqueKeyCols(),
compRIConstraint->getTableDesc(),
matchingPreds))
{
ValueIdSet uniqKeyNonPredOutputs, globalNonEssOutputs;
ValueIdSet myNonPredOutputs ;
riMatched = TRUE;
riConstraint->setIsMatched(TRUE);
RelExpr * referencedTable =
(RelExpr *) compRIConstraint->getReferencedTable();
uniqKeyNonPredOutputs = referencedTable->getGroupAttr()->
getCharacteristicOutputs();
ValueIdSet ucCols = compRIConstraint->uniqueKeyCols();
uniqKeyNonPredOutputs -= ucCols;
myNonPredOutputs = getGroupAttr()->getCharacteristicOutputs();
myNonPredOutputs -= ucCols;
if (NOT (myNonPredOutputs.referencesOneValueFromTheSet(uniqKeyNonPredOutputs)))
{
referencedTable->setMarkedForElimination(TRUE);
compRIConstraint->setIsMatchedForElimination(TRUE);
normWAPtr->setContainsJoinsToBeEliminated(TRUE);
setPredicatesToBeRemoved(matchingPreds);
}
else
{
if (normWAPtr->getExtraHubVertex() &&
CmpCommon::getDefault(MVQR_USE_EXTRA_HUB_TABLES) == DF_ON &&
CmpCommon::getDefault(MVQR_USE_RI_FOR_EXTRA_HUB_TABLES) == DF_ON )
{
normWAPtr->getExtraHubVertex()->getGroupAttr()->
getNonEssentialCharacteristicOutputs(globalNonEssOutputs);
if (uniqKeyNonPredOutputs.referencesAllValuesFromMe
(globalNonEssOutputs))
{
referencedTable->setIsExtraHub(TRUE);
setIsExtraHub(TRUE);
normWAPtr->setCheckForExtraHubTables(TRUE);
addExtraHubNonEssentialOutputs(
referencedTable->getGroupAttr()->
getEssentialCharacteristicOutputs());
}
}
} // eliminate or extra-hub or do nothing
} // hasMatchingRIPredicates
} // refOpt.name == compRefOpt.name
return riMatched;
}
CostScalar
Join::estimateMaxCardinality(const EstLogPropSharedPtr& inLP,
ColStatDescList & joinStatDescList)
{
EstLogPropSharedPtr leftEstLogProp = child(0).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
EstLogPropSharedPtr rightEstLogProp = child(1).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP));
CostScalar leftMax = leftEstLogProp->getMaxCardEst();
CostScalar rightMax = rightEstLogProp->getMaxCardEst();
CostScalar inCard = inLP->getResultCardinality();
// maxCardEst0 is max card estimate based on semantics only
CostScalar maxCardEst0 = leftMax * rightMax * inCard;
ValueIdSet joinedCols, joinPredicates = getSelectionPred();
joinPredicates.addSet(getJoinPred());
// max cardinality estimate for non-equi join is
// maxCardEst = maxCardEst(left) * maxCardEst(right)
if (joinPredicates.hasNoEquiPredicates(joinedCols))
{
return maxCardEst0;
}
// this is an equi-join (it has at least one equality join predicate)
// At this point the logical properties of a join have been synthesized,
// so we can check if each row from the first or second child is
// guaranteed to have a single match from the other child.
if (rowsFromLeftHaveUniqueMatch())
{
maxCardEst0 = MINOF(leftMax * inCard, maxCardEst0);
}
if (rowsFromRightHaveUniqueMatch())
{
maxCardEst0 = MINOF(rightMax * inCard, maxCardEst0);
}
// max cardinality for an equi-join
// T1 |X| T2 where T1.x1 = T2.y1 and T1.x2 = T2.y2 and ... T1.xn = T2.yn
// is MIN(maxCard(T1) * maxFreq(T2.ymin), maxCard(T2) * maxFreq(T1.xmin))
// T1 |X| T2 where T1.x1+1 = T2.y1 and ... T1.xn-9 = T2.yn
// is maxCard(T1) * maxFreq(T2.ymin)
// T1 |X| T2 where T1.x1 = T2.y1+6 and ... T1.xn = T2.yn*3
// is maxCard(T2) * maxFreq(T1.xmin)
// we do this by the way joinedCols is computed by hasNoEquiPredicates above
// and by the code below
CostScalar leftFreqMin = leftMax;
CostScalar rightFreqMin = rightMax;
CostScalar leftCard = leftEstLogProp->getResultCardinality();
CostScalar rightCard = rightEstLogProp->getResultCardinality();
CostScalar leftAvgFreq = leftMax;
CostScalar rightAvgFreq = rightMax;
for (ValueId col = joinedCols.init();
joinedCols.next(col);
joinedCols.advance(col) )
{
CostScalar leftFreq = leftEstLogProp->colStats().getMaxFreq(col);
if (leftFreq >= 0)
{
// leftFreq is based on expected cardinality histograms.
// to approximate maxFreq based on max cardinality histograms,
// scale up leftFreq by maxCard/estCard and make sure it's >= 1
leftFreq *= (leftMax / leftCard);
leftFreqMin = MINOF(leftFreqMin, leftFreq.minCsOne());
}
CostScalar rightFreq = rightEstLogProp->colStats().getMaxFreq(col);
if (rightFreq >= 0)
{
// rightFreq is based on expected cardinality histograms.
// to approximate maxFreq based on max cardinality histograms,
// scale up rightFreq by maxCard/estCard and make sure it's >= 1
rightFreq *= (rightMax / rightCard);
rightFreqMin = MINOF(rightFreqMin, rightFreq.minCsOne());
}
// avgFreq(T.z) = maxCard(T) / totalUec(T.z)
leftAvgFreq =
MINOF(leftAvgFreq,
CostScalar(leftMax/leftEstLogProp->colStats().getUEC(col))
.minCsOne());
rightAvgFreq =
MINOF(rightAvgFreq,
CostScalar(rightMax/rightEstLogProp->colStats().getUEC(col))
.minCsOne());
// avgFreq(T.z) may sometime exceed maxFreq(T.z)
}
// maxCardEst1 is maxCard based on maxFreq
CostScalar maxCardEst1 =
inCard * MINOF(leftMax * rightFreqMin, rightMax * leftFreqMin);
// combining the 2 estimates
maxCardEst1 = MINOF(maxCardEst1, maxCardEst0);
// maxCard based on maxFreq is often too high.
// Sometimes we can do better using Zipf's law.
// Many types of data (including join results) can be approximated with a
// Zipfian distribution. The most popular join value occurs about twice as
// often as the 2nd most popular one, which occurs twice as often as the
// 4th most popular one, etc.
// Formula for computing an equi-join's maxCard based on Zipf's law
// is:
// 1.6 * maxFreq(T2.ymin) * maxFreq(T1.xmin)
// + MIN( maxCard(T1)*avgFreq(T2.ymin), maxCard(T2)*avgFreq(T1.xmin)
// where
// avgFreq(T.z) = maxCard(T) / totalUec(T.z)
CostScalar maxCardEstz = CostScalar(1.6) * rightFreqMin * leftFreqMin +
MINOF(leftMax * rightAvgFreq, rightMax * leftAvgFreq);
Int32 cqd = (ActiveSchemaDB()->getDefaults()).getAsLong(COMP_INT_47);
CostScalar maxCardEst;
switch (cqd) {
case 1:
maxCardEst = maxCardEst1;
break;
case 2:
maxCardEst = MINOF(maxCardEstz, maxCardEst0);
break;
default:
maxCardEst = MINOF(maxCardEst1, maxCardEstz);
break;
}
return maxCardEst;
}
//---------------------------------------------------------------------------
// Join::synthConstraints
//---------------------------------------------------------------------------
void
Join::synthConstraints(NormWA * normWAPtr)
{
// QSTUFF
NAString fmtdList1(CmpCommon::statementHeap());
LIST(TableNameMap*) xtnmList1(CmpCommon::statementHeap());
NAString fmtdList2(CmpCommon::statementHeap());
LIST(TableNameMap*) xtnmList2(CmpCommon::statementHeap());
// QSTUFF
GroupAttributes &myGA = *getGroupAttr();
GroupAttributes &leftGA = *child(0).getGroupAttr();
GroupAttributes &rightGA = *child(1).getGroupAttr();
const ValueIdSet &leftConstraints = leftGA.getConstraints();
const ValueIdSet &rightConstraints = rightGA.getConstraints();
// ---------------------------------------------------------------------
// Find out if each row from the left/right can have a
// single matching row from the right/left
// ---------------------------------------------------------------------
if (NOT getEquiJoinExprFromChild1().isEmpty())
leftHasUniqueMatches_ = rightGA.isUnique(getEquiJoinExprFromChild1());
else
leftHasUniqueMatches_ = FALSE;
if (NOT getEquiJoinExprFromChild0().isEmpty())
rightHasUniqueMatches_ = leftGA.isUnique(getEquiJoinExprFromChild0());
else
rightHasUniqueMatches_ = FALSE;
if (isSemiJoin() OR isAntiSemiJoin() OR isIndexJoin_)
leftHasUniqueMatches_ = TRUE;
// QSTUFF
// this is a cursor update, delete or insert and therefore
// the constraint is guaranteed to be true
if (isCursorUpdate())
leftHasUniqueMatches_ = TRUE;
// QSTUFF
Cardinality minLeft, minRight, maxLeft, maxRight;
Cardinality minRows, maxRows;
// ---------------------------------------------------------------------
// synthesize cardinality constraints
// ---------------------------------------------------------------------
(void) leftGA.hasCardConstraint(minLeft,maxLeft);
(void) rightGA.hasCardConstraint(minRight,maxRight);
if (maxLeft == 1 || isIndexJoin_)
rightHasUniqueMatches_ = TRUE;
if (maxRight == 1)
leftHasUniqueMatches_ = TRUE;
// If this GroupAttr() has already been synthesised return now
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// if both the tables produced by the children have a maximum cardinality,
// the result of the join can't be larger than the product of their
// cardinalities
minRows = (Cardinality)0;
if (leftHasUniqueMatches_)
if (rightHasUniqueMatches_)
maxRows = maxLeft < maxRight ? maxLeft : maxRight;
else
maxRows = maxLeft;
else if (rightHasUniqueMatches_)
maxRows = maxRight;
else if (maxLeft < INFINITE_CARDINALITY AND
maxRight < INFINITE_CARDINALITY)
maxRows = maxLeft * maxRight;
else
maxRows = (Cardinality)INFINITE_CARDINALITY;
// synthesize minimum cardinality constraints, if the join has no
// selection predicate and (it has no join predicate or it is an
// outer join) and the left child table has a minimum cardinality
// constraint and (the right child table has a minimum cardinality
// constraint or this is an outer join)
// $$$$ TBC
// add the cardinality constraint, if there is any
if (minRows > 0 OR maxRows < INFINITE_CARDINALITY)
{
myGA.addConstraint(new(CmpCommon::statementHeap())
CardConstraint(minRows,maxRows));
}
// ---------------------------------------------------------------------
// synthesize functional dependencies
// ---------------------------------------------------------------------
if (CmpCommon::getDefault(OPTIMIZER_SYNTH_FUNC_DEPENDENCIES) != DF_OFF)
{
// Generate functional dependencies. Functional dependencies say
// that some columns depend on a set of other columns, where
// that set may be different or smaller than the set of unique
// columns. With "depend on" we mean that if column c depends
// on columns (a,b) then we will never have two result rows of
// our subtree with the same (a,b) values but different c
// values.
// Example: Take a join of NATION and SUPPLIER in TPC-D on the
// NATIONKEY column. The result has a uniqueness constraint on
// S_SUPPKEY. In addition we can record that N_REGIONKEY is
// functionally dependent on N_NATIONKEY. The join result will
// never have two rows with the same N_NATIONKEY (e.g. for
// China) and two different N_REGIONKEY values (e.g. for Asia
// and America), it will always be the same N_REGIONKEY (the one
// for Asia in this case).
// Generate functional dependencies only if the original
// uniqueness constraints of a child are lost. This is because
// all columns of a result are always functionally dependent on
// any set of unique columns.
FuncDependencyConstraint::synthFunctionalDependenciesFromChild(
myGA,
child(0),
NOT leftHasUniqueMatches_);
FuncDependencyConstraint::synthFunctionalDependenciesFromChild(
myGA,
child(1),
NOT rightHasUniqueMatches_);
}
// ---------------------------------------------------------------------
// synthesize uniqueness constraints
// ---------------------------------------------------------------------
if (rightHasUniqueMatches_)
{
// if the left table has just one row, propagate all "interesting"
// uniqueness constraints from the right table to the top, since
// the join will never duplicate a row from the right table
for (ValueId rx = rightConstraints.init();
rightConstraints.next(rx);
rightConstraints.advance(rx))
{
ItemExpr *rc = rx.getItemExpr();
OperatorTypeEnum roptype = rc->getOperatorType();
// is this an "interesting" uniqueness constraint
if (roptype == ITM_UNIQUE_OPT_CONSTRAINT)
// would have to use a coverage method because of VEGies
// AND
// myGA.getCharacteristicOutputs().contains(
// ((UniqueOptConstraint *)rc)->uniqueCols()))
{
myGA.addConstraint(rx);
}
}
}
if (leftHasUniqueMatches_)
{
// walk the constraints of the left child table
for (ValueId lx = leftConstraints.init();
leftConstraints.next(lx);
leftConstraints.advance(lx))
{
ItemExpr *lc = lx.getItemExpr();
OperatorTypeEnum loptype = lc->getOperatorType();
// is this a uniqueness constraint and are the unique columns in
// it "interesting" (are they part of the characteristic outputs)
if (loptype == ITM_UNIQUE_OPT_CONSTRAINT)
// would have to use a coverage method because of VEGies
// AND
// myGA.getCharacteristicOutputs().contains(
// ((UniqueOptConstraint *)lc)->uniqueCols()))
{
// found an "interesting" unique constraint
// on the left child table
myGA.addConstraint(lx);
} // interesting uniqueness constraint on left table
} // for loop over
}
else if(rightHasUniqueMatches_ == FALSE)
{
// Neither side has a unique match from the other.
// walk the constraints of the left child table
for (ValueId lx = leftConstraints.init();
leftConstraints.next(lx);
leftConstraints.advance(lx))
{
ItemExpr *lc = lx.getItemExpr();
OperatorTypeEnum loptype = lc->getOperatorType();
// is this a uniqueness constraint and are the unique columns in
// it "interesting" (are they part of the characteristic outputs)
if (loptype == ITM_UNIQUE_OPT_CONSTRAINT)
// would have to use a coverage method because of VEGies
// AND
// myGA.getCharacteristicOutputs().contains(
// ((UniqueOptConstraint *)lc)->uniqueCols()))
{
// found an "interesting" unique constraint
// on the left child table
// for each unique (candidate) key of the right table, form
// a new uniqueness constraint for the join with the
// combination of the unique columns
for (ValueId rx = rightConstraints.init();
rightConstraints.next(rx);
rightConstraints.advance(rx))
{
ItemExpr *rc = rx.getItemExpr();
OperatorTypeEnum roptype = rc->getOperatorType();
// is this an "interesting" uniqueness constraint
if (roptype == ITM_UNIQUE_OPT_CONSTRAINT)
// would have to use coverage because of VEGies
// AND
// myGA.getCharacteristicOutputs().contains(
// ((UniqueOptConstraint *)rc)->uniqueCols()))
{
// make a new uniqueness constraint with
// the unique columns from the left child table...
UniqueOptConstraint *newConstr =
new(CmpCommon::statementHeap())
UniqueOptConstraint(((UniqueOptConstraint *)lc)->
uniqueCols());
// ...and add the unique columns from the right table
newConstr->uniqueCols() +=
((UniqueOptConstraint *)rc)->uniqueCols();
// now add the new uniqueness constraint
myGA.addConstraint(newConstr);
} // interesting right uniqueness constraint
} // for loop over right child constraints
} // interesting uniqueness constraint on left table
} // for loop over
} // neither table had a unique match
// QSTUFF VV
// make sure we never multiply left tuples when doing joins on nested
// update/delete result sets
if (leftGA.isEmbeddedUpdateOrDelete() && !leftHasUniqueMatches_)
{
if( child(1)->getRETDesc() && getRETDesc() ) // soln:10_040806_8608, in the case of JoinLeftShiftRule the RETDesc_ may be NULL
{
child(1)->getRETDesc()->getTableList(xtnmList1, &fmtdList1);
getRETDesc()->getTableList(xtnmList2, &fmtdList2);
*CmpCommon::diags() << DgSqlCode(-4150)
<< DgString0(fmtdList1)
<< (leftGA.isEmbeddedUpdate() ?
DgString1("update"):DgString1("delete"))
<< DgString2(fmtdList2);
}
}
if (rightGA.isEmbeddedUpdateOrDelete() && !rightHasUniqueMatches_)
{
if( child(0)->getRETDesc() && getRETDesc() ) // soln:10_040806_8608, in the case of JoinLeftShiftRule the RETDesc_ may be NULL
{
child(0)->getRETDesc()->getTableList(xtnmList1, &fmtdList1);
getRETDesc()->getTableList(xtnmList2, &fmtdList2);
*CmpCommon::diags() << DgSqlCode(-4150)
<< DgString0(fmtdList1)
<< (rightGA.isEmbeddedUpdate() ?
DgString1("update"):DgString1("delete"))
<< DgString2(fmtdList2) ;
}
}
// QSTUFF ^^
processCompRefOptConstraints(normWAPtr);
myGA.addSuitableRefOptConstraints(leftConstraints);
myGA.addSuitableRefOptConstraints(rightConstraints);
} // Join::synthConstraints()
void
Join::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (NOT getGroupAttr()->existsLogExprForSynthesis())
{
RelExpr::synthLogProp(normWAPtr);
// set the number of tables in the join (that form the join backbone).
getGroupAttr()->setNumJoinedTables (
child(0).getGroupAttr()->getNumJoinedTables() + 1);
// Let's clear the logExprForSynthesis just set in
// RelExpr::synthLogProp() so that synthConstraints() can
// do its job.
getGroupAttr()->setLogExprForSynthesis(NULL);
}
//-----------------------------------------------------
// Find out our equijoin predicates and save them in
// equiJoinPredicates_.
// They remain also in selectionPred() or joinPred().
//-----------------------------------------------------
findEquiJoinPredicates();
//-----------------------------------------------------
// synthesize my cardinality and uniqueness constraints
//-----------------------------------------------------
synthConstraints(normWAPtr);
// If we are synthesising constraints for the first time
// for the group rmember the exprssion we used.
if (NOT getGroupAttr()->existsLogExprForSynthesis())
getGroupAttr()->setLogExprForSynthesis(this);
// for left joins we can eliminate the whole join if
// (a) outputs of right child are unique in the equijoin cols and
// (b) outputs from right child are not passed up from the join
// this join elimination is not based on RI constraints
if (isLeftJoin() && getSelectionPred().isEmpty() &&
(CmpCommon::getDefault(ELIMINATE_REDUNDANT_JOINS) != DF_OFF) &&
(NOT isFullOuterJoin()) && normWAPtr)
{
ValueIdSet equiJoinCols1 = getEquiJoinExprFromChild1();
if ((NOT equiJoinCols1.isEmpty()) &&
child(1)->getGroupAttr()->isUnique(equiJoinCols1))
{
ValueIdSet emptySet, emptySet1, coveredExpr, coveredSubExpr;
ValueIdSet myOutputs = getGroupAttr()->getCharacteristicOutputs();
child(1)->getGroupAttr()->coverTest(
myOutputs,
emptySet,
coveredExpr,
emptySet1,
&coveredSubExpr);
if(coveredExpr.isEmpty() && coveredSubExpr.isEmpty())
{
NABoolean usesInstNullInOutput =
myOutputs.referencesOneValueFromTheSet(nullInstantiatedOutput());
if(!usesInstNullInOutput)
{
setMarkedForElimination(TRUE);
normWAPtr->setContainsJoinsToBeEliminated(TRUE);
}
}
else
{
ValueIdSet globalNonEssOutputs;
if (normWAPtr->getExtraHubVertex() &&
CmpCommon::getDefault(MVQR_USE_EXTRA_HUB_TABLES) == DF_ON )
{
normWAPtr->getExtraHubVertex()->getGroupAttr()->
getNonEssentialCharacteristicOutputs(globalNonEssOutputs);
ValueIdSet nullOutputs(nullInstantiatedOutput());
nullOutputs.intersectSet(myOutputs); // affects nullOutputs
nullOutputs -= child(0)->getGroupAttr()->getCharacteristicOutputs();
// removes join preds from nullOutputs, since those can be considered to
// be provided by the child(0) and therefore should not affect whether
// child(1) is considered an extra-hub table.
if ((!nullOutputs.isEmpty()) && nullOutputs.referencesAllValuesFromMe(globalNonEssOutputs))
{
RelExpr * descendant = child(1)->castToRelExpr();
while (descendant->getArity() == 1)
descendant = descendant->child(0)->castToRelExpr();
if (descendant->getArity() == 0)
{
descendant->setIsExtraHub(TRUE);
setIsExtraHub(TRUE);
/*normWAPtr->setCheckForExtraHubTables(TRUE);
addExtraHubNonEssentialOutputs(
referencedTable->getGroupAttr()->
getEssentialCharacteristicOutputs()); */
}
}
}
}
}
}
}// Join::synthLogProp()
// -----------------------------------------------------------------------
// The following method is called as a first step during optimization
// to reorder the join query by increasing estimated rowcount. The
// purpose of reordering the join tree is to come up with a reasonable
// cost limit during the first optimization pass.
//
// The reorderTree() is used also for pre-optimizer pass
// -----------------------------------------------------------------------
RelExpr *
Join::reorderTree(NABoolean & treeModified, NABoolean doReorderJoin)
{
treeModified = FALSE; // Indicates whether this tree is modified.
NABoolean childModified; // Indicates whether any child of any of
// the join nodes are modified.
// If this is not an inner join (i.e. semi-join, tsj, or outer join),
// then we can't reorder this join node. Just reorder the children
// of this join node.
if (getOperatorType() != REL_JOIN)
{
for (Lng32 i = 0; i < getArity(); i++)
{
child(i) = child(i)->reorderTree (childModified, doReorderJoin);
if (childModified)
treeModified = TRUE;
}
// If any of my children were reordered, then we need to
// clear my logical properties for resynthesis.
if (treeModified)
getGroupAttr()->clearLogProperties();
return this;
}
if (!doReorderJoin)
return(this);
RelExprList * OBRowcountList = new(CmpCommon::statementHeap())
RelExprList(CmpCommon::statementHeap());
RelExprList * orderedList = new(CmpCommon::statementHeap())
RelExprList(CmpCommon::statementHeap());
RelExprList * joinBackBoneList = new(CmpCommon::statementHeap())
RelExprList(CmpCommon::statementHeap());
// Walk down the Join tree, inserting children of the join nodes
// in an ordered list (ordered by increasing estimated rowcount).
RelExpr * expr = this;
while ((expr != NULL) AND (expr->getOperatorType() == REL_JOIN)
// QSTUFF
// we must prevent that accidentially a join is pushed into
// a generic update subtree
AND (NOT expr->getGroupAttr()->isGenericUpdateRoot())
// QSTUFF
)
{
// Remember the list of join backbones so we can do
// reverse tree walks.
joinBackBoneList->insertAt (0, expr);
// Keep an ordered list of the join children (ordered by rowcount).
OBRowcountList->insertOrderByRowcount (expr->child(1));
// Keep an original ordering of join children.
orderedList->insertAt (0, expr->child(1));
if (expr->child(0)->getOperatorType() != REL_JOIN)
{
OBRowcountList->insertOrderByRowcount (expr->child(0));
orderedList->insertAt (0, expr->child(0));
}
expr = expr->child(0);
}
// something went wrong, while inserting the expressions
CMPASSERT (joinBackBoneList->entries() == OBRowcountList->entries()-1);
if (*OBRowcountList != *orderedList)
treeModified = TRUE;
// **************************************************************
// If none of the children of the join nodes visited here needed to be
// reordered, then all that is left to be done in this call is to call
// this method recursively on the children to see if they need to be
// reordered.
// **************************************************************
if (NOT treeModified)
{
// First, reorder the left child of the innermost join node.
(*joinBackBoneList)[0]->child(0) =
(*OBRowcountList)[0]->reorderTree (childModified, doReorderJoin);
if (childModified) treeModified = TRUE;
// Second, reorder all the right children.
for (Lng32 i = 0; i < (Lng32)joinBackBoneList->entries(); i++)
{
// set up the right child of the current join node.
(*joinBackBoneList)[i]->child(1) =
(*OBRowcountList)[i+1]->reorderTree (childModified, doReorderJoin);
if (childModified)
treeModified = TRUE;
// If any of the children were reordered, then we need to clear
// the logical properties of the current join expr for resynthesis.
if (treeModified)
(*joinBackBoneList)[i]->getGroupAttr()->clearLogProperties();
}
// cleanup
delete OBRowcountList;
delete orderedList;
delete joinBackBoneList;
return this;
}
// **************************************************************
// SECOND: ACTUALLY REORDER the join tree.
// **************************************************************
// Now, perform a reverse tree walk of the join nodes.
// Pull up join predicates to the top most join node, recomputing
// outer references along the way.
// First, set up the left child of the innermost join node.
(*joinBackBoneList)[0]->child(0) =
(*OBRowcountList)[0]->reorderTree (childModified, doReorderJoin);
if (childModified) treeModified = TRUE;
for (Lng32 i = 0; i < (Lng32)joinBackBoneList->entries(); i++)
{
// set up the right child of the current join node.
(*joinBackBoneList)[i]->child(1) =
(*OBRowcountList)[i+1]->reorderTree (childModified, doReorderJoin);
if (childModified) treeModified = TRUE;
// Pull up join predicates from its child join node, if any.
// Also, recompute outer references for this child node.
RelExpr * joinExpr = (*joinBackBoneList)[i];
if (i > 0)
{
joinExpr->selectionPred() += joinExpr->child(0)->selectionPred();
joinExpr->child(0)->selectionPred().clear();
}
// Make the inputs minimal and the outputs maximal before calling
// pushdownCoveredExpressions
if (i < (Lng32)joinBackBoneList->entries() -1)
{
ValueIdSet inputValues;
inputValues =
joinExpr->child(0).getGroupAttr()->getCharacteristicInputs();
inputValues +=
joinExpr->child(1).getGroupAttr()->getCharacteristicInputs();
joinExpr->getGroupAttr()->setCharacteristicInputs(inputValues);
joinExpr->primeGroupAttributes();
}
}
// If any of my children were reordered, then we need to
// clear my logical properties for resynthesis.
if (treeModified)
getGroupAttr()->clearLogProperties();
// Now, walk down this newly transformed tree of join nodes,
// pushing down join predicates that were pulled up.
expr = this;
// Is the left child of this inner join a inner join itself? We would
// have pulled up join predicates only if this is true.
if ((expr->child(0)->getOperatorType() == REL_JOIN)
// QSTUFF
// We didn't touch a generic update root, so no need to consider
// pushing any predicates down to it.
AND (NOT expr->child(0)->getGroupAttr()->isGenericUpdateRoot())
// QSTUFF
)
{
NABoolean moreJoinNodesWhosePredsWerePulledUp = TRUE;
while (moreJoinNodesWhosePredsWerePulledUp)
{
// Push predicates down to the join so the poor guy gets the join
// predicates he deserves.
ValueIdSet originalPred = expr->getSelectionPred();
RelExpr *exprRChild = expr->child(1);
RelExpr *exprLChild = expr->child(0);
ValueIdSet originalRCPred = exprRChild->getSelectionPred();
ValueIdSet originalLCPred = exprLChild->getSelectionPred();
ValueIdSet rcOutputs = exprRChild->getGroupAttr()->
getCharacteristicOutputs();
expr->pushdownCoveredExpr(
expr->getGroupAttr()->getCharacteristicOutputs(),
expr->getGroupAttr()->getCharacteristicInputs(),
expr->selectionPred());
// remove predicates that are not covered at the right child
// why we remove pushed down predicates from right child of join:
// earlier in the method, predicates are pulled up only from the child(0)
// but pushdown covered expression pushes down predicates to both
// children. Now we attempt to undo work done for the right child
// We need to remove uncovered predicates from child(1)
// Solution: 10-070620-5692
// restore original Right Child Selection predicate
// restore original right child outputs
// restore selection predicate of join expression: add predicates
// pushed down to right child, but not to left
// compute selection predicates passed down to right child,
// but not to left child
ValueIdSet updatedRChildPred = exprRChild->getSelectionPred();
updatedRChildPred.subtractSet (originalRCPred);
ValueIdSet updatedLChildPred = exprLChild->getSelectionPred();
updatedLChildPred.subtractSet (originalLCPred);
exprRChild->selectionPred() = originalRCPred;
exprRChild->getGroupAttr()->setCharacteristicOutputs(rcOutputs);
updatedRChildPred.subtractSet(updatedLChildPred);
expr->selectionPred().addSet(
updatedRChildPred);
// Go to the next join node
expr = expr->child(0);
// The join may involve different tables then it did before, so we
// will have to resynthesize the logical properties. Clearing them
// now will force us to do this later.
expr->getGroupAttr()->clearLogProperties();
// If the next join node's left child isn't another inner join, then
// we've pushed down all the join predicates that we pulled up.
// So we are done.
if ((expr->child(0)->getOperatorType() != REL_JOIN)
// QSTUFF
// We didn't touch a generic update root, so no need to consider
// pushing any predicates down to it.
OR expr->child(0)->getGroupAttr()->isGenericUpdateRoot()
// QSTUFF
)
{
moreJoinNodesWhosePredsWerePulledUp = FALSE;
}
} // end while more join nodes who need preds pushed to them
// Finally, we need to remove from the lowest level join any veg
// predicates we pushed down that are not true join predicates for
// this join. The pushdown logic pushes all veg preds down even if
// they are only covered by one of the children so that the veg pred
// can be translated into a "IS NOT NULL" pred when it gets to a
// leaf. But we don't keep pushing down all the way to the leaves,
// so these veg preds can end up stranded at a join node where they
// don't belong. So, we need to get rid of them. We don't have to
// worry about actually pushing them to the leaves because the
// normalizer already did that. This code is stolen directly
// from the end of Join::pushdownCoveredExpr().
ValueIdSet VEGEqPreds;
expr->selectionPred().lookForVEGPredicates(VEGEqPreds);
// ---------------------------------------------------------------------
// Remove those VEGPreds which are not covered at first child. For
// example VEGPred(VEG{T2.a,T3.a}) in JOIN1 of (T1 JOIN1 (T2 JOIN2 T3))
// is not covered at the first child. The predicate could be pushed
// down to the second child without being retained at JOIN1.
// ---------------------------------------------------------------------
VEGEqPreds.removeUnCoveredExprs(
expr->child(0).getGroupAttr()->getCharacteristicOutputs());
// ---------------------------------------------------------------------
// Remove those VEGPreds which are not covered at second child. For
// example VEGPred(VEG{T1.a,T2.a}) in JOIN2 of ((T1 JOIN1 T2) JOIN2 T3)
// is not covered at the second child. The predicate could be pushed
// down to the first child without being retained at JOIN2.
// ---------------------------------------------------------------------
VEGEqPreds.removeUnCoveredExprs(
expr->child(1).getGroupAttr()->getCharacteristicOutputs());
} // end if we need to push down join preds we pulled up
// cleanup
delete OBRowcountList;
delete orderedList;
delete joinBackBoneList;
return this;
} // Join::reorderTree
const MergeType Join::getMergeTypeToBeUsedForSynthLogProperties()
{
MergeType mergeMethod;
if (isSemiJoin())
mergeMethod = SEMI_JOIN_MERGE;
else if (isAntiSemiJoin())
mergeMethod = ANTI_SEMI_JOIN_MERGE ;
else if (isInnerNonSemiJoin() || getOperatorType() == REL_TSJ_FLOW)
mergeMethod = INNER_JOIN_MERGE;
else if (isLeftJoin() || isFullOuterJoin())
mergeMethod = OUTER_JOIN_MERGE;
else
{
CCMPASSERT("Incorrect join type");
mergeMethod = INNER_JOIN_MERGE;
}
return mergeMethod;
}
const MCSkewedValueList * Join::getMCSkewedValueListForJoinPreds(ValueIdList & colGroup)
{
ColStatDescList leftColStatDescList = child(0).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->colStats();
ColStatDescList rightColStatDescList = child(1).outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->colStats();
ValueId col;
ValueIdSet lhsCols, rhsCols;
CollIndex index = NULL_COLL_INDEX;
ValueIdSet joiningCols = getEquiJoinExprFromChild0();
for (col = joiningCols.init();
joiningCols.next(col);
joiningCols.advance(col) )
{
leftColStatDescList.getColStatDescIndex(index, col);
if (index == NULL_COLL_INDEX)
continue;
lhsCols.insert(leftColStatDescList[index]->getColumn());
}
index = NULL_COLL_INDEX;
joiningCols.clear();
joiningCols = getEquiJoinExprFromChild1();
for (col = joiningCols.init();
joiningCols.next(col);
joiningCols.advance(col) )
{
rightColStatDescList.getColStatDescIndex(index, col);
if (index == NULL_COLL_INDEX)
continue;
rhsCols.insert(rightColStatDescList[index]->getColumn());
}
if((lhsCols.entries() == 0) ||
(rhsCols.entries() == 0) ||
(lhsCols.entries() != rhsCols.entries()))
return NULL;
//Extract MC Skew values for column group that are part of join predicates
ValueIdList lhsColGrp, rhsColGrp;
MCSkewedValueList* leftMCSkewedValueList = (MCSkewedValueList *)leftColStatDescList.getMCSkewedValueListForCols(lhsCols, lhsColGrp);
MCSkewedValueList* rightMCSkewedValueList = (MCSkewedValueList *)rightColStatDescList.getMCSkewedValueListForCols(rhsCols, rhsColGrp);
CostScalar avgRowcountForNonSkewValuesOnLeftSide = leftColStatDescList.getAvgRowcountForNonSkewedMCValues(lhsCols, leftMCSkewedValueList);
CostScalar avgRowcountForNonSkewValuesOnRightSide = rightColStatDescList.getAvgRowcountForNonSkewedMCValues(rhsCols, rightMCSkewedValueList);
MCSkewedValueList * result = new (STMTHEAP) MCSkewedValueList(STMTHEAP);
result->mergeMCSkewedValueList(leftMCSkewedValueList,
rightMCSkewedValueList,
avgRowcountForNonSkewValuesOnLeftSide,
avgRowcountForNonSkewValuesOnRightSide,
getMergeTypeToBeUsedForSynthLogProperties());
colGroup = lhsColGrp;
if ( result->entries() == 0 ) {
NADELETE(result, MCSkewedValueList, STMTHEAP);
result = NULL;
}
return result;
}
// -----------------------------------------------------------------------
// member functions for class Union
//
// First: Union::synthEstLogProp
// -----------------------------------------------------------------------
void
Union::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp());
const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();
EstLogPropSharedPtr leftEstProps;
EstLogPropSharedPtr rightEstProps;
EstLogPropSharedPtr copyInputEstProp;
// if inputForSemiTSJ is set for inputEstLogProp it cannot be passed below the union
// so create a new EstLogProp with the flag set off to pass to my children
if (inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ)
{
copyInputEstProp = EstLogPropSharedPtr(new (HISTHEAP)
EstLogProp(*inputEstLogProp));
copyInputEstProp->setInputForSemiTSJ(EstLogProp::NOT_SEMI_TSJ);
}
else
{
copyInputEstProp = inputEstLogProp;
}
leftEstProps = child(0).outputLogProp(copyInputEstProp);
rightEstProps = child(1).outputLogProp(copyInputEstProp);
CostScalar rowCount = leftEstProps->getResultCardinality() +
rightEstProps->getResultCardinality();
// The unresolved predicates of a union are those that are unresolved
// by both children.
myEstProps->unresolvedPreds() += leftEstProps->getUnresolvedPreds();
myEstProps->unresolvedPreds().intersectSet(
rightEstProps->getUnresolvedPreds());
// -----------------------------------------------------------------
// access the column statistics descriptor list from the left and
// right child's estimated logical properties.
// -----------------------------------------------------------------
const ColStatDescList &leftColStatsList = leftEstProps->getColStats();
const ColStatDescList &rightColStatsList = rightEstProps->getColStats();
ColStatDescList unionStatDescList; // Result Column Stats.
unionStatDescList.insertIntoUecList (leftColStatsList.getUecList()) ;
unionStatDescList.insertIntoUecList (rightColStatsList.getUecList()) ;
// Get the information needed for associating the children's columns
// with the union's result columns
const ValueIdList & unionMapTable = colMapTable();
// -------------------------------------------------------------------
// Because of the way that outer references are dealt with, there is
// an implicit unioning operation between the copy of the outer ref'
// column descriptors that were sent to the left child of the union and
// those sent to the right.
// First, produce the result of that implicit union so that it may
// become part of the output logical properties of this operator.
// -------------------------------------------------------------------
CollIndex i;
CollIndex j;
CollIndex leftMatch = NULL_COLL_INDEX, rightMatch = NULL_COLL_INDEX;
NABoolean foundLeftMatch = FALSE;
NABoolean foundRightMatch = FALSE;
for (i = 0; i < outerColStatsList.entries(); i++)
{
// ------------------------------------------------------------------
// For each outer reference.
// Look for the matching column statistics from the left child.
// if found:
// Look for the matching column statistics from the right child
// if found:
// if left and right column statistics include a histogram
// Produce a histogram for the result of the Union
// ------------------------------------------------------------------
ColStatDescSharedPtr outerStatDesc = outerColStatsList[i];
const ItemExpr * outerItem =
outerStatDesc->getVEGColumn().getItemExpr();
foundLeftMatch = FALSE;
foundRightMatch = FALSE;
// look for matching column statistics from the left side.
for (j = 0; j < leftColStatsList.entries() && !foundLeftMatch; j++)
{
ColStatDescSharedPtr lStatDesc = leftColStatsList[j];
const ItemExpr * leftItem =
lStatDesc->getVEGColumn().getItemExpr();
if (leftItem->getValueId() == outerItem->getValueId())
{
foundLeftMatch = TRUE;
leftMatch = j;
}
}
// look for matching column statistics from the right side.
for (j = 0; j < rightColStatsList.entries() && !foundRightMatch; j++)
{
ColStatDescSharedPtr rStatDesc = rightColStatsList[j];
const ItemExpr * rightItem =
rStatDesc->getVEGColumn().getItemExpr();
if (rightItem->getValueId() == outerItem->getValueId())
{
foundRightMatch = TRUE;
rightMatch = j;
}
}
if (foundLeftMatch && foundRightMatch)
{
// Union together left & right column statistics' histograms
ColStatDescSharedPtr lStatDesc = leftColStatsList[leftMatch];
ColStatsSharedPtr lColStats = lStatDesc->getColStats();
ColStatDescSharedPtr rStatDesc = rightColStatsList[rightMatch];
ColStatsSharedPtr rColStats = rStatDesc->getColStats();
// perform the 'union' operation into the new col stats,
// and insert the new column statistics into the results list
CollIndex entry = unionStatDescList.entries();
unionStatDescList.insertDeepCopyAt(entry, lStatDesc);
unionStatDescList[entry]->mergeColStatDesc(rStatDesc,
UNION_MERGE,
FALSE,
REL_UNION);
} // if foundLeftMatch && foundRightMatch
} // for i
// Now Union the actual result columns of the union operation, as
// opposed to the returned input columns that are unioned above.
for ( i = 0; i < unionMapTable.entries(); i++ )
{
// ------------------------------------------------------------------
// For each entry in the union's colMapTable,
// Look for the matching column statistics from the left child.
// if found:
// Look for the matching column statistics from the right child
// if found:
// if left and right column statistics include a histogram
// Produce a histogram for the result of the Union
// ------------------------------------------------------------------
ItemExpr * idUnion = unionMapTable[i].getItemExpr();
if (idUnion->getOperatorType() != ITM_VALUEIDUNION)
{
CCMPASSERT("Invalid expression in UNION");
continue;
}
// cast the source columns of UNION to base columns
BaseColumn * leftSource = ((ValueIdUnion *)idUnion)->getLeftSource().castToBaseColumn();
BaseColumn * rightSource = ((ValueIdUnion *)idUnion)->getRightSource().castToBaseColumn();
foundLeftMatch = FALSE;
foundRightMatch = FALSE;
// look for matching column statistics from the left side.
for (j = 0; j < leftColStatsList.entries() && !foundLeftMatch; j++)
{
ColStatDescSharedPtr lStatDesc = leftColStatsList[j];
const BaseColumn * leftItem =
lStatDesc->getVEGColumn().castToBaseColumn();
if (leftItem == leftSource)
{
foundLeftMatch = TRUE;
leftMatch = j;
}
}
// look for matching column statistics from the right side.
for (j = 0; j < rightColStatsList.entries() && !foundRightMatch; j++)
{
ColStatDescSharedPtr rStatDesc = rightColStatsList[j];
const BaseColumn * rightItem =
rStatDesc->getVEGColumn().castToBaseColumn();
if (rightItem == rightSource)
{
foundRightMatch = TRUE;
rightMatch = j;
}
}
if (foundLeftMatch && foundRightMatch)
{
// Union together left & right column statistics' histograms
ColStatDescSharedPtr lStatDesc = leftColStatsList[leftMatch];
ColStatsSharedPtr lColStats = lStatDesc->getColStats();
ColStatDescSharedPtr rStatDesc = rightColStatsList[rightMatch];
ColStatsSharedPtr rColStats = rStatDesc->getColStats();
// perform the 'union' operation into the new col stats, and
// insert the new column statistics into the results col stat list
CollIndex entry = unionStatDescList.entries();
unionStatDescList.insertDeepCopyAt(entry, lStatDesc);
unionStatDescList[entry]->mergeColStatDesc(rStatDesc,
UNION_MERGE,
FALSE,
REL_UNION);
// update the result's description of itself
// Note that it is proper that this isn't done in the previous
// body of code that looks (as is pointed out above), similar to
// this code.
unionStatDescList[entry]->VEGColumn() = unionMapTable[i];
// need to do the following, adding "my" ValueId to the list of
// columns I've joined to, because in ColStatDesc::mergeColStatDesc()
// we can get into trouble when we have a mergeState_ that is empty
// --> we set to empty first (via clear()) because we do not want
// the left table's mergeState information
unionStatDescList[entry]->mergeState().clear() ;
unionStatDescList[entry]->mergeState().insert(idUnion->getValueId()) ;
} // if foundLeftMatch && foundRightMatch
} // for i
// the cardinality of a union is the sum of its child cardinalities
myEstProps->setResultCardinality(rowCount);
// maxCard(s1 UNION s2) == maxCard(s1) + maxCard(s2)
myEstProps->setMaxCardEst
(MAXOF(leftEstProps->getMaxCardEst() +
rightEstProps->getMaxCardEst(),
rowCount));
// -------------------------------------------------------------------
// Lastly, determine which of these histograms are useful based on
// the characteristic outputs and selection predicates for the group.
// -------------------------------------------------------------------
myEstProps->pickOutputs( unionStatDescList, inputEstLogProp, getGroupAttr()->getCharacteristicOutputs(), getSelectionPred());
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstProps);
} // Union::synthEstLogProp
// ---------------------------------------------------------------------
// Union::synthLogProp
// ---------------------------------------------------------------------
void
Union::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
RelExpr::synthLogProp(normWAPtr);
// if both left and right child have cardinality constraint, then it
// is possible to create a cardinality constraint for the result
Cardinality minLeft, minRight, maxLeft, maxRight;
// ---------------------------------------------------------------------
// synthesize uniqueness and cardinality constraints
// ---------------------------------------------------------------------
(void) child(0).getGroupAttr()->hasCardConstraint(minLeft,maxLeft);
(void) child(1).getGroupAttr()->hasCardConstraint(minRight,maxRight);
if (minLeft > 0 OR minRight > 0
OR
(maxLeft < INFINITE_CARDINALITY AND maxRight < INFINITE_CARDINALITY))
{
// if one of the upper bounds is infinity, then make the total upper
// bound infinity, otherwise add the lower and upper bound together
if (maxLeft == INFINITE_CARDINALITY OR maxRight == INFINITE_CARDINALITY)
{
getGroupAttr()->addConstraint
(new(CmpCommon::statementHeap())
CardConstraint(minLeft+minRight,
(Cardinality)INFINITE_CARDINALITY));
}
else
{
getGroupAttr()->addConstraint
(new(CmpCommon::statementHeap())
CardConstraint(minLeft+minRight,
maxLeft+maxRight));
}
}
// do uniqueness constraints someday (find uniqueness constraints on both
// sides and then find check constraints that make sure the values from
// the left side don't overlap the values of the right side) $$$$
} // Union::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class GroupByAgg
// -----------------------------------------------------------------------
void
GroupByAgg::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp());
EstLogPropSharedPtr intermedEstProps(new (HISTHEAP) EstLogProp());
CostScalar maxCard = COSTSCALAR_MAX;
//--------------------------------------------------------------------
// access the column statistics descriptor lists passed in as outer
// references and those generated by the child.
//--------------------------------------------------------------------
const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();
CollIndex outerRefCount = outerColStatsList.entries();
// We will now get the Estimated Logical Properties of the child of
// Materialize; also multipleCalls will tell us whether or not such
// child gets called only once.
Int32 multipleCalls;
EstLogPropSharedPtr modInputLP;
if (inputEstLogProp->getResultCardinality() > CostScalar (1) &&
CmpCommon::getDefault(COSTING_SHORTCUT_GROUPBY_FIX) != DF_ON)
{
// Executor doesn't materialize grouby operator, but optimizer assumes
// it does. Because of this we under estimate groupby cost when it
// is right side of NJ. This CQD is being used since it is already
// available and fix is generic, not specific to short cut grby.
modInputLP =
child(0).getGroupAttr()
->materializeInputLogProp(inputEstLogProp, &multipleCalls);
}
else
modInputLP = inputEstLogProp;
EstLogPropSharedPtr childEstProps;
EstLogPropSharedPtr copyInputEstProp;
// if inputForSemiTSJ is set for inputEstLogProp it cannot be passed
// below the group-by ==> so create a new EstLogProp with the flag set
// off to pass to my children
if (inputEstLogProp->getInputForSemiTSJ() != EstLogProp::NOT_SEMI_TSJ)
{
copyInputEstProp = EstLogPropSharedPtr(new (HISTHEAP) EstLogProp(*modInputLP));
copyInputEstProp->setInputForSemiTSJ(EstLogProp::NOT_SEMI_TSJ);
}
else
{
copyInputEstProp = modInputLP;
}
childEstProps = child(0).outputLogProp(copyInputEstProp);
const ColStatDescList &childColStatsList = childEstProps->getColStats();
// Initially, the unresolved predicates of a GROUPBY are those that are
// unresolved by the child.
myEstProps->unresolvedPreds() += childEstProps->getUnresolvedPreds();
ColStatDescList groupStatDescList;
CollIndex i;
CostScalar resultCardinality;
NABoolean foundFlag;
// make the group-by local copy of the information returned by the child
groupStatDescList.makeDeepCopy( childColStatsList );
//--------------------------------------------------------------------
// The GROUP BY, implicitly, contains any COLUMNS appearing as Outer
// References. Add any such columns to the set of GROUPing columns.
// Note: it is not necessary to add these columns to groupStatDescList,
// as they should already be there.
//
// This modeling of the impact of outer references is not perfect, but
// it is (in many cases) close to the truth, and it is about as good
// as the group-by can do at this time.
// For cardinality estimations we shall use only the selected columns
//--------------------------------------------------------------------
ValueIdSet workGroup = groupExpr_;
const ValueIdSet requiredInput = getGroupAttr()->getCharacteristicInputs();
workGroup.removeCoveredExprs(requiredInput);
NABoolean trueScalAgg = (!aggregateExpr_.isEmpty() && groupExpr_.isEmpty());
if(!trueScalAgg)
{
for ( i = 0; i < outerColStatsList.entries(); i++ )
{
const ItemExpr * colExpr = outerColStatsList[i]->getVEGColumn().getItemExpr() ;
foundFlag = FALSE; // assume invalid; then disprove assumption
for ( ValueId idOut = requiredInput.init() ;
requiredInput.next (idOut) AND NOT foundFlag ;
requiredInput.advance (idOut) )
{
if ( colExpr->getValueId() == idOut )
{
foundFlag = TRUE;
break ; // jump out to outer for-loop
}
else if ( idOut.getItemExpr()->getOperatorType() ==
ITM_VEG_REFERENCE )
{
VEG * nestedVEG = ((VEGReference *) (idOut.getItemExpr()))->getVEG() ;
const ValueIdSet & VEGGroup = nestedVEG->getAllValues() ;
for ( ValueId id = VEGGroup.init() ;
VEGGroup.next(id) ;
VEGGroup.advance(id) )
{
if ( colExpr->getValueId() == id )
{
foundFlag = TRUE ;
break ; // break out to 2nd loop -- wish we could jump out more!
}
}
}
}
if ( foundFlag )
{
workGroup.insert (colExpr->getValueId()) ;
}
}
}
ValueIdSet workGroupCopy = workGroup; // save the GROUP-BY specification
//--------------------------------------------------------------------
// Determine something of a worst-case result cardinality for this
// grouping operation. This is the cardinality of the child of GroupBy
// for the given inputEstLogProp - nothing to group.
//--------------------------------------------------------------------
CostScalar oldCount = childEstProps->getResultCardinality();
if (oldCount < 0)
{
CCMPASSERT (oldCount >= 0) ;
oldCount = 0;
}
//--------------------------------------------------------------------
// Then attempt to improve on that estimate.
// is this a simple aggregate (no groupby columns) or a "real" groupby
//--------------------------------------------------------------------
CostScalar uecTotal = 1;
CostScalar maxUECofGrpBy = 0;
if(trueScalAgg)
{
// set the estimated cardinality to 1 also
resultCardinality = 1 ;
// For groupbys below nested join multiply by input cardinality.
// This fix generic to scalar aggregate but is being controlled by
// existing CQD COSTING_SHORTCUT_GROUPBY_FIX.
if (CmpCommon::getDefault(COSTING_SHORTCUT_GROUPBY_FIX) == DF_ON)
{
CostScalar inputCard = copyInputEstProp->getResultCardinality();
if (inputCard > csOne)
resultCardinality = MIN_ONE(inputCard < oldCount ? inputCard : oldCount);
}
}
else
{
resultCardinality = 1 ; // make sure this variable is assigned to!
NAList<CollIndex> singleStats(CmpCommon::statementHeap());
CollIndex lastStats = NULL_COLL_INDEX;
CostScalar uecTotalFromCompExp = csOne;
ValueIdSet vegWorkGroup, compExpWorkGroup;
if (CmpCommon::getDefault(COMP_BOOL_148) == DF_ON)
{
workGroup.lookForVEGReferences(vegWorkGroup);
workGroup.lookForVEGPredicates(vegWorkGroup);
compExpWorkGroup.insert(workGroup.subtractSet(vegWorkGroup));
for ( ValueId compExpWorkGroupItem = compExpWorkGroup.init();
compExpWorkGroup.next(compExpWorkGroupItem);
compExpWorkGroup.advance(compExpWorkGroupItem))
{
ItemExpr *colExpr = compExpWorkGroupItem.getItemExpr() ;
CostScalar minUec = csOne, maxUec = csOne;
if(colExpr->calculateMinMaxUecs(groupStatDescList, minUec, maxUec))
uecTotalFromCompExp *= minUec;
else
vegWorkGroup.insert(compExpWorkGroupItem);
}
workGroup = vegWorkGroup;
}
// -----------------------------------------------------------------
// Determine how many groups there are.
// The trick is to avoid having a single grouping column counted
// multiple times because it appears in multiple statistics.
//
// Walk the group-by list multiple times, where every pass
// - locates the relevant multi-column histogram with the most
// columns;
// - factors the uec of that histogram into the running uecTotal;
// - removes the columns associated with that histogram from the
// group-by column set 'workGroup.'
//
//
// First: Attempt to locate statistics for
// - individual columns currently appearing in the GROUP BY;
// - multi-column histograms, ALL of whose columns appear in the
// GROUP BY (eventually, test for Leading columns).
//
// While doing this, remember the multi-column histogram with the
// most columns.
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// mar : at this point we need to check : do any of the
// grouping columns uniquely determine any of the others?
//
// -> if so, this is the point (right before the loop below)
// that we need to remove them from workGroup
// -----------------------------------------------------------------
// look at base table information, use primary key information
// from the NABaseColumn's
//
// we iterate through all of the columns in the
// workGroup list
//
// . for each one, we look for information about
// it in the groupStatDescList
//
// MAKE SURE THE COLUMN IS A ITM_BASECOLUMN
// if (colExpr->getOperatorType() == ITM_BASECOLUMN) ...
//
// . if we find a column 'c' that is a primary key in its
// base table, then we remove all columns from 'workGroup'
// that are in the same table as 'c'
const ValueIdList workGroupList = workGroup ;
// first, look at all entries from the VEG's contained
// in 'workGroup', saving the ones that are base columns
// into 'baseColumnSet'.
// Definitions:
// baseColumnSet - set of base columns which constitute the work group.
// columnOfThisVEG - it is a list of ValueIdSets, where each ValueIdASet
// is a set of baseColumns for each workGroup item.
// Please note, that the workGroup is composed of VEG
// columns and not the base columns
// groupByTables - Set of all tables whose columns are participating
// in the groupBy.
// columnToRemove - From all the tables which participate in the groupBy
// collect all non-primary key columns. These columns
// are probable redundant columns.
// probableRedundantColSet - are the columnToRemove that are also present
// in the baseColumnSet.
// interestingColSet - are the set of columns from the baseColumnSet which
// are primary keys of the tables, these will be used later
// to check for dependencies
ValueIdSet baseColumnSet ;
LIST(ValueIdSet ) columnsOfThisVEG(STMTHEAP);
ValueIdSet probableRedundantColSet;
SET(TableDesc *) * groupByTables = NULL;
for ( i = 0 ; i < workGroupList.entries() ; i++ )
{
ItemExpr *colExpr = workGroupList[i].getItemExpr() ;
ValueIdSet columns;
colExpr->findAll( ITM_BASECOLUMN, columns, TRUE, TRUE );
baseColumnSet.insert(columns);
columnsOfThisVEG.insertAt(i, columns);
}
// Now get all the base tables involved in GroupBy
groupByTables = baseColumnSet.getAllTables();
// for all tables, collect the columns that can removed. These should
// not be non-primary key column
ValueIdSet columnsToRemove;
// Save columns that are interesting, and should stay if they do not have
// any dependencies
ValueIdSet interestingColSet;
for (i = 0; i < groupByTables->entries(); i++ )
{
TableDesc * thisTable = (*groupByTables)[i];
// get all primary key column and non-primary keys columns for this table
ValueIdSet primaryKeyColumns = (ValueIdSet)(thisTable->getPrimaryKeyColumns() );
ValueIdList userColumns;
// get All user columns for this table;
thisTable->getUserColumnList(userColumns);
// Non primary key columns will be user columns minus the primary key columns
ValueIdSet nonPrimaryKeyColumns(userColumns);
// subtract primary key columns from here to get non-primary key columns
nonPrimaryKeyColumns.subtractSet(primaryKeyColumns);
probableRedundantColSet.insert(nonPrimaryKeyColumns.intersect(baseColumnSet));
// use this nonprimarykey columns set to get all the columns that can
// be removed from grouping columns set. These can be removed only if the
// entire key is covered by the grouping column set
if ( !(primaryKeyColumns.isEmpty()) &&
(primaryKeyColumns.intersect(baseColumnSet) == primaryKeyColumns ) )
{
columnsToRemove.insert(nonPrimaryKeyColumns.intersect(baseColumnSet) );
interestingColSet.insert(primaryKeyColumns);
}
}
// If there are no columns to remove, or if there are no interesting columns
// then we don't want to remove any columns from the workGroup.
// Else, we shall remove the VEGes from the work group which contain
// these columns, but do not contain any columns from the interesting
// columns set. We will also check for dependency information if there
// are primary keys from more than one tables.
if (!columnsToRemove.isEmpty() && !interestingColSet.isEmpty() )
{
ValueIdSet updatedColumnsToRemove = columnsToRemove;
// go to each VEG in the work group to see if it can be removed
// can we use dependency information?
NABoolean considerDependency;
SET(TableDesc *) * interestingColTables = interestingColSet.getAllTables();
if ( (!probableRedundantColSet.isEmpty()) && (interestingColTables->entries() > 1))
considerDependency = TRUE;
else
considerDependency = FALSE;
//First remove the redundant columns that have direct dependency
for ( i = 0 ; i < workGroupList.entries() ; i++ )
{
ValueIdSet interestingColsFromThisVeg = columnsOfThisVEG[i].intersect(interestingColSet);
// sanity check to ensure that there is atleast one item in the workGroup.
if (workGroup.entries() == 1) break;
ValueIdSet redColCandidates = columnsOfThisVEG[i].intersect(columnsToRemove);
if ( (!redColCandidates.isEmpty() ) &&
(interestingColsFromThisVeg.isEmpty()) )
{
workGroup.remove(workGroupList[i]);
updatedColumnsToRemove.subtractSet(redColCandidates);
}
}
// Now, consider indirect dependencies among remaining grouping columns
if (considerDependency)
handleIndirectDepInGroupingcols(workGroup, interestingColSet, updatedColumnsToRemove,
probableRedundantColSet, groupStatDescList);
}
// Now that we have a final list of grouping cols, lets figure out
// the maximum limit for group by cardinality
AppliedStatMan * appStatMan = QueryAnalysis::ASM();
if (appStatMan && (workGroup.entries() > 0))
{
NABoolean allTablesConsidered = TRUE;
// currMax is used to keep track of the running max card
// used to set the upper limit to the cardinality of GroupBy
CostScalar currentMax = csOne;
SET(TableDesc *) * workGroupTables = new STMTHEAP SET(TableDesc *)(STMTHEAP);
SET(TableDesc *) * relevantGroupTables = new STMTHEAP SET(TableDesc *)(STMTHEAP);
// get all subtree tables of this group
if (getGroupAttr()->getGroupAnalysis())
{
CANodeIdSet subtreeset = getGroupAttr()->getGroupAnalysis()->getAllSubtreeTables();
for (CANodeId id = subtreeset.init();
subtreeset.next(id);
subtreeset.advance(id))
{
TableDesc * tdesc = id.getNodeAnalysis()->getTableAnalysis()->getTableDesc();
workGroupTables->insert(tdesc);
}
}
// Now go thru all sub tree tables to see which one of them
// satisfy all grouping expressions
for (CollIndex j = 0; j < workGroupTables->entries(); j ++)
{
TableDesc * thisTable = (*workGroupTables)[j];
const ValueIdSet allColsOfThisTable = thisTable->getColumnList();
NABoolean useTableForMaxCard = TRUE;
for ( ValueId workGroupItem = workGroup.init();
workGroup.next(workGroupItem);
workGroup.advance(workGroupItem))
{
ItemExpr *colExpr = workGroupItem.getItemExpr() ;
colExpr = colExpr->getLeafValueIfUseStats();
ValueIdSet columns;
colExpr->findAll( ITM_BASECOLUMN, columns, TRUE, TRUE );
if ( (colExpr->getOperatorType() != ITM_VEG_REFERENCE) ||
(columns.isEmpty()) )
{
// This is a case where the grouping expression contains
// ValueIdUnion of constants. This could be a typical case
// of Transpose. In this case we do not want to set any
// limit to the max cardinality
// if there are no grouping columns, there is nothing to do
useTableForMaxCard = FALSE;
break;
}
if (!columns.isEmpty())
{
// is any column in this workGroup contained in the subtree table?
// If not, then we cannot use this table to compute maxCard of GroupBy
// skip to next table
columns.intersectSet(allColsOfThisTable);
if (columns.isEmpty())
{
useTableForMaxCard = FALSE;
break;
}
}
}
if (useTableForMaxCard)
relevantGroupTables->insert(thisTable);
}
if (relevantGroupTables->entries() != workGroupTables->entries())
allTablesConsidered = FALSE;
{
for (i = 0; i < relevantGroupTables->entries(); i++ )
{
TableDesc * thisTable = (*relevantGroupTables)[i];
const TableAnalysis * thisTableAnalysis = thisTable->getTableAnalysis() ;
if (thisTableAnalysis &&
thisTableAnalysis->getNodeAnalysis() )
{
CANodeId localNodeId = thisTableAnalysis->getNodeAnalysis()->getId();
NABoolean flag = (inputEstLogProp->isCacheable() ? TRUE : FALSE);
if (inputEstLogProp != (*GLOBAL_EMPTY_INPUT_LOGPROP))
inputEstLogProp->setCacheableFlag(FALSE);
currentMax *= appStatMan->getStatsForCANodeId(localNodeId, inputEstLogProp)->getResultCardinality();
inputEstLogProp->setCacheableFlag(flag);
}
else
{
allTablesConsidered = FALSE;
break;
}
} // for all relevantGroupTables
}
if ((allTablesConsidered) && (workGroupTables->entries()))
{
maxCard = currentMax;
maxUECofGrpBy = currentMax;
}
} // if AppStatMan
// ------------------------------------------------------------------
// the first thing we want to do is take advantage of any
// multi-column uec information that's available to us;
// if we have a multi-column uec value for columns that all
// appear in the grouping list, then remove those columns from the
// grouping list and use this uec value in their place.
// ------------------------------------------------------------------
const MultiColumnUecList * ueclist = groupStatDescList.getUecList() ;
ValueIdSet subset, prevSubset ;
NABoolean largeTableNeedsStats = FALSE;
// should stats exist for this table?
if (groupStatDescList.entries() > 0)
largeTableNeedsStats = groupStatDescList[0]->getColStats()->isUpStatsNeeded();
// we need baseColumnSet, if we need to display missing stats warning.
// This will be needed only if there are more than one grouping columns
// remaining.
SET(TableDesc *) * tablesWithMissingStats = new STMTHEAP SET(TableDesc *)(STMTHEAP);
// can't do anything if the groupby list only contains 1 column
while ( workGroup.entries() > 1 && ueclist != NULL )
{
baseColumnSet.clear();
for ( ValueId workGroupItem = workGroup.init();
workGroup.next(workGroupItem);
workGroup.advance(workGroupItem))
{
ItemExpr *colExpr = workGroupItem.getItemExpr() ;
ValueIdSet columns;
if (colExpr == NULL) continue;
colExpr->findAll( ITM_BASECOLUMN, columns, TRUE, TRUE );
baseColumnSet.insert(columns);
}
prevSubset = subset ; // used to avoid infinite loops!
subset.clear() ;
// There could be cases when the null-istantiated column has participated
// in the join. This could be a case of left joins or in Union. In those
// cases, the mergeState is updated by the nulledExpression (don't know why?).
// But this should not have an impact on adjusting cardinalities for groupBy
// So, in case of null_instantiated column, retrieve its child and use
// that to compute multi-col uec adjustment. Sol: 10-060609-7077 and 10-060607-7010
subset = ueclist->largestSubset (baseColumnSet) ;
NABoolean displayWarning = TRUE;
if ( subset.entries() < 2 )
{
// there are no multi-column UECs left, hence display
// the missing stats warning for all the grouping columns
// of the remaining tables
for (i = 0; i < groupByTables->entries(); i++ )
{
TableDesc * thisTable = (*groupByTables)[i];
if (thisTable == NULL) continue;
ValueIdSet userColumnsSet(thisTable->getColumnList());
userColumnsSet.intersectSet(baseColumnSet);
// if warning has not been inserted for this table
// and number of columns to display the warning is greater than 1
if ( tablesWithMissingStats
&& !tablesWithMissingStats->contains(thisTable)
&& (userColumnsSet.entries() >= 2) )
{
// Check if the MC stats dont exist in the private copy of MC UEC list of the table
const MultiColumnUecList * ueclistOfThisTable = thisTable->getTableColStats().getUecList();
if (!ueclistOfThisTable
|| (ueclistOfThisTable->lookup(userColumnsSet) == csMinusOne))
{
// display missing stats warning, only if the warning_level
// is appropriately set and MC stats could be useful
if ((CURRSTMT_OPTDEFAULTS->histMissingStatsWarningLevel() > 2) &&
(ueclist->isMCStatsUseful(userColumnsSet, thisTable) ) )
displayWarning = TRUE;
else
displayWarning = FALSE;
ueclist->displayMissingStatsWarning(thisTable,
userColumnsSet,
largeTableNeedsStats,
displayWarning,
groupStatDescList,
csMinusOne,
FALSE,
REL_GROUPBY);
tablesWithMissingStats->insert(thisTable);
}
}
baseColumnSet.subtractSet(userColumnsSet);
if (baseColumnSet.entries() ==0)
break;
}
break ; // no multi-column-uec's of interest are available
}
// We do have multi-column UEC available. Check to see if it exists for
// all the grouping columns of the table. If not, display missing
// stats warning for those columns, and then continue to look for
// partial multi-col UECs. This would take care of case, where say
// we have group by on (a, b, c, d) and multi-col UEC for (a, b)
// and (c, d). The optimizer would use these multi-col statistics
// on the same hand it will also give a missing stats warning for
// (a, b, c, d)
ValueId firstElement;
if (subset.isEmpty() ) break;
subset.getFirst(firstElement);
ItemExpr * thisExpr = firstElement.getItemExpr();
if (thisExpr == NULL) break;
TableDesc * thisTable = ((BaseColumn *)thisExpr)->getTableDesc();
if (thisTable == NULL) break;
ValueIdSet userColumnsSet(thisTable->getColumnList());
userColumnsSet.intersectSet(baseColumnSet);
// if warning has not been inserted for this table
// and number of columns to display the warning is greater than 1
if ( tablesWithMissingStats
&& !tablesWithMissingStats->contains(thisTable)
&& (subset.entries() != userColumnsSet.entries() ) )
{
// display missing stats warning, only if the warning_level
// is appropriately set and MC stats could be useful
if ((CURRSTMT_OPTDEFAULTS->histMissingStatsWarningLevel() > 2) &&
(ueclist->isMCStatsUseful(userColumnsSet, thisTable)) )
displayWarning = TRUE;
else
displayWarning = FALSE;
ueclist->displayMissingStatsWarning(thisTable,
userColumnsSet,
largeTableNeedsStats,
displayWarning,
groupStatDescList,
csMinusOne,
FALSE,
REL_GROUPBY);
tablesWithMissingStats->insert(thisTable);
}
if ( subset == prevSubset )
break; // to avoid inf loop
// what's the group uec associated with this subset
CostScalar subsetUec = ueclist->lookup (subset) ;
if ( subsetUec <= 0 )
break; // some problem with statistics!
// if any single-column uec info or histograms are missing,
// then we can't use the multi-uec-list information
NABoolean cannotContinue = FALSE ;
// now figure out how much each of the columns in subset has
// had its uec reduced
CollIndex i ;
ValueIdList subsetList = subset ; // convenience : set->list
// product fo single column UECs will be used to set the subsetUec
CostScalar SC_Uec = csOne;
// loop over all subset entries
for ( i = 0 ; i < subsetList.entries() ; i++ )
{
// first, find out what each single-column's uec was initially
ValueIdSet wrapper ;
wrapper.insert (subsetList[i]) ;
CostScalar initUec = ueclist->lookup (wrapper) ;
if ( initUec == -1 ) // error flag indicating it wasn't found
{
cannotContinue = TRUE ;
break ;
}
// second, find out what each single-column's uec is now
CostScalar currUec = 0 ;
CollIndex index ;
// do a lookup in the CSDL for this ValueId
if ( groupStatDescList.getColStatDescIndexForColumn (index, subsetList[i]) )
{
currUec = groupStatDescList[index]->getColStats()->getTotalUec() ;
}
else
{
cannotContinue = TRUE ;
break ;
}
// groupBy card should be a MIN
// of multiColUec and the product of single column UEC of
// participating columns
SC_Uec *= currUec;
} // done processing this subset
if ( cannotContinue ) // some stats were missing
break ;
subsetUec = MINOF(subsetUec, SC_Uec);
uecTotal *= subsetUec ;
// Now remove this subset and continue
//
// want to write : workGroup.remove (subset) ;
//
// But: workGroup contains VEGRef's mostly (entirely?), so we
// need to use a slightly more complicated function to perform
// this operation.
//
for ( i = 0 ; i < subsetList.entries() ; i++ )
{
UInt32 initialGrpCnt = workGroup.entries();
ValueId referencingExpr ;
while (( initialGrpCnt > 0) && (workGroup.entries() > 0) )
{
if (workGroup.referencesTheGivenValue( subsetList[i], referencingExpr) )
workGroup.remove (referencingExpr) ;
initialGrpCnt --;
}
}
} // while ( workGroup.entries() > 0 )
// ------------------------------------------------------------------
// now that we've handled all multi-column uec information, now
// see what information we have for single column stats
// ------------------------------------------------------------------
singleStats.clear();
for ( i = 0; i < groupStatDescList.entries(); i++ )
{
const ItemExpr * colExpr =
groupStatDescList[i]->getVEGColumn().getItemExpr() ;
ValueId tmp ;
if ( workGroup.contains( colExpr->getValueId() ) )
singleStats.insert(i);
//
// $$$ The following 2nd check is part of a fix for a problem which came
// $$$ up in TPC-D -- we're grouping by an EXTRACT value, and in this
// $$$ case the UEC should at the very most stay the same (if not go down --
// $$$ that's a change we can leave for another day). To wit, we need
// $$$ this column's uec value!
// $$$
// $$$ The current solution may be too general -- maybe we should limit
// $$$ this to entries in workGroup which are for EXTRACT ops ...?
//
else if ( workGroup.referencesTheGivenValue ( colExpr->getValueId(), tmp ) )
singleStats.insert(i) ;
} // for i
// -----------------------------------------------------------------
// Next, walk the list of remaining single-column statistics.
// -----------------------------------------------------------------
CollIndex loopCount = 0 ;
for (i = 0; (i < singleStats.entries()) && (uecTotal < oldCount); i++ )
{
ColStatsSharedPtr groupStats =
groupStatDescList[ singleStats[i] ]->getColStats();
const ValueId columnVEG =
groupStatDescList[ singleStats[i] ]->getVEGColumn();
const ItemExpr * colExpr = columnVEG.getItemExpr();
const ValueId columnId = colExpr->getValueId() ;
// $$$ this is the second part to take care of the
// $$$ "EXTRACT-groupby-TPCD" problem
//
// 1. first, we see whether this column is referenced by the expression
// 2. if so, then we find the item in workGroup that ref's this column
// 3. remove that item from workGroup
// 4. There could be more than one expressions referencing that column
// remove all of them
UInt32 initialGrpCnt = workGroup.entries();
while(( initialGrpCnt > 0) && (workGroup.entries() > 0) )
{
ValueId referencingExpr ;
if (workGroup.referencesTheGivenValue // not always true, if expr refs 2 cols
( columnId, referencingExpr) )
workGroup.remove (referencingExpr) ;
initialGrpCnt--;
}
// else ... it's probably correct to multiply by this column's uec
// ... I think ...
uecTotal *= groupStats->getTotalUec();
loopCount++;
lastStats = singleStats[i];
}
uecTotal *= uecTotalFromCompExp;
// -----------------------------------------------------------------
// Finally, determine the reported result Cardinality based upon
// the above uecTotal, combined with knowing whether or not we've
// found statistics for all the grouping columns.
// -----------------------------------------------------------------
if ( workGroup.entries() == 0 )
{
// In the special case where one single ColStats totally covers
// the grouping list, and only in that case, the rowcounts of
// that column's histogram should be set to match its UECs.
if ( loopCount == 1 )
{
ColStatsSharedPtr groupStats =
groupStatDescList[ lastStats ]->getColStatsToModify();
groupStats->makeGrouped(); // apply impact of grouping
}
// For groupbys below nested join we do some extra adjustments
CostScalar inputCard = copyInputEstProp->getResultCardinality();
if (inputCard > csOne)
{
double rInput = inputCard.getValue();
double uChild = uecTotal.getValue();
double rChild = oldCount.getValue();
double finalCard;
finalCard = rInput * uChild * ( 1 - ((rInput - 1) / rInput) * exp( - rChild / (rInput*uChild)) );
resultCardinality = MIN_ONE(finalCard < rChild ? finalCard : rChild );
}
else
resultCardinality = MIN_ONE_CS(uecTotal < oldCount ? uecTotal : oldCount);
}
else
{
// for now, do worst-case based model
resultCardinality = oldCount;
}
} // a "real" groupby
if (resultCardinality > maxCard)
resultCardinality = maxCard;
// Ensure that all histograms report the same rowcount.
CollIndex loopLimit = groupStatDescList.entries();
for (i = 0; i < loopLimit; i++)
{
oldCount = groupStatDescList[i]->getColStats()->getRowcount();
if (oldCount != resultCardinality)
{
// IF the identified column is part of the GROUP BY, then its
// UEC shouldn't change due to Grouping. Otherwise, its uec
// should be reduced (as elsewhere) to the same degree that its
// rowcount has been reduced.
ValueId columnVEG = groupStatDescList[i]->getVEGColumn() ;
const ItemExpr * colExpr = columnVEG.getItemExpr();
if ( workGroupCopy.contains(colExpr->getValueId()) )
{
const NABoolean doNotReduceUEC = TRUE ;
groupStatDescList[i]->synchronizeStats (oldCount,
resultCardinality,
ColStatDesc::DO_NOT_REDUCE_UEC) ;
}
else
{
groupStatDescList[i]->synchronizeStats (oldCount,
resultCardinality) ;
}
}
}
//---------------------------------------------------------------------
// Save an intermediate EstLogProp for use by Physical Costing.
// As usual, this is a Deep copy to prevent inadvertent modifications
// of the data, by subsequent changes to the Histograms.
//---------------------------------------------------------------------
ColStatDescList & intermedStatsList = intermedEstProps->colStats();
intermedStatsList.makeDeepCopy( groupStatDescList );
if (isAPartialGroupByLeaf())
{
// DP2 will group as many rows as it can; the rows it cannot group it
// will simply pass through to its parent to complete the group-by ==>
// so that's why this code below involves physical costing stuff ...
// No of groups the memory accommodates.
// Length of the group with aggregates and the hash table overhead.
const ValueIdSet& grbyVis = groupExpr();
const ValueIdSet& aggrVis = aggregateExpr();
// Length in bytes of the group key and the aggregates.
CostScalar groupKeyLength = (grbyVis.isEmpty() ? 0 : grbyVis.getRowLength());
CostScalar aggregateLength = (aggrVis.isEmpty() ? 0 : aggrVis.getRowLength());
CostScalar hashedRowOverhead = (CostPrimitives::getBasicCostFactor(HH_OP_HASHED_ROW_OVERHEAD));
CostScalar extGroupLength =
groupKeyLength + aggregateLength + hashedRowOverhead;
CostScalar memoryLimitInDP2 = (CostPrimitives::getBasicCostFactor(HGB_DP2_MEMORY_LIMIT));
CostScalar groupCountInMemory =
memoryLimitInDP2 * 1024. / extGroupLength;
groupCountInMemory = MINOF(groupCountInMemory, resultCardinality);
CostScalar mem = groupCountInMemory * extGroupLength / 1024.;
CostScalar ipRows = ( groupExpr_.isEmpty() ? inputEstLogProp->getResultCardinality() :
childEstProps->getResultCardinality());
CostScalar rowsEliminated = groupCountInMemory / resultCardinality * ipRows;
resultCardinality = MAXOF (groupCountInMemory,
ipRows - rowsEliminated + groupCountInMemory);
// WaveFix Begin
// This is part of the fix for the count(*) wave
// if there is a scalar agregate query on a multi-partitioned table,
// something like Select count(*) from fact;
// In such a case we would like to get a layer of esps,
// doing so causes the plan to fixup in parallel avoiding the serial
// fixup if the plan is just the master executor on top of dp2. The
// serial fixup causes the query to execute in wave pattern, since
// each dp2 is fixed up and then starts execution. Due to serial
// fixup a dp2 is fixed up, and then we move to the next dp2 causing
// the wave pattern.
if (QueryAnalysis::Instance() &&
QueryAnalysis::Instance()->dontSurfTheWave() &&
isAPartialGroupByLeaf())
{
resultCardinality = childEstProps->getResultCardinality()/2;
}
// WaveFix End
}
intermedEstProps->unresolvedPreds() += myEstProps->getUnresolvedPreds();
intermedEstProps->setResultCardinality( resultCardinality.minCsOne() );
// maxCard(GrpBy(X)) ==
// MIN(card(GrpBy(x))*(maxCard(X)/card(X)), max UEC of grouping columns)
CostScalar maxRows = resultCardinality.minCsOne() *
(childEstProps->getMaxCardEst() /
childEstProps->getResultCardinality().minCsOne());
if (maxUECofGrpBy > 0 // it is credible
&& maxUECofGrpBy < maxRows) // max UEC of grouping columns < maxCard
{
// maxCard cannot exceed max UEC of grouping columns
maxRows = maxUECofGrpBy;
}
CostScalar maxSelectivity = csOne;
//------------------------------------------------------------------------
// Next, apply any HAVING Clause
//------------------------------------------------------------------------
const ValueIdSet & inputValues = getGroupAttr()->getCharacteristicInputs();
ValueIdSet outerReferences;
// Get the set of outer references. If input value is a VEGReference,then
// include this VEGReference only if the group consists of no constants.
if (inputValues.entries() > 0)
inputValues.getOuterReferences (outerReferences);
ColStatDescList groupStatDescListForMaxSel(CmpCommon::statementHeap());
groupStatDescListForMaxSel.makeDeepCopy(groupStatDescList);
// compute maxSelectivity. toss rowcount return value.
(void) groupStatDescListForMaxSel.estimateCardinality
(resultCardinality, getSelectionPred(), outerReferences, NULL, NULL, NULL,
outerRefCount, myEstProps->unresolvedPreds(), INNER_JOIN_MERGE,
ITM_FIRST_ITEM_OP, &maxSelectivity);
CostScalar newRowCount =
groupStatDescList.estimateCardinality (resultCardinality, /*in*/
getSelectionPred(), /*in*/
outerReferences, /*in*/
NULL,
NULL, /*for selectivityHint, which can only be given for Scan*/
NULL, /* for Cardinality hint, which can only be given for Scan */
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(), /*in/out*/
INNER_JOIN_MERGE, /*in*/
ITM_FIRST_ITEM_OP, /*no-op*/
NULL);
groupStatDescList.computeMaxFreq(TRUE);
myEstProps->setResultCardinality( newRowCount );
myEstProps->setMaxCardEst(MAXOF(maxRows * maxSelectivity, newRowCount));
groupStatDescList.synchronizeStats(newRowCount, groupStatDescList.entries());
// -------------------------------------------------------------------
// Now, determine which of these histograms are useful based on the
// characteristic outputs for the group.
// -------------------------------------------------------------------
ValueIdSet predSet;
myEstProps->pickOutputs( groupStatDescList, inputEstLogProp, getGroupAttr()->getCharacteristicOutputs(), predSet);
//---------------------------------------------------------------------------
// Save two sets of output estimated logical properties.
// 1) Intermediate OLP = rowcount estimates after applying grouping
// 2) Final OLP = rowcount estimates after applying grouping and having preds
//----------------------------------------------------------------------------
getGroupAttr()->addInputOutputLogProp (inputEstLogProp,
myEstProps /* final OLP - after applying grouping and HAVING preds */,
intermedEstProps /* intermediate OLP - after grouping */);
} // GroupByAgg::synthEstLogProp
void GroupByAgg::handleIndirectDepInGroupingcols(ValueIdSet& workGroup,
ValueIdSet& interestingColSet,
ValueIdSet& updatedColumnsToRemove,
ValueIdSet& probableRedundantColSet,
const ColStatDescList & groupStatDescList)
{
const ValueIdList newWorkGroupList = workGroup ;
// The following list stores the list of VEG'es that share circular
// dependencies among the columns in grouping expression
CollIndexList cirDepCandidates(CmpCommon::statementHeap());
SET(TableDesc *) * interestingColTables = interestingColSet.getAllTables();
for ( CollIndex i = 0 ; i < newWorkGroupList.entries() ; i++ )
{
// sanity check to ensure that there is atleast one item in the workGroup.
if (workGroup.entries() == 1) break;
ItemExpr *colExpr = newWorkGroupList[i].getItemExpr() ;
ValueIdSet colsFromThisVeg;
colExpr->findAll( ITM_BASECOLUMN, colsFromThisVeg, TRUE, TRUE );
ValueIdSet interestingColsFromThisVeg = colsFromThisVeg.intersect(interestingColSet);
// now see if any primary keys columns have dependencies with non-key columns
// and should be removed. This would be necessary only if there are more than one
// primary keys and also there are some redundant columns
if (!interestingColsFromThisVeg.isEmpty() )
{
ValueIdSet nonInterestingColSetFromVeg = colsFromThisVeg;
nonInterestingColSetFromVeg.subtractSet(interestingColSet);
nonInterestingColSetFromVeg.intersectSet(probableRedundantColSet);
if( !(nonInterestingColSetFromVeg.isEmpty()) )
{
NABoolean itemCanBeRemoved = FALSE;
// The following logic tests for indirect dependency.
// Only the VEG from the grouping expression that satisfies
// the following conditions will be removed;
//
// 1. Non-primary key columns in the VEG should have the primary key column of the
// respective table in the grouping expression;
// 2. Primary key column in the VEG should be the only column from that particular
// table in the grouping expression;
SET(TableDesc *) * nonInterestingColTablesFromVeg = nonInterestingColSetFromVeg.getAllTables();
// For the first condition
for (CollIndex j = 0; j < nonInterestingColTablesFromVeg->entries(); j++ )
{
if(interestingColTables->contains(nonInterestingColTablesFromVeg->at(j)))
{
itemCanBeRemoved = TRUE;
break;
}
}
if(itemCanBeRemoved)
{
for (ValueId col = interestingColsFromThisVeg.init();
interestingColsFromThisVeg.next(col);
interestingColsFromThisVeg.advance(col))
{
for ( CollIndex l = 0; l < groupStatDescList.entries(); l++ )
{
if(newWorkGroupList[i] == groupStatDescList[l]->getVEGColumn())
{
ValueIdSet colSetOfThisTbl = col.castToBaseColumn()->getTableDesc()->getColumnList();
// For the second condition
if(!colSetOfThisTbl.intersectSet(updatedColumnsToRemove).isEmpty())
{
itemCanBeRemoved = FALSE;
// Found a column that has a circular dependency, add it to the list
cirDepCandidates.insert(i);
}
break;
}
if(l == groupStatDescList.entries()-1)
itemCanBeRemoved = FALSE;
}
if(!itemCanBeRemoved)
break;
}
}
if(itemCanBeRemoved)
workGroup.remove(newWorkGroupList[i]);
}
}
}
// Retain only the column with minimum UEC from the list of columns that have circular dependencies
CollIndex count = cirDepCandidates.entries();
if(count > 1)
{
CostScalar minUec = csZero, uec = csOne;
for ( CollIndex k = 0; k < count; k++ )
{
// At least one of the columns from the circular dependency list should be retained.
if( (k == count-1) && (minUec == csZero) )
break;
for ( CollIndex l = 0; l < groupStatDescList.entries(); l++ )
{
if(newWorkGroupList[cirDepCandidates[k]] == groupStatDescList[l]->getVEGColumn())
{
uec = groupStatDescList[l]->getColStats()->getTotalUec();
// Remove all the columns except the one with lowest UEC
if (uec > minUec)
workGroup.remove(newWorkGroupList[cirDepCandidates[k]]);
else
minUec = uec;
break;
}
// Remove the VEG if the UEC couldnt be calculated
if(l == groupStatDescList.entries()-1)
workGroup.remove(newWorkGroupList[cirDepCandidates[k]]);
}
}
}
}
void
GroupByAgg::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and all my children.
RelExpr::synthLogProp(normWAPtr);
if (NOT isAPartialGroupByLeaf())
{
// is this a simple aggregate (no groupby columns) or a "real" groupby
if (groupExpr_.isEmpty())
{
// if the grouping expression is empty (simple aggregate), add
// a cardinality constraint for at most one row or exactly one
// row if there is no HAVING clause that could remove the single
// output row
if (selectionPred().isEmpty())
getGroupAttr()->addConstraint
(new(CmpCommon::statementHeap()) CardConstraint((Cardinality)1,(Cardinality)1));
else
getGroupAttr()->addConstraint
(new(CmpCommon::statementHeap()) CardConstraint((Cardinality)0,(Cardinality)1));
}
else
{
// create a uniqueness constraint for the grouping columns
ValueIdSet uniqueCols = groupExpr_;
// remove from the grouping columns any expressions covered
// by the inputs
uniqueCols.removeCoveredExprs(getGroupAttr()->getCharacteristicInputs());
if (uniqueCols.isEmpty())
{
// If nothing left then at most a single row will be returned
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)0,(Cardinality)1));
}
else
{
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
UniqueOptConstraint(uniqueCols));
// if grpby child has cardinality constraint, create one for grpby
Cardinality minrows, maxrows;
if (child(0).getGroupAttr()->hasCardConstraint(minrows, maxrows)) {
getGroupAttr()->addConstraint
(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)1, // grpby cannot go below 1 grp
maxrows));
}
}
getGroupAttr()->addSuitableRefOptConstraints
(child(0).getGroupAttr()->getConstraints());
processCompRefOptConstraints(normWAPtr);
}
}
} // GroupByAgg::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class RelRoot
// -----------------------------------------------------------------------
void
RelRoot::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
// Synthesize estimated logical properties from my child's.
CostScalar childMaxCardEst =
child(0).outputLogProp(inputEstLogProp)->getMaxCardEst();
EstLogPropSharedPtr myEstLogProp =
synthEstLogPropForUnaryLeafOp(inputEstLogProp,
child(0).outputLogProp(inputEstLogProp)->getColStats(),
child(0).outputLogProp(inputEstLogProp)->getResultCardinality(),
&childMaxCardEst);
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp);
} // RelRoot::synthEstLogProp
void
RelRoot::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and my children.
RelExpr::synthLogProp(normWAPtr);
// just copy the constraints from my child.
getGroupAttr()->addConstraints(child(0).getGroupAttr()->getConstraints());
} // RelRoot::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class Filter
// -----------------------------------------------------------------------
void
Filter::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
// We will now get the Estimated Logical Properties of the child
// also multipleCalls will tell us whether or not such
// child gets called only once.
Int32 multipleCalls;
EstLogPropSharedPtr modInputLP;
const CostScalar orgRowcount = inputEstLogProp->getResultCardinality();
if (orgRowcount > 1)
{
modInputLP =
child(0).getGroupAttr()->materializeInputLogProp(inputEstLogProp, &multipleCalls);
const CostScalar newRowcount = modInputLP->getResultCardinality();
if (orgRowcount == newRowcount)
modInputLP = inputEstLogProp;
}
else
modInputLP = inputEstLogProp;
if ( getGroupAttr()->isPropSynthesized(modInputLP) &&
getGroupAttr()->isPropSynthesized(inputEstLogProp) )
{
return ; // already done this
}
// Synthesize the intermediate logical properties
EstLogPropSharedPtr myEstLogProp =
synthEstLogPropForUnaryLeafOp (inputEstLogProp,
child(0).outputLogProp(inputEstLogProp)->getColStats(),
child(0).outputLogProp(inputEstLogProp)->getResultCardinality());
// Synthesize estimated logical properties from my child's, taking
EstLogPropSharedPtr intermedEstLogProp =
synthEstLogPropForUnaryLeafOp (modInputLP,
child(0).outputLogProp(modInputLP)->getColStats(),
child(0).outputLogProp(modInputLP)->getResultCardinality());
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp, intermedEstLogProp);
} // Filter::synthEstLogProp
void
Filter::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and my children
RelExpr::synthLogProp(normWAPtr);
GroupAttributes &myGA = *getGroupAttr();
ValueIdSet nonRIConstraints;
for (ValueId x= child(0).getGroupAttr()->getConstraints().init();
child(0).getGroupAttr()->getConstraints().next(x);
child(0).getGroupAttr()->getConstraints().advance(x) )
{
if ((x.getItemExpr()->getOperatorType() != ITM_COMP_REF_OPT_CONSTRAINT) &&
(x.getItemExpr()->getOperatorType() != ITM_REF_OPT_CONSTRAINT))
nonRIConstraints += x;
}
myGA.addConstraints(nonRIConstraints);
myGA.addSuitableRefOptConstraints
(child(0).getGroupAttr()->getConstraints());
processCompRefOptConstraints(normWAPtr);
// if a cardinality constraint was copied and if it constrains the
// minimum number of rows, remove that, since the filter predicate
// may filter out some rows
Cardinality minRows, maxRows;
if (myGA.hasCardConstraint(minRows,maxRows) AND minRows > 0)
{
const ValueIdSet &myConstraints = myGA.getConstraints();
for (ValueId x = myConstraints.init();
myConstraints.next(x);
myConstraints.advance(x))
{
ItemExpr *xx = x.getItemExpr();
OperatorTypeEnum loptype = xx->getOperatorType();
if (loptype == ITM_CARD_CONSTRAINT)
{
CardConstraint *c = (CardConstraint *) xx;
if (c->getLowerBound() > 0)
{
// remove this constraint from the group attrs and
// add a new one (if needed) with the upper bound only
myGA.deleteConstraint(x);
if (c->getUpperBound() < INFINITE_CARDINALITY)
myGA.addConstraint(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)0,
c->getUpperBound()));
}
}
}
}
} // Filter::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class MapValueIds
// -----------------------------------------------------------------------
void
MapValueIds::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
// Synthesize estimated logical properties from my child's
const ColStatDescList *colStats =
&(child(0).outputLogProp(inputEstLogProp)->getColStats());
CostScalar baseCardinality =
child(0).outputLogProp(inputEstLogProp)->getResultCardinality();
if (cseRef_) // could also do this unconditionally
{
ColStatDescList *mappedStats = new(CmpCommon::statementHeap())
ColStatDescList(CmpCommon::statementHeap());
mappedStats->makeMappedDeepCopy(*colStats,
map_,
TRUE);
colStats = mappedStats;
}
EstLogPropSharedPtr myEstLogProp =
synthEstLogPropForUnaryLeafOp (inputEstLogProp,
*colStats,
baseCardinality);
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp);
} // MapValueIds::synthEstLogProp
void
MapValueIds::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and my children.
RelExpr::synthLogProp(normWAPtr);
GroupAttributes &myGA = *getGroupAttr();
ValueIdSet nonRIConstraints;
for (ValueId x= child(0).getGroupAttr()->getConstraints().init();
child(0).getGroupAttr()->getConstraints().next(x);
child(0).getGroupAttr()->getConstraints().advance(x) )
{
if (x.getItemExpr()->getOperatorType() == ITM_UNIQUE_OPT_CONSTRAINT)
{
ValueIdSet uniqueCols ;
getMap().rewriteValueIdSetUp
(uniqueCols, ((UniqueOptConstraint *)x.getItemExpr())->uniqueCols());
myGA.addConstraint(new(CmpCommon::statementHeap())
UniqueOptConstraint(uniqueCols));
}
else if (x.getItemExpr()->getOperatorType() == ITM_CARD_CONSTRAINT)
myGA.addConstraint(x);
// func. dependency and check opt constraints can be added here if needed
}
} // MapValueIds::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class Scan
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// This constant implements heuristics on how to limit the number of plans
// generated by the file scan rule. Currently we won't consider plans for
// a logical scan node that involve more than 2 joins (3-way joins) of
// indexes.
// After walking through the code we noticed that currently 3-way joins
// never gets into the plan because there is no predicate passed to it.
// As a result the second index gets only primary key columns and has to
// be scanned completely. In this case going to the base table after the
// first index(2 way join) will always be cheaper than the same job plus
// full scan of the second index (3 way join). So we decided to save time
// and eliminate 3 way join by setting MAX_NUM_INDEX_JOIN = 1 instead of
// 2. So, a logical scan node will have no more than 1 index join.
// -----------------------------------------------------------------------
const Lng32 Scan::MAX_NUM_INDEX_JOINS = 1;
void
Scan::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp))
return;
// synthesize my estimated logical properties from the initial
// table statistics or the stats of a common subexpression
const ColStatDescList *colStats =
&getTableDesc()->getTableColStats();
CostScalar baseCardinality = getBaseCardinality();
if (commonSubExpr_)
{
ValueIdList tempTableCols;
ValueIdList tempTableVEGCols;
ValueIdList cseCols;
CSEInfo *info =
CmpCommon::statement()->getCSEInfo(commonSubExpr_->getName());
// The original tree is the child of the CommonSubExprRef that
// did the analysis of whether to use CSEs
CommonSubExprRef *analyzingCSERef =
info->getConsumer(info->getIdOfAnalyzingConsumer());
// this makes the ValueIdList of only those CSE columns that are
// actually used in the temp table
commonSubExpr_->makeValueIdListFromBitVector(
cseCols,
analyzingCSERef->getColumnList(),
info->getNeededColumns());
getTableDesc()->getUserColumnList(tempTableCols);
getTableDesc()->getEquivVEGCols(tempTableCols, tempTableVEGCols);
// make a ValueIdMap from the original tree for the common
// subexpression (bottom) to the columns of our temp table (top)
ValueIdMap cseToTempScanMap(tempTableVEGCols,
cseCols);
// get to the ColStatDesc of the common subexpression
const ColStatDescList &origCSEColStats(
analyzingCSERef->getEstLogProps()->colStats());
ColStatDescList *mappedStats = new(CmpCommon::statementHeap())
ColStatDescList(CmpCommon::statementHeap());
// now translate the col stats of the original CSE to the temp table
mappedStats->makeMappedDeepCopy(origCSEColStats,
cseToTempScanMap,
FALSE);
colStats = mappedStats;
baseCardinality = analyzingCSERef->getEstLogProps()->getResultCardinality();
}
EstLogPropSharedPtr myEstLogProp =
synthEstLogPropForUnaryLeafOp(inputEstLogProp,
*colStats,
baseCardinality);
QueryAnalysis *qa = QueryAnalysis::Instance();
if ( (CmpCommon::getDefault(COMP_BOOL_42) == DF_ON) &&
(qa && qa->isCompressedHistsViable()) )
{
// compress histograms to a single interval histogram
ColStatDescList &myColStatsList = myEstLogProp->colStats();
myColStatsList.compressColStatsToSingleInt();
if(!myColStatsList.isEmpty())
myEstLogProp->setResultCardinality(myColStatsList[0]->getColStats()->getRowcount());
}
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp);
} // Scan::synthEstLogProp
// Check the rowcount obtained from histogram manipulation with the
// rowcount obtained after executing the query with same set of predicates
// on a sample table. Using a confidence of 95% we have already computed
// a range within which the cardinality should lie within
NABoolean
Scan::checkForCardRange(const ValueIdSet & setOfPredicates,
CostScalar & newRowCount /* in and out */)
{
if (!CURRSTMT_OPTDEFAULTS->histUseSampleForCardEst())
return FALSE;
NABoolean cardChange = FALSE;
TableDesc * td = getTableDesc();
if (td == NULL)
return cardChange;
if (td->getTableAnalysis() == NULL)
return cardChange;
// Since the selection predicates may contain equality predicates with
// columns from other table, get the actual local predicates from
// table analysis, and see if all those were applied or not. Replace any
// RangeSpecRefs with their source predicates so they are the ones
// used in the intersect operation.
const ValueIdSet& localPreds = td->getTableAnalysis()->getLocalPreds();
ValueIdSet derangedLocalPreds = localPreds.replaceRangeSpecRefs();
ValueIdSet derangedSetOfPreds = setOfPredicates.replaceRangeSpecRefs();
derangedLocalPreds = derangedSetOfPreds.intersect(derangedLocalPreds);
// Now we have only the local predicates for this Scan node
if (derangedLocalPreds.entries() == 0)
return cardChange;
// To take into account indexes and TSJs, see if the predicates
// for which the estimates were done is a super set or a subset
// of the set of predicates for which the CTS was executed
ValueIdSet predsExecuted = td->predsExecuted();
ValueIdSet commonPreds = derangedLocalPreds.intersect(predsExecuted);
// nothing in common, return taking cardinality estimates from histograms as correct
if (commonPreds.entries() == 0)
return cardChange;
// if local preds are identical to the preds that were executed
// cardinalities should be within the range frm CTS
if (derangedLocalPreds == predsExecuted)
{
// since the set of predicates are identical, we want to ensure
// the rowcount computed from the histogram lies within the error
// range.
newRowCount = MAXOF(newRowCount, getTableDesc()->getMinRC());
newRowCount = MINOF(newRowCount, getTableDesc()->getMaxRC());
cardChange = TRUE;
}
else
if ((commonPreds.entries() < predsExecuted.entries()) &&
(commonPreds.entries() == derangedLocalPreds.entries()))
{
// It could be a case of an index which does not cover all predicates
// Basically the local predicates is a subset of predicates for which
// FetchCount was executed.
// Example, index is on columns (a, b) while the predicates for which
// FetchCount was used was (a, b, c). In this case the cardinality estimates
// from histogram cannot be more than what we got from FetchCount
newRowCount = MAXOF(newRowCount, getTableDesc()->getMinRC());
cardChange = TRUE;
}
else
if ((commonPreds.entries() == predsExecuted.entries()) &&
(commonPreds.entries() < derangedLocalPreds.entries()))
{
// case where local predicates is a super set of predicates for which
// FetchCount was executed. In this case the estimates from histogram cannot
// be less than the estimates that were computed from the FetchCount
newRowCount = MINOF(newRowCount, getTableDesc()->getMaxRC());
cardChange = TRUE;
}
return cardChange;
}
void
Scan::synthLogProp(NormWA * normWAPtr)
{
// check to see this GA has already been associated with
// a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
ValueIdList tempClusteringKey;
ValueIdSet clusteringKey;
// get the VEGReferences of the clustering key into a ValueIdSet
tabId_->getEquivVEGCols(tabId_->getClusteringIndex()->getIndexKey(),
tempClusteringKey);
clusteringKey = tempClusteringKey;
// Synthesize log. properties for me and my children.
RelExpr::synthLogProp(normWAPtr);
// this node reads one base table
getGroupAttr()->setNumBaseTables(1);
// create uniqueness constraints (for now, only the primary key)
const LIST(IndexDesc *) & ixlist = tabId_->getIndexes();
NABoolean addCardConstraint = TRUE;
// check for empty scans
for (ValueId p=getSelectionPred().init();
getSelectionPred().next(p);
getSelectionPred().advance(p))
{
NABoolean negateIt = FALSE;
ConstValue *cv = p.getItemExpr()->castToConstValue(negateIt);
if (cv && !negateIt && cv->isAFalseConstant())
{
// this scan doesn't produce any outputs at all
getGroupAttr()->addConstraint(
new(CmpCommon::statementHeap())
CardConstraint((Cardinality)0,(Cardinality)0));
addCardConstraint = FALSE;
}
}
for (CollIndex indexNo = 0; indexNo < ixlist.entries(); indexNo++)
{
IndexDesc *idesc = ixlist[indexNo];
// Determining uniqueness constraints from indexes is not quite
// straightforward, since no information is available whether
// this index was created as a "unique" index. The method used
// here assumes that the index key is always unique (currently
// and probably for the future a DP2 requirement). Since we don't
// want to add a lot of unnecessary uniqueness constraints, eliminate
// all those who contain the clustering key (except for the clustering
// index itself, of course).
const ValueIdList &indexKey(idesc->getIndexKey());
ValueIdList tempUniqueCols;
ValueIdSet uniqueCols;
// get the VEGReferences of the key columns into a ValueIdSet
tabId_->getEquivVEGCols(indexKey,
tempUniqueCols);
uniqueCols = tempUniqueCols;
if (idesc->isClusteringIndex() OR
(NOT uniqueCols.contains(clusteringKey)))
{
// Remove from the unique key any values that are covered
// by the inputs. If the key ends up empty then add a
// cardinality constraint.
uniqueCols.removeCoveredExprs(getGroupAttr()->getCharacteristicInputs());
// Remove any columns that are computed from the remaining
// unique columns
for (ValueId cc=uniqueCols.init();
uniqueCols.next(cc);
uniqueCols.advance(cc))
{
BaseColumn *bc = cc.castToBaseColumn();
ValueId computedExpr(bc->getComputedColumnExpr());
if (computedExpr != NULL_VALUE_ID)
{
// Check whether the underlying base columns
// are part of the unique key. If so, then
// this computed column can be computed from
// the other columns and therefore is not
// part of the minimal unique key
ValueIdSet underlyingCols;
ValueIdSet underlyingVEGCols;
bc->getUnderlyingColumnsForCC(underlyingCols);
tabId_->getEquivVEGCols(underlyingCols,
underlyingVEGCols);
if (uniqueCols.contains(underlyingVEGCols))
uniqueCols -= cc;
}
}
if (uniqueCols.isEmpty())
{
if (addCardConstraint)
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)0,(Cardinality)1));
addCardConstraint = FALSE; // do it once
}
else
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
UniqueOptConstraint(uniqueCols));
}
}
// we don't need to add RefOptConstraints during analyze and optimizer phases
// the normWAPtr check serves that purpose.
if (normWAPtr &&
(CmpCommon::getDefault(ELIMINATE_REDUNDANT_JOINS) != DF_OFF))
{
RefConstraint * riConstraint = NULL ;
ValueIdList keyColVEGVids ;
ValueIdSet keyColVEGVidsSet ;
const ValueIdSet & myOutputs = getGroupAttr()->getCharacteristicOutputs();
const AbstractRIConstraintList RIConstraintsList =
getTableDesc()->getNATable()->getRefConstraints() ;
for (CollIndex i = 0; i < RIConstraintsList.entries(); i++)
{
riConstraint = (RefConstraint *) RIConstraintsList.at(i);
if ((riConstraint->getIsEnforced()) ||
(CmpCommon::getDefault(ELIMINATE_REDUNDANT_JOINS) != DF_MINIMUM))
{
keyColVEGVids.clear();
keyColVEGVidsSet.clear();
const Constraint::KeyColumns& keyCols = riConstraint->keyColumns();
for (CollIndex j = 0; j < keyCols.entries(); j++)
{
keyColVEGVids.insert(tabId_->getColumnVEGList()[keyCols.at(j)->getPosition()]);
}
keyColVEGVidsSet = keyColVEGVids ;
// If my outputs include the foreign key columns then there may be a
// matching RI join further up the query tree. This is a necessary but
// not a sufficient condition.
if (myOutputs.contains(keyColVEGVidsSet))
{
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
RefOptConstraint(keyColVEGVids,
riConstraint->getUniqueConstraintName()));
// this flag is used to quickly determine if we have RefOpt constraints later.
getGroupAttr()->setHasRefOptConstraint(TRUE);
}
}
}
// synthesize CompRefOpt constraints now.
processCompRefOptConstraints(normWAPtr);
}
// For debugging purposes only:
// Provides a way of faking the estimated rowcount for a table
// by using a funny correlation name "ROWSnnnn..." where nnnn... stands for
// the number of rows.
#ifndef NDEBUG
NAString cname(getTableDesc()->getCorrNameObj().getCorrNameAsString(),
CmpCommon::statementHeap());
if ((cname.length() > 3) && (cname(0,4) == NAString("ROWS")))
setBaseCardinality((Cardinality)NAStringToReal(cname(4,cname.length()-4)));
else
#endif
setBaseCardinality (MIN_ONE (getTableDesc()->getNATable()->getEstRowCount())) ;
if ((CURRSTMT_OPTDEFAULTS->histDefaultSampleSize() > 0) &&
this->getRETDesc() &&
this->getRETDesc()->getBindWA() &&
this->getRETDesc()->getBindWA()->inDDL())
{
CURRSTMT_OPTDEFAULTS->setHistDefaultSampleSize(0);
}
// For each set of column statistics, convert the base column valueids
// to the equivalent VEG cols if not done already.
ColStatDescList & initialStats = getTableDesc()->tableColStats();
for (CollIndex i = 0; i < initialStats.entries(); i++)
initialStats[i]->VEGColumn() =
getTableDesc()->getEquivVEGCol(initialStats[i]->getColumn());
// set if this scan node is trafodion SMD table
if ( getTableDesc()->getNATable()->isSeabaseMDTable() )
getGroupAttr()->setSeabaseMDTable(TRUE);
} // Scan::synthLogProp()
void Scan::processCompRefOptConstraints(NormWA * normWAPtr)
{
if (normWAPtr &&
(CmpCommon::getDefault(ELIMINATE_REDUNDANT_JOINS) != DF_OFF) &&
getGroupAttr()->getCharacteristicInputs().isEmpty())
{
UniqueConstraint * uniqConstraint = NULL;
ValueIdList keyColVEGVids ;
ValueIdSet keyColVEGVidsSet;
const ValueIdSet & myOutputs = getGroupAttr()->getCharacteristicOutputs();
const ValueIdSet& myEssOutputs =
getGroupAttr()->getEssentialCharacteristicOutputs();
const AbstractRIConstraintList UniqConstraintsList =
getTableDesc()->getNATable()->getUniqueConstraints() ;
for (CollIndex i = 0; i < UniqConstraintsList.entries(); i++)
{
keyColVEGVids.clear();
keyColVEGVidsSet.clear();
uniqConstraint = (UniqueConstraint *)UniqConstraintsList.at(i);
const Constraint::KeyColumns& keyCols = uniqConstraint->keyColumns();
for (CollIndex j = 0; j < keyCols.entries(); j++)
{
keyColVEGVids.insert
(getTableDesc()->getColumnVEGList()[keyCols.at(j)->getPosition()]);
}
keyColVEGVidsSet = keyColVEGVids;
// The three necessary conditions that determine if this UniqueConstraint
// can provide a match for a RefOpt constraint in some join up above is
// (a) my outputs include the unique key columns
// (b) my essential outputs do not include anything other than the unique key cols
// (c) there is at least one Referential constraint defined on me
// (d) this node does not contain a reducing predicate of the form
// table.col = 1 or table.col1 = table.col2
if ((myOutputs.contains(keyColVEGVidsSet)) &&
(keyColVEGVidsSet.contains(myEssOutputs))&&
(uniqConstraint->hasRefConstraintsReferencingMe())&&
(NOT containsReducingPredicates(keyColVEGVidsSet)))
{
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
ComplementaryRefOptConstraint(keyColVEGVids,
uniqConstraint->getConstraintName(), this, getTableDesc()));
getGroupAttr()->setHasCompRefOptConstraint(TRUE);
}
}
}
}
NABoolean Scan::containsReducingPredicates(const ValueIdSet& keyCols) const
{
const ValueIdSet& selPreds = getSelectionPredicates();
for (ValueId x = selPreds.init();
selPreds.next(x);
selPreds.advance(x))
{
ValueId childId = NULL_VALUE_ID;
ItemExpr *ie = x.getItemExpr();
// If the ItemExpr is a range spec operator, sub in the actual predicate,
// which is its right subtree.
if (ie->getOperatorType() == ITM_RANGE_SPEC_FUNC)
ie = ie->child(1);
if (ie->getOperatorType() == ITM_VEG_PREDICATE)
{
VEG* pred = ((VEGPredicate*)ie)->getVEG();
// catches cases like T1.a = 1, T1.a = ? or T1.a = hostvar or (T1.a = T2.a and T2.a = 1)
// Here "a" is not a primary key column of T1. If it is then we don't consider
// T1.a = 1 a reducing predicate, because the if there is a FK-PK relation, the
// T1.a =1 relation will be applied on the FK table too.
if ((pred->getCountOfUserSuppliedInputs() > 0) &&
(NOT(pred->getAllValues().referencesOneValueFromTheSet(keyCols))))
return TRUE;
}
else if (ie->isARangePredicate())
{
if (ie->child(0)->doesExprEvaluateToConstant(FALSE) &&
ie->child(1)->getOperatorType() == ITM_VEG_REFERENCE) {
childId = (ie->child(1).getPtr())->getValueId();
}
else if (ie->child(1)->doesExprEvaluateToConstant(FALSE) &&
ie->child(0)->getOperatorType() == ITM_VEG_REFERENCE) {
childId = (ie->child(0).getPtr())->getValueId();
}
// catches cases like T1.a > 1, T1.a < ? or T1.a > hostvar
// where T1.a is not a key column.
// Also catches t1.a > t1.b or T1.key < T1.a
if (NOT(keyCols.contains(childId)))
return TRUE;
}
else
return TRUE; // something unexpected, so assume the worst
}
// This will catch preds like T1.a = T1.b, (T1.a = T2.b and T2.b = T1.c)
// When we have two columns of T1 that are related by an equality pred either
// directly or indirectly, the VEGColList has the same VEG for pth columns
// and we use this to detect such predicates here.
const TableDesc * tdesc = ((Scan *)this)->getTableDesc();
ULng32 numCols = tdesc->getColumnVEGList().entries();
const ValueIdSet colVEGSet(tdesc->getColumnVEGList());
if (colVEGSet.entries() < numCols)
return TRUE;
return FALSE; // no reducing predicate detected
}
// -------------------------------------------------------------------------
// This method would set the basic cardinality and selecttivity information
// in the group attributes. This includes cardinality after aplying all local
// predicates, selectivity and cardinality hints if any
// -------------------------------------------------------------------------
void
Scan::finishSynthEstLogProp()
{
ExtendedQualName::SpecialTableType specialType = getTableName().getSpecialType();
// minimum row count of this group is the row count after applying local predicates
// Set this only if it is not a triggers table
if( specialType != ExtendedQualName::TRIGTEMP_TABLE)
{
TableDesc * tableDesc = getTableDescForBaseSelectivity();
if (specialType == ExtendedQualName::NORMAL_TABLE)
{
CardinalityHint * cardHint = tableDesc->cardinalityHint();
SelectivityHint * selHint = tableDesc->selectivityHint();
if ((cardHint || selHint) && !suppressHints_)
{
if ((cardHint && ( cardHint->getBaseScanSelectivityFactor() == -1.0 ) ) ||
(selHint && ( selHint->getBaseScanSelectivityFactor() == -1.0 ) ) )
{
// baseScanSelectivityFactor should be set only once for Scan node
// for corresponding indexes, it would be picked up from the tableDesc
// of Scan. Compute cardinality for emptyInputLogProp using no hints
// also, do not cache these logical properties in any of the cache, as
// these are not what the user wants. He wants the cardinality based
// on the hint he has provided.
CostScalar baseSelectivity = computeBaseSelectivity();
if (cardHint)
{
tableDesc->setBaseSelectivityHintForScan(cardHint, baseSelectivity);
}
if (selHint)
{
tableDesc->setBaseSelectivityHintForScan(selHint, baseSelectivity);
}
setTableDesc(tableDesc);
} // baseScanSelectivityFactor == -1.0
} // cardHint || selHint
// In both the cases of Hint and FetchCount, synchronize histograms with ColStats and
} // specialType == NORMAL_TABLE
CostScalar minimumRowcount = getGroupAttr()->getResultCardinalityForEmptyInput();
getGroupAttr()->setMinChildEstRowCount(minimumRowcount);
} // specialType != TRIGTEMP_TABLE
} // Scan::finishSynthEstLogProp()
// -----------------------------------------------------------------------
// methods for class Tuple
// -----------------------------------------------------------------------
void
Tuple::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
// create a default OutputLogProp with cardinality of 1
EstLogPropSharedPtr myEstLogProp(new (HISTHEAP) EstLogProp(1));
// create a histogram for each tuple, such that the row count and
// uec for each histogram is one. Reason for that is for cases
// like insert, these tuples get mapped to the columns in which
// the value is being inserted. These columns can then appear in the
// characteristic outputs. This means that we should have colStats for
// those columns. But since we were not generating colStats for the
// constants, there are no colStats generated for the columns to which
// these constants are being mapped.
ColStatDescList & outputColStats = myEstLogProp->colStats();
ValueIdList & tupleList = tupleExpr();
CollIndex nTupleListEntries = (CollIndex)tupleList.entries();
for (CollIndex i = 0; i < nTupleListEntries ; i++)
{
ValueId ituple = tupleList[i];
outputColStats.addColStatDescForVirtualCol(1,
1,
ituple,
ituple,
ituple,
NULL
);
}
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstLogProp);
} // Tuple::synthEstLogProp
void
Tuple::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and my children.
RelExpr::synthLogProp(normWAPtr);
// create the cardinality constraint
if (selectionPred().isEmpty())
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)1,(Cardinality)1));
else
getGroupAttr()->addConstraint(new(CmpCommon::statementHeap())
CardConstraint((Cardinality)0,(Cardinality)1));
} // Tuple::synthLogProp()
// -----------------------------------------------------------------------
// methods for class TupleList
// -----------------------------------------------------------------------
void
TupleList::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
CostScalar numberOfTuples = numberOfTuples_;
numberOfTuples.minCsZero();
if (CmpCommon::getDefault(COMP_BOOL_76) == DF_ON)
// Take into account the number of probes coming from the outer
numberOfTuples *= inputEstLogProp->getResultCardinality();
// create a OutputLogProp with cardinality of numberOfTuples
EstLogPropSharedPtr myEstLogProp(new (HISTHEAP) EstLogProp(numberOfTuples));
// create a histogram for each tuple, such that the row count and
// uec for each histogram is one. Reason for that is for cases
// like insert, these tuples get mapped to the columns in which
// the value is being inserted. These columns can then appear in the
// characteristic outputs. This means that we should have colStats for
// those columns. But since we were not generating colStats for the
// constants, there are no colStats generated for the columns to which
// these constants are being mapped.
ColStatDescList & outputColStats = myEstLogProp->colStats();
ValueIdList & tupleList = tupleExpr();
CollIndex nTupleListEntries = (CollIndex)tupleList.entries();
for (CollIndex i = 0; i < nTupleListEntries ; i++)
{
ValueId ituple = tupleList[i];
NABoolean defineVirtual = TRUE;
if (CmpCommon::getDefault(COMP_BOOL_48) == DF_ON)
{
ItemExpr * tupleExpr = ituple.getItemExpr();
tupleExpr = tupleExpr->getLeafValueIfUseStats();
ituple = tupleExpr->getValueId();
if (createdForInList())
defineVirtual = FALSE;
}
outputColStats.addColStatDescForVirtualCol(numberOfTuples,
numberOfTuples,
ituple,
ituple,
ituple,
this,
defineVirtual);
}
const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();
CollIndex outerRefCount = outerColStatsList.entries();
if (outerRefCount > 0)
{
CostScalar innerScale = numberOfTuples_;
innerScale.minCsOne();
outputColStats.prependDeepCopy (outerColStatsList, // source ColStatDescList
outerRefCount, // prepend limit
innerScale, // scale prepended Rowcounts
FALSE) ; // clear shapeChanged flag
}
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstLogProp);
} // TupleList::synthEstLogProp
void
TupleList::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// Synthesize log. properties for me and my children.
RelExpr::synthLogProp(normWAPtr);
// create the cardinality constraint
if (selectionPred().isEmpty())
getGroupAttr()->addConstraint(
new(CmpCommon::statementHeap()) CardConstraint(
(Cardinality) numberOfTuples_,
(Cardinality) numberOfTuples_));
else
getGroupAttr()->addConstraint(
new(CmpCommon::statementHeap()) CardConstraint(
(Cardinality) 0,
(Cardinality) numberOfTuples_));
} // TupleList::synthLogProp()
// -----------------------------------------------------------------------
// member functions for class GenericUpdate
// -----------------------------------------------------------------------
void
GenericUpdate::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
// Synthesize my estimated logical properties from either the table
// statistics if this is a leaf GenericUpdate, or from my child's
// statistics if this is a unary GenericUpdate operator.
EstLogPropSharedPtr myEstLogProp;
if (getArity() == 0)
{
myEstLogProp =
// for the third parameter we use the default value (1) for the
// initial rowcount
synthEstLogPropForUnaryLeafOp (inputEstLogProp, getTableDesc()->getTableColStats());
}
else
{
EstLogPropSharedPtr childOutputLogProp = child(0).outputLogProp(inputEstLogProp);
myEstLogProp =
synthEstLogPropForUnaryLeafOp (inputEstLogProp,
childOutputLogProp->getColStats(),
childOutputLogProp->getResultCardinality());
}
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstLogProp);
}
void
GenericUpdate::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis())
return;
Int32 numBaseTables;
// synthesize logical properties for me and my child (if any).
RelExpr::synthLogProp(normWAPtr);
if (getArity() == 0)
{
// For each set of column statistics, convert the base column valueids
// to the equivalent VEG cols.
ColStatDescList & initialStats = getTableDesc()->tableColStats();
for (CollIndex i = 0; i < initialStats.entries(); i++)
initialStats[i]->VEGColumn() =
getTableDesc()->getEquivVEGCol(initialStats[i]->getColumn());
numBaseTables = 1; // a leaf update counts as one base table
}
else
{
// For an update with children we don't count our own table again,
// since there is already a scan node for that table below us.
// This makes the counting independent on whether a subset or a
// cursor update is being used.
numBaseTables = child(0).getGroupAttr()->getNumBaseTables();
}
getGroupAttr()->setNumBaseTables(numBaseTables);
} // GenericUpdate::synthLogProp()
void
GenericUpdate::finishSynthEstLogProp()
{
CostScalar minRowCount = COSTSCALAR_MAX;
NABoolean triggersTempTable = \
(getTableName().getSpecialType() == ExtendedQualName::TRIGTEMP_TABLE) ?\
TRUE : FALSE;
if (triggersTempTable)
{
if (getArity() == 0)
{
minRowCount = getGroupAttr()->outputLogProp((*GLOBAL_EMPTY_INPUT_LOGPROP))->\
getResultCardinality();
}
else
{
minRowCount = child(0).getGroupAttr()->getMinChildEstRowCount();
}
}
getGroupAttr()->setMinChildEstRowCount(minRowCount);
} // GenericUpdate::finishSynthEstLogProp
// -----------------------------------------------------------------------
// methods for class ExplainFunc
// -----------------------------------------------------------------------
void
ExplainFunc::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp))
return;
// Create a new Output Log Property with cardinality of 10 for now.
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(10));
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);
} // ExplainFunc::synthEstLogProp
void
ExplainFunc::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
RelExpr::synthLogProp(normWAPtr);
} // ExplainFunc::synthLogProp()
// -----------------------------------------------------------------------
// methods for class Transpose
// -----------------------------------------------------------------------
// Transpose::synthEstLogProp() ------------------------------------------
// synthesize estimated logical properties given a specific set of
// input log. properties.
//
// Parameters:
//
// const EstLogPropSharedPtr& inputEstLogProp
// IN : A set of input logical properties used to estimate the logical
// properities of this node.
//
// For Example:
//
// SELECT *
// FROM Table
// TRANSPOSE A,B AS C1
// X,Y,Z as C2
// (1,'hello'),(2,'world') AS (C3, C4)
// KEY BY K1
//
// Terminology:
// transUnionVector_:This is a vector of ValueIdLists. There is one entry
// for each transpose set, plus one entry for the key values. Each entry
// contains a list of ValueIdUnion Nodes. The first entry contains a list
// with one ValueIdUnion node. This node is for the Const. Values (1 - N)
// representing the Key Values. The other entries contain lists of
// ValueIdUnion nodes for the Transposed Values. Each of these entries of
// the vector represent a transpose set. If the transpose set contains a
// list of values, then there will be only one ValueIdUnion node in the
// list. If the transpose set contains a list of lists of values, then
// there will be as many ValueIdUnion nodes as there are items in the
// sublists. (see example below.)
//
// TRANSPOSE
// transUnionVectorSize_: 4
// transUnionVector_:
// ValueIdUnion(1,2,3,4,5,6,7)
// ValueIdUnion(A,B)
// ValueIdUnion(X,Y,Z)
// ValueIdUnion(1,2) , ValueIdUnion('hello','world')
//
// Cardinality of Transpose - is the cardinality of the child * transpose expressions.
// In the above example, there are 4 transpose expressions. So the cardinality will be
// Cardinality of the child * 4
//
// UEC of Transpose : is the sum of UEC of columns comprising of the transposed column. There
// is also one added for each constant. Hence
// UEC of C1 = UEC of A + UEC of B
// UEC of C2 = UEC of X + UEC of Y + UEC of Z
// UEC of C3 = 2
// UEC of C4 = 2
//
void
Transpose::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp) == TRUE)
return;
// Create a new Output Log Property
//
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp());
// Get the estimated logical properties of the child. To be used
// to estimate the logical properties of this node.
//
EstLogPropSharedPtr childEstProp = child(0).outputLogProp(inputEstLogProp);
// Get the current column stats.
//
const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();
const ColStatDescList &childColStatsList = childEstProp->getColStats();
// Result Column Stats. Based on childs col stats list
// since result of transpose can contain columns from child.
//
ColStatDescList transStatDescList(childColStatsList,CmpCommon::statementHeap());
// Composite Column Stats. for all transposed columns and
// the key column if it exists. This is because there is
// a corelation between the key column and the transposed
// columns. If the key column does not exist, no composite
// stats will be generated.
//
ColStatsSharedPtr compColStats;
ColStatDescSharedPtr compColStatDesc;
// Does the key column exist?...
//
if(transUnionVector()[0].entries() > 0)
{
// The key values exist.
// Add col stats for this generated column,
// and the composite column stats for the key
// column and the transposed columns.
//
ValueIdUnion *keyValIdUnion =
(ValueIdUnion *)(transUnionVector()[0])[0].getValueDesc()->getItemExpr();
CostScalar numKeyVals = keyValIdUnion->entries();
CostScalar rowCount = childEstProp->getResultCardinality() * numKeyVals;
// Create a histogram for the Key column.
// Empty for now.
//
HistogramSharedPtr emptyHist(new (HISTHEAP) Histogram(HISTHEAP));
// Create the ColStats for the key column.
//
ComUID id(ColStats::nextFakeHistogramID());
ColStatsSharedPtr keyStats(
new (HISTHEAP) ColStats(id,
numKeyVals,
rowCount,
rowCount,
FALSE,
FALSE,
emptyHist,
FALSE,
1.0,
1.0,
-1, //average varchar size
HISTHEAP));
// Setting this flag will ensure that the compiler does not start
// to look for this column name in the NAColumn. As there does not
// exist a column for a constant
keyStats->setVirtualColForHist(TRUE);
keyStats->setIsCompressed(TRUE);
// Create a copy of the column stats to be used for the
// composite column stats.
//
compColStats = ColStatsSharedPtr(new (HISTHEAP) ColStats(*keyStats, HISTHEAP));
// Set the UEC of the composite column stats to 0. This is
// because the key column does not affect the total UEC of
// the composite column. Later, the UEC's of the transpose columns
// will be added in.
//
compColStats->setRowsAndUec(0, 0);
ColStatDescSharedPtr keyColStatDesc(new (HISTHEAP) ColStatDesc(keyStats,
(transUnionVector()[0])[0]), HISTHEAP);
compColStatDesc = ColStatDescSharedPtr(new (HISTHEAP) ColStatDesc(compColStats,
(transUnionVector()[0])[0]), HISTHEAP);
transStatDescList.insert(keyColStatDesc);
} // (transUnionVector()[0].entries() > 0)
Int32 haveAllColStats = 0;
for(CollIndex vec = 1; vec < transUnionVectorSize(); vec++)
{
ValueIdList &valIdList = transUnionVector()[vec];
for(CollIndex vidu = 0; vidu < valIdList.entries(); vidu++)
{
// Keep count of constant members in the Union.
CostScalar constMembers = 0;
ValueId firstSourceID;
ValueIdUnion *valIdUnion =
(ValueIdUnion *)valIdList[vidu].getValueDesc()->getItemExpr();
for(CollIndex vid = 0; vid < valIdUnion->entries(); vid++)
{
ValueId sourceValId = valIdUnion->getSource((Lng32)vid);
sourceValId = getSourceColFromExprForUec(sourceValId, childColStatsList);
CollIndex index;
if(childColStatsList.
getColStatDescIndex(index, sourceValId ))
{
if (!firstSourceID)
firstSourceID = sourceValId;
}
else
constMembers++;
} // for (valIdUnion->entries() )
if (firstSourceID) haveAllColStats++;
if (!firstSourceID)
{
// This column is a union of all constants, so create a fake histogram
// with UEC equal to number of unique constants in the Union, and the
// row count equal to the number of rows of the child
// This fake histogram is used by all the nodes above this to compute
// cardinalities
// get rid of all duplicates to get number of unique constants.
ValueIdSet sourceConstants = valIdUnion->getSources();
constMembers = sourceConstants.entries();
CostScalar rowCount = MINOF(constMembers, childEstProp->getResultCardinality() );
// number of distinct constants form the UEC
// VEGColumn and the mergeState comprise of the ValueIdUnion for this column
transStatDescList.addColStatDescForVirtualCol(constMembers,
rowCount,
valIdUnion->getSource(0),
valIdList[vidu],
valIdUnion->getValueId(),
NULL);
}
else
{
// Have col stats for atleast 1 member.
CollIndex entry = transStatDescList.entries();
CollIndex firstEntry = NULL_COLL_INDEX;
childColStatsList.
getColStatDescIndex(firstEntry,firstSourceID);
ColStatDescSharedPtr firstColStatDesc = childColStatsList[firstEntry];
// transStatDescList.insertAt(entry, firstColStatDesc);
// for safety, perform a deep copy.....
ColStatsSharedPtr transColStats =
firstColStatDesc->getColStatsToModify();
transColStats->copyAndScaleHistogram( 1 );
ValueId firstCol = valIdUnion->getSource(0);
firstCol = getSourceColFromExprForUec(firstCol, childColStatsList);
ColStatDescSharedPtr transStatDesc(new (HISTHEAP)
ColStatDesc (transColStats,firstCol), HISTHEAP);
transStatDescList.insertAt(entry,transStatDesc);
CollIndex index;
for(CollIndex vid = 1; vid < valIdUnion->entries(); vid++)
{
index = NULL_COLL_INDEX;
// The column in the Transpose could be within an expression
// hence first get all VEG expressions from the source, and then
// use that VEG expression to look for the column
ValueId sourceColumn = valIdUnion->getSource((Lng32)vid);
sourceColumn = getSourceColFromExprForUec(sourceColumn, childColStatsList);
childColStatsList.
getColStatDescIndex(index, sourceColumn);
if (index == NULL_COLL_INDEX) continue;
ColStatDescSharedPtr colStatDesc = childColStatsList[index];
transStatDescList[entry]->mergeColStatDesc(colStatDesc,
UNION_MERGE,
FALSE,
REL_UNION);
} // for (ValueIdUnion->entries() )
// update the result's description of itself
// Note that it is proper that this isn't done in the previous
// body of code that looks (as is pointed out above), similar to
// this code.
transStatDescList[entry]->VEGColumn() = valIdList[vidu];
// need to do the following, adding "my" ValueId to the list of
// columns I've joined to, because in ColStatDesc::mergeColStatDesc()
// we can get into trouble when we have a mergeState_ that is empty
// --> we set to empty first (via clear()) because we do not want
// the left table's mergeState information
transStatDescList[entry]->mergeState().clear() ;
transStatDescList[entry]->mergeState().insert(valIdUnion->getValueId());
// add 1 to the UEC for each constant member
ColStatsSharedPtr transposedColStat = transStatDescList[entry]->getColStatsToModify();
CostScalar unionUec = transposedColStat->getTotalUec();
unionUec += constMembers;
// Even while setting the UEc we want to be sure that we are maintaining
// the sanity relationship between UEC and rowcount. That is why the UEC
// is never set alone
CostScalar rowCount = transposedColStat->getRowcount();
transposedColStat->setRowsAndUec(rowCount, unionUec);
} // (!firstSourceID)
} // for (valIdList.entries() )
} // for (transUnionVectorSize() )
if(haveAllColStats && compColStats)
{
// There exists a key column for the transpose
CostScalar compUEC = 0;
for(CollIndex vec = 1; vec < transUnionVectorSize(); vec++)
{
ValueIdList &valIdList = transUnionVector()[vec];
ValueIdUnion *firstValIdUnion =
(ValueIdUnion *)valIdList[0].getValueDesc()->getItemExpr();
for(CollIndex vid = 0; vid < firstValIdUnion->entries(); vid++)
{
CostScalar interUEC = 1;
for(CollIndex vidu = 0; vidu < valIdList.entries(); vidu++)
{
ValueIdUnion *valIdUnion =
(ValueIdUnion *)valIdList[vidu].getValueDesc()->getItemExpr();
CollIndex index = NULL_COLL_INDEX;
ValueId sourceColumn = valIdUnion->getSource((Lng32)vid);
sourceColumn = getSourceColFromExprForUec(sourceColumn, childColStatsList);
childColStatsList.
getColStatDescIndex(index, sourceColumn);
if (index == NULL_COLL_INDEX) continue;
ColStatDescSharedPtr colStatDesc = childColStatsList[index];
interUEC = interUEC * colStatDesc->getColStats()->getTotalUec();
} // for (valIdList.entries() )
compUEC = compUEC + interUEC;
} // for (firstValIdUnion->entries() )
compColStatDesc->mergeState().clear() ;
for(CollIndex vidu = 0; vidu < valIdList.entries(); vidu++)
{
compColStatDesc->mergeState().insert(valIdList[vidu]);
}
} // for (transUnionVectorSize() )
const CostScalar compRow = compColStats->getRowcount() ;
compColStats->setRowsAndUec(compRow, compUEC);
transStatDescList.insert(compColStatDesc);
} // if(haveAllColStats && compColStats)
// Estimate the cardinality of this node by multipling the estimated
// cardinality of the child by the total number of transpose expressions.
// The total number of transpose expressions is also the number of
// key values, or the number of entries in the first entry of
// transUnionVector()
//
CollIndex numTransposeExprs = 0;
// The first entry for the key values, which is optional.
// Start with the second entry. The number of key values is
// the sum of entries of the first valIdUnion of each (1 - N)
// ValueIdList of the transUnionVector.
//
for(CollIndex v = 1 ; v < transUnionVectorSize(); v++)
{
ValueIdUnion *valIdUnion =
(ValueIdUnion *)(transUnionVector()[v])[0].getValueDesc()->getItemExpr();
numTransposeExprs += valIdUnion->entries();
}
// Estimated rowCount.
//
CostScalar rowCount =
childEstProp->getResultCardinality().minCsOne() * numTransposeExprs;
// Set the cardinality estimate.
//
myEstProps->setResultCardinality(rowCount);
// maxCard(transpose(X)) == card(transpose(X)) * (maxCard(X)/card(X))
CostScalar maxRows = rowCount *
(childEstProp->getMaxCardEst() /
childEstProp->getResultCardinality().minCsOne());
myEstProps->setMaxCardEst(MAXOF(maxRows, rowCount));
// -------------------------------------------------------------------
// Lastly, determine which of these histograms are useful based on
// the characteristic outputs and selection predicates for the group.
// -------------------------------------------------------------------
myEstProps->pickOutputs( transStatDescList, inputEstLogProp, getGroupAttr()->getCharacteristicOutputs(), getSelectionPred());
// -------------------------------------------------------------------
// VERY IMPORTANT! Before we register myEstProps as the OLP of the ILP
// inputEstLogProp, it's vital that every histogram in myEstProps
// reports the same rowcount. Otherwise, it's very difficult for
// resulting code to make any sense of these statistics (in particular,
// what's the rowcount associated with them?).
// -------------------------------------------------------------------
ColStatDescList & columnStats = myEstProps->colStats() ; // nasty access to private member
columnStats.synchronizeStats (rowCount, columnStats.entries()) ;
// Set the logical properties of this node.
//
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);
} // Transpose::synthEstLogProp
// Transpose::synthLogProp ----------------------------------------------
// synthesize logical properties
//
void
Transpose::synthLogProp(NormWA * normWAPtr)
{
// check to see whether properties are already synthesized.
if (getGroupAttr()->existsLogExprForSynthesis())
return;
// Need to check for cardinality constraints and uniques constraints.
// !!!
RelExpr::synthLogProp(normWAPtr);
} // Transpose::synthLogProp()
// -----------------------------------------------------------------------
// methods for class Pack
// -----------------------------------------------------------------------
void
Pack::synthEstLogProp(const EstLogPropSharedPtr& inEstLogProp)
{
if(getGroupAttr()->isPropSynthesized(inEstLogProp) == TRUE) return;
EstLogPropSharedPtr myEstProp(new (HISTHEAP) EstLogProp());
// ---------------------------------------------------------------------
// $$$ No more column statistics will be available above the Pack node.
// $$$ just set the estimate cardinality.
// ---------------------------------------------------------------------
EstLogPropSharedPtr childEstProp = child(0).outputLogProp(inEstLogProp);
// Just to make sure division by zero doesn't happen in prototype code.
ULng32 pf = MIN_ONE (packingFactorLong()) ;
CostScalar myRowCount = childEstProp->getResultCardinality() / pf;
myEstProp->setResultCardinality(myRowCount);
getGroupAttr()->addInputOutputLogProp(inEstLogProp, myEstProp);
}
void
Pack::synthLogProp(NormWA * normWAPtr)
{
// check to see whether properties are already synthesized.
if (getGroupAttr()->existsLogExprForSynthesis()) return;
// ---------------------------------------------------------------------
// $$$ anything else on logical properties that need to be added ??
// ---------------------------------------------------------------------
// Synthesize things like my record length and drive synthesis for child.
RelExpr::synthLogProp(normWAPtr);
}
void CommonSubExprRef::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp))
return ; // already done this
// make the child's est log props my own
getGroupAttr()->addInputOutputLogProp (
inputEstLogProp,
child(0).getGroupAttr()->outputLogProp(inputEstLogProp));
}
void CommonSubExprRef::synthLogProp(NormWA * normWAPtr)
{
// Follow the RelExpr base class logic first
if (getGroupAttr()->existsLogExprForSynthesis())
return;
RelExpr::synthLogProp(normWAPtr);
// simply propagate the child's constraints
getGroupAttr()->addConstraints(child(0).getGroupAttr()->getConstraints());
} // RelRoutine::synthLogProp()
void RelRoutine::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis())
return;
// For now assume the lower classes will do what they need.
RelExpr::synthLogProp(normWAPtr);
} // RelRoutine::synthLogProp()
void IsolatedScalarUDF::synthLogProp(NormWA * normWAPtr)
{
GroupAttributes *GA = getGroupAttr();
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (GA->existsLogExprForSynthesis()) return;
super::synthLogProp(normWAPtr);
if (getRoutineDesc()->getEffectiveNARoutine()->isDeterministic() == FALSE)
GA->setHasNonDeterministicUDRs(TRUE);
// For isolated scalar UDFs, always add a constraint of (0,1) since
// these funtions only return 1 row per probe (call).
GA->addConstraint(new(CmpCommon::statementHeap()) CardConstraint(0,1));
// We also want to add a FuncDependencyConstraint to establish the
// relationship beetween the functions outputs and its inputs.
FuncDependencyConstraint *fdc =
new(CmpCommon::statementHeap())
FuncDependencyConstraint(
GA->getCharacteristicInputs(),
GA->getCharacteristicOutputs());
GA->addConstraint(fdc);
} // IsolatedScalarUDF::synthLogProp()
void IsolatedScalarUDF::synthEstLogProp(const EstLogPropSharedPtr &inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp)) return;
// Make sure we have a fanOut of 1!
CMPASSERT(getRoutineDesc()->getEffFanOut() == 1);
// create a EstLogProp for the routine
// Scaled by the input cardinality.
EstLogPropSharedPtr myEstProps(new (HISTHEAP)
EstLogProp(
inputEstLogProp->getResultCardinality() *
getRoutineDesc()->getEffFanOut()
)
);
// We know that at most we will return every row seen.
myEstProps->setMaxCardEst(inputEstLogProp->getResultCardinality() *
getRoutineDesc()->getEffFanOut()
);
// get the default setting for UEC.
UInt32 defaultUdfUec = (ActiveSchemaDB()->getDefaults()).
getAsULong(ROUTINE_DEFAULT_UEC);
// loop through the input columns and get the Max of their Uec.
// We will use this as the output UEC if our metadata did not
// specify any UEC for the output column,
CostScalar maxInputUec = csMinusOne;
const ColStatDescList &inputColStatsList = inputEstLogProp->getColStats();
for (UInt32 i=0; i < inputColStatsList.entries(); i++)
{
// XXX need to check that the input is part of our characteristicInputs
// as it may contain more columns than what the routine has as inputs.
ColStatDescSharedPtr inputStatDesc = inputColStatsList[i];
maxInputUec = MAXOF(inputStatDesc->getColStats()->getTotalUec(),
maxInputUec);
}
// Associate the UEC we stored in the RoutineDesc to this
// EstLogPropSharedPtr.
ColStatDescList &colStatDescList = getRoutineDesc()->
getEffOutParamColStatDescList();
// Check the UEC of each given output parameter
for (UInt32 k=0; k < colStatDescList.entries(); k++)
{
ColStatDescSharedPtr outColStatDesc = colStatDescList[k];
CostScalar maxOutputUec = outColStatDesc->getColStats()->getTotalUec();
// If we didn't get a value from Metadata, we will either use
// a value from the CQD or, the max of the inputs.
if (maxOutputUec == csMinusOne)
{
// We only have one Column in each array
// We access TotalUec since that is what gets set during
// ColStats() construction ..
// Check to see what our DEFAULTS is set to..
// A value higher than 0 in the defaults table overrides the
// use of the input UECs as a estimate of the Output UECs.
if (defaultUdfUec > 0)
maxOutputUec = MAXOF(csOne, defaultUdfUec);
// If we don't have output UEC value, and we didn't set a value
// in the defaults table, use the max of the input UEC for the
// output UEC.
if (maxOutputUec == csMinusOne)
{
if (maxInputUec == csMinusOne)
{
// Since both our input and output column UECs are unknown
// Force it to be 1.
maxOutputUec = csOne;
}
else
{
// Otherwise set it to the max of the inputs.
maxOutputUec = maxInputUec;
}
}
// Update the Uec for the Output Column
// setRowsAndUec() updates the totalUec..
outColStatDesc->getColStats()->setRowsAndUec(
outColStatDesc->getColStats()->getRowcount(), maxOutputUec);
}
}
// This is the maximum we will return.
CostScalar newRowCount = myEstProps->getMaxCardEst();
// Synchronize the cardinality - estimateCardinalty below assumes
// that everything has been synchronized.
colStatDescList.synchronizeStats(newRowCount, colStatDescList.entries());
// These are inputs from the left
// can be empty if all the parameters are literals or hostVars.
ValueIdSet outerReferences = getGroupAttr()->getCharacteristicInputs();
CollIndex outerRefCount = outerReferences.entries();
MergeType mergeMethod = INNER_JOIN_MERGE; // starting assumption
newRowCount =
colStatDescList.estimateCardinality (newRowCount, /*in*/
getSelectionPred(),/*in*/
outerReferences, /*in*/
NULL,
NULL,
NULL,
outerRefCount, /*in/out*/
myEstProps->unresolvedPreds(),/*in/out*/
mergeMethod, /*in*/
REL_ISOLATED_SCALAR_UDF); /*no-op*/
// This is the number of rows that we think we will return
myEstProps->setResultCardinality(newRowCount);
// From the given ColStatDescList, populate columnStats_ with column
// descriptors that are useful based on the characteristic outputs
// for the group.
myEstProps->pickOutputs(colStatDescList,
inputEstLogProp,
getGroupAttr()->getCharacteristicOutputs(),
selectionPred());
// -----------------------------------------------------------------------
// This method adds a reference to the provided input estimated logical
// property. It also allocates a corresponding set of output estimated
// logical properties.
getGroupAttr()->addInputOutputLogProp (inputEstLogProp, myEstProps);
} // IsolatedScalarUDF::synthEstLogProp()
void CallSP::synthLogProp(NormWA * normWAPtr)
{
// Check to see whether this GA has already been associated
// with a logExpr for synthesis. If so, no need to resynthesize
// for this equivalent log. expression.
if (getGroupAttr()->existsLogExprForSynthesis())
return;
super::synthLogProp(normWAPtr);
} // CallSP::synthLogProp()
void CallSP::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp) == TRUE)
return;
// Create a new Output Log Property with cardinality of 1
const Int32 myCardinality = 1;
EstLogPropSharedPtr myEstProps(new (HISTHEAP) EstLogProp(myCardinality));
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);
} // CallSP::synthEstLogProp()