blob: f0ce94117756f09927933607cd4f668a6b8853ad [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: RelSequence.cpp
* Description: All the methods of RelSequence() and PhysSequence().
*
* Created: 6/27/97
* Language: C++
*
*
******************************************************************************
*/
#include "AllItemExpr.h"
#include "ItemSample.h"
#include "AllRelExpr.h"
#include "RelSequence.h"
#include "SchemaDB.h"
#include "GroupAttr.h"
#include "BindWA.h"
#include "NormWA.h"
#include "Cost.h"
#include "CostMethod.h"
#include "opt.h"
#include "Globals.h"
// -----------------------------------------------------------------------
// This file contains most of the methods for the class RelSequence
// This is a departure from the way other RelExpr's are organized.
//
// The RelSequence constructor.
// Inputs:
//
// child: The child node of this node. presumably a Scan node.
//
RelSequence::RelSequence(RelExpr *child,
ItemExpr *requiredOrder,
CollHeap *oHeap)
: RelExpr(REL_SEQUENCE, child, NULL, oHeap),
requiredOrderTree_(requiredOrder),
partitionBy_(NULL),
cancelExprTree_(NULL),
hasOlapFunctions_(FALSE),
hasTDFunctions_(FALSE),
cachedNumHistoryRows_(0),
cachedHistoryRowLength_(0),
cachedUnboundedFollowing_(FALSE),
cachedMinFollowingRows_(0),
historyInfoCached_(FALSE)
{
setNonCacheable();
}
RelSequence::RelSequence(RelExpr *child,
ItemExpr *partitionBy,
ItemExpr *orderBy,
CollHeap *oHeap)
: RelExpr(REL_SEQUENCE, child, NULL, oHeap),
requiredOrderTree_(orderBy),
partitionBy_(partitionBy),
cancelExprTree_(NULL),
hasOlapFunctions_(FALSE),
hasTDFunctions_(FALSE),
cachedNumHistoryRows_(0),
cachedHistoryRowLength_(0),
cachedUnboundedFollowing_(FALSE),
cachedMinFollowingRows_(0),
historyInfoCached_(FALSE)
{
setNonCacheable();
}
// RelSequence::~RelSequence() -----------------------------------------------
// The destructor
//
RelSequence::~RelSequence()
{
}
ItemExpr *
RelSequence::removeRequiredOrderTree()
{
ItemExpr *requiredOrderTree = requiredOrderTree_;
requiredOrderTree_ = NULL;
return requiredOrderTree;
}
//++MV
void RelSequence::addRequiredOrderTree(ItemExpr *orderExpr)
{
ExprValueId c = requiredOrderTree_;
ItemExprTreeAsList(&c, ITM_ITEM_LIST).insert(orderExpr);
requiredOrderTree_ = c.getPtr();
}
//--MV
// RelSequence::topHash() --------------------------------------------------
// Compute a hash value for a chain of derived RelExpr nodes.
// Used by the Cascade engine as a quick way to determine if
// two nodes are identical.
// Can produce false positives (nodes appear to be identical),
// but should not produce false negatives (nodes are definitely different)
//
// Inputs: none (other than 'this')
//
// Outputs: A HashValue of this node and all nodes in the
// derivation chain below (towards the base class) this node.
//
HashValue RelSequence::topHash()
{
// Compute a hash value of the derivation chain below this node.
//
HashValue result = RelExpr::topHash();
result ^= requiredOrder();
result ^= partition();
result ^= sequenceFunctions();
result ^= sequencedColumns();
result ^= cancelExpr();
return result;
}
// RelSequence::duplicateMatch()
// A more thorough method to compare two RelExpr nodes.
// Used by the Cascades engine when the topHash() of two
// nodes returns the same hash values.
//
// Inputs: other - a reference to another node of the same type.
//
// Outputs: NABoolean - TRUE if this node is 'identical' to the
// 'other' node. FALSE otherwise.
//
// In order to match, this node must match all the way down the
// derivation chain to the RelExpr class.
//
// For the RelSequence node, the only relevant data members which
// need to be compared are partition_, requiredOrder_ and sequenceFunctions_
//
NABoolean
RelSequence::duplicateMatch(const RelExpr & other) const
{
// Compare this node with 'other' down the derivation chain.
//
if (!RelExpr::duplicateMatch(other))
return FALSE;
// Cast the RelExpr to a RelSequence node. (This must be a RelSequence node)
//
const RelSequence &o = (RelSequence &) other;
// If the required order keys are not the same
// then the nodes are not identical
//
if (!(requiredOrder() == o.requiredOrder()))
return FALSE;
if (!(partition() == o.partition()))
return FALSE;
// If the sequence functions are not the same
// then the nodes are not identical
//
if (!(sequenceFunctions() == o.sequenceFunctions()))
return FALSE;
if (!(sequencedColumns() == o.sequencedColumns()))
return FALSE;
if (!(cancelExpr() == o.cancelExpr()))
return FALSE;
return TRUE;
} // RelSequence::duplicateMatch
// RelSequence::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls RelExpr::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
// which is derived from this node. If NULL, then this
// node is the top of the derivation chain and a node must
// be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and an RelSequence
// node must be constructed.
//
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (RelExpr::copyTopNode() in this
// case).
//
RelExpr *
RelSequence::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
RelSequence *result;
if (derivedNode == NULL)
// This is the top of the derivation chain
// Create an empty RelSequence node.
//
result = new (outHeap) RelSequence();
else
// A node has already been constructed as a derived class.
//
result = (RelSequence *) derivedNode;
// Copy the relavant fields.
result->requiredOrder() = requiredOrder();
result->partition() = partition();
result->sequenceFunctions() = sequenceFunctions();
result->sequencedColumns() = sequencedColumns();
result->cancelExpr() = cancelExpr();
result->checkPartitionChangeExpr_ = checkPartitionChangeExpr_;
result->hasOlapFunctions_ = hasOlapFunctions_;
result->hasTDFunctions_= hasTDFunctions_;
result->movePartIdsExpr_ = movePartIdsExpr_;
//need to review the commented code below
//result->requiredOrderTree_ = requiredOrderTree_->copyTree(outHeap)->castToItemExpr();
if (partitionBy_)
result->partitionBy_ = partitionBy_->copyTree(outHeap)->castToItemExpr();
//result->cancelExprTree_ = cancelExprTree_->copyTree(outHeap)->castToItemExpr();
// Copy any data members from the classes lower in the derivation chain.
//
return RelExpr::copyTopNode(result, outHeap);
} // RelSequence::copyTopNode
// RelSequence::addLocalExpr() -----------------------------------------------
// Insert into a list of expressions all the expressions of this node and
// all nodes below this node in the derivation chain. Insert into a list of
// names, all the names of the expressions of this node and all nodes below
// this node in the derivation chain. This method is used by the GUI tool
// and by the Explain Function to have a common method to get all the
// expressions associated with a node.
//
// Inputs/Outputs: xlist - a list of expressions.
// llist - a list of names of expressions.
//
// The xlist contains a list of all the expressions associated with this
// node. The llist contains the names of these expressions. (This lists
// must be kept in the same order).
// RelSequence::addLocalExpr potentially adds the requiredOrder_ expression
// ("required_order") and the sequenceFunctions_ ("sequence_functions")
//
// It then calls RelExpr::addLocalExpr() which will add any RelExpr
// expressions to the list.
//
void RelSequence::addLocalExpr(LIST(ExprNode *) &xlist,
LIST(NAString) &llist) const
{
if (requiredOrderTree_) {
xlist.insert(requiredOrderTree_);
llist.insert("required_order");
} else if (requiredOrder().entries() > 0) {
xlist.insert(requiredOrder().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("required_order");
}
if (partitionBy_) {
xlist.insert(partitionBy_);
llist.insert("partition_by");
} else if (partition().entries() > 0) {
xlist.insert(partition().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("partition_by");
}
if (sequenceFunctions().entries() > 0) {
xlist.insert(sequenceFunctions().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("read_functions");
}
if (returnSeqFunctions().entries() > 0) {
xlist.insert(returnSeqFunctions().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("return_functions");
}
if (sequencedColumns().entries() > 0) {
xlist.insert(sequencedColumns().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("sequenced_columns");
}
if (movePartIdsExpr().entries() > 0) {
xlist.insert(movePartIdsExpr().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("move_partition");
}
if (cancelExpr().entries() > 0) {
xlist.insert(cancelExpr().rebuildExprTree(ITM_ITEM_LIST));
llist.insert("cancel_expression");
}
if (checkPartitionChangeExpr_.entries() > 0)
{
xlist.insert(checkPartitionChangeExpr_.rebuildExprTree(ITM_ITEM_LIST));
llist.insert("olap_partition_change");
}
RelExpr::addLocalExpr(xlist,llist);
} // RelSequence::addLocalExpr
// RelSequence::getPotentialOutputValues() ---------------------------------
// Construct a Set of the potential outputs of this node.
//
// Inputs: none (other than 'this')
//
// Outputs: outputValues - a ValueIdSet representing the potential outputs
// of this node.
//
// The potential outputs for the RelSequence node are the sequenced columns
// generated by the RelSequence node.
//
void
RelSequence::getPotentialOutputValues(ValueIdSet & outputValues) const
{
// Make sure the ValueIdSet is empty.
//
outputValues.clear();
outputValues += sequencedColumns();
// Add the values generated by the RelSequence node.
//
outputValues += sequenceFunctions();
// Add the cancel expression.
//
outputValues.insertList(cancelExpr());
} // RelSequence::getPotentialOutputValues()
// The destructor
//
PhysSequence::~PhysSequence()
{
}
// PhysSequence::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls RelSequence::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
// which is derived from this node. If NULL, then this
// node is the top of the derivation chain and a node must
// be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and a RelSequence
// node must be constructed.
//
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (RelSequence::copyTopNode() in this
// case).
//
RelExpr *
PhysSequence::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
PhysSequence *result;
if (derivedNode == NULL)
// This is the top of the derivation chain
// Generate an empty PhysSequence node.
//
result = new (outHeap) PhysSequence();
else
// A node has already been constructed as a derived class.
//
result = (PhysSequence *) derivedNode;
// PhysSequence has no data members.
// Copy any data members from the classes lower in the derivation chain.
//
return RelSequence::copyTopNode(result, outHeap);
} // PhysSequence::copyTopNode
// RelSequence::transformNode() -------------------------------------------
// Unconditional query transformations such as the transformation of
// a subquery to a semijoin are implemented by the virtual function
// transformNode(). The aim of such transformations is to bring the
// query tree to a canonical form. transformNode() also ensures
// that the "required" (or characteristic) input values are "minimal"
// and the "required" (or characteristic) outputs values are
// "maximal" for each operator.
//
// transformNode() is an overloaded name, which is used for a set
// of methods that implement the transformation phase of query
// normalization.
//
// We use the term query tree for a tree of relational operators,
// each of which can contain none or more scalar expression trees.
// The transformations performed by transformNode() brings scalar
// expressions into a canonical form. The effect of most such
// transformations is local to the scalar expression tree.
// However, the transformation of a subquery requires a semijoin
// to be performed between the relational operator that contains
// the subquery and the query tree for the subquery. The effect
// of such a subquery transformation is therefore visible not
// only in the scalar expression tree but also in the relational
// expression tree.
//
// Parameters:
//
// NormWA & normWARef
// IN : a pointer to the normalizer work area
//
// ExprGroupId & locationOfPointerToMe
// IN : a reference to the location that contains a pointer to
// the RelExpr that is currently being processed.
//
void RelSequence::transformNode(NormWA &normWARef,
ExprGroupId &locationOfPointerToMe)
{
CMPASSERT( this == locationOfPointerToMe );
// If this node has already been transformed, we are done.
//
if (nodeIsTransformed())
return;
// Make sure that it is only transformed once.
//
markAsTransformed();
// transformNode takes up a bound tree and turns into a transformed
// tree. For a RelExpr that means the following.
// + expressions are transformed. If the expressions contain
// subqueries then new RelExpr are created for them and
// they are usually added above (as an ancestor) of the node
// that contained them.
// + predicates are pulled up from the children and their
// required inputs are modified
// + the required inputs of the node the node itself are changed
// from being a sufficient set to being a sufficient minimal
// set.
//
// Transform the child.
// Pull up their transformed predicates
// recompute their required inputs.
//
child(0)->transformNode(normWARef, child(0));
if(requiredOrder().
transformNode(normWARef,
child(0),
getGroupAttr()->getCharacteristicInputs())) {
// The requiredOrder list apparently had some subqueries that had
// not been processed before (is this possible?). Normalize the
// new tree that has become our child.
//
child(0)->transformNode(normWARef, child(0));
}
if(partition().
transformNode(normWARef,
child(0),
getGroupAttr()->getCharacteristicInputs())) {
// The partition list apparently had some subqueries that had
// not been processed before (is this possible?). Normalize the
// new tree that has become our child.
//
child(0)->transformNode(normWARef, child(0));
}
if (CmpCommon::getDefault(COMP_BOOL_201) == DF_ON){
normWARef.resetSeqFunctionsCache();
}
if(sequenceFunctions().
transformNode(normWARef,
child(0),
getGroupAttr()->getCharacteristicInputs())) {
// The sequenceFunctions apparently had some subqueries that had not
// been processed before (is this possible?). Normalize the new
// tree that has become our child.
//
child(0)->transformNode(normWARef, child(0));
}
/*
Note: This code resulted in an internal error when TD RANK code was added.
if (CmpCommon::getDefault(COMP_BOOL_200) == DF_ON){
normWARef.resetAllSeqFunctions();
for(ValueId seqId = sequenceFunctions().init(); sequenceFunctions().next(seqId);
sequenceFunctions().advance(seqId) ){
ItemExpr * ie;
ie = seqId.getItemExpr();
normWARef.optimizeSeqFunctions( ie, NULL, 0 );
}
}*/
if(sequencedColumns().
transformNode(normWARef,
child(0),
getGroupAttr()->getCharacteristicInputs())) {
// The sequencedColumns apparently had some subqueries that had not
// been processed before (is this possible?). Normalize the new
// tree that has become our child.
//
child(0)->transformNode(normWARef, child(0));
}
if(cancelExpr().
transformNode(normWARef,
child(0),
getGroupAttr()->getCharacteristicInputs())) {
// The cancelExpr apparently had some subqueries that had not
// been processed before (is this possible?). Normalize the new
// tree that has become our child.
//
child(0)->transformNode(normWARef, child(0));
}
// Pull up the predicates and recompute the required inputs
// of whoever my children are now.
//
pullUpPreds();
// transform the selection predicates
//
transformSelectPred(normWARef, locationOfPointerToMe);
} // RelSequence::transformNode()
// RelSequence::rewriteNode() ---------------------------------------------
// rewriteNode() is the virtual function that computes
// the transitive closure for "=" predicates and rewrites value
// expressions.
//
// Parameters:
//
// NormWA & normWARef
// IN : a pointer to the normalizer work area
//
void RelSequence::rewriteNode(NormWA & normWARef)
{
RelExpr::rewriteNode(normWARef);
if(requiredOrder().normalizeNode(normWARef)) {
}
if(partition().normalizeNode(normWARef)) {
}
if(sequenceFunctions().normalizeNode(normWARef)) {
}
if(sequencedColumns().normalizeNode(normWARef)) {
}
if(cancelExpr().normalizeNode(normWARef)) {
}
} // RelSequence::rewriteNode()
// RelSequence::pullUpPreds() --------------------------------------------
// is redefined to disallow the pullup of most predicates from the
// operator's child. RelSequence can not pull up any predicates from
// its child since this will change the semantics of the sequence
// functions. The exception is that predicates which consist entirely
// of outer references (are covered by the inputs) can (and must) be
// pulled up. The reason that these predicates can be pulled up is
// that these types of predicates will either be TRUE for all rows and
// therefore have no affect on the result, or will be FALSE for all
// rows and the sequence will not produce any rows (so when the pred
// is pulled up, the request will not even get to sequence). The
// reason that these predicates must be pulled up is because if these
// preds become VEGPreds, they will not be handled properly if they
// cannot be pulled up and pushed to their source (see Genesis case:
// 10-070111-6157)
//
void
RelSequence::pullUpPreds()
{
// ---------------------------------------------------------------------
// Pull up predicates from the child.
// move them to my selection predicates
// ---------------------------------------------------------------------
// Only predicates that reference only input values can be pulled
// up.
ValueIdSet availableInputs = getGroupAttr()->getCharacteristicInputs();
ValueIdSet predicatesToPullUp;
ValueIdSet notUsed;
ValueIdSet predicatesThatStay;
GroupAttributes emptyGA;
// Find the set of predicates on the child which are covered by the inputs
// These predicates can be pulled up above the Sequence operator
//
emptyGA.coverTest(child(0)->selectionPred(),
availableInputs,
predicatesToPullUp,
notUsed,
&predicatesThatStay);
// If there are some predicates that can be pulled, then remove them
// from the child and add them to my selection predicates
//
if (NOT predicatesToPullUp.isEmpty())
{
selectionPred() += predicatesToPullUp;
child(0)->selectionPred() -= predicatesToPullUp;
}
// ---------------------------------------------------------------------
// WARNING: One rule that this procedure must follow is
// that recomputeOuterReferences() must be called on the children even
// if no predicates are pulled up from them. This is to correct
// the outer references that are added to a right child of a
// semi or outer join when processing subqueries in the ON clause.
// ---------------------------------------------------------------------
child(0)->recomputeOuterReferences();
} // RelSequence::pullUpPreds()
// RelSequence::recomputeOuterReferences() --------------------------------
// This method is used by the normalizer for recomputing the
// outer references (external dataflow input values) that are
// still referenced by each operator in the subquery tree
// after the predicate pull up is complete.
//
// Side Effects: sets the characteristicInputs of the groupAttr.
//
void RelSequence::recomputeOuterReferences()
{
// This is virtual method on RelExpr. When this is called it is
// assumed that the children have already been transformed. The
// required inputs of the child are therefore already minimal and
// sufficient. It is also assumed that the RelExpr itself has been
// bound. That implies that the group attributes have already been
// allocated and the required inputs is a sufficient (but not
// necessarilly minimum) set of external values needed to evaluate
// all expressions in this subtree.
//
// Delete all those input values that are no longer referenced on
// this operator because the predicates that reference them have
// been pulled up.
//
ValueIdSet outerRefs = getGroupAttr()->getCharacteristicInputs();
// The set of valueIds need by this node.
//
ValueIdSet allMyExpr(getSelectionPred());
allMyExpr.insertList(requiredOrder());
allMyExpr.insertList(partition());
allMyExpr += sequenceFunctions();
allMyExpr += sequencedColumns();
allMyExpr.insertList(cancelExpr());
// Remove from outerRefs those valueIds that are not needed
// by all my expressions
//
allMyExpr.weedOutUnreferenced(outerRefs);
// Add to outerRefs those that my children need.
//
outerRefs += child(0).getPtr()->getGroupAttr()->getCharacteristicInputs();
// set my Character Inputs to this new minimal set.
//
getGroupAttr()->setCharacteristicInputs(outerRefs);
} // RelSequence::recomputeOuterReferences()
// RelSequence::synthEstLogProp() ------------------------------------------
// synthesize estimated logical properties given a specific set of
// input log. properties.
//
// Parameters:
//
// EstLogPropSharedPtr inputEstLogProp
// IN : A set of input logical properties used to estimate the logical
// properities of this node.
//
void RelSequence::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
if (getGroupAttr()->isPropSynthesized(inputEstLogProp) == TRUE)
return;
// 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);
EstLogPropSharedPtr myEstProps =
synthEstLogPropForUnaryLeafOp(inputEstLogProp,
childEstProp->getColStats(),
childEstProp->getResultCardinality());
// Set the logical properties of this node.
//
getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);
} // RelSequence::synthEstLogProp
// RelSequence::synthLogProp ----------------------------------------------
// synthesize logical properties
//
void
RelSequence::synthLogProp(NormWA * normWAPtr)
{
// check to see whether properties are already synthesized.
//
if (getGroupAttr()->existsLogExprForSynthesis())
return;
// Synthesize log. properties for me and my children.
//
RelExpr::synthLogProp(normWAPtr);
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;
}
getGroupAttr()->addConstraints(nonRIConstraints);
getGroupAttr()->addSuitableRefOptConstraints
(child(0).getGroupAttr()->getConstraints());
} // RelSequence::synthLogProp()
// RelSequence::costMethod()
// Obtain a pointer to a CostMethod object providing access
// to the cost estimation functions for nodes of this type.
CostMethod*
PhysSequence::costMethod() const
{
static THREAD_P CostMethodRelSequence *m = NULL;
if (m == NULL)
m = new (GetCliGlobals()->exCollHeap()) CostMethodRelSequence();
return m;
} // PhysSequence::costMethod()
ValueIdList
RelSequence::mapSortKey(const ValueIdList &sortKey) const
{
ValueIdMap seqColsMap;
for(ValueId sequenceCol = sequencedColumns().init();
sequencedColumns().next(sequenceCol);
sequencedColumns().advance(sequenceCol)) {
CMPASSERT(sequenceCol.getItemExpr()->getOperatorType() == ITM_NOTCOVERED);
seqColsMap.addMapEntry(sequenceCol,
sequenceCol.getItemExpr()->child(0)->getValueId());
}
ValueIdList newSortKey;
seqColsMap.mapValueIdListUp(newSortKey, sortKey);
return newSortKey;
}
PhysicalProperty *
PhysSequence::synthPhysicalProperty(const Context *context,
const Lng32 pn,
PlanWorkSpace *pws)
{
// Call the default implementation
// (RelExpr::synthPhysicalProperty()) to synthesize the properties
// on the number of cpus.
//
PhysicalProperty* sppTemp = RelExpr::synthPhysicalProperty(context,pn,pws);
const PhysicalProperty * const sppOfChild =
context->getPhysicalPropertyOfSolutionForChild(0);
// ---------------------------------------------------------------------
// Replace the sort key in my spp.
// ---------------------------------------------------------------------
PhysicalProperty* sequencePP =
new (CmpCommon::statementHeap())
PhysicalProperty(*sppOfChild,
mapSortKey(sppOfChild->getSortKey()),
sppOfChild->getSortOrderType(),
sppOfChild->getDp2SortOrderPartFunc());
sequencePP->setCurrentCountOfCPUs(sppTemp->getCurrentCountOfCPUs());
delete sppTemp;
return sequencePP;
} // RelSequence::synthPhysicalProperty()
void
RelSequence::pushdownCoveredExpr(const ValueIdSet & outputExpr,
const ValueIdSet & newExternalInputs,
ValueIdSet & predicatesOnParent,
const ValueIdSet * setOfValuesReqdByParent,
Lng32 childIndex
)
{
ValueIdSet exprOnParent;
if(setOfValuesReqdByParent)
exprOnParent = *setOfValuesReqdByParent;
exprOnParent += sequenceFunctions();
exprOnParent.insertList(requiredOrder());
exprOnParent.insertList(partition());
ValueIdSet outputSet(outputExpr);
// Prune from the sequencedColumns() ValueIdSet, those expressions
// that are not needed above (in setOfValuesReqdByParent) or by
// the selectionPred.
//
ValueId refVal;
for(ValueId seqCol = sequencedColumns().init();
sequencedColumns().next(seqCol);
sequencedColumns().advance(seqCol)) {
if(!exprOnParent.referencesTheGivenValue(seqCol, refVal) &&
!selectionPred().referencesTheGivenValue(seqCol, refVal) &&
!outputSet.referencesTheGivenValue(seqCol, refVal)) {
sequencedColumns() -= seqCol;
}
}
exprOnParent += sequencedColumns();
RelExpr::pushdownCoveredExpr(outputSet,
newExternalInputs,
predicatesOnParent,
&exprOnParent,
childIndex
);
// predicatesOnParent has not been changed.
//
} // RelSequence::pushdownCoveredExpr
Context* RelSequence::createContextForAChild(Context* myContext,
PlanWorkSpace* pws,
Lng32& childIndex)
{
// ---------------------------------------------------------------------
// If one Context has been generated for each child, return NULL
// to signal completion.
// ---------------------------------------------------------------------
if (pws->getCountOfChildContexts() == getArity())
return NULL;
childIndex = 0;
Lng32 planNumber = 0;
const ReqdPhysicalProperty* rppForMe = myContext->getReqdPhysicalProperty();
PartitioningRequirement* partReqForMe =
rppForMe->getPartitioningRequirement();
// If a partitioning requirement exists and it requires broadcast
// replication, then return NULL now. Only an exchange operator
// can satisfy a broadcast replication partitioning requirement.
if ((partReqForMe != NULL) AND
partReqForMe->isRequirementReplicateViaBroadcast())
return NULL;
RequirementGenerator rg(child(0),rppForMe);
// ---------------------------------------------------------------------
// Add the order requirements needed for this RelSequence node
// ---------------------------------------------------------------------
// Remove any sort order requirement from parent.
//
rg.removeSortKey();
rg.removeArrangement();
rg.removeSortOrderTypeReq();
ValueIdList sortKey;
// Note for Bob W.
// Look into whether requiredOrder() can be used the same way for OLAP
// and TD Rank so this special case can be eliminated.
if( !getHasTDFunctions() )
{
sortKey.insert(partition());
}
sortKey.insert(requiredOrder());
// Shouldn't/Can't add a sort order type requirement
// if we are in DP2
if (rppForMe->executeInDP2())
rg.addSortKey(sortKey,NO_SOT);
else
rg.addSortKey(sortKey,ESP_SOT);
// Cannot execute in DP2
//
rg.addLocationRequirement(EXECUTE_IN_MASTER_OR_ESP);
Lng32 childNumPartsRequirement = ANY_NUMBER_OF_PARTITIONS;
float childNumPartsAllowedDeviation = 0.0;
NABoolean numOfESPsForced = FALSE;
if(okToAttemptESPParallelism(myContext,
pws,
childNumPartsRequirement,
childNumPartsAllowedDeviation,
numOfESPsForced) AND
(partition().entries() > 0)) {
rg.addPartitioningKey(partition());
if (NOT numOfESPsForced)
rg.makeNumOfPartsFeasible(childNumPartsRequirement,
&childNumPartsAllowedDeviation);
rg.addNumOfPartitions(childNumPartsRequirement,
childNumPartsAllowedDeviation);
} else {
// Cannot execute in parallel
//
rg.addNumOfPartitions(1);
}
// ---------------------------------------------------------------------
// Done adding all the requirements together, now see whether it worked
// and give up if it is not possible to satisfy them
// ---------------------------------------------------------------------
if (NOT rg.checkFeasibility())
return NULL;
// ---------------------------------------------------------------------
// Compute the cost limit to be applied to the child.
// ---------------------------------------------------------------------
CostLimit* costLimit = computeCostLimit(myContext, pws);
// ---------------------------------------------------------------------
// Get a Context for optimizing the child.
// Search for an existing Context in the CascadesGroup to which the
// child belongs that requires the same properties as those in
// rppForChild. Reuse it, if found. Otherwise, create a new Context
// that contains rppForChild as the required physical properties..
// ---------------------------------------------------------------------
Context* result = shareContext(
childIndex,
rg.produceRequirement(),
myContext->getInputPhysicalProperty(),
costLimit,
myContext,
myContext->getInputLogProp());
// ---------------------------------------------------------------------
// Store the Context for the child in the PlanWorkSpace.
// ---------------------------------------------------------------------
pws->storeChildContext(childIndex, planNumber, result);
return result;
} // RelSequence::createContextForAChild()
// Helper routine for reporting errors with sequence functions. Called
// by RelRoot::bindNode().
//
static NABoolean checkUnresolvedSequenceFunctions(BindWA *bindWA)
{
ValueIdSet &seqs =
bindWA->getCurrentScope()->getUnresolvedSequenceFunctions();
if (seqs.isEmpty())
return FALSE; // no error
NAString unparsed(CmpCommon::statementHeap());
for (ValueId vid = seqs.init(); seqs.next(vid); seqs.advance(vid)) {
ItemExpr *ie = vid.getItemExpr();
unparsed += ", ";
ie->unparse(unparsed, DEFAULT_PHASE, USER_FORMAT_DELUXE);
}
unparsed.remove(0,2); // remove initial ", "
// 4109 Sequence functions placed incorrectly.
*CmpCommon::diags() << DgSqlCode(-4109) << DgString0(unparsed);
bindWA->setErrStatus();
return TRUE;
} // checkUnresolvedSequenceFunctions()
// RelSequence::bindNode - Bind the RelSequence node.
//
RelExpr *RelSequence::bindNode(BindWA *bindWA)
{
// If this node has already been bound, we are done.
//
if (nodeIsBound())
return this;
CMPASSERT(!bindWA->getCurrentScope()->getSequenceNode());
bindWA->getCurrentScope()->getSequenceNode()= this;
// Add sequence functions found in parent node(s)
//
sequenceFunctions() +=
bindWA->getCurrentScope()->getUnresolvedSequenceFunctions();
bindWA->getCurrentScope()->getUnresolvedSequenceFunctions().clear();
bindWA->getCurrentScope()->getAllSequenceFunctions().clear();
// Bind the child nodes.
//
bindChildren(bindWA);
if (bindWA->errStatus())
return this;
ItemExpr * partitionChange =NULL;
ValueIdList change;
if(partitionBy_) {
partitionChange = partitionBy_->copyTree(bindWA->getCurrentScope()->collHeap());
bindWA->getCurrentScope()->context()->inOrderBy() = TRUE;
partitionBy_->convertToValueIdList(partition(),
bindWA,
ITM_ITEM_LIST);
bindWA->getCurrentScope()->context()->inOrderBy() = FALSE;
if(bindWA->errStatus())
return this;
partitionBy_ = NULL;
}
ItemExpr *requiredOrderTree = removeRequiredOrderTree();
if(requiredOrderTree) {
bindWA->getCurrentScope()->context()->inOrderBy() = TRUE;
requiredOrderTree->convertToValueIdList(requiredOrder(),
bindWA,
ITM_ITEM_LIST);
bindWA->getCurrentScope()->context()->inOrderBy() = FALSE;
/*
//
// Next, we enforce the restriction that no CZECH collation
// is involved in any of the SEQUENCE BY columns/expressions.
//
for (CollIndex i = 0; i < requiredOrder().entries(); i++)
{
ValueId ValId = requiredOrder()[i];
NABuiltInTypeEnum TQ = ValId.getType().getTypeQualifier();
if ( TQ == NA_CHARACTER_TYPE)
{
const CharType& ColumnCT = (const CharType&)ValId.getType();
if ( ColumnCT.getCollation() == CharInfo::CZECH_COLLATION )
{
*CmpCommon::diags() << DgSqlCode(-4380)
<< DgString0(ColumnCT.getCollationName());
bindWA->setErrStatus();
}
}
}
*/
if(bindWA->errStatus())
return this;
}
// if unresolved sequence functions have been found in the children
// of the Sequence node or in the requiredOrderTree, that would mean
// that we are referencing sequence functions before the sequence
// operation is performed If this is the case, the following method
// will issue the error.
//
if (checkUnresolvedSequenceFunctions(bindWA))
return this;
// Construct the RETDesc for this node.
//
RETDesc *resultTable = new(bindWA->wHeap()) RETDesc(bindWA);
// Add the columns from the child to the RETDesc.
//
const RETDesc &childTable = *child(0)->getRETDesc();
if(childTable.isGrouped())
resultTable->setGroupedFlag();
const ColumnDescList *sysColList = childTable.getSystemColumnList();
const ColumnDescList *colList = childTable.getColumnList();
CollIndex i = 0;
for(i = 0; i < sysColList->entries(); i++)
{
ValueId columnValueId = sysColList->at(i)->getValueId();
ItemExpr *newColumn = new (bindWA->wHeap())
NotCovered (columnValueId.getItemExpr());
newColumn->synthTypeAndValueId();
resultTable->addColumn(bindWA,
sysColList->at(i)->getColRefNameObj(),
newColumn->getValueId(),
SYSTEM_COLUMN,
sysColList->at(i)->getHeading());
if(sysColList->at(i)->isGrouped()) {
ColumnNameMap *cnm =
resultTable->findColumn(sysColList->at(i)->getColRefNameObj());
cnm->getColumnDesc()->setGroupedFlag();
}
sequencedColumns() += newColumn->getValueId();
}
ValueIdMap ncToOldMap;
for(i = 0; i < colList->entries(); i++)
{
ValueId columnValueId = colList->at(i)->getValueId();
ItemExpr *newColumn = new (bindWA->wHeap())
NotCovered (columnValueId.getItemExpr());
newColumn->synthTypeAndValueId();
newColumn->setOrigOpType(columnValueId.getItemExpr()->origOpType());
resultTable->addColumn(bindWA,
colList->at(i)->getColRefNameObj(),
newColumn->getValueId(),
USER_COLUMN,
colList->at(i)->getHeading());
ncToOldMap.addMapEntry(newColumn->getValueId(),columnValueId);
if(colList->at(i)->isGrouped()) {
ColumnNameMap *cnm =
resultTable->findColumn(colList->at(i)->getColRefNameObj());
cnm->getColumnDesc()->setGroupedFlag();
}
sequencedColumns() += newColumn->getValueId();
}
// Set the return descriptor
//
setRETDesc(resultTable);
bindWA->getCurrentScope()->setRETDesc(resultTable);
//bindWA->getCurrentScope()->getSequenceNode() = this;
if (!bindWA->getCurrentScope()->context()->inUpsertXform())
{
if(partitionChange) {
partitionChange->convertToValueIdList(partitionChange_, bindWA, ITM_ITEM_LIST);
}
if(bindWA->errStatus())
return this;
partitionChange = NULL;
/// bindWA->getCurrentScope()->setTDPartitionChange(change);
}
// -- MV
// Bind the cancel expression.
//
if (cancelExprTree_)
{
cancelExprTree_->convertToValueIdList(cancelExpr(), bindWA, ITM_ITEM_LIST);
cancelExprTree_ = NULL;
if (bindWA->errStatus()) {
delete resultTable;
return NULL;
}
}
//
// bind the qualify predicates and attach the resulting value id set
// to the node (as a selection predicate on the sequence node)
//
ItemExpr *qualifyPred = removeSelPredTree();
if (qualifyPred) {
bindWA->getCurrentScope()->context()->inQualifyClause() = TRUE;
qualifyPred->convertToValueIdSet(selectionPred(), bindWA, ITM_AND);
bindWA->getCurrentScope()->context()->inQualifyClause() = FALSE;
if (bindWA->errStatus()) return this;
}
bindWA->getCurrentScope()->getSequenceNode() = NULL;
//
// Bind the base class.
//
RelExpr *boundExpr = bindSelf(bindWA);
if (bindWA->errStatus())
return boundExpr;
// Register this node in current scope. Later (in
// RelRoot::bindNode()), the unresolved sequence functions will be
// attached to this registered node.
//
//CMPASSERT(!bindWA->getCurrentScope()->getSequenceNode());
bindWA->getCurrentScope()->getSequenceNode() = boundExpr;
// save the ncToOldmap in the current scope. It will be used in Insert::bindnode for a special case.
bindWA->getCurrentScope()->setNCToOldMap( ncToOldMap);
return boundExpr;
} // RelSequence::bindNode()
NABoolean PhysSequence::isBigMemoryOperator(const Context* context,
const Lng32 planNumber)
{
const double memoryLimitPerCPU = CURRSTMT_OPTDEFAULTS->getMemoryLimitPerCPU();
// The Sequence operator allocates the history buffer in memory.
//
const ReqdPhysicalProperty* rppForMe = context->getReqdPhysicalProperty();
// Start off assuming that the sort will use all available CPUs.
Lng32 cpuCount = rppForMe->getCountOfAvailableCPUs();
PartitioningRequirement* partReq = rppForMe->getPartitioningRequirement();
const PhysicalProperty* spp = context->getPlan()->getPhysicalProperty();
Lng32 numOfStreams;
// If the physical properties are available, then this means we
// are on the way back up the tree. Get the actual level of
// parallelism from the spp to determine if the number of cpus we
// are using are less than the maximum number available.
if (spp != NULL)
{
PartitioningFunction* partFunc = spp->getPartitioningFunction();
numOfStreams = partFunc->getCountOfPartitions();
if (numOfStreams < cpuCount)
cpuCount = numOfStreams;
}
else
if ((partReq != NULL) AND
(partReq->getCountOfPartitions() != ANY_NUMBER_OF_PARTITIONS))
{
// If there is a partitioning requirement, then this may limit
// the number of CPUs that can be used.
numOfStreams = partReq->getCountOfPartitions();
if (numOfStreams < cpuCount)
cpuCount = numOfStreams;
}
EstLogPropSharedPtr inLogProp = context->getInputLogProp();
const double probeCount =
MIN_ONE(inLogProp->getResultCardinality().value());
CMPASSERT ( numHistoryRows() > 0 &&
getEstHistoryRowLength() > 0);
const double historyBufferRowCount = MIN_ONE(numHistoryRows());
const double rowsPerCpu = MIN_ONE(historyBufferRowCount / cpuCount);
const double rowsPerCpuPerProbe = MIN_ONE(rowsPerCpu / probeCount);
const Lng32 bufferWidth = estHistoryRowLength_;
const double bufferSizePerCpu = (rowsPerCpuPerProbe / 1024. * bufferWidth);
return (bufferSizePerCpu >= memoryLimitPerCPU);
} // PhysSequence::isBigMemoryOperator
// addUnResolvedSeqFunctions - Add unresolved sequence functions to
// the RelSequence operator found when binding the select list. Also,
// if OLAP, bind the partition by and order by clauses and add to
// RelSequence.
//
void RelSequence::addUnResolvedSeqFunctions(ValueIdSet &unresolvedSeqFuncs,
BindWA *bindWA)
{
// Add the unresolved sequence functions to this RelSequence
//
sequenceFunctions_ += unresolvedSeqFuncs;
if(sequenceFunctions_.entries()) {
if (CmpCommon::getDefault(OLAP_CAN_INVERSE_ORDER) == DF_ON)
{
NABoolean mustInv = FALSE;
NABoolean canInv = TRUE;
Lng32 precedingMax = 0;
Lng32 followingMax = 0;
for(ValueId sfId = sequenceFunctions_.init();
sequenceFunctions_.next(sfId); sequenceFunctions_.advance(sfId))
{
ItmSequenceFunction * sf = (ItmSequenceFunction *)(sfId.getItemExpr());
if(!sf->isOLAP())
{
canInv = FALSE;
continue;
}
ItmSeqOlapFunction *olapF = (ItmSeqOlapFunction *)sf;
precedingMax = MAXOF(precedingMax, -(olapF->getframeStart()));
followingMax = MAXOF(followingMax, olapF->getframeEnd());
if(!olapF->canInverseOLAPOrder()) {
canInv = FALSE;
continue;
}
if(olapF->mustInverseOLAPOrder()) {
mustInv = TRUE;
}
}
NABoolean shouldInv = (canInv && (followingMax > precedingMax));
if((mustInv && canInv) || shouldInv) {
for(ValueId sfId = sequenceFunctions_.init();
sequenceFunctions_.next(sfId); sequenceFunctions_.advance(sfId))
{
ItmSeqOlapFunction * olapF = (ItmSeqOlapFunction *)(sfId.getItemExpr());
olapF->inverseOLAPOrder(bindWA->wHeap());
}
} else if(mustInv && !canInv) {
*CmpCommon::diags() << DgSqlCode(-4349);
bindWA->setErrStatus();
return;
}
}
// Get the first sequence function
//
ValueId sFunc = sequenceFunctions_.init();
sequenceFunctions_.next(sFunc);
ItemExpr *ie = sFunc.getItemExpr();
CMPASSERT(ie->isASequenceFunction());
ItmSequenceFunction *seqFunc = (ItmSequenceFunction *)ie;
// If it is OLAP, the bind the partition by and order by clauses.
// and attach them to this RelSequence operator.
//
if( getHasOlapFunctions() || getHasTDFunctions() ) {
// only for OLAP.
// for TD, the partition by is already bound
if( getHasOlapFunctions() )
{
CMPASSERT(partition().entries() == 0);
}
CMPASSERT(requiredOrder().entries() == 0);
ItemExpr * prevSF = NULL;
for(ValueId sfId = sequenceFunctions_.init();
sequenceFunctions_.next(sfId); sequenceFunctions_.advance(sfId))
{
ItemExpr * ie = sfId.getItemExpr();
CMPASSERT(ie->isASequenceFunction());
ItmSequenceFunction *sf = (ItmSequenceFunction *)ie;
if (
(getHasTDFunctions() && !sf->isTDFunction())
||
(getHasOlapFunctions() && !sf->isOLAP())
)
{ //Using rank function and sequence functions together
// in the same query scope is not supported.
*CmpCommon::diags() << DgSqlCode(-4367);
bindWA->setErrStatus();
return;
}
if ((!getHasOlapFunctions()) && prevSF)
if (!sf->hasBaseEquivalence(prevSF))
{ //All rank functions need to have the same expression as argument
*CmpCommon::diags() << DgSqlCode(-4361);
bindWA->setErrStatus();
return;
}
prevSF = sf;
}
// Bind the order by and partition by using the child's
// RETDesc.
//
BindScope *currScope = bindWA->getCurrentScope();
RETDesc *myRETDesc = getRETDesc();
RETDesc *currentRETDesc = currScope->getRETDesc();
setRETDesc(child(0)->getRETDesc());
currScope->setRETDesc(child(0)->getRETDesc());
if (getHasOlapFunctions()) {
ItemExpr *partitionBy = seqFunc->getOlapPartitionBy();
if(partitionBy) {
bindWA->getCurrentScope()->context()->inOlapPartitionBy() = TRUE;
partitionBy->convertToValueIdList(partition(),
bindWA,
ITM_ITEM_LIST);
bindWA->getCurrentScope()->context()->inOlapPartitionBy() = FALSE;
if(bindWA->errStatus())
return;
}
}
ItemExpr *orderBy = seqFunc->getOlapOrderBy();
if(orderBy) {
if( seqFunc->isTDFunction() ) {
// TBD: Try to do requiredOrder()=partition() somewhere else to
// make it consistent with OLAP
requiredOrder()=partition();
orderBy = orderBy->changeDefaultOrderToDesc();
}
// if for OLAP and there's a double inverse, remove them.
else if( seqFunc->isOLAP() ) {
NABoolean inv = FALSE;
orderBy = orderBy->removeInverseFromExprTree(inv,TRUE);
}
bindWA->getCurrentScope()->context()->inOrderBy() = TRUE;
bindWA->getCurrentScope()->context()->inOlapOrderBy() = TRUE;
orderBy->convertToValueIdList(requiredOrder(),
bindWA,
ITM_ITEM_LIST);
bindWA->getCurrentScope()->context()->inOrderBy() = FALSE;
bindWA->getCurrentScope()->context()->inOlapOrderBy() = FALSE;
if(bindWA->errStatus())
return;
// NULLs are in a different order for TD
if( seqFunc->isTDFunction() ) {
ValueIdList newOrder;
ValueId valId;
for (CollIndex i=0; i<requiredOrder().entries(); i++)
{
valId = requiredOrder()[i];
NAType * type = valId.getType().newCopy();
if (type->supportsSQLnull())
{
ItemExpr * ie =NULL;
if (valId.getItemExpr()->getOperatorType() == ITM_INVERSE)
{
ie = valId.getItemExpr()->removeInverseOrder();
}
else
{
ie = valId.getItemExpr();
}
ItemExpr * constZero = new (bindWA->wHeap()) ConstValue(0);
ItemExpr * constOne = new (bindWA->wHeap()) ConstValue(1);
ItemExpr *isnull = new HEAP UnLogic (ITM_IS_NULL, ie);
ItemExpr *ifthenelse = new (bindWA->wHeap()) IfThenElse(isnull, constZero, constOne);
ItemExpr * newExpr = new (bindWA->wHeap()) Case(NULL, ifthenelse);
newExpr = new (bindWA->wHeap())
Cast(newExpr,
((ConstValue *) constZero)->getType()->newCopy());
if (valId.getItemExpr()->getOperatorType() == ITM_INVERSE)
{
newExpr = new (bindWA->wHeap()) InverseOrder( newExpr);
}
newExpr->synthTypeAndValueId(TRUE);
newOrder.insert(newExpr->getValueId());
newOrder.insert(valId);
}
else
{
newOrder.insert(valId);
}
}
requiredOrder()= newOrder;
}
}
// Return the RETDescs to their proper values.
//
setRETDesc(myRETDesc);
currScope->setRETDesc(currentRETDesc);
}
}
}
void PhysSequence::setNumHistoryRows(Lng32 numHistoryRows)
{
numHistoryRows_ = numHistoryRows;
};
void PhysSequence::setUnboundedFollowing(NABoolean v)
{
unboundedFollowing_ = v;
};
NABoolean PhysSequence::getUnboundedFollowing() const
{
return unboundedFollowing_;
};
void PhysSequence::setMinFollowingRows(Lng32 v)
{
minFollowingRows_ = v;
};
void PhysSequence::computeAndSetMinFollowingRows(Lng32 v)
{
if ( v > minFollowingRows_ )
minFollowingRows_ = v;
};
Lng32 PhysSequence::getMinFollowingRows() const
{
return minFollowingRows_;
};
void PhysSequence::estimateHistoryRowLength(const ValueIdSet &sequenceFunctions,
const ValueIdSet &outputFromChild,
ValueIdSet &histIds,
Lng32 &estimatedLength)
{
ValueIdSet children;
for(ValueId valId = sequenceFunctions.init();
sequenceFunctions.next(valId);
sequenceFunctions.advance(valId))
{
if(valId.getItemExpr()->isASequenceFunction())
{
ItemExpr *itmExpr = valId.getItemExpr();
switch(itmExpr->getOperatorType())
{
case ITM_OLAP_LEAD:
case ITM_OLAP_LAG:
case ITM_OFFSET:
case ITM_ROWS_SINCE:
case ITM_THIS:
case ITM_NOT_THIS:
updateEstHistoryRowLength(histIds,
itmExpr->child(0)->getValueId(),
estimatedLength);
//estimatedLength += itmExpr->child(0)->getValueId().getType().getTotalSize();
break;
case ITM_RUNNING_SUM:
case ITM_RUNNING_COUNT:
case ITM_RUNNING_MIN:
case ITM_RUNNING_MAX:
case ITM_LAST_NOT_NULL:
updateEstHistoryRowLength(histIds,
itmExpr->getValueId(),
estimatedLength);
break;
case ITM_OLAP_SUM:
case ITM_OLAP_COUNT:
case ITM_OLAP_RANK:
case ITM_OLAP_DRANK:
{
ItemExpr * tmpExpr =
((ItmSeqOlapFunction *)itmExpr)->transformOlapFunction(
CmpCommon::statementHeap());
tmpExpr->synthTypeAndValueId(TRUE);
CMPASSERT (valId != tmpExpr->getValueId());
children += tmpExpr->getValueId();
}
break;
case ITM_OLAP_MIN:
case ITM_OLAP_MAX:
{
if (((ItmSeqOlapFunction *)itmExpr)->getframeStart() < 0 &&
((ItmSeqOlapFunction *)itmExpr)->getframeEnd() >0)
{
ItemExpr * tmpExpr =
((ItmSeqOlapFunction *)itmExpr)->transformOlapFunction(
CmpCommon::statementHeap());
tmpExpr->synthTypeAndValueId(TRUE);
CMPASSERT (valId != tmpExpr->getValueId());
children += tmpExpr->getValueId();
}
else
{
updateEstHistoryRowLength(histIds,
itmExpr->getValueId(),
estimatedLength);
updateEstHistoryRowLength(histIds,
itmExpr->child(0)->getValueId(),
estimatedLength);
//estimatedLength += itmExpr->getValueId().getType().getTotalSize(); //
//estimatedLength += itmExpr->child(0)->getValueId().getType().getTotalSize(); //
}
}
break;
case ITM_MOVING_MIN:
case ITM_MOVING_MAX:
{
updateEstHistoryRowLength(histIds,
itmExpr->getValueId(),
estimatedLength);
updateEstHistoryRowLength(histIds,
itmExpr->child(0)->getValueId(),
estimatedLength);
//estimatedLength += itmExpr->getValueId().getType().getTotalSize(); //
//estimatedLength += itmExpr->child(0)->getValueId().getType().getTotalSize(); //
}
break;
case ITM_RUNNING_CHANGE:
if (itmExpr->child(0)->getOperatorType() == ITM_ITEM_LIST)
{
ExprValueId treePtr = itmExpr->child(0);
ItemExprTreeAsList changeValues(&treePtr,
ITM_ITEM_LIST,
RIGHT_LINEAR_TREE);
CollIndex nc = changeValues.entries();
for (CollIndex i = nc; i > 0; i--)
{
updateEstHistoryRowLength(histIds,
changeValues[i-1]->getValueId(),
estimatedLength);
}
}
else
{
updateEstHistoryRowLength(histIds,
itmExpr->child(0)->getValueId(),
estimatedLength);
// estimatedLength += itmExpr->child(0)->getValueId().getType().getTotalSize();
}
updateEstHistoryRowLength(histIds,
itmExpr->getValueId(),
estimatedLength);
//estimatedLength += itmExpr->getValueId().getType().getTotalSize();
break;
default:
CMPASSERT(0);
}
}
// Gather all the children, and if not empty, recurse down to the
// next level of the tree.
//
for(Lng32 i = 0; i < valId.getItemExpr()->getArity(); i++)
{
if (!outputFromChild.contains(valId.getItemExpr()->child(i)->getValueId()))
{
children += valId.getItemExpr()->child(i)->getValueId();
}
}
}
if (!children.isEmpty())
{
estimateHistoryRowLength(children,
outputFromChild,
histIds,
estimatedLength);
}
}
void PhysSequence::retrieveCachedHistoryInfo(RelSequence * cacheRelSeq)
{
ValueIdSet outputFromChild = child(0).getGroupAttr()->getCharacteristicOutputs();
if (!cacheRelSeq->getHistoryInfoCached())
{
Lng32 unableToCalculate = 0;
Lng32 estimatedRowLength = 0;
Lng32 numHistoryRows = 0;
NABoolean unboundedFollowing = 0;
Lng32 minFollowing = 0;
ValueIdSet histIds;
estimateHistoryRowLength( sequenceFunctions(),
outputFromChild,
histIds,
estimatedRowLength);
if (sequencedColumns().entries()>0)
{
estimatedRowLength += sequencedColumns().getRowLength();
}
if (partition().entries()>0)
{
estimatedRowLength += partition().getRowLength();
}
estimatedRowLength += sequenceFunctions().getRowLength();
cacheRelSeq->setCachedHistoryRowLength(estimatedRowLength);
computeHistoryRows(sequenceFunctions(),
numHistoryRows,
unableToCalculate,
unboundedFollowing,
minFollowing,
outputFromChild);
if(unableToCalculate || numHistoryRows ==0)
{
numHistoryRows = getDefault(DEF_MAX_HISTORY_ROWS);
}
cacheRelSeq->setCachedNumHistoryRows( numHistoryRows);
cacheRelSeq->setCachedUnboundedFollowing(unboundedFollowing);
cacheRelSeq->setCachedMinFollowingRows(minFollowing);
cacheRelSeq->setHistoryInfoCached(TRUE);
}
CMPASSERT(cacheRelSeq->getCachedHistoryRowLength() != 0 &&
cacheRelSeq->getCachedNumHistoryRows() != 0 &&
cacheRelSeq->getHistoryInfoCached());
estHistoryRowLength_ = cacheRelSeq->getCachedHistoryRowLength();
numHistoryRows_ = cacheRelSeq->getCachedNumHistoryRows();
unboundedFollowing_ = cacheRelSeq->getCachedUnboundedFollowing();
minFollowingRows_ = cacheRelSeq->getCachedMinFollowingRows();
}