blob: fda177e0453597818772e896dfe75fcb869cea91 [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: <file>
* Description:
*
*
* Created: 10/30/96
* Language: C++
*
*
*
*
*****************************************************************************
*/
// -----------------------------------------------------------------------
#include "ex_stdh.h"
#include "key_range.h"
#include "key_mdam.h"
#include "ex_mdam.h"
// #include "exp_clause_derived.h"
MdamPredIterator::MdamPredIterator(MdamColumn * first,Lng32 maxDisjunctNumber)
: currentDisjunctNumber_(-1),
maxDisjunctNumber_(maxDisjunctNumber),
returnedAPred_(FALSE)
{
for (MdamColumn * c = first; c != 0; c = c->nextColumn())
{
// initialize state in MdamColumn objects used by MdamPredIterator
c->initCurrentPred();
}
};
Lng32 MdamPredIterator::getNextDisjunctNumber()
{
Lng32 rc = -1; // assume no more disjuncts
if (currentDisjunctNumber_ < maxDisjunctNumber_)
{
// increment disjunct number and return
currentDisjunctNumber_++;
rc = currentDisjunctNumber_;
}
return rc;
}
NABoolean MdamPredIterator::positionToNextOr(MdamPred **currentPred)
{
NABoolean rc = FALSE; // assume no more Or groups
MdamPred *p = *currentPred;
if (p)
{
while ((p) &&
(p->getDisjunctNumber() < currentDisjunctNumber_) &&
(!(p->firstInOrGroup())))
{
p = p->getNext();
}
*currentPred = p;
if ((p) && (p->getDisjunctNumber() == currentDisjunctNumber_))
rc = TRUE;
}
returnedAPred_ = FALSE;
return rc;
}
MdamPred * MdamPredIterator::getNextPred(MdamPred **currentPred)
{
MdamPred *rc = *currentPred;
if (rc)
{
// if the current predicate belongs to the current disjunct,
// and it is in the current OR group, return it
if ((rc->getDisjunctNumber() == currentDisjunctNumber_) &&
(!(returnedAPred_ && rc->firstInOrGroup())))
{
*currentPred = rc->getNext();
returnedAPred_ = TRUE;
}
else // otherwise indicate no more predicates in this OR group
{
rc = 0;
}
}
return rc;
}
// Position the predicate list to the current disjunct.
void MdamPredIterator::positionToCurrentDisjunct(MdamPred **currentPred)
{
MdamPred *p = *currentPred;
while((p) &&
(p->getDisjunctNumber() < currentDisjunctNumber_))
{
p = p->getNext();
}
*currentPred = p;
}
MdamColumn::MdamColumn(MdamColumn * previous,
MdamColumnGen *columnGenInfo,
ex_globals *glob,
sql_buffer_pool *pool,
atp_struct *atp0,
atp_struct *workAtp,
unsigned short valueAtpIndex,
const ex_tcb *tcb) :
#if defined ( NA_MDAM_EXECUTOR_DEBUG_ILTF )
intervals_(5),
tentative_intervals_(6),
#endif /* NA_MDAM_EXECUTOR_DEBUG_ILTF */
next_(0),previous_(previous),
interval_iterator_(intervals_),
current_interval_(0),
current_is_subset_interval_(FALSE),
currentValue_(0),
traversal_in_progress_(FALSE),
useSparseProbes_(columnGenInfo->useSparseProbes()),
lastProductiveFetchRangeCounter_(0),
sparseProbeNeeded_(FALSE),
sparseProbeSucceeded_(FALSE),
sparseProbeFailed_(FALSE),
columnGenInfo_(columnGenInfo),
currentPred_(0),
glob_(glob)
{
// link this column to the one before it
if (previous)
{
previous->setNextColumn(this);
}
// allocate storage to hold current, hi, lo, nonNullHi, nonNullLo values
pool->get_free_tuple(currentValue_,columnGenInfo_->getLength());
pool->get_free_tuple(hi_,columnGenInfo_->getLength());
pool->get_free_tuple(lo_,columnGenInfo_->getLength());
pool->get_free_tuple(nonNullHi_,columnGenInfo_->getLength());
pool->get_free_tuple(nonNullLo_,columnGenInfo_->getLength());
// go tell MdamColumnGen to fix up its expressions
// (aside: too bad we have to do it here -- if there were fixup
// methods on tdb's we would only have to fix up once. The disadvantage
// of doing it here is that there may be many MdamColumn's per
// MdamColumnGen -- just as there may be many tcb's per tdb. So,
// we call fixup for each MdamColumn, more times than necessary.)
columnGenInfo_->fixup(0,0,glob->getSpace(),glob->getDefaultHeap(), tcb);
// evaluate expressions to compute hi, lo, nonNullHi, nonNullLo values
workAtp->getTupp(valueAtpIndex) = hi_;
(void)(columnGenInfo_->getHiExpr()->eval(atp0,workAtp));
workAtp->getTupp(valueAtpIndex) = lo_;
(void)(columnGenInfo_->getLoExpr()->eval(atp0,workAtp));
workAtp->getTupp(valueAtpIndex) = nonNullHi_;
(void)(columnGenInfo_->getNonNullHiExpr()->eval(atp0,workAtp));
workAtp->getTupp(valueAtpIndex) = nonNullLo_;
(void)(columnGenInfo_->getNonNullLoExpr()->eval(atp0,workAtp));
}
MdamColumn::~MdamColumn()
{
// Note that we assume that keyMdamEx::~keyMdamEx() is the only
// procedure that calls this procedure and that it deleted the
// intervals. Hence there is no call to MdamColumn::release() here.
}
void MdamColumn::setNextColumn(MdamColumn *next)
{
next_ = next;
}
MdamIntervalList & MdamColumn::getIntervalList()
{
return intervals_;
}
NABoolean MdamColumn::initNextValue()
{
traversal_in_progress_ = TRUE;
current_interval_ = 0;
sparseProbeNeeded_ = FALSE;
sparseProbeSucceeded_ = FALSE;
sparseProbeFailed_ = FALSE;
return TRUE;
}
// Note: In MdamColumn::getNextValue(), the beginValue and endValue
// parameters are output only. This method doesn't make any assumptions
// about their input values (and should not, since we may have done
// nasty things such as complement them in place).
MdamColumn::getNextValueReturnType MdamColumn::getNextValue(
ULng32 productiveFetchRangeCounter,
char *beginValue, // out only!
char *endValue, // out only!
short &beginExclFlag,
short &endExclFlag,
FixedSizeHeapManager & mdamRefListEntryHeap)
{
getNextValueReturnType rc = TRAVERSE_UP; // assume failure
NABoolean found_value = FALSE;
beginValue = beginValue + columnGenInfo_->getOffset();
endValue = endValue + columnGenInfo_->getOffset();
while ((traversal_in_progress_) && (!found_value))
{
// if we have a current interval and it is not a subset interval
if ((current_interval_) && (!current_is_subset_interval_))
{
// look for the next value in this interval (if any)
char * cv = currentValue_.getDataPointer();
ULng32 len = columnGenInfo_->getLength();
// if the Optimizer told us to do sparse probes, or if the
// last dense probe didn't result in any fetch ranges with
// rows, do a sparse probe
if ((useSparseProbes_) ||
(lastProductiveFetchRangeCounter_ ==
productiveFetchRangeCounter))
{
ex_assert(sparseProbeNeeded_ == FALSE,
"MdamColumn::getNextvalue called at wrong time.");
if (sparseProbeSucceeded_)
{
// a successful sparse probe completed, and has set
// currentValue_ -- use that value now
sparseProbeSucceeded_ = FALSE;
found_value = TRUE;
str_cpy_all(beginValue,cv,(Int32)len);
str_cpy_all(endValue,cv,(Int32)len);
rc = TRAVERSE_DOWN;
ex_assert(sparseProbeFailed_ == FALSE,
"MdamColumn::getNextValue probe flags bad.");
}
else if (sparseProbeFailed_)
{
// a sparse probe was done, but it returned no data --
// that means there are no more values in this interval
sparseProbeFailed_ = FALSE;
}
else // need to do a sparse probe now
{
found_value = TRUE;
str_cpy_all(beginValue,cv,(Int32)len);
MdamPoint *endpt =
current_interval_->getPointPtr(MdamEnums::MDAM_END);
str_cpy_all(endValue,endpt->getDataPointer(),(Int32)len);
// set exclusion flags
beginExclFlag = 1; // exclude the current value
endExclFlag = 0;
if (endpt->getInclusion() == MdamEnums::MDAM_EXCLUDED)
endExclFlag = 1;
rc = PROBE;
sparseProbeNeeded_ = TRUE;
}
}
else // use dense probes
{
if (current_interval_->getNextValue(len,cv))
{
// we found the next value in this interval
lastProductiveFetchRangeCounter_ =
productiveFetchRangeCounter;
found_value = TRUE;
str_cpy_all(beginValue,cv,(Int32)len);
str_cpy_all(endValue,cv,(Int32)len);
rc = TRAVERSE_DOWN;
}
}
}
if (!found_value)
{
// either there is no current interval, or the current interval
// is a subset interval, or there are no more values in the
// current interval -- in all three of these cases, the next
// thing to do is to find the next interval
current_is_subset_interval_ = FALSE;
if (current_interval_ == 0)
{
// must be we are looking for the first such interval
interval_iterator_.init();
}
current_interval_ = interval_iterator_();
if (previous_)
{
// find the next interval whose ref list has a non-empty
// intersection with the current_context_ of the previous
// column
while ((current_interval_ != 0) &&
(previous_->current_context_.intersectEmpty(
*(current_interval_->getRefListPtr()))))
{
current_interval_ = interval_iterator_();
}
}
if (current_interval_ == 0)
{
// out of intervals -- traverse up
traversal_in_progress_ = FALSE;
rc = TRAVERSE_UP;
current_context_.deleteEntries(mdamRefListEntryHeap);
}
// notice that the next test sets current_is_subset_interval_
// as a side effect...
// The test in the following "if" was changed to fix Genesis
// case CR #10-010204-0980. Formerly, we just intersected the
// stop list with the current interval ref list; but we also
// need to intersect the stop list with the previous column's
// current context as well (if there is a previous column).
// When a previous column exists we should intersect all three
// lists - (1) stop list with the current interval refList
// (2) stop list with the previous column's current context.
// (3) previous column's current context and current interval refList.
// (3) was not covered as part of the fix for case CR #10-010204-098.
// This is a fix for Solution ID: 10-040108-2266.
// else if this is a subset interval...
else if (current_is_subset_interval_ =
(previous_ ?
!stop_list_.intersectEmpty(previous_->current_context_,
*(current_interval_->getRefListPtr()))
: (!stop_list_.intersectEmpty(*(current_interval_->getRefListPtr())))
)
)
{
// we are at a subset interval
MdamPoint *beginpt =
current_interval_->getPointPtr(MdamEnums::MDAM_BEGIN);
MdamPoint *endpt =
current_interval_->getPointPtr(MdamEnums::MDAM_END);
ULng32 len = columnGenInfo_->getLength();
str_cpy_all(beginValue,beginpt->getDataPointer(),(Int32)len);
str_cpy_all(endValue,endpt->getDataPointer(),(Int32)len);
beginExclFlag = 0;
if (beginpt->getInclusion() == MdamEnums::MDAM_EXCLUDED)
beginExclFlag = 1;
endExclFlag = 0;
if (endpt->getInclusion() == MdamEnums::MDAM_EXCLUDED)
endExclFlag = 1;
found_value = TRUE;
rc = SUBSET;
}
else // not a subset interval
{
// find the first value in the interval (note that it
// is possible that there might not be one, in which
// case we will go around the while loop again and
// look for another interval)
char * cv = currentValue_.getDataPointer();
ULng32 len = columnGenInfo_->getLength();
if (current_interval_->getFirstValue(len,cv))
{
// the interval has a first value -- return it
found_value = TRUE;
str_cpy_all(beginValue,cv,(Int32)len);
// set context so we traverse only to appropriate
// intervals in the next column to the right
if (previous_)
{
MdamRefList & intervalRefList =
*(current_interval_->getRefListPtr());
current_context_.intersect(intervalRefList,
previous_->current_context_,
mdamRefListEntryHeap);
}
else
{
MdamRefList & intervalRefList =
*(current_interval_->getRefListPtr());
current_context_.deleteEntries(mdamRefListEntryHeap);
current_context_.copyEntries(intervalRefList,
mdamRefListEntryHeap);
}
if (useSparseProbes_)
{
// do a sparse probe to find first value in interval
MdamPoint *endpt =
current_interval_->getPointPtr(MdamEnums::MDAM_END);
str_cpy_all(endValue,endpt->getDataPointer(),(Int32)len);
// set exclusion flags
beginExclFlag = 0; // include the current value
endExclFlag = 0;
if (endpt->getInclusion() == MdamEnums::MDAM_EXCLUDED)
endExclFlag = 1;
rc = PROBE;
sparseProbeNeeded_ = TRUE;
}
else // dense probes
{
// use this value and traverse down now
str_cpy_all(endValue,cv,(Int32)len);
rc = TRAVERSE_DOWN;
}
}
}
}
}
return rc;
}
void MdamColumn::reportProbeResult(char *keyData)
{
ex_assert(sparseProbeNeeded_,
"MdamColumn::reportProbeResult called unexpectedly.");
sparseProbeNeeded_ = FALSE;
if (keyData)
{
// the sparse probe was successful -- save the key value
Int32 len = Int32(columnGenInfo_->getLength());
Lng32 offset = columnGenInfo_->getOffset();
char * cv = currentValue_.getDataPointer();
str_cpy_all(cv,keyData + offset,len);
sparseProbeSucceeded_ = TRUE;
}
else
{
// the sparse probe returned no data
sparseProbeFailed_ = TRUE;
}
}
void MdamColumn::completeKey(char *bktarget,
char *ektarget,
short bkexcl,
short ekexcl)
{
Int32 len = Int32(columnGenInfo_->getLength());
char *extremalValue;
bktarget = bktarget + columnGenInfo_->getOffset();
ektarget = ektarget + columnGenInfo_->getOffset();
if (bkexcl)
// use hi value if begin key is excluded
extremalValue = hi_.getDataPointer();
else
// use lo value if begin key is included
extremalValue = lo_.getDataPointer();
str_cpy_all(bktarget,extremalValue,len);
if (ekexcl)
// use lo value if end key is excluded
extremalValue = lo_.getDataPointer();
else
// use hi value if end key is included
extremalValue = hi_.getDataPointer();
str_cpy_all(ektarget,extremalValue,len);
};
NABoolean MdamColumn::buildDisjunct(MdamPredIterator & predIterator,
sql_buffer_pool *pool,
atp_struct *atp0,
atp_struct *workAtp,
unsigned short valueAtpIndex,
Lng32 disjunct_number,
NABoolean disjunct_number_in_stop_list,
FixedSizeHeapManager & mdamIntervalHeap,
FixedSizeHeapManager & mdamRefListEntryHeap,
FixedSizeHeapManager &
mdamRefListEntrysForStopListsHeap,
Lng32 & dataConvErrorFlag)
{
NABoolean rc = disjunct_number_in_stop_list;
NABoolean got_a_predicate = FALSE;
// Note that we defer supplying the ref list for now
MdamInterval * accumulator = new(mdamIntervalHeap)
MdamInterval(lo_,
MdamEnums::MDAM_INCLUDED,
hi_,
MdamEnums::MDAM_INCLUDED);
tentative_intervals_.append(accumulator);
// Advance the predicate list to the current disjunct, if necessary.
// This will cause any left-over predicates that were not processed due
// to an earlier conflict to be skipped.
// Case number 10-971205-3848.
predIterator.positionToCurrentDisjunct(&currentPred_);
// Loop over the OR groups.
while ((predIterator.positionToNextOr(&currentPred_)) &&
(!tentative_intervals_.isEmpty()))
{
#if defined ( NA_MDAM_EXECUTOR_DEBUG_ILTF )
MdamIntervalList or_result(3);
#else
MdamIntervalList or_result;
#endif /* NA_MDAM_EXECUTOR_DEBUG_ILTF */
MdamPred * predPtr;
// Loop over the predicates within an OR group.
while (predPtr = predIterator.getNextPred(&currentPred_))
{
// build an interval representing this predicate, if possible
MdamInterval * or_accumulator = 0;
tupp & pv = workAtp->getTupp(valueAtpIndex);
tupp & pv2 = workAtp->getTupp(valueAtpIndex+1);
pool->get_free_tuple(pv,columnGenInfo_->getLength());
if (predPtr->getPredType() == MdamPred::MDAM_BETWEEN)
pool->get_free_tuple(pv2,columnGenInfo_->getLength());
got_a_predicate = TRUE;
dataConvErrorFlag = 0; // The zero hard-coded here should be
// ex_conv_clause::CONV_RESULT_OK in
// file exp/exp_clause_derived.h.
// The call to getValue sets dataConvErrorFlag. We use it twice in the
// case of MDAM_BETWEEN, copying the values to other local variables.
ex_expr::exp_return_type errorCode = predPtr->getValue(atp0,workAtp);
Int32 dcErrFlag1 = dataConvErrorFlag;
Int32 dcErrFlag2 = 0;
if (errorCode == ex_expr::EXPR_OK &&
predPtr->getPredType() == MdamPred::MDAM_BETWEEN)
{
dataConvErrorFlag = 0;
errorCode = predPtr->getValue2(atp0,workAtp);
dcErrFlag2 = dataConvErrorFlag;
}
MdamPred::MdamPredType predType = MdamPred::MDAM_RETURN_FALSE;
// Next 2 used only for MDAM_BETWEEN.
MdamEnums::MdamInclusion startInclusion = predPtr->getStartInclusion();
MdamEnums::MdamInclusion endInclusion = predPtr->getEndInclusion();
if (errorCode == ex_expr::EXPR_OK)
predType = predPtr->getTransformedPredType(dcErrFlag1, dcErrFlag2,
startInclusion, endInclusion);
// The switch statement below implements the following mapping...
// MDAM_EQ -> [pv,pv]
// MDAM_LE -> [nonNullLo, pv]
// MDAM_LT -> [nonNullLo, pv)
// MDAM_GE -> [pv, nonNullHi]
// MDAM_GT -> (pv,nonNullHi]
// MDAM_BETWEEN -> [pv,pv2]
// MDAM_ISNULL -> [hi,hi]
// MDAM_ISNULL_DESC -> [lo,lo]
// MDAM_ISNOTNULL -> [nonNullLo,nonNullHi]
// MDAM_RETURN_FALSE -> no interval
switch (predType)
{
case MdamPred::MDAM_EQ:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(pv,
MdamEnums::MDAM_INCLUDED,
pv,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_LE:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(nonNullLo_,
MdamEnums::MDAM_INCLUDED,
pv,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_LT:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(nonNullLo_,
MdamEnums::MDAM_INCLUDED,
pv,
MdamEnums::MDAM_EXCLUDED);
break;
}
case MdamPred::MDAM_GE:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(pv,
MdamEnums::MDAM_INCLUDED,
nonNullHi_,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_GT:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(pv,
MdamEnums::MDAM_EXCLUDED,
nonNullHi_,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_BETWEEN:
{
// If the predicate is on a descending key column, switch the
// endpoints.
if (predPtr->reverseEndpoints())
or_accumulator = new(mdamIntervalHeap)
MdamInterval(pv2,
endInclusion,
pv,
startInclusion);
else
or_accumulator = new(mdamIntervalHeap)
MdamInterval(pv,
startInclusion,
pv2,
endInclusion);
break;
}
case MdamPred::MDAM_ISNULL: // IS NULL predicate on ASC column
{
// if the column is nullable and ASC, we know that hi_
// is the NULL value
or_accumulator = new(mdamIntervalHeap)
MdamInterval(hi_,
MdamEnums::MDAM_INCLUDED,
hi_,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_ISNULL_DESC: // IS NULL predicate on DESC column
{
// if the column is nullable and DESC, we know that lo_
// is the NULL value
or_accumulator = new(mdamIntervalHeap)
MdamInterval(lo_,
MdamEnums::MDAM_INCLUDED,
lo_,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_ISNOTNULL:
{
or_accumulator = new(mdamIntervalHeap)
MdamInterval(nonNullLo_,
MdamEnums::MDAM_INCLUDED,
nonNullHi_,
MdamEnums::MDAM_INCLUDED);
break;
}
case MdamPred::MDAM_RETURN_FALSE:
{
// The predicate cannot be satisfied so no interval is created.
break;
}
default:
{
ex_assert(0,"Invalid predicate type");
break;
}
}
// release tupp_descriptor assigned to pv now that we are done
// with pv
pv.release();
// OR in the interval just obtained into or_result
if (or_accumulator)
{
if (or_result.isEmpty())
{
or_result.append(or_accumulator);
}
else
{
#if defined ( NA_MDAM_EXECUTOR_DEBUG_ILTF )
MdamIntervalList or_temp1(4);
#else
MdamIntervalList or_temp1;
#endif /* NA_MDAM_EXECUTOR_DEBUG_ILTF */
or_temp1.append(or_accumulator);
or_result.unionSameDisjunct(or_temp1,
columnGenInfo_->getLength(),
mdamIntervalHeap,
mdamRefListEntryHeap);
or_temp1.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
}
}
}
// AND the OR result to the accumulator
tentative_intervals_.intersect(or_result,
columnGenInfo_->getLength(),
mdamIntervalHeap,
mdamRefListEntryHeap);
or_result.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
}
if (((got_a_predicate) && (!rc)) ||
((previous_ == 0) && (!rc)))
{
// this is the last key column that has a predicate
// for this disjunct, or there is no key column having
// a predicate for this column (in which case one wonders
// why MDAM was picked!) -- add the disjunct number to the
// stop list for this key column
stop_list_.insert((Int32)disjunct_number,mdamRefListEntrysForStopListsHeap);
rc = TRUE;
}
return rc;
}
NABoolean MdamColumn::disjunctIsEmpty()
{
return this->tentative_intervals_.isEmpty();
}
void MdamColumn::tossDisjunct(FixedSizeHeapManager & mdamIntervalHeap,
FixedSizeHeapManager & mdamRefListEntryHeap)
{
tentative_intervals_.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
}
void MdamColumn::mergeDisjunct(Lng32 disjunct_number,
FixedSizeHeapManager & mdamIntervalHeap,
FixedSizeHeapManager & mdamRefListEntryHeap)
{
intervals_.unionSeparateDisjuncts(tentative_intervals_,
(Int32)disjunct_number,
columnGenInfo_->getLength(),
mdamIntervalHeap,
mdamRefListEntryHeap);
tentative_intervals_.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
}
void MdamColumn::releaseNetwork(FixedSizeHeapManager & mdamIntervalHeap,
FixedSizeHeapManager & mdamRefListEntryHeap)
{
intervals_.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
// shouldn't need to do this on tentative_intervals_ since
// mergeDisjunct() or tossDisjunct() should have already done it,
// but we do this anyway just to be robust
tentative_intervals_.deleteAllIntervals(mdamIntervalHeap,
mdamRefListEntryHeap);
// Delete the ref list entries for current_context_.
current_context_.deleteEntries(mdamRefListEntryHeap);
}
void MdamColumn::release(FixedSizeHeapManager & mdamRefListEntrysForStopListsHeap)
{
stop_list_.deleteEntries(mdamRefListEntrysForStopListsHeap);
}
keyMdamEx::keyMdamEx(const keyRangeGen & keyRangeGen,
const short in_version,
sql_buffer_pool *pool,
ex_globals *glob,
const ex_tcb *tcb)
: keyRangeEx(keyRangeGen,glob,in_version,pool),
network_built_(FALSE),
stop_lists_built_(FALSE),
current_column_(0),
current_column_index_(-1),
productiveFetchRangeCounter_(0),
mdamRefListEntryHeap_(sizeof(MdamRefListEntry),
getGenInfo().getMaxMdamRefs()),
mdamRefListEntrysForStopListsHeap_(sizeof(MdamRefListEntry),
getGenInfo().getMaxMdamRefsForStopLists()),
mdamIntervalHeap_(sizeof(MdamInterval),
getGenInfo().getMaxMdamIntervals()),
complementKeysBeforeReturning_(
getGenInfo().complementKeysBeforeReturning())
{
number_of_key_cols_ = 0;
first_column_ = last_column_ = 0;
const keyMdamGen & keyM = (const keyMdamGen &)keyRangeGen;
for (MdamColumnGen *cg = keyM.getFirst();
cg != 0;
cg = cg->getNext())
{
last_column_ =
new(glob->getSpace()) MdamColumn(last_column_,
cg,
glob,
pool,
workAtp_, // $$$ will this work?
workAtp_,
keyM.getValueAtpIndex(), tcb);
if (first_column_ == 0)
first_column_ = last_column_;
number_of_key_cols_++;
}
NABoolean refListEntrysForStopListsHeapAcquired
= mdamRefListEntrysForStopListsHeap_.acquireHeapMemory(
globals_->getDefaultHeap());
}
void keyMdamEx::destroyNetwork()
{
if (network_built_)
{
for (MdamColumn * c = first_column_; c != 0; c = c->nextColumn())
{
c->releaseNetwork(mdamIntervalHeap_,
mdamRefListEntryHeap_);
}
network_built_ = FALSE;
}
// reset tree traversal variables so any attempt to traverse will
// return end-of-traverse
current_column_ = 0;
current_column_index_ = -1;
// Release memory for MdamInterval's and MdamRefListEntry's.
mdamRefListEntryHeap_.releaseHeapMemory();
mdamIntervalHeap_.releaseHeapMemory();
}
ExeErrorCode keyMdamEx::buildNetwork(sql_buffer_pool *pool,atp_struct *atp0)
{
destroyNetwork(); // clean out any old network still hanging around
keyMdamGen & mdamGen = (keyMdamGen &)tdbKey_;
// Acquire memory for MdamRefListEntry's.
ExeErrorCode refListEntryHeapErrorCode
= mdamRefListEntryHeap_.acquireHeapMemory(globals_->getDefaultHeap());
if (refListEntryHeapErrorCode != (ExeErrorCode)0)
return refListEntryHeapErrorCode;
// Acquire memory for MdamInterval's.
ExeErrorCode intervalHeapErrorCode
= mdamIntervalHeap_.acquireHeapMemory(globals_->getDefaultHeap());
if (intervalHeapErrorCode != (ExeErrorCode)0)
return intervalHeapErrorCode;
MdamPredIterator predIterator(first_column_,
mdamGen.getMaxDisjunctNumber());
Lng32 disjunct_number;
while ((disjunct_number = predIterator.getNextDisjunctNumber()) >= 0)
{
NABoolean disjunct_number_in_stop_list = stop_lists_built_;
NABoolean empty_disjunct = FALSE;
Lng32 column_number = number_of_key_cols_-1; // $$$ remove later
for (MdamColumn * m = last_column_;
m != 0;
m = m->previousColumn())
{
ex_assert(column_number >= 0,
"keyMdamEx::Mdam -- bad column_number"); // $$$ remove later
disjunct_number_in_stop_list =
m->buildDisjunct(predIterator,
pool,
atp0,
workAtp_,
getGenInfo().getValueAtpIndex(),
disjunct_number,
disjunct_number_in_stop_list,
mdamIntervalHeap_,
mdamRefListEntryHeap_,
mdamRefListEntrysForStopListsHeap_,
dataConvErrorFlag_);
if (m->disjunctIsEmpty())
empty_disjunct = TRUE;
column_number--; // $$$ remove later
} // end for
ex_assert(column_number == -1,
"keyMdamEx::Mdam -- wrong number of columns"); // $$$ remove later
ex_assert(disjunct_number_in_stop_list,
"keyMdamEx::Mdam -- disjunct number not in any stop list");
// if the disjunct is empty for some column (i.e. if some column
// has contradictory predicates for this disjunct), throw the disjunct
// away; otherwise merge it in
if (empty_disjunct)
{
for (MdamColumn * c = first_column_;
c != 0;
c = c->nextColumn())
{
c->tossDisjunct(mdamIntervalHeap_,
mdamRefListEntryHeap_);
}
}
else
{
for (MdamColumn * c = first_column_;
c != 0;
c = c->nextColumn())
{
c->mergeDisjunct(disjunct_number,
mdamIntervalHeap_,
mdamRefListEntryHeap_);
}
}
} // end while
network_built_ = TRUE;
stop_lists_built_ = TRUE;
// for testing... display the network
#if defined ( NA_MDAM_EXECUTOR_DEBUG )
print();
#endif /* NA_MDAM_EXECUTOR_DEBUG */
return (ExeErrorCode)0; // No error.
}
keyMdamEx::~keyMdamEx()
{
destroyNetwork();
// Delete the MdamColumn*s.
MdamColumn * current;
MdamColumn * next;
for (current = first_column_;
current != 0;
current = next)
{
current->release(mdamRefListEntrysForStopListsHeap_);
next = current->nextColumn();
delete current;
}
// Release memory for the stop list ref list entires.
// stop_list_.deleteEntries(mdamRefListEntryHeap);
mdamRefListEntrysForStopListsHeap_.releaseHeapMemory();
}
ExeErrorCode keyMdamEx::initNextKeyRange(sql_buffer_pool *pool,
atp_struct * atp0)
{
ExeErrorCode rc = buildNetwork(pool,atp0);
if (rc == (ExeErrorCode)0) // if no error
{
current_column_ = first_column_;
current_column_index_ = 0;
current_column_->initNextValue();
}
return rc;
}
keyRangeEx::getNextKeyRangeReturnType keyMdamEx::getNextKeyRange
(atp_struct *,NABoolean fetchRangeHadRows, NABoolean detectNullRange)
{
// This logic + MdamColumn::getNextValue are essentially a re-coding
// of the SQL/MP procedure evac^next^traverse. One fundamental
// difference is that unlike SQL/MP, sparse probes are done in the
// layer on top of Mdam, instead of below. So, this procedure, for
// example, might return a key range for probing or for fetching.
// In SQL/MP, evac^next^traverse returns only key ranges for
// fetching. The probing is done inside the scope of the
// evac^next^traverse procedure call.
if (fetchRangeHadRows)
productiveFetchRangeCounter_++;
getNextKeyRangeReturnType rc = NO_MORE_RANGES; // assume no more ranges
NABoolean not_done = TRUE;
char *bktarget = bkData_.getDataPointer();
char *ektarget = ekData_.getDataPointer();
ULng32 keyLength = getGenInfo().getKeyLength();
if (getGenInfo().getKeytag() > 0)
{
unsigned short keytag = getGenInfo().getKeytag();
str_cpy_all(bktarget, (char *)&keytag, sizeof(short));
str_cpy_all(ektarget, (char *)&keytag, sizeof(short));
bktarget += sizeof(short);
ektarget += sizeof(short);
keyLength -= sizeof(short);
}
if (complementKeysBeforeReturning_)
{
// if reverse scan, need to swap where we put begin and end keys
char *temp = bktarget;
bktarget = ektarget;
ektarget = temp;
}
// Part of fix for Genesis case 10-971031-9814:
// Keep track of the offset of the left-most column traversed.
ULng32 minOffset = 0;
if (current_column_)
minOffset = current_column_->getOffset();
// End of this part of the fix.
while ((current_column_) && (not_done))
{
MdamColumn::getNextValueReturnType status =
current_column_->getNextValue(productiveFetchRangeCounter_,
bktarget,
ektarget,
bkExcludeFlag_,
ekExcludeFlag_,
mdamRefListEntryHeap_);
switch (status)
{
case MdamColumn::TRAVERSE_DOWN:
{
current_column_ = current_column_->nextColumn();
current_column_index_++;
ex_assert(current_column_ != 0,
"keyMdamEx::getNextKeyRange() traversed off end");
current_column_->initNextValue();
break;
}
case MdamColumn::TRAVERSE_UP:
{
current_column_ = current_column_->previousColumn();
current_column_index_--;
// Part of fix for Genesis case 10-971031-9814:
// Keep track of the offset of the left-most column traversed.
ULng32 offset = 0;
if (current_column_) // note current_column_ is 0 at end of all traversal
offset = current_column_->getOffset();
if (offset < minOffset)
minOffset = offset;
// End of this part of the fix.
ex_assert(current_column_index_ >= -1,
"keyMdamEx::getNextKeyRange() column index wrong");
break;
}
case MdamColumn::SUBSET:
case MdamColumn::PROBE:
{
MdamColumn * col;
Lng32 index = current_column_index_ + 1;
for (col = current_column_->nextColumn();
col != 0;
col = col->nextColumn())
{
col->completeKey(bktarget,
ektarget,
bkExcludeFlag_,
ekExcludeFlag_);
index++;
}
ex_assert(index == number_of_key_cols_,
"keyMdamEx::getNextKeyRange() completeKey failure");
not_done = FALSE;
rc = FETCH_RANGE;
if (status == MdamColumn::PROBE)
rc = PROBE_RANGE;
if (complementKeysBeforeReturning_)
{
// On reverse scans, the keys in the Mdam network have
// been complemented; we must undo this complementing
// before returning the keys to the caller.
// Part of fix for Genesis case 10-971031-9814:
// Complement only that part of the key that was computed
// on this call to getNextKeyRange(); the preceding parts
// of the key have already been complemented on prior calls.
str_complement(keyLength-minOffset,bktarget+minOffset);
str_complement(keyLength-minOffset,ektarget+minOffset);
// End of fix.
// also we need to swap the exclude flags (just as we
// did the key buffers earlier)
short temp;
temp = bkExcludeFlag_;
bkExcludeFlag_ = ekExcludeFlag_;
ekExcludeFlag_ = temp;
}
break;
}
default:
{
ex_assert(0,"keyMdamEx::getNextKeyRange() invalid getNextValue rc");
break;
}
}
}
return rc;
}
void keyMdamEx::reportProbeResult(char *keyData)
{
ex_assert(current_column_,
"keyMdamEx::reportProbeResult() called outside traverse");
if (complementKeysBeforeReturning_)
{
if (keyData)
{
// for reverse scans, complement the encoding so it is consistent
// with what is stored in the MDAM network
str_complement(getGenInfo().getKeyLength(),keyData);
}
}
current_column_->reportProbeResult(keyData);
}
// release tupp storage associated with an Mdam network
void keyMdamEx::release()
{
destroyNetwork();
keyRangeEx::release(); // invoke release() on base class
}
// Print functions.
#if defined ( NA_MDAM_EXECUTOR_DEBUG )
void MdamColumn::print(const char * header) const
{
cout << header << endl;
if (intervals_.isEmpty())
{
cout << "No intervals" << endl;
}
else
{
intervals_.printBrief();
}
cout << " Stop list: ";
stop_list_.printBrief();
cout << endl;
}
void keyMdamEx::print(const char * header) const
{
cout << header << endl;
MdamColumn * c;
Lng32 i = 0;
for (c = first_column_;
c != 0;
c = c->nextColumn())
{
cout << "Column " << i;
c->print(":");
i++;
}
}
#endif /* NA_MDAM_EXECUTOR_DEBUG */