Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1065 lines
24 KiB

// ***************************************************************************
// Copyright (C) 2000- Microsoft Corporation.
// @File: snapsql.cpp
//
// PURPOSE:
//
// Implement the SQLServer Volume Snapshot Writer.
//
// NOTES:
//
//
// HISTORY:
//
// @Version: Whistler/Shiloh
// 66601 srs 10/05/00 NTSNAP improvements
//
//
// @EndHeader@
// ***************************************************************************
#if HIDE_WARNINGS
#pragma warning( disable : 4786)
#endif
#include <stdafx.h>
#include <new.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 "SQLSNAPC"
//
////////////////////////////////////////////////////////////////////////
int __cdecl out_of_store(size_t size)
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"out_of_store");
ft.Trace(VSSDBG_SQLLIB, L"out of memory");
throw HRESULT (E_OUTOFMEMORY);
return 0;
}
class AutoNewHandler
{
public:
AutoNewHandler ()
{
m_oldHandler = _set_new_handler (out_of_store);
}
~AutoNewHandler ()
{
_set_new_handler (m_oldHandler);
}
private:
_PNH m_oldHandler;
};
//-------------------------------------------------------------------------
// Handle enviroment stuff:
// - tracing/error logging
// - mem alloc
//
IMalloc * g_pIMalloc = NULL;
HRESULT
InitSQLEnvironment()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"InitSqlEnvironment");
try
{
ft.hr = CoGetMalloc(1, &g_pIMalloc);
if (ft.HrFailed())
ft.Trace(VSSDBG_SQLLIB, L"Failed to get task allocator: hr=0x%X", ft.hr);
}
catch (...)
{
ft.hr = E_SQLLIB_GENERIC;
}
return ft.hr;
}
//-------------------------------------------------------------------------
// Return TRUE if the server is online and a connection shouldn't take forever!
//
BOOL
IsServerOnline (const WCHAR* serverName)
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"IsServerOnline");
WCHAR eventName [300];
WCHAR* pInstance;
wcscpy (eventName, L"Global\\sqlserverRecComplete");
// A "\" indicates a named instance, so append the name...
//
pInstance = wcschr (serverName, L'\\');
if (pInstance)
{
wcscat (eventName, L"$");
wcscat (eventName, pInstance+1);
}
HANDLE hEvent = CreateEventW (NULL, TRUE, FALSE, eventName);
if (hEvent == NULL)
{
ft.hr = HRESULT_FROM_WIN32(GetLastError());
ft.LogError(VSS_ERROR_SQLLIB_CANT_CREATE_EVENT, VSSDBG_SQLLIB << ft.hr);
THROW_GENERIC;
}
// If the event isn't signaled, the server is not up.
//
BOOL result = (WaitForSingleObject (hEvent, 0) == WAIT_OBJECT_0);
CloseHandle (hEvent);
return result;
}
//-------------------------------------------------------------------------
// Return TRUE if the database properties are retrieved:
// simple: TRUE if using the simple recovery model.
// online: TRUE if the database is usable and currently open
//
void
FrozenServer::GetDatabaseProperties (const WString& dbName,
BOOL* pSimple,
BOOL* pOnline)
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::GetDatabaseProperties");
// We use status bit 0x40000000 (1073741824) to identify
// clean-shutdown databases which are "offline".
//
WString query =
L"select databaseproperty(N'" + dbName + L"','IsTruncLog'),"
L"case status & 1073741824 "
L"when 1073741824 then 0 "
L"else 1 end "
L"from master..sysdatabases where name = N'" + dbName + L"'";
m_Connection.SetCommand (query);
m_Connection.ExecCommand ();
if (!m_Connection.FetchFirst ())
{
LPCWSTR wsz = dbName.c_str();
ft.LogError(VSS_ERROR_SQLLIB_DATABASE_NOT_IN_SYSDATABASES, VSSDBG_SQLLIB << wsz);
THROW_GENERIC;
}
*pSimple = (BOOL)(*(int*)m_Connection.AccessColumn (1));
*pOnline = (BOOL)(*(int*)m_Connection.AccessColumn (2));
}
//------------------------------------------------------------------------------
// Called only by "FindDatabasesToFreeze" to implement a smart access strategy:
// - use sysaltfiles to qualify the databases.
// This avoids access to shutdown or damaged databases.
//
// Autoclose databases which are not started are left out of the freeze-list.
// We do this to avoid scaling problems, especially on desktop systems.
// However, such db's are still evaluated to see if they are "torn".
//
// The 'model' database is allowed to be a full recovery database, since only
// database backups are sensible for it.
//
BOOL
FrozenServer::FindDatabases2000 (
CCheckPath* checker)
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabases2000");
// Create an ordered set of tuples (dbname, filename, simpleRecovery, dbIsActive)
//
// We use status bit 0x40000000 (1073741824) to identify
// clean-shutdown databases which are not active.
//
m_Connection.SetCommand (
L"select db_name(af.dbid),rtrim(af.filename), "
L"case databasepropertyex(db_name(af.dbid),'recovery') "
L"when 'SIMPLE' then 1 "
L"else 0 end,"
L"case databasepropertyex(db_name(af.dbid),'Status') "
L"when 'ONLINE' then case db.status & 1073741824 "
L"when 1073741824 then 0 "
L"else 1 end "
L"else 0 end "
L"from master..sysaltfiles af, master..sysdatabases db "
L"where af.dbid = db.dbid and af.dbid != db_id('tempdb') "
L"order by af.dbid"
);
m_Connection.ExecCommand ();
WCHAR* pDbName;
WCHAR* pFileName;
int* pSimple;
int* pIsOnline;
WString currentDbName;
BOOL firstDb = TRUE;
BOOL firstFile;
BOOL shouldFreeze = FALSE;
BOOL masterLast = FALSE;
BOOL currDbIsOnline;
if (!m_Connection.FetchFirst ())
{
ft.LogError(VSS_ERROR_SQLLIB_SYSALTFILESEMPTY, VSSDBG_SQLLIB);
THROW_GENERIC;
}
pDbName = (WCHAR*)m_Connection.AccessColumn (1);
pFileName = (WCHAR*)m_Connection.AccessColumn (2);
pSimple = (int*)m_Connection.AccessColumn (3);
pIsOnline = (int*)m_Connection.AccessColumn (4);
while (1)
{
// Check out the current row
//
BOOL fileInSnap = checker->IsPathInSnapshot (pFileName);
if (fileInSnap && !*pSimple && wcscmp (L"model", pDbName))
{
ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << pDbName);
throw HRESULT (E_SQLLIB_NONSIMPLE);
}
// Is this the next database?
//
if (firstDb || currentDbName.compare (pDbName))
{
if (!firstDb)
{
// Deal with completed database
//
if (shouldFreeze && currDbIsOnline)
{
if (currentDbName == L"master")
{
masterLast = TRUE;
}
else
{
m_FrozenDatabases.push_back (currentDbName);
}
}
}
// Keep info about the newly encountered db
//
currentDbName = WString (pDbName);
currDbIsOnline = *pIsOnline;
firstFile = TRUE;
firstDb = FALSE;
ft.Trace(VSSDBG_SQLLIB, L"Examining %s. SimpleRecovery:%d Online:%d\n", pDbName, *pSimple, *pIsOnline);
}
ft.Trace(VSSDBG_SQLLIB, L"%s\n", pFileName);
if (firstFile)
{
shouldFreeze = fileInSnap;
firstFile = FALSE;
}
else
{
if (shouldFreeze ^ fileInSnap)
{
ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB);
throw HRESULT (E_SQLLIB_TORN_DB);
}
}
if (!m_Connection.FetchNext ())
{
// Deal with the current database
//
if (shouldFreeze && currDbIsOnline)
{
m_FrozenDatabases.push_back (currentDbName);
}
break;
}
}
if (masterLast)
{
m_FrozenDatabases.push_back (L"master");
}
return m_FrozenDatabases.size () > 0;
}
//------------------------------------------------------------------------------
// Determine if there are databases which qualify for a freeze on this server.
// Returns TRUE if so.
// Throws if any qualified databases are any of:
// - "torn" (not fully covered by the snapshot)
// - hosted by a server which can't support freeze
// - are not "simple" databases
//
BOOL
FrozenServer::FindDatabasesToFreeze (
CCheckPath* checker)
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabasesToFreeze");
m_Connection.Connect (m_Name);
m_FrozenDatabases.clear ();
if (m_Connection.GetServerVersion () > 7)
{
// SQL2000 allows us to use a better access strategy.
//
return FindDatabases2000 (checker);
}
m_Connection.SetCommand (L"select name from sysdatabases where name != 'tempdb'");
m_Connection.ExecCommand ();
std::auto_ptr<StringVector> dbList (m_Connection.GetStringColumn ());
BOOL masterLast = FALSE;
for (StringVectorIter i = dbList->begin (); i != dbList->end (); i++)
{
// UNDONE: SKIP OVER DB'S in SHUTDOWN DB'S?
// DB'S IN LOAD, ETC?
// We'll avoid freezing shutdown db's, but we don't avoid
// enumerating their files (they might be torn)
//
// Note the [] around the dbname to handle non-trivial dbnames.
//
WString command = L"select rtrim(filename) from [";
command += *i + L"]..sysfiles";
m_Connection.SetCommand (command);
try
{
m_Connection.ExecCommand ();
}
catch (...)
{
// We've decided to be optimistic:
// If we can't get the list of files, ignore this database.
//
ft.Trace(VSSDBG_SQLLIB, L"Failed to get db files for %s\n", i->c_str ());
continue;
}
std::auto_ptr<StringVector> fileList (m_Connection.GetStringColumn ());
BOOL first=TRUE;
BOOL shouldFreeze;
for (StringVectorIter iFile = fileList->begin ();
iFile != fileList->end (); iFile++)
{
BOOL fileInSnap = checker->IsPathInSnapshot (iFile->c_str ());
if (first)
{
shouldFreeze = fileInSnap;
}
else
{
if (shouldFreeze ^ fileInSnap)
{
ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB << i->c_str());
throw HRESULT (E_SQLLIB_TORN_DB);
}
}
}
if (shouldFreeze)
{
BOOL simple, online;
GetDatabaseProperties (i->c_str (), &simple, &online);
if (!simple && L"model" != *i)
{
ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << i->c_str ());
throw HRESULT (E_SQLLIB_NONSIMPLE);
}
if (online)
{
if (L"master" == *i)
{
masterLast = TRUE;
}
else
{
m_FrozenDatabases.push_back (*i);
}
}
}
}
if (masterLast)
{
m_FrozenDatabases.push_back (L"master");
}
return m_FrozenDatabases.size () > 0;
}
//-------------------------------------------------------------------
// Prep the server for the freeze.
// For SQL2000, start a BACKUP WITH SNAPSHOT.
// For SQL7, issuing checkpoints to each database.
// This minimizes the recovery processing needed when the snapshot is restored.
//
BOOL
FrozenServer::Prepare ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Prepare");
if (m_Connection.GetServerVersion () > 7)
{
m_pFreeze2000 = new Freeze2000 (m_Name, m_FrozenDatabases.size ());
// Release the connection, we won't need it anymore
//
m_Connection.Disconnect ();
for (StringListIter i=m_FrozenDatabases.begin ();
i != m_FrozenDatabases.end (); i++)
{
m_pFreeze2000->PrepareDatabase (*i);
}
m_pFreeze2000->WaitForPrepare ();
}
else
{
WString command;
for (StringListIter i=m_FrozenDatabases.begin ();
i != m_FrozenDatabases.end (); i++)
{
command += L"use [" + *i + L"]\ncheckpoint\n";
}
m_Connection.SetCommand (command);
m_Connection.ExecCommand ();
}
return TRUE;
}
//---------------------------------------------
// Freeze the server by issuing freeze commands
// to each database.
// Returns an exception if any failure occurs.
//
BOOL
FrozenServer::Freeze ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Freeze");
if (m_pFreeze2000)
{
m_pFreeze2000->Freeze ();
}
else
{
WString command;
for (StringListIter i=m_FrozenDatabases.begin ();
i != m_FrozenDatabases.end (); i++)
{
command += L"dbcc freeze_io (N'" + *i + L"')\n";
}
m_Connection.SetCommand (command);
m_Connection.ExecCommand ();
}
return TRUE;
}
//---------------------------------------------
// Thaw the server by issuing thaw commands
// to each database.
// For SQL7, we can't tell if the database was
// already thawn.
// But for SQL2000, we'll return TRUE only if
// the databases were still all frozen at the thaw time.
BOOL
FrozenServer::Thaw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Thaw");
if (m_pFreeze2000)
{
return m_pFreeze2000->Thaw ();
}
WString command;
for (StringListIter i=m_FrozenDatabases.begin ();
i != m_FrozenDatabases.end (); i++)
{
command += L"dbcc thaw_io (N'" + *i + L"')\n";
}
m_Connection.SetCommand (command);
m_Connection.ExecCommand ();
return TRUE;
}
//-------------------------------------------------------------------------
// Create an object to handle the SQL end of the snapshot.
//
CSqlSnapshot*
CreateSqlSnapshot () throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlSnapshot");
try
{
return new Snapshot;
}
catch (...)
{
ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
}
return NULL;
}
//---------------------------------------------------------------
// Move to an uninitialized state.
//
void
Snapshot::Deinitialize ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Deinitialize");
if (m_Status == Frozen)
{
Thaw ();
}
for (ServerIter i=m_FrozenServers.begin ();
i != m_FrozenServers.end (); i++)
{
delete *i;
}
m_FrozenServers.clear ();
m_Status = NotInitialized;
}
Snapshot::~Snapshot ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::~Snapshot");
try
{
ft.Trace(VSSDBG_SQLLIB, L"\n~CSqlSnapshot called\n");
Deinitialize ();
}
catch (...)
{
// swallow!
}
}
//---------------------------------------------------------------------------------------
// Prepare for the snapshot:
// - identify the installed servers
// - for each server that is "up":
// - identify databases affected by the snapshot
// - if there are such databases, fail the snapshot if:
// - the server doesn't support snapshots
// - the database isn't a SIMPLE database
// - the database is "torn" (not all files in the snapshot)
//
//
HRESULT
Snapshot::Prepare (CCheckPath* checker) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Prepare");
HRESULT hr = S_OK;
try
{
AutoNewHandler t;
// hack in a test of the new handler
//
#if 0
while (1)
{
char*p = new char [100000];
if (p==NULL)
{
ft.Trace(VSSDBG_SQLLIB, L"Can never happen!\n");
THROW_GENERIC;
}
}
#endif
if (m_Status != NotInitialized)
{
Deinitialize ();
}
// The state moves immediately to enumerated, indicating
// that the frozen server list may be non-empty.
//
m_Status = Enumerated;
// Build a list of servers on this machine.
//
{
std::auto_ptr<StringVector> servers (EnumerateServers ());
// Scan over the servers, picking out the online ones.
//
for (int i=0; i < servers->size (); i++)
{
if (IsServerOnline ((*servers)[i].c_str ()))
{
FrozenServer* p = new FrozenServer ((*servers)[i]);
m_FrozenServers.push_back (p);
}
else
{
ft.Trace(VSSDBG_SQLLIB, L"Server %s is not online\n", (*servers)[i].c_str ());
}
}
}
// Evaulate the server databases to find those which need to freeze.
//
ServerIter i=m_FrozenServers.begin ();
while (i != m_FrozenServers.end ())
{
if (!(**i).FindDatabasesToFreeze (checker))
{
ft.Trace(VSSDBG_SQLLIB, L"Server %s has no databases to freeze\n", ((**i).GetName ()).c_str ());
// Forget about this server, it's got nothing to do.
//
delete *i;
i = m_FrozenServers.erase (i);
}
else
{
i++;
}
}
// Prep the servers for the freeze
//
for (i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
{
(*i)->Prepare ();
}
#if 0
// debug: print the frozen list
//
for (i=m_FrozenServers.begin ();
i != m_FrozenServers.end (); i++)
{
ft.Trace(VSSDBG_SQLLIB, L"FrozenServer: %s\n", ((**i).GetName ()).c_str ());
}
#endif
m_Status = Prepared;
}
catch (HRESULT& e)
{
hr = e;
}
catch (...)
{
hr = E_SQLLIB_GENERIC;
}
return hr;
}
//---------------------------------------------------------------------------------------
// Freeze any prepared servers
//
HRESULT
Snapshot::Freeze () throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Freeze");
HRESULT hr = S_OK;
if (m_Status != Prepared)
{
return E_SQLLIB_PROTO;
}
try
{
AutoNewHandler t;
// If any server is frozen, we are frozen.
//
m_Status = Frozen;
// Ask the servers to freeze
//
for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
{
(*i)->Freeze ();
}
}
catch (...)
{
hr = E_SQLLIB_GENERIC;
}
return hr;
}
//-----------------------------------------------
// Thaw all the servers.
// This routine must not throw. It's safe in a destructor
//
// DISCUSS WITH BRIAN....WE MUST RETURN "SUCCESS" only if the
// servers were all still frozen. Otherwise the snapshot must
// have been cancelled.
//
HRESULT
Snapshot::Thaw () throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Thaw");
HRESULT hr = S_OK;
AutoNewHandler t;
// Ask the servers to thaw
//
for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
{
try
{
if (!(*i)->Thaw ())
{
hr = E_SQLLIB_GENERIC;
}
}
catch (...)
{
hr = E_SQLLIB_GENERIC;
ft.LogError(VSS_ERROR_SQLLIB_ERRORTHAWSERVER, VSSDBG_SQLLIB << ((**i).GetName ()).c_str ());
}
}
// We still have the original list of servers.
// The snapshot object is reusable if another "Prepare" is done, which will
// re-enumerate the servers.
//
m_Status = Enumerated;
return hr;
}
// Setup some try/catch/handlers for our interface...
// The invoker defines "hr" which is set if an exception
// occurs.
//
#define TRY_SQLLIB \
try {\
AutoNewHandler _myNewHandler;
#define END_SQLLIB \
} catch (HRESULT& e)\
{\
ft.hr = e;\
}\
catch (...)\
{\
ft.hr = E_SQLLIB_GENERIC;\
}
//-------------------------------------------------------------------------
// Create an object to handle enumerations
//
CSqlEnumerator*
CreateSqlEnumerator () throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlEnumerator");
try
{
return new SqlEnumerator;
}
catch (...)
{
ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
}
return NULL;
}
//-------------------------------------------------------------------------
//
SqlEnumerator::~SqlEnumerator ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::~SqlEnumerator");
if (m_pServers)
delete m_pServers;
}
//-------------------------------------------------------------------------
// Begin retrieval of the servers.
//
HRESULT
SqlEnumerator::FirstServer (ServerInfo* pSrv) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstServer");
if (m_pServers)
{
delete m_pServers;
m_pServers = NULL;
}
m_CurrServer = 0;
TRY_SQLLIB
{
m_pServers = EnumerateServers ();
if (m_pServers->size () == 0)
{
ft.hr = DB_S_ENDOFROWSET;
}
else
{
wcscpy (pSrv->name, (*m_pServers)[0].c_str ());
pSrv->isOnline = IsServerOnline (pSrv->name) ? true : false;
m_CurrServer = 1;
ft.hr = NOERROR;
}
}
END_SQLLIB
return ft.hr;
}
//-------------------------------------------------------------------------
// Continue retrieval of the servers.
//
HRESULT
SqlEnumerator::NextServer (ServerInfo* pSrv) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextServer");
if (!m_pServers)
{
ft.hr = E_SQLLIB_PROTO;
}
else
{
TRY_SQLLIB
{
if (m_CurrServer >= m_pServers->size ())
{
ft.hr = DB_S_ENDOFROWSET;
}
else
{
wcscpy (pSrv->name, (*m_pServers)[m_CurrServer].c_str ());
m_CurrServer++;
pSrv->isOnline = IsServerOnline (pSrv->name) ? true : false;
ft.hr = NOERROR;
}
}
END_SQLLIB
}
return ft.hr;
}
//-------------------------------------------------------------------------
// Begin retrieval of the databases
//
HRESULT
SqlEnumerator::FirstDatabase (const WCHAR *pServerName, DatabaseInfo* pDbInfo) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstDatabase");
TRY_SQLLIB
{
m_Connection.Connect (pServerName);
m_Connection.SetCommand (
L"select name,DATABASEPROPERTY(name,'IsTruncLog') from master..sysdatabases");
m_Connection.ExecCommand ();
if (!m_Connection.FetchFirst ())
{
ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
THROW_GENERIC;
}
WCHAR *pDbName = (WCHAR*)m_Connection.AccessColumn (1);
int* pSimple = (int*)m_Connection.AccessColumn (2);
wcscpy (pDbInfo->name, pDbName);
pDbInfo->supportsFreeze = *pSimple &&
m_Connection.GetServerVersion () >= 7;
m_State = DatabaseQueryActive;
ft.hr = NOERROR;
}
END_SQLLIB
return ft.hr;
}
//-------------------------------------------------------------------------
// Continue retrieval of the databases
//
HRESULT
SqlEnumerator::NextDatabase (DatabaseInfo* pDbInfo) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextDatabase");
if (m_State != DatabaseQueryActive)
{
ft.hr = E_SQLLIB_PROTO;
}
else
{
TRY_SQLLIB
{
if (!m_Connection.FetchNext ())
{
ft.hr = DB_S_ENDOFROWSET;
}
else
{
WCHAR* pDbName = (WCHAR*)m_Connection.AccessColumn (1);
int* pSimple = (int*)m_Connection.AccessColumn (2);
wcscpy (pDbInfo->name, pDbName);
pDbInfo->supportsFreeze = *pSimple &&
m_Connection.GetServerVersion () >= 7;
ft.hr = NOERROR;
}
}
END_SQLLIB
}
return ft.hr;
}
//-------------------------------------------------------------------------
// Begin retrieval of the database files
//
HRESULT
SqlEnumerator::FirstFile (
const WCHAR* pServerName,
const WCHAR* pDbName,
DatabaseFileInfo* pFileInfo) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstFile");
TRY_SQLLIB
{
m_Connection.Connect (pServerName);
WString query;
if (m_Connection.GetServerVersion () >= 8)
{
query = L"select rtrim(filename),status & 64 from sysaltfiles where DB_ID('"
+ WString(pDbName) + L"') = dbid";
}
else
{
query = L"select rtrim(filename),status & 64 from ["
+ WString(pDbName) + L"]..sysfiles";
}
m_Connection.SetCommand (query);
m_Connection.ExecCommand ();
if (!m_Connection.FetchFirst ())
{
ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
THROW_GENERIC;
}
WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
int* pLogFile = (int*)m_Connection.AccessColumn (2);
wcscpy (pFileInfo->name, pName);
pFileInfo->isLogFile = (*pLogFile != 0);
m_State = FileQueryActive;
ft.hr = NOERROR;
}
END_SQLLIB
return ft.hr;
}
//-------------------------------------------------------------------------
// Continue retrieval of the files
//
HRESULT
SqlEnumerator::NextFile (DatabaseFileInfo* pFileInfo) throw ()
{
CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextFile");
if (m_State != FileQueryActive)
{
ft.hr = E_SQLLIB_PROTO;
}
else
{
TRY_SQLLIB
{
if (!m_Connection.FetchNext ())
{
ft.hr = DB_S_ENDOFROWSET;
}
else
{
WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
int* pLogFile = (int*)m_Connection.AccessColumn (2);
wcscpy (pFileInfo->name, pName);
pFileInfo->isLogFile = (*pLogFile != 0);
ft.hr = NOERROR;
}
}
END_SQLLIB
}
return ft.hr;
}