blob: 7f6289010e11c47050116a2df714867c33b6895d [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: ChangesTable.cpp
* Description: Insertion, scanning and deleting from a changes table
* such as the triggers temporary table, and the MV log.
* Created: 10/10/2000
* Language: C++
*
*
*
*
******************************************************************************
*/
#include "NumericType.h"
#include "AllRelExpr.h"
#include "AllItemExpr.h"
#include "BindWA.h"
#include "NATable.h"
#include "ChangesTable.h"
#include "ComMvDefs.h"
#include "Refresh.h"
#include "MvLog.h"
#include "MVInfo.h"
#include "parser.h"
#include "ItmFlowControlFunction.h"
/*****************************************************************************
******************************************************************************
**** Class TriggersTempTable
******************************************************************************
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ChangesTable::ChangesTable(const CorrName& name,
OperatorTypeEnum opType,
BindWA *bindWA,
RowsType scanType)
: naTable_(NULL),
subjectTable_(name,bindWA->wHeap()),
subjectNaTable_(NULL),
opType_(opType),
tableCorr_(NULL),
scanType_(scanType),
scanNode_(NULL),
corrNameForNewRow_(NEWCorr,bindWA->wHeap()),
corrNameForOldRow_(OLDCorr,bindWA->wHeap()),
bindWA_(bindWA),
heap_(bindWA->wHeap())
{
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ChangesTable::ChangesTable(const GenericUpdate *baseNode,
BindWA *bindWA)
: naTable_(NULL),
subjectTable_(baseNode->getTableDesc()->getCorrNameObj(),bindWA->wHeap()),
subjectNaTable_(baseNode->getTableDesc()->getNATable()),
opType_(baseNode->getOperatorType()),
tableCorr_(NULL),
scanNode_(NULL),
corrNameForNewRow_(NEWCorr,bindWA->wHeap()),
corrNameForOldRow_(OLDCorr,bindWA->wHeap()),
bindWA_(bindWA),
heap_(bindWA->wHeap())
{
CMPASSERT(baseNode != NULL);
if (baseNode->getUpdateCKorUniqueIndexKey() &&
baseNode->getOperatorType() == REL_UNARY_INSERT)
opType_ = REL_UNARY_UPDATE;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ChangesTable::ChangesTable(Scan *baseNode,
RowsType scanType,
BindWA *bindWA)
: subjectTable_(baseNode->getTableName(),bindWA->wHeap()),
subjectNaTable_(NULL),
naTable_(NULL),
opType_(baseNode->getOperatorType()),
tableCorr_(NULL),
scanType_(scanType),
scanNode_(baseNode),
corrNameForNewRow_(NEWCorr,bindWA->wHeap()),
corrNameForOldRow_(OLDCorr,bindWA->wHeap()),
bindWA_(bindWA),
heap_(bindWA->wHeap())
{
CMPASSERT(baseNode != NULL);
}
//////////////////////////////////////////////////////////////////////////////
// This method is called after the Ctors of sub-classes of ChangesTable,
// in order to initialize the changes table name and NATable pointer.
// calcTargetTableName() is a pure virtual function, and so cannot be called
// by the base class Ctor.
//////////////////////////////////////////////////////////////////////////////
void ChangesTable::initialize()
{
CMPASSERT(subjectTable_ != ""); // Subject table name must be initialized.
// Calculate the changes table name from the subject table name.
const NATable *subjectNaTable = getSubjectNaTable();
if (subjectNaTable && subjectNaTable->getIsSynonymTranslationDone())
{
QualifiedName subjectQualName(subjectNaTable->getSynonymReferenceName(),
3, // minNameParts
bindWA_->wHeap(),
bindWA_);
tableCorr_ = calcTargetTableName(subjectQualName);
}
else
tableCorr_ =
calcTargetTableName(getSubjectTableName().getQualifiedNameObj());
// If the table is using late name resolution, the log should as well.
if (getSubjectTableName().getPrototype() && supportsLateBinding())
{
HostVar *logPrototype = new(bindWA_->wHeap())
HostVar(*getSubjectTableName().getPrototype());
// Mark the table type on the prototype.
logPrototype->setSpecialType(tableCorr_->getSpecialType());
tableCorr_->setPrototype(logPrototype);
}
// Save the state of the RI flag
BindContext *currentBindContext = bindWA_->getCurrentScope()->context();
NABoolean prevRiState = currentBindContext->inRIConstraint();
// Set the RI flag to allow calling getNATable() without counting it.
currentBindContext->inRIConstraint() = TRUE;
// Get the NATable of the changes table.
naTable_ = bindWA_->getNATable(*tableCorr_);
// if we hit any error while constructing naTable_, we should
// catch it here as we will try to dereference it
CMPASSERT(naTable_!=NULL);
if (subjectNaTable_ == NULL)
{
subjectNaTable_ = bindWA_->getNATable(subjectTable_);
CMPASSERT(subjectNaTable_) // NATable must have been found!
}
// Restore the RI flag.
currentBindContext->inRIConstraint() = prevRiState;
}
//////////////////////////////////////////////////////////////////////////////
// build the node to insert the OLD@ and NEW@ data into the changes table.
// Insert and Delete operations each cause a single row to be inserted into
// the changes table per affected set row. Update operations cause two rows
// to be inserted into the changes table per affected set row: one with the
// OLD data and one with the NEW data.
// For update operations, we can enforce insertion of only one set (NEW/OLD)
// using the second parameter. This parameter is defaulted to ALL_ROWS, so if
// not explicitly requested, it doesn't affect the insert at whole.
//////////////////////////////////////////////////////////////////////////////
RelExpr *ChangesTable::buildInsert(NABoolean useLeafInsert,
Lng32 enforceRowsTypeForUpdate,
NABoolean isUndo,
NABoolean isForMvLogging
) const
{
ItemExprList *tupleColRefList = NULL;
ItemExpr *insItemList = NULL;
ItemExpr *delItemList = NULL;
NABoolean needOld=FALSE, needNew=FALSE;
NABoolean isUpdate=FALSE, isInsert=FALSE, isDelete=FALSE;
// Get the columns of the temp table.
const NAColumnArray &tempColumns = getNaTable()->getNAColumnArray();
switch (getOpType()) // What type of GenericUpdate node is this?
{
case REL_UNARY_INSERT: // hhhheeeelllpppp - I'm falling to next case
case REL_LEAF_INSERT: needNew=TRUE;
isInsert=TRUE;
break;
case REL_UNARY_DELETE: needOld=TRUE;
isDelete=TRUE;
break;
case REL_UNARY_UPDATE: needNew=TRUE;
needOld=TRUE;
isUpdate=TRUE;
break;
default : CMPASSERT(FALSE);
}
// For update operations, the second parameter controls which type of rows
// is inserted into the temp-table. When both sets of values are inserted,
// the insert is unary insert. When only one set of values is inserted, a
// leaf insert is used.
if (getOpType() == REL_UNARY_UPDATE)
{
CMPASSERT(needOld && needNew);
if (!(enforceRowsTypeForUpdate & INSERTED_ROWS))
{
needNew = FALSE;
}
if (!(enforceRowsTypeForUpdate & DELETED_ROWS))
{
needOld = FALSE;
}
// use LeafInsert node for UPDATE operation as well, unless we need both
// sets of values (NEW and OLD).
if (needNew && needOld)
{
useLeafInsert=FALSE;
}
}
if (useLeafInsert)
tupleColRefList = new(heap_) ItemExprList(heap_);
// Now for each of the log columns,
// enter the correct value to be inserted.
ItemExpr *delColExpr, *insColExpr;
for (CollIndex i=0; i < tempColumns.entries(); i++)
{
NAColumn *currentTempCol = tempColumns.getColumn(i);
const NAString& colName = currentTempCol->getColName();
delColExpr = insColExpr = NULL;
if (colName.data()[0] == '@')
{
if (needOld)
delColExpr = createAtColExpr(currentTempCol, FALSE, isUpdate);
if (needNew)
insColExpr = createAtColExpr(currentTempCol, TRUE, isUpdate, isUndo);
}
else
{
if (!colName.compareTo("SYSKEY"))
{
// This is the log SYSKEY column
// iud log no longer has a SYSKEY
if (needOld && useLeafInsert)
delColExpr = createSyskeyColExpr(colName, FALSE);
if (needNew && useLeafInsert)
insColExpr = createSyskeyColExpr(colName, TRUE);
}
else
{
if (needOld)
delColExpr = createBaseColExpr(colName, FALSE, isUpdate);
if (needNew)
insColExpr = createBaseColExpr(colName, TRUE, isUpdate);
}
}
// Add to column list of the new record expression.
if (useLeafInsert)
{
if (needOld)
tupleColRefList->insert(delColExpr);
if (needNew)
tupleColRefList->insert(insColExpr);
}
else
{
if (needOld && delColExpr!=NULL)
if (delItemList == NULL)
delItemList = delColExpr;
else
delItemList = new (heap_) ItemList(delItemList, delColExpr);
if (needNew && insColExpr!=NULL)
if (insItemList == NULL)
insItemList = insColExpr;
else
insItemList = new (heap_) ItemList(insItemList, insColExpr);
}
}
Insert *insertNode;
Union *unionNode = NULL;
if (useLeafInsert)
{
// Create the leaf Insert node for INSERT or DELETE operations into the log table.
insertNode = new (heap_)
LeafInsert(*getTableName(), NULL, tupleColRefList);
}
else
{
Tuple *delTuple = NULL, *insTuple = NULL;
RelExpr *belowInsert = NULL;
if (needOld)
belowInsert = delTuple = new(heap_) Tuple(delItemList);
if (needNew)
belowInsert = insTuple = new(heap_) Tuple(insItemList);
if (isUpdate)
{
unionNode = new(heap_) Union(delTuple, insTuple,
NULL, NULL, REL_UNION,
CmpCommon::statementHeap(), TRUE);
setTuplesUnionType(unionNode);
belowInsert = unionNode;
}
insertNode = new(heap_)
Insert(*getTableName(), NULL, REL_UNARY_INSERT, belowInsert);
}
// Rows inserted into the log table should not be counted.
insertNode->rowsAffected() = GenericUpdate::DO_NOT_COMPUTE_ROWSAFFECTED;
// set MvLogging flags in TrueRoot, Union, and Insert nodes
if( isForMvLogging &&
((isUpdate && CmpCommon::getDefault(MV_LOG_PUSH_DOWN_DP2_UPDATE) == DF_ON) ||
(isDelete && CmpCommon::getDefault(MV_LOG_PUSH_DOWN_DP2_DELETE) == DF_ON) ||
(isInsert && CmpCommon::getDefault(MV_LOG_PUSH_DOWN_DP2_INSERT) == DF_ON) ))
{
bindWA_->getTopRoot()->getInliningInfo().setFlags(II_isUsedForMvLogging);
insertNode->getInliningInfo().setFlags(II_isUsedForMvLogging);
if( NULL != unionNode )
{
unionNode->getInliningInfo().setFlags(II_isUsedForMvLogging);
}
}
return insertNode;
} // ChangesTable::buildInsert()
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RelExpr *ChangesTable::buildDelete(RowsType whichRows) const
{
// First build a Scan on the table.
RelExpr *scanNode = buildScan(whichRows);
// Build the Delete node on top of the Scan.
Delete *deleteNode = new(heap_)
Delete(*getTableName(), NULL, REL_UNARY_DELETE, scanNode);
deleteNode->rowsAffected() = GenericUpdate::DO_NOT_COMPUTE_ROWSAFFECTED;
// Put a root on top.
RelRoot *rootNode = new(heap_) RelRoot(deleteNode);
rootNode->setRootFlag(FALSE);
rootNode->setEmptySelectList();
return rootNode;
} // ChangesTable::buildDelete()
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RelExpr *ChangesTable::transformScan() const
{
CMPASSERT(scanNode_ != NULL);
// If the Scan table does not have a correlation name, than the target
// name should be made the correlation name.
// Otherwise - the target name is redundant, and the Scan correlation
// name stays.
const NAString& baseCorrName =
scanNode_->getTableName().getCorrNameAsString();
const NAString& transitionName =
baseCorrName != "" ?
baseCorrName :
isEmptyDefaultTransitionName() ?
*(new (heap_) NAString("", heap_)) :
scanNode_->getTableName().getQualifiedNameObj().getObjectName();
tableCorr_->setCorrName(transitionName);
scanNode_->getTableName() = *tableCorr_;
ItemExpr *selectionPredicate = createSpecificWhereExpr(scanType_);
scanNode_->addSelPredTree(selectionPredicate);
return scanNode_;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
Scan *ChangesTable::buildScan(RowsType type) const
{
Scan *scanNode = new(heap_) Scan(*getTableName());
// Add uniqifier predicates to the scan node
ItemExpr *selectionPredicate = createSpecificWhereExpr(type);
scanNode->addSelPredTree(selectionPredicate);
return scanNode;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RelExpr *ChangesTable::buildOldAndNewJoin() const
{
NAString newName(NEWCorr);
NAString oldName(OLDCorr);
// Create two Scan nodes on the temporary table for the OLD and NEW data).
Scan *tempScanOldNode = buildScan(ChangesTable::DELETED_ROWS);
RenameTable *renameOld = new(heap_)
RenameTable(tempScanOldNode, OLDCorr);
Scan *tempScanNewNode = buildScan(ChangesTable::INSERTED_ROWS);
RenameTable *renameNew = new(heap_)
RenameTable(tempScanNewNode, NEWCorr);
// Join the two Scans for one long row
ItemExpr *joinPredicate = new(heap_)
BiRelat(ITM_EQUAL,
buildClusteringIndexVector(&newName, TRUE),
buildClusteringIndexVector(&oldName, TRUE) );
Join *joinNode = new(heap_)
Join(renameOld, renameNew, REL_JOIN, joinPredicate);
// This join must be forced to a merge join.
// joinNode->forcePhysicalJoinType(Join::MERGE_JOIN_TYPE);
return joinNode;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ItemExpr *ChangesTable::buildBaseColsSelectList(const CorrName& tableName) const
{
ItemExpr *selectList=NULL;
ItemExpr *colRef;
// Get the columns of the subject table.
const NAColumnArray &subjectColumns =
getSubjectNaTable()->getNAColumnArray();
for (CollIndex i=0; i<subjectColumns.entries(); i++)
{
NAString colName(subjectColumns.getColumn(i)->getColName());
if (!colName.compareTo("SYSKEY"))
continue; // Skip SYSKEY.
colRef = new(heap_)
ColReference(new(heap_) ColRefName(colName, tableName,heap_));
if (selectList==NULL)
selectList = colRef;
else
selectList = new(heap_) ItemList(selectList, colRef);
}
return selectList;
}
//////////////////////////////////////////////////////////////////////////////
// Build a selection-list of the columns of the ChangesTable that have a
// corresponding column in the subject table.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *
ChangesTable::buildColsCorrespondingToBaseSelectList() const
{
ItemExpr *selectList=NULL;
ItemExpr *colRef;
// Get the columns of the ChangesTable.
const NAColumnArray &columns = getNaTable()->getNAColumnArray();
for (CollIndex i=0; i<columns.entries(); i++)
{
NAString colName(columns.getColumn(i)->getColName());
if (colName.data()[0] == '@' && colName.compareTo("@SYSKEY"))
continue; // Skip any special column that is not @SYSKEY.
colRef = new(heap_)
ColReference(new(heap_) ColRefName(colName, *getTableName(),heap_));
if (selectList==NULL)
selectList = colRef;
else
selectList = new(heap_) ItemList(selectList, colRef);
}
return selectList;
}
//////////////////////////////////////////////////////////////////////////////
// Build a list of ColReferences to the columns of the base table's
// clustering index columns.
// When the second parameter (useInternalSyskey) is TRUE, the returned vector
// is using the "@SYSKEY" column instead of "SYSKEY" column.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *ChangesTable::buildClusteringIndexVector(const NAString *corrName,
NABoolean useInternalSyskey) const
{
ItemExpr *result = NULL;
const NATable *naTable = getSubjectNaTable();
const NAColumnArray &indexColumns =
naTable->getClusteringIndex()->getIndexKeyColumns();
NAString tableName(""), schemaName(""), catalogName("");
if (corrName != NULL)
{
tableName = *corrName;
}
else
{
const QualifiedName& qualName = naTable->getTableName();
schemaName = qualName.getSchemaName();
catalogName = qualName.getCatalogName();
tableName = naTable->getTableName().getObjectName();
}
CorrName tableNameCorr(tableName, heap_, schemaName, catalogName);
CollIndex lastCol = indexColumns.entries()-1;
for (CollIndex i=0; i<=lastCol; i++)
{
NAString colName = indexColumns[i]->getColName();
if (useInternalSyskey && !colName.compareTo("SYSKEY"))
{
colName = "@SYSKEY"; // "@SYSKEY" corresponds to SYSKEY column
}
ItemExpr *colExpr = new(heap_)
ColReference(new(heap_) ColRefName(colName, tableNameCorr,heap_));
if (result == NULL)
result = colExpr;
else
result = new(heap_) ItemList(result, colExpr);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// Build a list of RenameCols to map from the changes table column names to
// their euqivalent names in the subject table.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *ChangesTable::buildRenameColsList() const
{
ItemExpr *renameList=NULL;
// Get the columns of the subject table.
const NAColumnArray &subjectColumns =
getSubjectNaTable()->getNAColumnArray();
for (CollIndex i=0; i<subjectColumns.entries(); i++)
{
const NAString subjectCol(subjectColumns.getColumn(i)->getColName());
const NAString *colName = &subjectCol;
if (!subjectCol.compareTo("SYSKEY"))
{
// @SYSKEY in the changes table is the equivalent of the SYSKEY
// column in the subject table.
colName = new (heap_) NAString("@SYSKEY", heap_);
}
ColReference *newColName = new(heap_)
ColReference(new (heap_) ColRefName(*colName, *getTableName(), heap_));
RenameCol *renCol = new(heap_)
RenameCol(newColName,
new (heap_) ColRefName(subjectCol, getSubjectTableName(), heap_));
if (renameList==NULL)
renameList = renCol;
else
renameList = new(heap_) ItemList(renameList, renCol);
}
return renameList;
} // ChangesTable::buildRenameColsList
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ItemExpr *ChangesTable::createBaseColExpr(const NAString& colName,
NABoolean isInsert,
NABoolean isUpdate) const
{
const CorrName& corrName =
isInsert ? getCorrNameForNewRow() : getCorrNameForOldRow();
return new(heap_) ColReference(new(heap_)
ColRefName(colName, corrName, heap_));
}
//////////////////////////////////////////////////////////////////////////////
// Take the name of the subject table, and add to it the suffix to create
// the name of the changes table.
//////////////////////////////////////////////////////////////////////////////
void ChangesTable::addSuffixToTableName(NAString& tableName, const NAString& suffix)
{
size_t nameLen = tableName.length();
ComBoolean isQuoted = FALSE;
// special case: string ends with a quote char
if ( '"' == tableName[ nameLen - 1 ] )
{
tableName.resize(nameLen-1);
isQuoted = TRUE;
}
tableName.append( suffix );
if (isQuoted)
tableName.append( "\"" ); // '"'
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
CorrName* ChangesTable::buildChangesTableCorrName(const QualifiedName& tableName,
const NAString& suffix,
ExtendedQualName::SpecialTableType tableType,
CollHeap *heap)
{
NAString changesTableName(tableName.getObjectName());
addSuffixToTableName(changesTableName, suffix);
CorrName *result = new(heap)
CorrName(changesTableName,
heap,
tableName.getSchemaName(),
tableName.getCatalogName());
// Specify the trigger temporary table namespace.
result->setSpecialType(tableType);
return result;
}
/*****************************************************************************
******************************************************************************
**** Class TriggersTempTable
******************************************************************************
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TriggersTempTable::TriggersTempTable(const GenericUpdate *baseNode,
BindWA *bindWA)
: ChangesTable(baseNode, bindWA),
beforeTriggersExist_(FALSE),
boundExecId_(NULL)
{
initialize();
}
//////////////////////////////////////////////////////////////////////////////
// This Ctor is used to transform the scan on the temp-table inside the
// trigger action sub-tree of a statement trigger.
// (see Scan::buildTriggerTransitionTableView in Inlining.cpp)
//////////////////////////////////////////////////////////////////////////////
TriggersTempTable::TriggersTempTable(const CorrName &subjectTableName,
Scan *baseNode,
RowsType scanType,
BindWA *bindWA)
: ChangesTable(subjectTableName, NO_OPERATOR_TYPE, bindWA, scanType),
beforeTriggersExist_(FALSE),
boundExecId_(NULL)
{
initialize();
scanNode_ = baseNode;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RelExpr *TriggersTempTable::transformScan() const
{
// The base class method fixes the scanned table name and takes care
// of the uniquifier selection predicates.
RelExpr *scanNode = ChangesTable::transformScan();
// The select list of this root will filter away all the special columns
// that are not columns of the subject table.
ItemExpr *selectList = buildBaseColsSelectList(*getTableName());
RelRoot *rootNode = new(heap_) RelRoot(scanNode, REL_ROOT, selectList);
NAString transitionName = tableCorr_->getCorrNameAsString();
RenameTable *renameNode = new(heap_) RenameTable(rootNode, transitionName);
return renameNode;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
ItemExpr *TriggersTempTable::createAtColExpr(const NAColumn *naColumn,
NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
const NAString& colName = naColumn->getColName();
if (!colName.compareTo(UNIQUEEXE_COLUMN))
{
// With before triggers, the UniqueExecuteId column is used to propagate
// the Signal actions. If this is an update, only one signal should be
// fired, not two.
if (getBeforeTriggersExist() &&
(!isUpdate || isInsert) )
{
return new(heap_)
ColReference(new(heap_)
ColRefName(InliningInfo::getExecIdVirtualColName(),heap_));
}
else
{
if (boundExecId_ != NULL)
{
// Use the already bound existing exec id function.
return boundExecId_;
}
else
{
// Normal case - use the UniqueExecuteId builtin function.
return new(heap_) UniqueExecuteId();
}
}
}
if (!colName.compareTo(UNIQUEIUD_COLUMN))
{
BindWA::uniqueIudNumOffset offset =
isInsert ?
BindWA::uniqueIudNumForInsert :
BindWA::uniqueIudNumForDelete;
return
new(heap_) ConstValue(bindWA_->getUniqueIudNum(offset));
}
if (!colName.compareTo("@SYSKEY"))
{
return createBaseColExpr("SYSKEY", isInsert, TRUE);
}
return NULL;
} // TriggersTempTable::createColumnExpression()
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// iud log no longer has a SYSKEY
ItemExpr *TriggersTempTable::createSyskeyColExpr(const NAString& colName,
NABoolean isInsert) const
{
CMPASSERT(FALSE);
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
CorrName *TriggersTempTable::calcTargetTableName(const QualifiedName &tableName) const
{
return buildChangesTableCorrName(tableName,
TRIG_TEMP_TABLE_SUFFIX, // __TEMP
ExtendedQualName::TRIGTEMP_TABLE,
heap_);
} // TriggersTempTable::calcTargetTableName()
//////////////////////////////////////////////////////////////////////////////
//
// createSpecificWhereExpr
//
// Create the uniqifier where expresion for the scan node on the temp table.
// This method is used for scanning the temp table for statement triggers,
// for the temp Delete node, and for the effective GU of before triggers.
//
// The uniqifier fields UNIQUE_EXECUTE_ID and UNIQUE_IUD_ID are used to
// identify the rows in a temporary table belong to a specific IUD and a
// specific query execution
//
//////////////////////////////////////////////////////////////////////////////
ItemExpr *TriggersTempTable::createSpecificWhereExpr(RowsType type) const
{
// Create the ItemExpr for the function UniqueExecuteId()
ItemExpr *col1 = new(heap_)
ColReference(new(heap_) ColRefName(UNIQUEEXE_COLUMN,heap_));
ItemExpr *execId;
if (boundExecId_ != NULL)
{
// Use the already bound existing exec id function.
execId = boundExecId_;
}
else
{
// Normal case - use the UniqueExecuteId builtin function.
execId = new(heap_) UniqueExecuteId();
}
BiRelat *predExecId = new(heap_) BiRelat(ITM_EQUAL, col1, execId);
ItemExpr *result;
BiRelat *predIudId = NULL;
if (type == ALL_ROWS)
result = predExecId;
else
{
// Create the ItemExpr for the constatnt UniqueIudNum
ItemExpr *col2 = new(heap_)
ColReference(new(heap_) ColRefName(UNIQUEIUD_COLUMN,heap_));
// Compare it to the correct offset.
BindWA::uniqueIudNumOffset offset =
type == INSERTED_ROWS ?
BindWA::uniqueIudNumForInsert :
BindWA::uniqueIudNumForDelete;
ItemExpr *iudConst = new(heap_) ConstValue(bindWA_->getUniqueIudNum(offset));
predIudId = new(heap_) BiRelat(ITM_EQUAL, col2, iudConst);
result = new(heap_) BiLogic(ITM_AND, predExecId, predIudId);
}
return result;
} // TriggersTempTable::createSpecificWhereExpr()
//////////////////////////////////////////////////////////////////////////////
//
// buildScanForMV
//
// Create a sub-tree that read's only the NEW values from the temp-table, for
// use as a replacement tree for the scan on the subject table in the MV tree.
// This replacement is taking place in insert operations that needs to update
// an ON STATEMENT MV defined on the inserted table.
//
// The built sub-tree is as follows:
//
// RenameTable (temp-table -> subject-table, "@syskey" -> syskey)
// |
// RelRoot (remove Uniquifier columns)
// |
// Scan temp-table (NEW@ values only)
//
// Note: The RelRoot node that removes the uniquifier columns is needed, since
// the RenameTable node ensures that the two tables have the same degree.
///////////////////////////////////////////////////////////////////////////////
RelExpr *TriggersTempTable::buildScanForMV() const
{
// build scan to select NEW@ values only
Scan *scanNode = buildScan(ChangesTable::INSERTED_ROWS);
// build RelRoot to reduce to only columns that correspond to any column
// in the subject table
ItemExpr *selectList = buildColsCorrespondingToBaseSelectList();
RelRoot *rootNode = new (heap_) RelRoot(scanNode, REL_ROOT, selectList);
// build RenameTable to rename table name and columns' names to immitate
// a scan on the subject table to the rest of the MV sub-tree
ItemExpr *renameList = buildRenameColsList();
RelExpr *renameNode = new(heap_)
RenameTable(TRUE, rootNode, getSubjectTableName(), renameList);
return renameNode;
} //TriggersTempTable::buildScanForMV
/*****************************************************************************
******************************************************************************
**** Class MvIudLog
******************************************************************************
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// Ctor for Refresh - building the Scan nodes.
//////////////////////////////////////////////////////////////////////////////
MvIudLog::MvIudLog(CorrName& name,
BindWA *bindWA,
OperatorTypeEnum opType)
: ChangesTable(name, opType, bindWA),
needsRangeLogging_(FALSE),
updatedColumns_(NULL),
deltaDef_(NULL),
addOrigScanPredicate_(FALSE),
mvInfo_(NULL)
{
initialize();
}
//////////////////////////////////////////////////////////////////////////////
// Ctor for logging - building the Insert node.
//////////////////////////////////////////////////////////////////////////////
MvIudLog::MvIudLog(const GenericUpdate *baseNode,
BindWA *bindWA)
: ChangesTable(baseNode, bindWA),
needsRangeLogging_(FALSE),
updatedColumns_(NULL),
deltaDef_(NULL),
addOrigScanPredicate_(FALSE),
mvInfo_(NULL)
{
initialize();
initRangeLogging();
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void MvIudLog::initRangeLogging()
{
if (getOpType() == REL_UNARY_INSERT)
{
const ComMvAttributeBitmap& mvBitmap =
getSubjectNaTable()->getMvAttributeBitmap();
if (mvBitmap.getAutomaticRangeLoggingRequired())
needsRangeLogging_=TRUE;
}
}
//////////////////////////////////////////////////////////////////////////////
// Identify the @ columns of the MV log, and call specific methods for
// building the appropriate expressions.
// This method is used for MvIudLog and its sub-classes, but the
// specific methods are overridden by the sub-classes.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createAtColExpr(const NAColumn *naColumn,
NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
const NAString& colName = naColumn->getColName();
ItemExpr *result = NULL;
switch (colName.data()[sizeof(COMMV_CTRL_PREFIX)-1])
{
case 'E' : // @EPOCH
CMPASSERT(!colName.compareTo(COMMV_EPOCH_COL));
result = createColExprForEpoch();
break;
case 'O' : // @OPERATION_TYPE
CMPASSERT(!colName.compareTo(COMMV_OPTYPE_COL));
result = createColExprForOpType(isInsert, isUpdate, isUndo);
break;
case 'I' : // @IGNORE
CMPASSERT(!colName.compareTo(COMMV_IGNORE_COL));
result = createColExprForIgnore();
break;
case 'U' : // @UPDATE_BITMAP
CMPASSERT(!colName.compareTo(COMMV_BITMAP_COL));
result = createColExprForBitmap(naColumn, isUpdate);
break;
case 'R' : // @RANGE_SIZE
CMPASSERT(!colName.compareTo(COMMV_RANGE_SIZE_COL));
result = createColExprForRangeSize(isInsert);
break;
case 'S' : // @SYSKEY - This is the base table SYSKEY column
CMPASSERT(!colName.compareTo(COMMV_BASE_SYSKEY_COL));
result = createColExprForAtSyskey(isInsert);
break;
case 'T' : // @TS - timestamp
CMPASSERT(!colName.compareTo(COMMV_TS_COL));
result = createColExprForAtTimestamp(isInsert,isUpdate,isUndo);
break;
case 'A' : // @ALIGNMENT
// ALIGNMENT is not used
CMPASSERT(!colName.compareTo(COMMV_ALIGNMENT_COL));
{
Int32 bitmapSize = naColumn->getType()->getNominalSize();
NAString *bitmap = new(heap_) NAString('\0', bitmapSize, heap_);
result = new(heap_) SystemLiteral(*bitmap);
}
break;
default : // Unknown column
CMPASSERT(FALSE);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @EPOCH column.
// Insert the CurrentEpoch function pipelined from the triggering node.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForEpoch() const
{
return new(heap_)
ColReference(new(heap_)
ColRefName(InliningInfo::getEpochVirtualColName(),heap_));
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @OPERATION_TYPE column.
// If range logging is enabled - the value is generated by the VSBBInsert
// node. Otherwise use the values for Insert/Delete/Update.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForOpType(NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
ItemExpr *result = NULL;
if (needsRangeLogging_)
{
CMPASSERT(isInsert)
result = new(heap_)
ColReference(new(heap_)
ColRefName(InliningInfo::getRowTypeVirtualColName(),heap_));
}
else
{
Int32 typeValue = isInsert ? ComMvRowType_Insert : ComMvRowType_Delete;
if (isUndo)
typeValue = ComMvRowType_Delete;
if (isUpdate)
typeValue |= ComMvRowType_Update;
result = new(heap_) SystemLiteral(typeValue);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @IGNORE column.
// Use zero. This field is later updated by the refresh utility during
// the duplicate elimination algorithm.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForIgnore() const
{
return new(heap_) SystemLiteral(0);
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @UPDATE_BITMAP column.
// If it is an Update operation - calculate a bitmap of updated columns.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForBitmap(const NAColumn *naColumn,
NABoolean isUpdate) const
{
ItemExpr *result = NULL;
if (!isUpdate)
{
result = new(heap_) SystemLiteral(); // NULL
}
else
{
Int32 bitmapSize = naColumn->getType()->getNominalSize();
CMPASSERT(updatedColumns_ != NULL);
unsigned char *colsBitmap = new(heap_) unsigned char[bitmapSize];
updatedColumns_->markColumnsOnBitmap(colsBitmap, bitmapSize);
NAString *bitmap = new(heap_) NAString((const char *) colsBitmap, bitmapSize, heap_);
result = new(heap_) SystemLiteral(*bitmap);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @RANGE_SIZE column.
// If range logging is enabled, the value is generated by the VSBBInsert node.
// Otherwise use 0 for Delete and 1 for Insert.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForRangeSize(NABoolean isInsert) const
{
ItemExpr *result = NULL;
if (needsRangeLogging_)
{
// range logging is not supported
CMPASSERT(isInsert);
result = new(heap_)
ColReference(new(heap_)
ColRefName(InliningInfo::getRowCountVirtualColName(),heap_));
}
else
{
result = new(heap_)
SystemLiteral(isInsert ? 1 : 0);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @SYSKEY column.
// This is the base table SYSKEY column.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForAtSyskey(NABoolean isInsert) const
{
const CorrName& corrName =
isInsert ? getCorrNameForNewRow() : getCorrNameForOldRow();
return new(heap_)
ColReference(new(heap_) ColRefName("SYSKEY", corrName, heap_));
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the SYSKEY column.
// This is the log SYSKEY column. Use 0 since the real value is generated
// by DP2 as the timestamp of insertion.
//////////////////////////////////////////////////////////////////////////////
// iud log no longer have a syskey, we now enforce uniqueness using a @TS
ItemExpr *MvIudLog::createSyskeyColExpr(const NAString& colName,
NABoolean isInsert) const
{
return new(heap_) SystemLiteral(0);
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @TS column.
// Generate a unique, sequential value for each new row in the iud log
// The intention of this method is to generate a timestamp and increment
// by some value for each new row to avoid duplicate timestamps.
//
// The value is incremented by 3 in order to leave room for updates where
// each side of the union begins with the same persistent value. The insert
// side of the union will use an additional +1. Additionally, leave room
// for undo which uses a +2 on top of the persistent value.
//
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createColExprForAtTimestamp(NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
ItemExpr *counterExpr, *incrementExpr, *tsExpr;
counterExpr = new (heap_) ItmPersistentExpressionVar(0);
incrementExpr = new (heap_) ItmBlockFunction
(counterExpr,
new (heap_) Assign (counterExpr,
new (heap_) BiArith(ITM_PLUS,
counterExpr,
new (heap_) ConstValue(ROW_TS_INCR))));
// Synthesize the types and value IDs for the new items
incrementExpr->synthTypeAndValueId(TRUE);
tsExpr = new (heap_)
BiArith(ITM_PLUS,
incrementExpr,
new(heap_) ColReference(
new(heap_) ColRefName(InliningInfo::getMvLogTsColName(),heap_)));
// This is the +1 on the insert row of an update operation
// or a +2 for an undo row
if ( (isUpdate && isInsert ) || isUndo )
{
Lng32 incrAmt = ( isUndo ) ? 2 : 1;
tsExpr = new (heap_)
BiArith(ITM_PLUS,tsExpr,new (heap_)SystemLiteral(incrAmt));
}
return tsExpr;
}
//////////////////////////////////////////////////////////////////////////////
// Calculate the log name from the base table name.
//////////////////////////////////////////////////////////////////////////////
CorrName *MvIudLog::calcTargetTableName(const QualifiedName &tableName) const
{
return buildChangesTableCorrName(tableName,
COMMV_IUD_LOG_SUFFIX,
ExtendedQualName::IUD_LOG_TABLE,
heap_);
}
//////////////////////////////////////////////////////////////////////////////
// The Epoch Predicate is:
// singleEpoch: @EPOCH = <beginEpoch>
// otherwise: @EPOCH BETWEEN <beginEpoch> AND <endEpoch>
// The Epoch predicate is the same for the range log as well. This is why
// this method is declared static - it is called by buildJoinWithRangeLog().
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::buildLogEpochPredicate(const DeltaDefinition *deltaDef,
CollHeap *heap)
{
CMPASSERT(deltaDef != NULL);
Lng32 beginEpoch = deltaDef->getBeginEpoch();
Lng32 endEpoch = deltaDef->getEndEpoch();
NABoolean singleEpoch = (beginEpoch == endEpoch);
ItemExpr *epochPred = NULL;
if (singleEpoch)
{
epochPred =
BinderUtils::buildPredOnCol(ITM_EQUAL, COMMV_EPOCH_COL, beginEpoch, heap);
}
else
{
epochPred = new(heap)
Between(new(heap) ColReference(new(heap) ColRefName(COMMV_EPOCH_COL,heap)),
new(heap) SystemLiteral(beginEpoch),
new(heap) SystemLiteral(endEpoch));
}
return epochPred;
}
//////////////////////////////////////////////////////////////////////////////
// Build the log selection predicate on the @EPOCH, @IGNORE and
// @OPERATION_TYPE,@BITMAP columns.
// This predicate depends on two factors: the scan type (inserts only, deletes
// only or both) and if its a single epoch refresh (beginEpoch = endEpoch).
// This predicate has four parts:
// epochPred is:
// singleEpoch: @EPOCH = <beginEpoch>
// otherwise: @EPOCH BETWEEN <beginEpoch> AND <endEpoch>
// ignorePred is:
// singleEpoch: N/A
// otherwise: @IGNORE < <beginEpoch>
// opTypePred depends on scanType (should ignore range rows):
// if SCAN_INSERTS: (@OPERATION_TYPE = 0) OR (@OPERATION_TYPE = 2)
// if SCAN_DELETES: (@OPERATION_TYPE = 1) OR (@OPERATION_TYPE = 3)
// if SCAN_BOTH : @OPERATION_TYPE < 4
// updateBitmapPred : (@BITMAP IS NULL) OR ((MV.BITMAP & @BITMAP) > 0)
// We use here a special builtin function that evalutes
// this expression
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::createSpecificWhereExpr(RowsType type) const
{
CMPASSERT(deltaDef_ != NULL);
Lng32 beginEpoch = deltaDef_->getBeginEpoch();
Lng32 endEpoch = deltaDef_->getEndEpoch();
NABoolean singleEpoch = (beginEpoch == endEpoch);
ItemExpr *result = NULL;
result =
BinderUtils::buildPredOnCol(ITM_LESS,
COMMV_IGNORE_COL,
beginEpoch,
heap_);
ItemExpr *opTypePred = NULL;
switch (type)
{
case INSERTED_ROWS:
opTypePred = new (heap_)
BiLogic(ITM_OR,
BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_Insert,
heap_),
BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_InsertOfUpdate,
heap_) );
break;
case DELETED_ROWS:
opTypePred = new (heap_)
BiLogic(ITM_OR,
BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_Delete,
heap_),
BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_DeleteOfUpdate,
heap_) );
break;
case ALL_ROWS:
// Since all the range operations are logged in negative epochs
// we do not need to screen them while we are reading the IUD records
// (always in positive epochs)
break;
default: CMPASSERT(FALSE);
}
if (opTypePred)
{
if (result)
{
result = new(heap_) BiLogic(ITM_AND, result, opTypePred);
}
else
{
result = opTypePred;
}
}
ItemExpr *usedColPredicate = buildBitmapColPredicate();
if (result)
{
result = new(heap_) BiLogic(ITM_AND, result, usedColPredicate);
}
else
{
result = usedColPredicate;
}
result->setSelectivityFactor(1.0);
// For Multi-Txn refresh we need to add the original selection predicates.
if (getAddOrigScanPredicateFlag())
{
ItemExpr *origScanPredicate = buildOrigScanPredicate();
if (origScanPredicate != NULL)
result = new(heap_) BiLogic(ITM_AND, result, origScanPredicate);
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// The orig selection predicate is on the base table columns. This recursive
// method finds all such column references, and fixes them to be on the
// log table instead.
//////////////////////////////////////////////////////////////////////////////
NABoolean MvIudLog::fixReferencesFromBaseTableToLog(ItemExpr *expr) const
{
NABoolean result = TRUE;
if (expr->getArity() > 0)
{
for (Int32 i=0; i<expr->getArity(); i++)
result &= fixReferencesFromBaseTableToLog(expr->child(i));
}
else
{
if (expr->getOperatorType() == ITM_REFERENCE)
{
ColReference *colRef = (ColReference *)expr;
QualifiedName& tableName =
colRef->getCorrNameObj().getQualifiedNameObj();
// Ignore predicates that reference other tables.
if (tableName != getSubjectTableName().getQualifiedNameObj())
return FALSE;
tableName = getTableName()->getQualifiedNameObj();
}
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
// For Multi-Txn refresh we need to add the original selection predicates.
// Get the predicate text from MVInfo, parse them and return them.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::buildOrigScanPredicate() const
{
const QualifiedName& tableName = getSubjectTableName().getQualifiedNameObj();
const MVUsedObjectInfo *usedObject =
getMvInfo()->findUsedInfoForTable(tableName);
const NAString& textPredicate = usedObject->getSelectionPredicates();
if (textPredicate == "")
return NULL;
Parser parser(bindWA_->currentCmpContext());
ItemExpr *origScanPredicate =
parser.getItemExprTree((char *)textPredicate.data());
ItemExprList predicateList(origScanPredicate, bindWA_->wHeap(), ITM_AND);
for (Int32 i=predicateList.entries()-1; i>=0; i--)
{
if (fixReferencesFromBaseTableToLog(predicateList[i]) == FALSE)
predicateList.removeAt(i);
}
ItemExpr *fixedPredicate = predicateList.convertToItemExpr();
return fixedPredicate;
}
//////////////////////////////////////////////////////////////////////////////
// updateBitmapPred : (@OPERATION_TYPE = 0) INSERT
// OR
// (@OPERATION_TYPE = 1) DELETE
// OR
// (@BITMAP IS NULL) OR
// OR
// ((MV.BITMAP & @BITMAP) > 0)
// We use here a special builtin function that evalutes
// this expression
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::buildBitmapColPredicate() const
{
const LIST(Lng32) &usedColumns =
mvInfo_->getUsedColumns(getSubjectTableName().getQualifiedNameObj());
ItemExpr *constantBitmap = constructUpdateBitmapFromList(usedColumns);
ItemExpr *bitmapCol = new(heap_)
ColReference(new(heap_) ColRefName(COMMV_BITMAP_COL, heap_));
ItemExpr *isBitwiseTrue = new(heap_) IsBitwiseAndTrue(bitmapCol,constantBitmap );
ItemExpr *isBitmapColNull = new(heap_) UnLogic(ITM_IS_NULL,bitmapCol);
ItemExpr *insertRecord = BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_Insert,
heap_);
ItemExpr *deleteRecord = BinderUtils::buildPredOnCol(ITM_EQUAL,
COMMV_OPTYPE_COL,
ComMvRowType_Delete,
heap_);
return new(heap_)
BiLogic(ITM_OR, insertRecord, new(heap_)
BiLogic(ITM_OR, deleteRecord, new(heap_)
BiLogic(ITM_OR, isBitmapColNull, isBitwiseTrue)));
}
//////////////////////////////////////////////////////////////////////////////
// Create a constant bitmap in the size of the bitmap log column
// from the input parameter list of integers.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::constructUpdateBitmapFromList(const LIST(Lng32) &columnList) const
{
CMPASSERT (mvInfo_ != NULL);
// Find out the size of the bitmap column in the iud log
const NAColumnArray &logColumns = getNaTable()->getNAColumnArray();
NAColumn *currentTempCol = NULL;
for (CollIndex i=0; i < logColumns.entries(); i++)
{
currentTempCol = logColumns.getColumn(i);
if (!currentTempCol->getColName().compareTo(COMMV_BITMAP_COL))
break;
}
CMPASSERT(!currentTempCol->getColName().compareTo(COMMV_BITMAP_COL));
Int32 bitmapSize = currentTempCol->getType()->getNominalSize();
// Initialize a constant bitmap from the used cols list of the mv
unsigned char *usedColsBitmap = new(heap_) unsigned char[bitmapSize];
for (int i=0; i<bitmapSize; i++)
usedColsBitmap[i] = 0;
// Iterate through the column list and set the corresponding bit in the
// bitmap
for (CollIndex j=0; j < columnList.entries(); j++)
{
CollIndex b = columnList[j];
// set bit number b in the array
if (b/8 < bitmapSize)
usedColsBitmap[b/8] |= 0x01 << b % 8;
}
// Construct a constant string expression from the bitmap
NAString *bitmap = new(heap_) NAString((char *) usedColsBitmap, bitmapSize, heap_);
ItemExpr *result = new(heap_) SystemLiteral(*bitmap);
return result;
}
//////////////////////////////////////////////////////////////////////////////
// This method builds an expression that evaluates to 1 only for rows that
// represent an indirect update operation (has to be implemented by a
// delete-insert combination).
// We rely on the fact that the update bitmap is not null only for update rows.
// (@BITMAP IS NOT NULL)
// AND
// ((MV.BITMAP & @BITMAP) > 0)
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLog::buildIndirectUpdateExpression() const
{
const MVUsedObjectInfo *usedObject =
mvInfo_->findUsedInfoForTable(getSubjectTableName().getQualifiedNameObj());
const LIST(Lng32) &mvInfoIndirectCols = usedObject->getIndirectUpdateCols();
ItemExpr *constantBitmap =
constructUpdateBitmapFromList(mvInfoIndirectCols);
ItemExpr *bitmapCol = new(heap_)
ColReference(new(heap_) ColRefName(COMMV_BITMAP_COL, heap_));
ItemExpr *isBitwiseTrue = new(heap_)
IsBitwiseAndTrue(bitmapCol, constantBitmap);
ItemExpr *isBitmapColNotNull = new(heap_)
UnLogic(ITM_IS_NOT_NULL, bitmapCol);
return new(heap_)
BiLogic(ITM_AND, isBitmapColNotNull, isBitwiseTrue);
}
// For mvIudLog, the union above Tuples must be ordered.
void MvIudLog::setTuplesUnionType(Union *unionNode) const
{
unionNode->setOrderedUnion();
}
/*****************************************************************************
******************************************************************************
**** Class MvIudLogForMvLog
**** used by the MVLOG command
******************************************************************************
*****************************************************************************/
// MVLOG command is currently not supported
//////////////////////////////////////////////////////////////////////////////
// Ctor for building the Insert node.
//////////////////////////////////////////////////////////////////////////////
MvIudLogForMvLog::MvIudLogForMvLog(CorrName& tableName,
BindWA *bindWA)
: MvIudLog(tableName, bindWA, REL_UNARY_UPDATE)
{
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the -@EPOCH column.
//
// we log the ranges into negative epochs in order to provide efficent
// reading from the IUD log of the range records
//
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForEpoch() const
{
ItemExpr *epochColExpr = MvIudLog::createColExprForEpoch();
ItemExpr *epochExpr = new(heap_)BiArith(ITM_TIMES,
epochColExpr,
new(heap_) SystemLiteral(-1));
return epochExpr;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @OPERATION_TYPE column.
// In the context row, this field is used for storing the MSW of the MV UID.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForOpType(NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
Int32 typeValue = isInsert ?
ComMvRowType_EndRange :
ComMvRowType_BeginRange;
return new(heap_) SystemLiteral(typeValue);
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @IGNORE column.
// In the context row, this field is used for storing the Begin Epoch number.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForIgnore() const
{
return new(heap_) SystemLiteral(0);
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @UPDATE_BITMAP column.
// Not used for context rows.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForBitmap(const NAColumn *naColumn,
NABoolean isUpdate) const
{
return new(heap_) SystemLiteral(); // NULL.;
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @RANGE_SIZE column.
// In the context row, this field is used for storing the LSW of the MV UID.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForRangeSize(NABoolean isInsert) const
{
return new(heap_) SystemLiteral(10000); // Should be greater than 0.
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @SYSKEY column.
// This is the base table SYSKEY column.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForAtSyskey(NABoolean isInsert) const
{
return createBaseColExpr("SYSKEY", isInsert, TRUE);
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the @TS column.
// Not used for context rows.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createColExprForAtTimestamp(NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
return new(heap_) SystemLiteral(0);
}
//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvIudLogForMvLog::createBaseColExpr(const NAString& colName,
NABoolean isInsert,
NABoolean isUpdate) const
{
NAString virtualColumnName(colName);
if (isInsert)
virtualColumnName.append(MvLogInternalNames::getEndRangeSuffix());
else
virtualColumnName.append(MvLogInternalNames::getBeginRangeSuffix());
return new(heap_)
ColReference(new(heap_) ColRefName(virtualColumnName, heap_));
}
/*****************************************************************************
******************************************************************************
**** Class MvLogForContextRows
**** used by multi-transactional activatoins of INTERNAL REFRESH, for
**** saving context rows.
******************************************************************************
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// Context logs are using the name of the MV (with no suffix), but the
// columns of the base table. The ChangesTable data members are initialized
// as follows:
// tableCorr_ - Context log name.
// subjectTable_ - Name of MV.
// naTable_ - NATable of context log.
// subjectNaTable_ - NATable of base table.
//////////////////////////////////////////////////////////////////////////////
MvLogForContextRows::MvLogForContextRows(const QualifiedName& baseTableName,
const CorrName& mvName,
Lng32 epochNumber,
ItemExpr *catchupNo,
BindWA *bindWA)
: ChangesTable(mvName, REL_UNARY_INSERT, bindWA),
epochNumber_(epochNumber),
catchupNo_(catchupNo)
{
// Save the state of the RI flag
BindContext *currentBindContext = bindWA_->getCurrentScope()->context();
NABoolean prevRiState = currentBindContext->inRIConstraint();
// Set the RI flag to allow calling getNATable() without counting it.
currentBindContext->inRIConstraint() = TRUE;
// Get the NATable of the base table.
CorrName baseTableCorrName(baseTableName);
subjectNaTable_ = bindWA_->getNATable(baseTableCorrName);
// Restore the RI flag.
currentBindContext->inRIConstraint() = prevRiState;
// Initialize the rest of the data members.
initialize();
corrNameForNewRow_ = baseTableName;
// Old names are not used
}
//////////////////////////////////////////////////////////////////////////////
// Calculate the log name from the base table name.
// The context table has the same name as the MV (no suffix), and is in the
// range log name space.
//////////////////////////////////////////////////////////////////////////////
CorrName *MvLogForContextRows::calcTargetTableName(const QualifiedName &tableName) const
{
return buildChangesTableCorrName(tableName,
"", // No Suffix
ExtendedQualName::RANGE_LOG_TABLE,
heap_);
}
//////////////////////////////////////////////////////////////////////////////
// For the MV context log, there are only two @ column: @EPOCH and @SYSKEY.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvLogForContextRows::createAtColExpr(const NAColumn *naColumn,
NABoolean isInsert,
NABoolean isUpdate,
NABoolean isUndo) const
{
const NAString& colName = naColumn->getColName();
if (colName.data()[1] == 'E')
{
CMPASSERT(!colName.compareTo(COMMV_EPOCH_COL));
return new(heap_) SystemLiteral(epochNumber_);
}
else
{
CMPASSERT(!colName.compareTo(COMMV_SYSKEY_COL));
return new(heap_)
ColReference(new(heap_) ColRefName(COMMV_SYSKEY_COL,
getCorrNameForNewRow(),heap_));
}
}
//////////////////////////////////////////////////////////////////////////////
// build the expression to insert into the log SYSKEY column.
// There is no log SYSKEY column for the context log!
//////////////////////////////////////////////////////////////////////////////
// should never get here
ItemExpr *MvLogForContextRows::createSyskeyColExpr(const NAString& colName,
NABoolean isInsert) const
{
CMPASSERT(FALSE);
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
// Build the selection predicate for reading a context row from the log.
// This method is used for reading the phase 1 context and the catchup
// context.
//////////////////////////////////////////////////////////////////////////////
ItemExpr *MvLogForContextRows::createSpecificWhereExpr(RowsType type) const
{
ItemExpr *contextEpochType = NULL;
switch(type)
{
case PHASE1_ROWS :
// For Phase 1 Scans, the @EPOCH column should equal the begin epoch
// number from the INTERNAL REFRESH delta definition.
contextEpochType = new(heap_) SystemLiteral(epochNumber_);
break;
case CATCHUP_ROWS :
// For Catchup Scans, the @EPOCH column should equal the CATCHUP
// parameter.
contextEpochType = catchupNo_;
break;
default: CMPASSERT(FALSE);
}
ItemExpr *contextEpochExpr = new(heap_)
BiRelat(ITM_EQUAL,
new(heap_) ColReference(new(heap_) ColRefName(COMMV_EPOCH_COL,heap_) ),
contextEpochType);
return contextEpochExpr;
}