blob: 9ededf55993d5977e7f221175ed5c9b14495dbe2 [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: RuPreRuntimeCheck.cpp
* Description: Implementation of class CRUPreRuntimeCheck
*
* Created: 10/10/2000
* Language: C++
*
*
******************************************************************************/
#include "RuPreRuntimeCheck.h"
#include "RuOptions.h"
#include "RuGlobals.h"
#include "RuRefreshTask.h"
#include "RuTbl.h"
#include "uosessioninfo.h"
//--------------------------------------------------------------------------//
// Constructor
//--------------------------------------------------------------------------//
CRUPreRuntimeCheck::CRUPreRuntimeCheck(CRURefreshTask &refreshTask) :
refreshTask_(refreshTask),
mv_(refreshTask_.GetRootMV()),
mvName_(mv_.GetFullName()),
errDesc_(refreshTask_.GetErrorDesc())
{}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::PerformCheck()
//
// Perform all the pre-runtime check stages
//--------------------------------------------------------------------------//
void CRUPreRuntimeCheck::PerformCheck()
{
// 1. Check that there are no problems
// about the DDL locks on the MV(and indexes on it) and/or used objects.
if (FALSE == CheckDDLLockErrors())
{
return;
}
// 2. In order to perform the refresh, the user
// must have the INSERT priviledge for this MV.
// In order to perform purgedata, the user must have
// the SELECT privilege for this MV.
// In order to populate the index, the user must have
// the SELECT and DELETE privileges for this MV.
if (FALSE == CheckPrivileges())
{
return;
}
// 3. Check that all the used objects are initialized
if (FALSE == CheckUsedObjectsInitialization())
{
return;
}
// 4. The user can recompute an ON REQUEST/ON STATEMENT MV
// only if it is not based on NO LOCKONREFRESH tables.
if (FALSE == CheckUsedTablesNoLockOnRefresh())
{
return;
}
// 5. Check that an ON STATEMENT MV is not re-initialized.
if (FALSE == CheckOnStatementMVReInit())
{
return;
}
// 6. Verify that after the refresh
// the MV will reflect a correct database snapshot.
if (FALSE == CheckConsistency())
{
return;
}
// 7. If the MV is incremental, verify that
// all the used regular tables are audited.
if (FALSE == CheckUsedTablesAuditing())
{
return;
}
// 8. Check that if a non-available non-audited MV
// is recomputed, the user must specify
// the RECOMPUTE option explicitly.
CheckNonAvailableMVRecompute();
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckDDLLockErrors()
//
// Check the error objects associated with the MV and the tables used by it.
//
// If the error objects carry a non-zero (error) code,
// this is the consequence of failure to acquire/replace a DDL lock
// during the cache construction. In this case, the task will fail.
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckDDLLockErrors()
{
if (0 != mv_.GetStatus())
{
// Copy the MV's error description
errDesc_ = mv_.GetErrorDesc();
return FALSE;
}
CRUTblList &tblList = mv_.GetTablesUsedByMe();
DSListPosition pos = tblList.GetHeadPosition();
while (NULL != pos)
{
CRUTbl *pTbl = tblList.GetNext(pos);
if (0 != pTbl->GetStatus())
{
// Copy the table's error description
errDesc_ = pTbl->GetErrorDesc();
return FALSE;
}
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckOnStatementMVReInit()
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckOnStatementMVReInit()
{
if (CDDMV::eON_STATEMENT == mv_.GetRefreshType()
&&
CDDObject::eINITIALIZED == mv_.GetMVStatus()
)
{
errDesc_.SetError(IDS_RU_ON_STATEMENT_REINIT);
errDesc_.AddArgument(mvName_);
return FALSE;
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckPrivileges()
//
// (1) INSERT privilege for refresh in general.
// (2) SELECT privilege for popindex.
// (3) DELETE and SELECT privileges for purgedata.
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckPrivileges()
{
// Allow super.super and super.services to perform REFRESH
// IsServices checks for both
CUOSessionInfo sessionInfo (TRUE, FALSE, FALSE);
if (sessionInfo.BelongsToServicesRole())
return TRUE;
Lng32 priv = mv_.GetPrivMap();
BOOL isInsertPriv = (0 != (priv & CRUMV::INSERT_PRIV));
BOOL isSelectPriv = (0 != (priv & CRUMV::SELECT_PRIV));
BOOL isDeletePriv = (0 != (priv & CRUMV::DELETE_PRIV));
if (FALSE == isInsertPriv)
{
errDesc_.SetError(IDS_RU_NO_REFRESH_PRIVILGE);
errDesc_.AddArgument(mvName_);
return FALSE;
}
Lng32 refreshPattern = mv_.GetRefreshPatternMap();
BOOL isPurgedata = (0 != (refreshPattern & CRUMV::PURGEDATA));
BOOL isPopindex = (0 != (refreshPattern & CRUMV::POPINDEX));
if (TRUE == isPurgedata ||
CDDObject::eNON_INITIALIZED == mv_.GetMVStatus() ||
CDDObject::eAUDIT != mv_.GetAuditType())
{
if (FALSE == isSelectPriv || FALSE == isDeletePriv)
{
// We need select and delete privs in 3 cases :
// 1. A purgedata must be performed after a failure
// 2. The MV is not initialize and initialization is needed.
// If the initialization process will fail then the purge data
// is a must. In order to avoid entering the usr into a dead end
// we require at the begining of the process all the might needed
// privileges
// 3. A No audit on refresh MV is refreshed. In this case any failure
// in the refresh process will demand that in the following refresh
// a purgedata will be executed and the mv will be recomputed
errDesc_.SetError(IDS_RU_NO_PURGEDATA_PRIVILEGE);
errDesc_.AddArgument(mvName_);
return FALSE;
}
}
if (TRUE == isPopindex
&&
FALSE == isSelectPriv)
{
// Index population is a must, not an optimization
RUASSERT(CDDObject::eNON_INITIALIZED == mv_.GetMVStatus());
errDesc_.SetError(IDS_RU_NO_POPINDEX_PRIVILEGE);
errDesc_.AddArgument(mvName_);
return FALSE;
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckUsedObjectsInitialization()
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckUsedObjectsInitialization()
{
CRUTblList &tblList = mv_.GetTablesUsedByMe();
DSListPosition pos = tblList.GetHeadPosition();
while (NULL != pos)
{
CRUTbl *pTbl = tblList.GetNext(pos);
if (FALSE == pTbl->IsInitialized()
&&
FALSE == pTbl->IsInvolvedMV())
{
// If a used object is regular table, it is always initialized.
// The check is only relevant for a table's implementation by an MV.
errDesc_.SetError(IDS_RU_USED_MV_UNINITIALIZED);
errDesc_.AddArgument(mvName_);
return FALSE;
}
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckUsedTablesNoLockOnRefresh()
//
// The user can recompute an ON REQUEST/ON STATEMENT MV
// only if it is NOT based on NO LOCKONREFRESH tables.
//
// This limitation follows from the need to lock the table
// during the incremental MV's recompute (see CRUTbl::IsLongLockNeeded()),
// which contradicts the NO LOCKONREFRESH attribute.
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckUsedTablesNoLockOnRefresh()
{
if (FALSE == mv_.WillBeRecomputed()
||
CDDObject::eRECOMPUTE == mv_.GetRefreshType())
{
return TRUE;
}
CRUTblList &tblList = mv_.GetTablesUsedByMe();
DSListPosition pos = tblList.GetHeadPosition();
while (NULL != pos)
{
CRUTbl *pTbl = tblList.GetNext(pos);
if (TRUE == pTbl->IsNoLockOnRefresh() )
{
errDesc_.SetError(IDS_RU_RECOMPUTE_NOLOCKONREFRESH);
errDesc_.AddArgument(mvName_);
return FALSE;
}
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckConsistency()
//
// - ON REQUEST and RECOMPUTE MVs that are based on
// more than one MV cannot be refreshed standalone.
// - RECOMPUTED MV based on some other MV cannot have
// any other object beneath it.
// - ON REQUEST MV based on some other MV must observe
// empty table-deltas (this check can be done only in
// runtime, after the EmpCheck task).
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckConsistency()
{
if (CRUOptions::SINGLE_MV !=
CRUGlobals::GetInstance()->GetOptions().GetInvocType())
{
return TRUE; // Check irrelevant for group refresh
}
Int32 nNonFullySyncTablesUsedByMe =
mv_.GetTablesUsedByMe().GetCount()
-
mv_.GetFullySyncTablesUsedByMe().GetCount();
switch (nNonFullySyncTablesUsedByMe)
{
case 0:
{
// The MV is based only on regular tables.
// No consistency check is required whatsoever.
return TRUE;
}
case 1:
{
return CheckConsistencyIfSingleMVUsed();
}
default: // More than one directly used MV
{
// ON REQUEST and RECOMPUTE MVs that are based on more
// than one MV non-ON-STATEMENT MV cannot be
// refreshed standalone (in a single-MV refresh)
errDesc_.SetError(IDS_RU_INCONSISTENT_JOIN);
errDesc_.AddArgument(mvName_);
return FALSE;
}
}
// Just make the compiler happy
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckConsistencyIfSingleMVUsed()
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckConsistencyIfSingleMVUsed()
{
// If the MV is single-delta (no base tables, or all of
// them are IGNORE CHANGES), no additional checks are required.
if (TRUE == mv_.IsDeclarativeSingleDeltaMV())
{
return TRUE;
}
// If the MV is recomputed (for whatever reason),
// there is no emptiness check - no way to check consistency
if (TRUE == mv_.WillBeRecomputed())
{
errDesc_.SetError(IDS_RU_INCONSISTENT_JOIN);
errDesc_.AddArgument(mvName_);
return FALSE;
}
// The decision is delayed to runtime,
// The Refresh task executor must verify
// that the regular tables' deltas are empty for this MV.
CRUTblList &tblList = mv_.GetFullySyncTablesUsedByMe();
DSListPosition pos = tblList.GetHeadPosition();
while (NULL != pos)
{
CRUTbl *pTbl = tblList.GetNext(pos);
pTbl->SetEmptyDeltaNeeded();
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckUsedTablesAuditing()
//
// An incremental MV cannot be refreshed as long as at least
// one REGULAR table used by it is non-audited.
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckUsedTablesAuditing()
{
if (CDDObject::eRECOMPUTE == mv_.GetRefreshType())
{
return TRUE;
}
CRUTblList &tblList = mv_.GetTablesUsedByMe();
DSListPosition pos = tblList.GetHeadPosition();
while (NULL != pos)
{
CRUTbl *pTbl = tblList.GetNext(pos);
if (TRUE == pTbl->IsRegularTable()
&&
FALSE == pTbl->IsAudited())
{
errDesc_.SetError(IDS_RU_TABLE_UNAUDITED);
errDesc_.AddArgument(mv_.GetFullName());
errDesc_.AddArgument(pTbl->GetFullName());
return FALSE;
}
}
return TRUE;
}
//--------------------------------------------------------------------------//
// CRUPreRuntimeCheck::CheckNonAvailableMVRecompute()
//
// If a non-available non-audited MV is recomputed,
// the user must specify the RECOMPUTE option explicitly.
//
//--------------------------------------------------------------------------//
BOOL CRUPreRuntimeCheck::CheckNonAvailableMVRecompute()
{
if (CDDMV::eUNAVAILABLE == mv_.GetMVStatus())
{
RUASSERT(TRUE == mv_.WillBeRecomputed());
if (FALSE ==
CRUGlobals::GetInstance()->GetOptions().IsRecompute())
{
errDesc_.SetError(IDS_RU_UNAVAILABLE_RECOMPUTE);
errDesc_.AddArgument(mvName_);
return FALSE;
}
}
return TRUE;
}