blob: a52d5ec45d39903f7e17ebb3b471c6b2f40f84c9 [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: RuRange.cpp
* Description: Implementation of class CRURange and the auxiliary classes
*
* Created: 08/01/2000
* Language: C++
*
*
******************************************************************************
*/
#include "ddobject.h"
#include "RuRange.h"
#include "RuDupElimLogRecord.h"
//--------------------------------------------------------------------------//
// Constructors
//--------------------------------------------------------------------------//
CRURange::CRURange() :
origBoundaryPair_(),
fragmentList_(eItemsAreOwned),
epoch_(0),
rangeId_(0),
isDeleteOfScreenedRecordsRequired_(FALSE),
isUpdateOfScreenedRecordsRequired_(FALSE),
// Range analysis state
pMyFragment_(NULL),
myPos_(NULL)
{}
//--------------------------------------------------------------------------//
// CRURange::SetBRRecord()
//--------------------------------------------------------------------------//
void CRURange::SetBRRecord(const CRUIUDLogRecord *pBRRec)
{
RUASSERT(NULL != pBRRec
&&
NULL == origBoundaryPair_.GetBRRecord());
origBoundaryPair_.SetBRRecord(pBRRec);
epoch_ = pBRRec->GetEpoch();
rangeId_ = pBRRec->GetSyskey();
}
//--------------------------------------------------------------------------//
// CRURange::SetERRecord()
//--------------------------------------------------------------------------//
void CRURange::SetERRecord(const CRUIUDLogRecord *pERRec)
{
RUASSERT(NULL != pERRec);
origBoundaryPair_.SetERRecord(pERRec);
}
//--------------------------------------------------------------------------//
// CRURange::PerformCrossTypeDE()
//
// This method is applied only if the single-row resolution is enforced
// (and hence, both single-row and range records are scanned).
//
// A single-row IUD log record is *screened* by a range if:
// (1) It is *covered* by this range.
// (2) The range was logged *before* the record.
//
// If the screening epoch is smaller than the record's one, the record's
// @IGNORE mark in the log must be updated the screening range's epoch.
//
// If the screening epoch is equal to the record's one,
// the record must be deleted from the log.
//
// These decisions will be marked in the record's internal flags.
// They will save work for the single-row resolver, if there is one.
//
// For example, if the single-row resolver (that works after the range
// resolver) decides to delete a record while the range resolver made the
// same decision, the single-row resolver will not perform the operation.
//
// The range resolver will also reflect the decisions in its own flags
// (isDeleteOfScreenedRecordsRequired_ and isUpdateOfScreenedRecordsRequired_)
// in order to save "blind" delete/update in the IUD log during the flush.
//
//--------------------------------------------------------------------------//
void CRURange::PerformCrossTypeDE(CRUIUDLogRecord *pRec)
{
RUASSERT(TRUE == pRec->IsSingleRowOp());
if (FALSE == IsClusteringKeyCovered(pRec)
||
pRec->GetSyskey() < GetBRRecord()->GetSyskey())
{
return;
}
TInt32 ep = pRec->GetEpoch();
if (ep > epoch_)
{
// The @IGNORE mark must be set to epoch_ ...
if (epoch_ <= pRec->GetIgnoreMark())
{
// However, if it is already epoch_ or more,
// there is nothing to do!
return;
}
pRec->SetIgnoreMark(epoch_);
// Set the flag for the single-row resolver...
pRec->SetRangeResolvWillUpdateMe();
// ... and for myself
isUpdateOfScreenedRecordsRequired_ = TRUE;
}
else
{
RUASSERT(ep == epoch_);
// Set the flag for the single-row resolver...
pRec->SetRangeResolvWillDeleteMe();
// ... and for myself
isDeleteOfScreenedRecordsRequired_ = TRUE;
}
}
//--------------------------------------------------------------------------//
// CRURange::IsClusteringKeyCovered()
//
// The clustering key value CK is *covered* by range R
// if it holds that R.<BR-columns> <= CK <= R.<ER-columns>
//
// There are two possible cases when the CK is covered.
// (1) The range is *not* balanced, i.e., the end-range record
// matching the begin-range record has not encountered yet.
//
// (2) The range is balanced, but the end-range value is equal
// to the CK value.
//
//--------------------------------------------------------------------------//
BOOL CRURange::IsClusteringKeyCovered(const CRUIUDLogRecord *pRec) const
{
// Case 1 - the range is not balanced yet
if (NULL == GetERRecord())
{
return TRUE;
}
// Case 2
if (TRUE == GetERRecord()->IsClusteringKeyEqualTo(*pRec))
{
return TRUE;
}
return FALSE;
}
//--------------------------------------------------------------------------//
// CRURange::PrepareForFlush()
//
// Initialize the fragment list
//--------------------------------------------------------------------------//
void CRURange::PrepareForFlush()
{
CRURangeFragment *pFragment = new CRURangeFragment();
pFragment->SetBRRecord(this->GetBRRecord());
pFragment->SetERRecord(this->GetERRecord());
fragmentList_.AddTail(pFragment);
}
//--------------------------------------------------------------------------//
// RANGE ANALYSIS METHODS
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
// CRURange::PerformRangeAnalysis()
//
// Perform the range analysis against the range which has been logged
// *later* then myself (we call it a *younger* range).
//
// In the case of overlap between my fragment and the younger range,
// the following principle guides:
// - The fragment of (my) range which has been logged earlier must be
// changed (deleted, split, or resized) to resolve the overlap, while
// the range which has been logged later must remain intact.
//
// This rule is easiest to explain for two ranges that belong to different
// epocs. The range with the greater epoch number can be observed alone by
// some MV, but not vice versa. Hence, the range from the smaller epoch
// "suffers".
//
// The same principle also holds for two overlapping ranges that belong
// to the same epoch. This is a requirement of the INTERNAL REFRESH
// optimization that filters out the cross-type duplicates through the
// Delta Computation Block (rather than through DE).
//
// The algorithm is organized as a scan through the range's fragment list
// (which is sorted by the Begin-Range clustering key). Each fragment is
// compared to the younger range, and the appropriate DE decision
// is performed (always in the younger range's favor).
//
//--------------------------------------------------------------------------//
void CRURange::PerformRangeAnalysis(const CRURange &otherRange)
{
RUASSERT(GetRangeId() <= otherRange.GetRangeId());
if (TRUE == fragmentList_.IsEmpty()
||
TRUE == otherRange.GetFragmentList().IsEmpty())
{
// All the fragments of one of the two ranges are dominated
// by "third-party" ranges (and hence removed from the list).
// Nothing to do in this case.
return;
}
myPos_ = fragmentList_.GetHeadPosition();
ExposureType brExpType, erExpType;
while (NULL != myPos_)
{
pMyFragment_ = fragmentList_.GetNext(myPos_);
// Compute whether the begin-range and end-range records
// are exposed (i.e., not screened by the other range).
brExpType = GetExposureType(*(pMyFragment_->GetBRRecord()), otherRange);
erExpType = GetExposureType(*(pMyFragment_->GetERRecord()), otherRange);
switch (brExpType)
{
case SMALLER_EXPOSED:
{
PerformRAIfMyBRIsSmallerExposed(otherRange, erExpType);
break;
}
case NOT_EXPOSED:
{
PerformRAIfMyBRIsNotExposed(otherRange, erExpType);
break;
}
case GREATER_EXPOSED:
{
// This fragment (as well as all the following ones)
// are not in conflict with the other range
return;
}
default: RUASSERT(FALSE);
}
}
}
//--------------------------------------------------------------------------//
// CRURange::PerformRAIfMyBRIsSmallerExposed()
//
// The begin-range value of my current fragment is smaller than
// the begin-range value of the second range's current fragment.
//
// The picture is:
//
// -------------------------------------------------------
// ^ (1) ^ (2) ^ (3)
// | | |
// MY BR OTHER BR OTHER ER
//
// My ER can be located in one of (1), (2) or (3).
//
// Perform the range analysis in the relevant case.
//
//--------------------------------------------------------------------------//
void CRURange::
PerformRAIfMyBRIsSmallerExposed(const CRURange &otherRange,
ExposureType erExpType)
{
switch (erExpType)
{
case SMALLER_EXPOSED:
{
// Case (1) - No overlap
// -------- other
// ------ my
break;
}
case NOT_EXPOSED:
{
// Case (2)
// -------- other ==> --------
// ------ my --
CutMyFragmentFromEnd(otherRange);
break;
}
case GREATER_EXPOSED:
{
// Case (3)
// --- other ==> ---
// ------- my -- --
SplitMyFragment(otherRange);
break;
}
default: RUASSERT(FALSE);
}
}
//--------------------------------------------------------------------------//
// CRURange::PerformRAIfMyBRIsNotExposed()
//
// The begin-range value of my current fragment is not exposed (i.e.,
// is screened by the other range).
//
// The picture is:
//
// -------------------------------------------------------
// ^ ^ (1) ^ (2)
// | | |
// OTHER BR MY BR OTHER ER
//
// My ER can be located in one of (1) or (2).
//
// Perform the range analysis in the relevant case.
//
//--------------------------------------------------------------------------//
void CRURange::
PerformRAIfMyBRIsNotExposed(const CRURange &otherRange,
ExposureType erExpType)
{
switch (erExpType)
{
case NOT_EXPOSED:
{
// Case (1)
// ------------- other ==> -------------
// ---- my
RemoveMyFragment();
break;
}
case GREATER_EXPOSED:
{
// Case (2)
// ------ other ==> ------
// ----- my ---
CutMyFragmentFromBegin(otherRange);
break;
}
case SMALLER_EXPOSED: // This cannot happen
default:
{
RUASSERT(FALSE);
}
}
}
//--------------------------------------------------------------------------//
// CRURange::CutMyFragmentFromBegin()
//
// Solve the following conflict:
//
// ------ other ==> ------
// ----- my ---
//
//--------------------------------------------------------------------------//
void CRURange::CutMyFragmentFromBegin(const CRURange &otherRange)
{
pMyFragment_->SetBRRecord(otherRange.GetERRecord());
// My fragment will become open at the beginning
pMyFragment_->UnsetTypeBit(CRURangeFragment::IS_BR_INCLUDED);
}
//--------------------------------------------------------------------------//
// CRURange::CutMyFragmentFromEnd()
//
// Solve the following conflict:
//
// -------- other ==> --------
// ------ my --
//
//--------------------------------------------------------------------------//
void CRURange::CutMyFragmentFromEnd(const CRURange &otherRange)
{
pMyFragment_->SetERRecord(otherRange.GetBRRecord());
// My fragment will become open at the end
pMyFragment_->UnsetTypeBit(CRURangeFragment::IS_ER_INCLUDED);
}
//--------------------------------------------------------------------------//
// CRURange::SplitMyFragment()
//
// Solve the following conflict:
//
// --- other ==> ---
// ------- my -- --
// #1 #2
//
// Following this change, a new fragment (fragment #2 in the picture)
// will be created an inserted into the fragment list. It will become
// my new current fragment. Fragment #1 will be my old current fragment,
// cut from the end.
//
//--------------------------------------------------------------------------//
void CRURange::SplitMyFragment(const CRURange &otherRange)
{
// Create fragment #2
CRURangeFragment *pNewFragment = new CRURangeFragment();
pNewFragment->SetBRRecord(otherRange.GetERRecord());
pNewFragment->SetERRecord(pMyFragment_->GetERRecord());
// The new fragment is "born" closed from both ends.
// Set the end-range bit of fragment #2 to be the same
// as the end range of fragment #1 (open or closed).
if (0 == (pMyFragment_->GetType() & CRURangeFragment::IS_ER_INCLUDED))
{
pNewFragment->UnsetTypeBit(CRURangeFragment::IS_ER_INCLUDED);
}
pMyFragment_->SetERRecord(otherRange.GetBRRecord());
// Fragment #1 will become open at the end.
pMyFragment_->UnsetTypeBit(CRURangeFragment::IS_ER_INCLUDED);
// Fragment #2 will become open at the beginning.
pNewFragment->UnsetTypeBit(CRURangeFragment::IS_BR_INCLUDED);
// Insert the fragment #2 into the list
if (NULL == myPos_)
{
fragmentList_.AddTail(pNewFragment);
}
else
{
fragmentList_.InsertBefore(myPos_, pNewFragment);
// Make a step back. The newly-created fragment will become current.
fragmentList_.GetPrev(myPos_);
}
}
//--------------------------------------------------------------------------//
// CRURange::RemoveMyFragment()
//
// Solve the following conflict:
//
// ------------- other ==> -------------
// ---- my
//
//--------------------------------------------------------------------------//
void CRURange::RemoveMyFragment()
{
if (NULL == myPos_)
{
// This is the last node in the list
fragmentList_.RemoveTail();
}
else
{
DSListPosition myPrevPos = myPos_;
// The scan has already skipped my node, make a single step back.
fragmentList_.GetPrev(myPrevPos);
fragmentList_.RemoveAt(myPrevPos);
}
}
//--------------------------------------------------------------------------//
// CRURange::GetExposureType()
//
// Check where the record lies with regards to the younger range
// (and whether it is screened by it). There are three options:
//
// <--- SMALLER_EXPOSED <-- NOT_EXPOSED --> GREATER_EXPOSED --->
// ------------------------[-------------------]-----------------------
//
// Note that if the clustering key coincides with one of the range's
// boundaries, it is always NOT exposed.
//
// In order to refrain from complex (and datatype-dependent) comparisons
// between the data values, the algorithm exploits the fact that the delta
// computation query sorts the data by the clustering key. Therefore,
// the comparisons are made between the *tags* that reflect the sorting order,
// rather than the actual values (this is also much faster).
//
//--------------------------------------------------------------------------//
CRURange::ExposureType CRURange::GetExposureType(const CRUIUDLogRecord &rec,
const CRURange &otherRange)
{
const CRUIUDLogRecord &otherBRRec = *(otherRange.GetBRRecord());
const CRUIUDLogRecord &otherERRec = *(otherRange.GetERRecord());
TInt64 ckTag = rec.GetCKTag();
TInt64 brCkTag = otherRange.GetBRRecord()->GetCKTag();
TInt64 erCkTag = otherRange.GetERRecord()->GetCKTag();
RUASSERT(brCkTag <= erCkTag);
if (ckTag < brCkTag)
{
return SMALLER_EXPOSED;
}
else
{
if (ckTag > erCkTag)
{
return GREATER_EXPOSED;
}
else
{
// brCkTag <= ckTag <= erCkTag
return NOT_EXPOSED;
}
}
}
// List classes' definition completions
DEFINE_PTRLIST(CRURange);
DEFINE_PTRLIST(CRURangeFragment);