| /********************************************************************** |
| // @@@ 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: Refresh.cpp |
| * Description: Binding methods of class Refresh for MV INTERNAL REFRESH command. |
| * |
| * Created: 12/21/99 |
| * Language: C++ |
| * Status: $State: Exp $ |
| * |
| * |
| ****************************************************************************** |
| */ |
| |
| #define SQLPARSERGLOBALS_FLAGS // must precede all #include's |
| #define SQLPARSERGLOBALS_CONTEXT_AND_DIAGS |
| |
| |
| #include "Sqlcomp.h" |
| #include "AllItemExpr.h" |
| #include "AllRelExpr.h" |
| #include "BindWA.h" |
| #include "GroupAttr.h" |
| #include "parser.h" |
| #include "StmtNode.h" |
| #include "Inlining.h" |
| #include "Triggers.h" |
| #include "MVInfo.h" |
| #include "MVJoinGraph.h" |
| #include "Refresh.h" |
| #include "MvRefreshBuilder.h" |
| #include "MjvBuilder.h" |
| #include "RelSequence.h" |
| #include "BinderUtils.h" |
| #include "ChangesTable.h" |
| #include <CmpMain.h> |
| |
| #ifdef NA_DEBUG_GUI |
| #include "ComSqlcmpdbg.h" |
| #endif |
| |
| #include "SqlParserGlobals.h" // must be last #include |
| |
| |
| // ----------------------------------------------------------------------- |
| // ----------------------------------------------------------------------- |
| // ---------------- member functions for class Refresh ---------------- |
| // ----------------------------------------------------------------------- |
| // ----------------------------------------------------------------------- |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Ctor for RECOMPUTE |
| Refresh::Refresh(const QualifiedName& mvName, |
| ComBoolean noDelete, |
| CollHeap *oHeap) |
| : BinderOnlyNode(REL_REFRESH, oHeap), |
| refreshType_(RECOMPUTE), |
| mvName_(mvName, oHeap), |
| deltaDefList_(NULL), |
| phase_(0), |
| pNRowsClause_(NULL), |
| pipelineClause_(NULL), |
| noDeleteOnRecompute_(noDelete), |
| bindContext_(NULL), |
| additionalPhaseNeeded_(FALSE) |
| {} |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Ctor for SINGLEDELTA |
| Refresh::Refresh(const QualifiedName& mvName, |
| const DeltaDefinitionPtrList *pDeltaDefList, |
| NRowsClause *pOptionalNRowsClause, |
| PipelineClause *pOptionalPipelineClause, |
| CollHeap *oHeap) |
| : BinderOnlyNode(REL_REFRESH, oHeap), |
| refreshType_(SINGLEDELTA), |
| mvName_(mvName, oHeap), |
| deltaDefList_(pDeltaDefList), |
| phase_(0), |
| pNRowsClause_(pOptionalNRowsClause), |
| pipelineClause_(pOptionalPipelineClause), |
| noDeleteOnRecompute_(FALSE), |
| bindContext_(NULL), |
| additionalPhaseNeeded_(FALSE) |
| {} |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Ctor for MULTIDELTA |
| Refresh::Refresh(const QualifiedName& mvName, |
| const DeltaDefinitionPtrList *pDeltaDefList, |
| Lng32 phaseVal, |
| PipelineClause *pOptionalPipelineClause, |
| CollHeap *oHeap) |
| : BinderOnlyNode(REL_REFRESH, oHeap), |
| refreshType_(MULTIDELTA), |
| mvName_(mvName, oHeap), |
| deltaDefList_(pDeltaDefList), |
| phase_(phaseVal), |
| pNRowsClause_(NULL), |
| pipelineClause_(pOptionalPipelineClause), |
| noDeleteOnRecompute_(FALSE), |
| bindContext_(NULL), |
| additionalPhaseNeeded_(FALSE) |
| {} |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////// |
| Refresh::~Refresh() |
| { |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // This is where all the action begins. |
| // The simple RelRoot-Refresh tree constructed by the parser, is transformed |
| // to a complex RelExpr tree that implements the MV refresh algorithm. |
| // The method: build a MvRefreshBuilder object, have it build the MV refresh |
| // tree, and then bind the resulting tree. The bind may cause additional |
| // levels of pipelined refresh to be constructed and bound as well. |
| ////////////////////////////////////////////////////////////////////////////// |
| RelExpr *Refresh::bindNode(BindWA *bindWA) |
| { |
| CollHeap *heap = bindWA->wHeap(); |
| // for DEFAULT_SCHEMA_ACCESS_ONLY, there is no need to check here |
| // because this fucntion is only called internally |
| mvName_.applyDefaults(bindWA->getDefaultSchema()); |
| if (deltaDefList_ != NULL) |
| deltaDefList_->applyDefaultSchemaToAll(bindWA); |
| if (pipelineClause_ != NULL) |
| pipelineClause_->applyDefaultSchemaToAll(bindWA); |
| |
| CorrName mvCorrName(mvName_); |
| MVInfoForDML *mvInfo = getMvInfo(bindWA, mvCorrName); |
| if (mvInfo == NULL) |
| return NULL; |
| |
| // User maintained MVs can only be recomputed (for initialization). |
| if (mvInfo->getRefreshType() == COM_BY_USER && |
| refreshType_ != RECOMPUTE ) |
| return NULL; |
| |
| if (verifyDeltaDefinition(bindWA, mvInfo)) |
| return NULL; |
| |
| // From now on, the binder needs to know we are binding a refresh statement. |
| bindWA->setBindingMvRefresh(); |
| bindWA->setPropagateOpAndSyskeyColumns(); |
| |
| RelExpr *refreshTree = buildCompleteRefreshTree(bindWA, mvInfo); |
| if (bindWA->errStatus() || refreshTree==NULL) |
| return this; |
| |
| // Bind the refresh tree. |
| refreshTree = refreshTree->bindNode(bindWA); |
| if (bindWA->errStatus()) |
| return this; |
| |
| // If this is a multi-delta refresh, and we are not done yet - we need |
| // to be called again with another phase. |
| // Signal to the refresh utility using warning. |
| if (getAdditionalPhaseNeeded()) |
| { |
| // 12304 An additional phase is needed to complete the refresh. |
| *CmpCommon::diags() << DgSqlCode(12304); |
| } |
| |
| // Delete all data members before saying goodbye. |
| cleanupBeforeSelfDestruct(); |
| |
| // Return the bound refresh tree to the root. The Refresh node itself has |
| // done its job, and is not part of the tree anymore. |
| return refreshTree; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Since the destructor is never really called, at least clean up |
| // internal data before disappearing. Should be called before returning |
| // from bindNode(). |
| ////////////////////////////////////////////////////////////////////////////// |
| void Refresh::cleanupBeforeSelfDestruct() |
| { |
| delete deltaDefList_; |
| delete pipelineClause_; |
| delete pNRowsClause_; |
| delete bindContext_; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////// |
| RelExpr *Refresh::buildCompleteRefreshTree(BindWA *bindWA, MVInfoForDML *mvInfo) |
| { |
| CollHeap *heap = bindWA->wHeap(); |
| |
| MvRefreshBuilder *firstBuilder = constructRefreshBuilder(bindWA, mvInfo); |
| if (bindWA->errStatus()) |
| return NULL; |
| |
| RelExpr *topTree = firstBuilder->buildRefreshTree(); |
| |
| // For recompute the refresh tree does not scan the log. |
| // For MJV, it sometimes does, and when it does, it handles it itself. |
| // So, for these types, the refresh tree is ready. |
| if (refreshType_ == RECOMPUTE || |
| mvInfo->getMVType() == COM_MJV) |
| { |
| delete firstBuilder; |
| return topTree; |
| } |
| |
| MvBindContext *bindContext = new(heap) MvBindContext(heap); |
| bindContext->setRefreshBuilder(firstBuilder); |
| |
| // In multi-delta, this is done inside the refresh tree. |
| if (refreshType_ == SINGLEDELTA) |
| { |
| CMPASSERT(deltaDefList_->entries() == 1); |
| DeltaDefinition *deltaDef = deltaDefList_->at(0); |
| const QualifiedName *baseTableName = deltaDef->getTableName(); |
| |
| RelExpr *scanLogBlock = |
| firstBuilder->buildLogsScanningBlock(*baseTableName); |
| if (bindWA->errStatus() || scanLogBlock == NULL) |
| return NULL; |
| |
| bindContext->setReplacementFor(baseTableName, scanLogBlock); |
| } |
| |
| if (pipelineClause_ != NULL) |
| topTree = constructPipelinedBuilders(bindWA, bindContext, topTree); |
| if (topTree == NULL) |
| return NULL; |
| |
| // Make sure no data is projected out. |
| RelRoot *rootNode = new(heap) RelRoot(topTree); |
| #ifndef NDEBUG |
| if (!getenv("DEBUG_DCB")) |
| #endif |
| rootNode->setEmptySelectList(); |
| |
| rootNode->setMvBindContext(bindContext); |
| RelExpr *topNode = rootNode; |
| |
| // Save the bind context for cleanup. |
| bindContext_ = bindContext; |
| |
| return topNode; |
| } // Refresh::buildCompleteRefreshTree() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Determine the correct MvRefreshBuilder sub-class to handle the refresh job, |
| // construct, and return it. |
| // This method is called from two places in the code: |
| // 1) From Refresh::bindNode() with isPipelined = FALSE, for a non-pipelined |
| // refresh job. |
| // 2) From PipelinedMavBuilder::buildRefreshTree() with isPipelined = TRUE, |
| // for the lowest level of a pipelined refresh job. |
| ////////////////////////////////////////////////////////////////////////////// |
| MvRefreshBuilder *Refresh::constructRefreshBuilder(BindWA *bindWA, |
| MVInfoForDML *mvInfo) |
| { |
| CollHeap *heap = bindWA->wHeap(); |
| |
| CorrName mvCorrName(mvName_); |
| mvCorrName.setSpecialType(ExtendedQualName::MV_TABLE); |
| |
| MvRefreshBuilder *treeBuilder = NULL; |
| switch (refreshType_) |
| { |
| case RECOMPUTE: |
| { |
| treeBuilder = new(heap) |
| MvRecomputeBuilder(mvCorrName, mvInfo, noDeleteOnRecompute_, bindWA); |
| break; |
| } |
| |
| case SINGLEDELTA: |
| // Verify no Multidelta in SINGLEDELTA definition. |
| CMPASSERT(deltaDefList_->entries() == 1); |
| // Fall through to multidelta. |
| |
| case MULTIDELTA: |
| CMPASSERT(mvInfo->getRefreshType() == COM_ON_REQUEST); |
| |
| switch (mvInfo->getMVType()) |
| { |
| case COM_MAV: |
| case COM_MAJV: |
| { |
| treeBuilder = |
| constructMavSpecifichBuilder(bindWA, mvCorrName, mvInfo); |
| break; |
| } |
| |
| case COM_MJV: |
| { |
| treeBuilder = |
| constructMjvSpecifichBuilder(bindWA, mvCorrName, mvInfo); |
| break; |
| } |
| |
| default: |
| CMPASSERT(FALSE); |
| } |
| break; |
| |
| default: |
| // Unknown refresh type |
| CMPASSERT(FALSE); |
| } |
| |
| CMPASSERT(treeBuilder!=NULL); |
| return treeBuilder; |
| } // Refresh::constructRefreshBuilder() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Construct a refresh builder for a MAV. |
| ////////////////////////////////////////////////////////////////////////////// |
| MvRefreshBuilder *Refresh::constructMavSpecifichBuilder(BindWA *bindWA, |
| CorrName mvCorrName, |
| MVInfoForDML *mvInfo) |
| { |
| CollHeap *heap = bindWA->wHeap(); |
| MvRefreshBuilder *treeBuilder = NULL; |
| |
| // Is this MAV refresh pipelning its results to another refresh? |
| NABoolean isProjectingMvDelta = (pipelineClause_ != NULL); |
| |
| if (deltaDefList_->entries() > 1) |
| { |
| // We have more than one delta, we need a multi-delta builder. |
| treeBuilder = new(heap) |
| MultiDeltaMavBuilder(mvCorrName, mvInfo, this, isProjectingMvDelta, bindWA); |
| } |
| else if (getNRowsClause() != NULL) |
| { |
| // We have a "COMMIT EACH" clause, need the multi-txn builder. |
| if (deltaDefList_->at(0)->useIudLog() && deltaDefList_->at(0)->useRangeLog() |
| #ifndef NDEBUG // ?????? TEMP |
| && !getenv( "REFRESH_WITH_DE" ) |
| #endif |
| ) |
| { |
| treeBuilder = new(heap) |
| MultiTxnDEMavBuilder(mvCorrName, mvInfo, this, isProjectingMvDelta, bindWA); |
| #ifndef NDEBUG |
| cout << endl << " Executing Internal refresh with Duplicate elimination " << endl; |
| #endif |
| } |
| else |
| { |
| treeBuilder = new(heap) |
| MvMultiTxnMavBuilder(mvCorrName, mvInfo, this, isProjectingMvDelta, bindWA); |
| } |
| } |
| else if (canUseMinMaxBuilder(bindWA, mvInfo)) |
| { |
| // We have a Min/Max MAV with a supporting index. |
| treeBuilder = new(heap) |
| MinMaxOptimizedMavBuilder(mvCorrName, mvInfo, this, isProjectingMvDelta, bindWA); |
| } |
| else |
| { |
| // Just a simple MAV. |
| treeBuilder = new(heap) |
| MavBuilder(mvCorrName, mvInfo, this, isProjectingMvDelta, bindWA); |
| } |
| |
| return treeBuilder; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Construct a refresh builder for an MJV. |
| ////////////////////////////////////////////////////////////////////////////// |
| MvRefreshBuilder *Refresh::constructMjvSpecifichBuilder(BindWA *bindWA, |
| CorrName mvCorrName, |
| MVInfoForDML *mvInfo) |
| { |
| CollHeap *heap = bindWA->wHeap(); |
| MvRefreshBuilder *treeBuilder = NULL; |
| |
| if (deltaDefList_->entries() > 1) |
| { |
| // We have more than one delta, we need a multi-delta builder. |
| treeBuilder = new(heap) |
| MjvOnRequestMultiDeltaBuilder(mvCorrName, mvInfo, this, bindWA); |
| } |
| // else if (getNRowsClause() != NULL) |
| // { |
| // // We have a "COMMIT EACH" clause, need the multi-txn builder. |
| // treeBuilder = new(heap) |
| // MvMultiTxnMjvBuilder(mvCorrName, mvInfo, this, bindWA); |
| // } |
| else |
| { |
| // Just a simple MJV. |
| treeBuilder = new(heap) |
| MjvOnRequestBuilder(mvCorrName, mvInfo, this, bindWA); |
| } |
| |
| return treeBuilder; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Construct builders for all pipelined mvs, use them to build the pipelined |
| // refresh trees and put the pointers to these trees in the bindContext. |
| ////////////////////////////////////////////////////////////////////////////// |
| RelExpr *Refresh::constructPipelinedBuilders(BindWA *bindWA, |
| MvBindContext *bindContext, |
| RelExpr *firstRefreshTree) |
| { |
| const QualifiedName *prevMvName = &mvName_; |
| const QualifiedName *currentMvName = getNextLevelMv(mvName_); |
| const QualifiedName *nextMvName = NULL; |
| CMPASSERT(currentMvName != NULL); |
| RelExpr *topTree = firstRefreshTree; |
| |
| while (currentMvName != NULL) |
| { |
| CorrName mvCorrName(*currentMvName); |
| mvCorrName.setSpecialType(ExtendedQualName::MV_TABLE); |
| |
| MVInfoForDML *mvInfo = getMvInfo(bindWA, mvCorrName); |
| if (mvInfo == NULL) |
| return NULL; |
| CMPASSERT(mvInfo->getMVType()==COM_MAV || mvInfo->getMVType()==COM_MAJV); |
| |
| nextMvName = getNextLevelMv(*currentMvName); |
| NABoolean isPipelined = (nextMvName != NULL); |
| PipelinedMavBuilder *pipelinedBuilder = new(bindWA->wHeap()) |
| PipelinedMavBuilder(mvCorrName, mvInfo, this, isPipelined, prevMvName, bindWA); |
| |
| RelExpr *pipelinedTree = |
| pipelinedBuilder->buildAndConnectPipeliningRefresh(topTree); |
| |
| bindContext->setReplacementFor(prevMvName, pipelinedTree); |
| |
| topTree = pipelinedBuilder->buildRefreshTree(); |
| |
| // This builder is done. |
| delete pipelinedBuilder; |
| |
| prevMvName = currentMvName; |
| currentMvName = nextMvName; |
| } |
| |
| return topTree; |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Do we need to use the special Min/Max builder? |
| // The answer is yes iff: |
| // 1. The MAV uses the MIN and/or MAX aggregate functions. |
| // 2. This is a single delta refresh. |
| // 3. This is a MAV on a single base table. |
| // 4. This base table has a supporting index on the MAV GroupBy columns. |
| // (Supporting index means that the MAV GroupBy columns must be a prefix |
| // of the index columns). |
| ////////////////////////////////////////////////////////////////////////////// |
| NABoolean Refresh::canUseMinMaxBuilder(BindWA *bindWA, MVInfoForDML *mvInfo) |
| { |
| // Check condition no. 1 |
| if (mvInfo->isMinMaxUsed() == FALSE) |
| return FALSE; |
| |
| // Verify condition no. 2 - we are not supposed to get here otherwise. |
| CMPASSERT(deltaDefList_->entries() == 1); |
| |
| // Check condition no. 3 |
| if (!mvInfo->isMvOnSingleTable()) |
| return FALSE; |
| |
| // Check condition no. 4. |
| if (!doesBaseTableHaveSupportingIndex(bindWA, mvInfo)) |
| return FALSE; |
| |
| return TRUE; |
| } // Refresh::canUseMinMaxBuilder() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Does this base table have a supporting index on the MAV GroupBy columns? |
| // (Supporting index means that the MAV GroupBy columns must be a prefix |
| // of the index columns). |
| ////////////////////////////////////////////////////////////////////////////// |
| NABoolean Refresh::doesBaseTableHaveSupportingIndex(BindWA *bindWA, |
| MVInfoForDML *mvInfo) const |
| { |
| CollIndex i; |
| LIST (MVUsedObjectInfo*)& UsedObjList = mvInfo->getUsedObjectsList(); |
| MVUsedObjectInfo* pUsedTable = UsedObjList[0]; |
| |
| // Extract GroupBy columns |
| const MVColumns& pMvInfoColumnList = |
| mvInfo->getMVColumns(); |
| LIST (MVColumnInfo *) mvGroupByColumns(bindWA->wHeap()); |
| for ( i = 0 ; i < pMvInfoColumnList.entries() ; i++) |
| { |
| MVColumnInfo *currentMvColInfo = pMvInfoColumnList[i]; |
| if (COM_MVCOL_GROUPBY == currentMvColInfo->getColType()) |
| { |
| mvGroupByColumns.insert(currentMvColInfo); |
| } |
| } |
| |
| // If the MAV does not have any group by columns - than there is |
| // no supporting index. |
| if (mvGroupByColumns.entries() == 0) |
| return FALSE; |
| |
| // Get the NATable |
| QualifiedName underlyingTableName = pUsedTable->getObjectName(); |
| CorrName corrTableName(underlyingTableName); |
| NATable * pNaTable = bindWA->getNATable(corrTableName, FALSE); |
| |
| // Construct table GroupBy |
| NAColumnArray tableGroupByArray; |
| const NAColumnArray & columnArray = pNaTable->getNAColumnArray(); |
| for ( i = 0 ; i < mvGroupByColumns.entries() ; i++) |
| { |
| Int32 tableColNum = (mvGroupByColumns)[i]->getOrigColNumber(); |
| NAColumn * pColumn = columnArray.getColumn(tableColNum); |
| tableGroupByArray.insert(pColumn); |
| } |
| |
| // Check the clustering Index. |
| const NAFileSet *pClusteringIndexFileSet = pNaTable->getClusteringIndex(); |
| const NAColumnArray& ciColumns = |
| pClusteringIndexFileSet->getIndexKeyColumns(); |
| |
| if (TRUE == areGroupByColsAnIndexPrefix(tableGroupByArray, ciColumns)) |
| { |
| return TRUE; |
| } |
| |
| // Check any secondary indices. |
| const NAFileSetList & indexFileSetList = pNaTable->getIndexList(); |
| for ( i = 0 ; i < indexFileSetList.entries() ; i++) |
| { |
| const NAFileSet *pSecondaryIndexFileSet = indexFileSetList[i]; |
| const NAColumnArray& siColumns = |
| pSecondaryIndexFileSet->getIndexKeyColumns(); |
| |
| if (TRUE == areGroupByColsAnIndexPrefix(tableGroupByArray, siColumns)) |
| { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // For a given vector of index columns, check if the GroupBy columns are |
| // a prefix of the index, in any order. |
| ////////////////////////////////////////////////////////////////////////////// |
| NABoolean Refresh::areGroupByColsAnIndexPrefix(const NAColumnArray & groupByCols, |
| const NAColumnArray & columnArray) const |
| { |
| if(groupByCols.entries() > columnArray.entries()) |
| return FALSE; |
| |
| // For nGroupBys GroupBy columns, check that the first nGroupBys columns |
| // of the index are GroupBy columns. |
| CollIndex nGroupBys = groupByCols.entries(); |
| for (CollIndex indexCol(0); indexCol < nGroupBys; indexCol++) |
| { |
| NABoolean foundIt = FALSE; |
| const NAString& indexColName = columnArray[indexCol]->getColName(); |
| for (CollIndex groupByCol(0); groupByCol < nGroupBys; groupByCol++) |
| { |
| if(groupByCols[groupByCol]->getColName() == indexColName) |
| { |
| foundIt = TRUE; |
| break; |
| } |
| } |
| if (!foundIt) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } // Refresh::groupByContainedAsPrefix |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // return the MV that fromMV is pipelining to. |
| ////////////////////////////////////////////////////////////////////////////// |
| const QualifiedName *Refresh::getNextLevelMv(const QualifiedName& fromMV) const |
| { |
| const PipelineClause *pipelineClause = getPipelineClause(); |
| |
| if (fromMV == mvName_) |
| { |
| const QualNamePtrList *firstDef = pipelineClause->getFirstPipelineDef(); |
| CMPASSERT(firstDef->entries() == 1); |
| return firstDef->at(0); |
| } |
| else |
| { |
| return pipelineClause->getNextLevelMv(fromMV); |
| } |
| } // Refresh::getNextLevelMv() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////// |
| MVInfoForDML *Refresh::getMvInfo(BindWA *bindWA, CorrName& mvToRefresh) const |
| { |
| // Get the NATable for the MV. |
| NATable *mavNaTable = bindWA->getNATable(mvToRefresh); |
| if (bindWA->errStatus()) |
| return NULL; |
| CMPASSERT(mavNaTable!=NULL); |
| CMPASSERT(mavNaTable->isAnMV()); |
| |
| // If this is an incremental refresh, verify that the MV is initialized. |
| // Recompute can be done on un-initialized. |
| // Don't check if the MV is unavailable - the utility may have set it to |
| // unavailable during the refresh operation. |
| const ComMvAttributeBitmap& bitmap = mavNaTable->getMvAttributeBitmap(); |
| if (!bitmap.getIsMvUnAvailable() && bitmap.getIsMvUnInitialized()) |
| CMPASSERT(refreshType_ == RECOMPUTE); |
| |
| // Now get the MVInfo. |
| MVInfoForDML *mvInfo = mavNaTable->getMVInfo(bindWA); |
| CMPASSERT(mvInfo != NULL); |
| mvInfo->initUsedObjectsHash(); |
| |
| return mvInfo; |
| } // Refresh::getMvInfo() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Verify that all the tables in the delta definition list are indeed used |
| // by this MV. |
| ////////////////////////////////////////////////////////////////////////////// |
| NABoolean Refresh::verifyDeltaDefinition(BindWA *bindWA, MVInfoForDML *mvInfo) const |
| { |
| const DeltaDefinitionPtrList *deltaDefs = getDeltaDefList(); |
| if (deltaDefs == NULL) |
| return FALSE; |
| |
| for (CollIndex i=0; i<deltaDefs->entries(); i++) |
| { |
| const QualifiedName *tableName = deltaDefs->at(i)->getTableName(); |
| if (mvInfo->findUsedInfoForTable(*tableName) == NULL) |
| { |
| // 12314 Table $0~TableName is not used by this materialized view. |
| *CmpCommon::diags() << DgSqlCode(-12314) |
| << DgTableName(tableName->getQualifiedNameAsString()); |
| bindWA->setErrStatus(); |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////// |
| const NAString Refresh::getText() const |
| { |
| return "refresh"; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| ////////////////////////////////////////////////////////////////////////////// |
| // Exclude from coverage testing - Must be implemented as a RelExpr subclass, but not used. |
| RelExpr *Refresh::copyTopNode(RelExpr *derivedNode, CollHeap* outHeap) |
| { |
| Refresh *result = NULL; |
| |
| if (derivedNode == NULL) |
| { |
| switch (refreshType_) |
| { |
| case RECOMPUTE: |
| result = new(outHeap) Refresh(mvName_, |
| noDeleteOnRecompute_, |
| outHeap); |
| break; |
| |
| case SINGLEDELTA: |
| result = new(outHeap) Refresh(mvName_, |
| deltaDefList_, |
| pNRowsClause_, |
| pipelineClause_, |
| outHeap); |
| break; |
| |
| case MULTIDELTA: |
| result = new(outHeap) Refresh(mvName_, |
| deltaDefList_, |
| phase_, |
| pipelineClause_, |
| outHeap); |
| break; |
| |
| default: CMPASSERT(FALSE); |
| } |
| } |
| else |
| result = (Refresh *) derivedNode; |
| |
| return BinderOnlyNode::copyTopNode(result, outHeap); |
| } // Refresh::copyTopNode() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Return the DeltaDefinition for the table named 'name'. |
| ////////////////////////////////////////////////////////////////////////////// |
| DeltaDefinition *Refresh::getDeltaDefinitionFor(const QualifiedName& name) |
| { |
| return deltaDefList_->findEntryFor(name); |
| } // Refresh::getDeltaDefinitionFor() |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Below this line it's Ori's code... |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| |
| //---------------------------------------------------------------------------- |
| //class PipelineClause |
| |
| //---------------------------------------------------------------------------- |
| PipelineClause::~PipelineClause() |
| { |
| delete firstPipelineDef_; |
| delete pipelineDefList_; |
| } |
| |
| //---------------------------------------------------------------------------- |
| // Apply the default catalog and schema to all the MV names. |
| // The syntax supports multi-parent pipelining. |
| void PipelineClause::applyDefaultSchemaToAll(BindWA *bindWA) |
| { |
| const SchemaName &defaultSchema = bindWA->getDefaultSchema(); |
| |
| const QualNamePtrList& nameList=*firstPipelineDef_; |
| for (CollIndex i=0; i<nameList.entries(); i++) |
| nameList[i]->applyDefaults(defaultSchema); |
| |
| if (pipelineDefList_ == NULL) |
| return; |
| |
| for (CollIndex j=0; j<pipelineDefList_->entries(); j++) |
| { |
| pipelineDefList_->at(j)->getFromClause()->applyDefaults(defaultSchema); |
| QualNamePtrList *nameList2 = pipelineDefList_->at(j)->getToClause(); |
| for (CollIndex k=0; k<nameList.entries(); k++) |
| nameList2->at(k)->applyDefaults(defaultSchema); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| // return the MV that fromMV is pipelining to. |
| const QualifiedName *PipelineClause::getNextLevelMv(const QualifiedName& fromMV) const |
| { |
| if (pipelineDefList_ != NULL) |
| { |
| // Go through the next levels to find toMV. |
| for (CollIndex i=0; i<pipelineDefList_->entries(); i++) |
| { |
| const PipelineDef *pipelineLevel = pipelineDefList_->at(i); |
| if (*pipelineLevel->getFromClause() == fromMV) |
| { |
| const QualNamePtrList *pipelineToList = pipelineLevel->getToClause(); |
| CMPASSERT(pipelineToList->entries() == 1); |
| return pipelineToList->at(0); |
| } |
| } |
| } |
| |
| // If we get here - This is the top most MV, that is not pipelined anymore. |
| return NULL; |
| } |
| |
| //---------------------------------------------------------------------------- |
| //class DeltaDefinition |
| |
| DeltaDefinition::~DeltaDefinition() |
| { |
| delete updatedColumnList_; |
| delete pDeltaOptions_; |
| } |
| |
| void DeltaDefinition::synthesize() |
| { |
| DeltaDefLogs *pDeltaDefLogs = NULL; |
| |
| switch(pDeltaOptions_->getDELevel()) |
| { |
| case DeltaOptions::NO_DE: |
| insertOnly_ = pDeltaOptions_->getIsInsertOnly(); |
| break; |
| |
| case DeltaOptions::RANGE_RESOLUTION_ONLY: |
| case DeltaOptions::RANGE_AND_CROSS_TYPE_RESOLUTIONS: |
| case DeltaOptions::ALL: |
| pDeltaDefLogs = pDeltaOptions_->getDeltaStats(); |
| break; |
| |
| default: |
| CMPASSERT(FALSE); |
| } |
| |
| if(NULL == pDeltaDefLogs) |
| { |
| return; |
| } |
| |
| // RANGE DEF |
| switch(pDeltaDefLogs->getRangeDef()->getOption()) |
| { |
| case DeltaDefRangeLog::NO_LOG: |
| useRangeLog_ = FALSE; |
| break; |
| |
| case DeltaDefRangeLog::ALL: |
| coveredRows_ = pDeltaDefLogs->getRangeDef()->getCoveredRows(); |
| //no need to break; |
| |
| case DeltaDefRangeLog::CARDINALITY_ONLY: |
| numOfRanges_ = pDeltaDefLogs->getRangeDef()->getNumOfRanges(); |
| useRangeLog_ = TRUE; |
| break; |
| |
| default: |
| CMPASSERT(FALSE); |
| |
| } |
| |
| // IUD DEF |
| switch(pDeltaDefLogs->getIUDDef()->getOption()) |
| { |
| case DeltaDefIUDLog::NO_LOG: |
| useIudLog_ = FALSE; |
| break; |
| case DeltaDefIUDLog::INSERT_ONLY: |
| case DeltaDefIUDLog::NO_STAT: |
| useIudLog_ = TRUE; |
| break; |
| case DeltaDefIUDLog::STAT: |
| useIudLog_ = TRUE; |
| iudInsertedRows_ = |
| pDeltaDefLogs->getIUDDef()->getStatistics()->getNumRowsInserted(); |
| iudDeletedRows_ = |
| pDeltaDefLogs->getIUDDef()->getStatistics()->getNumRowsDeleted(); |
| iudUpdatedRows_ = |
| pDeltaDefLogs->getIUDDef()->getStatistics()->getNumRowsUpdated(); |
| updatedColumnList_ = |
| pDeltaDefLogs->getIUDDef()->getStatistics()->getOptionalColumnList(); |
| break; |
| |
| default: |
| CMPASSERT(FALSE); |
| } |
| } // DeltaDefinition::synthesize |
| |
| |
| // Returns TRUE if all the updated columns of this delta are not used by MV |
| NABoolean DeltaDefinition::updateColumnsNotUsedByMV(MVInfoForDML *mvInfo) |
| { |
| processUpdateColumns(mvInfo); |
| return !containsUpdateColUsedByMv_; |
| } |
| |
| // Process update columns of the delta to populate data members |
| // containsIndirectUpdateColumn_, containsDirectUpdateColumn_ and |
| // containsUpdateColUsedByMv_ |
| void DeltaDefinition::processUpdateColumns(MVInfoForDML *mvInfo) |
| { |
| if (updateColumnsProcessed_) |
| return; |
| |
| mvInfo->initUsedObjectsHash(); |
| const MVUsedObjectInfo *usedObject = mvInfo-> |
| findUsedInfoForTable(*getTableName()); |
| |
| const IntegerList *updatedCols = getUpdatedColumnList(); |
| if (updatedCols==NULL) |
| { |
| // We have no statistics on updated column - we have to assume the worst case. |
| containsIndirectUpdateColumn_ = TRUE; |
| containsDirectUpdateColumn_ = TRUE; |
| containsUpdateColUsedByMv_ = TRUE; |
| } |
| else |
| { |
| for (CollIndex i = 0; i < updatedCols->entries(); i++ ) |
| { |
| Lng32 updatedCol = updatedCols->at(i); |
| |
| if (usedObject->isIndirectUpdateCol(updatedCol)) |
| { |
| containsUpdateColUsedByMv_ = TRUE; |
| containsIndirectUpdateColumn_ = TRUE; |
| } |
| else if (usedObject->isUsedColumn(updatedCol)) |
| { |
| containsUpdateColUsedByMv_ = TRUE; |
| containsDirectUpdateColumn_ = TRUE; |
| } |
| |
| // if already seen one DIRECT update column, one INDIRECT update column |
| // and one update column that is used by the MV |
| if (containsIndirectUpdateColumn_ && |
| containsDirectUpdateColumn_ && |
| containsUpdateColUsedByMv_) |
| break; |
| } |
| } |
| |
| updateColumnsProcessed_ = TRUE; |
| } |
| |
| // Returns TRUE if the updated columns of this delta contains a |
| // DIRECT update column |
| NABoolean DeltaDefinition::containsDirectUpdateColumn(MVInfoForDML *mvInfo) |
| { |
| processUpdateColumns(mvInfo); |
| return containsDirectUpdateColumn_; |
| } |
| |
| |
| // Returns TRUE if the updated columns of this delta contains |
| // an INDIRECT update column |
| NABoolean DeltaDefinition::containsIndirectUpdateColumn(MVInfoForDML *mvInfo) |
| { |
| processUpdateColumns(mvInfo); |
| return containsIndirectUpdateColumn_; |
| } |
| |
| |
| //---------------------------------------------------------------------------- |
| // Class DeltaDefinitionPtrList |
| |
| void DeltaDefinitionPtrList::applyDefaultSchemaToAll(BindWA *bindWA) const |
| { |
| const SchemaName &defaultSchema = bindWA->getDefaultSchema(); |
| for (CollIndex i=0; i<entries(); i++) |
| at(i)->getTableName()->applyDefaults(defaultSchema); |
| } |
| |
| DeltaDefinition *DeltaDefinitionPtrList::findEntryFor(const QualifiedName& tableName) const |
| { |
| for (CollIndex i=0; i<entries(); i++) |
| { |
| DeltaDefinition *deltaDef = at(i); |
| if (tableName == *deltaDef->getTableName()) |
| return deltaDef; |
| } |
| return NULL; |
| } |
| |
| NABoolean DeltaDefinitionPtrList::areAllDeltasInsertOnly() const |
| { |
| for (CollIndex i=0; i<entries(); i++) |
| if (!at(i)->isInsertOnly()) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| NABoolean DeltaDefinitionPtrList::areAllDeltasWithFullDE() const |
| { |
| for (CollIndex i=0; i<entries(); i++) |
| if (!at(i)->isFullDE()) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |