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.
647 lines
13 KiB
647 lines
13 KiB
// ***************************************************************************
|
|
// Copyright (C) 2000- Microsoft Corporation.
|
|
// @File: sqlsnapi.h
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
// The internal include file for the sql snapshot module
|
|
//
|
|
// NOTES:
|
|
//
|
|
// 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
|
|
// 66601 srs 10/05/00 NTSNAP improvements
|
|
//
|
|
//
|
|
// @EndHeader@
|
|
// ***************************************************************************
|
|
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <list>
|
|
|
|
#include <oledb.h>
|
|
#include <oledberr.h>
|
|
#include <sqloledb.h>
|
|
|
|
#include "vdi.h" // interface declaration from SQLVDI kit
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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 "SQLSNPIH"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
typedef unsigned long ULONG;
|
|
typedef wchar_t WCHAR;
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
class CLogMsg;
|
|
|
|
// Converting from int to bool "naturally" gives a 4800 warning....
|
|
//
|
|
inline bool IntToBool (int v)
|
|
{
|
|
return v ? true : false;
|
|
}
|
|
|
|
// Unexpected, "internal" errors can be logged with some
|
|
// generic international text, like "Internal error: <English servicibility text>"
|
|
//
|
|
// Situations we expect, for which the user needs to know should
|
|
// occur with proper internationalization
|
|
//
|
|
|
|
// Process wide globals used by the sql modules
|
|
//
|
|
extern IMalloc * g_pIMalloc;
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
typedef std::wstring WString;
|
|
typedef std::vector<WString> StringVector;
|
|
typedef StringVector::const_iterator StringVectorIter;
|
|
typedef std::list<WString> StringList;
|
|
typedef StringList::const_iterator StringListIter;
|
|
|
|
StringVector* EnumerateServers ();
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// database names need special syntactical handling
|
|
//
|
|
const MaxSysNameLen = 128;
|
|
const SysNameBufferLen = MaxSysNameLen * 2; // easily accomodate doubled quotes and delimiters
|
|
|
|
void
|
|
FormStringForName (WCHAR* pString, const WCHAR* pName);
|
|
void
|
|
FormDelimitedIdentifier (WCHAR* pString, const WCHAR* pName);
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Handle our simple needs for DB services.
|
|
//
|
|
// Useage:
|
|
// - Connect : establish a connection to a given server
|
|
// - SetCommand: setup the SQL text to send
|
|
// - ExecCommand: execute some SQL, returns TRUE if a result set
|
|
// is open and ready for retrieval
|
|
// - Get*: retrieve info from the result set
|
|
//
|
|
// The destructor will automatically disconnect from the server.
|
|
//
|
|
class SqlConnection
|
|
{
|
|
public:
|
|
SqlConnection () :
|
|
m_pCommandFactory (NULL),
|
|
m_pCommand (NULL),
|
|
m_pRowset (NULL),
|
|
m_pBuffer (NULL),
|
|
m_BufferSize (0),
|
|
m_hAcc (NULL),
|
|
m_pAccessor (NULL),
|
|
m_pBindings (NULL),
|
|
m_cBindings (0)
|
|
{}
|
|
|
|
~SqlConnection ();
|
|
|
|
void
|
|
Connect (const std::wstring& serverName);
|
|
|
|
void
|
|
Disconnect ();
|
|
|
|
void
|
|
ReleaseRowset ();
|
|
|
|
void
|
|
SetCommand (const std::wstring& command);
|
|
|
|
BOOL
|
|
ExecCommand ();
|
|
|
|
StringVector*
|
|
GetStringColumn ();
|
|
|
|
ULONG
|
|
GetServerVersion () {return m_ServerVersion;}
|
|
|
|
BOOL
|
|
FetchFirst ();
|
|
|
|
BOOL
|
|
FetchNext ();
|
|
|
|
void*
|
|
AccessColumn (int id);
|
|
|
|
private:
|
|
void LogOledbError
|
|
(
|
|
CVssFunctionTracer &ft,
|
|
CVssDebugInfo &dbgInfo,
|
|
LPCWSTR wszRoutine,
|
|
CLogMsg &msg
|
|
);
|
|
|
|
WString m_ServerName;
|
|
IDBCreateCommand* m_pCommandFactory;
|
|
ICommandText* m_pCommand;
|
|
IRowset* m_pRowset;
|
|
ULONG m_ServerVersion;
|
|
|
|
// used for the generic findfirst/findnext
|
|
DBBINDING* m_pBindings;
|
|
ULONG m_cBindings;
|
|
BYTE* m_pBuffer;
|
|
ULONG m_BufferSize;
|
|
HACCESSOR m_hAcc;
|
|
IAccessor* m_pAccessor;
|
|
};
|
|
|
|
BOOL
|
|
IsServerOnline (const WCHAR* serverName);
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Provide a simple container for BACKUP metadata.
|
|
//
|
|
class MetaData
|
|
{
|
|
public:
|
|
MetaData ();
|
|
~MetaData ();
|
|
|
|
// Data is appended until complete
|
|
//
|
|
void
|
|
Append (const BYTE* pData, UINT length);
|
|
|
|
// At completion, a checksum is appended
|
|
//
|
|
void
|
|
Finalize ();
|
|
|
|
// The buffer and its length are made available.
|
|
//
|
|
const BYTE*
|
|
GetImage (UINT* pLength);
|
|
|
|
// Verify the checksum
|
|
//
|
|
static
|
|
BOOL
|
|
IsValidImage (const BYTE* pData, UINT length);
|
|
|
|
private:
|
|
static
|
|
UINT
|
|
Checksum (const BYTE* pData, UINT length);
|
|
|
|
BYTE* m_pData;
|
|
UINT m_AllocatedLength;
|
|
UINT m_UsedLength;
|
|
};
|
|
|
|
class Freeze2000;
|
|
class FrozenServer;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Handle a single frozen database (>=SQL2000 only).
|
|
//
|
|
class FrozenDatabase
|
|
{
|
|
friend Freeze2000;
|
|
friend FrozenServer;
|
|
|
|
//protected:
|
|
enum VDState
|
|
{
|
|
Unknown=0,
|
|
Created,
|
|
Open,
|
|
PreparedToFreeze,
|
|
Frozen
|
|
};
|
|
|
|
FrozenDatabase () :
|
|
m_pContext (NULL),
|
|
m_hThread (NULL),
|
|
m_pIVDSet (NULL),
|
|
m_pIVD (NULL),
|
|
m_pSnapshotCmd (NULL),
|
|
m_VDState (Unknown),
|
|
m_SuccessDetected (FALSE)
|
|
{}
|
|
|
|
Freeze2000* m_pContext;
|
|
HANDLE m_hThread;
|
|
IClientVirtualDeviceSet2* m_pIVDSet;
|
|
IClientVirtualDevice* m_pIVD;
|
|
WString m_DbName;
|
|
VDC_Command* m_pSnapshotCmd;
|
|
VDState m_VDState;
|
|
WCHAR m_SetName [80];
|
|
bool m_SuccessDetected;
|
|
MetaData m_MetaData;
|
|
// bool m_IsSimpleModel; // true if recovery model is simple
|
|
bool m_IsMaster; // true if this is the "master" database
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// Handle a SQL2000 server.
|
|
//
|
|
// In SQL2000 we'll use VDI snapshots to avoid bug 58266: thaw fails.
|
|
//
|
|
// We'll prepare each database by starting a BACKUP WITH SNAPSHOT.
|
|
// This will require one thread per database.
|
|
// The backups will stall waiting for the VDI client to pull metadata.
|
|
// When the "freeze" message comes along, the controlling thread will
|
|
// pull all the BACKUPs to the frozen state.
|
|
// Later the "thaw" message results in gathering the "success" report
|
|
// from each thread.
|
|
//
|
|
//
|
|
class Snapshot;
|
|
class Freeze2000
|
|
{
|
|
friend Snapshot;
|
|
friend FrozenServer;
|
|
|
|
public:
|
|
Freeze2000 (
|
|
const WString& serverName,
|
|
ULONG maxDatabases);
|
|
|
|
~Freeze2000 ();
|
|
|
|
void
|
|
PrepareDatabase (
|
|
const WString& dbName);
|
|
|
|
const BYTE*
|
|
GetMetaData (
|
|
const WString& dbName,
|
|
UINT* pDataLength);
|
|
|
|
void
|
|
WaitForPrepare ();
|
|
|
|
void
|
|
Freeze ();
|
|
|
|
BOOL
|
|
Thaw () throw ();
|
|
|
|
static DWORD
|
|
DatabaseThreadStart (LPVOID pContext);
|
|
|
|
private:
|
|
enum State {
|
|
Unprepared,
|
|
Preparing,
|
|
Prepared,
|
|
Frozen,
|
|
Complete,
|
|
Aborted
|
|
};
|
|
|
|
DWORD
|
|
DatabaseThread (
|
|
FrozenDatabase* pDbContext);
|
|
|
|
void
|
|
WaitForThreads ();
|
|
|
|
void
|
|
AdvanceVDState (
|
|
FrozenDatabase::VDState targetState);
|
|
|
|
void // race-free method to persist an abort condition
|
|
SetAbort ()
|
|
{
|
|
InterlockedIncrement (&m_AbortCount);
|
|
}
|
|
|
|
bool // return true if the freeze is aborting
|
|
CheckAbort ()
|
|
{
|
|
return 0 != InterlockedCompareExchange (
|
|
#ifdef DOWNLEVEL_WINBASE
|
|
(void**)
|
|
#endif
|
|
&m_AbortCount, 0, 0);
|
|
}
|
|
|
|
void
|
|
Abort () throw ();
|
|
|
|
void
|
|
Lock ()
|
|
{
|
|
EnterCriticalSection (&m_Latch);
|
|
}
|
|
void
|
|
Unlock ()
|
|
{
|
|
LeaveCriticalSection (&m_Latch);
|
|
}
|
|
BOOL // return TRUE if we got the lock
|
|
TryLock ()
|
|
{
|
|
return TryEnterCriticalSection (&m_Latch);
|
|
}
|
|
|
|
LONG m_AbortCount;
|
|
CRITICAL_SECTION m_Latch;
|
|
State m_State;
|
|
WString m_ServerName;
|
|
GUID m_BackupId;
|
|
UINT m_NumDatabases;
|
|
UINT m_MaxDatabases;
|
|
FrozenDatabase* m_pDBContext;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Represent a server which can be frozen.
|
|
//
|
|
class FrozenServer
|
|
{
|
|
friend Snapshot;
|
|
|
|
public:
|
|
FrozenServer (const std::wstring& serverName) :
|
|
m_Name (serverName),
|
|
m_pFreeze2000 (NULL)
|
|
{}
|
|
|
|
~FrozenServer ()
|
|
{
|
|
if (m_pFreeze2000)
|
|
{
|
|
delete m_pFreeze2000;
|
|
m_pFreeze2000 = NULL;
|
|
}
|
|
}
|
|
|
|
const std::wstring& GetName () const
|
|
{ return m_Name; }
|
|
|
|
BOOL
|
|
FindDatabasesToFreeze (
|
|
CCheckPath* checker);
|
|
|
|
BOOL
|
|
Prepare ();
|
|
|
|
BOOL
|
|
Freeze ();
|
|
|
|
BOOL
|
|
Thaw () throw ();
|
|
|
|
#if 0
|
|
void
|
|
GetServerInfo (ServerInfo* pSrv);
|
|
|
|
UINT
|
|
GetDatabaseCount ();
|
|
#endif
|
|
|
|
void
|
|
GetDatabaseInfo (UINT dbIndex, FrozenDatabaseInfo* pInfo);
|
|
|
|
private:
|
|
BOOL
|
|
FindDatabases2000 (
|
|
CCheckPath* checker);
|
|
|
|
void
|
|
GetDatabaseProperties70 (const WString& dbName,
|
|
BOOL* pSimple,
|
|
BOOL* pOnline);
|
|
|
|
private:
|
|
std::wstring m_Name;
|
|
SqlConnection m_Connection;
|
|
StringList m_FrozenDatabases;
|
|
Freeze2000* m_pFreeze2000;
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
class Snapshot : public CSqlSnapshot
|
|
{
|
|
enum Status {
|
|
NotInitialized,
|
|
Enumerated,
|
|
Prepared,
|
|
Frozen };
|
|
|
|
public:
|
|
HRESULT Prepare (
|
|
CCheckPath* checker) throw ();
|
|
|
|
HRESULT Freeze () throw ();
|
|
|
|
HRESULT Thaw () throw ();
|
|
|
|
Snapshot () {m_Status = NotInitialized;}
|
|
|
|
~Snapshot () throw ();
|
|
|
|
#if 0
|
|
CSqlEnumerator* GetEnumerator () throw ()
|
|
{return (CSqlEnumerator*)this;}
|
|
|
|
// Call this at "Post-snapshot" time, after all databases are frozen and MD is complete.
|
|
//
|
|
HRESULT GetBackupMetadata (
|
|
const WCHAR* pInstance,
|
|
const WCHAR* pDatabase,
|
|
BYTE** ppData, // returns a pointer to the string of metadata
|
|
unsigned int* pDataLen) // length of the metadata (in bytes)
|
|
throw ();
|
|
|
|
// The enumeration interface
|
|
//
|
|
HRESULT FirstServer (
|
|
ServerInfo* pServer) throw ();
|
|
|
|
HRESULT NextServer (
|
|
ServerInfo* pServer) throw ();
|
|
|
|
HRESULT FirstDatabase (
|
|
const WCHAR* pServerName,
|
|
DatabaseInfo* pDbInfo) throw ()
|
|
{return E_NOTIMPL;}
|
|
|
|
HRESULT FirstDatabase (
|
|
DatabaseInfo* pDbInfo) throw ();
|
|
|
|
HRESULT NextDatabase (
|
|
DatabaseInfo* pDbInfo) throw ();
|
|
|
|
HRESULT FirstFile (
|
|
const WCHAR* pServerName,
|
|
const WCHAR* pDbName,
|
|
DatabaseFileInfo* pDbInfo) throw ()
|
|
{return E_NOTIMPL;}
|
|
|
|
HRESULT NextFile (
|
|
DatabaseFileInfo* pDbInfo) throw ()
|
|
{return E_NOTIMPL;}
|
|
#endif
|
|
|
|
HRESULT GetFirstDatabase (
|
|
FrozenDatabaseInfo* pInfo) throw ();
|
|
HRESULT GetNextDatabase (
|
|
FrozenDatabaseInfo* pInfo) throw ();
|
|
|
|
private:
|
|
void
|
|
Deinitialize ();
|
|
|
|
Status m_Status;
|
|
std::list<FrozenServer*> m_FrozenServers;
|
|
|
|
typedef std::list<FrozenServer*>::iterator ServerIter;
|
|
|
|
ServerIter m_ServerIter; // iterate over the frozen servers
|
|
UINT m_DbIndex; // iterate over databases within a server
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
class RestoreHandler : public CSqlRestore
|
|
{
|
|
public:
|
|
RestoreHandler ();
|
|
|
|
// Inform SQLServer that data laydown is desired on the full database.
|
|
// Performs a DETACH, preventing SQLServer from touching the files.
|
|
//
|
|
virtual HRESULT PrepareToRestore (
|
|
const WCHAR* pInstance,
|
|
const WCHAR* pDatabase)
|
|
throw ();
|
|
|
|
// After data is laid down, this performs RESTORE WITH SNAPSHOT[,NORECOVERY]
|
|
//
|
|
virtual HRESULT FinalizeRestore (
|
|
const WCHAR* pInstance,
|
|
const WCHAR* pDatabase,
|
|
bool compositeRestore, // true if WITH NORECOVERY desired
|
|
const BYTE* pMetadata, // metadata obtained from BACKUP
|
|
unsigned int dataLen) // size of metadata (in bytes)
|
|
throw ();
|
|
|
|
// Internal use only. Called from the thread proc wrapper.
|
|
//
|
|
void
|
|
RestoreVD ();
|
|
|
|
private:
|
|
SqlConnection m_Connection;
|
|
HANDLE m_hThread;
|
|
IClientVirtualDeviceSet2* m_pIVDSet;
|
|
IClientVirtualDevice* m_pIVD;
|
|
WCHAR m_SetName [80];
|
|
GUID m_VDSId;
|
|
const BYTE* m_pMetaData;
|
|
UINT m_MetaDataSize;
|
|
};
|
|
|
|
|
|
// We'll use very simple exception handling.
|
|
//
|
|
#define THROW_GENERIC throw exception ();
|
|
|
|
//----------------------------------------------------------
|
|
// Implement our simple enumeration service
|
|
//
|
|
class SqlEnumerator : public CSqlEnumerator
|
|
{
|
|
enum Status {
|
|
Unknown = 0,
|
|
DatabaseQueryActive,
|
|
FileQueryActive
|
|
};
|
|
|
|
public:
|
|
~SqlEnumerator () throw ();
|
|
|
|
SqlEnumerator () :
|
|
m_State (Unknown),
|
|
m_pServers (NULL)
|
|
{}
|
|
|
|
HRESULT FirstServer (
|
|
ServerInfo* pServer) throw ();
|
|
|
|
HRESULT NextServer (
|
|
ServerInfo* pServer) throw ();
|
|
|
|
HRESULT FirstDatabase (
|
|
const WCHAR* pServerName,
|
|
DatabaseInfo* pDbInfo) throw ();
|
|
|
|
HRESULT NextDatabase (
|
|
DatabaseInfo* pDbInfo) throw ();
|
|
|
|
HRESULT FirstFile (
|
|
const WCHAR* pServerName,
|
|
const WCHAR* pDbName,
|
|
DatabaseFileInfo* pDbInfo) throw ();
|
|
|
|
HRESULT NextFile (
|
|
DatabaseFileInfo* pDbInfo) throw ();
|
|
|
|
private:
|
|
void
|
|
SetupDatabaseInfo (
|
|
DatabaseInfo* pDbInfo);
|
|
|
|
Status m_State;
|
|
SqlConnection m_Connection;
|
|
StringVector* m_pServers;
|
|
UINT m_CurrServer;
|
|
};
|
|
|
|
#if defined (DEBUG)
|
|
|
|
// Type of assertion passed through to utassert_fail function.
|
|
//
|
|
|
|
|
|
#define DBG_ASSERT(exp) BS_ASSERT(exp)
|
|
|
|
// Allow for noop 64 bit asserts on win32 for things like
|
|
// overflowing 32 bit long, etc.
|
|
//
|
|
#ifdef _WIN64
|
|
#define DBG64_ASSERT(exp) BS_ASSERT(exp)
|
|
#else
|
|
#define DBG64_ASSERT(exp)
|
|
#endif
|
|
|
|
#else
|
|
#define DBG_ASSERT(exp)
|
|
#define DBG64_ASSERT(exp)
|
|
#define DBG_ASSERTSZ(exp, txt)
|
|
#endif
|