blob: 0a2626777e2c83fe2e29467b6d0df9817205a352 [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: mdam.C
*
* Created: //96
* Language: C++
*
*
*
*
*****************************************************************************
*/
// -----------------------------------------------------------------------
#include "mdam.h"
#include "disjunct.h"
#include "disjuncts.h"
#include "ItemExpr.h"
#include "mdamkey.h"
#include "ItemOther.h"
#include "ItemColRef.h"
#include "NATable.h"
#include "ValueDesc.h"
#include "SchemaDB.h"
#include "OptRange.h"
#undef FSOWARNING
#ifndef NDEBUG
#define FSOWARNING(x) fprintf(stdout, "FileScan optimizer warning: %s\n", x);
#else
#define FSOWARNING(x)
#endif
//-----------------------------------------------------------------------
// This amplifies the threshold,MDAM_MAX_NUM_DISJUNCTS allowing greater
// freedom to while we are computing the disjuncts.
const CollIndex MULIPLICATION_FACTOR = 8;
//-----------------------------------------------------------------------
// ***********************************************************************
// $$$ Functions related to DisjunctArray
// ***********************************************************************
// -----------------------------------------------------------------------
// mdamANDDisjunctArrays()
// -----------------------------------------------------------------------
DisjunctArray * mdamANDDisjunctArrays
( DisjunctArray * leftDisjunctArray,
DisjunctArray * rightDisjunctArray )
{
//10-040128-2749 -begin
//---------------------------------------------------------------------
// IF one of the operands is NULL. This would mean we have Aborted
// Disjunct calculation due to the computation exceeding the threshold
// So Return NULL and Convey the message to MY parent.
//---------------------------------------------------------------------
if(!leftDisjunctArray OR !rightDisjunctArray)
return NULL; //Abort! -- make it know to others.
//---------------------------------------------------------------------
// Check if we Exceed the threshold here. If we do then start spreading
// the bad news.
//---------------------------------------------------------------------
CollIndex LEntries = leftDisjunctArray->entries();
CollIndex REntries = rightDisjunctArray->entries();
if((LEntries * REntries) > MDAM_MAX_NUM_DISJUNCTS * MULIPLICATION_FACTOR)
{
delete leftDisjunctArray;
delete rightDisjunctArray;
return NULL;
}
//10-040128-2749 -end
// ---------------------------------------------------------------------
// If either DisjunctArray is empty (due to contradictory
// predicates) the result will also, so we can just return whichever
// has the contradictory predicates.
// ---------------------------------------------------------------------
//
if ( rightDisjunctArray->entries() == 0 )
{
return rightDisjunctArray;
}
else if ( leftDisjunctArray->entries() == 0 )
{
return leftDisjunctArray;
}
else
// ---------------------------------------------------------------------
// If the right DisjunctArray has only a single entry then the
// predicates in the ValueIdSet that this entry points to can
// simply be added to the ValueIdSets that the entries in the left
// DisjunctArray point to. The right DisjunctArray and its
// ValueIdSet entries can then be deleted. The left DisjunctArray
// is returned to the previous ItemExpr in this recursion over the
// expression tree.
// ---------------------------------------------------------------------
if ( rightDisjunctArray->entries() == 1 )
{
leftDisjunctArray->andDisjunct(rightDisjunctArray);
return leftDisjunctArray;
}
else
// ---------------------------------------------------------------------
// If the left DisjunctArray has only a single entry then the
// predicates in the ValueIdSet that this entry points to can
// simply be added to the ValueIdSets that the entries in the right
// DisjunctArray point to. The left DisjunctArray and its
// ValueIdSet entries can then be deleted. The right DisjunctArray
// is returned to the previous ItemExpr in this recursion over the
// expression tree.
// ---------------------------------------------------------------------
if ( leftDisjunctArray->entries() == 1 )
{
rightDisjunctArray->andDisjunct(leftDisjunctArray);
return rightDisjunctArray;
}
else
// ---------------------------------------------------------------------
// If both the left and right DisjunctArrays have more than 1 entry
// a new DisjunctArray will have to be created. The predicates in
// each disjunct in the left DisjunctArray will be added to the
// predicates in each disjunct in the right DisjunctArray-> This will
// result in a new DisjunctArray with the number of entires equal to
// the product of the number of entries in both the DisjunctArrays.
// This DisjunctArray is then returned to the previous ItemExpr in
// this recursion over the expression tree. The left and right
// DisjunctArrays and their ValueIdSet entries are deleted.
// ---------------------------------------------------------------------
{
DisjunctArray * disjunctArray = new(CmpCommon::statementHeap())
DisjunctArray();
disjunctArray->andDisjunctArrays( leftDisjunctArray,
rightDisjunctArray );
return disjunctArray;
}
} // mdamANDDisjunctArrays
// -----------------------------------------------------------------------
// mdamORDisjunctArrays()
// -----------------------------------------------------------------------
DisjunctArray * mdamORDisjunctArrays
( DisjunctArray * leftDisjunctArray,
DisjunctArray * rightDisjunctArray )
{
//10-040128-2749 -begin
//---------------------------------------------------------------------
// IF one of the operands is NULL. This would mean we have Aborted
// Disjuct calculation due to the computation excceding the threshold
// So Return NULL and Convey the message to MY parent.
//---------------------------------------------------------------------
if(!leftDisjunctArray OR !rightDisjunctArray)
return NULL; //Abort! -- make it know to others.
//---------------------------------------------------------------------
// Check if we Exceed the threshold here. If we do then start spreading
// the bad news.
//---------------------------------------------------------------------
CollIndex LEntries = leftDisjunctArray->entries();
CollIndex REntries = rightDisjunctArray->entries();
if((LEntries + REntries) > MDAM_MAX_NUM_DISJUNCTS * MULIPLICATION_FACTOR)
{
delete leftDisjunctArray;
delete rightDisjunctArray;
return NULL;
}
//10-040128-2749 -end
// ---------------------------------------------------------------------
// If either DisjunctArray is empty (i.e. contains contradictory
// predicates), we know it will not contribute to the OR. So, we
// can just return the other one.
// ---------------------------------------------------------------------
//
if ( rightDisjunctArray->entries() == 0 )
{
return leftDisjunctArray;
}
else if ( leftDisjunctArray->entries() == 0 )
{
return rightDisjunctArray;
}
else
// ---------------------------------------------------------------------
// If the entries in the left DisjunctArray are fewer than in the
// right DisjunctArray then they are merged into the right Disjunct
// Array. Otherwise, the entries in the right DisjunctArray are
// merged into the left DisjunctArray. The array whose entries are
// merged is deleted. The other is sent back to the previous
// ItemExpr in this recursion over the expression tree.
// ---------------------------------------------------------------------
if ( leftDisjunctArray->entries() < rightDisjunctArray->entries() )
{
rightDisjunctArray->orDisjunctArray( leftDisjunctArray );
return rightDisjunctArray;
}
else
{
leftDisjunctArray->orDisjunctArray( rightDisjunctArray );
return leftDisjunctArray;
}
} // mdamORDisjunctArrays
// ***********************************************************************
// $$$ DisjunctArray
// member functions for class DisjunctArray
// ***********************************************************************
// -----------------------------------------------------------------------
// DisjunctArray::~DisjunctArray()
// -----------------------------------------------------------------------
DisjunctArray::~DisjunctArray()
{
// Goes through all the array entries and for each entry gets rid of the
// ValueIdSet it points to
for (CollIndex index = 0; index < entries(); index++)
delete at(index);
} // DisjunctArray::~DisjunctArray
// -----------------------------------------------------------------------
// DisjunctArray::andDisjunct()
// -----------------------------------------------------------------------
void DisjunctArray::andDisjunct
( DisjunctArray * otherDisjunctArray )
{
// confirm that there is only one disjunct in the disjunct array passed
assert(otherDisjunctArray->entries() == 1);
// ---------------------------------------------------------------------
// Loop through the entries of the disjunct array being processed. For
// each one access the ValueIdSet that the entry points to. Add to this
// the predicate value ids from the single disjunct (ValueIdSet) in the
// disjunct array passed in as a parameter (first entry in
// otherDisjunctArray). The += operator does a logical OR of the two
// ValueIdSets (bitmaps).
// ---------------------------------------------------------------------
for (CollIndex index = 0; index < entries(); index++)
*at(index) += *(otherDisjunctArray->at(0));
// delete the otherDisjunctArray and its ValueIdSet entry
delete otherDisjunctArray;
} // andDisjunct
// -----------------------------------------------------------------------
// DisjunctArray::andDisjunctArrays()
// -----------------------------------------------------------------------
void DisjunctArray::andDisjunctArrays
( DisjunctArray * leftDisjunctArray,
DisjunctArray * rightDisjunctArray )
{
// ---------------------------------------------------------------------
// Loop through the left disjunct array and for each entry in it it loop
// though the right disjunct array. For a combination of each of the
// entries in the two disjunctArrays insert an entry into the new
// disjunctArray. Essentially perform a cross-product of the two
// arrays. So the number of entries in the new array should be the
// number of entries in the left disjunct array times the number in the
// right disjunct array.
//
// For each combination of the entries first copy the left disjunct
// entry creating a new ValueIdSet. To this newly created ValueIdSet
// add the predicate value ids in the right array ValueIdSet. The
// += operator helps do this. It performs a logical OR of the
// predicates in both the left and the right ValueIdSet entries.
// ---------------------------------------------------------------------
for (CollIndex l = 0; l < leftDisjunctArray->entries(); l++)
for (CollIndex r = 0; r < rightDisjunctArray->entries(); r++)
{
ValueIdSet * disjunct = new (CmpCommon::statementHeap())
ValueIdSet(*(leftDisjunctArray->at(l)));
*disjunct += *(rightDisjunctArray->at(r));
insert(disjunct);
}
// ---------------------------------------------------------------------
// Delete the left and right disjunct arrays also deleting all the
// ValueIdSets that they pointed to.
// ---------------------------------------------------------------------
delete leftDisjunctArray;
delete rightDisjunctArray;
} // andDisjunctArrays
// -----------------------------------------------------------------------
// DisjunctArray::orDisjunctArray()
// -----------------------------------------------------------------------
void DisjunctArray::orDisjunctArray
( DisjunctArray * otherDisjunctArray )
{
// ---------------------------------------------------------------------
// Loop through the disjunct array passed and insert each entry into
// the disjunct array being processed.
// ---------------------------------------------------------------------
for (CollIndex index = 0; index < otherDisjunctArray->entries(); index++)
insert(otherDisjunctArray->at(index));
// ---------------------------------------------------------------------
// Since the ValueIdSets that the array pointed to still need to be
// preserved, clear the disjunct array that is not needed anymore (has
// been merged) and then delete it. Simply deleting it would also
// delete the ValueIdSets that it points to.
// ---------------------------------------------------------------------
otherDisjunctArray->clear();
delete otherDisjunctArray;
} // orDisjunctArray
void DisjunctArray::display() const
{
print();
}
void DisjunctArray::print( FILE* ofd,
const char* indent,
const char* title) const
{
fprintf(ofd,title);
fprintf(ofd,"\n");
// for every disjunct:
NAString disStr(CmpCommon::statementHeap());
for (CollIndex i = 0; i < entries(); i++) {
ValueIdSet& disjunct = *(*this)[i];
// Print it:
disStr = NAString("disjunct[") +
NAString((unsigned char)('0'+Int32(i))) + NAString("]: ");
disjunct.print(ofd,indent,disStr);
} // for every disjunct
} // display()
//---------------------------------------------------------
// Methods for class ScanKey
//---------------------------------------------------------
// -----------------------------------------------------------------------
// replicateNonKeyVEGPredicates()
//
// Analyze VEGPredicates that appear in the setOfKeyPredicates.
// Replicate them as non-key predicates when their VEG contains entries
// such that we must result in more than ONE comparison.
// Genesis Solution 10-091110-6239
//
// For example, a query like this:
// SELECT *
// FROM CUBE1 T4
// WHERE
// EXISTS(SELECT T6."SID" FROM SI001 T6 WHERE T6."SID" = T4."SID_RSDRSIR01" AND
// T6."SID" = 7)
// OR
// T4. "SID_RSDRSIR01" = 8
// ;
//
// The key expression of the scan of t6 will contain t6.sid=7, however if
// we don't replicate the predicate to the executor predicate we will not
// do the comparison to the outer table t4.
//
// So, if the list of VEG members contains more than 1 entry after we have
// removed the keyColumns of the index, we need to replicate the expression.
// Note that we remove the index columns from the VEG members beforehand,
// so that we only count base columns and constants.
//
// -----------------------------------------------------------------------
void
ScanKey::replicateNonKeyVEGPredicates(
const ValueIdSet& setOfKeyPredicates,
ValueIdSet& setOfNonKeyPredicates /* out */
) const
{
// we need the valueIds of the key columns of the index,
// expressed in terms of base columns
ValueIdSet expandedKeyCols = getIndexDesc()->getIndexKey();
const IndexDesc *UDIdesc = getUDIndexDesc();
// If we are doing update/delete, then get the keyCols from
// the update table as well.
if (UDIdesc != NULL)
{
ValueIdSet expandedUDKeyCols(UDIdesc->getIndexKey());
expandedKeyCols += expandedUDKeyCols;
}
expandedKeyCols = expandedKeyCols.convertToBaseIds();
// If we are doing update/delete, then get the nonkeyCols from
// the update table as well.
ValueIdSet ghostNonKeyColumnSet;
if (UDIdesc != NULL)
UDIdesc->getNonKeyColumnSet(ghostNonKeyColumnSet);
// Find true outer references
ValueIdSet trueOuterReferences;
ValueIdSet inputs = getOperatorInputs();
// This CQD is turned off by default as it is difficult to guarantee
// that we will not remove two many elements, in which case we could
// possibly, incorrectly replicate to few predicates, which can leads to
// incorrect results.
// For now only trigger half of the algorithm, when we have no inputs to
// improve on insert/delete type queries in particular.
if (CmpCommon::getDefaultLong(ALLOW_INPUT_PRED_REPLICATION_REDUCTION) > 1)
{
for (ValueId inputExprId = inputs.init();
inputs.next(inputExprId);
inputs.advance(inputExprId) )
{
// Found a VEGReference?
ItemExpr * inputExprPtr = inputExprId.getItemExpr();
if (inputExprPtr->getOperatorType() == ITM_VEG_REFERENCE)
{
ValueIdSet referenceValues;
// Check if it contains any values that are table columns
// but not key columns:
referenceValues = ((VEGReference*)inputExprPtr)->getVEG()->getAllValues();
ValueIdSet vegConstants;
referenceValues.getConstants(vegConstants);
referenceValues -= vegConstants;
for (ValueId refExprId = referenceValues.init();
referenceValues.next(refExprId);
referenceValues.advance(refExprId) )
{
if (refExprId.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN)
referenceValues -= refExprId;
if (getNonKeyColumnSet().contains(refExprId))
referenceValues -= refExprId;
if (expandedKeyCols.contains(refExprId))
referenceValues -= refExprId;
}
// remember the true outer references
// we only need one value
if (referenceValues.entries())
trueOuterReferences += referenceValues.init();
}
else
{
// remember the true outer references
trueOuterReferences += inputExprId;
}
}
}
// Iterate over all key predicates.
ItemExpr* exprPtr = NULL;
for (ValueId exprId = setOfKeyPredicates.init();
setOfKeyPredicates.next(exprId);
setOfKeyPredicates.advance(exprId) )
{
// Found a VEGPredicate?
exprPtr = exprId.getItemExpr();
if (exprPtr->getOperatorType() == ITM_VEG_PREDICATE)
{
ValueIdSet predicateValues;
ValueIdSet constantValues;
// Check if it contains any values that are table columns
// but not key columns:
predicateValues = ((VEGPredicate*)exprPtr)->getVEG()->getAllValues();
// Remove any physical column references, look only at base columns
// (our local table or outer references) and constants
for (ValueId elemId = predicateValues.init();
predicateValues.next(elemId);
predicateValues.advance(elemId))
{
if (elemId.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN)
predicateValues -= elemId;
// Remember the constants
if ((elemId.getItemExpr()->getOperatorType() == ITM_CONSTANT) ||
(elemId.getItemExpr()->getOperatorType() == ITM_CACHE_PARAM) ||
(elemId.getItemExpr()->getOperatorType() == ITM_DYN_PARAM))
constantValues += elemId;
}
// Remove our key columns
// - this leaves outer references, constants and our non-key columns
predicateValues -= expandedKeyCols;
// Remove references to Update/Delete node
// ghostNonKeyColumnSet will be empty for any other operation
predicateValues -= ghostNonKeyColumnSet;
// If this scan/search key was created for a GenericUpdate with
// a Scan child, make sure we subtract those valueIds from the
// expressions as well as they represent the before valueId of those
// associated with the table/index desc of the GenericUpdate. Ie.
// they represent the same thing.
//
// For example update t set b=0 where a=1
//
// Will generate a predicate like this "before_t.a = 1 = after_t.a"
// and we want to avoid scan predicates like this
// "before_t.a =1 AND after_t.a = 1"
//
// so for Update and Delete we the ghost column references below
// are for the after_t columns.
//
// Another good example is this compund update:
//
// BEGIN
// SELECT D_TAX, D_NEXT_O_ID
// FROM T42DIST
// WHERE (D_W_ID, D_ID) = (1, 10)
// FOR REPEATABLE ACCESS IN EXCLUSIVE MODE;
//
// UPDATE T42DIST
// SET D_NEXT_O_ID = D_NEXT_O_ID + 1
// WHERE (D_W_ID, D_ID) = (8, 10);
// END;
//
// Here the VEG will contain 3 separate reference to D_ID for example,
// from each of the 3 instances of the table in the plan:
// The code below is able to detect that and remove them before we
// incorrectly replicate the predicate.
//
// The plan looked like this:
// CompoundStmt
// / \
// scan update
// \
// scan
//
// the two VEGs for the update contained (D_ID, D_ID, 10, D_ID) and
// (D_W_ID, D_W_ID, 8, D_W_ID) respectively
//
// The code below translates these VEGs to (D_ID, 10) and (D_W_ID,8)
// where the column references are those from the update table.
// To detect these types of references we apply the following logic:
//
// a) subtract from the predicateValues any valueId that is NOT
// 1) a constant
// 2) reference to a nonKeyColumn for the current table
// 3) an input to the operator
// since the input may be a VEGReference like (D_ID,10,D_ID),
// we need to dig into the inputs and pull out elements
// that are not local non key base columns and constants.
// these then would be the true outer references..
//
if (CmpCommon::getDefaultLong(ALLOW_INPUT_PRED_REPLICATION_REDUCTION) > 0)
{
ValueIdSet ghostVids = predicateValues;
// Begin -
// nonKeyColumnSet does not have base column value id
// it has value id for the column in physical access path
// So have to account for that. This caused a wrong result
// bug.
ValueIdSet nonKeyBaseColumns;
ValueIdSet nonKeyColSet = getNonKeyColumnSet();
for(ValueId vid=nonKeyColSet.init();
nonKeyColSet.next(vid);
nonKeyColSet.advance(vid))
{
// Since we can call this on any ValueIdSet, make sure we actually have
// an IndexColumn.
if(vid.getItemExpr()->getOperatorType() == ITM_INDEXCOLUMN)
nonKeyBaseColumns.insert(((BaseColumn *)(((IndexColumn *)vid.getItemExpr())->
getDefinition().getItemExpr()))->getValueId());
}
nonKeyColSet += nonKeyBaseColumns;
// End -
// remove any of our own columns
ghostVids -= nonKeyColSet;
// remove any constants
ghostVids -= constantValues;
// remove any inputs
// after removing the inputs, ghostVids contains only are
// references that looks like outer references but since they
// are not part of the operator inputs, we can delete these
ghostVids -= trueOuterReferences;
// delete the ghostVids references..
predicateValues -= ghostVids;
}
NABoolean replicatePredicate = FALSE;
// If we have multiple non-key columns, replicate predicate
if (predicateValues.entries() > 1)
replicatePredicate = TRUE;
else if (predicateValues.entries() == 1)
{
// Even if only one entry is left, if this is a non-key
// column of our table, then we still have to replicate
// the predicate. (Note: I doubt that this can ever happen.
// We need at least one outer reference or constant to form
// a key predicate. So, if there is only one ItemExpr other
// than a key column, then it must be that outer reference
// or constant).
// If it is an outer reference or constant we don't have
// to replicate the VEGPredicate as the keyExpression suffices.
//
// If it is a base table nonKeyColumn we take a DCMPASSERT()
// since this isn't supposed to be possible.
predicateValues.intersectSet(getNonKeyColumnSet());
DCMPASSERT (predicateValues.entries() == 0);
}
if (replicatePredicate == TRUE)
{
// There is at least non-key column in the VEG!
// (i.e. keycolumn=1 and nonkeycolumn=1)
// thus need to replicate the veg
setOfNonKeyPredicates += exprId;
}
}
} // end for
} //static replicateNonKeyVEGPredicates()
// -----------------------------------------------------------------------
// Input:
// ie an item expr
// column a column
// If the ie is a base column OR the ie is a veg reference, this
// method will return true if the ie makes reference to the
// given column. Else it returns FALSE
// -----------------------------------------------------------------------
NABoolean ScanKey::expressionContainsColumn(
const ItemExpr& ie
,const ValueId& column
)
{
NABoolean retVal = FALSE;
if (ie.getValueId() == column)
{
// a base column matches a base column
// OR
// an index column matches an index column
retVal = TRUE;
}
else
{
// the ie must be a reference or a base column, thus
// if the column is an index column, get its definition:
ItemExpr *colPtr = column.getItemExpr();
ValueId col = column;
if (colPtr->getOperatorType() == ITM_INDEXCOLUMN)
{
col = ((IndexColumn *)(colPtr))->getDefinition();
}
if (ie.getOperatorType() == ITM_VEG_REFERENCE)
{
// see if the column is inside the VEG of the reference:
if (ie.referencesTheGivenValue(col))
{
retVal = TRUE;
}
}
else if (ie.getOperatorType() == ITM_INDEXCOLUMN)
{
// is it an index column for base table column "col"
if (((IndexColumn &)(ie)).getDefinition() == col)
{
retVal = TRUE;
}
}
else if (ie.getValueId() == col)
{ // the index column matches the base column
retVal = TRUE;
} // if column is an index column
} //
return retVal;
}
NABoolean
ScanKey::isAKeyPredicate(
const ValueId & predId,
ValueId & referencedInput,
ValueId & intervalExclusionExpr) const
{
NABoolean retVal = FALSE;
const ValueIdList& keyCols = getKeyColumns();
for (CollIndex i=0; i < keyCols.entries(); i++)
{
const ValueId& col= keyCols[i];
if (isAKeyPredicateForColumn(predId
,referencedInput
,intervalExclusionExpr
,col
))
{
retVal = TRUE;
break;
}
}
return retVal;
}
NABoolean
ScanKey::isAKeyPredicate(const ValueId& predId) const
{
ValueId dummy1(NULL_VALUE_ID);
ValueId dummy2(NULL_VALUE_ID);
return isAKeyPredicate(predId, dummy1, dummy2);
} // isAKeyPredicate
NABoolean
ScanKey::isAKeyPredicateForColumn(
const ValueId& predId
,const ValueId& keyColumn
) const
{
ValueId dummy1(NULL_VALUE_ID);
ValueId dummy2(NULL_VALUE_ID);
return isAKeyPredicateForColumn(predId
,dummy1
,dummy2
,keyColumn
,getOperatorInputs());
} // isAKeyPredicate
NABoolean
ScanKey::isAKeyPredicateForColumn(
const ValueId & predId
,ValueId & referencedInput /* out */
,ValueId & intervalExclusionExpr /* out */
,const ValueId& keyColumn
) const
{
return isAKeyPredicateForColumn(predId, referencedInput,
intervalExclusionExpr, keyColumn,
getOperatorInputs());
}
NABoolean
ScanKey::isAKeyPredicateForColumn(
const ValueId & predId
,ValueId & referencedInput /* out */
,ValueId & intervalExclusionExpr /* out */
,const ValueId& keyColumn
,const ValueIdSet& inputValues
)
{
ValueIdSet externalInputs;
externalInputs += inputValues;
referencedInput = NULL_VALUE_ID;
intervalExclusionExpr = NULL_VALUE_ID;
ItemExpr * iePtr = predId.getItemExpr();
// ---------------------------------------------------------------------
// The predicate must be a unary or a binary comparison predicate.
// ---------------------------------------------------------------------
switch (iePtr->getOperatorType())
{
case ITM_VEG_PREDICATE:
case ITM_EQUAL:
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
case ITM_IS_NULL:
case ITM_ASSIGN:
// continue processing
break;
default:
return FALSE;
}
// ---------------------------------------------------------------------
// One operand of the predicate MUST be the key column.
// ---------------------------------------------------------------------
CollIndex arity = iePtr->getArity();
switch (arity)
{
case 3:
// Ternary preds are created by the optimizer to represent
// partitioning predicates boundaries
// child(0) and child(1) contain the usual operands (i.a. A > 3)
// child(2) contains the intervalExclusion flag:
intervalExclusionExpr = iePtr->child(2)->getValueId();
// "fall through" to case 2
case 2:
// if the left hand side is (represents) the given key column:
if (expressionContainsColumn(
*(iePtr->child(0)->castToItemExpr())
,keyColumn) )
{
// then the right hand side is the operand to the key pred:
referencedInput = iePtr->child(1)->castToItemExpr()->getValueId();
}
// if the rhs is the key column:
// (but in the case of an assign (use in insert), it cannot be)
else if ( (iePtr->getOperatorType() != ITM_ASSIGN)
AND
expressionContainsColumn(*(iePtr->
child(1)->castToItemExpr())
,keyColumn) )
{
// then the lhs is the operand to the key pred:
referencedInput = iePtr->child(0)->castToItemExpr()->getValueId();
}
else
// the binary operator did not contain a key col in any of its
// children, thus it cannot be a key pred:
return FALSE;
break;
case 1:
// the only unary operator that can be key col. is the
// is NULL
if (iePtr->getOperatorType() == ITM_IS_NULL)
{
if (expressionContainsColumn(*(iePtr->
child(0)->castToItemExpr())
,keyColumn))
{
return TRUE;
}
else
{
return FALSE;
}
}
else
return FALSE;
default:
// ------------------------------------------------------------------
// If this is an equality predicate, represented by a VEG predicate,
// ensure that the key column is a member of the VEG. Also, ensure
// that at least one external input value is a member of the VEG.
// ------------------------------------------------------------------
if (iePtr->getOperatorType() == ITM_VEG_PREDICATE)
{
// --------------------------------------------------------------
// If the key column is indeed a member of the VEG
// --------------------------------------------------------------
const VEG *veg =
((VEGPredicate *)iePtr)->getVEG();
const VEGReference *vegRef =
veg->getVEGReference();
if (expressionContainsColumn(*vegRef,keyColumn))
{
// ----------------------------------------------------------
// Check if any external input is also a member of the VEG.
// It is if the intersection of the external inputs
// with the elements of the VEG pred is non-empty
// ----------------------------------------------------------
ValueIdSet VEGMembers;
ValueIdSet vegSet =veg->getAllValues();
VEGMembers.replaceVEGExpressionsAndCopy(vegSet);
// The constants are no longer in the inputs, but
// below we require that they be in there, thus add
// them to the VEGMembers:
//
// Do not pass in 'TRUE' for now. Seems this is a good fix
// to recognize cache parameters in key predications. But
// more work is needed to fix qatddl04 failure due to this change.
//
//VEGMembers.getConstants(externalInputs, TRUE);
//
VEGMembers.getConstants(externalInputs);
ValueIdSet extInputs;
extInputs.replaceVEGExpressionsAndCopy(externalInputs);
VEGMembers.intersectSet(extInputs);
if (NOT VEGMembers.isEmpty()) // if VEG contains external inputs
{
// Indicate that the VEGReference is the referenced value
referencedInput = ((VEGPredicate *)iePtr)->getVEG()
->getVEGReference()->getValueId();
}
else
return FALSE; // VEG does not contain a candidate key value
}
else
return FALSE;
}
else
{
DCMPASSERT(FALSE);
return FALSE; // internal error case
}
}
// ---------------------------------------------------------------------a
// The operand of the key predicate (i.e. the "referenced input")
// MUST be a value that is covered by the
// external Inputs:
//
// We also need to rule out a pred of the form A=B, where A,B are columns
// of the same table and A is a key column.
// ---------------------------------------------------------------------a
if (referencedInput != NULL_VALUE_ID)
{
if (NOT (externalInputs.contains(referencedInput)))
{
// Create a valueIdSet that contains referencedInput
ValueIdSet referencedSet;
referencedSet += referencedInput;
// Check if it is covered by the available values
referencedSet.removeUnCoveredExprs(externalInputs);
// If the referencedInput is not covered we can't use
// this predicate as a key predicate
if (referencedSet.isEmpty())
{
// Nop, they are not covered, thus it is not
// a key pred:
return FALSE;
}
}
}
if (intervalExclusionExpr != NULL_VALUE_ID AND
NOT externalInputs.contains(intervalExclusionExpr))
{
return FALSE;
}
// If we reached here then it is a key pred:
return TRUE;
} // ScanKey::isAKeyPredicateForColumn(...)
// helper method used in ScanKey::createComputedColumnPredicates below
static ItemExpr *convertCastToNarrow(ItemExpr *expr,
CollHeap *outHeap,
void *)
{
if (expr->getOperatorType() == ITM_CAST)
{
// avoid potential errors of this cast by converting it to a Narrow
Cast *src = static_cast<Cast *>(expr);
// Tried to use NAType::errorsCanOccur to do this only in cases
// where errors are possible, but ran into multiple issues with
// the types assigned to constants and VEGRefs, so we convert
// this unconditionally to a Narrow.
return new(outHeap)
Narrow(expr->child(0),
new(outHeap) HostVar("_sys_ignored_CC_convErrorFlag",
new (outHeap) SQLInt(outHeap, TRUE,FALSE),
TRUE),
src->getType()->newCopy(outHeap));
}
else
return expr;
}
void ScanKey::createComputedColumnPredicates(ValueIdSet &predicates, /* in/out */
const ValueIdSet &keyColumns, /* in */
const ValueIdSet &operatorInputs, /* in */
ValueIdSet &generatedPredicates /* out */)
{
generatedPredicates.clear();
if (predicates.isEmpty())
return; // nothing to do
ValueIdSet keyCols = keyColumns.convertToBaseIds();
CollIndex order = 0;
for (ValueId v = keyCols.init(); keyCols.next(v); keyCols.advance(v))
{
ItemExpr *iePtr = v.getItemExpr();
switch (iePtr->getOperatorType())
{
case ITM_BASECOLUMN:
{
if (((BaseColumn *) iePtr)->getNAColumn()->isComputedColumn())
{
// get the keyColumns referenced in the computed Column expr
BaseColumn * bcol = (BaseColumn *) iePtr;
ItemExpr * compExpr = bcol->getComputedColumnExpr().getItemExpr();
ValueIdSet potentialPredicates(predicates);
ValueIdSet keyColsReferencedByCompExpr;
ValueIdSet keyPredicatesOnCC;
ValueIdMap colToKeyValueMap;
bcol->getUnderlyingColumnsForCC(keyColsReferencedByCompExpr);
potentialPredicates = potentialPredicates.replaceRangeSpecRefs();
if (keyColsReferencedByCompExpr.entries() > 1 ||
!bcol->getNAColumn()->isDivisioningColumn())
{
// if the computed column expression references
// multiple key columns or is not a divisioning
// column, we only know how to mirror that
// keyPredicate if it is an equiPred.
potentialPredicates.weedOutNonEquiPreds();
}
// make sure we have a key pred for all the columns
// used in the expression of the computed column
NABoolean predsForAllUnderlyingCols = TRUE;
for (ValueId u=keyColsReferencedByCompExpr.init();
keyColsReferencedByCompExpr.next(u);
keyColsReferencedByCompExpr.advance(u))
{
NABoolean foundPredForThisCol = FALSE;
for (ValueId p=potentialPredicates.init();
potentialPredicates.next(p);
potentialPredicates.advance(p))
{
ValueId keyValueExpr;
ValueId dummy;
if (isAKeyPredicateForColumn(p,keyValueExpr,dummy,u,operatorInputs))
{
foundPredForThisCol = TRUE;
// Farther down we are going to replace any occurrences of column
// u with the expression keyValueExpr in the ItemExpr tree compExpr
// that computes column v. Note that compExpr is written usually in
// terms of BaseColumns except for cases like key predicates. So
// add base column and its VEGRefs to the map.
DCMPASSERT(u.getItemExpr()->getOperatorType() == ITM_BASECOLUMN);
BaseColumn *bc = (BaseColumn *)(u.getItemExpr());
colToKeyValueMap.addMapEntry(u,keyValueExpr);
colToKeyValueMap.addMapEntry(bc->getTableDesc()->
getColumnVEGList()[bc->getColNumber()],
keyValueExpr);
keyPredicatesOnCC.insert(p);
}
}
if (!foundPredForThisCol)
predsForAllUnderlyingCols = FALSE;
}
if (predsForAllUnderlyingCols)
{
// create an expression to add to the keyPreds
// that will be used to generate begin-end key exprs
if (bcol->getNAColumn()->isDivisioningColumn())
generatedPredicates +=
keyPredicatesOnCC.createMirrorPreds(v, keyColsReferencedByCompExpr);
else
{
// for columns other than divisioning cols, use
// a ValueIdMap to do the rewrite for "=" preds,
// this handles compExpr trees that reference
// multiple base columns
// use the basecolumn Veg, using the basecolumn byitself can cause issues
// during codegen downstream
ValueId egVid = bcol->getTableDesc()->getColumnVEGList()[bcol->getColNumber()];
// The computed column expression may contain CAST operators
// that could cause conversion errors, which we don't want
// to handle here, they should be handled in the underlying
// key columns. Change those CASTs to NARROWs and ignore the
// error indicators of those Narrow operators.
compExpr = compExpr->treeWalk(convertCastToNarrow,
CmpCommon::statementHeap());
compExpr->synthTypeAndValueId();
// create a new predicate bcol = compExpr
ItemExpr *mirrorPred =
new(CmpCommon::statementHeap()) BiRelat(
ITM_EQUAL,
egVid.getItemExpr(),
compExpr);
mirrorPred->synthTypeAndValueId();
ValueId mpValId = mirrorPred->getValueId();
ValueId mpValIdRewritten;
// now rewrite the predicate such that instead
// of base columns it uses the values that those
// base columns are equated to. Example (simplified):
//
// mpValId: compCol = hash(col1 + col2)
// predicates: col1 = 5 and col2 = ?
// colToKeyValueMap: col1 -> 5, col2 -> ?
// mpValIdRewritten: compCol = hash(5 + ?)
colToKeyValueMap.rewriteValueIdDown(mpValId,
mpValIdRewritten);
generatedPredicates += mpValIdRewritten;
} // not a divisioning column
} // preds for all underlying columns
} // a computed column
} // a base column
} // switch on operator type
} // loop over key columns
predicates += generatedPredicates;
} // createComputedColumnPredicates (...)
void ScanKey::getKeyPredicates(ValueIdSet &keyPredicates, /* out */
NABoolean * allKeyPredicates,/*out*/
CollIndex disjunctNumber /*in*/) const
{
// This function fills up keyPredicates with
// the key predicates in this disjunct. Thus
// it must return *exactly* the key predicates. Then,
// assert that the output comes in empty in the first place:
DCMPASSERT(keyPredicates.isEmpty());
// get predicates from disjunct
Disjunct disjunct;
//Parameters used in ScanOptimizer for MDAM costing.
//The following two parameters enables us to create an empty executor predicate list
// if all the predicates get applied as key predicates.
*allKeyPredicates = TRUE;
CollIndex keyPredThisDisjunct = CollIndex(0);
NABoolean ok = getDisjuncts().get(disjunct,disjunctNumber);
DCMPASSERT(ok); // the disjuct must exist
// add the key predicates:
// Add each predicate in this disjunct into
// the appropriate column of the cache:
// for each predicate in the i-th disjunct:
for (ValueId predId = disjunct.getAsValueIdSet().init();
disjunct.getAsValueIdSet().next(predId);
disjunct.getAsValueIdSet().advance(predId) )
{
// Changes Needed for New RangeSpecRef ItemExpression tree:
ValueIdSet vdset;
if( predId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC)
{
// Do we need to call getValueIdSetForReconsItemExpr and use it accordingly
// ( Need to think a bit )
// predId = predId.getItemExpr()->child(1).getValueId();
predId.getItemExpr()->child(1)->getLeafPredicates(vdset);
for (ValueId predIdi = vdset.init();
vdset.next(predIdi);
vdset.advance(predIdi) )
{
if (isAKeyPredicate(predIdi))
{
keyPredicates.insert(predIdi);
keyPredThisDisjunct = keyPredThisDisjunct+1;
if ( *allKeyPredicates )
{
ItemExpr* exprPtr = predIdi.getItemExpr();
// VegPred can contain non-key columns, need to check
if ( exprPtr->getOperatorType() == ITM_VEG_PREDICATE)
{
ValueIdSet predicateValues;
// Check if it contains any values that are table columns
// but not key columns:
predicateValues = ((VEGPredicate*)exprPtr)->getVEG()->getAllValues();
predicateValues.intersectSet(getNonKeyColumnSet());
*allKeyPredicates = (predicateValues.entries() == 0);
}
}
}
else
{
*allKeyPredicates = FALSE;
}
} // 2nd for RangeSpecRef
} // if ends.
else
{
if (isAKeyPredicate(predId))
{
keyPredicates.insert(predId);
keyPredThisDisjunct = keyPredThisDisjunct+1;
if ( *allKeyPredicates )
{
ItemExpr* exprPtr = predId.getItemExpr();
// VegPred can contain non-key columns, need to check
if ( exprPtr->getOperatorType() == ITM_VEG_PREDICATE)
{
ValueIdSet predicateValues;
// Check if it contains any values that are table columns
// but not key columns:
predicateValues = ((VEGPredicate*)exprPtr)->getVEG()->getAllValues();
predicateValues.intersectSet(getNonKeyColumnSet());
*allKeyPredicates = (predicateValues.entries() == 0);
}
}
}
else
{
*allKeyPredicates = FALSE;
}
}
} // for every predicate
}// getKeyPredicates(...)
void ScanKey::splitRangeSpecRef(ColumnOrderList& keyPredsByCol,
const ValueId& predId,
const ValueIdList& columns,
NABoolean firstElemInRangeSpec,
const ValueId& predIdRange) const
{
ItemExpr* exprPtr = predId.getItemExpr();
NABoolean doBreak = TRUE;
if(exprPtr->getOperatorType() == ITM_VEG_PREDICATE)
{
ValueIdSet predicateValues;
predicateValues = ((VEGPredicate*)exprPtr)->getVEG()->getAllValues();
if(predicateValues.entries() > 2 && predicateValues.referencesAConstExpr())
doBreak = FALSE;
}
// for every column in the key:
for (CollIndex i=0; i < columns.entries(); i++)
{
// if the current pred is a key predicate for the
// current column, insert it into the appropriate
// bucket:
const ValueId& column = columns[i];
if (isAKeyPredicateForColumn(predId,column))
{
if (firstElemInRangeSpec == TRUE && predIdRange != NULL_VALUE_ID)
{
keyPredsByCol.insertPredicateInColumn(predIdRange,column);
}
else if (firstElemInRangeSpec == FALSE && predIdRange != NULL_VALUE_ID)
{
// Nothing to be done.
}
else
{
// Sol 10-091202-6798, do not pass colum argument
// because key column could have index column vid and
// keyPredsByCol could have base column vid.
// Fix is controlled by CQD COMP_INT_73, set to 1 by default.
if ((ActiveSchemaDB()->getDefaults()).getAsLong(COMP_INT_73) >= 1)
keyPredsByCol.insertPredicate(predId);
else
keyPredsByCol.insertPredicateInColumn(predId,column);
}
if (doBreak)
break;
//10-050403-6270: Prior to this change the inner loop
//used to exit.This caused some of the predicates to be
//skipped if there where in a veg of the form
//VEG(T1.a1, T2.a2 , <const>) or VEG(T1.a1, T2.a2 ...)
//Now we go to all the members of the VEG.
}
} // for every column
}
// construct a key column order from the given set:
void ScanKey::getKeyColumnOrderList(ColumnOrderList& keyPredsByCol, /* out*/
const ValueIdSet& predSet) const
{
// add the key predicates:
// Add each predicate in this disjunct into
// the appropriate column of the cache:
// for each predicate in the i-th disjunct:
// 10-050403-6270:
// The Idea here is we need to generate a List of unique
// Column entries on a first seen basis. For Example :
// If we have key columns like [ d,a,b,c,d,e ] we need to
// Generate a List which will look like [ d,a,b,c,e ].
ValueIdList columns;
const ValueIdList &tempColumns = getKeyColumns();
columns.insert(tempColumns[0]);
for(CollIndex j = 1; j < tempColumns.entries(); j++)
{
NABoolean noDuplicateFound = TRUE;
for(CollIndex i=0; i < columns.entries(); i++)
{
//Check if we are actually looking at the same base table column.
if(columns[i].getNAColumn() == tempColumns[j].getNAColumn())
{
noDuplicateFound = FALSE;
break;
}
}
if(noDuplicateFound)
columns.insert(tempColumns[j]);
}
for (ValueId predId = predSet.init();
predSet.next(predId);
predSet.advance(predId) )
{
NABoolean firstElemInRangeSpec = TRUE;
if ( (CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) )
{
if ( predId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC)
{
ValueIdSet vdset;
predId.getItemExpr()->child(1)->getLeafPredicates(vdset);
for (ValueId predIdp = vdset.init();
vdset.next(predIdp);
vdset.advance(predIdp) )
{
splitRangeSpecRef(keyPredsByCol,predIdp,columns, firstElemInRangeSpec,predId.getItemExpr()->child(1)->castToItemExpr()->getValueId());
firstElemInRangeSpec = FALSE;
}
}
else
splitRangeSpecRef(keyPredsByCol,predId,columns,firstElemInRangeSpec, NULL_VALUE_ID);
}
else
splitRangeSpecRef(keyPredsByCol,predId,columns,firstElemInRangeSpec, NULL_VALUE_ID);
} // for every predicate
} // getKeyColumnOrderList(...)
//---------------------------------------------------------
// Methods for class MdamKey |
//---------------------------------------------------------
MdamKey::MdamKey(
const ValueIdList& keyColumnIdList
,const ValueIdSet& operatorInputs
,const Disjuncts& associatedDisjuncts
,const ValueIdSet& nonKeyColumnSet
,const IndexDesc * indexDesc) :
ScanKey(keyColumnIdList
,operatorInputs
,associatedDisjuncts
,nonKeyColumnSet
,indexDesc
),
columnOrderListPtrArrayPtr_(NULL),
stopColumnArray_(NULL),
sparseFlagArray_(NULL),
noExePred_(FALSE)
{
// construct the stop column array (but only if
// there are disjuncts)
if ( getDisjuncts().entries() )
{
// The default stop column is the highest:
stopColumnArray_ = new (CmpCommon::statementHeap())
CollIndex[getDisjuncts().entries()];
DCMPASSERT(stopColumnArray_ != NULL);
setAllStopColumnsToMax();
}
// all columns are sparse
sparseFlagArray_ = new (CmpCommon::statementHeap())
NABoolean[getKeyColumns().entries()];
DCMPASSERT(sparseFlagArray_ != NULL);
setAllColumnsToSparse();
} // MdamKey::MdamKey(..)
MdamKey::~MdamKey()
{
// No need to delete, MdamKey is allocated from HEAP
NADELETEBASIC(stopColumnArray_,HEAP); // built in types
NADELETEBASIC(sparseFlagArray_,HEAP); // built in types
if (columnOrderListPtrArrayPtr_ != NULL)
{
for (CollIndex i=0; i < columnOrderListPtrArrayPtr_->entries(); i++)
delete (*columnOrderListPtrArrayPtr_)[i];
delete columnOrderListPtrArrayPtr_;
}
} // ~MdamKey
void MdamKey::setAllStopColumnsToMax( )
{
for (CollIndex i=0; i < getDisjuncts().entries(); i++)
{
DCMPASSERT(getKeyColumns().entries() > 0);
setStopColumn(i,getKeyColumns().entries()-1);
}
}
void MdamKey::setAllColumnsToSparse()
{
for (CollIndex i=0; i < getKeyColumns().entries(); i++)
sparseFlagArray_[i] = TRUE;
}
void MdamKey::setStopColumn(CollIndex disjunctNumber,
CollIndex columnOrder)
{
DCMPASSERT(disjunctNumber >= 0 AND disjunctNumber < getDisjuncts().entries());
DCMPASSERT(columnOrder >= 0 AND columnOrder < getKeyColumns().entries());
DCMPASSERT(stopColumnArray_ != NULL);
stopColumnArray_[disjunctNumber] = columnOrder;
}
CollIndex MdamKey::getStopColumn(CollIndex disjunctNumber) const
{
DCMPASSERT(disjunctNumber >= 0 AND disjunctNumber < getDisjuncts().entries());
DCMPASSERT(stopColumnArray_ != NULL);
return stopColumnArray_[disjunctNumber];
}
NABoolean MdamKey::getSparseFlag(CollIndex columnOrder) const
{
DCMPASSERT(columnOrder >= 0 AND columnOrder < getKeyColumns().entries());
DCMPASSERT(sparseFlagArray_ != NULL);
return sparseFlagArray_[columnOrder];
}
void MdamKey::setSparseFlag(CollIndex columnOrder, NABoolean isSparse)
{
DCMPASSERT(columnOrder >= 0 AND columnOrder < getKeyColumns().entries());
DCMPASSERT(sparseFlagArray_ != NULL);
sparseFlagArray_[columnOrder] = isSparse;
}
void MdamKey::reuseMdamKeyInfo(MdamKey * other)
{
CollIndex numOfDisjuncts = (other->getDisjuncts()).entries();
if ( numOfDisjuncts > 0 )
{
DCMPASSERT( numOfDisjuncts == getDisjuncts().entries() );
for (CollIndex i=0; i<numOfDisjuncts; i++)
setStopColumn(i, other->getStopColumn(i));
}
CollIndex numOfKeyColumns = (other->getKeyColumns()).entries();
DCMPASSERT( numOfKeyColumns == getKeyColumns().entries() );
for (CollIndex j=0; j<numOfKeyColumns; j++)
setSparseFlag(j, other->getSparseFlag(j));
if ( other->getNoExePred() )
setNoExePred();
}
void MdamKey::preCodeGen(ValueIdSet& executorPredicates,
const ValueIdSet& selectionPredicates,
const ValueIdSet& availableValues,
const ValueIdSet& inputValues,
VEGRewritePairs * vegPairsPtr,
NABoolean replicateExpression,
NABoolean partKeyPredsAdded)
{
//--------
// The actions taken here are:
// 1.- Generate the generator data structures depending on
// the access path case decided by the optimizer.
// While the data structures are being generated,
// any predicates in them are rewritten too (i.e.
// VEG predicates are transformed to real predicates).
//
// There are three access paths possible:
// (see mdam's compiler design doc for details)
// 1.- Non-MDAM Common predicates
// 2.- Mdam Common predicates
// 3.- Disjuncts
// Actions for access path 1:
// These actions do not correspond to MdamKey, they are
// done in FileScan::preCodeGen: (they need data from
// MdamKey however:
// 1.1 Generate (and rewrite preds) SearchKey
// Actions for access path 1 and 2:
// 2.1 Generate ColumnOrderListArray (and rewrite its predicates)
// Access path 1 & 2 look the same to the generator (as far as the
// the generator is concerned, both ara MDAM access)
// (The executor predicates are computed outside of MdamKey
// by FileScan)
// 1.1 Generate (and rewrite preds) ColumnOrderListArray:
columnOrderListPtrArrayPtr_ = new (CmpCommon::statementHeap())
ColumnOrderListPtrArray;
DCMPASSERT(columnOrderListPtrArrayPtr_ != NULL);
// for every disjunct:
ValueIdSet keyPredicates;
Disjunct disjunct;
ValueIdSet vegKeyNonKeyPreds;
// Prepare the executor predicates:
//getNoExePred() returns the value for noExePred_ identifying if
//all the predicates has been applied at dp2 so no executor preds
//are necessary
//
ValueIdSet keyColSet(getKeyColumns());
ValueIdSet inSet(selectionPredicates);
usePartofSelectionPredicatesFromTheItemExpressionTree(inSet,keyColSet.convertToBaseIds());
Disjuncts *curDisjuncts = new HEAP MaterialDisjuncts(inSet);
if (CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON &&
selectionPredicates.entries() &&
!getNoExePred())
{
ValueIdSet temp;
ItemExpr * inputItemExprTree =
selectionPredicates.rebuildExprTree(ITM_AND,FALSE,FALSE);
ItemExpr * resultOld = revertBackToOldTree(CmpCommon::statementHeap(),
inputItemExprTree);
executorPredicates.clear();
resultOld->convertToValueIdSet(executorPredicates, NULL, ITM_AND, FALSE);
doNotReplaceAnItemExpressionForLikePredicates(resultOld,executorPredicates,resultOld);
// executorPredicates.clear();
// revertBackToOldTreeUsingValueIdSet(const_cast<ValueIdSet &>(selectionPredicates), executorPredicates);
executorPredicates.replaceVEGExpressions
(availableValues, inputValues,
FALSE, // no need for key predicate generation here
vegPairsPtr, replicateExpression);
}
else if( NOT getNoExePred())
{
executorPredicates = selectionPredicates;
executorPredicates.replaceVEGExpressions
(availableValues, inputValues,
FALSE, // no need for key predicate generation here
vegPairsPtr, replicateExpression);
}
// transform VEGPreds and VEGRefs in the mdam key:
// If the curDisjuncts have more than one disjunct and the chosen plan
// only has 1 disjunct then the mdam common predicates case has occurred
if (getDisjuncts().entries() == 1 AND curDisjuncts->entries() > 1)
{
ValueIdSet commonPreds = curDisjuncts->getCommonPredicates();
CMPASSERT(NOT commonPreds.isEmpty());
delete curDisjuncts;
curDisjuncts = new HEAP MaterialDisjuncts(commonPreds);
}
CollIndex i = 0;
for (i=0; i < curDisjuncts->entries(); i++)
{
// Get key predicates from the disjunct:
curDisjuncts->get(disjunct,i);
appendKeyPredicates(keyPredicates,disjunct.getAsValueIdSet(), inputValues);
// Compute VEG preds that contain
// key columns as well as non-key column:
replicateNonKeyVEGPredicates( keyPredicates,
vegKeyNonKeyPreds
);
// Rewrite disjunct predicates:
// enforce generation of key predicates by:
// 1) Setting the generate key predicates flag
keyPredicates.replaceVEGExpressions(availableValues,
inputValues,
TRUE, // generate key predicates
vegPairsPtr,
replicateExpression,
NULL,
NULL,
getIndexDesc() // pass indexDesc
// so we can pick a
// keyCol.
);
// Create & Populate the columnOrderList:
ColumnOrderList *columnOrderListPtr =
new (CmpCommon::statementHeap())
ColumnOrderList(getKeyColumns());
DCMPASSERT(columnOrderListPtr != NULL);
columnOrderListPtr->append(keyPredicates);
// Insert it into array:
columnOrderListPtrArrayPtr_->insertAt(i,columnOrderListPtr);
// prepare for nex round:
keyPredicates.clear();
disjunct.clear();
} // for all disjuncts
// --------------------------------------------------------------------
// To the optimizer, an empty key disjunct means "universally true",
// i.e. it represents a full table scan. The optimizer can generate
// empty disjuncts in many cases, especially when MDAM is forced.
// An empty key disjunct means, in the optimizer, a universally true
// predicate, i.e. a full table scan.,The generator
// and executor already generate a full table scan for the case
// of mdamkeys with a *single*, empty disjunct. However, it does
// not generate a full table scan for the case of multiple disjuncts
// and at least one of them is empty.
//
// I added code in MdamKey::preCodeGen that detects whether there
// are more than one disjunct in the mdamkey and one of them is
// empty. In this case, an mdamkey with a single, empty, disjunct
// is generated. Thus, the generator will generate the appropiate
// full table scan.
//
// A warning in precode gen might be useful to indicate that
// the MdamKey will perform a full table scan (not what the user
// might have wanted). This code *does not* print any
// *official* warnings (but it prints a warning to the console).
// --------------------------------------------------------------------
// Note: The "for" loop below does two very important things:
// 1. Detects empty disjuncts (as discussed above)
//
// and, while it is doing that,
//
// 2. Sets the stop column for each disjunct.
//
// Note that if we don't set the stop column, then MDAM will traverse
// *all* of the key columns, whether they have predicates or not, and
// performance will be terrible.
NABoolean isEmpty = FALSE;
for (i=0; i < curDisjuncts->entries(); i++)
{
(*columnOrderListPtrArrayPtr_)[i]->setStopColumn(getStopColumn(i));
// Find out if the disjunct has key preds:
const ColumnOrderList& coList= *((*columnOrderListPtrArrayPtr_)[i]);
if (!partKeyPredsAdded)
isEmpty = TRUE; // assume it is empty
for (CollIndex j=0; j < coList.entries(); j++)
{
if (coList[j] AND (NOT coList[j]->isEmpty()))
{
isEmpty=FALSE;
break;
}
}
if (isEmpty)
{
FSOWARNING("Empty disjunct");
break;
}
} // set stop columns and also find out whether there is an empty disjunct
if (isEmpty)
{
// There is an empty disjunct!
// Create an empty columnOrderList:
ColumnOrderList *columnOrderListPtr =
new (CmpCommon::statementHeap())
ColumnOrderList(getKeyColumns());
DCMPASSERT(columnOrderListPtr != NULL);
columnOrderListPtr->append(keyPredicates);
// get rid of all other disjuncts:
columnOrderListPtrArrayPtr_->clear();
// Insert the new, empty, disjunct into array:
columnOrderListPtrArrayPtr_->insertAt(0,columnOrderListPtr);
// all predicates are executor predicates..
}
else
{
if(NOT getNoExePred()){
// No empty disjuncts; generate appropiate executor
// preds...
//
// common key predicates are not executor preds,
// and some key preds must be replicated in executor
// preds:
// The key predicates (up to the MAX stop column)
// common to all disjuncts can be safely
// removed from the executor predicates:
ValueIdSet commonKeyPredicates;
getColumnOrderListPtrArray().
computeCommonKeyPredicates(commonKeyPredicates);
ColumnOrderList keyPredsByCol(getKeyColumns());
keyPredsByCol.append(commonKeyPredicates);
// compute MIN stop column
CollIndex minStopColumn=getStopColumn(0);
CollIndex i = 0;
for (i=0; i < getKeyDisjunctEntries(); i++)
{
CollIndex curStopColumn = getStopColumn(i);
if (curStopColumn < minStopColumn)
{
minStopColumn = curStopColumn;
}
}
// now remove all common key predicates that are
// already in the mdam key:
// Fix for MDAM issue with wrong results
// Soln. 10-100508-0134
// We can safely remove the common key predicates only
// until the minimum stop column as only these would be
// truly common to all disjuncts.
// The earlier code was removing the common predicates until
// the maximum stop column, if any of the disjuncts had a stop
// column less than the max stop column, the common key predicate
// cannot be removed from the executor predicates as it would not
// be used by the particular mdam scan disjunct.
for (i=0; i<=minStopColumn; i++)
{
const ValueIdSet *predsPtr = keyPredsByCol[i];
if (predsPtr AND NOT predsPtr->isEmpty())
{
executorPredicates.remove(*predsPtr);
}
}
// Now add the preds. resulting from VEG preds that contain
// key columns as well as non-key columns
// (for the case when:
// 24:
// VEGPred_24(VEG{T1.A(1),T1.D(4),T1.A(8),T1.D(11),T1A.A(15),
// T1A.A(16),4(19)}
// and we have key column t1.A and non-key column
// T1.D the rewrite resolves
// to:
//60: (T1.A = 4) and (T1.D = 4)
// Thus, if we don't do this, exe pred. is empty and we get
// the wrong semantics
vegKeyNonKeyPreds.replaceVEGExpressions
(availableValues,
inputValues,
FALSE, // no need for key predicate generation here
vegPairsPtr,
replicateExpression
);
executorPredicates.insert(vegKeyNonKeyPreds);
}
} // mdamkey does not contain empty disjuncts
delete curDisjuncts;
} // MdamKey::preCodeGen(...)
void MdamKey::
appendKeyPredicates(ValueIdSet& keyPredicates, const ValueIdSet& predicates,
const ValueIdSet& inputValues) const
{
// for each predicate in predicates:
for (ValueId predId = predicates.init();
predicates.next(predId);
predicates.advance(predId) )
{
// append it to keyPredicates if it is indeed a key predicate:
NABoolean firstElemInRangeSpec = TRUE;
if ( (CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON ) &&
( predId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC) )
{
ValueIdSet vdset;
predId.getItemExpr()->child(1)->getLeafPredicates(vdset);
for (ValueId predIdp = vdset.init();
vdset.next(predIdp);
vdset.advance(predIdp) )
{
// Let it flow through the all the predicates in the set to validate, in case there are any errors
if (isAKeyPredicate(predIdp))
{
if (firstElemInRangeSpec){
keyPredicates.insert(predId.getItemExpr()->child(1)->castToItemExpr()->getValueId());
break;
}
else
{} // Do nothing, since already added as INLIST, getType should show as inlist
firstElemInRangeSpec = FALSE;
}
}
}
else
{
if (isAKeyPredicate(predId))
keyPredicates.insert(predId);
else if (isAPartKeyPredicateForMdam(predId, inputValues))
keyPredicates.insert(predId);
}
// Old Code
//if (isAKeyPredicate(predId))
//keyPredicates.insert(predId);
}
} // MdamKey::appendKeyPredicates(..)
NABoolean
MdamKey::isAPartKeyPredicateForMdam(const ValueId& predId, const ValueIdSet& inputValues) const
{
NABoolean retVal = FALSE;
ValueId referencedInput = NULL_VALUE_ID;
ValueId intervalExclusionExpr = NULL_VALUE_ID;
const ValueIdList& keyCols = getKeyColumns();
ValueIdSet operatorInputs(inputValues);
operatorInputs += getOperatorInputs();
for (CollIndex i=0; i < keyCols.entries(); i++)
{
const ValueId& col= keyCols[i];
if (isAKeyPredicateForColumn(predId
,referencedInput
,intervalExclusionExpr
,col
,operatorInputs
))
{
retVal = TRUE;
break;
}
}
return retVal;
}
void MdamKey::print( FILE* ofd,
const char* indent,
const char* title) const
{
// is print enabled?
char *ev = getenv("MDAM_PRINT");
if (ev != NULL AND strcmp(ev,"ON")==0)
{
fprintf(ofd,"%s\n",title);
// Print the key columns:
const ValueIdList& keyColumns = getKeyColumns();
DCMPASSERT(NOT keyColumns.isEmpty());
fprintf(ofd,"//--- Key Columns:\n");
keyColumns.print(ofd,indent,"");
fprintf(ofd,"\n\n");
// now print the disjuncts for every column, if any:
getDisjuncts().print(ofd,indent,"//--- disjuncts_:\n");
fprintf(ofd,"\n");
// print stop columns
CollIndex i = 0;
for (i=0; i < getDisjuncts().entries(); i++)
fprintf(ofd,"stopColumnArray_[%d] = %d\n", i,
getStopColumn(i));
// print sparse flag:
CollIndex maxStopColumn = getStopColumn(0);
for (i=1; i < getDisjuncts().entries(); i++)
{
if (maxStopColumn < getStopColumn(i))
maxStopColumn = getStopColumn(i);
}
for (i=0; i <= maxStopColumn; i++)
fprintf(ofd,
"Column: [%d] is %s\n",
i,
(isColumnSparse(i) ? "Sparse" : "Dense"));
// print the key predicates by column:
if (columnOrderListPtrArrayPtr_)
{
columnOrderListPtrArrayPtr_->print();
// compute common key predicates:
ValueIdSet commonKeyPredicates;
columnOrderListPtrArrayPtr_->
computeCommonKeyPredicates(commonKeyPredicates);
commonKeyPredicates.print(ofd,indent,"Common key predicates: ");
}
else
fprintf(ofd,"columnOrderListPtrArrayPtr_ is empty\n");
fprintf(ofd,"\n");
} // is printing enabled?
} // MdamKey::print()
const ColumnOrderListPtrArray& MdamKey::getColumnOrderListPtrArray() const
{
// array must have been generated in pre-code
// gen to use it:
DCMPASSERT(columnOrderListPtrArrayPtr_ != NULL);
return *columnOrderListPtrArrayPtr_;
}
CollIndex MdamKey::getKeyDisjunctEntries() const
{
DCMPASSERT(getDisjuncts().entries()>0);
return getDisjuncts().entries();
}
void MdamKey::getKeyPredicatesByColumn(ColumnOrderList& keyPredsByCol,
CollIndex disjunctNumber) const
{
// get predicates from disjunct
Disjunct disjunct;
NABoolean ok = getDisjuncts().get(disjunct,disjunctNumber);
DCMPASSERT(ok); // the disjuct must exist
keyPredsByCol.clearPredicates(); // to guarantee semantics
getKeyColumnOrderList(keyPredsByCol,disjunct.getAsValueIdSet());
// All columns return their predicate sets:
// (The concrete class may change the stop column
// depending on particular criteria. For instance,
// for SearchKey we are interested only in orders
// that looklike [=]^*[RANGE] (seen as a regular expression) )
keyPredsByCol.setStopColumn(keyPredsByCol.entries()-1);
}// MdamKey::getKeyPredicatesByColumn(...)
//---------------------------------------------------------
// Methods for class Disjuncts |
//---------------------------------------------------------
void Disjuncts::computeCommonPredicates()
{
// the common predicates are the intersection of all the disjuncts
// in the local disjunct array:
// they are used to compute the cost for non-mdam and mdam common
// cases.
if (entries() > 0) {
DCMPASSERT(commonPredicates_.entries()==0);
Disjunct disjunct;
for (CollIndex i=0; i < entries(); i++)
{
get(disjunct,i);
if(CmpCommon::getDefault(RANGESPEC_TRANSFORMATION) == DF_ON )
{
ValueIdSet inVidset = disjunct.getAsValueIdSet();
ValueIdSet outVidset,parsedVs;
for (ValueId predId = inVidset.init();
inVidset.next(predId);
inVidset.advance(predId) )
{
//TRAFODION-2988
if( ITM_RANGE_SPEC_FUNC == predId.getItemExpr()->getOperatorType() )
{
if( ITM_AND == predId.getItemExpr()->child(1)->getOperatorType() )
{
predId.getItemExpr()->child(1)->convertToValueIdSet(parsedVs, NULL, ITM_AND, FALSE);
outVidset +=parsedVs;
}
else if( ITM_OR == predId.getItemExpr()->child(1)->getOperatorType() )
{//add deal with OR operator
outVidset +=predId;
}
else
{
outVidset += predId.getItemExpr()->child(1)->castToItemExpr()->getValueId();
}
}
else
outVidset +=predId;
//TRAFODION-2988
parsedVs.clear();
}
if (i==0)
commonPredicates_.insert(outVidset);
else
commonPredicates_.intersectSet(outVidset);
}
else
{
if (i==0)
commonPredicates_.insert(disjunct.getAsValueIdSet());
else
commonPredicates_.intersectSet(disjunct.getAsValueIdSet());
}
}
}
} // Disjuncts::computeCommonPredicates(..)
void Disjuncts::print( FILE* ofd,
const char* indent,
const char* title) const
{
fprintf(ofd,"%s:\n",title);
if (entries() > 0)
{
commonPredicates_.print(ofd,indent,"\n\nCommon predicates_");
// print disjuncts:
NAString disStr(CmpCommon::statementHeap());
Disjunct disjunct;
fprintf(ofd,"\n\nDisjuncts_ (one by one):\n");
for (CollIndex i=0; get(disjunct,i); i++) {
disStr = NAString("Disjunct[") +
NAString((unsigned char)('0'+Int32(i))) + NAString("]: ");
disjunct.print(ofd,indent,disStr);
disjunct.clear();
}
}
else
fprintf(ofd,"No disjunct entries...\n");
} // print()
void Disjuncts::print() const
{
print(stdout);
}
NABoolean Disjuncts::
get(Disjunct& disjunct, CollIndex i) const
{
if (i < entries())
{
disjunct.clear();
return TRUE;
}
else
return FALSE;
}
DisjunctArray * Disjuncts::createEmptyDisjunctArray() const
{
ValueIdSet *emptyDisjunctPtr = new HEAP ValueIdSet;
DCMPASSERT(emptyDisjunctPtr != NULL);
DisjunctArray *disjunctArrayPtr = new HEAP DisjunctArray(emptyDisjunctPtr);
DCMPASSERT(disjunctArrayPtr != NULL);
return disjunctArrayPtr;
}
NABoolean isOrItemExpr(ItemExpr* iePtr)
{
return (iePtr && iePtr->getOperatorType() == ITM_OR);
}
NABoolean isAndOrItemExpr(ItemExpr* iePtr)
{
return (iePtr &&
(iePtr->getOperatorType() == ITM_AND ||
iePtr->getOperatorType() == ITM_OR)
)
;
}
NABoolean Disjuncts::containsSomePredsInRanges(funcPtrT funcP) const
{
// CollIndex order;
// CollIndex numOfKeyCols=keyPredsByCol.entries();
// KeyColumns::KeyColumn::KeyColumnType typeOfRange = KeyColumns::KeyColumn::EMPTY;
Disjunct disjunct;
ValueIdSet vs;
for (CollIndex i=0; i < entries(); i++)
{
this->get(disjunct,i);
vs = disjunct.getAsValueIdSet();
for (ValueId predId = vs.init();
vs.next(predId);
vs.advance(predId) )
{
if(predId.getItemExpr()->getOperatorType() == ITM_RANGE_SPEC_FUNC )
{
if ((*funcP)(predId.getItemExpr()->child(1)))
return TRUE;
}
}
}
return FALSE;
}
NABoolean Disjuncts::containsOrPredsInRanges() const
{
return containsSomePredsInRanges(isOrItemExpr);
}
NABoolean Disjuncts::containsAndorOrPredsInRanges() const
{
return containsSomePredsInRanges(isAndOrItemExpr);
/*
if (keyPredsByCol.containsPredicates())
{
for (order = 0; order < numOfKeyCols; order++)
{
if (keyPredsByCol.getPredicateExpressionPtr(order) != NULL)
typeOfRange = keyPredsByCol.getPredicateExpressionPtr(order)->getType();
else
typeOfRange = KeyColumns::KeyColumn::EMPTY;
if (typeOfRange == KeyColumns::KeyColumn::INLIST )
// ||
// typeOfRange != KeyColumns::KeyColumn::RANGE)
return TRUE;
} // end of for-loop
} // if (containsPredicates())
*/
//return FALSE;
}
//---------------------------------------------------------
// Methods for class DisjunctsDisjuncts |
//---------------------------------------------------------
MaterialDisjuncts::MaterialDisjuncts(const ValueIdSet& selectionPredicates) :
disjunctArrayPtr_(NULL)
{
createDisjunctArray(selectionPredicates);
// The semantics is such that at least one entry must be
// created (even if selectionPredicates is empty, in this
// case we have one disjunct array with exactly one entry:
// the empty disjunct)
DCMPASSERT(disjunctArrayPtr_->entries() >= 1);
computeCommonPredicates();
}
MaterialDisjuncts::MaterialDisjuncts(Disjunct* disjunct)
{
disjunctArrayPtr_ = new HEAP
DisjunctArray(new HEAP ValueIdSet(disjunct->getAsValueIdSet()));
DCMPASSERT(disjunctArrayPtr_->entries() == 1);
}
MaterialDisjuncts::~MaterialDisjuncts()
{
// $$ Do we still need to delete?
for (CollIndex i=0; i < disjunctArrayPtr_->entries(); i++)
delete (*disjunctArrayPtr_)[i];
}
void MaterialDisjuncts::
createDisjunctArray(const ValueIdSet& selectionPredicates)
{
// $$$ In the future we may want to fuse class DisjunctArray
// $$$ and Disjuncts, especially when the DisjunctGlobal
// $$$ is no longer needed.
// Get the DisjunctArray built until now for the query
DisjunctArray * prevDisjunctArray = NULL;
DisjunctArray * currDisjunctArray = NULL;
// ---------------------------------------------------------------------
// Loop through the value ids in the ValueIdSet. For each value id get
// its ItemExpr node. Perform an mdamTreeWalk on the ItemExpr sub-tree
// represented by this node. A DisjunctArray built during this tree
// walk is returned. AND this DisjunctArray with the previous existing
// DisjunctArray. If no DisjunctArray existed before (is NULL) replace
// it with this DisjunctArray.
// ---------------------------------------------------------------------
for (ValueId exprId = selectionPredicates.init();
selectionPredicates.next(exprId);
selectionPredicates.advance(exprId) )
{
currDisjunctArray = (exprId.getItemExpr())->mdamTreeWalk();
if ( prevDisjunctArray == NULL )
prevDisjunctArray = currDisjunctArray;
else
prevDisjunctArray = mdamANDDisjunctArrays( prevDisjunctArray,
currDisjunctArray );
if (!prevDisjunctArray)
break;
} // loop thru the value ids in the ValueIdSet
// If the previous disjunct array is NULL then the selections
// predicates are empty. Thus create a disjunct array with
// exactly one disjunct, the empty disjunct.
// If it is not NULL then it means that the selection preds. contained
// a pred tree and there was a disj. array created. Then,
// set the disjunctArrayPtr:
if (prevDisjunctArray)
{
// A disjunct array was created,
// Does it have at least one entry?
if (prevDisjunctArray->entries() == 0)
{
// No.
// A disjunct array must have at least one entry:
// an empty set of value id's:
delete prevDisjunctArray;
prevDisjunctArray = createEmptyDisjunctArray();
}
// Set the ptr:
disjunctArrayPtr_ = prevDisjunctArray;
}
else
{
if (CmpCommon::getDefault(COMP_BOOL_21) == DF_OFF)
disjunctArrayPtr_ = new STMTHEAP DisjunctArray(new STMTHEAP ValueIdSet(selectionPredicates));
else
// create a disjunct array with the empty disjunct as it
// sole entry:
disjunctArrayPtr_ = createEmptyDisjunctArray();
}
} // Disjuncts::createDisjunctArray(...)
NABoolean MaterialDisjuncts::
get(Disjunct& disjunct, CollIndex i) const
{
NABoolean retVal = FALSE;
if (Disjuncts::get(disjunct,i)) {
disjunct.insertSet(*(*disjunctArrayPtr_)[i]);
retVal = TRUE;
}
return retVal;
}// MaterialDisjuncts::get(...)
//---------------------------------------------------------
// Methods for class KeyColumn |
//---------------------------------------------------------
void KeyColumns::KeyColumn::insert(const ValueId &predicate)
{
// Figure out the type of the predicate expression for
// the column when the new
// predicate is added:
switch(getType())
{
case EMPTY:
// if the column is EMPTY, then its new type
// is whatever's type is the new predicate:
if (predicate.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE)
{
VEG * predVEG = ((VEGPredicate *)(predicate.getItemExpr()))->getVEG();
const ValueIdSet & VEGGroup = predVEG->getAllValues();
if (VEGGroup.referencesConstExprCount() > 1)
setType(CONFLICT_EQUALS);
else
setType(getType(predicate));
}
else
setType(getType(predicate));
break;
case EQUAL:
// we have a conflict (but maybe an EQUAL) in any case
setType(CONFLICT_EQUALS);
break;
case RANGE:
// If the column is already a range,
// the only way that we cannot have a conflict is if
// 1.- There is only one predicate in the column AND
// 2.- the pred is of opposite
// direction to the existing predicate in the column:
// ELSE there is a conflict
if (getPredicates().entries() == 1)
{
OperatorTypeEnum newPredOperType =
predicate.getItemExpr()->getOperatorType();
ValueId predId = getPredicates().init();
getPredicates().next(predId);
OperatorTypeEnum exPredOperType =
predId.getItemExpr()->getOperatorType();
switch (newPredOperType)
{
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
if (exPredOperType == ITM_GREATER
OR
exPredOperType == ITM_GREATER_EQ
OR
exPredOperType == ITM_GREATER_OR_GE)
{
setType(RANGE);
}
else
{
setType(CONFLICT);
}
break;
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
if (exPredOperType == ITM_LESS
OR
exPredOperType == ITM_LESS_EQ
OR
exPredOperType == ITM_LESS_OR_LE)
{
setType(RANGE);
}
else
{
setType(CONFLICT);
}
break;
case ITM_VEG_PREDICATE:
case ITM_EQUAL:
setType(CONFLICT_EQUALS);
break;
default:
// the new predicate for this column is incompatible
// with a range (i.e. old pred: B > 2, new predicate:
// B=3
setType(CONFLICT);
} // inner switch
} // if there is one predicate
else
{
// There is already a full range (i.e. A > 3 and A < :hv)
// The only outcome is CONFLICT:
if (getType(predicate) == EQUAL)
{
setType(CONFLICT_EQUALS);
}
else
{
setType(CONFLICT);
}
}
break;
case IS_NULL:
// else if the column is NULL then if the new operator
// is not null we are in a conflict
if (getType(predicate) != IS_NULL)
{
setType(CONFLICT);
}
break;
case INLIST:
// else if the column is INLIST and if the new operator
// is not INLIST then it must be a CONFLICT (am I getting philosophical
// here?)
if (getType(predicate) == EQUAL)
{
setType(CONFLICT_EQUALS);
}
else
{
setType(CONFLICT);
}
break;
case CONFLICT_EQUALS:
case CONFLICT:
// it stays conflict
break;
default:
// The column MUST be EMPTY, EQUAL, INLIST, IS_NULL, RANGE, etc
DCMPASSERT(FALSE);
} // switch getType(predicate)
// Now insert the new predicate:
predicatesForColumn_.insert(predicate);
} // KeyColumns::KeyColumn::insert(const ValueId &predicate)
KeyColumns::KeyColumn::KeyColumnType
KeyColumns::KeyColumn::getType(const ValueId& predId) const
{
switch(predId.getItemExpr()->getOperatorType())
{
case ITM_VEG_PREDICATE:
case ITM_EQUAL:
return EQUAL;
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
return RANGE;
case ITM_IS_NULL:
return IS_NULL;
case ITM_IN:
case ITM_OR:// RangeSpecRef Item Expression Handling
return INLIST;
case ITM_ASSIGN:
return ASSIGN;
default:
CMPABORT; // unsupported predicate
break;
}
return INLIST; // to keep the compiler happy, fix this!
} // getType(predId)
void KeyColumns::KeyColumn::print( FILE* ofd,
const char* indent,
const char* /*title*/ ) const
{
char message[80];
switch(getType())
{
case EMPTY:
strcpy(message,"EMPTY");
break;
case EQUAL:
strcpy(message,"EQUAL");
break;
case RANGE:
strcpy(message,"RANGE");
break;
case INLIST:
strcpy(message,"INLIST");
break;
case CONFLICT:
strcpy(message,"CONFLICT");
break;
case CONFLICT_EQUALS:
strcpy(message,"CONFLICT_EQUALS");
break;
case ASSIGN:
strcpy(message,"ASSIGN");
break;
default:
strcpy(message,"Unknown type...");
break;
}
// print predicates:
fprintf(ofd,"The type of this column is: [%s]\n"
"and its predicates are:\n",
message);
getPredicates().print(ofd,indent,"");
fprintf(ofd,"\n");
}
void KeyColumns::KeyColumn::clearPredicates()
{
// -----------------------------------------------------------------------
// Remove all predicates
// -----------------------------------------------------------------------
predicatesForColumn_.clear();
// -----------------------------------------------------------------------
// And reset the type:
// -----------------------------------------------------------------------
setType(EMPTY);
} // clearPredicates()
// Resolving a conflict means picking some predicate that
// can be used as a key predicate among several conflicting
// key preds for the same column. For instance A < 2 and A < 3
// are conflicting predicates. Resolve conflict will pick any
// one of them (in this case we may like to get A < 2, but
// this is difficult to do in the optimizer)
void KeyColumns::KeyColumn::resolveConflict()
{
if (getType() == CONFLICT_EQUALS OR
getType() == CONFLICT)
{
// ------------------------------------------------------------------
// Try to pick an equal, if there is one:
// ------------------------------------------------------------------
const ValueIdSet& preds = getPredicates();
ValueId pred; // outside the loop so it lives until it is inserted...
ValueId *chosenPredPtr = NULL;
for (pred = preds.init();
preds.next(pred);
preds.advance(pred))
{
if (pred.getItemExpr()->getOperatorType() == ITM_EQUAL
OR
pred.getItemExpr()->getOperatorType() == ITM_VEG_PREDICATE)
{
chosenPredPtr = &pred;
break; // important!
}
} // for
// -----------------------------------------------------------
// If we did not find the equal, pick any other that makes
// sense for a search key (i.e. not a IN, etc):
// $$$ Should we pick the join predicate if there is one?
// -----------------------------------------------------------
if (chosenPredPtr == NULL)
{
NABoolean predFound = FALSE;
ValueId *equalPredPtr = NULL;
for (pred = preds.init();
chosenPredPtr == NULL AND preds.next(pred);
preds.advance(pred))
{
switch (pred.getItemExpr()->getOperatorType())
{
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
chosenPredPtr = &pred;
break;
}
if (chosenPredPtr != NULL)
{
break; // important!
}
} // for
}
// There must be a useful predicate in a conflict:
DCMPASSERT(chosenPredPtr != NULL);
// --------------------------------------------------------
// Clear all predicates:
// --------------------------------------------------------
clearPredicates();
// --------------------------------------------------------------
// Insert back the chosen predicate:
// -------------------------------------------------------------
insert(*chosenPredPtr);
}
} // resolveConflict(...)
//---------------------------------------------------------
// Methods for class KeyColumns |
//---------------------------------------------------------
KeyColumns::KeyColumns() :
keyColumnPtrCache_(HEAP)
{}
KeyColumns::KeyColumns(const ValueIdList& orderList) :
keyColumnPtrCache_(HEAP)
{
for (CollIndex i=0; i < orderList.entries(); i++)
insertColumn(orderList[i]);
}
void KeyColumns::insertColumn(const ValueId& column)
{
// don't add it if it's already there!
NABoolean alreadyIn = FALSE;
for(CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
{
if (column == keyColumnPtrCache_[i]->getColumnId())
{
alreadyIn = TRUE;
break;
}
}
if (NOT alreadyIn)
{
KeyColumn *keyColumnPtr = new (CmpCommon::statementHeap())
KeyColumn(column);
DCMPASSERT(keyColumnPtr != NULL);
keyColumnPtrCache_.insertAt(keyColumnPtrCache_.entries(),
keyColumnPtr);
columnSet_.insert(column);
}
} // KeyColumns::insertColumn(..)
void KeyColumns::append(const ValueIdSet& andPredicateExpression)
{
// there must be columns in the keyColumns:
DCMPASSERT(NOT isEmpty());
// Add each predicate in this disjunct into
// the appropiate column of the cache:
ItemExpr *iePtr=NULL;
for (ValueId predId = andPredicateExpression.init();
andPredicateExpression.next(predId);
andPredicateExpression.advance(predId) )
{
insertPredicate(predId);
} // for every predicate
} // KeyColumns::fill(..)
KeyColumns::~KeyColumns()
{
clear();
} // KeyColumns::~KeyColumns()
const KeyColumns::KeyColumn&
KeyColumns::getKeyColumn(const ValueId& column) const
{
DCMPASSERT(NOT keyColumnPtrCache_.isEmpty());
KeyColumns::KeyColumn* result = NULL;
// look up column in the cache and return the set:
for (CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
{
if (column == keyColumnPtrCache_[i]->getColumnId())
{
result = keyColumnPtrCache_[i];
break;
}
}
if (result == NULL)
DCMPASSERT(FALSE);
return *result;
}
KeyColumns::KeyColumn*
KeyColumns::getKeyColumnPtr(const ValueId& column)
{ // The cache must have columns and predicates:
DCMPASSERT(NOT keyColumnPtrCache_.isEmpty());
KeyColumns::KeyColumn* result = NULL;
// look up column in the cache and return the set:
for (CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
{
if (column == keyColumnPtrCache_[i]->getColumnId())
{
result = keyColumnPtrCache_[i];
break;
}
}
DCMPASSERT(result != NULL);
return result;
} // KeyColumns::getKeyColumn()
const ValueIdSet&
KeyColumns::getPredicatesForColumn(const ValueId& column) const
{
return getKeyColumn(column).getPredicates();
} // KeyColumns::getPredicatesForColumn(..)
void KeyColumns::getAllPredicates(ValueIdSet& allPredicates) const
{
// (i.e. predicates in every column referenced in the list):
for (ValueId column = columnSet_.init();
columnSet_.next(column);
columnSet_.advance(column))
{
allPredicates.insert(getPredicatesForColumn(column));
}
}
void KeyColumns::insertPredicateInColumn(const ValueId& predicate,
const ValueId& column)
{
insertPredicate(predicate, &column);
}
void KeyColumns::insertPredicate(const ValueId& predicate)
{
insertPredicate(predicate,NULL);
}
void KeyColumns::insertPredicate(const ValueId& predicate,
const ValueId* columnPtr)
{
ItemExpr * iePtr = predicate.getItemExpr();
// The key must be formed only of predicates, or
// in the case of an insert statament, of ASSIGN expressions;
// in the case of a TriRelational predicate: ITM_LESS(GREATER)_OR_LE(GE)
DCMPASSERT((predicate.getItemExpr()->isAPredicate())
OR
(predicate.getItemExpr()->getOperatorType()==ITM_ASSIGN)
OR
(predicate.getItemExpr()->getOperatorType()==ITM_LESS_OR_LE)
OR
(predicate.getItemExpr()->getOperatorType()==ITM_GREATER_OR_GE)
OR
(predicate.getItemExpr()->getOperatorType()==ITM_OR) // For RangeSpecRef Item Expression
);
// The ValueId for the column must come from a column:
DCMPASSERT(NOT columnPtr
OR
((columnPtr->getItemExpr()->getOperatorType()==ITM_BASECOLUMN)
OR
(columnPtr->getItemExpr()->getOperatorType()==ITM_INDEXCOLUMN)) );
// Is it a valid key predicate?
switch (iePtr->getOperatorType())
{
case ITM_VEG_PREDICATE:
case ITM_EQUAL: // this are created when = is under an OR
case ITM_NOT_EQUAL:
case ITM_LESS:
case ITM_LESS_EQ:
case ITM_LESS_OR_LE:
case ITM_GREATER:
case ITM_GREATER_EQ:
case ITM_GREATER_OR_GE:
case ITM_IN:
case ITM_ASSIGN:
case ITM_IS_NULL:
case ITM_AND: // this will get cleaned up below...
case ITM_OR: // Changes for RangeSpecRef Item Expression
// continue processing
break;
default:
DCMPASSERT(FALSE); // invalid predicate
}
// Some veggies can be rewritted to preds like 2 = 7
// when the query contains contradictory predicates (as
// in the query below
//select * from t1 where (a=2 and b=3)and (d=4 OR a=7 OR B> 1 AND B <= 45);
// If there is a contradictory pred like 2 = 7 throw away the whole
// disjunct:
if (predicate.getItemExpr()->getArity() == 2)
if ( (predicate.getItemExpr()->getConstChild(0)->
castToItemExpr()->getOperatorType() == ITM_CONSTANT)
AND
(predicate.getItemExpr()->getConstChild(1)->
castToItemExpr()->getOperatorType() == ITM_CONSTANT) )
DCMPASSERT(FALSE); // $$$ How do we handle CONST OPERATOR CONST???
// Because of VEG rewriting, the predicate may look like:
// A=2 AND B=2, parse it if so:
if (predicate.getItemExpr()->getOperatorType() == ITM_AND)
{ // the AND expression can be very complex, in general:
insertPredicate(predicate.getItemExpr()->getConstChild(0)->
castToItemExpr()->getValueId()
,columnPtr
);
insertPredicate(predicate.getItemExpr()->getConstChild(1)->
castToItemExpr()->getValueId()
,columnPtr
);
}
else // it's an appropiate pred, insert it in the appropiate column:
{
for (CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
{
// if columnPtr is not NULL, then it means that we
// need to insert the predicate in the given column:
if (columnPtr != NULL)
{
// insert it into the given column:
NABoolean found = FALSE;
if (keyColumnPtrCache_[i]->getColumnId() == *columnPtr)
{
keyColumnPtrCache_[i]->insert(predicate);
}
}
else
{
// insert it in all columns that it references:
// does it refer to the curgrent column?
// It does if it references an index column or if it references
// the base table column corresponding to an index column:
ItemExpr *refExpr = predicate.getItemExpr();
if (refExpr->getOperatorType() == ITM_ASSIGN)
refExpr = refExpr->child(0);
if (refExpr->referencesTheGivenValue(
keyColumnPtrCache_[i]->getColumnId())
OR
refExpr->referencesTheGivenValue(
((IndexColumn *)(keyColumnPtrCache_[i]->
getColumnId().getItemExpr()))->
getDefinition() ) )
{
// now that we found the column, append the predicate:
keyColumnPtrCache_[i]->insert(predicate);
// don't break, VEG predicates may refer to several
// columns!
}
} // columnPtr == NULL
} // for..
}
// The predicate may not be inserted if we try to insert an
// AND expression of the form
// 60: (T1.A = 4) and (T1.D = 4)
// and T1.A is a key col. but T1.D is not a key col.
} // KeyColumns::insertPredicate(..)
void KeyColumns::clear()
{
// Remove keyColumns & predicates
if (NOT keyColumnPtrCache_.isEmpty())
{
// remove the KeyColumn instances that were allocated
// using new (by buildCacheColumns())
clearPredicates();
// remove the entries of the cache itself:
keyColumnPtrCache_.clear();
}
} // clear
void KeyColumns::clearPredicates()
{
if (NOT keyColumnPtrCache_.isEmpty())
{
// remove the KeyColumn instances that were allocated
// using new (by buildCacheColumns())
for (CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
keyColumnPtrCache_[i]->clearPredicates();
}
}
void KeyColumns::print( FILE* ofd,
const char* indent,
const char* title) const
{
// print list:
fprintf(ofd,title);
for(CollIndex i=0; i < keyColumnPtrCache_.entries(); i++)
{
// print the column
fprintf(ofd,"Predicates for column: ");
Int32 c = (Int32)((CollIndex) (keyColumnPtrCache_[i]->getColumnId()));
NAString unparsed(CmpCommon::statementHeap());
keyColumnPtrCache_[i]->getColumnId().getItemExpr()->unparse(unparsed);
fprintf(ofd,"%4d: %s\n",c,(const char *) unparsed);
// print the predicates for this column:
if ( keyColumnPtrCache_[i]->getPredicates().isEmpty())
fprintf(ofd,"\nNo predicates for this column\n\n");
else
{
keyColumnPtrCache_[i]->print(ofd,indent,"");
}
} // for every column
} // KeyColumns::print
// -----------------------------------------------------------------------
// methods for ColumnOrderList:
// -----------------------------------------------------------------------
//
ValueId ColumnOrderList::getKeyColumnId(CollIndex order) const
{
if (order >= entries() || orderKeyColumnPtrList_[order] == NULL)
return NULL_VALUE_ID;
return orderKeyColumnPtrList_[order]->getColumnId();
}
NABoolean ColumnOrderList::containsPredicates() const
{
// for every order:
// check if there are no predicates:
NABoolean containsPreds = FALSE;
for (CollIndex order = 0; order < entries(); order++)
{
// Exit the loop as soon as we hit an expression that
// it's not EMPTY
if (getPredicateExpressionPtr(order) AND
getPredicateExpressionPtr(order)->getType() != KeyColumn::EMPTY)
{
containsPreds = TRUE;
break;
}
}
return containsPreds;
} // containsPredicates()
void ColumnOrderList::print( FILE* ofd,
const char* indent,
const char* /*title*/) const
{
// This prints the predicates for columns that are valid
fprintf(ofd,"The mdam columns are (missing trailing key columns were ruled out by the optimizer):\n ");
for (CollIndex order=0; order < entries(); order++)
{
// print the column
if (orderKeyColumnPtrList_[order])
{
fprintf(ofd,"Predicates for column: ");
Int32 c = (Int32)((CollIndex) (orderKeyColumnPtrList_[order]->getColumnId()));
NAString unparsed(CmpCommon::statementHeap());
orderKeyColumnPtrList_[order]->
getColumnId().getItemExpr()->unparse(unparsed);
fprintf(ofd,"%4d: %s\n",c,(const char *) unparsed);
// print the predicates for this column:
if ( (*this)[order] == NULL )
{
fprintf(ofd,"\nThere are no predicates for this column");
}
else
{
getPredicateExpressionPtr(order)->print(ofd,indent,"");
}
}
}
} // ColumnOrderList::print(...)
ColumnOrderList::ColumnOrderList(const ValueIdList& listOfColumns):
KeyColumns(listOfColumns),
columnList_(listOfColumns),
orderKeyColumnPtrList_(HEAP)
{
// Create the orde list and validate all orders to TRUE:
for(CollIndex i=0; i < listOfColumns.entries(); i++)
{
orderKeyColumnPtrList_.insert(NULL);
validateOrder(i);
}
} // ColumnOrderList(...)
void ColumnOrderList::validateOrder(CollIndex order)
{
DCMPASSERT(order >= 0 && order < columnList_.entries());
orderKeyColumnPtrList_[order] = getKeyColumnPtr(columnList_[order]);
}
void ColumnOrderList::invalidateOrder(CollIndex order)
{
DCMPASSERT(order >= 0 && order < columnList_.entries());
orderKeyColumnPtrList_[order] = NULL;
}
void ColumnOrderList::invalidateAllColumns()
{
// All columns up to (and including) stopColumn are invalid:
for (CollIndex order=0; order < entries(); order++)
invalidateOrder(order);
}
const ColumnOrderList::KeyColumn* ColumnOrderList::
getPredicateExpressionPtr(CollIndex order) const
{
DCMPASSERT(order >= 0 && order < orderKeyColumnPtrList_.entries());
if (orderKeyColumnPtrList_[order] == NULL)
return NULL;
else
return orderKeyColumnPtrList_[order];
}
const ValueIdSet* ColumnOrderList::operator[] (CollIndex order) const
{
const KeyColumn *kc = getPredicateExpressionPtr(order);
return ( kc == NULL ? NULL : &(kc->getPredicates()) );
}
void ColumnOrderList::setStopColumn(CollIndex stopColumn)
{
// All columns up to (and including) stopColumn are valid:
CollIndex order = 0;
for (order=0; order <= stopColumn; order++)
validateOrder(order);
// The rest are invalid (i.e. they return a NULL
// pointer when operator[] is used on the columnOrderList)
for (; order < entries(); order++)
invalidateOrder(order);
}
void ColumnOrderList::resolveConflict(CollIndex order)
{
DCMPASSERT(order >= 0 && order < orderKeyColumnPtrList_.entries());
if (orderKeyColumnPtrList_[order] != NULL)
{
orderKeyColumnPtrList_[order]->resolveConflict();
}
} // resolveConflict(CollIndex order)
// -----------------------------------------------------------------------
// Methods for ColumnOrderListPtrArray
// -----------------------------------------------------------------------
// Function and comments were rewritten based on original version
// Created: //96
// Language: C++
// Modified: July 2000
//
// -----------------------------------------------------------------------
// We'll call a "single subset" a set of table rows that could be
// retrieved by MDAM without extra probes(between two probes).
// Suppose, a table is ordered by the list of colums: C0, C1,..., Cn.
// called "column order list". Let Pj be a set of predicates for the
// column Cj. If there is no predicate on the column (Pj is EMPTY)
// we assume that all rows satisfy this predicate.
//
// The single subset order (SSO) for a column order list is the MAXIMUM
// column position j: 0<=j<=n such that the set of table rows selected
// according to all predicates P0,P1,...,Pj could be a single subset.
//
// For instance, given a table ordered by A B C and an expression B=5
// as the predicate set. Since there is no predicates on column A or
// P0 is EMPTY then applying P0 results in a single subset: the whole
// table.
// A B C
// 1 5 4
// 1 6 4
// 6 7 8
// 7 5 9
//
// Applying the next column predicate B = 5 (P1 is EQUAL) results in
// two subsets:
// A B C
// ==========
// 1 5 4 first subset
// ==========
// 1 6 4
// 6 7 8
// ==========
// 7 5 9 second subset
// ==========
// So, in this case SSO is 0.
//
// However if the preds are: A = 1 and B >= 5 and B <= 6, then
// the SSO is 1 (corresponding to col. B)
// A B C
//===========
// 1 5 4 single subset
// 1 6 4
//===========
// 6 7 8
// 7 5 9
//
// Note that SSO should be 0 in case when there is no single subset.
//
// The function returns true if the pred. expression in the column order
// list retrieves a single subset (the whole table is a trivial case of
// a single subset), or FALSE if it isn't ( when the first pericate is
// IN pred, like A IN (2,7,8)
// NOTE. The cases when pred. is CONFLICT or CONFLICT EQUAL needs to be
// investigated more carefully before including them in this function.
// -----------------------------------------------------------------------
// Sample cases: sinSubOrder itIsSingleSubset
// 1.- = = ... = n TRUE
// 2.- = = R 1 TRUE
// 3.- R 0 TRUE
// 4.- EMPTY 0 TRUE
// 5.- IN 0 FALSE
// 6.- = = IN 1 TRUE
// 7.- = EMPTY 0 TRUE
// -----------------------------------------------------------------------
NABoolean
ColumnOrderList::getSingleSubsetOrder(CollIndex& sinSubOrder /* out */) const
{
NABoolean itIsSingleSubset = TRUE;
CollIndex order;
CollIndex numOfKeyCols=entries();
KeyColumns::KeyColumn::KeyColumnType typeOfRange = KeyColumns::KeyColumn::EMPTY;
if (containsPredicates())
{
for (order = 0; order < numOfKeyCols; order++)
{
if (getPredicateExpressionPtr(order) != NULL)
typeOfRange = getPredicateExpressionPtr(order)->getType();
else
typeOfRange= KeyColumns::KeyColumn::EMPTY;
// Exit the loop as soon as we hit an expression that
// it's not an EQUAL:
if (typeOfRange != KeyColumns::KeyColumn::EQUAL)
break;
} // end of for-loop
// not a single subset when the first pred. is an IN
if ( order == 0 AND
typeOfRange == KeyColumns::KeyColumn::INLIST)
{
itIsSingleSubset=FALSE; // Case 5.
}
// if All preds were EQUAL, or the last pred. was IN or EMPTY,
// we need to substract one, because the end of the
// single subset actually was reached on previous step
if ( order == numOfKeyCols OR // Case 1.
typeOfRange == KeyColumns::KeyColumn::INLIST OR // Case 6.
typeOfRange == KeyColumns::KeyColumn::EMPTY ) // Case 4,7.
{
if ( order < 1 )
sinSubOrder=0;
else
sinSubOrder=order-1;
}
else
{
// this includes RANGE, CONFLICT and CONFLICT EQUAL
sinSubOrder=order; // Case 2,3.
}
} // if (containsPredicates())
else
{
// single subset by definition when there is no predicates
sinSubOrder=0;
}
return itIsSingleSubset;
} // getSingleSubsetOrder(CollIndex& sinSubOrder)
NABoolean
ColumnOrderList::getSingleSubsetOrderForMdam(CollIndex& sinSubOrder /* out */) const
{
NABoolean itIsSingleSubset = TRUE;
CollIndex order;
CollIndex numOfKeyCols=entries();
KeyColumns::KeyColumn::KeyColumnType typeOfRange = KeyColumns::KeyColumn::EMPTY;
if (containsPredicates())
{
for (order = 0; order < numOfKeyCols; order++)
{
if (getPredicateExpressionPtr(order) != NULL)
typeOfRange = getPredicateExpressionPtr(order)->getType();
else
typeOfRange= KeyColumns::KeyColumn::EMPTY;
if (typeOfRange != KeyColumns::KeyColumn::EQUAL
&&
typeOfRange != KeyColumns::KeyColumn::RANGE)
break;
} // end of for-loop
// not a single subset when the first pred. is an IN
if ( order == 0 AND
typeOfRange == KeyColumns::KeyColumn::INLIST)
{
itIsSingleSubset=FALSE; // Case 5.
}
// if All preds were EQUAL, or the last pred. was IN or EMPTY,
// we need to substract one, because the end of the
// single subset actually was reached on previous step
if ( order == numOfKeyCols OR // Case 1.
typeOfRange == KeyColumns::KeyColumn::INLIST OR // Case 6.
typeOfRange == KeyColumns::KeyColumn::EMPTY ) // Case 4,7.
{
if ( order < 1 )
sinSubOrder=0;
else
sinSubOrder=order-1;
}
else
{
// this includes RANGE, CONFLICT and CONFLICT EQUAL
sinSubOrder=order; // Case 2,3.
}
} // if (containsPredicates())
else
{
// single subset by definition when there is no predicates
sinSubOrder=0;
}
return itIsSingleSubset;
} // getSingleSubsetOrderForMdam(CollIndex& sinSubOrder)
// -----------------------------------------------------------------------
// Methods for ColumnOrderListPtrArray
// -----------------------------------------------------------------------
ColumnOrderListPtrArray::ColumnOrderListPtrArray():
ARRAY(ColumnOrderList *)(HEAP)
{}
void ColumnOrderListPtrArray::
computeCommonKeyPredicates(ValueIdSet& commonKeyPredicates) const
{
DCMPASSERT(commonKeyPredicates.isEmpty());
ValueIdSet predicates;
for (CollIndex i=0; i < entries(); i++)
{
if (i==0)
{
// gather all key predicates
((*this)[i])->getAllPredicates(commonKeyPredicates);
}
else // intersect with all predicates in all the orders:
{
((*this)[i])->getAllPredicates(predicates);
commonKeyPredicates.intersectSet(predicates);
predicates.clear(); // will be reused in the next round
}
} // for all disjuncts
} // ColumnOrderListPtrArray::computeCommonKeyPredicates(..)
void ColumnOrderListPtrArray::print( FILE* ofd,
const char* indent,
const char* /*title*/) const
{
char cstrDisjunct[100];
for (CollIndex i=0; i < entries(); i++)
{
sprintf(cstrDisjunct,"Predicates by column for disjunct %d\n",i);
(*this)[i]->print(ofd,indent,cstrDisjunct);
}
}
const ValueIdSet ScanKey::getAllColumnsReferenced() const
{
ValueIdSet all(getKeyColumns());
all.addSet(getNonKeyColumnSet());
all.addSet(getExecutorPredicates());
return all;
}
// eof