You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
706 lines
17 KiB
706 lines
17 KiB
// ***************************************************************************
|
|
// Copyright (C) 2000- Microsoft Corporation.
|
|
// @File: vdifreeze.cpp
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
// Use a coordinated VDI BACKUP WITH SNAPSHOT (SQL2000 and above)
|
|
//
|
|
// NOTES:
|
|
// We'll now support rollforward from the snapshot by providing the
|
|
// BACKUP metadata in the BackupComponentsDocument.
|
|
//
|
|
// The VDI method of freeze/thaw avoids the potential resource deadlock
|
|
// which prevents SQLServer from accepting a "dbcc thaw_io" when one or more
|
|
// databases is frozen.
|
|
//
|
|
// Extern dependencies:
|
|
// provision of "_Module" and the COM guids....
|
|
//
|
|
// HISTORY:
|
|
//
|
|
// @Version: Whistler/Shiloh
|
|
// 85581 SRS 08/15/01 Event security
|
|
// 76910 SRS 08/08/01 Rollforward from VSS snapshot
|
|
// 68228 12/05/00 ntsnap work
|
|
// 68202 11/07/00 ntsnap work
|
|
//
|
|
// @EndHeader@
|
|
// ***************************************************************************
|
|
|
|
|
|
#if DISABLE_4786
|
|
#pragma warning( disable : 4786)
|
|
#endif
|
|
|
|
#include <stdafx.h>
|
|
|
|
#include "vdierror.h"
|
|
#include "vdiguid.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "SQLVFRZC"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
Freeze2000::Freeze2000 (
|
|
const WString& serverName,
|
|
ULONG maxDatabases) :
|
|
m_ServerName (serverName),
|
|
m_MaxDatabases (maxDatabases),
|
|
m_NumDatabases (0),
|
|
m_State (Unprepared),
|
|
m_AbortCount (0)
|
|
{
|
|
CVssFunctionTracer(VSSDBG_SQLLIB, L"Freeze2000::Freeze2000");
|
|
|
|
m_pDBContext = new FrozenDatabase [maxDatabases];
|
|
CoCreateGuid (&m_BackupId);
|
|
try
|
|
{
|
|
InitializeCriticalSection (&m_Latch);
|
|
}
|
|
catch(...)
|
|
{
|
|
// delete created object if we fail InitializeCriticalSection
|
|
delete m_pDBContext;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------
|
|
// Wait for all the database threads to terminate.
|
|
// This is only called by the coordinating thread while
|
|
// holding exclusive access on the object.
|
|
//
|
|
void
|
|
Freeze2000::WaitForThreads ()
|
|
{
|
|
CVssFunctionTracer(VSSDBG_SQLLIB, L"Freeze2000::WaitForThreads");
|
|
|
|
for (UINT i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
if (pDb->m_hThread != NULL)
|
|
{
|
|
DWORD status;
|
|
do
|
|
{
|
|
status = WaitForSingleObjectEx (pDb->m_hThread, 2000, TRUE);
|
|
|
|
if (m_State != Aborted && CheckAbort ())
|
|
Abort ();
|
|
|
|
} while (status != WAIT_OBJECT_0);
|
|
|
|
CloseHandle (pDb->m_hThread);
|
|
pDb->m_hThread = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Handle an abort.
|
|
// The main thread will already hold the the lock and so
|
|
// will always be successful at aborting the operation.
|
|
// The database threads will attempt to abort, but won't
|
|
// block in order to do so. The abort count is incremented
|
|
// and the main thread is ulimately responsible for cleanup.
|
|
//
|
|
void
|
|
Freeze2000::Abort () throw ()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Abort");
|
|
|
|
SetAbort ();
|
|
if (TryLock ())
|
|
{
|
|
m_State = Aborted;
|
|
for (UINT i=0; i<m_NumDatabases; i++)
|
|
{
|
|
if (m_pDBContext[i].m_pIVDSet)
|
|
{
|
|
m_pDBContext[i].m_pIVDSet->SignalAbort ();
|
|
m_pDBContext[i].m_pIVDSet->Close ();
|
|
m_pDBContext[i].m_pIVDSet->Release ();
|
|
m_pDBContext[i].m_pIVDSet = NULL;
|
|
m_pDBContext[i].m_pIVD = NULL;
|
|
}
|
|
}
|
|
Unlock ();
|
|
}
|
|
}
|
|
|
|
Freeze2000::~Freeze2000 ()
|
|
{
|
|
Lock ();
|
|
|
|
if (m_State != Complete)
|
|
{
|
|
// Trigger any waiting threads, cleaning up any VDI's.
|
|
//
|
|
Abort ();
|
|
|
|
WaitForThreads ();
|
|
}
|
|
|
|
delete[] m_pDBContext;
|
|
DeleteCriticalSection (&m_Latch);
|
|
}
|
|
|
|
//-------------------------------------------
|
|
// Map the voids and proc call stuff to the real
|
|
// thread routine.
|
|
//
|
|
DWORD WINAPI FreezeThreadProc(
|
|
LPVOID lpParameter ) // thread data
|
|
{
|
|
return Freeze2000::DatabaseThreadStart (lpParameter);
|
|
}
|
|
|
|
DWORD Freeze2000::DatabaseThreadStart (
|
|
LPVOID lpParameter ) // thread data
|
|
{
|
|
FrozenDatabase* pDbContext = (FrozenDatabase*)lpParameter;
|
|
return pDbContext->m_pContext->DatabaseThread (pDbContext);
|
|
}
|
|
|
|
//-------------------------------------------
|
|
// Add a database to the freeze set.
|
|
//
|
|
void
|
|
Freeze2000::PrepareDatabase (
|
|
const WString& dbName)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Free2000::PrepareDatabase");
|
|
|
|
// can't backup tempdb!
|
|
//
|
|
if (dbName == L"tempdb")
|
|
return;
|
|
|
|
Lock ();
|
|
|
|
try
|
|
{
|
|
if (m_State == Unprepared)
|
|
{
|
|
m_State = Preparing;
|
|
}
|
|
|
|
if (m_NumDatabases >= m_MaxDatabases ||
|
|
m_State != Preparing)
|
|
{
|
|
DBG_ASSERT(FALSE && L"Too many databases or not preparing");
|
|
THROW_GENERIC;
|
|
}
|
|
|
|
FrozenDatabase* pDbContext = m_pDBContext+m_NumDatabases;
|
|
m_NumDatabases++;
|
|
|
|
pDbContext->m_pContext = this;
|
|
|
|
#ifdef TESTDRV
|
|
ft.hr = CoCreateInstance (
|
|
CLSID_MSSQL_ClientVirtualDeviceSet,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IClientVirtualDeviceSet2,
|
|
(void**)&pDbContext->m_pIVDSet);
|
|
#else
|
|
ft.CoCreateInstanceWithLog(
|
|
VSSDBG_SQLLIB,
|
|
CLSID_MSSQL_ClientVirtualDeviceSet,
|
|
L"MSSQL_ClientVirtualDeviceSet",
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IClientVirtualDeviceSet2,
|
|
(IUnknown**)&(pDbContext->m_pIVDSet));
|
|
#endif
|
|
|
|
if (ft.HrFailed())
|
|
{
|
|
ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
|
|
ft.Throw
|
|
(
|
|
VSSDBG_SQLLIB,
|
|
ft.hr,
|
|
L"Failed to create VDS object. hr = 0x%08lx",
|
|
ft.hr
|
|
);
|
|
}
|
|
|
|
VDConfig config;
|
|
memset (&config, 0, sizeof(config));
|
|
config.deviceCount = 1;
|
|
config.features |= VDF_SnapshotPrepare;
|
|
|
|
StringFromGUID2 (m_BackupId, pDbContext->m_SetName, sizeof (pDbContext->m_SetName)/sizeof(WCHAR));
|
|
swprintf (pDbContext->m_SetName+wcslen(pDbContext->m_SetName), L"%d", m_NumDatabases);
|
|
|
|
// A "\" indicates a named instance; we need the "raw" instance name
|
|
//
|
|
WCHAR* pInstance = wcschr (m_ServerName.c_str (), L'\\');
|
|
|
|
if (pInstance)
|
|
{
|
|
pInstance++; // step over the separator
|
|
}
|
|
|
|
// Create the virtual device set
|
|
//
|
|
ft.hr = pDbContext->m_pIVDSet->CreateEx (pInstance, pDbContext->m_SetName, &config);
|
|
if (ft.HrFailed())
|
|
{
|
|
ft.LogError(VSS_ERROR_SQLLIB_CANTCREATEVDS, VSSDBG_SQLLIB << ft.hr);
|
|
ft.Throw
|
|
(
|
|
VSSDBG_SQLLIB,
|
|
ft.hr,
|
|
L"Failed to create VDS object. hr = 0x%08lx",
|
|
ft.hr
|
|
);
|
|
}
|
|
pDbContext->m_VDState = FrozenDatabase::Created;
|
|
pDbContext->m_DbName = dbName;
|
|
|
|
pDbContext->m_IsMaster = (dbName == L"master");
|
|
|
|
pDbContext->m_hThread = CreateThread (NULL, 0,
|
|
FreezeThreadProc, pDbContext, 0, NULL);
|
|
|
|
if (pDbContext->m_hThread == NULL)
|
|
{
|
|
ft.hr = HRESULT_FROM_WIN32(GetLastError());
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"CreateThread");
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
throw;
|
|
}
|
|
Unlock ();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Prep a database by setting up a BACKUP WITH SNAPSHOT
|
|
// Since the backup has no way to stall for the prepare, we stall it by delaying
|
|
// the VDI processing until freeze time.
|
|
// We use 1024 bytes as the block size because the MTF FLBSize is 1024,
|
|
// so we'll see fewer VD commands with no larger resulting MD size.
|
|
//
|
|
// It would be nice to add a Prepare phase to SQLServer so that the database
|
|
// checkpoint could occur during Prepare, rather than during FREEZE.
|
|
//
|
|
DWORD
|
|
Freeze2000::DatabaseThread (
|
|
FrozenDatabase* pDbContext)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_XML, L"Freeze2000::DatabaseThread");
|
|
|
|
try
|
|
{
|
|
SqlConnection sql;
|
|
sql.Connect (m_ServerName);
|
|
|
|
WCHAR delimitedName [SysNameBufferLen];
|
|
FormDelimitedIdentifier (delimitedName, pDbContext->m_DbName.c_str ());
|
|
|
|
WString command =
|
|
L"BACKUP DATABASE " + WString (delimitedName) + L" TO VIRTUAL_DEVICE='" +
|
|
pDbContext->m_SetName + L"' WITH SNAPSHOT,BUFFERCOUNT=1,BLOCKSIZE=1024";
|
|
|
|
sql.SetCommand (command);
|
|
sql.ExecCommand ();
|
|
pDbContext->m_SuccessDetected = TRUE;
|
|
}
|
|
catch (...)
|
|
{
|
|
Abort ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Advance the status of each VD.
|
|
// Will throw if problems are encountered.
|
|
//
|
|
// This routine is called in two contexts:
|
|
// 1. During the "Prepare" phase, 'targetState' is 'PreparedToFreeze'.
|
|
// The goal is to move each VD to an "PreparedToFreeze" state.
|
|
//
|
|
// 2. During the "Freeze" phase, 'targetState' is 'Frozen'.
|
|
// The remaining metadata is consumed, then the BACKUP will freeze
|
|
// the database and send the 'VDC_Snapshot' command.
|
|
//
|
|
// Master must be frozen last, so we skip pulling its VD commands until all other
|
|
// databases are complete. If master is frozen before other databases, SQLServer
|
|
// can hang.
|
|
//
|
|
void
|
|
Freeze2000::AdvanceVDState (
|
|
FrozenDatabase::VDState targetState)
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::AdvanceVDState");
|
|
bool oneDatabaseLeft = (m_NumDatabases == 1);
|
|
|
|
// Poll over the VD's moving them to the target state.
|
|
//
|
|
while (1)
|
|
{
|
|
bool didSomething = false;
|
|
int nDatabasesReady = 0;
|
|
DWORD timeOut = oneDatabaseLeft ? 1000 : 0;
|
|
|
|
for (UINT i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
|
|
if (CheckAbort ())
|
|
{
|
|
THROW_GENERIC;
|
|
}
|
|
|
|
if (pDb->m_VDState >= targetState)
|
|
{
|
|
nDatabasesReady++;
|
|
|
|
if (nDatabasesReady == m_NumDatabases)
|
|
return;
|
|
|
|
if (nDatabasesReady == m_NumDatabases-1)
|
|
{
|
|
oneDatabaseLeft = true;
|
|
}
|
|
|
|
continue; // with the next database
|
|
}
|
|
|
|
switch (pDb->m_VDState)
|
|
{
|
|
case FrozenDatabase::Created:
|
|
VDConfig config;
|
|
ft.hr = pDb->m_pIVDSet->GetConfiguration (timeOut, &config);
|
|
if (ft.hr == VD_E_TIMEOUT)
|
|
continue; // nothing to do for this database right now
|
|
|
|
if (ft.HrFailed())
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDeviceSet2::GetConfiguration");
|
|
|
|
ft.hr = pDb->m_pIVDSet->OpenDevice (pDb->m_SetName, &pDb->m_pIVD);
|
|
if (ft.HrFailed())
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDeviceSet2::OpenDevice");
|
|
|
|
pDb->m_VDState = FrozenDatabase::Open;
|
|
didSomething = true;
|
|
|
|
// fall thru
|
|
|
|
case FrozenDatabase::PreparedToFreeze:
|
|
|
|
if (pDb->m_IsMaster &&
|
|
targetState == FrozenDatabase::Frozen &&
|
|
!oneDatabaseLeft)
|
|
{
|
|
#ifdef DEBUG_PRINTF
|
|
printf ("Skipping master\n");
|
|
#endif
|
|
continue; // try another database
|
|
}
|
|
|
|
if (pDb->m_pSnapshotCmd)
|
|
{
|
|
// Acknowledge the prepare command, which lets the
|
|
// database proceed to frozen.
|
|
//
|
|
ft.hr = pDb->m_pIVD->CompleteCommand (
|
|
pDb->m_pSnapshotCmd, ERROR_SUCCESS, 0, 0);
|
|
|
|
// only attempt completion once
|
|
//
|
|
pDb->m_pSnapshotCmd = NULL;
|
|
|
|
if (ft.HrFailed())
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::CompletePrepare");
|
|
|
|
didSomething = true;
|
|
}
|
|
|
|
// fall thru
|
|
|
|
case FrozenDatabase::Open:
|
|
|
|
// pull commands until we see a "prepare" or "mountSnapshot"
|
|
//
|
|
|
|
VDC_Command * cmd;
|
|
HRESULT hr;
|
|
|
|
while (SUCCEEDED (hr=pDb->m_pIVD->GetCommand (timeOut, &cmd)))
|
|
{
|
|
DWORD completionCode;
|
|
DWORD bytesTransferred=0;
|
|
didSomething = true;
|
|
|
|
switch (cmd->commandCode)
|
|
{
|
|
case VDC_Write:
|
|
bytesTransferred = cmd->size;
|
|
pDb->m_MetaData.Append (cmd->buffer, bytesTransferred);
|
|
// fall thru
|
|
|
|
case VDC_Flush:
|
|
completionCode = ERROR_SUCCESS;
|
|
|
|
ft.hr = pDb->m_pIVD->CompleteCommand (
|
|
cmd, completionCode, bytesTransferred, 0);
|
|
if (ft.HrFailed())
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::CompleteCommand");
|
|
|
|
break;
|
|
|
|
case VDC_PrepareToFreeze:
|
|
// Record the state transition and
|
|
// save the command for completion during the freeze phase.
|
|
//
|
|
DBG_ASSERT (targetState == FrozenDatabase::PreparedToFreeze);
|
|
|
|
pDb->m_VDState = FrozenDatabase::PreparedToFreeze;
|
|
pDb->m_pSnapshotCmd = cmd;
|
|
|
|
#ifdef DEBUG_PRINTF
|
|
printf ("DBPrepared: %ls\n", pDb->m_DbName.c_str ());
|
|
#endif
|
|
continue;
|
|
|
|
case VDC_Snapshot:
|
|
// Record the state transition and
|
|
// save the command for completion during the thaw phase.
|
|
//
|
|
// Running with SQL2000 prior to SP2 will hit this assert.
|
|
// We'll need to tell customers to use SP2 with Whistler snapshots.
|
|
// The effect is that the databases will freeze during the prepare
|
|
// phase, thus the freeze interval is longer than needed.
|
|
//
|
|
//DBG_ASSERT (targetState == FrozenDatabase::Frozen);
|
|
|
|
pDb->m_VDState = FrozenDatabase::Frozen;
|
|
pDb->m_pSnapshotCmd = cmd;
|
|
|
|
#ifdef DEBUG_PRINTF
|
|
printf ("DBFrozen: %ls\n", pDb->m_DbName.c_str ());
|
|
#endif
|
|
continue;
|
|
|
|
default:
|
|
ft.Trace(VSSDBG_SQLLIB, L"Unexpected VDCmd: x%x\n", cmd->commandCode);
|
|
THROW_GENERIC;
|
|
} // end command switch
|
|
} // end command loop
|
|
|
|
ft.hr = hr;
|
|
|
|
if (ft.hr == VD_E_TIMEOUT)
|
|
break; // no command was ready.
|
|
if (ft.HrFailed())
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::GetCommand");
|
|
|
|
break;
|
|
|
|
default:
|
|
DBG_ASSERT(FALSE && L"Shouldn't get here");
|
|
THROW_GENERIC;
|
|
} // end switch to handle this db
|
|
} // end loop over each db
|
|
|
|
// Unless we found something to do,
|
|
// delay a bit and try again.
|
|
//
|
|
if (!didSomething && !oneDatabaseLeft)
|
|
{
|
|
#ifdef DEBUG_PRINTF
|
|
printf ("Sleep(100)\n");
|
|
#endif
|
|
|
|
SleepEx (100, TRUE);
|
|
}
|
|
|
|
} // wait for all databases to go "Ready"
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Wait for the databases to finish preparing.
|
|
// This waits for the virtual devices to open up
|
|
//
|
|
void
|
|
Freeze2000::WaitForPrepare ()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::WaitForPrepare");
|
|
|
|
Lock ();
|
|
if (m_State != Preparing || CheckAbort ())
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
THROW_GENERIC;
|
|
}
|
|
m_State = Prepared;
|
|
try
|
|
{
|
|
AdvanceVDState (FrozenDatabase::PreparedToFreeze);
|
|
}
|
|
catch (...)
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
throw;
|
|
}
|
|
Unlock ();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
// Perform the freeze, waiting for a "Take-snapshot" from each db.
|
|
//
|
|
void
|
|
Freeze2000::Freeze ()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Freeze");
|
|
|
|
Lock ();
|
|
if (m_State != Prepared || CheckAbort ())
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
THROW_GENERIC;
|
|
}
|
|
|
|
try
|
|
{
|
|
m_State = Frozen;
|
|
AdvanceVDState (FrozenDatabase::Frozen);
|
|
}
|
|
catch (...)
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
throw;
|
|
}
|
|
Unlock ();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------
|
|
// Perform the thaw.
|
|
//
|
|
// Return TRUE if the databases were all successfully backed up
|
|
// and were thawed out as expected.
|
|
// FALSE is returned in any other case.
|
|
// No exceptions are thrown (this routine can be used as a cleanup routine).
|
|
//
|
|
BOOL
|
|
Freeze2000::Thaw () throw ()
|
|
{
|
|
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Freeze2000::Thaw");
|
|
|
|
Lock ();
|
|
|
|
if (m_State != Frozen || CheckAbort ())
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
return FALSE;
|
|
}
|
|
|
|
try
|
|
{
|
|
|
|
// Send the "snapshot complete" messages.
|
|
//
|
|
UINT i;
|
|
|
|
for (i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
|
|
DBG_ASSERT (pDb->m_VDState == FrozenDatabase::Frozen);
|
|
ft.hr = pDb->m_pIVD->CompleteCommand (pDb->m_pSnapshotCmd, ERROR_SUCCESS, 0, 0);
|
|
if (FAILED (ft.hr))
|
|
ft.CheckForError(VSSDBG_SQLLIB, L"IClientVirtualDevice::CompleteCommand");
|
|
}
|
|
|
|
// Wait for the BACKUP threads to report success.
|
|
//
|
|
WaitForThreads ();
|
|
|
|
for (i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
|
|
if (!pDb->m_SuccessDetected)
|
|
{
|
|
THROW_GENERIC;
|
|
}
|
|
}
|
|
|
|
// Pull the "close" message from each VD
|
|
//
|
|
for (i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
VDC_Command * cmd;
|
|
ft.hr=pDb->m_pIVD->GetCommand (INFINITE, &cmd);
|
|
if (ft.hr != VD_E_CLOSE)
|
|
ft.LogError(VSS_ERROR_SQLLIB_FINALCOMMANDNOTCLOSE, VSSDBG_SQLLIB << ft.hr);
|
|
|
|
pDb->m_pIVDSet->Close ();
|
|
pDb->m_pIVDSet->Release ();
|
|
pDb->m_pIVDSet = NULL;
|
|
pDb->m_pIVD = NULL;
|
|
|
|
pDb->m_MetaData.Finalize ();
|
|
}
|
|
|
|
m_State = Complete;
|
|
}
|
|
catch (...)
|
|
{
|
|
Abort ();
|
|
Unlock ();
|
|
return FALSE;
|
|
}
|
|
Unlock ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// Fetch the metadata for a database
|
|
//
|
|
const BYTE*
|
|
Freeze2000::GetMetaData (
|
|
const WString& dbName,
|
|
UINT *pLength) // return area for data length.
|
|
{
|
|
UINT i;
|
|
for (i=0; i<m_NumDatabases; i++)
|
|
{
|
|
FrozenDatabase* pDb = m_pDBContext+i;
|
|
|
|
if (pDb->m_DbName == dbName)
|
|
{
|
|
return pDb->m_MetaData.GetImage (pLength);
|
|
}
|
|
}
|
|
*pLength = 0;
|
|
return NULL;
|
|
}
|
|
|