blob: 31b533f9879557c8296ebc1f8052da9cb8825294 [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: RuDupElimSingleRowResolv.cpp
* Description: Implementation of class CRUDupElimSingleRowResolver
*
*
* Created: 07/02/2000
* Language: C++
*
*
*
******************************************************************************
*/
#include "RuDupElimSingleRowResolv.h"
#include "RuException.h"
#include "RuDupElimLogScanner.h"
#include "RuDupElimLogRecord.h"
#include "RuDupElimGlobals.h"
#include "uofsIpcMessageTranslator.h"
//--------------------------------------------------------------------------//
// Constructor
//--------------------------------------------------------------------------//
CRUDupElimSingleRowResolver::
CRUDupElimSingleRowResolver(const CRUDupElimGlobals &globals,
CRUSQLDynamicStatementContainer &ctrlStmtContainer) :
inherited(
globals,
ctrlStmtContainer,
CRUDupElimConst::NUM_SINGLE_RESOLV_STMTS
),
// Automaton status
isPureUpdateChain_(TRUE),
isUpdateRecInChain_(FALSE),
// Common Update bitmap
updateBitmap_(globals.GetUpdateBmpSize()),
// Statistics
numDeleteRecord_(0),
numUpdateIgnMark_(0),
numUpdateBitmap_(0),
numUpdateOpType_(0)
{}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::Reset()
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::Reset()
{
isPureUpdateChain_ = TRUE;
isUpdateRecInChain_ = FALSE;
// Set the bitmap to zeroes
updateBitmap_.Reset();
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::PrepareSQL()
//
// Prepare the IUD statements to be performed on the IUD log.
//
// In order to force the optimizer to employ MDAM in the subset Update
// statements, execute the CONTROL QUERY SHAPE statement before the
// compilation, and execute the *reverse* control statement after it.
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::PrepareSQL()
{
PrepareStatement(CRUDupElimConst::IUD_LOG_SINGLE_DELETE);
PrepareStatement(CRUDupElimConst::IUD_LOG_SINGLE_UPDATE_IGNORE);
ExecuteCQSForceMDAM(CRUDupElimResolver::IUD_LOG);
PrepareStatement(CRUDupElimConst::IUD_LOG_UPDATE_BITMAP);
PrepareStatement(CRUDupElimConst::IUD_LOG_UPDATE_OPTYPE);
ExecuteCQSOff();
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::Resolve()
//
// The main conflict resolution handler.
//
// The class tracks the *duplicate chain* context (a duplicate chain
// is a sequence of log records that reflect the operations applied
// to the same clustering key value, in the correct temporal order).
// The resolution actions are of two types:
// (1) In the middle of the chain (handling non-trailing Inserts and
// not-pivot Deletes, see the design document).
// (2) At the end of the chain (handling the update bitmaps).
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::Resolve(CRUDupElimLogScanner &scanner)
{
// Re-initialize the flag
SetCanCompletePhase(FALSE);
CRUIUDLogRecord *pCurrentRec = scanner.GetCurrentRecord();
CRUIUDLogRecord *pPrevRec = scanner.GetPrevRecord();
if (NULL == pPrevRec &&
NULL == pCurrentRec &&
TRUE == scanner.IsEntireDeltaScanned() )
{
// This chain was empty.
// Nothing to do here.
return;
}
if (TRUE == scanner.IsEntireDeltaScanned())
{
RUASSERT(NULL != pPrevRec);
EndChain(pPrevRec);
return;
}
RUASSERT(NULL != pCurrentRec);
if (FALSE == pCurrentRec->IsSingleRowOp() )
{
return;
}
// Resolve the single-row conflicts ...
if (NULL != pPrevRec)
{
if (TRUE == pCurrentRec->IsClusteringKeyEqualTo(*pPrevRec))
{
// Take care of non-trailing Inserts
if (TRUE == pPrevRec->IsInsert())
{
ResolveInsert(pPrevRec);
}
// Take care of non-pivot Deletes
if (TRUE == pCurrentRec->IsDelete())
{
ResolveDelete(pCurrentRec, pPrevRec);
}
}
else
{
EndChain(pPrevRec);
// Allow the commit now
SetCanCompletePhase(TRUE);
}
}
// The record is also a part of UPDATE operation, monitor
// its contribution to the chain's common @UPDATE_BITMAP.
MonitorUpdate(pCurrentRec);
}
#ifdef _DEBUG
//------------------------------------------------------------//
// CRUDupElimSingleRowResolver::DumpPerformanceStatistics()
//
// Dump the number of times each IUD statement was performed.
//
//------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
DumpPerformanceStatistics(CDSString &to) const
{
CDSString msg;
msg = "IUD log single-row delete";
DumpStmtStatistics(to, msg, numDeleteRecord_);
msg = "IUD log single-row update @IGNORE";
DumpStmtStatistics(to, msg, numUpdateIgnMark_);
msg = "IUD log update @UPDATE_BITMAP";
DumpStmtStatistics(to, msg, numUpdateBitmap_);
msg = "IUD log update @OPTYPE";
DumpStmtStatistics(to, msg, numUpdateOpType_);
}
#endif
//--------------------------------------------------------------------------//
// PRIVATE AREA
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::LoadReply()
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
LoadReply(CUOFsIpcMessageTranslator &translator)
{
translator.ReadBlock(&numDeleteRecord_, sizeof(Lng32));
translator.ReadBlock(&numUpdateIgnMark_, sizeof(Lng32));
translator.ReadBlock(&numUpdateBitmap_, sizeof(Lng32));
translator.ReadBlock(&numUpdateOpType_, sizeof(Lng32));
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::StoreReply()
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
StoreReply(CUOFsIpcMessageTranslator &translator)
{
translator.WriteBlock(&numDeleteRecord_, sizeof(Lng32));
translator.WriteBlock(&numUpdateIgnMark_, sizeof(Lng32));
translator.WriteBlock(&numUpdateBitmap_, sizeof(Lng32));
translator.WriteBlock(&numUpdateOpType_, sizeof(Lng32));
}
//--------------------------------------------------------------------------//
// DUPLICATE ELIMINATION CONFLICTS RESOLUTION
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ResolveInsert()
//
// The INSERT record that is not a trailing one it its chain
// must be deleted (unless the Range resolver has already
// decided so). We can conclude whether the Insert is trailing
// not before than we read the next record in the same chain
// (i.e., the current record).
//
// NOTE. This method is called only if the CURRENT record is
// a single-row record. Hence, we are checking for the case:
//
// (1) Insert(CK) <-- prev
// (2) Delete(CK) <-- current
//
// The following case cannot happen if the range is
// logged automatically (an Insert immediately before
// a range with a boundary on the same CK):
//
// (1) Insert(CK)
// (2) {Begin/End}Range(CK) <-- prev
// (3) Delete(CK) <-- current
//
// Therefore, the upper case is the only relevant one.
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ResolveInsert(CRUIUDLogRecord *pPrevRec)
{
if (TRUE == pPrevRec->WillRangeResolvDeleteMe())
{
// Nothing to do. The range resolver will perform the deletion.
return;
}
ExecuteDeleteRecord(pPrevRec);
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ResolveDelete()
//
// Process the single-row DELETE record.
//
// (1) If this is not a the first DELETE in the duplicate chain
// in this record's epoch - delete it.
//
// (2) Otherwise, the DELETE (we call it pivot) should receive
// an ignore mark equal to the epoch of the preceding
// INSERT/DELETE record in the chain, unless the Range
// resolver has assigned it a greater value.
//
// This method considers the following case:
//
// (1) ???(CK) <-- prev (optype does not matter)
// (2) Delete(CK) <-- current
//
// The duplicate resolution rules are correct both in the
// case when the previous record is a {Begin/End}Range and
// in the case when it is an Insert. In the first case, the
// Range resolver has already marked its decisions on the
// current record.
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ResolveDelete(CRUIUDLogRecord *pCurrentRec, CRUIUDLogRecord *pPrevRec)
{
if (TRUE == pCurrentRec->WillRangeResolvDeleteMe())
{
// Nothing to do. The range resolver will perform the deletion.
return;
}
TInt32 prevEp = pPrevRec->GetEpoch();
if (pCurrentRec->GetEpoch() == prevEp)
{
// Not a pivot
ExecuteDeleteRecord(pCurrentRec);
return;
}
if (prevEp > pCurrentRec->GetIgnoreMark())
{
pCurrentRec->SetIgnoreMark(prevEp);
ExecuteUpdateIgnMark(pCurrentRec);
}
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::MonitorUpdate()
//
// Process a single-row INSERT or DELETE record which was logged
// as a part of an Update operation (i.e., the @OPERATION_TYPE column
// has the IS_PART_OF_UPDATE bit on).
//
// Single-row vs single-row duplicate elimination (which is actually
// applied by the single-row resolver) can cause that the INTERNAL REFRESH
// folliwing it will apply only one record in the Update (Delete + Insert)
// pair in the IUD log. For example, consider the chain D1, I2, D3, I4
// where D1 and I2 are part of update #1, D3 and I4 are part of update #2.
// After DE, INTERNAL REFRESH will apply only D1 and I4. If D1 and I4 have
// different bitmaps, the result of INTERNAL REFRESH can be incorrect.
//
// Note that these problems cannot appear as part of the cross-type DE
// (range vs single-row) that is performed by the range resolver, because
// in cross-type DE the Update records "survive" or "die" in pairs (i.e.,
// INTERNAL REFRESH applies either both or none of them.
//
// In order to prevent INTERNAL REFRESH from confusion, all the DE "survivors"
// must either inherit the superposition of the update bitmaps in the chain,
// or must be unmarked as a part of update. This requires monitoring of the
// common bitmap while scanning the chain, and, potentially a subset Update
// (applied to the whole chain).
//
// The method monitors the following statistics for the whole chain:
// (1) Is there at least one Update record in the chain
// that was not deleted by DE?
// (2) Are all the records in the chain a part of update
// (we call this chain a *pure update* chain).
//
// If the first condition is false at the end of chain, there is
// no need to address the issues that are specific to update bitmaps.
//
// As long as the second condition is true, the algorithm keeps maintaining
// the common update bitmap for the whole duplicate chain, which is
// a superposition of individual records' bitmaps. If the chain is not pure
// update, the resolver will unset the IS_PART_OF_UPDATE bit in all the
// records when the chain is finished (and hence, the bitmaps
// will turn meaningless).
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
MonitorUpdate(CRUIUDLogRecord *pCurrentRec)
{
if (FALSE == pCurrentRec->IsPartOfUpdate())
{
isPureUpdateChain_ = FALSE;
return;
}
// Only if some Update record in chain will survive,
// it makes sense to perform a "collective" change.
// Hence, count only the non-deleted records.
if (FALSE == pCurrentRec->WillRangeResolvDeleteMe())
{
isUpdateRecInChain_ = TRUE;
}
// Keep maintaining the bitmap only inside a pure update chain
if (FALSE == isPureUpdateChain_)
{
return;
}
const CRUUpdateBitmap *pBmp = pCurrentRec->GetUpdateBitmap();
if (TRUE == updateBitmap_.IsNull())
{
// This is the first update in this chain.
// It *initializes* the bitmap, but doesn't *change* it.
updateBitmap_ = *pBmp;
}
else
{
updateBitmap_ |= *pBmp;
}
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::EndChain()
//
// (1) Apply the changes to all the Update records in the chain
// (including the write to disk), if required
// (2) Reset all the automaton's state variables.
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::EndChain(CRUIUDLogRecord *pLastRecInChain)
{
RUASSERT(NULL != pLastRecInChain);
if (TRUE == isUpdateRecInChain_)
{
// Perform a uniform change
// to all the Update records in the chain
HandleUpdateBitmapAtEndOfChain(pLastRecInChain);
}
Reset();
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::HandleUpdateBitmapAtEndOfChain()
//
// EndChain() callee.
//
// When the duplicate chain is finished, *and* there was at
// least one Update record in the chain that was not deleted
// by DE:
// (1) If the whole chain was update-only:
// - If *not* all the bitmaps in the chain are equal -
// update all of them to their superposition (OR).
// (2) Otherwise, unset the IS_PART_OF_UPDATE bit in the
// @OPERATION_TYPE column of all the Update records in
// the chain (then, INTERNAL REFRESH will not treat these
// records as updates).
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
HandleUpdateBitmapAtEndOfChain(CRUIUDLogRecord *pLastRecInChain)
{
if (TRUE == isPureUpdateChain_)
{
if (TRUE == updateBitmap_.WasChanged())
{
ExecuteUpdateBitmapInChain(pLastRecInChain);
}
}
else
{
ExecuteUpdateOpTypeInChain(pLastRecInChain);
}
}
//--------------------------------------------------------------------------//
// DUPLICATE ELIMINATION DECISIONS' EXECUTION
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ExecuteDeleteRecord()
//
// DELETE FROM <T-IUD-log>
// WHERE
// @EPOCH=?
// AND
// (<T-CK-col1 = ?> AND ... <T-CK-colN = ?>)
// AND
// @TS=?
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ExecuteDeleteRecord(CRUIUDLogRecord *pRec)
{
CDMPreparedStatement *pStmt =
GetPreparedStatement(CRUDupElimConst::IUD_LOG_SINGLE_DELETE);
RUASSERT (NULL != pStmt);
// Search predicate
pStmt->SetInt(1, pRec->GetEpoch());
pRec->CopyCKTupleValuesToParams(*pStmt,2);
Int32 syskeyPos = 2 + pRec->GetCKLength();
pStmt->SetLargeInt(syskeyPos, pRec->GetSyskey());
pStmt->ExecuteUpdate();
// RUASSERT(1 == pStmt->GetRowsAffected());
pStmt->Close();
numDeleteRecord_++;
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ExecuteUpdateIgnMark()
//
// UPDATE <T-IUD-log>
// SET @IGNORE=?
// WHERE
// @EPOCH=?
// AND
// (<T-CK-col1 = ?> AND ... <T-CK-colN = ?>)
// AND
// @TS=?
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ExecuteUpdateIgnMark(CRUIUDLogRecord *pRec)
{
CDMPreparedStatement *pStmt =
GetPreparedStatement(CRUDupElimConst::IUD_LOG_SINGLE_UPDATE_IGNORE);
RUASSERT (NULL != pStmt);
// The new value
pStmt->SetInt(1, pRec->GetIgnoreMark());
// Search predicate
pStmt->SetInt(2, pRec->GetEpoch());
pRec->CopyCKTupleValuesToParams(*pStmt, 3);
Int32 syskeyPos = 3 + pRec->GetCKLength();
pStmt->SetLargeInt(syskeyPos, pRec->GetSyskey());
pStmt->ExecuteUpdate();
// RUASSERT(1 == pStmt->GetRowsAffected());
pStmt->Close();
numUpdateIgnMark_++;
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ExecuteUpdateBitmapInChain()
//
// UPDATE <T-IUD-log>
// SET @UPDATE_BITMAP = ?
// WHERE
// (@EPOCH BETWEEN begin-epoch and end-epoch)
// AND
// (<T-CK-col1 = ?> AND ... <T-CK-colN = ?>)
// AND
// (@OPERATION_TYPE = 2 OR @OPERATION_TYPE = 3)
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ExecuteUpdateBitmapInChain(CRUIUDLogRecord *pLastRecInChain)
{
CDMPreparedStatement *pStmt =
GetPreparedStatement(CRUDupElimConst::IUD_LOG_UPDATE_BITMAP);
RUASSERT (NULL != pStmt);
// The common bitmap value
pStmt->SetString(1, updateBitmap_.GetBuffer(), updateBitmap_.GetSize());
// Search predicate
pLastRecInChain->CopyCKTupleValuesToParams(*pStmt, 2);
pStmt->ExecuteUpdate();
// RUASSERT(0 != pStmt->GetRowsAffected());
pStmt->Close();
numUpdateBitmap_++;
}
//--------------------------------------------------------------------------//
// CRUDupElimSingleRowResolver::ExecuteUpdateOpTypeInChain()
//
// UPDATE <T-IUD-log>
// SET @OPERATION_TYPE = @OPERATION_TYPE - 2
// WHERE
// (@EPOCH BETWEEN begin-epoch and end-epoch)
// AND
// (<T-CK-col1 = ?> AND ... <T-CK-colN = ?>)
// AND
// (@OPERATION_TYPE = 2 OR @OPERATION_TYPE = 3)
//
//--------------------------------------------------------------------------//
void CRUDupElimSingleRowResolver::
ExecuteUpdateOpTypeInChain(CRUIUDLogRecord *pLastRecInChain)
{
CDMPreparedStatement *pStmt =
GetPreparedStatement(CRUDupElimConst::IUD_LOG_UPDATE_OPTYPE);
RUASSERT (NULL != pStmt);
// Search predicate
pLastRecInChain->CopyCKTupleValuesToParams(*pStmt, 1);
pStmt->ExecuteUpdate();
// RUASSERT(0 != pStmt->GetRowsAffected());
pStmt->Close();
numUpdateOpType_++;
}