aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/sqlutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/sqlutil.cpp1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp
new file mode 100644
index 00000000..782c7088
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/sqlutil.cpp
@@ -0,0 +1,1002 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// okay, this may look a little weird, but sqlutil.h cannot be in the
6// pre-compiled header because we need to #define these things so the
7// correct GUID's get pulled into this object file
8#include <initguid.h>
9#define DBINITCONSTANTS
10#include "sqlutil.h"
11
12
13//Please note that only SQL native client 11 has TLS1.2 support
14#define _SQLNCLI_OLEDB_DEPRECATE_WARNING
15
16#if !defined(SQLNCLI_VER)
17#define SQLNCLI_VER 1100
18#endif
19
20#if SQLNCLI_VER >= 1100
21#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
22#define SQLNCLI_CLSID CLSID_SQLNCLI11
23#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
24extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } };
25#endif // SQLNCLI_VER >= 1100
26
27// Exit macros
28#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
29#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
30#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
31#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
32#define SqlExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
33#define SqlExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
34#define SqlExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
35#define SqlExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__)
36#define SqlExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__)
37#define SqlExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SQLUTIL, p, x, e, s, __VA_ARGS__)
38#define SqlExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, p, x, s, __VA_ARGS__)
39#define SqlExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SQLUTIL, e, x, s, __VA_ARGS__)
40#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__)
41
42// private prototypes
43static HRESULT InitializeDatabaseConnection(
44 __in REFCLSID rclsid,
45 __in_z LPCSTR szFriendlyClsidName,
46 __in DBPROPSET rgdbpsetInit[],
47 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
48 __out IDBCreateSession** ppidbSession
49 );
50HRESULT DumpErrorRecords();
51static HRESULT FileSpecToString(
52 __in const SQL_FILESPEC* psf,
53 __out LPWSTR* ppwz
54 );
55static HRESULT EscapeSqlIdentifier(
56 __in_z LPCWSTR wzDatabase,
57 __deref_out_z LPWSTR* ppwz
58 );
59
60
61/********************************************************************
62 SqlConnectDatabase - establishes a connection to a database
63
64 NOTE: wzInstance is optional
65 if fIntegratedAuth is set then wzUser and wzPassword are ignored
66********************************************************************/
67extern "C" HRESULT DAPI SqlConnectDatabase(
68 __in_z LPCWSTR wzServer,
69 __in_z LPCWSTR wzInstance,
70 __in_z LPCWSTR wzDatabase,
71 __in BOOL fIntegratedAuth,
72 __in_z LPCWSTR wzUser,
73 __in_z LPCWSTR wzPassword,
74 __out IDBCreateSession** ppidbSession
75 )
76{
77 Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession);
78
79 HRESULT hr = S_OK;
80 LPWSTR pwzServerInstance = NULL;
81 DBPROP rgdbpInit[4] = { };
82 DBPROPSET rgdbpsetInit[1] = { };
83 ULONG cProperties = 0;
84
85 // if there is an instance
86 if (wzInstance && *wzInstance)
87 {
88 hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance);
89 }
90 else
91 {
92 hr = StrAllocString(&pwzServerInstance, wzServer, 0);
93 }
94 SqlExitOnFailure(hr, "failed to allocate memory for the server instance");
95
96 // server[\instance]
97 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE;
98 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
99 rgdbpInit[cProperties].colid = DB_NULLID;
100 ::VariantInit(&rgdbpInit[cProperties].vValue);
101 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
102 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance);
103 ++cProperties;
104
105 // database
106 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG;
107 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
108 rgdbpInit[cProperties].colid = DB_NULLID;
109 ::VariantInit(&rgdbpInit[cProperties].vValue);
110 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
111 rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase);
112 ++cProperties;
113
114 if (fIntegratedAuth)
115 {
116 // username
117 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED;
118 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
119 rgdbpInit[cProperties].colid = DB_NULLID;
120 ::VariantInit(&rgdbpInit[cProperties].vValue);
121 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
122 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication
123 ++cProperties;
124 }
125 else
126 {
127 // username
128 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID;
129 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
130 rgdbpInit[cProperties].colid = DB_NULLID;
131 ::VariantInit(&rgdbpInit[cProperties].vValue);
132 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
133 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser);
134 ++cProperties;
135
136 // password
137 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD;
138 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
139 rgdbpInit[cProperties].colid = DB_NULLID;
140 ::VariantInit(&rgdbpInit[cProperties].vValue);
141 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
142 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword);
143 ++cProperties;
144 }
145
146 // put the properties into a set
147 rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT;
148 rgdbpsetInit[0].rgProperties = rgdbpInit;
149 rgdbpsetInit[0].cProperties = cProperties;
150
151 // obtain access to the SQL Native Client provider
152 hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
153 if (FAILED(hr))
154 {
155 SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB...");
156
157 // try OLE DB but if that fails return original error failure
158 HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
159 if (FAILED(hr2))
160 {
161 SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up.");
162 }
163 else
164 {
165 hr = S_OK;
166 }
167 }
168
169LExit:
170 for (; 0 < cProperties; cProperties--)
171 {
172 ::VariantClear(&rgdbpInit[cProperties - 1].vValue);
173 }
174
175 ReleaseStr(pwzServerInstance);
176
177 return hr;
178}
179
180
181/********************************************************************
182 SqlStartTransaction - Starts a new transaction that must be ended
183
184*********************************************************************/
185extern "C" HRESULT DAPI SqlStartTransaction(
186 __in IDBCreateSession* pidbSession,
187 __out IDBCreateCommand** ppidbCommand,
188 __out ITransaction** ppit
189 )
190{
191 Assert(pidbSession && ppit);
192
193 HRESULT hr = S_OK;
194
195 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand);
196 SqlExitOnFailure(hr, "unable to create command from session");
197
198 hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit);
199 SqlExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal");
200
201 hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
202
203LExit:
204
205 return hr;
206}
207
208/********************************************************************
209 SqlEndTransaction - Ends the transaction
210
211 NOTE: if fCommit, will commit the transaction, otherwise rolls back
212*********************************************************************/
213extern "C" HRESULT DAPI SqlEndTransaction(
214 __in ITransaction* pit,
215 __in BOOL fCommit
216 )
217{
218 Assert(pit);
219
220 HRESULT hr = S_OK;
221
222 if (fCommit)
223 {
224 hr = pit->Commit(FALSE, XACTTC_SYNC, 0);
225 SqlExitOnFailure(hr, "commit of transaction failed");
226 }
227 else
228 {
229 hr = pit->Abort(NULL, FALSE, FALSE);
230 SqlExitOnFailure(hr, "abort of transaction failed");
231 }
232
233LExit:
234
235 return hr;
236}
237
238
239/********************************************************************
240 SqlDatabaseExists - determines if database exists
241
242 NOTE: wzInstance is optional
243 if fIntegratedAuth is set then wzUser and wzPassword are ignored
244 returns S_OK if database exist
245 returns S_FALSE if database does not exist
246 returns E_* on error
247********************************************************************/
248extern "C" HRESULT DAPI SqlDatabaseExists(
249 __in_z LPCWSTR wzServer,
250 __in_z LPCWSTR wzInstance,
251 __in_z LPCWSTR wzDatabase,
252 __in BOOL fIntegratedAuth,
253 __in_z LPCWSTR wzUser,
254 __in_z LPCWSTR wzPassword,
255 __out_opt BSTR* pbstrErrorDescription
256 )
257{
258 Assert(wzServer && wzDatabase && *wzDatabase);
259
260 HRESULT hr = S_OK;
261 IDBCreateSession* pidbSession = NULL;
262
263 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
264 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
265
266 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
267
268LExit:
269 ReleaseObject(pidbSession);
270
271 return hr;
272}
273
274
275/********************************************************************
276 SqlSessionDatabaseExists - determines if database exists
277
278 NOTE: pidbSession must be connected to master database
279 returns S_OK if database exist
280 returns S_FALSE if database does not exist
281 returns E_* on error
282********************************************************************/
283extern "C" HRESULT DAPI SqlSessionDatabaseExists(
284 __in IDBCreateSession* pidbSession,
285 __in_z LPCWSTR wzDatabase,
286 __out_opt BSTR* pbstrErrorDescription
287 )
288{
289 Assert(pidbSession && wzDatabase && *wzDatabase);
290
291 HRESULT hr = S_OK;
292
293 LPWSTR pwzQuery = NULL;
294 IRowset* pirs = NULL;
295
296 DBCOUNTITEM cRows = 0;
297 HROW rghRows[1];
298 HROW* prow = rghRows;
299
300 //
301 // query to see if the database exists
302 //
303 hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase);
304 SqlExitOnFailure(hr, "failed to allocate query string to ensure database exists");
305
306 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription);
307 SqlExitOnFailure(hr, "failed to get database list from 'master' database");
308 Assert(pirs);
309
310 //
311 // check to see if the database was returned
312 //
313 hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow);
314 SqlExitOnFailure(hr, "failed to get row with database name");
315
316 // succeeded but no database
317 if ((DB_S_ENDOFROWSET == hr) || (0 == cRows))
318 {
319 hr = S_FALSE;
320 }
321
322LExit:
323 ReleaseObject(pirs);
324 ReleaseStr(pwzQuery);
325
326 return hr;
327}
328
329
330/********************************************************************
331 SqlDatabaseEnsureExists - creates a database if it does not exist
332
333 NOTE: wzInstance is optional
334 if fIntegratedAuth is set then wzUser and wzPassword are ignored
335********************************************************************/
336extern "C" HRESULT DAPI SqlDatabaseEnsureExists(
337 __in_z LPCWSTR wzServer,
338 __in_z LPCWSTR wzInstance,
339 __in_z LPCWSTR wzDatabase,
340 __in BOOL fIntegratedAuth,
341 __in_z LPCWSTR wzUser,
342 __in_z LPCWSTR wzPassword,
343 __in_opt const SQL_FILESPEC* psfDatabase,
344 __in_opt const SQL_FILESPEC* psfLog,
345 __out_opt BSTR* pbstrErrorDescription
346 )
347{
348 Assert(wzServer && wzDatabase && *wzDatabase);
349
350 HRESULT hr = S_OK;
351 IDBCreateSession* pidbSession = NULL;
352
353 //
354 // connect to the master database to create the new database
355 //
356 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
357 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
358
359 hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
360 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
361
362 Assert(S_OK == hr);
363LExit:
364 ReleaseObject(pidbSession);
365
366 return hr;
367}
368
369
370/********************************************************************
371 SqlSessionDatabaseEnsureExists - creates a database if it does not exist
372
373 NOTE: pidbSession must be connected to the master database
374********************************************************************/
375extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists(
376 __in IDBCreateSession* pidbSession,
377 __in_z LPCWSTR wzDatabase,
378 __in_opt const SQL_FILESPEC* psfDatabase,
379 __in_opt const SQL_FILESPEC* psfLog,
380 __out_opt BSTR* pbstrErrorDescription
381 )
382{
383 Assert(pidbSession && wzDatabase && *wzDatabase);
384
385 HRESULT hr = S_OK;
386
387 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
388 SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
389
390 if (S_FALSE == hr)
391 {
392 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
393 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
394 }
395 // else database already exists, return S_FALSE
396
397 Assert(S_OK == hr);
398LExit:
399
400 return hr;
401}
402
403
404/********************************************************************
405 SqlCreateDatabase - creates a database on the server
406
407 NOTE: wzInstance is optional
408 if fIntegratedAuth is set then wzUser and wzPassword are ignored
409********************************************************************/
410extern "C" HRESULT DAPI SqlCreateDatabase(
411 __in_z LPCWSTR wzServer,
412 __in_z LPCWSTR wzInstance,
413 __in_z LPCWSTR wzDatabase,
414 __in BOOL fIntegratedAuth,
415 __in_z LPCWSTR wzUser,
416 __in_z LPCWSTR wzPassword,
417 __in_opt const SQL_FILESPEC* psfDatabase,
418 __in_opt const SQL_FILESPEC* psfLog,
419 __out_opt BSTR* pbstrErrorDescription
420 )
421{
422 Assert(wzServer && wzDatabase && *wzDatabase);
423
424 HRESULT hr = S_OK;
425 IDBCreateSession* pidbSession = NULL;
426
427 //
428 // connect to the master database to create the new database
429 //
430 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
431 SqlExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
432
433 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
434 SqlExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
435
436 Assert(S_OK == hr);
437LExit:
438 ReleaseObject(pidbSession);
439
440 return hr;
441}
442
443
444/********************************************************************
445 SqlSessionCreateDatabase - creates a database on the server
446
447 NOTE: pidbSession must be connected to the master database
448********************************************************************/
449extern "C" HRESULT DAPI SqlSessionCreateDatabase(
450 __in IDBCreateSession* pidbSession,
451 __in_z LPCWSTR wzDatabase,
452 __in_opt const SQL_FILESPEC* psfDatabase,
453 __in_opt const SQL_FILESPEC* psfLog,
454 __out_opt BSTR* pbstrErrorDescription
455 )
456{
457 HRESULT hr = S_OK;
458 LPWSTR pwzDbFile = NULL;
459 LPWSTR pwzLogFile = NULL;
460 LPWSTR pwzQuery = NULL;
461 LPWSTR pwzDatabaseEscaped = NULL;
462
463 if (psfDatabase)
464 {
465 hr = FileSpecToString(psfDatabase, &pwzDbFile);
466 SqlExitOnFailure(hr, "failed to convert db filespec to string");
467 }
468
469 if (psfLog)
470 {
471 hr = FileSpecToString(psfLog, &pwzLogFile);
472 SqlExitOnFailure(hr, "failed to convert log filespec to string");
473 }
474
475 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
476 SqlExitOnFailure(hr, "failed to escape database string");
477
478 hr = StrAllocFormatted(&pwzQuery, L"CREATE DATABASE %s %s%s %s%s", pwzDatabaseEscaped, pwzDbFile ? L"ON " : L"", pwzDbFile ? pwzDbFile : L"", pwzLogFile ? L"LOG ON " : L"", pwzLogFile ? pwzLogFile : L"");
479 SqlExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped);
480
481 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
482 SqlExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery);
483
484LExit:
485 ReleaseStr(pwzQuery);
486 ReleaseStr(pwzLogFile);
487 ReleaseStr(pwzDbFile);
488 ReleaseStr(pwzDatabaseEscaped);
489
490 return hr;
491}
492
493
494/********************************************************************
495 SqlDropDatabase - removes a database from a server if it exists
496
497 NOTE: wzInstance is optional
498 if fIntegratedAuth is set then wzUser and wzPassword are ignored
499********************************************************************/
500extern "C" HRESULT DAPI SqlDropDatabase(
501 __in_z LPCWSTR wzServer,
502 __in_z LPCWSTR wzInstance,
503 __in_z LPCWSTR wzDatabase,
504 __in BOOL fIntegratedAuth,
505 __in_z LPCWSTR wzUser,
506 __in_z LPCWSTR wzPassword,
507 __out_opt BSTR* pbstrErrorDescription
508 )
509{
510 Assert(wzServer && wzDatabase && *wzDatabase);
511
512 HRESULT hr = S_OK;
513 IDBCreateSession* pidbSession = NULL;
514
515 //
516 // connect to the master database to search for wzDatabase
517 //
518 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
519 SqlExitOnFailure(hr, "Failed to connect to 'master' database");
520
521 hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription);
522
523LExit:
524 ReleaseObject(pidbSession);
525
526 return hr;
527}
528
529
530/********************************************************************
531 SqlSessionDropDatabase - removes a database from a server if it exists
532
533 NOTE: pidbSession must be connected to the master database
534********************************************************************/
535extern "C" HRESULT DAPI SqlSessionDropDatabase(
536 __in IDBCreateSession* pidbSession,
537 __in_z LPCWSTR wzDatabase,
538 __out_opt BSTR* pbstrErrorDescription
539 )
540{
541 Assert(pidbSession && wzDatabase && *wzDatabase);
542
543 HRESULT hr = S_OK;
544 LPWSTR pwzQuery = NULL;
545 LPWSTR pwzDatabaseEscaped = NULL;
546
547 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
548 SqlExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
549
550 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
551 SqlExitOnFailure(hr, "failed to escape database string");
552
553 if (S_OK == hr)
554 {
555 hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped);
556 SqlExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped);
557
558 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
559 SqlExitOnFailure(hr, "Failed to drop database");
560 }
561
562LExit:
563 ReleaseStr(pwzQuery);
564 ReleaseStr(pwzDatabaseEscaped);
565
566 return hr;
567}
568
569
570/********************************************************************
571 SqlSessionExecuteQuery - executes a query and returns the results if desired
572
573 NOTE: ppirs and pcRoes and pbstrErrorDescription are optional
574********************************************************************/
575extern "C" HRESULT DAPI SqlSessionExecuteQuery(
576 __in IDBCreateSession* pidbSession,
577 __in __sql_command LPCWSTR wzSql,
578 __out_opt IRowset** ppirs,
579 __out_opt DBROWCOUNT* pcRows,
580 __out_opt BSTR* pbstrErrorDescription
581 )
582{
583 Assert(pidbSession);
584
585 HRESULT hr = S_OK;
586 IDBCreateCommand* pidbCommand = NULL;
587 ICommandText* picmdText = NULL;
588 ICommand* picmd = NULL;
589 DBROWCOUNT cRows = 0;
590
591 if (pcRows)
592 {
593 *pcRows = NULL;
594 }
595
596 //
597 // create the command
598 //
599 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand);
600 SqlExitOnFailure(hr, "failed to create database session");
601 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
602 SqlExitOnFailure(hr, "failed to create command to execute session");
603
604 //
605 // set the sql text into the command
606 //
607 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
608 SqlExitOnFailure(hr, "failed to get command text object for command");
609 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
610 SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
611
612 //
613 // execute the command
614 //
615 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
616 SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
617
618 if (DB_S_ERRORSOCCURRED == hr)
619 {
620 hr = E_FAIL;
621 }
622
623 if (pcRows)
624 {
625 *pcRows = cRows;
626 }
627
628LExit:
629
630 if (FAILED(hr) && picmd && pbstrErrorDescription)
631 {
632 HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English
633 if (FAILED(hrGetErrors))
634 {
635 ReleaseBSTR(*pbstrErrorDescription);
636 }
637 }
638
639 ReleaseObject(picmd);
640 ReleaseObject(picmdText);
641 ReleaseObject(pidbCommand);
642
643 return hr;
644}
645
646
647/********************************************************************
648 SqlCommandExecuteQuery - executes a SQL command and returns the results if desired
649
650 NOTE: ppirs and pcRoes are optional
651********************************************************************/
652extern "C" HRESULT DAPI SqlCommandExecuteQuery(
653 __in IDBCreateCommand* pidbCommand,
654 __in __sql_command LPCWSTR wzSql,
655 __out IRowset** ppirs,
656 __out DBROWCOUNT* pcRows
657 )
658{
659 Assert(pidbCommand);
660
661 HRESULT hr = S_OK;
662 ICommandText* picmdText = NULL;
663 ICommand* picmd = NULL;
664 DBROWCOUNT cRows = 0;
665
666 if (pcRows)
667 {
668 *pcRows = NULL;
669 }
670
671 //
672 // create the command
673 //
674 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
675 SqlExitOnFailure(hr, "failed to create command to execute session");
676
677 //
678 // set the sql text into the command
679 //
680 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
681 SqlExitOnFailure(hr, "failed to get command text object for command");
682 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
683 SqlExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
684
685 //
686 // execute the command
687 //
688 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
689 SqlExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
690
691 if (DB_S_ERRORSOCCURRED == hr)
692 {
693 hr = E_FAIL;
694 }
695
696 if (pcRows)
697 {
698 *pcRows = cRows;
699 }
700
701LExit:
702 ReleaseObject(picmd);
703 ReleaseObject(picmdText);
704
705 return hr;
706}
707
708
709/********************************************************************
710 SqlGetErrorInfo - gets error information from the last SQL function call
711
712 NOTE: pbstrErrorSource and pbstrErrorDescription are optional
713********************************************************************/
714extern "C" HRESULT DAPI SqlGetErrorInfo(
715 __in IUnknown* pObjectWithError,
716 __in REFIID IID_InterfaceWithError,
717 __in DWORD dwLocaleId,
718 __out_opt BSTR* pbstrErrorSource,
719 __out_opt BSTR* pbstrErrorDescription
720 )
721{
722 HRESULT hr = S_OK;
723 Assert(pObjectWithError);
724
725 // interfaces needed to extract error information out
726 ISupportErrorInfo* pISupportErrorInfo = NULL;
727 IErrorInfo* pIErrorInfoAll = NULL;
728 IErrorRecords* pIErrorRecords = NULL;
729 IErrorInfo* pIErrorInfoRecord = NULL;
730
731 // only ask for error information if the interface supports it.
732 hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo);
733 SqlExitOnFailure(hr, "No error information was found for object.");
734
735 hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError);
736 SqlExitOnFailure(hr, "InterfaceWithError is not supported for object with error");
737
738 // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway
739 hr = ::GetErrorInfo(0, &pIErrorInfoAll);
740 SqlExitOnFailure(hr, "failed to get error info");
741
742 if (S_OK == hr && pIErrorInfoAll)
743 {
744 // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records
745 hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
746 if (SUCCEEDED(hr))
747 {
748 ULONG cErrors = 0;
749 pIErrorRecords->GetRecordCount(&cErrors);
750
751 // get the error information for each record
752 for (ULONG i = 0; i < cErrors; ++i)
753 {
754 hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord);
755 if (SUCCEEDED(hr))
756 {
757 if (pbstrErrorSource)
758 {
759 pIErrorInfoRecord->GetSource(pbstrErrorSource);
760 }
761 if (pbstrErrorDescription)
762 {
763 pIErrorInfoRecord->GetDescription(pbstrErrorDescription);
764 }
765
766 ReleaseNullObject(pIErrorInfoRecord);
767
768 break; // TODO: return more than one error in the future!
769 }
770 }
771
772 ReleaseNullObject(pIErrorRecords);
773 }
774 else // we have a simple error record
775 {
776 if (pbstrErrorSource)
777 {
778 pIErrorInfoAll->GetSource(pbstrErrorSource);
779 }
780 if (pbstrErrorDescription)
781 {
782 pIErrorInfoAll->GetDescription(pbstrErrorDescription);
783 }
784 }
785 }
786 else
787 {
788 hr = E_NOMOREITEMS;
789 }
790
791LExit:
792 ReleaseObject(pIErrorInfoRecord);
793 ReleaseObject(pIErrorRecords);
794 ReleaseObject(pIErrorInfoAll);
795 ReleaseObject(pISupportErrorInfo);
796
797 return hr;
798}
799
800
801//
802// private
803//
804
805static HRESULT InitializeDatabaseConnection(
806 __in REFCLSID rclsid,
807 __in_z LPCSTR szFriendlyClsidName,
808 __in DBPROPSET rgdbpsetInit[],
809 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
810 __out IDBCreateSession** ppidbSession
811)
812{
813 Unused(szFriendlyClsidName); // only used in DEBUG builds
814
815 HRESULT hr = S_OK;
816 IDBInitialize* pidbInitialize = NULL;
817 IDBProperties* pidbProperties = NULL;
818
819 hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize);
820 SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName);
821
822 // create and set the property set
823 hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties);
824 SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName);
825
826 hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit);
827 SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName);
828
829 // initialize connection to datasource
830 hr = pidbInitialize->Initialize();
831 if (FAILED(hr))
832 {
833 DumpErrorRecords();
834 }
835 SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName);
836
837 hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession);
838 SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName);
839
840LExit:
841 ReleaseObject(pidbProperties);
842 ReleaseObject(pidbInitialize);
843
844 return hr;
845}
846
847HRESULT DumpErrorRecords()
848{
849 HRESULT hr = S_OK;
850 IErrorInfo* pIErrorInfo = NULL;
851 IErrorRecords* pIErrorRecords = NULL;
852 IErrorInfo* pIErrorInfoRecord = NULL;
853 BSTR bstrDescription = NULL;
854 ULONG i = 0;
855 ULONG cRecords = 0;
856 ERRORINFO ErrorInfo = { };
857
858 // Get IErrorInfo pointer from OLE.
859 hr = ::GetErrorInfo(0, &pIErrorInfo);
860 if (FAILED(hr))
861 {
862 ExitFunction();
863 }
864
865 // QI for IID_IErrorRecords.
866 hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
867 if (FAILED(hr))
868 {
869 ExitFunction();
870 }
871
872 // Get error record count.
873 hr = pIErrorRecords->GetRecordCount(&cRecords);
874 if (FAILED(hr))
875 {
876 ExitFunction();
877 }
878
879 // Loop through the error records.
880 for (i = 0; i < cRecords; i++)
881 {
882 // Get pIErrorInfo from pIErrorRecords.
883 hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord);
884
885 if (SUCCEEDED(hr))
886 {
887 // Get error description and source.
888 hr = pIErrorInfoRecord->GetDescription(&bstrDescription);
889
890 // Retrieve the ErrorInfo structures.
891 hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo);
892
893 SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription);
894
895 ReleaseNullObject(pIErrorInfoRecord);
896 ReleaseNullBSTR(bstrDescription);
897 }
898 }
899
900LExit:
901 ReleaseNullBSTR(bstrDescription);
902 ReleaseObject(pIErrorInfoRecord);
903 ReleaseObject(pIErrorRecords);
904 ReleaseObject(pIErrorInfo);
905
906 return hr;
907}
908
909/********************************************************************
910 FileSpecToString
911
912*********************************************************************/
913static HRESULT FileSpecToString(
914 __in const SQL_FILESPEC* psf,
915 __out LPWSTR* ppwz
916 )
917{
918 Assert(psf && ppwz);
919
920 HRESULT hr = S_OK;
921 LPWSTR pwz = NULL;
922
923 hr = StrAllocString(&pwz, L"(", 1024);
924 SqlExitOnFailure(hr, "failed to allocate string for database file info");
925
926 SqlExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info");
927 SqlExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info");
928
929 hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName);
930 SqlExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName);
931
932 hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename);
933 SqlExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename);
934
935 if (0 != psf->wzSize[0])
936 {
937 hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize);
938 SqlExitOnFailure(hr, "failed to format database file info size: %ls", psf->wzSize);
939 }
940
941 if (0 != psf->wzMaxSize[0])
942 {
943 hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize);
944 SqlExitOnFailure(hr, "failed to format database file info maxsize: %ls", psf->wzMaxSize);
945 }
946
947 if (0 != psf->wzGrow[0])
948 {
949 hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow);
950 SqlExitOnFailure(hr, "failed to format database file info growth: %ls", psf->wzGrow);
951 }
952
953 hr = StrAllocFormatted(&pwz, L"%s)", pwz);
954 SqlExitOnFailure(hr, "failed to allocate string for file spec");
955
956 *ppwz = pwz;
957 pwz = NULL; // null here so it doesn't get freed below
958
959LExit:
960 ReleaseStr(pwz);
961 return hr;
962}
963
964static HRESULT EscapeSqlIdentifier(
965 __in_z LPCWSTR wzIdentifier,
966 __deref_out_z LPWSTR* ppwz
967 )
968{
969 Assert(ppwz);
970
971 HRESULT hr = S_OK;
972 LPWSTR pwz = NULL;
973
974 if (wzIdentifier == NULL)
975 {
976 //Just ignore a NULL identifier and clear out the result
977 ReleaseNullStr(*ppwz);
978 ExitFunction();
979 }
980
981 int cchIdentifier = lstrlenW(wzIdentifier);
982
983 //If an empty string or already escaped just copy
984 if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']'))
985 {
986 hr = StrAllocString(&pwz, wzIdentifier, 0);
987 SqlExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier);
988 }
989 else
990 {
991 //escape it
992 hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier);
993 SqlExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier);
994 }
995
996 *ppwz = pwz;
997 pwz = NULL; // null here so it doesn't get freed below
998
999LExit:
1000 ReleaseStr(pwz);
1001 return hr;
1002}