blob: cdaa98d1f75adb0ae5da3a415cc38ae3329b5def [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: MvLog.cpp
* Description: implementation of the MVLOG command
* Created: 09/25/2000
* Language: C++
*
*
*
*
******************************************************************************
*/
#include "MvLog.h"
#include "BindWA.h"
#include "NATable.h"
#include "PartFunc.h"
#include "ItemOther.h"
#include "ItemLog.h"
#include "ItemFunc.h"
#include "AllRelExpr.h"
#include "ChangesTable.h"
#include "ComMvAttributeBitmap.h"
// MVLOG command is not supported
const MvLogInternalNames MvLog::internalNames_;
const char MvLogInternalNames::beginRangeSuffix_[] = "_BEGIN";
const char MvLogInternalNames::endRangeSuffix_[] = "_FIRST";
const char MvLogInternalNames::brSuffix_[] = "_BR";
const char MvLogInternalNames::erSuffix_[] = "_ER";
const char MvLogInternalNames::endSuffix_[] = "_END";
const char MvLogInternalNames::finalResultName_[] = "TOP_NAME";
const char MvLogInternalNames::tupleListName_[] = "TUPLE_LIST";
const char MvLogInternalNames::tupleListRoot1_[] = "TUPLE_LIST_P1";
const char MvLogInternalNames::tupleListRoot2_[] = "TUPLE_LIST_P2";
const char MvLogInternalNames::getFirstRoot_[] = "FIRST1_SCAN";
const char MvLogInternalNames::brIsGreater_[] = "BR_IS_GREATER";
const char MvLogInternalNames::erIsLess_[] = "ER_IS_LESS";
const char MvLogInternalNames::simpleRangeTuple_[] = "SIMPLE_RANGE_TUPLE";
//----------------------------------------------------------------------------
MvLog::MvLog(const QualifiedName *tableName,
const ItemExpr *columnNames,
ItemExpr *rangeBegin,
ItemExpr *rangeEnd,
CollHeap *oHeap)
: BinderOnlyNode(REL_MVLOG, oHeap),
tableName_ (tableName),
pColumnNamesItem_(columnNames),
pBeginRange_ (rangeBegin),
pEndRange_ (rangeEnd),
isRangePartitioned_(TRUE),
pBeginRangeExprList_(pBeginRange_, oHeap),
pEndRangeExprList_(pEndRange_, oHeap),
pBindWA_ (NULL),
ciBoundries_(oHeap),
tupleListPhase1CorrName_(getNames().getTupleListRoot1(), oHeap),
tupleListPhase2CorrName_(getNames().getTupleListRoot2(), oHeap),
tupleListPhase3CorrName_(getNames().getTupleListName(), oHeap),
tupleListCorrName_(getNames().getTupleListName(), oHeap),
first1NodeCorrName_(getNames().getGetFirstRoot(), oHeap),
emptyCorrName_("", oHeap),
pRelevantCiPositionsList_(oHeap),
mvLogColNamesList_(oHeap),
baseTableCiNamesList_(oHeap),
nonCiColsNamesList_(oHeap),
nonCiColsExprList_(oHeap),
beginColNamesList_(oHeap),
endColNamesList_(oHeap),
first1ColNamesList_(oHeap),
brColNamesList_(oHeap),
erColNamesList_(oHeap),
colsMinDefualtValList_(oHeap),
colsMaxDefualtValList_(oHeap),
pDirectionVector_(NULL),
pNaTable_(NULL),
heap_(oHeap)
{
}
//----------------------------------------------------------------------------
MvLog::~MvLog()
{
}
//----------------------------------------------------------------------------
void MvLog::cleanupBeforeSelfDestruct()
{
delete tableName_;
delete pColumnNamesItem_;
delete pBeginRange_;
delete pEndRange_;
// Do NOT delete pDirectionVector_! It is used by constructed expressions.
}
//----------------------------------------------------------------------------
const NAString MvLog::getText() const
{
return "mvlog";
}
//----------------------------------------------------------------------------
RelExpr * MvLog::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap)
{
CMPASSERT(FALSE); // not used.
return NULL;
}
//----------------------------------------------------------------------------
void MvLog::addLocalExpr(LIST(ExprNode *) &xlist,
LIST(NAString) &llist) const
{
RelExpr::addLocalExpr(xlist,llist);
}
//----------------------------------------------------------------------------
// The bindNode() method builds the RelExpr tree that does the work, binds it
// and return it instead of itself.
// The schematic tree that is built is as follows:
// TSJ
// / \
// Read Root
// Epoch |
// Block TSJ
// / \
// Rename Insert
// TOP_NAME Log
// | Block
// Root
// |
// Union
// / \
// Root EndRange
// | Tuple
// TSJ Block
// / \
// Tuple GetFirst
// List Block
// Block
//
// Each of the 5 blocks is described in the method tht builds it.
//
RelExpr *MvLog::bindNode(BindWA *bindWA)
{
pBindWA_ = bindWA;
if (initializeInternalDataStructures() == FALSE)
return NULL;
RelExpr * topNode = NULL;
if (isRangePartitioned_)
{
// Prepare the lists of partition boundary values.
constructBoundriesList();
topNode = buildTupleListBlock();
topNode = buildJoinWithGetFirstBlock(topNode);
topNode = buildUnionWithEndRangeTupleList(topNode);
}
else
{
topNode = buildSimpleRangeTuple();
}
topNode = buildFinalSelectList(topNode);
topNode = buildInsertIntoLog(topNode);
topNode = buildScanEpochFromTable(topNode);
// Make sure no outputs are pipelined out.
RelRoot *topRoot = new(heap_) RelRoot(topNode);
topRoot->setEmptySelectList();
// Bind the constructed tree.
RelExpr *mvLogTree = topRoot->bindNode(bindWA);
// Delete all data members before saying goodbye.
cleanupBeforeSelfDestruct();
// Return the bound MVLog tree instead of this.
return mvLogTree;
} // MvLog::bindNode
//----------------------------------------------------------------------------
// Returns FALSE if an error was encountered.
NABoolean MvLog::initializeInternalDataStructures()
{
CorrName corrName(*tableName_);
pNaTable_ = pBindWA_->getNATable(corrName, FALSE);
if (pBindWA_->errStatus())
return FALSE;
if (!isTableValidForMvLog())
return FALSE;
isRangePartitioned_ = isTableRangePartitioned();
pDirectionVector_ =
BinderUtils::buildClusteringIndexDirectionVector(pNaTable_, heap_);
// Prepare the lists of column names (all columns, CI columns etc.).
buildBaseTableColumnNamesLists();
if (pBindWA_->errStatus())
return FALSE;
checkMvLogColsAreCiPrefix();
if (pBindWA_->errStatus())
return FALSE;
// Prepare the column names for use by other methods.
buildAllRefNames();
return TRUE;
}
//----------------------------------------------------------------------------
// Make sure running the MVLOG command on this table is legal.
// It is not legal for AUTOMATIC RANGELOG tables, or if logging
// is not required at all.
NABoolean MvLog::isTableValidForMvLog() const
{
const ComMvAttributeBitmap& bitmap = pNaTable_->getMvAttributeBitmap();
if (!bitmap.getEnableMVLOGExecution())
{
ComRangeLogType logType = bitmap.getRangeLogType();
if (logType != COM_MANUAL_RANGELOG && logType != COM_MIXED_RANGELOG)
{
// 12309 MVLOG command can only be used on MANUAL RANGELOG and MIXED RANGELOG tables.
*CmpCommon::diags() << DgSqlCode(-12309);
}
else
{
// 12310 No Materialized Views are defined on table $0~TableName.
*CmpCommon::diags() << DgSqlCode(-12310)
<< DgTableName(tableName_->getQualifiedNameAsString());
}
pBindWA_->setErrStatus();
return FALSE;
}
return TRUE;
}
//----------------------------------------------------------------------------
// Returns FALSE if the table is hash partitioned, or if there is only
// a single partition.
NABoolean MvLog::isTableRangePartitioned() const
{
const PartitioningFunction *partFunc =
pNaTable_->getClusteringIndex()->getPartitioningFunction();
if (partFunc->isAHashPartitioningFunction() ||
partFunc->isAHashDistPartitioningFunction() ||
partFunc->isAHash2PartitioningFunction() )
{
// Table is Hash partitioned.
return FALSE;
}
else
{
if (partFunc->isASinglePartitionPartitioningFunction())
{
// Table has a single partition.
return FALSE;
}
else
{
CMPASSERT(partFunc->isARangePartitioningFunction());
}
}
return TRUE;
}
//----------------------------------------------------------------------------
// Prepare column name lists for use by other methods. The lists are:
// 1. The columns from the MVLOG command line.
// 2. The base table clustering index columns.
// 3. The base table columns that are not in the clustering index.
void MvLog::buildBaseTableColumnNamesLists()
{
CMPASSERT(NULL != pNaTable_);
const NAFileSet * pCiFileSet = pNaTable_->getClusteringIndex();
// MVLOG COMMAND LINE NAMES
ItemExprList columnNamesList((ItemExpr *)pColumnNamesItem_, heap_);
for (CollIndex i(0); i < columnNamesList.entries() ; i++)
{
ItemExpr * pItemExpr = columnNamesList[i];
CMPASSERT(ITM_REFERENCE == pItemExpr->getOperatorType());
ColReference * pColRef = (ColReference*)pItemExpr;
const NAString& colRefName = pColRef->getColRefNameObj().getColName();
mvLogColNamesList_.insert(new(heap_)NAString(colRefName, heap_));
}
// BASE TABLE CI COLUMN NAMES
const NAColumnArray & ciColumns = pCiFileSet->getIndexKeyColumns();
CollIndex numberOfMvLogCols = mvLogColNamesList_.entries();
if (numberOfMvLogCols > ciColumns.entries())
{
// 12311 The column list is not a prefix of the clustering key columns of table $0~TableName.
*CmpCommon::diags() << DgSqlCode(-12311)
<< DgTableName(tableName_->getQualifiedNameAsString());
pBindWA_->setErrStatus();
return;
}
CollIndex lastMvLogColIndex = numberOfMvLogCols -1;
for ( CollIndex j = 0 ; j < ciColumns.entries() ; j++)
{
const NAString& ciColName = ciColumns[j]->getColName();
baseTableCiNamesList_.insert(new (heap_)NAString(ciColName, heap_));
}
// Initialize the list of non-clustering-key columns (both names and min
// values). The min values are the values inserted into the log, since
// they are not read anyway.
const NAColumnArray& allBaseTableCols = pNaTable_->getNAColumnArray();
for (CollIndex k=0; k<allBaseTableCols.entries(); k++)
{
const NAColumn *colObj = allBaseTableCols[k];
const NAString *colName = &colObj->getColName();
if (!colObj->isClusteringKey())
{
nonCiColsNamesList_.insert(colName);
const NAType *type = colObj->getType();
ItemExpr *minValue = NULL;
if (type->supportsSQLnull())
minValue = new(heap_) Cast(new(heap_) SystemLiteral(), type);
else
minValue = new(heap_) SystemLiteral(type, TRUE, TRUE);
nonCiColsExprList_.insert(minValue);
}
}
} // MvLog::buildBaseTableColumnNamesLists
//----------------------------------------------------------------------------
// Verify that the list of columns from the MVLOG command line are a prefix
// of the clustering index columns of the base table.
void MvLog::checkMvLogColsAreCiPrefix() const
{
CMPASSERT(mvLogColNamesList_.entries() <= baseTableCiNamesList_.entries());
// For each column from the command line
for ( CollIndex i(0) ; i < mvLogColNamesList_.entries() ; i++)
{
const NAString& tableColName = *baseTableCiNamesList_[i];
const NAString& commandColName = *mvLogColNamesList_[i];
// check it against the corresponding column in the CI.
if(tableColName != commandColName)
{
// 12311 The column list is not a prefix of the clustering key columns of table $0~TableName.
*CmpCommon::diags() << DgSqlCode(-12311)
<< DgTableName(tableName_->getQualifiedNameAsString());
pBindWA_->setErrStatus();
return;
}
}
} // MvLog::checkMvLogColsAreCiPrefix
//----------------------------------------------------------------------------
// Prepare lists of column names, that are derived from the base table
// column names, with several different suffixes.
void MvLog::buildAllRefNames()
{
for (CollIndex i(0); i < baseTableCiNamesList_.entries() ; i++)
{
const NAString& colRefName = *baseTableCiNamesList_[i];
NAString *pBeginName = new(heap_) NAString(colRefName, heap_);
pBeginName->append(getNames().getBeginRangeSuffix()); // "_BEGIN"
beginColNamesList_.insert(pBeginName);
NAString *pEndName = new(heap_) NAString(colRefName, heap_);
pEndName->append(getNames().getEndSuffix()); // "_END"
endColNamesList_.insert(pEndName);
NAString *pBRName = new(heap_) NAString(colRefName, heap_);
pBRName->append(getNames().getBrSuffix()); // "_BR"
brColNamesList_.insert(pBRName);
NAString *pERName = new(heap_) NAString(colRefName, heap_);
pERName->append(getNames().getErSuffix()); // "_ER"
erColNamesList_.insert(pERName);
NAString *pFirst1Name = new(heap_) NAString(colRefName, heap_);
pFirst1Name->append(getNames().getEndRangeSuffix()); // "_FIRST"
first1ColNamesList_.insert(pFirst1Name);
}
} // MvLog::buildAllRefNames
//----------------------------------------------------------------------------
// Here we create default values for CI columns. These values are used when
// not all the CI columns are specified by the user.
// we actually create two default lists - one is a MAX default value and the
// other is the MIN default value.
// Notice that if a column is DESC, than the max is actually a MIN. And vice
// versa.
void MvLog::buildClusteringIndexDefaultValues(const NAFileSet * pCiFileSet)
{
const NAColumnArray& ciColumns = pCiFileSet->getIndexKeyColumns();
// These values are used as parameters to ConstValue.
enum { MAX = FALSE, MIN = TRUE };
NABoolean nullable = FALSE; // all CI are not nullable
CMPASSERT(pRelevantCiPositionsList_.entries() == mvLogColNamesList_.entries());
for(CollIndex i(0) ; i < ciColumns.entries() ; i++)
{
NAColumn *pCol = ciColumns[i];
const NAType *type = pCol->getType();
ConstValue *orderingMinVal = NULL;
ConstValue *orderingMaxVal = NULL;
if (DESCENDING == pCol->getClusteringKeyOrdering())
{
orderingMinVal = new (heap_) SystemLiteral(type, MAX, nullable);
orderingMaxVal = new (heap_) SystemLiteral(type, MIN, nullable);
}
else
{
orderingMinVal = new (heap_) SystemLiteral(type, MIN, nullable);
orderingMaxVal = new (heap_) SystemLiteral(type, MAX, nullable);
}
colsMinDefualtValList_.insert(orderingMinVal);
colsMaxDefualtValList_.insert(orderingMaxVal);
}
} // MvLog::buildClusteringIndexDefaultValues
//----------------------------------------------------------------------------
void MvLog::constructBoundriesList()
{
const NAFileSet * pCiFileSet = pNaTable_->getClusteringIndex();
getColumnsPositionsInCI(pCiFileSet); // kept in pRelevantCiPositionsList_
buildClusteringIndexDefaultValues(pCiFileSet);
CMPASSERT(pBeginRangeExprList_.entries() == pEndRangeExprList_.entries());
CollIndex numberOfCICols = colsMinDefualtValList_.entries();
CMPASSERT(numberOfCICols == colsMaxDefualtValList_.entries());
CollIndex suffixIndex = pBeginRangeExprList_.entries();
for ( ; suffixIndex < numberOfCICols ; suffixIndex++)
{
ItemExpr * minValue =
colsMinDefualtValList_[suffixIndex]->copyTree(heap_);
ItemExpr * maxValue =
colsMaxDefualtValList_[suffixIndex]->copyTree(heap_);
pBeginRangeExprList_.insert(minValue);
pEndRangeExprList_.insert(maxValue);
}
CMPASSERT(pBeginRangeExprList_.entries() ==
baseTableCiNamesList_.entries());
CMPASSERT(pEndRangeExprList_.entries() ==
baseTableCiNamesList_.entries());
ciBoundries_.insert(&pBeginRangeExprList_);
addClusteringIndexBoundries(ciBoundries_, pCiFileSet);
ciBoundries_.insert(&pEndRangeExprList_);
} // MvLog::constructBoundriesList
//----------------------------------------------------------------------------
void MvLog::getColumnsPositionsInCI(const NAFileSet * pCiFileSet)
{
const NAColumnArray& ciColumns = pCiFileSet->getIndexKeyColumns();
CMPASSERT(mvLogColNamesList_.entries() <= ciColumns.entries());
CollIndex inputColIndex(0);
for ( ; inputColIndex < mvLogColNamesList_.entries() ; inputColIndex++)
{
const NAString& ciColName = ciColumns[inputColIndex]->getColName();
const NAString& colRefName = *mvLogColNamesList_[inputColIndex];
if(ciColName == colRefName)
{
pRelevantCiPositionsList_.insert(inputColIndex);
}
else // not a CI prefix
{
// 12311 The column list is not a prefix of the clustering key columns of table $0~TableName.
*CmpCommon::diags() << DgSqlCode(-12311)
<< DgTableName(tableName_->getQualifiedNameAsString());
pBindWA_->setErrStatus();
return;
}
}
} // MvLog::getColumnsPositionsInCI
//----------------------------------------------------------------------------
// extract from the FileSet the CI boundries and from each boundry get only
// the columns indicated by the columnsCiPositionsList
void MvLog::addClusteringIndexBoundries(LIST(ItemExprList*) & ciBoundries,
const NAFileSet * pCiFileSet) const
{
PartitioningFunction *pPartFunction = pCiFileSet->getPartitioningFunction();
if (pPartFunction->isARangePartitioningFunction())
{
const RangePartitioningFunction *pRangePartFunction =
pPartFunction->castToRangePartitioningFunction();
CMPASSERT(pRangePartFunction!=NULL);
addRangePartitionBoundries(ciBoundries, pRangePartFunction);
}
} // MvLog::addClusteringIndexBoundries
//----------------------------------------------------------------------------
void MvLog::addRangePartitionBoundries(
LIST(ItemExprList*) & ciBoundries,
const RangePartitioningFunction *pRangePartFunction) const
{
const RangePartitionBoundaries* pBoundries =
pRangePartFunction->getRangePartitionBoundaries();
// the first boundry is for the primary partition and therefore NULL
CMPASSERT(NULL == pBoundries->getBoundaryValues(0));
CollIndex boundryIndex(1);
for ( ;
boundryIndex < (CollIndex)pBoundries->getCountOfPartitions() ;
boundryIndex++)
{
const ItemExprList* pBoundry =
pBoundries->getBoundaryValues(boundryIndex);
CMPASSERT(NULL != pBoundry);
ItemExprList *pBoundryCopy = new(heap_)ItemExprList(heap_);
CollIndex numberOfCICols = baseTableCiNamesList_.entries();
CMPASSERT(numberOfCICols == colsMinDefualtValList_.entries());
CollIndex listIndex(0);
for (; listIndex < numberOfCICols ; listIndex++)
{
ItemExpr * pValue = NULL;
ItemExpr * pValueCopy = NULL;
if (listIndex < pBoundry->entries())
pValue = (*pBoundry)[listIndex];
else
pValue = colsMinDefualtValList_[listIndex];
pValueCopy = pValue->copyTree(heap_);
if (listIndex < pBoundry->entries())
{
// we get the boundry ItemExpr with a valueId.
// we need to initialize it
pValueCopy->setValueId(ValueId());
pValueCopy->markAsUnBound();
}
pBoundryCopy->insert(pValueCopy);
}
ciBoundries.insert(pBoundryCopy);
}
} // MvLog::addRangePartitionBoundries
// ===========================================================================
// ===========================================================================
// ======== The TupleList Block
// ===========================================================================
// ===========================================================================
//----------------------------------------------------------------------------
// build a tuple list. on top build a rename and oon it build a root with
// selection predicates. The notation *_BEGIN means its the vector of
// clustering index column names, with the suffix _BEGIN.
//
// Root } BeginGreaterThanEndPhase : (*_BEGIN <= *_END)
// |
// Rename
// | } Phase 3 : (*_BEGIN = MAX(*_BR, *_BEGIN), *_END = MIN(*_ER,*_END))
// Root
// |
// Rename
// | } Phase 2 : (*_BR, *_BEGIN, *_ER, *_END, BR_IS_GREATER, ER_IS_LESS)
// Root
// |
// Rename } Phase 1 : (*_BEGIN, *_END)
// |
// TupleList
//
RelRoot *MvLog::buildTupleListBlock() const
{
TupleList * pTupleList = constructTupleListFromBoundries(ciBoundries_);
// *_BEGIN, *_END : the physical partition boundaries.
RenameTable * pPhase1Rename = constructTupleListPhase1(pTupleList);
// *_BR, *_BEGIN, *_ER, *_END, BR_IS_GREATER, ER_IS_LESS
// *_BR and *_ER are the logical range boundaries from the command line.
// BR_IS_GREATER and ER_IS_LESS are boolean scalars.
RenameTable * pPhase2Rename = constructTupleListPhase2(pPhase1Rename);
// *_BEGIN = MAX(*_BR, *_BEGIN), *_END = MIN(*_ER,*_END)
RenameTable * pPhase3Rename = constructTupleListPhase3(pPhase2Rename);
// selection predicate on *_BEGIN <= *_END
RelRoot * pRoot = constructTupleListBeginGreaterThanEndPhase(pPhase3Rename);
// we want the left side to know the right side
pRoot->setDontOpenNewScope();
return pRoot;
} // MvLog::buildTupleListBlock
//----------------------------------------------------------------------------
// build the tuple list node from the physical partition boundaries.
// a tuple is: CI values of partition begin, CI values of partition end.
TupleList *MvLog::constructTupleListFromBoundries(
const LIST(ItemExprList*) & ciBoundries) const
{
ItemExprList listOfTuplesRows(heap_);
for (CollIndex i(ciBoundries.entries() - 1) ; i > 0 ; i--)
{
CMPASSERT(NULL != ciBoundries[i] && NULL != ciBoundries[i - 1]);
const ItemExprList firstList = *(ciBoundries[i - 1]);
const ItemExprList secondList = *(ciBoundries[i]);
ItemExprList allList(heap_);
allList.insert(firstList);
allList.insert(secondList);
ItemExpr *pAllColsExpr =
BinderUtils::setItemExprFromList(allList, heap_, RIGHT_LINEAR_TREE);
ItemExpr * pCopy = pAllColsExpr->copyTree(heap_);
ItemExpr *tupleRow = new(heap_) Convert(pCopy);
listOfTuplesRows.insert(tupleRow);
}
ItemExpr * tupleExpr =
BinderUtils::setItemExprFromList(listOfTuplesRows,
heap_,
RIGHT_LINEAR_TREE);
return new(heap_) TupleList(tupleExpr);
} // MvLog::constructTupleListFromBoundries
//----------------------------------------------------------------------------
// give the tuple list columns names: *_BEGIN, *_END
RenameTable * MvLog::constructTupleListPhase1(TupleList *pTupleList) const
{
ItemExprList allCols(heap_);
appendToExprList(allCols, beginColNamesList_);
appendToExprList(allCols, endColNamesList_);
ItemExpr *pRenColsExpr =
BinderUtils::setItemExprFromList(allCols, heap_, RIGHT_LINEAR_TREE);
RenameTable *renameNode = new(heap_)
RenameTable(TRUE, pTupleList, tupleListPhase1CorrName_, pRenColsExpr , heap_);
return renameNode;
} // MvLog::constructTupleListPhase1
//----------------------------------------------------------------------------
// Build a Root node with the phase 2 select list, and over it a Rename node
// with the col names: (*_BR, *_BEGIN, *_ER, *_END, BR_IS_GREATER, ER_IS_LESS)
RenameTable * MvLog::constructTupleListPhase2(RelExpr *pPhase1) const
{
RelRoot *pP2Results = new(heap_) RelRoot(pPhase1);
ItemExpr * pP2SelectList = buildTupleListPhase2SelectResults();
pP2Results->addCompExprTree(pP2SelectList);
RenameTable * pTupleListP2Reanme = buildTupleListPhase2Rename(pP2Results);
return pTupleListP2Reanme;
} // MvLog::constructTupleListPhase2
//----------------------------------------------------------------------------
// Add two column vectors to the existing (*_BEGIN, *_END) row:
// *_BR is the vector of CI values for the logical range start.
// *_ER is the vector of CI values for the logical range end.
// These vectors were specified by the user in the MVLOG command line.
// Two additional boolean scalars are BR_IS_GREATER and ER_IS_LESS:
// BR_IS_GREATER = (BR > BEGIN) ? TRUE : FALSE.
// ER_IS_LESS = (ER > END) ? TRUE : FALSE.
// These are used by the min max evaluation in the next phase.
ItemExpr * MvLog::buildTupleListPhase2SelectResults() const
{
CMPASSERT(pBeginRangeExprList_.entries() > 0 );
ItemExpr * pBeginRang = // *_BR
pBeginRangeExprList_.convertToItemExpr()->copyTree(heap_);
ItemExpr * pEndRang = // *_ER
pEndRangeExprList_.convertToItemExpr()->copyTree(heap_);
ItemExpr * pBeginColumnsRef = // *_BEGIN
BinderUtils::getNamesListAsItemExpr(beginColNamesList_, heap_,
ITM_REFERENCE,
tupleListPhase1CorrName_);
ItemExpr * pEndColumnsRef = // *_END
BinderUtils::getNamesListAsItemExpr(endColNamesList_, heap_,
ITM_REFERENCE,
tupleListPhase1CorrName_);
ItemExpr * beginRangeGreaterThanBeginCols = // BR_IS_GREATER
compareItems(pBeginRang, pBeginColumnsRef, ITM_GREATER, pDirectionVector_);
ItemExpr * endRangeLessThanEndCols = // ER_IS_LESS
compareItems(pEndRang, pEndColumnsRef, ITM_LESS, pDirectionVector_);
ItemExprList columns(heap_);
columns.insert(pBeginRang);
columns.insert(pBeginColumnsRef);
columns.insert(beginRangeGreaterThanBeginCols);
columns.insert(pEndRang);
columns.insert(pEndColumnsRef);
columns.insert(endRangeLessThanEndCols);
return columns.convertToItemExpr();
} // MvLog::buildTupleListPhase2SelectResults
//----------------------------------------------------------------------------
// Build the phase 2 rename node with the column names:
// (*_BR, *_BEGIN, *_ER, *_END, BR_IS_GREATER, ER_IS_LESS)
RenameTable * MvLog::buildTupleListPhase2Rename(RelExpr * pTupleListP2) const
{
// Construct the column names list
ConstStringList columnNamesList(heap_);
columnNamesList.insert(brColNamesList_);
columnNamesList.insert(beginColNamesList_);
columnNamesList.insert(new(heap_) NAString(getNames().getBrIsGreater(), heap_));
columnNamesList.insert(erColNamesList_);
columnNamesList.insert(endColNamesList_);
columnNamesList.insert(new(heap_) NAString(getNames().getErIsLess(), heap_));
ItemExpr * pColNames =
BinderUtils::getNamesListAsItemExpr(columnNamesList,
heap_,
ITM_RENAME_COL,
emptyCorrName_);
// And the Rename node itself.
CorrName P2Name(tupleListPhase2CorrName_);
RenameTable * pPhase2Rename = new(heap_)
RenameTable(TRUE, pTupleListP2, P2Name, pColNames, heap_);
return pPhase2Rename;
} // MvLog::buildTupleListPhase2Rename
//----------------------------------------------------------------------------
// The result column are (*_BEGIN, *_END) where
// *_BEGIN = MAX(*_BR, *_BEGIN) - done using phase 2 BR_IS_GREATER column
// *_END = MIN(*_ER, *_END) - done using phase 2 ER_IS_LESS column
RenameTable * MvLog::constructTupleListPhase3(RelExpr * pPhase2) const
{
RelRoot *pP3Results = new(heap_)RelRoot(pPhase2);
ItemExpr * pP3SelectList = buildTupleListPhase3SelectResults();
pP3Results->addCompExprTree(pP3SelectList);
RenameTable * pTupleListP3Rename = buildTupleListPhase3Rename(pP3Results);
return pTupleListP3Rename;
} // MvLog::constructTupleListPhase3
//----------------------------------------------------------------------------
// Construct the MIN and MAX expressions for the CI column vectors, using the
// condition that is alreay bound by now.
// For each column col_BEGIN in in *_BEGIN, its new value will be:
// If (BR_IS_GREATER) THEN col_BR ELSE col_BEGIN.
// For each column col_END in in *_END, its new value will be:
// If (ER_IS_LESS) THEN col_ER ELSE col_END.
// The fact that the conditions were already bound, and that the same ValueId
// is used for all the columns in the CI, means that in execution time, this
// condition will be evaluated once, and the result used for the expressions
// of all the columns that use it.
ItemExpr * MvLog::buildTupleListPhase3SelectResults() const
{
ItemExprList columnExpresionList(heap_);
NAString *pMaxDecisionFactor =
new(heap_) NAString(getNames().getBrIsGreater(), heap_);
ItemExpr *pMaxColsListExpr =
buildConditionalColumnList( brColNamesList_,
beginColNamesList_,
*pMaxDecisionFactor,
tupleListPhase2CorrName_);
columnExpresionList.insert(pMaxColsListExpr);
NAString *pMinDecisionFactor = new(heap_) NAString(getNames().getErIsLess(), heap_);
ItemExpr *pMinColsListExpr =
buildConditionalColumnList( erColNamesList_,
endColNamesList_,
*pMinDecisionFactor,
tupleListPhase2CorrName_);
columnExpresionList.insert(pMinColsListExpr);
return columnExpresionList.convertToItemExpr();
} // MvLog::buildTupleListPhase3SelectResults
//----------------------------------------------------------------------------
// Build the phase 3 rename node with the column names: (*_BEGIN, *_END)
RenameTable * MvLog::buildTupleListPhase3Rename(RelExpr * pPhase3) const
{
ConstStringList columnNamesList(heap_);
columnNamesList.insert(beginColNamesList_);
columnNamesList.insert(endColNamesList_);
ItemExpr * pColNames =
BinderUtils::getNamesListAsItemExpr(columnNamesList,
heap_,
ITM_RENAME_COL,
emptyCorrName_);
CorrName P3Name(tupleListPhase3CorrName_);
RenameTable * pPhase3Rename = new(heap_)
RenameTable(TRUE, pPhase3, P3Name, pColNames, heap_);
return pPhase3Rename;
} // MvLog::buildTupleListPhase3Rename
//----------------------------------------------------------------------------
// Construct a Root node with the selection predicate: (BEGIN <= END)
RelRoot *
MvLog::constructTupleListBeginGreaterThanEndPhase(RelExpr * pPhase) const
{
ItemExpr * beginExpr =
BinderUtils::getNamesListAsItemExpr(beginColNamesList_,
heap_,
ITM_REFERENCE,
emptyCorrName_);
ItemExpr * endExpr =
BinderUtils::getNamesListAsItemExpr(endColNamesList_,
heap_,
ITM_REFERENCE,
emptyCorrName_);
BiRelat * selectPredicate = new (heap_)
BiRelat(ITM_LESS_EQ, beginExpr, endExpr);
selectPredicate->setDirectionVector(pDirectionVector_);
RelRoot *pResults = new (heap_) RelRoot(pPhase);
pResults->addSelPredTree(selectPredicate);
return pResults;
} // MvLog::constructTupleListBeginGreaterThanEndPhase
//----------------------------------------------------------------------------
// compare two items and return a BoolVal
ItemExpr * MvLog::compareItems( const ItemExpr * pFirst,
const ItemExpr * pSecond,
OperatorTypeEnum compareType,
IntegerList * pDirectionVector) const
{
BiRelat * pCompare = new (heap_)
BiRelat(compareType,
((ItemExpr*)pFirst)->copyTree(heap_),
((ItemExpr*)pSecond)->copyTree(heap_));
if(NULL != pDirectionVector)
{
pCompare->setDirectionVector(pDirectionVector);
}
IfThenElse * pIfElse = new(heap_)
IfThenElse(pCompare,
new (heap_) BoolVal(ITM_RETURN_TRUE),
new (heap_) BoolVal(ITM_RETURN_FALSE));
return new(heap_) Case(NULL, pIfElse);
} // MvLog::compareItems
//----------------------------------------------------------------------------
ItemExpr *
MvLog::buildConditionalColumnList(const ConstStringList& option1names,
const ConstStringList& option2names,
const NAString& conditionColName,
const CorrName& corrName) const
{
CMPASSERT(option1names.entries() == option2names.entries());
ItemExprList allConditionCols(heap_);
ColRefName *pCondColRefName = new(heap_)
ColRefName(conditionColName, corrName, heap_);
ColReference * pCondColRef = new(heap_)
ColReference(pCondColRefName);
for (CollIndex i(0) ; i < option1names.entries() ; i++)
{
ConstStringList option1Name(heap_);
ConstStringList option2Name(heap_);
option1Name.insert(option1names[i]);
option2Name.insert(option2names[i]);
ItemExpr * pOp1Expr =
BinderUtils::getNamesListAsItemExpr(option1Name,
heap_,
ITM_REFERENCE,
corrName);
ItemExpr * pOp2Expr =
BinderUtils::getNamesListAsItemExpr(option2Name,
heap_,
ITM_REFERENCE,
corrName);
Case * pCase = new(heap_)
Case(NULL, new(heap_) IfThenElse(pCondColRef, pOp1Expr, pOp2Expr));
allConditionCols.insert(pCase);
}
return allConditionCols.convertToItemExpr();
} // MvLog::buildConditionalColumnList
// ===========================================================================
// ===========================================================================
// ========= The Get First Block
// ===========================================================================
// ===========================================================================
//----------------------------------------------------------------------------
// Build the GetFirst Block:
// Root (GetFirst 1 rows)
// |
// Rename (FIRST1_SCAN)
// |
// Root (select list: just CI cols)
// |
// Scan base table.
RelExpr * MvLog::buildGetFirstNode() const
{
CorrName tableName(*tableName_, heap_);
Scan * pScanTable = new(heap_) Scan(tableName);
pScanTable->setForceInverseOrder();
if (pNaTable_->getKeyCount() > 1)
pScanTable->getInliningInfo().setFlags(II_MdamForcedByInlining);
// Build a select list for just the CI columns
ItemExpr * pCiColsSelectList =
BinderUtils::getNamesListAsItemExpr(baseTableCiNamesList_,
heap_,
ITM_REFERENCE,
emptyCorrName_);
RelRoot * pRoot = new(heap_)
RelRoot(pScanTable, REL_ROOT, pCiColsSelectList);
// SEL PREDICATE
ItemExpr * pSelectionPred = buildGetFirstSelctionPredicate();
pRoot->addSelPredTree(pSelectionPred);
// BUILD RENAME
ItemExprList renamedCols(heap_);
ItemExpr * pRenamedCols =
BinderUtils::getNamesListAsItemExpr(first1ColNamesList_,
heap_,
ITM_RENAME_COL,
emptyCorrName_);
RenameTable * pRenameTable = new(heap_)
RenameTable(TRUE, pRoot, first1NodeCorrName_, pRenamedCols, heap_);
// we need this root so a new scope will be opened (if not we will run over
// the Tuple List RetDesc
RelRoot * pGetFirstRoot = new(heap_) RelRoot(pRenameTable);
// We only need the first (actually last) row from each partition.
pGetFirstRoot->needFirstSortedRows() = TRUE;
pGetFirstRoot->setFirstNRows(1);
pGetFirstRoot->getInliningInfo().setFlags(II_EnableFirstNRows);
return pGetFirstRoot;
} // MvLog::buildGetFirstNode
//----------------------------------------------------------------------------
// Our data comes from two sources.
// 1. the Tuple List: *_BEGIN and *_END columns (tBegin, tEnd)
// 2. Clustering Index Column results from the scaned table ( cicScan )
//
// the selectionPred is:
// cicScan >= tBegin AND cicScan < tEnd
ItemExpr * MvLog::buildGetFirstSelctionPredicate() const
{
const CorrName mainTupleCorrName(tupleListCorrName_);
// Get ALL Sources involved
ItemExpr * cicScan =
BinderUtils::getNamesListAsItemExpr(baseTableCiNamesList_,
heap_, ITM_REFERENCE,
emptyCorrName_);
ItemExpr * tBegin =
BinderUtils::getNamesListAsItemExpr(beginColNamesList_,
heap_, ITM_REFERENCE,
tupleListCorrName_);
ItemExpr * tEnd =
BinderUtils::getNamesListAsItemExpr(endColNamesList_,
heap_, ITM_REFERENCE,
tupleListCorrName_);
// BEGIN <= CIC < END
Between * pBetween = new (heap_)
Between(cicScan->copyTree(heap_),
tBegin->copyTree(heap_),
tEnd->copyTree(heap_),
TRUE, // scan >= begin
FALSE); // scan < end
pBetween->setDirectionVector(*pDirectionVector_, heap_);
return pBetween;
} // MvLog::buildGetFirstSelctionPredicate
//----------------------------------------------------------------------------
ItemExpr * MvLog::buildColumnNotEqualBoundriesExpr(ItemExpr * pColumn) const
{
// this is returned incase there are no boundries
ItemExpr * result = new (heap_) BoolVal(ITM_RETURN_TRUE);
// the first and the last boundries are the BEGIN RANGE and END RANGE
// we don't need them
for (CollIndex i(1) ; i < ciBoundries_.entries() - 2 ; i++)
{
CMPASSERT(NULL != ciBoundries_[i] );
ItemExprList * pBoundry = ciBoundries_[i];
CMPASSERT(NULL != pBoundry);
ItemExpr * boundryItem = pBoundry->convertToItemExpr();
BiRelat * colNEQboundry = new (heap_)
BiRelat(ITM_NOT_EQUAL,
pColumn->copyTree(heap_),
boundryItem->copyTree(heap_));
result = new (heap_) BiLogic(ITM_AND, result, colNEQboundry);
}
return result;
} // MvLog::buildColumnNotEqualBoundriesExpr
//----------------------------------------------------------------------------
// the tsj outputs tuples of the form BEGIN, END, FIRST
RelExpr *
MvLog::buildJoinWithGetFirstBlock(RelExpr * topNode) const
{
RelExpr * getFirst1Node = buildGetFirstNode();
Join *pTsj = new(heap_) Join(topNode, getFirst1Node, REL_TSJ);
pTsj->setTSJForWrite(TRUE);
RelRoot *rootNode = new(heap_) RelRoot(pTsj);
ItemExprList tsjResult(heap_);
appendToExprList(tsjResult, beginColNamesList_, ITM_REFERENCE);
appendToExprList(tsjResult, first1ColNamesList_, ITM_REFERENCE);
ItemExpr * tsjSelectList = tsjResult.convertToItemExpr();
rootNode->addCompExprTree(tsjSelectList);
return rootNode;
} // MvLog::buildJoinWithGetFirstBlock
// ===========================================================================
// ===========================================================================
// ======= End Range Block
// ===========================================================================
// ===========================================================================
//----------------------------------------------------------------------------
// Builds a TupleList with a row with the range from EndRange to EndRange.
// EndRange is the end range values given by the user on the command line.
// The result is a range that covers one base table row, if it exists, and
// an empty range otherwise. This row will not be further processed, and will
// be unioned with the processed results of the main TupleList, and entered
// into the IUD log.
RelExpr * MvLog::buildUnionWithEndRangeTupleList(RelExpr * topNode) const
{
LIST(ItemExprList*) endRangeBoundry(heap_);
constructEndRangeBoundries(endRangeBoundry, FALSE);
CMPASSERT(2 == endRangeBoundry.entries());
TupleList * pSingleTupleList =
constructTupleListFromBoundries(endRangeBoundry);
RelRoot * pRoot = new (heap_) RelRoot(pSingleTupleList);
topNode = new (heap_) Union(topNode, pRoot, NULL, NULL, REL_UNION,
CmpCommon::statementHeap(),TRUE);
RelRoot *rootNode = new (heap_) RelRoot(topNode);
rootNode->setDontOpenNewScope();
return topNode;
} // MvLog::buildUnionOverTsj
//----------------------------------------------------------------------------
// The boundry is from Start or End Range to End Range,
// each boundry is flipped over.
// Notice that we do not copy the End Range ItemExpr nodes since they can be
// HostVars and do not need to be replicated
// Normally, needStartRange is FALSE to create a single row range on End Range.
// For hash partitioned tables, the boundary is from Start to End Range.
void
MvLog::constructEndRangeBoundries(LIST(ItemExprList*)& endRangeBoundry,
NABoolean needStartRange) const
{
// Create the two mirrored lists
ItemExprList * pEndRangeList = new (heap_) ItemExprList(heap_);
ItemExprList * pBeginRangeList = NULL;
if (needStartRange)
pBeginRangeList = new (heap_) ItemExprList(heap_);
CollIndex numOfEntries = pEndRangeExprList_.entries();
for (CollIndex i(0) ; i < numOfEntries ; i++ )
{
pEndRangeList->insert(pEndRangeExprList_[(numOfEntries - 1) - i]);
if (needStartRange)
pBeginRangeList->insert(pBeginRangeExprList_[(numOfEntries - 1) - i]);
}
// Insert the boundaries in mirrored order.
endRangeBoundry.insert(pEndRangeList);
if (needStartRange)
endRangeBoundry.insert(pBeginRangeList);
else
endRangeBoundry.insert(pEndRangeList);
} // MvLog::constructEndRangeBoundries
// ===========================================================================
// ===========================================================================
// ======= Insert Into IUD Log Block.
// ===========================================================================
// ===========================================================================
//----------------------------------------------------------------------------
RelExpr *MvLog::buildInsertIntoLog(RelExpr *topNode) const
{
CorrName tableName(*tableName_);
MvIudLogForMvLog logTableObj(tableName, pBindWA_);
if (pBindWA_->errStatus())
return NULL;
RelExpr *insertNode = logTableObj.buildInsert(FALSE,
ChangesTable::ALL_ROWS,
FALSE, TRUE);
RelRoot *rootNode = new(heap_) RelRoot(insertNode);
Join *tsjNode = new(heap_) Join(topNode, rootNode, REL_TSJ);
tsjNode->setTSJForWrite(TRUE);
rootNode = new(heap_) RelRoot(tsjNode);
return rootNode;
} // MvLog::buildInsertIntoLog()
// ===========================================================================
// ===========================================================================
// ======= Read Current Epoch Block
// ===========================================================================
// ===========================================================================
//----------------------------------------------------------------------------
// Build a Scan node over the base table, and read from it only the
// CurrentEpoch. This is done with this tree:
// TSJ
// / \
// / Rest of tree
// /
// Rename (READ_EPOCH)
// |
// Root (select MIN(CurrentEpoch())
// |
// GroupBy (scalar - no GroupBy expression)
// |
// Root (Get First 1 rows)
// |
// Scan
//
// If the base table is empty, an error 12315 will be generated.
// The current epoch number of the base table is used in every
// row that MVLOG inserts into the IUD log. Since we are not updating
// the base table here (as in a logging operation) we use a Scan node
// to get the current epoch number of the table. Because we don't really
// need to read any rows from the base table, we just read the first row
// which will be projected with the Current Epoch column. The Aggregation
// insures that the result is a scalar and not table valued.
RelExpr *MvLog::buildScanEpochFromTable(RelExpr *topNode) const
{
CorrName tableName(*tableName_, heap_);
Scan *scanNode = new(heap_) Scan(tableName);
scanNode->getInliningInfo().setFlags(II_isMVLoggingInlined);
RelRoot *rootNode = new(heap_) RelRoot(scanNode);
rootNode->needFirstSortedRows() = TRUE;
rootNode->setFirstNRows(1);
rootNode->getInliningInfo().setFlags(II_EnableFirstNRows);
GroupByAgg *groupByNode = new(heap_)
GroupByAgg(rootNode);
ItemExpr *selectList = buildEpochSelectList();
RelRoot *rootNode2 = new(heap_)
RelRoot(groupByNode, REL_ROOT, selectList);
ItemExpr *renameList = new(heap_)
RenameCol(NULL, new(heap_)
ColRefName(InliningInfo::getEpochVirtualColName()));
RenameTable *renameNode = new(heap_)
RenameTable(rootNode2, "READ_EPOCH", renameList);
Join *tsjNode = new(heap_) Join(renameNode, topNode, REL_TSJ);
tsjNode->setTSJForWrite(TRUE);
return tsjNode;
} // MvLog::buildScanEpochFromTable()
//----------------------------------------------------------------------------
// The value we project here is Epoch = MIN(CurrentEpoch). This returns NULL
// when the table is empty, so we wrap it with this expression:
// Case( If (minEpoch IS NULL AND RaiseError(12315)
// Then minEpoch
// Else minEpoch)
// The value returned is always the Epoch value, but if it is NULL, the error
// will cancel execution. This trick is necessary because the type of
// RaiseError is Boolean, so putting it as the returned value in the
// Then or Else clauses, will not type-check.
ItemExpr *MvLog::buildEpochSelectList() const
{
// minEpoch = MIN("@CURRENT_EPOCH")
ItemExpr *minEpoch = new(heap_)
Aggregate(ITM_MIN, new(heap_) ColReference(new(heap_)
ColRefName(InliningInfo::getEpochVirtualColName())));
// The condition is (minEpoch IS NULL AND RaiseError)
ItemExpr *condition = new(heap_)
BiLogic(ITM_AND,
new(heap_) UnLogic(ITM_IS_NULL, minEpoch),
new(heap_) RaiseError(12315));
ItemExpr *selectList = new(heap_)
Case(NULL, new(heap_)
IfThenElse(condition, minEpoch, minEpoch));
return selectList;
}
//----------------------------------------------------------------------------
// This method is used when there is a single partition, or hash partitioning.
// Builds a TupleList with a single row with the begin and end range values
// given by the user on the command line.
RelExpr *MvLog::buildSimpleRangeTuple()
{
constructBoundriesList();
LIST(ItemExprList*) endRangeBoundry(heap_);
constructEndRangeBoundries(endRangeBoundry, TRUE);
CMPASSERT(2 == endRangeBoundry.entries());
TupleList * pSingleTupleList =
constructTupleListFromBoundries(endRangeBoundry);
RelRoot * pRoot = new (heap_) RelRoot(pSingleTupleList);
ItemExprList renameColList(heap_);
appendToExprList(renameColList, beginColNamesList_);
appendToExprList(renameColList, first1ColNamesList_);
ItemExpr *renameItemList =
BinderUtils::setItemExprFromList(renameColList, heap_, LEFT_LINEAR_TREE);
RenameTable *renameNode = new(heap_)
RenameTable(pRoot, getNames().getSimpleRangeTuple(), renameItemList);
return renameNode;
}
//----------------------------------------------------------------------------
// The final select list includes both *_BEGIN and *_FIRST columns. Both lists
// include all the columns of the base table - the non-CI columns are added
// here as constants of the minimum value of their type.
RelExpr *MvLog::buildFinalSelectList(RelExpr *topNode)
{
ItemExprList selectList(heap_);
ItemExprList renameList(heap_);
// Build the Root select list
appendToExprList(selectList, beginColNamesList_, ITM_REFERENCE);
for (CollIndex i=0; i<nonCiColsExprList_.entries(); i++)
selectList.insert(nonCiColsExprList_[i]->copyTree(heap_));
appendToExprList(selectList, first1ColNamesList_, ITM_REFERENCE);
for (CollIndex j=0; j<nonCiColsExprList_.entries(); j++)
selectList.insert(nonCiColsExprList_[j]->copyTree(heap_));
ItemExpr *selectListExpr =
BinderUtils::setItemExprFromList(selectList, heap_, LEFT_LINEAR_TREE);
// Build the Root
RelRoot *rootNode = new(heap_)
RelRoot(topNode, REL_ROOT, selectListExpr);
// Create name lists for the non-CI coluns with _BEGIN and _FIRST suffixs
ConstStringList nonCiBeginNames(heap_);
ConstStringList nonCiFirstNames(heap_);
for (CollIndex k=0; k < nonCiColsNamesList_.entries() ; k++)
{
const NAString& colName = *nonCiColsNamesList_[k];
NAString *pBeginName = new(heap_) NAString(colName, heap_);
pBeginName->append(getNames().getBeginRangeSuffix()); // "_BEGIN"
nonCiBeginNames.insert(pBeginName);
NAString *pFirstName = new(heap_) NAString(colName, heap_);
pFirstName->append(getNames().getEndRangeSuffix()); // "_FIRST"
nonCiFirstNames.insert(pFirstName);
}
// Build the Rename list
appendToExprList(renameList, beginColNamesList_);
appendToExprList(renameList, nonCiBeginNames);
appendToExprList(renameList, first1ColNamesList_);
appendToExprList(renameList, nonCiFirstNames);
ItemExpr *renameListExpr =
BinderUtils::setItemExprFromList(renameList, heap_, LEFT_LINEAR_TREE);
// Build the Rename on top of the Root.
RenameTable *renameNode = new(heap_)
RenameTable(rootNode, getNames().getFinalResultName(), renameListExpr , heap_);
return renameNode;
}
//----------------------------------------------------------------------------
// Shortcut to BinderUtils::appendToExprList();
// itemType has a default value of ITM_RENAME_COL.
void MvLog::appendToExprList(ItemExprList& toAddto,
const ConstStringList& columnNames,
OperatorTypeEnum itemType) const
{
BinderUtils::appendToExprList(toAddto,
columnNames,
heap_,
itemType,
emptyCorrName_);
}