void CRUCache::FetchNonInvolvedUsedObjectsMetadata() { DSListPosition pos = mvList_.GetHeadPosition(); while (NULL != pos) { CRUMV *pMV = mvList_.GetNext(pos); if (FALSE == pMV->IsInvolved() || CDDObject::eON_REQUEST != pMV->GetRefreshType()) { continue; } if (NULL == GetTable(pMV->GetUID())) { CDDSchema *pddSch = GetDDSchemaByName(pMV->GetCatName(), pMV->GetSchName()); CDDMV *pddMV = GetDDMVByName(pddSch, pMV->GetName()); CRUTbl *pTbl = new CRUMVTbl(pddMV); pTbl->FetchMetadata(); tableList_.AddTail(pTbl); } } }
void CRUAuditRefreshTaskExecutor::EpilogueHandleOnStatementMV() { CRUMV &mv = GetRootMV(); CRUTblList &tblList = mv.GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); Int32 i=0; while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); CDMPreparedStatement *pStat = pLockTablesTEDynamicContainer_->GetPreparedStatement(i); // Perform the LOCK TABLE statement on the used table // (the lock overlaps the original RP open, therefore // guaranteeing the lock continuity). ExecuteStatement(*pStat,IDS_RU_IREFRESH_FAILED); // Release the DDL lock AND the read-protected open on the used table. pTbl->ReleaseResources(); } }
void CRURefreshTaskExecutor::ComposeControlTableStmtForUsedTable(CRUTbl &tbl, Int32 &stmtIndex, CRUForceOptions::MdamOptions mdamOpt) { CRURefreshSQLComposer myComposer(GetRefreshTask()); if (CRUForceOptions::MDAM_NO_FORCE == mdamOpt) { CRUTableForceOptions *forceOpt = GetForceOptionForTable(tbl.GetFullName()); if (NULL == forceOpt) { mdamOpt = GetDefaultMdamOptForTable(tbl); } else { mdamOpt = forceOpt->GetMdamOptions(); } } if (CRUForceOptions::MDAM_NO_FORCE != mdamOpt) { // Compose CONTROL TABLE table_name MDAM option myComposer.ComposeCntrlTableMDAMText(mdamOpt, &(tbl.GetFullName())); pRefreshTEDynamicContainer_->SetStatementText #pragma nowarn(1506) // warning elimination (stmtIndex++, myComposer.GetSQL()); #pragma warn(1506) // warning elimination forceFlags_ |= FORCE_TABLE_MDAM; } }
CRUTbl *CRUCache::FetchSingleUsedObject(CRUMV *pUsingMV, const CDDUIDTriple &uidt) { CRUTbl *pTbl; BOOL isMV = pUsingMV->IsUsedObjectAnMV(uidt.objUID); if (FALSE == isMV) { CDDTable *pddTable = GetDDTableByUID(uidt); pTbl = new CRURegularTbl(pddTable); } else { CDDMV *pddMV = GetDDMVByUID(uidt); pTbl = new CRUMVTbl(pddMV); // Read the REFRESH-specific metadata pddMV->FetchMVRefreshMetadata(); // A table based on MV will always inherit the MV's timestamp pTbl->SetTimestamp(pddMV->GetRefreshedAtTimestamp()); // Inherit the MV's feature. // If a table is NOT an involved MV, take care not to remove // its dangling DDL lock (if any) accidentally. pTbl->SetReleaseDDLLock(pddMV->CanCancelDDLLock()); } pTbl->SetInvolved(); tableList_.AddTail(pTbl); return pTbl; }
void CRUCache::FixupMVTblInterfaces() { DSListPosition pos = mvList_.GetHeadPosition(); while (NULL != pos) { CRUMV *pMV = mvList_.GetNext(pos); if (FALSE == pMV->IsInvolved()) { continue; // Skip the non-involved MVs } CRUTbl *pTbl = GetTable(pMV->GetUID()); if (NULL == pTbl) { continue; // No table object in the cache, skip } // Setup pointers MV <---> table pMV->SetTblInterface(pTbl); pTbl->SetMVInterface(pMV); } }
TInt64 CRURefreshTaskExecutor::CalculateTimeStamp() { CRUTblList &tblList = GetRootMV().GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); CRUTbl *pTbl = tblList.GetNext(pos); TInt64 minTS = pTbl->GetTimestamp(); while (NULL != pos) { pTbl = tblList.GetNext(pos); TInt64 nextTS = pTbl->GetTimestamp(); minTS = (nextTS < minTS) ? nextTS : minTS; } if (0 == minTS) { // If the timestamp has not been executed until now, // this is time to compute it. // RUASSERT(CDDObject::eRECOMPUTE == GetRootMV().GetRefreshType()); minTS = CRUGlobals::GetCurrentTimestamp(); } return minTS; }
CRUCache::~CRUCache() { // Normally, all of the DDOL MV/table objects are in // the eClosed state at this point. However, // if the destructor is called from inside an exception // handler when some DDOL object is left in the eModified // state, the utility should not attempt to save the object // to the catalog. DSListPosition pos = mvList_.GetHeadPosition(); while (NULL != pos) { CRUMV *pMV = mvList_.GetNext(pos); pMV->CancelChanges(); } pos = tableList_.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tableList_.GetNext(pos); pTbl->CancelChanges(); } // Since sqlNode_ is an object (not a pointer), // its destructor is called automatically, // therefore disposing the cloud of DD objects. // Since mvList_ and tableList_ *own* the referenced objects, // there is no need to apply the RemoveAll() method to them. }
void CRUAuditRefreshTaskExecutor::PrologueHandleOnStatementMV() { CRUTblList &tblList = GetRootMV().GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); pTbl->ExecuteReadProtectedOpen(); } }
void CRURefreshTaskExecutor::UpdateRootMVCurrentEpochVector() { CRUMV &rootMV = GetRootMV(); CRUTblList &tblList = rootMV.GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); rootMV.SetEpoch(pTbl->GetUID(), pTbl->GetCurrentEpoch()); } }
BOOL CRULockEquivSetTask::HasObject(TInt64 uid) const { DSListPosition pos = tblList_.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList_.GetNext(pos); if (pTbl->GetUID() == uid) { return TRUE; } } return FALSE; }
//--------------------------------------------------------------------------// // CRULockEquivSetTaskExecutor::SetTimestampToAllTbls() //--------------------------------------------------------------------------// void CRULockEquivSetTaskExecutor::SetTimestampToAllTbls(TInt64 ts) { CRULockEquivSetTask *pParentTask = (CRULockEquivSetTask *)GetParentTask(); RUASSERT(NULL != pParentTask); CRUTblList &tblList = pParentTask->GetTableList(); DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); pTbl->SetTimestamp(ts); } }
void CRURefreshTaskExecutor::IncrementTopMvCurrentEpoch() { CRUMV &mv = GetRefreshTask()->GetTopMV(); if (TRUE == mv.IsInvolvedTbl()) { CRUTbl *pTbl = mv.GetTblInterface(); pTbl->IncrementCurrentEpoch(TRUE /* through DDOL */); pTbl->SaveMetadata(); pTbl->SetTimestamp(mv.GetTimestamp()); } }
void CRUUnAuditRefreshTaskExecutor::ComposeMySql() { CRURefreshTask *pTask = GetRefreshTask(); CRUMV &mv = pTask->GetRootMV(); CRUSimpleRefreshSQLComposer myComposer(pTask); // UNLOCK TABLE statement myComposer.ComposeUnLock(GetRootMVName()); unAuditRefreshTEDynamicContainer_.SetStatementText (RU_UNLOCK_TABLE, myComposer.GetSQL()); // POPINDEX CatApi request if (TRUE == isPopindex_) { numOfIndexes_ = mv.GetIndexList().GetCount(); if (0 < numOfIndexes_) { ComposeIndexesSql(); } } // Compose the LOCK TABLE sql statements for locking all tables // in the on statement MV initialization if (CDDObject::eON_STATEMENT == GetRootMVType()) { CRUTblList &tblList = mv.GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); pLockTablesTEDynamicContainer_ = new CRUSQLDynamicStatementContainer((short)tblList.GetCount()); Int32 i=0; while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); myComposer.ComposeLock(pTbl->GetFullName(), FALSE /*shared*/); pLockTablesTEDynamicContainer_->SetStatementText #pragma nowarn(1506) // warning elimination (i,myComposer.GetSQL()); #pragma warn(1506) // warning elimination i++; } } }
CRUTbl *CRUCache::GetTable(TInt64 objUid) const { DSListPosition pos = tableList_.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tableList_.GetNext(pos); if (pTbl->GetUID() == objUid) { return pTbl; } } return NULL; }
void CRUEmpCheck::ComposeSQL(CRUTbl &tbl, TInt32 upperBound) { CDSString sql; checkMask_ = tbl.GetIUDLogContentTypeBitmap(); if (0 != (checkMask_ & CRUTbl::RANGE)) { ComposeSelectStmt(tbl, sql, CHECK_NEG_EPOCHS, -upperBound); pSQLContainer_->SetStatementText(CHECK_NEG_EPOCHS, sql); } if (0 != (checkMask_ & CRUTbl::SINGLE_ROW_INSERTS)) { ComposeSelectStmt(tbl, sql, CHECK_POS_EPOCHS_INSERTS, upperBound); pSQLContainer_->SetStatementText(CHECK_POS_EPOCHS_INSERTS, sql); } if (0 != (checkMask_ & CRUTbl::SINGLE_ROW_OTHER)) { ComposeSelectStmt(tbl, sql, CHECK_POS_EPOCHS_DELETES, upperBound); pSQLContainer_->SetStatementText(CHECK_POS_EPOCHS_DELETES, sql); } }
void CRUAuditRefreshTaskExecutor::ComposeMySql() { CRUSimpleRefreshSQLComposer myComposer(GetRefreshTask()); CRUMV &rootMV = GetRootMV(); if (TRUE == isDeleteMultiTxnContext_) { myComposer.ComposeDeleteContextLogTable(); auditRefreshTEDynamicContainer_.SetStatementText (DELETE_MULT_TXN_CTX_TBL,myComposer.GetSQL()); } // POPINDEX CatApi request if (TRUE == isPopindex_) { numOfIndexes_ = rootMV.GetIndexList().GetCount(); if (0 < numOfIndexes_) { ComposeIndexesSql(); } } // Compose the LOCK TABLE sql statements for locking all tables // in the on statement MV initialization if (CDDObject::eON_STATEMENT == GetRootMVType()) { CRUTblList &tblList = rootMV.GetTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); pLockTablesTEDynamicContainer_ = new CRUSQLDynamicStatementContainer((short)tblList.GetCount()); Int32 i=0; while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); myComposer.ComposeLock(pTbl->GetFullName(), FALSE /*shared*/); pLockTablesTEDynamicContainer_->SetStatementText (i,myComposer.GetSQL()); i++; } } }
//--------------------------------------------------------------------------// // CRUTableLockProtocol::Init() // //--------------------------------------------------------------------------// void CRUTableLockProtocol::Init(CRUTblList &tblList, CRUDeltaDefList &DeltaDefList) { CRUDeltaDef *pDdef = NULL; CRUTbl *curTable = NULL; // For each table in the used table list, find the // corresponding delta def. DSListPosition tPos = tblList.GetHeadPosition(); while (tPos != NULL) { curTable = tblList.GetNext(tPos); DSListPosition dPos = DeltaDefList.GetHeadPosition(); while (dPos != NULL) { pDdef = DeltaDefList.GetNext(dPos); // Match each table to its delta def by table UID. if (curTable->GetUID() == pDdef->tblUid_) { // We have a match! // We need this lock only if the range log has any data in it. if (TRUE == pDdef->isRangeLogNonEmpty_) { if (LocksList_ == NULL) { // This is the first match we have found - alloc the list. LocksList_ = new LocksList(); NothingToLock_ = FALSE; } // Alloc a new lock object, init it and add it to the list. CRUSingleTableLockProtocol *thisLock = new CRUSingleTableLockProtocol(); thisLock->Init(curTable, pDdef->toEpoch_); LocksList_->AddTail(thisLock); } break; // Done with this table. } // if Matching table UID } // Loop on DeltaDefList } // Loop on tblList }
CDSString CRULockEquivSetTask::GetTaskName() const { CDSString name("LOCK SET("); DSListPosition pos = tblList_.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList_.GetNext(pos); name += pTbl->GetFullName(); if (NULL != pos) { name += ","; } } name += ")"; return name; }
void CRUCache::Dump(CDSString &to, BOOL isExtended) const { to += "\n\t\tCACHE DUMP\n"; DSListPosition pos = mvList_.GetHeadPosition(); while (NULL != pos) { CRUMV *pMV = mvList_.GetNext(pos); pMV->Dump(to, isExtended); } pos = tableList_.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tableList_.GetNext(pos); pTbl->Dump(to, isExtended); } }
CRULockEquivSetTask::CRULockEquivSetTask(Lng32 id,const CRUTblList &tblList) : CRUTask(id), tblList_(eItemsArentOwned) { DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); if (TRUE == pTbl->IsNoLockOnRefresh()) { tblList_.AddTail(pTbl); } else { tblList_.AddHead(pTbl); } } }
void CRUEmpCheck:: ComposeSelectStmt(CRUTbl &tbl, CDSString &to, StmtType StmtType, TInt32 upperBound) { RUASSERT(CHECK_POS_EPOCHS_INSERTS == StmtType || CHECK_POS_EPOCHS_DELETES == StmtType || CHECK_NEG_EPOCHS == StmtType); CDSString iudLogName(tbl.GetIUDLogFullName()); CDSString epochColName("EPOCH"); epochColName = CRUSQLComposer:: ComposeQuotedColName(CRUTbl::logCrtlColPrefix, epochColName); CDSString opColName("OPERATION_TYPE"); opColName = CRUSQLComposer:: ComposeQuotedColName(CRUTbl::logCrtlColPrefix, opColName); CDSString op1, op2; if (CHECK_NEG_EPOCHS == StmtType) { op1 = " <= "; op2 = " >= "; } else { op1 = " >= "; op2 = " <= "; } to = "SELECT [FIRST 1] " + epochColName + "\nFROM " + iudLogName; to += "\nWHERE " + epochColName + op1 + CRUSQLComposer::ComposeCastExpr("INT"); if (0 != upperBound) { to += "\nAND " + epochColName + op2 + CRUSQLComposer::TInt32ToStr(upperBound); } if (CHECK_POS_EPOCHS_INSERTS == StmtType) { // Add a predicate specific for single row inserts to += "\nAND " + opColName + " = 0"; } else if (CHECK_POS_EPOCHS_DELETES == StmtType) { // Add a predicate specific for single row deletes and updates to += "\nAND (" + opColName + " = 1 OR " + opColName + " = 3)"; } to += ";"; }
void CRURefreshTaskExecutor::CheckSingleDeltaRestriction() { CRUMV &rootMV = GetRootMV(); CRUTblList &tblList = rootMV.GetFullySyncTablesUsedByMe(); DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); RUASSERT (TRUE == pTbl->IsFullySynchronized()); if (TRUE == pTbl->IsEmptyDeltaNeeded() && TRUE == rootMV.IsDeltaNonEmpty(*pTbl)) { CRUException errDesc; errDesc.SetError(IDS_RU_INCONSISTENT_JOIN); errDesc.AddArgument(rootMVName_); throw errDesc; } } }
void CRUTblEquivSetBuilder::BuildSets() { // Initialize the array of sets of CRUTbl Int32 equivSetsSize = GetDisJointAlg().GetNumOfSets(); // Initialize the sets of CRUTbl for (Int32 i=0;i<equivSetsSize;i++) { equivSetsList_.AddTail( new CRUTblList(eItemsArentOwned)); } // Pass all regular tables and add them to the appropriate list DSListPosition tblPos = syncTablesList_.GetHeadPosition(); while (NULL != tblPos) { CRUTbl *pTbl = syncTablesList_.GetNext(tblPos); Int32 setId = GetDisJointAlg().GetNodeSetId(pTbl->GetUID()); equivSetsList_.GetAt(setId)->AddTail(pTbl); } // Remove set with zero size DSListPosition setsPos = equivSetsList_.GetHeadPosition(); while (NULL != setsPos) { DSListPosition setsPrevpos = setsPos; CRUTblList *pTblList = equivSetsList_.GetNext(setsPos); if (0 == pTblList->GetCount()) { equivSetsList_.RemoveAt(setsPrevpos); } } }
void CRUCache::FetchNonInvolvedMVsMetadata() { DSListPosition tpos = tableList_.GetHeadPosition(); while (NULL != tpos) { CRUTbl *pTbl = tableList_.GetNext(tpos); // Retrieve the table's private dependency list CDDUIDTripleList &privTblList = pTbl->GetUIDsOfAllMVsUsingMe(); DSListPosition mvpos = privTblList.GetHeadPosition(); while (NULL != mvpos) { CDDUIDTriple &uidt = privTblList.GetNext(mvpos); CRUMV *pMV = GetMV(uidt.objUID); if (NULL == pMV) { // The MV is non-involved, fetch the DD object CDDMV *pddMV = GetDDMVByUID(uidt); // If the MV is not ON REQUEST, skip it if (CDDObject::eON_REQUEST != pddMV->GetRefreshType()) { continue; } pMV = FetchSingleNonInvolvedMV(pddMV); } if (FALSE == pMV->IsInvolved()) { // If the MV is involved, the pointer exists already pTbl->AddRefToUsingMV(pMV); } } } }
void CRULockEquivSetTaskExecutor:: FreezeAllTables(CRULockEquivSetTask *pParentTask) { CRUTblList &tblList = pParentTask->GetTableList(); DSListPosition pos = tblList.GetHeadPosition(); while (NULL != pos) { CRUTbl *pTbl = tblList.GetNext(pos); if (TRUE == pTbl->IsIncEpochNeeded()) { pTbl->ExecuteLogReadProtectedOpen(); } else { if (TRUE == pTbl->IsLongLockNeeded()) { pTbl->ExecuteReadProtectedOpen(); } } } }
//--------------------------------------------------------------------------// // CRURefreshTaskExecutor::GetDefaultMdamOptForTable() // // If the table has more then two key columns and the range log is not empty // then mdam must be forced on the table unless the user forced otherwise //--------------------------------------------------------------------------// CRUForceOptions::MdamOptions CRURefreshTaskExecutor::GetDefaultMdamOptForTable(CRUTbl &tbl) { Int32 numTableKeyCol = tbl.GetKeyColumnList().GetCount(); if (numTableKeyCol < 2) { return CRUForceOptions::MDAM_NO_FORCE; } if (FALSE == GetRefreshTask()->IsSingleDeltaRefresh()) { return CRUForceOptions::MDAM_NO_FORCE; } const CRUDeltaDef *pDdef = GetRootMV().GetDeltaDefByUid(tbl.GetUID()); if (NULL != pDdef && TRUE == pDdef->isRangeLogNonEmpty_) { return CRUForceOptions::MDAM_ON; } return CRUForceOptions::MDAM_NO_FORCE; }
void CRUTblEquivSetBuilder::AddOnRequestMV(CRUMV *pMV) { RUASSERT(CDDObject::eON_REQUEST == pMV->GetRefreshType()); GetDisJointAlg().AddVertex(pMV->GetUID()); // All tables in this list are involved tables by definition CRUTblList &tblList = pMV->GetTablesUsedByMe(); DSListPosition tblPos = tblList.GetHeadPosition(); while (NULL != tblPos) { CRUTbl *pTbl = tblList.GetNext(tblPos); if (TRUE == pTbl->IsFullySynchronized()) { // This is as syncronized table ,we must place it in the // syncronized table list and in the disjoint graph AddSyncTable(pTbl); GetDisJointAlg().AddEdge(pTbl->GetUID(),pMV->GetUID()); } if (FALSE == pTbl->IsInvolvedMV()) { // This is not an involved mv do not add it to the set. // A not involved mv does not require syncronization // because it may only be changed by the refresh utility // and this is prevented by the ddl locks that are place on this // object continue; } GetDisJointAlg().AddEdge(pTbl->GetUID(),pMV->GetUID()); } }
void CRUTblEquivSetBuilder::DumpSets() { CDSString msg; msg = "\n Equivalent sets: \n" ; CRUGlobals::GetInstance()->GetJournal().LogMessage(msg); for (Int32 i=0;i<GetNumOfSets();i++) { msg = "\n Table Set: \n" ; CRUGlobals::GetInstance()->GetJournal().LogMessage(msg); DSListPosition tblPos = GetSet(i).GetHeadPosition(); while (NULL != tblPos) { CRUTbl *pTbl = GetSet(i).GetNext(tblPos); CRUGlobals::GetInstance()->GetJournal(). LogMessage(pTbl->GetFullName()); } } }
void CRUCache::FetchUsedObjectsMetadataForSingleMV(CRUMV *pMV) { CDDUIDTripleList &uList = pMV->GetAllUsedObjectsUIDs(); DSListPosition pos = uList.GetHeadPosition(); while (NULL != pos) { CDDUIDTriple& uidt = uList.GetNext(pos); TInt64 objUid = uidt.objUID; // Tables that are IGNORE CHANGES and UDFs do not interest us // for the purpose of REFRESH if (TRUE == pMV->IsIgnoreChanges(objUid) || TRUE == pMV->IsUsedObjectUDF(objUid)) { continue; } // Fetch the pointer to the used table's wrapper object // (create the wrapper if the table is referenced first) CRUTbl *pTbl = GetTable(objUid); if (NULL == pTbl) { pTbl = FetchSingleUsedObject(pMV, uidt); } // Add the pointer to the table to the MV's list of dependencies. pMV->AddRefToUsedObject(pTbl); // Add the pointer to the MV to the table's list of dependencies. pTbl->AddRefToUsingMV(pMV); // Register the used object for the DDL lock handling ... SetupUsedObject(pTbl); } }
void CRUTblEquivSetBuilder::AddRecomputeTree(CRUMV *pMV) { RUASSERT(CDDObject::eRECOMPUTE == pMV->GetRefreshType()); if (TRUE == GetDisJointAlg().HasVertex(pMV->GetUID())) { // The mv is already in the graph and his sub tree must also be there // by induction return; } GetDisJointAlg().AddVertex(pMV->GetUID()); // Go over all the childs CRUTblList &tblList = pMV->GetTablesUsedByMe(); DSListPosition tblPos = tblList.GetHeadPosition(); while (NULL != tblPos) { CRUTbl *pTbl = tblList.GetNext(tblPos); if (TRUE == pTbl->IsFullySynchronized()) { GetDisJointAlg().AddEdge(pTbl->GetUID(),pMV->GetUID()); AddSyncTable(pTbl); pTbl->SetLongLockIsNeeded(); continue; } if (FALSE == pTbl->IsInvolvedMV()) { // This is not an involved mv do not add it to the set. // A not involved mv does not require syncronization // because it may only be changed by the refresh utility // and this is prevented by the ddl locks that are place on this // object continue; } // The object is an mv - if its on request we should do nothing // forcing lock may be skipped and all involved on request // are already in the graph GetDisJointAlg().AddEdge(pTbl->GetUID(),pMV->GetUID()); CRUMV *pNextMV = pTbl->GetMVInterface(); RUASSERT(NULL != pNextMV); if (CDDObject::eON_REQUEST == pNextMV->GetRefreshType() ) { // All on requests mv's are already in the graph continue; } RUASSERT(CDDObject::eRECOMPUTE == pNextMV->GetRefreshType()); // call this function recursively for adding all sub tree AddRecomputeTree(pNextMV); } }