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

  1. // ***************************************************************************
  2. // Copyright (C) 2000- Microsoft Corporation.
  3. // @File: snapsql.cpp
  4. //
  5. // PURPOSE:
  6. //
  7. // Implement the SQLServer Volume Snapshot Writer.
  8. //
  9. // NOTES:
  10. //
  11. //
  12. // HISTORY:
  13. //
  14. // @Version: Whistler/Shiloh
  15. // 66601 srs 10/05/00 NTSNAP improvements
  16. //
  17. //
  18. // @EndHeader@
  19. // ***************************************************************************
  20. #if HIDE_WARNINGS
  21. #pragma warning( disable : 4786)
  22. #endif
  23. #include <stdafx.h>
  24. #include <new.h>
  25. ////////////////////////////////////////////////////////////////////////
  26. // Standard foo for file name aliasing. This code block must be after
  27. // all includes of VSS header files.
  28. //
  29. #ifdef VSS_FILE_ALIAS
  30. #undef VSS_FILE_ALIAS
  31. #endif
  32. #define VSS_FILE_ALIAS "SQLSNAPC"
  33. //
  34. ////////////////////////////////////////////////////////////////////////
  35. int __cdecl out_of_store(size_t size)
  36. {
  37. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"out_of_store");
  38. ft.Trace(VSSDBG_SQLLIB, L"out of memory");
  39. throw HRESULT (E_OUTOFMEMORY);
  40. return 0;
  41. }
  42. class AutoNewHandler
  43. {
  44. public:
  45. AutoNewHandler ()
  46. {
  47. m_oldHandler = _set_new_handler (out_of_store);
  48. }
  49. ~AutoNewHandler ()
  50. {
  51. _set_new_handler (m_oldHandler);
  52. }
  53. private:
  54. _PNH m_oldHandler;
  55. };
  56. //-------------------------------------------------------------------------
  57. // Handle enviroment stuff:
  58. // - tracing/error logging
  59. // - mem alloc
  60. //
  61. IMalloc * g_pIMalloc = NULL;
  62. HRESULT
  63. InitSQLEnvironment()
  64. {
  65. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"InitSqlEnvironment");
  66. try
  67. {
  68. ft.hr = CoGetMalloc(1, &g_pIMalloc);
  69. if (ft.HrFailed())
  70. ft.Trace(VSSDBG_SQLLIB, L"Failed to get task allocator: hr=0x%X", ft.hr);
  71. }
  72. catch (...)
  73. {
  74. ft.hr = E_SQLLIB_GENERIC;
  75. }
  76. return ft.hr;
  77. }
  78. //-------------------------------------------------------------------------
  79. // Return TRUE if the server is online and a connection shouldn't take forever!
  80. //
  81. BOOL
  82. IsServerOnline (const WCHAR* serverName)
  83. {
  84. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"IsServerOnline");
  85. WCHAR eventName [300];
  86. WCHAR* pInstance;
  87. wcscpy (eventName, L"Global\\sqlserverRecComplete");
  88. // A "\" indicates a named instance, so append the name...
  89. //
  90. pInstance = wcschr (serverName, L'\\');
  91. if (pInstance)
  92. {
  93. wcscat (eventName, L"$");
  94. wcscat (eventName, pInstance+1);
  95. }
  96. HANDLE hEvent = CreateEventW (NULL, TRUE, FALSE, eventName);
  97. if (hEvent == NULL)
  98. {
  99. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  100. ft.LogError(VSS_ERROR_SQLLIB_CANT_CREATE_EVENT, VSSDBG_SQLLIB << ft.hr);
  101. THROW_GENERIC;
  102. }
  103. // If the event isn't signaled, the server is not up.
  104. //
  105. BOOL result = (WaitForSingleObject (hEvent, 0) == WAIT_OBJECT_0);
  106. CloseHandle (hEvent);
  107. return result;
  108. }
  109. //-------------------------------------------------------------------------
  110. // Return TRUE if the database properties are retrieved:
  111. // simple: TRUE if using the simple recovery model.
  112. // online: TRUE if the database is usable and currently open
  113. //
  114. void
  115. FrozenServer::GetDatabaseProperties (const WString& dbName,
  116. BOOL* pSimple,
  117. BOOL* pOnline)
  118. {
  119. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::GetDatabaseProperties");
  120. // We use status bit 0x40000000 (1073741824) to identify
  121. // clean-shutdown databases which are "offline".
  122. //
  123. WString query =
  124. L"select databaseproperty(N'" + dbName + L"','IsTruncLog'),"
  125. L"case status & 1073741824 "
  126. L"when 1073741824 then 0 "
  127. L"else 1 end "
  128. L"from master..sysdatabases where name = N'" + dbName + L"'";
  129. m_Connection.SetCommand (query);
  130. m_Connection.ExecCommand ();
  131. if (!m_Connection.FetchFirst ())
  132. {
  133. LPCWSTR wsz = dbName.c_str();
  134. ft.LogError(VSS_ERROR_SQLLIB_DATABASE_NOT_IN_SYSDATABASES, VSSDBG_SQLLIB << wsz);
  135. THROW_GENERIC;
  136. }
  137. *pSimple = (BOOL)(*(int*)m_Connection.AccessColumn (1));
  138. *pOnline = (BOOL)(*(int*)m_Connection.AccessColumn (2));
  139. }
  140. //------------------------------------------------------------------------------
  141. // Called only by "FindDatabasesToFreeze" to implement a smart access strategy:
  142. // - use sysaltfiles to qualify the databases.
  143. // This avoids access to shutdown or damaged databases.
  144. //
  145. // Autoclose databases which are not started are left out of the freeze-list.
  146. // We do this to avoid scaling problems, especially on desktop systems.
  147. // However, such db's are still evaluated to see if they are "torn".
  148. //
  149. // The 'model' database is allowed to be a full recovery database, since only
  150. // database backups are sensible for it.
  151. //
  152. BOOL
  153. FrozenServer::FindDatabases2000 (
  154. CCheckPath* checker)
  155. {
  156. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabases2000");
  157. // Create an ordered set of tuples (dbname, filename, simpleRecovery, dbIsActive)
  158. //
  159. // We use status bit 0x40000000 (1073741824) to identify
  160. // clean-shutdown databases which are not active.
  161. //
  162. m_Connection.SetCommand (
  163. L"select db_name(af.dbid),rtrim(af.filename), "
  164. L"case databasepropertyex(db_name(af.dbid),'recovery') "
  165. L"when 'SIMPLE' then 1 "
  166. L"else 0 end,"
  167. L"case databasepropertyex(db_name(af.dbid),'Status') "
  168. L"when 'ONLINE' then case db.status & 1073741824 "
  169. L"when 1073741824 then 0 "
  170. L"else 1 end "
  171. L"else 0 end "
  172. L"from master..sysaltfiles af, master..sysdatabases db "
  173. L"where af.dbid = db.dbid and af.dbid != db_id('tempdb') "
  174. L"order by af.dbid"
  175. );
  176. m_Connection.ExecCommand ();
  177. WCHAR* pDbName;
  178. WCHAR* pFileName;
  179. int* pSimple;
  180. int* pIsOnline;
  181. WString currentDbName;
  182. BOOL firstDb = TRUE;
  183. BOOL firstFile;
  184. BOOL shouldFreeze = FALSE;
  185. BOOL masterLast = FALSE;
  186. BOOL currDbIsOnline;
  187. if (!m_Connection.FetchFirst ())
  188. {
  189. ft.LogError(VSS_ERROR_SQLLIB_SYSALTFILESEMPTY, VSSDBG_SQLLIB);
  190. THROW_GENERIC;
  191. }
  192. pDbName = (WCHAR*)m_Connection.AccessColumn (1);
  193. pFileName = (WCHAR*)m_Connection.AccessColumn (2);
  194. pSimple = (int*)m_Connection.AccessColumn (3);
  195. pIsOnline = (int*)m_Connection.AccessColumn (4);
  196. while (1)
  197. {
  198. // Check out the current row
  199. //
  200. BOOL fileInSnap = checker->IsPathInSnapshot (pFileName);
  201. if (fileInSnap && !*pSimple && wcscmp (L"model", pDbName))
  202. {
  203. ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << pDbName);
  204. throw HRESULT (E_SQLLIB_NONSIMPLE);
  205. }
  206. // Is this the next database?
  207. //
  208. if (firstDb || currentDbName.compare (pDbName))
  209. {
  210. if (!firstDb)
  211. {
  212. // Deal with completed database
  213. //
  214. if (shouldFreeze && currDbIsOnline)
  215. {
  216. if (currentDbName == L"master")
  217. {
  218. masterLast = TRUE;
  219. }
  220. else
  221. {
  222. m_FrozenDatabases.push_back (currentDbName);
  223. }
  224. }
  225. }
  226. // Keep info about the newly encountered db
  227. //
  228. currentDbName = WString (pDbName);
  229. currDbIsOnline = *pIsOnline;
  230. firstFile = TRUE;
  231. firstDb = FALSE;
  232. ft.Trace(VSSDBG_SQLLIB, L"Examining %s. SimpleRecovery:%d Online:%d\n", pDbName, *pSimple, *pIsOnline);
  233. }
  234. ft.Trace(VSSDBG_SQLLIB, L"%s\n", pFileName);
  235. if (firstFile)
  236. {
  237. shouldFreeze = fileInSnap;
  238. firstFile = FALSE;
  239. }
  240. else
  241. {
  242. if (shouldFreeze ^ fileInSnap)
  243. {
  244. ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB);
  245. throw HRESULT (E_SQLLIB_TORN_DB);
  246. }
  247. }
  248. if (!m_Connection.FetchNext ())
  249. {
  250. // Deal with the current database
  251. //
  252. if (shouldFreeze && currDbIsOnline)
  253. {
  254. m_FrozenDatabases.push_back (currentDbName);
  255. }
  256. break;
  257. }
  258. }
  259. if (masterLast)
  260. {
  261. m_FrozenDatabases.push_back (L"master");
  262. }
  263. return m_FrozenDatabases.size () > 0;
  264. }
  265. //------------------------------------------------------------------------------
  266. // Determine if there are databases which qualify for a freeze on this server.
  267. // Returns TRUE if so.
  268. // Throws if any qualified databases are any of:
  269. // - "torn" (not fully covered by the snapshot)
  270. // - hosted by a server which can't support freeze
  271. // - are not "simple" databases
  272. //
  273. BOOL
  274. FrozenServer::FindDatabasesToFreeze (
  275. CCheckPath* checker)
  276. {
  277. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::FindDatabasesToFreeze");
  278. m_Connection.Connect (m_Name);
  279. m_FrozenDatabases.clear ();
  280. if (m_Connection.GetServerVersion () > 7)
  281. {
  282. // SQL2000 allows us to use a better access strategy.
  283. //
  284. return FindDatabases2000 (checker);
  285. }
  286. m_Connection.SetCommand (L"select name from sysdatabases where name != 'tempdb'");
  287. m_Connection.ExecCommand ();
  288. std::auto_ptr<StringVector> dbList (m_Connection.GetStringColumn ());
  289. BOOL masterLast = FALSE;
  290. for (StringVectorIter i = dbList->begin (); i != dbList->end (); i++)
  291. {
  292. // UNDONE: SKIP OVER DB'S in SHUTDOWN DB'S?
  293. // DB'S IN LOAD, ETC?
  294. // We'll avoid freezing shutdown db's, but we don't avoid
  295. // enumerating their files (they might be torn)
  296. //
  297. // Note the [] around the dbname to handle non-trivial dbnames.
  298. //
  299. WString command = L"select rtrim(filename) from [";
  300. command += *i + L"]..sysfiles";
  301. m_Connection.SetCommand (command);
  302. try
  303. {
  304. m_Connection.ExecCommand ();
  305. }
  306. catch (...)
  307. {
  308. // We've decided to be optimistic:
  309. // If we can't get the list of files, ignore this database.
  310. //
  311. ft.Trace(VSSDBG_SQLLIB, L"Failed to get db files for %s\n", i->c_str ());
  312. continue;
  313. }
  314. std::auto_ptr<StringVector> fileList (m_Connection.GetStringColumn ());
  315. BOOL first=TRUE;
  316. BOOL shouldFreeze;
  317. for (StringVectorIter iFile = fileList->begin ();
  318. iFile != fileList->end (); iFile++)
  319. {
  320. BOOL fileInSnap = checker->IsPathInSnapshot (iFile->c_str ());
  321. if (first)
  322. {
  323. shouldFreeze = fileInSnap;
  324. }
  325. else
  326. {
  327. if (shouldFreeze ^ fileInSnap)
  328. {
  329. ft.LogError(VSS_ERROR_SQLLIB_DATABASEISTORN, VSSDBG_SQLLIB << i->c_str());
  330. throw HRESULT (E_SQLLIB_TORN_DB);
  331. }
  332. }
  333. }
  334. if (shouldFreeze)
  335. {
  336. BOOL simple, online;
  337. GetDatabaseProperties (i->c_str (), &simple, &online);
  338. if (!simple && L"model" != *i)
  339. {
  340. ft.LogError(VSS_ERROR_SQLLIB_DATABASENOTSIMPLE, VSSDBG_SQLLIB << i->c_str ());
  341. throw HRESULT (E_SQLLIB_NONSIMPLE);
  342. }
  343. if (online)
  344. {
  345. if (L"master" == *i)
  346. {
  347. masterLast = TRUE;
  348. }
  349. else
  350. {
  351. m_FrozenDatabases.push_back (*i);
  352. }
  353. }
  354. }
  355. }
  356. if (masterLast)
  357. {
  358. m_FrozenDatabases.push_back (L"master");
  359. }
  360. return m_FrozenDatabases.size () > 0;
  361. }
  362. //-------------------------------------------------------------------
  363. // Prep the server for the freeze.
  364. // For SQL2000, start a BACKUP WITH SNAPSHOT.
  365. // For SQL7, issuing checkpoints to each database.
  366. // This minimizes the recovery processing needed when the snapshot is restored.
  367. //
  368. BOOL
  369. FrozenServer::Prepare ()
  370. {
  371. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Prepare");
  372. if (m_Connection.GetServerVersion () > 7)
  373. {
  374. m_pFreeze2000 = new Freeze2000 (m_Name, m_FrozenDatabases.size ());
  375. // Release the connection, we won't need it anymore
  376. //
  377. m_Connection.Disconnect ();
  378. for (StringListIter i=m_FrozenDatabases.begin ();
  379. i != m_FrozenDatabases.end (); i++)
  380. {
  381. m_pFreeze2000->PrepareDatabase (*i);
  382. }
  383. m_pFreeze2000->WaitForPrepare ();
  384. }
  385. else
  386. {
  387. WString command;
  388. for (StringListIter i=m_FrozenDatabases.begin ();
  389. i != m_FrozenDatabases.end (); i++)
  390. {
  391. command += L"use [" + *i + L"]\ncheckpoint\n";
  392. }
  393. m_Connection.SetCommand (command);
  394. m_Connection.ExecCommand ();
  395. }
  396. return TRUE;
  397. }
  398. //---------------------------------------------
  399. // Freeze the server by issuing freeze commands
  400. // to each database.
  401. // Returns an exception if any failure occurs.
  402. //
  403. BOOL
  404. FrozenServer::Freeze ()
  405. {
  406. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Freeze");
  407. if (m_pFreeze2000)
  408. {
  409. m_pFreeze2000->Freeze ();
  410. }
  411. else
  412. {
  413. WString command;
  414. for (StringListIter i=m_FrozenDatabases.begin ();
  415. i != m_FrozenDatabases.end (); i++)
  416. {
  417. command += L"dbcc freeze_io (N'" + *i + L"')\n";
  418. }
  419. m_Connection.SetCommand (command);
  420. m_Connection.ExecCommand ();
  421. }
  422. return TRUE;
  423. }
  424. //---------------------------------------------
  425. // Thaw the server by issuing thaw commands
  426. // to each database.
  427. // For SQL7, we can't tell if the database was
  428. // already thawn.
  429. // But for SQL2000, we'll return TRUE only if
  430. // the databases were still all frozen at the thaw time.
  431. BOOL
  432. FrozenServer::Thaw ()
  433. {
  434. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"FrozenServer::Thaw");
  435. if (m_pFreeze2000)
  436. {
  437. return m_pFreeze2000->Thaw ();
  438. }
  439. WString command;
  440. for (StringListIter i=m_FrozenDatabases.begin ();
  441. i != m_FrozenDatabases.end (); i++)
  442. {
  443. command += L"dbcc thaw_io (N'" + *i + L"')\n";
  444. }
  445. m_Connection.SetCommand (command);
  446. m_Connection.ExecCommand ();
  447. return TRUE;
  448. }
  449. //-------------------------------------------------------------------------
  450. // Create an object to handle the SQL end of the snapshot.
  451. //
  452. CSqlSnapshot*
  453. CreateSqlSnapshot () throw ()
  454. {
  455. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlSnapshot");
  456. try
  457. {
  458. return new Snapshot;
  459. }
  460. catch (...)
  461. {
  462. ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
  463. }
  464. return NULL;
  465. }
  466. //---------------------------------------------------------------
  467. // Move to an uninitialized state.
  468. //
  469. void
  470. Snapshot::Deinitialize ()
  471. {
  472. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Deinitialize");
  473. if (m_Status == Frozen)
  474. {
  475. Thaw ();
  476. }
  477. for (ServerIter i=m_FrozenServers.begin ();
  478. i != m_FrozenServers.end (); i++)
  479. {
  480. delete *i;
  481. }
  482. m_FrozenServers.clear ();
  483. m_Status = NotInitialized;
  484. }
  485. Snapshot::~Snapshot ()
  486. {
  487. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::~Snapshot");
  488. try
  489. {
  490. ft.Trace(VSSDBG_SQLLIB, L"\n~CSqlSnapshot called\n");
  491. Deinitialize ();
  492. }
  493. catch (...)
  494. {
  495. // swallow!
  496. }
  497. }
  498. //---------------------------------------------------------------------------------------
  499. // Prepare for the snapshot:
  500. // - identify the installed servers
  501. // - for each server that is "up":
  502. // - identify databases affected by the snapshot
  503. // - if there are such databases, fail the snapshot if:
  504. // - the server doesn't support snapshots
  505. // - the database isn't a SIMPLE database
  506. // - the database is "torn" (not all files in the snapshot)
  507. //
  508. //
  509. HRESULT
  510. Snapshot::Prepare (CCheckPath* checker) throw ()
  511. {
  512. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Prepare");
  513. HRESULT hr = S_OK;
  514. try
  515. {
  516. AutoNewHandler t;
  517. // hack in a test of the new handler
  518. //
  519. #if 0
  520. while (1)
  521. {
  522. char*p = new char [100000];
  523. if (p==NULL)
  524. {
  525. ft.Trace(VSSDBG_SQLLIB, L"Can never happen!\n");
  526. THROW_GENERIC;
  527. }
  528. }
  529. #endif
  530. if (m_Status != NotInitialized)
  531. {
  532. Deinitialize ();
  533. }
  534. // The state moves immediately to enumerated, indicating
  535. // that the frozen server list may be non-empty.
  536. //
  537. m_Status = Enumerated;
  538. // Build a list of servers on this machine.
  539. //
  540. {
  541. std::auto_ptr<StringVector> servers (EnumerateServers ());
  542. // Scan over the servers, picking out the online ones.
  543. //
  544. for (int i=0; i < servers->size (); i++)
  545. {
  546. if (IsServerOnline ((*servers)[i].c_str ()))
  547. {
  548. FrozenServer* p = new FrozenServer ((*servers)[i]);
  549. m_FrozenServers.push_back (p);
  550. }
  551. else
  552. {
  553. ft.Trace(VSSDBG_SQLLIB, L"Server %s is not online\n", (*servers)[i].c_str ());
  554. }
  555. }
  556. }
  557. // Evaulate the server databases to find those which need to freeze.
  558. //
  559. ServerIter i=m_FrozenServers.begin ();
  560. while (i != m_FrozenServers.end ())
  561. {
  562. if (!(**i).FindDatabasesToFreeze (checker))
  563. {
  564. ft.Trace(VSSDBG_SQLLIB, L"Server %s has no databases to freeze\n", ((**i).GetName ()).c_str ());
  565. // Forget about this server, it's got nothing to do.
  566. //
  567. delete *i;
  568. i = m_FrozenServers.erase (i);
  569. }
  570. else
  571. {
  572. i++;
  573. }
  574. }
  575. // Prep the servers for the freeze
  576. //
  577. for (i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  578. {
  579. (*i)->Prepare ();
  580. }
  581. #if 0
  582. // debug: print the frozen list
  583. //
  584. for (i=m_FrozenServers.begin ();
  585. i != m_FrozenServers.end (); i++)
  586. {
  587. ft.Trace(VSSDBG_SQLLIB, L"FrozenServer: %s\n", ((**i).GetName ()).c_str ());
  588. }
  589. #endif
  590. m_Status = Prepared;
  591. }
  592. catch (HRESULT& e)
  593. {
  594. hr = e;
  595. }
  596. catch (...)
  597. {
  598. hr = E_SQLLIB_GENERIC;
  599. }
  600. return hr;
  601. }
  602. //---------------------------------------------------------------------------------------
  603. // Freeze any prepared servers
  604. //
  605. HRESULT
  606. Snapshot::Freeze () throw ()
  607. {
  608. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Freeze");
  609. HRESULT hr = S_OK;
  610. if (m_Status != Prepared)
  611. {
  612. return E_SQLLIB_PROTO;
  613. }
  614. try
  615. {
  616. AutoNewHandler t;
  617. // If any server is frozen, we are frozen.
  618. //
  619. m_Status = Frozen;
  620. // Ask the servers to freeze
  621. //
  622. for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  623. {
  624. (*i)->Freeze ();
  625. }
  626. }
  627. catch (...)
  628. {
  629. hr = E_SQLLIB_GENERIC;
  630. }
  631. return hr;
  632. }
  633. //-----------------------------------------------
  634. // Thaw all the servers.
  635. // This routine must not throw. It's safe in a destructor
  636. //
  637. // DISCUSS WITH BRIAN....WE MUST RETURN "SUCCESS" only if the
  638. // servers were all still frozen. Otherwise the snapshot must
  639. // have been cancelled.
  640. //
  641. HRESULT
  642. Snapshot::Thaw () throw ()
  643. {
  644. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"Snapshot::Thaw");
  645. HRESULT hr = S_OK;
  646. AutoNewHandler t;
  647. // Ask the servers to thaw
  648. //
  649. for (ServerIter i=m_FrozenServers.begin (); i != m_FrozenServers.end (); i++)
  650. {
  651. try
  652. {
  653. if (!(*i)->Thaw ())
  654. {
  655. hr = E_SQLLIB_GENERIC;
  656. }
  657. }
  658. catch (...)
  659. {
  660. hr = E_SQLLIB_GENERIC;
  661. ft.LogError(VSS_ERROR_SQLLIB_ERRORTHAWSERVER, VSSDBG_SQLLIB << ((**i).GetName ()).c_str ());
  662. }
  663. }
  664. // We still have the original list of servers.
  665. // The snapshot object is reusable if another "Prepare" is done, which will
  666. // re-enumerate the servers.
  667. //
  668. m_Status = Enumerated;
  669. return hr;
  670. }
  671. // Setup some try/catch/handlers for our interface...
  672. // The invoker defines "hr" which is set if an exception
  673. // occurs.
  674. //
  675. #define TRY_SQLLIB \
  676. try {\
  677. AutoNewHandler _myNewHandler;
  678. #define END_SQLLIB \
  679. } catch (HRESULT& e)\
  680. {\
  681. ft.hr = e;\
  682. }\
  683. catch (...)\
  684. {\
  685. ft.hr = E_SQLLIB_GENERIC;\
  686. }
  687. //-------------------------------------------------------------------------
  688. // Create an object to handle enumerations
  689. //
  690. CSqlEnumerator*
  691. CreateSqlEnumerator () throw ()
  692. {
  693. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"CreateSqlEnumerator");
  694. try
  695. {
  696. return new SqlEnumerator;
  697. }
  698. catch (...)
  699. {
  700. ft.Trace(VSSDBG_SQLLIB, L"Out of memory");
  701. }
  702. return NULL;
  703. }
  704. //-------------------------------------------------------------------------
  705. //
  706. SqlEnumerator::~SqlEnumerator ()
  707. {
  708. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::~SqlEnumerator");
  709. if (m_pServers)
  710. delete m_pServers;
  711. }
  712. //-------------------------------------------------------------------------
  713. // Begin retrieval of the servers.
  714. //
  715. HRESULT
  716. SqlEnumerator::FirstServer (ServerInfo* pSrv) throw ()
  717. {
  718. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstServer");
  719. if (m_pServers)
  720. {
  721. delete m_pServers;
  722. m_pServers = NULL;
  723. }
  724. m_CurrServer = 0;
  725. TRY_SQLLIB
  726. {
  727. m_pServers = EnumerateServers ();
  728. if (m_pServers->size () == 0)
  729. {
  730. ft.hr = DB_S_ENDOFROWSET;
  731. }
  732. else
  733. {
  734. wcscpy (pSrv->name, (*m_pServers)[0].c_str ());
  735. pSrv->isOnline = IsServerOnline (pSrv->name) ? true : false;
  736. m_CurrServer = 1;
  737. ft.hr = NOERROR;
  738. }
  739. }
  740. END_SQLLIB
  741. return ft.hr;
  742. }
  743. //-------------------------------------------------------------------------
  744. // Continue retrieval of the servers.
  745. //
  746. HRESULT
  747. SqlEnumerator::NextServer (ServerInfo* pSrv) throw ()
  748. {
  749. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextServer");
  750. if (!m_pServers)
  751. {
  752. ft.hr = E_SQLLIB_PROTO;
  753. }
  754. else
  755. {
  756. TRY_SQLLIB
  757. {
  758. if (m_CurrServer >= m_pServers->size ())
  759. {
  760. ft.hr = DB_S_ENDOFROWSET;
  761. }
  762. else
  763. {
  764. wcscpy (pSrv->name, (*m_pServers)[m_CurrServer].c_str ());
  765. m_CurrServer++;
  766. pSrv->isOnline = IsServerOnline (pSrv->name) ? true : false;
  767. ft.hr = NOERROR;
  768. }
  769. }
  770. END_SQLLIB
  771. }
  772. return ft.hr;
  773. }
  774. //-------------------------------------------------------------------------
  775. // Begin retrieval of the databases
  776. //
  777. HRESULT
  778. SqlEnumerator::FirstDatabase (const WCHAR *pServerName, DatabaseInfo* pDbInfo) throw ()
  779. {
  780. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstDatabase");
  781. TRY_SQLLIB
  782. {
  783. m_Connection.Connect (pServerName);
  784. m_Connection.SetCommand (
  785. L"select name,DATABASEPROPERTY(name,'IsTruncLog') from master..sysdatabases");
  786. m_Connection.ExecCommand ();
  787. if (!m_Connection.FetchFirst ())
  788. {
  789. ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
  790. THROW_GENERIC;
  791. }
  792. WCHAR *pDbName = (WCHAR*)m_Connection.AccessColumn (1);
  793. int* pSimple = (int*)m_Connection.AccessColumn (2);
  794. wcscpy (pDbInfo->name, pDbName);
  795. pDbInfo->supportsFreeze = *pSimple &&
  796. m_Connection.GetServerVersion () >= 7;
  797. m_State = DatabaseQueryActive;
  798. ft.hr = NOERROR;
  799. }
  800. END_SQLLIB
  801. return ft.hr;
  802. }
  803. //-------------------------------------------------------------------------
  804. // Continue retrieval of the databases
  805. //
  806. HRESULT
  807. SqlEnumerator::NextDatabase (DatabaseInfo* pDbInfo) throw ()
  808. {
  809. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextDatabase");
  810. if (m_State != DatabaseQueryActive)
  811. {
  812. ft.hr = E_SQLLIB_PROTO;
  813. }
  814. else
  815. {
  816. TRY_SQLLIB
  817. {
  818. if (!m_Connection.FetchNext ())
  819. {
  820. ft.hr = DB_S_ENDOFROWSET;
  821. }
  822. else
  823. {
  824. WCHAR* pDbName = (WCHAR*)m_Connection.AccessColumn (1);
  825. int* pSimple = (int*)m_Connection.AccessColumn (2);
  826. wcscpy (pDbInfo->name, pDbName);
  827. pDbInfo->supportsFreeze = *pSimple &&
  828. m_Connection.GetServerVersion () >= 7;
  829. ft.hr = NOERROR;
  830. }
  831. }
  832. END_SQLLIB
  833. }
  834. return ft.hr;
  835. }
  836. //-------------------------------------------------------------------------
  837. // Begin retrieval of the database files
  838. //
  839. HRESULT
  840. SqlEnumerator::FirstFile (
  841. const WCHAR* pServerName,
  842. const WCHAR* pDbName,
  843. DatabaseFileInfo* pFileInfo) throw ()
  844. {
  845. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::FirstFile");
  846. TRY_SQLLIB
  847. {
  848. m_Connection.Connect (pServerName);
  849. WString query;
  850. if (m_Connection.GetServerVersion () >= 8)
  851. {
  852. query = L"select rtrim(filename),status & 64 from sysaltfiles where DB_ID('"
  853. + WString(pDbName) + L"') = dbid";
  854. }
  855. else
  856. {
  857. query = L"select rtrim(filename),status & 64 from ["
  858. + WString(pDbName) + L"]..sysfiles";
  859. }
  860. m_Connection.SetCommand (query);
  861. m_Connection.ExecCommand ();
  862. if (!m_Connection.FetchFirst ())
  863. {
  864. ft.LogError(VSS_ERROR_SQLLIB_NORESULTFORSYSDB, VSSDBG_SQLLIB);
  865. THROW_GENERIC;
  866. }
  867. WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
  868. int* pLogFile = (int*)m_Connection.AccessColumn (2);
  869. wcscpy (pFileInfo->name, pName);
  870. pFileInfo->isLogFile = (*pLogFile != 0);
  871. m_State = FileQueryActive;
  872. ft.hr = NOERROR;
  873. }
  874. END_SQLLIB
  875. return ft.hr;
  876. }
  877. //-------------------------------------------------------------------------
  878. // Continue retrieval of the files
  879. //
  880. HRESULT
  881. SqlEnumerator::NextFile (DatabaseFileInfo* pFileInfo) throw ()
  882. {
  883. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"SqlEnumerator::NextFile");
  884. if (m_State != FileQueryActive)
  885. {
  886. ft.hr = E_SQLLIB_PROTO;
  887. }
  888. else
  889. {
  890. TRY_SQLLIB
  891. {
  892. if (!m_Connection.FetchNext ())
  893. {
  894. ft.hr = DB_S_ENDOFROWSET;
  895. }
  896. else
  897. {
  898. WCHAR* pName = (WCHAR*)m_Connection.AccessColumn (1);
  899. int* pLogFile = (int*)m_Connection.AccessColumn (2);
  900. wcscpy (pFileInfo->name, pName);
  901. pFileInfo->isLogFile = (*pLogFile != 0);
  902. ft.hr = NOERROR;
  903. }
  904. }
  905. END_SQLLIB
  906. }
  907. return ft.hr;
  908. }