// *************************************************************************** // 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 #include #include #include #include #include #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: " // // 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 StringVector; typedef StringVector::const_iterator StringVectorIter; typedef std::list 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 m_FrozenServers; typedef std::list::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