blob: 850f8196f40cef52a390e61e77cac0be6a142efb [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: memo.C
* Description: Cascades optimizer search engine
* implementation for methods on the CascadesGroup and CascadesMemo
* classes (everything that has to do with storing
* equivalence classes of expressions). These classes
* are defined in opt.h
* Created: 7/22/94
* Language: C++
*
*
*
*
******************************************************************************
*/
//<pb>
// -----------------------------------------------------------------------
#include "Sqlcomp.h"
#include "GroupAttr.h"
#include "PhyProp.h"
#include "Cost.h"
#include "opt.h"
#include "RelScan.h"
#include "CmpStatement.h"
//<pb>
// -----------------------------------------------------------------------
// Methods for class CascadesGroup
// -----------------------------------------------------------------------
CascadesGroup::CascadesGroup(CascadesGroupId groupId,
GroupAttributes *groupAttr):
plans_(CmpCommon::statementHeap()),
goals_(CmpCommon::statementHeap()),
exploredRules_(CmpCommon::statementHeap())
{
// empty linked lists, as yet
logExprs_ = NULL;
physExprs_ = NULL;
exploredInPass_ = 0;
isBelowRoot_=FALSE;
exploredRules_.clear();
// set group attributes and pattern memory
groupAttr_ = groupAttr;
CMPASSERT(groupAttr_ != NULL);
groupAttr_->incrementReferenceCount();
// create back pointer through CascadesMemo to self
groupId_ = groupId;
} // CascadesGroup::CascadesGroup
//<pb>
CascadesGroup::~CascadesGroup()
{
// delete the expression and pattern list
RelExpr *next_expr = logExprs_;
while (next_expr != NULL)
{
RelExpr *next_next_expr = next_expr->getNextInGroup();
// if this expression wasn't taken out of CascadesMemo, delete it
if (next_expr->getGroupId() != INVALID_GROUP_ID)
delete next_expr;
else
next_expr->setNextInGroup(NULL);
next_expr = next_next_expr;
}
next_expr = physExprs_;
while (next_expr != NULL)
{
RelExpr *next_next_expr = next_expr->getNextInGroup();
if (next_expr->getGroupId() != INVALID_GROUP_ID)
delete next_expr;
else
next_expr->setNextInGroup(NULL);
next_expr = next_next_expr;
}
// Group Attributes
if (groupAttr_ != NULL)
groupAttr_->decrementReferenceCount();
// delete associated contexts
#pragma nowarn(1506) // warning elimination
Lng32 maxc = goals_.entries();
#pragma warn(1506) // warning elimination
for (Lng32 i = 0; i < maxc; i++)
delete goals_[i];
} // CascadesGroup::~CascadesGroup
//<pb>
// LCOV_EXCL_START
void CascadesGroup::print(FILE * f, const char * prefix, const char *) const
{
} // CascadesGroup::print
// LCOV_EXCL_STOP
//<pb>
void CascadesGroup::addLogExpr(RelExpr * expr, RelExpr *src)
{
CMPASSERT(expr->isLogical());
expr->setGroupId(groupId_);
// This is a kludge, I will need to find a better place to put this in.
// We need to make sure that the available indexes of the group picks up
// the indexes of any scan inserted to the group.
if (expr->getOperatorType() == REL_SCAN)
{
Scan* scanNode = (Scan*) expr;
// if this scanNode was created by FilterRule0 and this expression is
// not an duplicate then we must have some properties that requires us
// to re-evaluate the indexes and their applicability
if(scanNode->isIndexInfoForced() == FALSE) scanNode->removeIndexInfo();
scanNode->addIndexInfo();
getGroupAttr()->addToAvailableBtreeIndexes(scanNode->deriveIndexOnlyIndexDesc());
getGroupAttr()->addToAvailableBtreeIndexes(scanNode->deriveIndexJoinIndexDesc());
}
// 12 years later, and the kludge continues
if (expr->getArity() == 1 &&
CmpCommon::getDefault(GA_PROP_INDEXES_ARITY_1) == DF_ON)
{
expr->getGroupAttr()->addToAvailableBtreeIndexes
(expr->child(0).getGroupAttr()->getAvailableBtreeIndexes());
}
// reconcile group attributes and logical properties, if necessary
// if the group attributes are not already the same, merge them
if (getGroupAttr() != expr->getGroupAttr())
{
CMPASSERT(expr->getGroupAttr() != NULL);
// improve log prop's/group attributes; recost all plans if required
if (expr->reconcileGroupAttr(getGroupAttr()))
// XXX recost all plans in this group
ABORT("reoptimizing after improveLogProp() not implemented"); // LCOV_EXCL_LINE
}
// remember that expr came from src
if (expr)
expr->setCascadesTraceInfo(src);
// insert new expression into the list
if (CmpCommon::getDefault(COMP_BOOL_19) == DF_ON)
{
expr->setNextInGroup(logExprs_);
logExprs_ = expr;
return;
}
if (!logExprs_)
{
expr->setNextInGroup(logExprs_);
logExprs_ = expr;
return;
}
RelExpr* currExpr = logExprs_;
if(CmpCommon::getDefault(COMP_BOOL_120) == DF_ON)
{
while(currExpr->getNextInGroup())
{
currExpr = currExpr->getNextInGroup();
}
currExpr->setNextInGroup(expr);
expr->setNextInGroup(NULL);
}
else{
RelExpr * prevExpr = currExpr;
while(currExpr)
{
if(expr->getPotential() < currExpr->getPotential())
{
prevExpr = currExpr;
currExpr = currExpr->getNextInGroup();
}
else
break;
}
if(prevExpr == currExpr)
{
logExprs_ = expr;
expr->setNextInGroup(currExpr);
}
else
{
prevExpr->setNextInGroup(expr);
expr->setNextInGroup(currExpr);
}
}
} // CascadesGroup::addLogExpr
NABoolean CascadesGroup::addPhysExpr(RelExpr* & expr, RelExpr *src)
{
CMPASSERT(expr->isPhysical());
// set the common values of a group
expr->setGroupId(groupId_);
expr->setGroupAttr(groupAttr_);
// pass along logExpr's isinCS attribute
if (getFirstLogExpr())
expr->setBlockStmt(getFirstLogExpr()->isinBlockStmt());
// insert new expression into the list
expr->setNextInGroup(physExprs_);
physExprs_ = expr;
// remember that expr came from src
if (expr)
expr->setCascadesTraceInfo(src);
return TRUE;
} // CascadesGroup::addLogExpr
//<pb>
void CascadesGroup::addPlan (CascadesPlan * plan)
{
CURRSTMT_OPTGLOBALS->plans_count++; // increment global counter for # of plans
#pragma nowarn(1506) // warning elimination
Lng32 index = plans_.entries();
#pragma warn(1506) // warning elimination
plans_.insertAt(index,plan); // insert plan at end of list
}
RelExpr * CascadesGroup::unlinkLogExpr(RelExpr *expr)
{
// -------------------------------------------------------------
// unlink e from the list of expressions in its group
// (NOTE: this is almost a clone of CascadesGroup::unlinkPhysExpr())
// -------------------------------------------------------------
RelExpr *pred = logExprs_;
if (pred == expr)
{
// expr is the first expression in the list, replace the anchor
logExprs_ = expr->getNextInGroup();
}
else
{
// find the predecessor of e in the list
while (pred != NULL AND
pred->getNextInGroup() != expr)
{
pred = pred->getNextInGroup();
}
if (pred == NULL)
// expr not found in this group
return NULL;
// replace the predecessor's link with e's link
pred->setNextInGroup(expr->getNextInGroup());
}
// return the unlinked expression (same as input parameter, just
// to remind the calling procedure that it owns that expression now)
return expr;
}
//<pb>
// A dangling method
// LCOV_EXCL_START
RelExpr * CascadesGroup::unlinkPhysExpr(RelExpr *expr)
{
// -------------------------------------------------------------
// unlink e from the list of expressions in its group
// (NOTE: this is almost a clone of CascadesGroup::unlinkLogExpr())
// -------------------------------------------------------------
RelExpr *pred = physExprs_;
if (pred == expr)
{
// expr is the first expression in the list, replace the anchor
physExprs_ = expr->getNextInGroup();
}
else
{
// find the predecessor of e in the list
while (pred != NULL AND
pred->getNextInGroup() != expr)
{
pred = pred->getNextInGroup();
}
if (pred == NULL)
// expr not found in this group
return NULL;
// replace the predecessor's link with e's link
pred->setNextInGroup(expr->getNextInGroup());
}
// return the unlinked expression (same as input parameter, just
// to remind the calling procedure that it owns that expression now)
return expr;
}
// LCOV_EXCL_STOP
//<pb>
CascadesPlan * CascadesGroup::getFirstPlan() const
{
if (plans_.entries() > 0)
return plans_[0];
else
return NULL;
}
RelExpr * CascadesGroup::getLastLogExpr() const
{
if (logExprs_ != NULL) // any logical exprs?
{
RelExpr * expr = logExprs_;
while (expr->getNextInGroup() != NULL) // last expr?
expr = expr->getNextInGroup();
return expr;
}
else
return NULL;
}
// use by OptDebug::showMemoStats(). Mask it out from code coverage
// // LCOV_EXCL_START
Lng32 CascadesGroup::getCountOfLogicalExpr() const
{
RelExpr * expr = logExprs_;
Lng32 count = 0;
while (expr != NULL)
{
count++;
expr = expr->getNextInGroup();
}
return count;
}
//<pb>
Lng32 CascadesGroup::getCountOfPhysicalExpr() const
{
RelExpr * expr = physExprs_;
Lng32 count = 0;
while (expr != NULL)
{
count++;
expr = expr->getNextInGroup();
}
return count;
}
double CascadesGroup::calculateNoOfLogPlans() const
{
RelExpr * expr = logExprs_;
double result = 0;
Lng32 numOfMergedExprs = 0;
while (expr != NULL)
{
result += expr->calculateNoOfLogPlans(numOfMergedExprs);
expr = expr->getNextInGroup();
}
// Add in any logical expressions that are derived from
// expressions that participated in a group merge.
result = result + (result*numOfMergedExprs);
return result;
}
// LCOV_EXCL_STOP
HashValue CascadesGroup::hash()
{
// take hash value of logical properties
HashValue hash_value = getGroupAttr()->hash();
return ( hash_value );
} // CascadesGroup::hash
//<pb>
void CascadesGroup::merge(CascadesGroup * other)
{
// a merged group gets the smaller of the two indices ...
// ... to ensure proper deletion in CascadesMemo::~CascadesMemo()
if (other->groupId_ < groupId_)
{
other->merge(this);
}
else
{
CMPASSERT(other->groupId_ != groupId_);
// -----------------------------------------------------------------
// insert all logical and physical expressions of "other" into "this"
// -----------------------------------------------------------------
RelExpr * move_expr;
RelExpr * next_expr;
for (next_expr = other->logExprs_;
(move_expr = next_expr) != NULL; )
{
next_expr = next_expr->getNextInGroup();
addLogExpr(move_expr);
} // insert all expressions of "other" into "this"
for (next_expr = other->physExprs_;
(move_expr = next_expr) != NULL; )
{
next_expr = next_expr->getNextInGroup();
addPhysExpr(move_expr);
} // insert all expressions of "other" into "this"
other->logExprs_ = NULL; // to preserve moved expressions
other->physExprs_ = NULL;
// -----------------------------------------------------------------
// move all contexts from the other group into this group
// -----------------------------------------------------------------
#pragma nowarn(1506) // warning elimination
Lng32 maxg = other->goals_.entries();
#pragma warn(1506) // warning elimination
for (Lng32 i = 0; i < maxg; i++)
{
#pragma nowarn(1506) // warning elimination
Lng32 maxc = goals_.entries();
#pragma warn(1506) // warning elimination
NABoolean found = FALSE;
// change the context to the new group number
other->goals_[i]->setGroupId(groupId_);
// search the already existing contexts for this group for
// an equivalent one
for (Lng32 j = 0; j < maxc; j++)
{
// compare the contexts
if (NOT goals_[j]->isADuplicate() AND
goals_[j]->compareContexts(*(other->goals_[i])) == SAME)
{
found = TRUE;
// both groups have the same context in them;
// this should only happen in very rare cases where
// a group merge is detected by applying a rule that
// wasn't used in a previous pass or that was disabled
// in one of the groups by guidance or by cost-based pruning
// we should never have two contexts where both of
// them have outstanding optimization tasks; if one of
// them has outstanding tasks then make that one the primary
if (other->goals_[i]->getOutstanding() > 0)
{
Context *moveThis;
// move the context in this group to the back of the
// list and mark it as a duplicate; copy the other one
CMPASSERT(goals_[j]->getOutstanding() == 0);
moveThis = goals_[j];
goals_.insert(moveThis);
// NOTE: looks like goals_.insert(goals_[j]); would
// be easier, but that may fail if we resize the
// collection while inserting
goals_[j]->setDuplicateOf(other->goals_[i]);
goals_[j] = other->goals_[i];
}
else
{
// delete the context in the other group, it has
// no outstanding tasks and should not be optimal
// in this current pass
goals_.insert(other->goals_[i]);
other->goals_[i]->setDuplicateOf(goals_[j]);
}
}
}
if (NOT found)
goals_.insert(other->goals_[i]);
}
// all contexts in the other group have now been moved or deleted
other->goals_.clear();
// update memo to reflect the group merge
CURRSTMT_OPTGLOBALS->memo->update(other, this);
// keep some statistics
CURRSTMT_OPTGLOBALS->group_merge_count++;
// delete old group structure
delete other;
// schedule a garbage collection task
CURRSTMT_OPTGLOBALS->task_list->push(new (CmpCommon::statementHeap()) GarbageCollectionTask());
}
} // CascadesGroup::merge
//<pb>
// -----------------------------------------------------------------------
// CascadesGroup::shareContext()
//
// The algorithm for sharing the Context is as described below:
//
// 1) Iterate over the Contexts that are associated with this Group
// 2) For each Context, compare the given set of required physical
// properties with the set of required physical properties
// associated with that Context.
// 3) If the required physical properties compare to SAME, then
// a) If the Context contains a solution that was created in
// an earlier pass, clear the status indicators and
// assign the given cost limit to it.
// b) If the Context contains a solution that was created in the
// current pass, then if the given cost limit is less than
// the cost for the solution, it cannot create a plan.
// c) If the Context was created in the current pass and
// contains a failed plan, then compare the given cost limit
// with the cost limit that caused the failure. If the
// given cost limit is greater, clear the failed status and
// assign the given cost limit to the Context.
// 4) If no Context is found, then create a new one and assign the
// given cost limit to it.
//
// -----------------------------------------------------------------------
//<pb>
/* ============================================================ */
Context * CascadesGroup::shareContext(
const ReqdPhysicalProperty* const reqdPhys,
const InputPhysicalProperty* const inputPhys,
CostLimit* costLimit,
Context* parentContext,
const EstLogPropSharedPtr &inputLogProp)
{
// create a brand new context like the one the caller wants
Context *newContext = new (CmpCommon::statementHeap())
Context(groupId_, reqdPhys, inputPhys, inputLogProp) ;
Context* result = newContext;
#pragma nowarn(1506) // warning elimination
Lng32 maxc = goals_.entries();
#pragma warn(1506) // warning elimination
NABoolean found = FALSE;
// The "setCostLimitInContext" below is used to indicate whether the
// CostLimit is set in a previous Context. If it is not set in a
// Context within the main loop and is not set within the new Context,
// then the CostLimit is freed.
NABoolean setCostLimitInContext = FALSE;
// search the already existing contexts for this group for an equivalent
// one that can be shared
for (Lng32 i = 0; i < maxc AND NOT found; i++)
{
Context *existingContext = goals_[i];
// Compare the requested context with the ith context in the group.
// Look for an existing context that is NOT marked duplicate and that
// compares the SAME as the new context.
if ( NOT existingContext->isADuplicate() AND
newContext->compareContexts(*existingContext) == SAME )
{
if (existingContext->getOutstanding() == 0)
{
// an equivalent context has been used on this group before;
// reuse the existing one instead of creating a new one
result = existingContext;
found = TRUE;
DBGLOGMSGCNTXT(" *** Found context to share ",result);
// 3a) Found a Context that was created in an earlier pass?
// Reuse the Context after clearing flags and cached info.
if (NOT result->optimizedInCurrentPass())
{
result->clearFailedStatus();
// when pruning is on and CQD OPH_REDUCE_COST_LIMIT_FROM_
// PASS1_SOLUTION 'ON' we want to use information about
// previous pass solution (if any) to reduce our current cost
// limit. This will give pruning more chances.
if ( costLimit AND result->getSolution() AND
CURRSTMT_OPTDEFAULTS->OPHreduceCLfromPass1Solution() )
costLimit->tryToReduce(*(result->getCostForSolution()),
result->getReqdPhysicalProperty());
result->setCostLimit(costLimit);
setCostLimitInContext = TRUE;
DBGLOGMSG(" *** has to be reoptimized in cur.pass ");
}
// 3b) Context created during the current pass contains an optimal
// solution, reuse it as-is.
else
if ( result->hasOptimalSolution() )
{
DBGLOGMSG(" *** has optimal solution ");
// If the given cost limit is less than the cost for the
// solution, then it is not a feasible cost limit for
// creating a plan. But if we want to reuse an existing
// solution we can do it, it won't cost much.
if ( costLimit AND
( costLimit->compareWithPlanCost
((CascadesPlan *)result->getSolution(),
result->getReqdPhysicalProperty()) == LESS )
)
{
DBGLOGMSG("*** Cost limit exceeded when sharing context ");
// if we want to reuse solution that exceeded cost limit
// we need to set its costLimitExceeded_ flag.
// Previously we were setting new costLimit but didn't change
// the flag unless costLimit becomes "negative", therefore in
// createPlan() we considered it as a normal solution which
// was not completely true. Now we try to set this flag and
// make pruning possible ASAP, when using failed plan cost.
if (CURRSTMT_OPTDEFAULTS->OPHuseFailedPlanCost() )
{
result->setCostLimitExceeded();
}
else
{
result->setCostLimit(costLimit);
setCostLimitInContext = TRUE;
}
} // if (costlimit AND ... LESS
else
{
result->clearFailedStatus();
}
}// if (result->hasOptimalSolution()
// OR if OPHuseFailedPlanCost() AND result->hasSolution()
// 3c) Context created during the current pass but does
// not contain an optimal solution
else
if (costLimit)
{
// re-optimize the old context if the new
// context has a higher costlimit than the old.
if (costLimit->compareCostLimits
(result->getCostLimit(), reqdPhys) == MORE )
{
DBGLOGMSG(" *** CL increased for previously failed context ");
result->clearFailedStatus();
result->resetCurrentPassNumber();
result->setCostLimit(costLimit);
setCostLimitInContext = TRUE;
} // costLimit MORE that result->costLimit
else
{
DBGLOGMSG(" *** CL reused for previously failed context ");
// if we don't resetCurrentPassNumber the next OptimizeGroup
// task will check it and quit right away. We can skip creating
// this task and saving a little if we return NULL now. For
// this CQD OPH_SKIP_OGT_FOR_SHARED_GC_FAILED_CL should be ON.
if ( CURRSTMT_OPTDEFAULTS->OPHskipOGTforSharedGCfailedCL())
{
delete newContext;
delete costLimit;
return NULL;
}
}
} //if (costLimit and no solution)
} // if (outstanding == 0
else
{
// This context is already in use. There are two explanations
// for this: a) we screwed up, or b) a loop in CascadesMemo caused
// us to recursively arrive at one of our parent contexts
DBGLOGMSG(" *** context already in use ");
Context *ancestor = parentContext;
while (ancestor != NULL AND ancestor != existingContext)
ancestor = ancestor->getCurrentAncestor();
// make sure this isn't case a)
CMPASSERT(ancestor == existingContext);
// Make sure this context isn't going to create any tasks,
// since a valid plan must not have a node in it whose child
// is also one of its ancestors.
// So, make an exception to the rule that only one context
// with a certain optimization goal can exist in a group:
// make a duplicate context and insert it at the end of
// the goal list. However: brand this duplicate context as
// one whose optimization has failed.
found = TRUE;
result->markAsDoneForCurrentPass();
result->setDuplicateOf(existingContext);
}
} // if ( contexts are SAME
} // for on group contexts
if (result == newContext)
{
// no sharable context found, add this one to the list of contexts
goals_.insert(result);
result->setCostLimit(costLimit);
}
else
{
// If a valid CostLimit was passed in and it wasn't set in a previous
// Context, then delete it here.
if (costLimit != NULL && !setCostLimitInContext)
delete costLimit;
// the brand new one won't be needed, we were able to recycle an old one
delete newContext;
}
// save the parent context that is currently using this one
// Coverity incorrectly flags the following result reference as a
// USE_AFTER_FREE cid. It is a false alarm because line 712
// "if (result==newContext)" assures that the "delete newContext" of
// line 72 occurs only when result is not aliased to newContext.
// This code annotation should suppresses this false positive.
// coverity[use_after_free]
result->setCurrentAncestor(parentContext);
result->clearCurrentPlan();
return result;
} // CascadesGroup::shareContext
CascadesMemo::CascadesMemo(CascadesGroupId groups, Lng32 buckets)
: group_(CmpCommon::statementHeap(),groups),
hash_(CmpCommon::statementHeap(),buckets)
{
if (groups <= 1 OR buckets <= 1)
ABORT("defining CascadesMemo structure too small");
hashSize_ = buckets;
} // CascadesMemo::CascadesMemo
//<pb>
// memo is allocated in opt.cpp on statement heap by method
// QueryOptimizerDriver::initializeOptimization(), and deleted in
// RelExpr::optimizeNode() by setting memo = NULL. This is a speeding
// deletion.
// LCOV_EXCL_START
CascadesMemo::~CascadesMemo()
{
if (NOT (CURRSTMT_OPTGLOBALS->BeSilent))
{
// collect and print some statistics about hash table usage
float m1 = (float)0, m2 = (float)0;
for (Lng32 bucket_no = 0; bucket_no < hashSize_; bucket_no++)
{
Int32 count = 0;
RelExpr * e;
if (hash_.used(bucket_no))
{
for (e = hash_[bucket_no]; e != NULL;
e = e->getNextInBucket())
++ count;
}
#pragma nowarn(1506) // warning elimination
m1 += count;
#pragma warn(1506) // warning elimination
#pragma nowarn(1506) // warning elimination
m2 += count * count;
#pragma warn(1506) // warning elimination
}
#pragma nowarn(1506) // warning elimination
m1 /= hashSize_; m2 /= hashSize_;
#pragma warn(1506) // warning elimination
printf("%.0lf entries / %d buckets = %.6lf, var %.6lf\n",
#pragma nowarn(1506) // warning elimination
m1 * hashSize_, hashSize_, m1, m2 - m1 * m1);
#pragma warn(1506) // warning elimination
}
// weed out those groups that have been merged, to avoid deleting
// some groups twice
#pragma nowarn(1506) // warning elimination
Lng32 groupEntries = group_.entries();
#pragma warn(1506) // warning elimination
CascadesGroupId groupId = 0;
for (groupId = 0; (Lng32)groupId < groupEntries; groupId++)
{
if (groupId != group_[groupId]->getGroupId())
group_[groupId] = NULL;
}
// now delete all groups that are left over
for (groupId = 0; (Lng32)groupId < groupEntries; groupId++)
delete group_[groupId];
} // CascadesMemo::~CascadesMemo
// LCOV_EXCL_STOP
//<pb>
// LCOV_EXCL_START
void CascadesMemo::print(FILE * f, const char *, const char *) const
{
} // CascadesMemo::print
//<pb>
void CascadesMemo::print_all_trees(CascadesGroupId root,
NABoolean incl_log,
NABoolean incl_phys, FILE * f) const
{
// Browsing optimizer/memo.cpp@@/main/1 indicates this method was part of
// an attempt to implement a sql compiler "display" GUI tool using MOTIF.
// All that code is in ClearCase history if it's ever needed again.
// But, for now, we're trying to clean up our source code as seen by
// Coverity. Coverity flags "tree" as an UNUSED_VALUE cid. In fact, the
// entire body of this method is essentially dead code until someone
// resurrects the MOTIF/X11 based mxcmp "display" GUI too.
} // CascadesMemo::print_all_trees
// LCOV_EXCL_STOP
//<pb>
RelExpr * CascadesMemo::include(RelExpr * expr,
NABoolean & duplicateExprFlag,
NABoolean & groupMergeFlag,
NABoolean GrpIdIsBinding, //soln-10-070330-3667
CascadesGroupId groupId,
Context * context,
RelExpr * before
)
{
RelExpr * result = expr;
duplicateExprFlag = groupMergeFlag = FALSE;
// for a cut operator, return CascadesGroupId bound earlier
if (expr->isCutOp())
{
// identify the cut and determine the group it is bound to
CutOp * acut = (CutOp *) expr;
CascadesGroupId groupIdOfCut = acut->getGroupId();
// if the cut points to a group other than the one it is supposed
// to be inserted in, we got a reduction rule (a rule that proves
// some expression to be unnecessary)
if (groupId != INVALID_GROUP_ID AND
groupId != groupIdOfCut AND
(*CURRSTMT_OPTGLOBALS->memo)[groupId] != (*CURRSTMT_OPTGLOBALS->memo)[groupIdOfCut])
{
groupMergeFlag = TRUE;
}
duplicateExprFlag = TRUE;
result = (* CURRSTMT_OPTGLOBALS->memo) [groupIdOfCut]->getFirstLogExpr();
// when Multijoin child node points to a merged group(duplicate) in the memo.
// inclusion of multijoin child node in the memo will check whether the groupid
// it is referring is duplicate.if so it will assaign the merged groupid to the
// child node:soln-10-070330-3667
if ( result != NULL AND groupIdOfCut != (*CURRSTMT_OPTGLOBALS->memo)[groupIdOfCut]->getGroupId()
AND GrpIdIsBinding)
result-> setGroupId(groupIdOfCut);
if (result == NULL)
result = (* CURRSTMT_OPTGLOBALS->memo) [groupIdOfCut]->getFirstPhysExpr();
} // return CascadesGroupId bound earlier for cut in rule application
// for a logical expression, check for duplicates etc.
else
{
// -----------------------------------------------------------------
// recursively include all the node's children into CascadesMemo
// -----------------------------------------------------------------
Int32 arity = result->getArity();
NABoolean inserted = FALSE;
NABoolean childDuplicateExprFlag; // ignore duplicates in the child nodes
NABoolean childGroupMergeFlag;
NABoolean childGrpIdIsBinding; // soln-10-070330-3667
for (Lng32 childIndex = 0; childIndex < arity; childIndex++)
{
// insert the child into the CascadesMemo structure and change
// the pointer to an child RelExpr to a group number
// do not pass a group no or a context, assume that all children
// are either cut nodes or logical expressions that go into
// a newly formed group
// Check whether the GroupIdMode is BINDING. If it is TRUE while including
// it in memo we do change the groupId ID of the merged expression in the
// above if condition during CutOp processing://soln-10-070330-3667
if ( result->child(childIndex).getMode() == ExprGroupId::BINDING )
childGrpIdIsBinding = TRUE;
else
childGrpIdIsBinding = FALSE;
result->child(childIndex) = include(result->child(childIndex).getPtr(),
childDuplicateExprFlag,
childGroupMergeFlag,
childGrpIdIsBinding,
INVALID_GROUP_ID,
NULL,
before)->getGroupId();
}
if (expr->isLogical())
{
// search for duplicates already in "memo"
RelExpr * other = CURRSTMT_OPTGLOBALS->memo->findDuplicate(result);
// delete if there has already been a duplicate in memo
if (other != NULL) // previous duplicate found!
{
// check whether group merging is required
CascadesGroupId groupIdOfOther = other->getGroupId();
if (groupId != INVALID_GROUP_ID AND
groupIdOfOther != groupId AND
// check whether groups already got merged
(* CURRSTMT_OPTGLOBALS->memo) [groupId] != (* CURRSTMT_OPTGLOBALS->memo) [groupIdOfOther])
{
groupMergeFlag = TRUE;
}
// merge any (newly generated) group attributes from
// this new expression into the group of the duplicate expression.
if (other->getGroupAttr() != NULL AND
expr->getGroupAttr() != NULL)
{
other->getGroupAttr()->reconcile (*(expr->getGroupAttr()));
// --------------------------------------------------------
// For the purpose of synthesis, ensure that any references
// to this expr references the "duplicate" expr in memo.
// ------------------------------------------------------------
if (expr->getGroupAttr()->getLogExprForSynthesis() == expr)
expr->getGroupAttr()->setLogExprForSynthesis (other);
}
groupId = groupIdOfOther;
duplicateExprFlag = TRUE;
CURRSTMT_OPTGLOBALS->duplicate_expr_count ++;
result = other;
}
else // truly a new logical expression
{
// if no group was given, create one
if (groupId == INVALID_GROUP_ID)
groupId = makeNewGroup(result->getGroupAttr());
// insert into group
(* this)[groupId]->addLogExpr(result, before);
// include in hash bucket
addExpr(result, result->treeHash());
CURRSTMT_OPTGLOBALS->logical_expr_count++;
inserted = TRUE;
} // truly a new logical expression
} // for a logical expression, check for duplicates etc.
// for a physical expression, insert into group if not done yet
// and also make the expression a candidate for a solution
// of the context
if (expr->isPhysical())
{
// insert into list of phys expressions only if not already
// inserted as a logical operator
if (NOT inserted AND NOT duplicateExprFlag)
{
// if no group was given, create one
if (groupId == INVALID_GROUP_ID)
groupId = makeNewGroup(result->getGroupAttr());
// insert into group
(* this)[groupId]->addPhysExpr(result, before);
inserted = TRUE;
CURRSTMT_OPTGLOBALS->physical_expr_count;
}
if (context == NULL)
{
// Trying to insert a physical expression without a context
// (this could happen during exploration or when a tree
// node that is both physical and logical is inserted)
// Create or share an empty context
EstLogPropSharedPtr inputLP(new (CmpCommon::statementHeap())
EstLogProp ((float)1.0));
// Coverity flags "context" as an UNUSED_VALUE. That is true.
// We don't care. So, we silence it for now.
// coverity[returned_pointer]
context =
group_[groupId]->shareContext(NULL,NULL,NULL,NULL,inputLP);
}
} // insert physical expression
// if the expression has not been inserted because it is a duplicate,
// then delete it now
if (NOT inserted &&
(CmpCommon::getDefault(COMP_BOOL_4) != DF_ON))
delete expr;
} // other than a cut operator
return( result );
} // CascadesMemo::include
//<pb>
void CascadesMemo::addExpr(RelExpr * expr, HashValue hash_value)
{
Int32 bucket = (Int32) (hash_value.getValue() % hashSize_);
RelExpr * old_ptr = NULL;
if (hash_.used(bucket))
old_ptr = hash_[bucket];
expr->setNextInBucket(old_ptr);
hash_.insertAt(bucket, expr);
} // CascadesMemo::addExpr
//<pb>
RelExpr * CascadesMemo::findDuplicate(RelExpr * expr) const
{
Lng32 bucket = (Lng32)(expr->treeHash().getValue() % hashSize_);
if (NOT hash_.used(bucket))
{
return NULL;
}
else
{
Int32 arity = expr->getArity();
GroupAttributes *ga;
// try all expressions in the appropriate hash bucket
for (RelExpr * old = hash_[bucket];
old != NULL; old = old->getNextInBucket())
{
Lng32 childIndex;
// compare the children
if (old->getArity() != arity)
goto not_a_duplicate;
for (childIndex = arity; -- childIndex >= 0; )
if ((* expr) [childIndex] != (* old) [childIndex])
goto not_a_duplicate;
// compare characteristic inputs/outputs
if ((ga = expr->getGroupAttr()) != NULL AND
NOT (ga->getCharacteristicInputs() ==
old->getGroupAttr()->getCharacteristicInputs()) OR
NOT (ga->getCharacteristicOutputs() ==
old->getGroupAttr()->getCharacteristicOutputs()))
goto not_a_duplicate;
// compare operators and their arguments
if (NOT expr->duplicateMatch(*old))
goto not_a_duplicate;
// "expr" is a duplicate of "old"
return( old );
not_a_duplicate :
continue; // check next expression in hash bucket
} // try all expressions in the appropriate hash bucket
}
return( NULL );
} // CascadesMemo::findDuplicate
//<pb>
CascadesGroupId CascadesMemo::makeNewGroup(GroupAttributes *ga)
{
CascadesGroupId newGroupId = group_.entries();
group_.insertAt(newGroupId,
new (CmpCommon::statementHeap()) CascadesGroup(newGroupId,ga));
return(newGroupId);
}
void CascadesMemo::update(CascadesGroup * oldGroup, CascadesGroup * newGroup)
{
// scan through entire table, because the old group
// might be the result of an earlier group merging
for (Lng32 groupId = (Int32)(group_.entries()); --groupId >= 0; )
if (group_[groupId] == oldGroup)
{
group_[groupId] = newGroup;
} // update group pointer
} // CascadesMemo::update
//<pb>
Int32 CascadesMemo::garbageCollection()
{
LIST(RelExpr *) changed(STMTHEAP); // list of outdated RelExprs
RelExpr *e; // a single rel expr
RelExpr *pred; // predecessor in the hash chain
Int32 nc; // number of children
Int32 i; // index for a child
CascadesGroupId childGroupId; // group # of child
NABoolean found; // found an entry to clean up
// ---------------------------------------------------------------------
// go through all the hash chains
// ---------------------------------------------------------------------
for (Lng32 bucket_no = 0; bucket_no < (Lng32)hashSize_; bucket_no++)
{
if (hash_.used(bucket_no))
{
// -------------------------------------------------------------
// walk a single hash chain
// -------------------------------------------------------------
pred = NULL;
for (e = hash_[bucket_no]; e != NULL;
e = e->getNextInBucket())
{
// ---------------------------------------------------------
// check an individual expression in the hash chain
// ---------------------------------------------------------
nc = e->getArity();
found = FALSE;
for (i = 0; i < nc; i++)
{
childGroupId = e->child(i).getGroupId();
if (childGroupId != INVALID_GROUP_ID AND
group_[childGroupId]->getGroupId() != childGroupId)
{
// -------------------------------------------------
// OK, found a case where the input of a RelExpr
// points to a group that no longer exists.
// Unlink that expression from the hash chain
// and remember it in the global list.
// -------------------------------------------------
found = TRUE;
}
}
if (found)
{
// -----------------------------------------------------
// This expression "e" needs to be brought up-to-date,
// remember it in a dynamic list and unlink it from
// the linked list that represents the hash bucket.
// -----------------------------------------------------
// -----------------------------------------------------
// unlink e from its current hash chain
// -----------------------------------------------------
if (pred != NULL)
pred->setNextInBucket(e->getNextInBucket());
else
{
hash_[bucket_no] = e->getNextInBucket();
if (hash_[bucket_no] == NULL)
// delete the NULL entry
hash_.remove(bucket_no);
}
// -----------------------------------------------------
// reset the hash chain link and fix the input
// group numbers in e
// -----------------------------------------------------
e->setNextInBucket(NULL);
for (i = 0; i < nc; i++)
{
e->child(i) =
group_[e->child(i).getGroupId()]->getGroupId();
}
// -----------------------------------------------------
// remember e in the global list of entries
// to clean up
// -----------------------------------------------------
changed.insert(e);
} // done handling a "found" case
// e becomes the predecessor for the next expression
pred = e;
} // done with one expression
} // done with a used hash chain
} // done with a hash chain
// remember the state of group merging at this point
Int32 lowWaterMark = CURRSTMT_OPTGLOBALS->group_merge_count;
// ---------------------------------------------------------------------
// now re-insert all the changed expressions into the hash chains
// ---------------------------------------------------------------------
for (i = 0; i < (Int32)changed.entries(); i++)
{
RelExpr *d;
e = changed[i];
// -----------------------------------------------------------------
// Expression "e" has been identified as an expression that needed
// to be updated (the input group numbers have already been fixed
// in the previous loop). Now do the following:
//
// - recalculate the hash value for "e"
// - check whether a duplicate expression "d" already exists in
// CascadesMemo and mark the expression "e" for deletion, if it does
// - if the duplicate "d" exists in another group, then
// merge the two groups
// - if the expression was marked for deletion, then update all
// tasks that refer to it (make them reference the duplicate "d"
// instead), unlink "e" from the chain of expressions in its
// group, and, finally, delete "e".
// -----------------------------------------------------------------
if ((d = findDuplicate(e)) != NULL)
{
// -------------------------------------------------------------
// oops, this expression was unnecessary, check
// whether its duplicate belongs to another group
// -------------------------------------------------------------
if (d->getGroupId() != e->getGroupId())
{
// merge groups of d and e
group_[d->getGroupId()]->merge(group_[e->getGroupId()]);
}
// -------------------------------------------------------------
// now hunt through the task stack, modifying any
// tasks that referenced e to use d from now on
// -------------------------------------------------------------
for (CascadesTask * task = CURRSTMT_OPTGLOBALS->task_list->getFirst();
task != NULL;
task = CURRSTMT_OPTGLOBALS->task_list->getNext(task))
{
task->garbageCollection(e,d,lowWaterMark);
}
// ------------------------------------------------------------
// For the purpose of synthesis, ensure that any references
// to expr e are modified to expr d
// ------------------------------------------------------------
if (e->getGroupAttr() != NULL AND
e->getGroupAttr()->getLogExprForSynthesis() == e)
e->getGroupAttr()->setLogExprForSynthesis (d);
// -------------------------------------------------------------
// Der Mohr hat seine Schuldigkeit getan, der Mohr kann geh'n.
// (Goethe?)
// -------------------------------------------------------------
delete group_[e->getGroupId()]->unlinkLogExpr(e);
}
else
{
// -------------------------------------------------------------
// re-insert e into its new hash chain
// -------------------------------------------------------------
addExpr(e, e->treeHash());
}
}
// return the number of changed expressions
#pragma nowarn(1506) // warning elimination
return changed.entries();
#pragma warn(1506) // warning elimination
} // CascadesMemo::garbageCollection()
NABoolean CascadesMemo::consistencyCheck() const
{
return TRUE;
} // CascadesMemo::consistencyCheck()