blob: 07cf029ce98a3331da3049b8ce56997e25e116b6 [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: RUObject.cpp
* Description: Implementation of class CRUObject
*
* Created: 03/13/2000
* Language: C++
*
*
******************************************************************************
*/
#include "ddlock.h"
#include "RuObject.h"
#include "RuSQLComposer.h"
#include "CatSQLShare.h"
//--------------------------------------------------------------------------//
// Globals
//--------------------------------------------------------------------------//
CDSString const CRUObject::DDLLockSuffix = "__REFRESH__DDLLOCK";
//--------------------------------------------------------------------------//
// Constructor
//--------------------------------------------------------------------------//
CRUObject::CRUObject() :
isInvolved_(FALSE),
isDDLLockPending_(FALSE),
canReleaseDDLLock_(TRUE),
ex_()
{}
//--------------------------------------------------------------------------//
// CRUObject::HandleDDLLockInCache()
//
// CRUCache::HandleDDLLock() callee.
//
// Handle (acquire/replace/cancel) the DDL lock associated with an
// involved MV or table, in order to provide mutual exclusion from
// the concurrent DDL and utility operations.
//
// If the utility is applied with the CANCEL option, it will only try
// to cancel the DDL lock associated with the object. If there is none,
// the operation is simply skipped.
//
// Otherwise, it will try to cancel the DDL lock (if exists), and to
// create its own one. If both operations are non-empty and succeed,
// this is actually a replacement of a stale DDL lock.
//--------------------------------------------------------------------------//
void CRUObject::HandleDDLLockInCache(BOOL isCancelOnly)
{
// Nothing is supposed to go wrong so far...
RUASSERT(0 == GetStatus());
// Drop the previously existing DDL lock (if there was one)
CancelDDLLock(isCancelOnly);
if (TRUE == isCancelOnly // No more work
||
0 != GetStatus()) // Something went wrong, do not continue
{
return;
}
RUASSERT(TRUE == GetDDLLockList()->IsEmpty());
// Create a new DDL lock
CreateDDLLock();
}
//--------------------------------------------------------------------------//
// CRUObject::ReleaseDDLLock()
//
// Called (indirectly) by CRURcReleaseTaskExecutor::Work().
//
// Drop the DDL lock that was created at the cache building stage.
//
//--------------------------------------------------------------------------//
void CRUObject::ReleaseDDLLock()
{
// The DDL lock's drop is done through the DDOL Save() API.
// If some DDL operation on this object failed at an earlier stage,
// the DDOL object is contaminated, and Save() will fail.
// Reset the modify flag on the object.
CancelChanges();
// Verify that there is a single DDL lock ...
CDDLockList *pLockList = GetDDLLockList();
RUASSERT(1 == pLockList->GetCount());
// And we have put it
CDDLock *pLock = pLockList->GetAt(0);
RUASSERT(CDDObject::eREFRESH == pLock->GetOperation());
#ifdef _DEBUG
CDSString msg(
"\nDDL lock released: " + pLock->GetName() + "\n" );
CRUGlobals::GetInstance()->
LogDebugMessage(CRUGlobals::DUMP_DDL_LOCKS,"",msg);
#endif
// something very bad went wrong, just raise an error and quit
if (!pLock)
{
SetDDLLockError(IDS_RU_DDLOCK_RELEASE_IMPOSSIBLE);
return;
}
// Go !
DropDDLLock(pLock->GetName());
SaveMetadata();
isDDLLockPending_ = FALSE;
}
//--------------------------------------------------------------------------//
// CRUObject::CancelDDLLock()
//
// CRUCache::HandleDDLLock() callee
//
// Drop the DDL lock associated with the object (if one exists).
// The lock can be dropped under the following conditions:
// (1) It is a SINGLE DDL lock on this object.
// (2) It was cast by some earlier invocation of REFRESH.
// (3) The Catman process that has aquired it is not running.
// (4) If the utility does not mean to re-acquire this lock
// (i.e., it is invoked with the CANCEL option), this
// operation must be legal (for example, a DDL lock cannot
// be dropped from an MV in the non-available state).
//
//--------------------------------------------------------------------------//
void CRUObject::CancelDDLLock(BOOL isCancelOnly)
{
RUASSERT(FALSE == IsDDLLockPending());
CDDLockList *pLockList = GetDDLLockList();
switch (pLockList->GetCount())
{
case 0:
{
return; // Nothing to cancel
}
case 1:
{
CDDLock *pLock = pLockList->GetAt(0);
if (CDDObject::eREFRESH != pLock->GetOperation())
{
// Condition #2
SetDDLLockError(IDS_RU_FOREIGN_DDLLOCK);
break;
}
if (isRunning == // CatSQLShare enum
CatProcessIsRunning(pLock->GetProcessId()))
{
// Condition #3
SetDDLLockError(IDS_RU_DDLLOCK_PROC_IS_RUNNING);
break;
}
if (TRUE == isCancelOnly
&&
FALSE == CanReleaseDDLLock())
{
// Condition #4
SetDDLLockError(IDS_RU_DDLOCK_CANCEL_IMPOSSIBLE);
break;
}
// Go!
DropDDLLock(pLock->GetName());
SaveDDLLockOperation();
break;
}
default:
{
// Condition #1.
// Cannot handle multiple DDL locks.
// Maybe this should never happen? Assertion?
SetDDLLockError(IDS_RU_MULTI_DDLLOCKS);
break;
}
}
}
//--------------------------------------------------------------------------//
// CRUObject::CreateDDLLock()
//
// Create a new DDL lock, using the following naming convention:
// <object-name>__REFRESH__DDLLOCK.
//
// WARNING! If shared DDL locks will be supported once, the naming
// convention must be more sophisticated.
//
//--------------------------------------------------------------------------//
void CRUObject::CreateDDLLock()
{
// Set up dummy (default) parameter values
CDDDetailTextList details;
CDSLocationList *pLocationList = NULL;
Int32 status = 0;
CreateDDLLock(details, pLocationList, status);
}
void CRUObject::CreateDDLLock(const CDDDetailTextList &details,
CDSLocationList *pLocationList,
Int32 status)
{
// Write through the DDOL
CDDLock *pLock = AddDDLLock("", CDDObject::eREFRESH, details, pLocationList, status);
SaveDDLLockOperation();
#ifdef _DEBUG
CDSString msg(
"\nDDL lock created: " + pLock->GetName() + "\n" );
CRUGlobals::GetInstance()->
LogDebugMessage(CRUGlobals::DUMP_DDL_LOCKS,"",msg);
#endif
if (0 == GetStatus())
{
isDDLLockPending_ = TRUE;
}
}
//--------------------------------------------------------------------------//
// CRUObject::SaveDDLLockOperation()
//
// Apply the CatApi request (through the DDOL) to save the
// creation/drop of DDL lock to the catalog.
//
// The call to SaveMetadata() is enclosed in a try ... catch
// clause, which is meant to idenitify the deadlock of the
// following type during the concurrent build of cache in two
// invocations of the utility:
//
// (1) Invocation A of Refresh performs a serializable read
// of the row that corresponds to the object from the Objects
// SMD table (basic DDOL mechanism).
// (2) Invocation B performs the same read.
// (3) Both invocations eventually try to put a DDL lock
// on the object - and enter a deadlock.
//
// The deadlock will finally result in a timeout. While the utility
// does not prevent this conflict - it prevents that the whole cache
// construction will crash because of it.
//
//--------------------------------------------------------------------------//
void CRUObject::SaveDDLLockOperation()
{
RUASSERT(0 == GetStatus());
try
{
SaveMetadata();
}
catch (CDSException &ex)
{
ex_ = ex; // Store the exception
}
}