main
cpp 1,002 lines 31.8 KB
Raw
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_)
24 extern 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
43 static 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 );
50 HRESULT DumpErrorRecords();
51 static HRESULT FileSpecToString(
52 __in const SQL_FILESPEC* psf,
53 __out LPWSTR* ppwz
54 );
55 static 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 ********************************************************************/
67 extern "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
169 LExit:
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 *********************************************************************/
185 extern "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
203 LExit:
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 *********************************************************************/
213 extern "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
233 LExit:
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 ********************************************************************/
248 extern "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
268 LExit:
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 ********************************************************************/
283 extern "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
322 LExit:
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 ********************************************************************/
336 extern "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);
363 LExit:
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 ********************************************************************/
375 extern "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);
398 LExit:
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 ********************************************************************/
410 extern "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);
437 LExit:
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 ********************************************************************/
449 extern "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
484 LExit:
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 ********************************************************************/
500 extern "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
523 LExit:
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 ********************************************************************/
535 extern "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
562 LExit:
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 ********************************************************************/
575 extern "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
628 LExit:
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 ********************************************************************/
652 extern "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
701 LExit:
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 ********************************************************************/
714 extern "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
791 LExit:
792 ReleaseObject(pIErrorInfoRecord);
793 ReleaseObject(pIErrorRecords);
794 ReleaseObject(pIErrorInfoAll);
795 ReleaseObject(pISupportErrorInfo);
796
797 return hr;
798 }
799
800
801 //
802 // private
803 //
804
805 static 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
840 LExit:
841 ReleaseObject(pidbProperties);
842 ReleaseObject(pidbInitialize);
843
844 return hr;
845 }
846
847 HRESULT 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
900 LExit:
901 ReleaseNullBSTR(bstrDescription);
902 ReleaseObject(pIErrorInfoRecord);
903 ReleaseObject(pIErrorRecords);
904 ReleaseObject(pIErrorInfo);
905
906 return hr;
907 }
908
909 /********************************************************************
910 FileSpecToString
911
912 *********************************************************************/
913 static 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
959 LExit:
960 ReleaseStr(pwz);
961 return hr;
962 }
963
964 static 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
999 LExit:
1000 ReleaseStr(pwz);
1001 return hr;
1002 }