aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/sqlutil.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-09-03 11:22:38 -0700
committerRob Mensching <rob@firegiant.com>2017-09-03 13:33:33 -0700
commit5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch)
treea76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/sqlutil.cpp
parent8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff)
downloadwix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip
Initial commit
Diffstat (limited to 'src/dutil/sqlutil.cpp')
-rw-r--r--src/dutil/sqlutil.cpp868
1 files changed, 868 insertions, 0 deletions
diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp
new file mode 100644
index 00000000..099c6ae9
--- /dev/null
+++ b/src/dutil/sqlutil.cpp
@@ -0,0 +1,868 @@
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// private prototypes
13static HRESULT FileSpecToString(
14 __in const SQL_FILESPEC* psf,
15 __out LPWSTR* ppwz
16 );
17
18static HRESULT EscapeSqlIdentifier(
19 __in_z LPCWSTR wzDatabase,
20 __deref_out_z LPWSTR* ppwz
21 );
22
23
24/********************************************************************
25 SqlConnectDatabase - establishes a connection to a database
26
27 NOTE: wzInstance is optional
28 if fIntegratedAuth is set then wzUser and wzPassword are ignored
29********************************************************************/
30extern "C" HRESULT DAPI SqlConnectDatabase(
31 __in_z LPCWSTR wzServer,
32 __in_z LPCWSTR wzInstance,
33 __in_z LPCWSTR wzDatabase,
34 __in BOOL fIntegratedAuth,
35 __in_z LPCWSTR wzUser,
36 __in_z LPCWSTR wzPassword,
37 __out IDBCreateSession** ppidbSession
38 )
39{
40 Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession);
41
42 HRESULT hr = S_OK;
43 IDBInitialize* pidbInitialize = NULL;
44 IDBProperties* pidbProperties = NULL;
45
46 LPWSTR pwzServerInstance = NULL;
47 DBPROP rgdbpInit[4];
48 DBPROPSET rgdbpsetInit[1];
49 ULONG cProperties = 0;
50
51 memset(rgdbpInit, 0, sizeof(rgdbpInit));
52 memset(rgdbpsetInit, 0, sizeof(rgdbpsetInit));
53
54 //obtain access to the SQLOLEDB provider
55 hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER,
56 IID_IDBInitialize, (LPVOID*)&pidbInitialize);
57 ExitOnFailure(hr, "failed to create IID_IDBInitialize object");
58
59 // if there is an instance
60 if (wzInstance && *wzInstance)
61 {
62 hr = StrAllocFormatted(&pwzServerInstance, L"%s\\%s", wzServer, wzInstance);
63 }
64 else
65 {
66 hr = StrAllocString(&pwzServerInstance, wzServer, 0);
67 }
68 ExitOnFailure(hr, "failed to allocate memory for the server instance");
69
70 // server[\instance]
71 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_DATASOURCE;
72 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
73 rgdbpInit[cProperties].colid = DB_NULLID;
74 ::VariantInit(&rgdbpInit[cProperties].vValue);
75 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
76 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(pwzServerInstance);
77 ++cProperties;
78
79 // database
80 rgdbpInit[cProperties].dwPropertyID = DBPROP_INIT_CATALOG;
81 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
82 rgdbpInit[cProperties].colid = DB_NULLID;
83 ::VariantInit(&rgdbpInit[cProperties].vValue);
84 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
85 rgdbpInit[cProperties].vValue.bstrVal= ::SysAllocString(wzDatabase);
86 ++cProperties;
87
88 if (fIntegratedAuth)
89 {
90 // username
91 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_INTEGRATED;
92 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
93 rgdbpInit[cProperties].colid = DB_NULLID;
94 ::VariantInit(&rgdbpInit[cProperties].vValue);
95 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
96 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(L"SSPI"); // default windows authentication
97 ++cProperties;
98 }
99 else
100 {
101 // username
102 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_USERID;
103 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
104 rgdbpInit[cProperties].colid = DB_NULLID;
105 ::VariantInit(&rgdbpInit[cProperties].vValue);
106 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
107 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzUser);
108 ++cProperties;
109
110 // password
111 rgdbpInit[cProperties].dwPropertyID = DBPROP_AUTH_PASSWORD;
112 rgdbpInit[cProperties].dwOptions = DBPROPOPTIONS_REQUIRED;
113 rgdbpInit[cProperties].colid = DB_NULLID;
114 ::VariantInit(&rgdbpInit[cProperties].vValue);
115 rgdbpInit[cProperties].vValue.vt = VT_BSTR;
116 rgdbpInit[cProperties].vValue.bstrVal = ::SysAllocString(wzPassword);
117 ++cProperties;
118 }
119
120 // put the properties into a set
121 rgdbpsetInit[0].guidPropertySet = DBPROPSET_DBINIT;
122 rgdbpsetInit[0].rgProperties = rgdbpInit;
123 rgdbpsetInit[0].cProperties = cProperties;
124
125 // create and set the property set
126 hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties);
127 ExitOnFailure(hr, "failed to get IID_IDBProperties object");
128 hr = pidbProperties->SetProperties(1, rgdbpsetInit);
129 ExitOnFailure(hr, "failed to set properties");
130
131 //initialize connection to datasource
132 hr = pidbInitialize->Initialize();
133 ExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase);
134
135 hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession);
136
137LExit:
138 for (; 0 < cProperties; cProperties--)
139 {
140 ::VariantClear(&rgdbpInit[cProperties - 1].vValue);
141 }
142
143 ReleaseObject(pidbProperties);
144 ReleaseObject(pidbInitialize);
145 ReleaseStr(pwzServerInstance);
146
147 return hr;
148}
149
150
151/********************************************************************
152 SqlStartTransaction - Starts a new transaction that must be ended
153
154*********************************************************************/
155extern "C" HRESULT DAPI SqlStartTransaction(
156 __in IDBCreateSession* pidbSession,
157 __out IDBCreateCommand** ppidbCommand,
158 __out ITransaction** ppit
159 )
160{
161 Assert(pidbSession && ppit);
162
163 HRESULT hr = S_OK;
164
165 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)ppidbCommand);
166 ExitOnFailure(hr, "unable to create command from session");
167
168 hr = (*ppidbCommand)->QueryInterface(IID_ITransactionLocal, (LPVOID*)ppit);
169 ExitOnFailure(hr, "Unable to QueryInterface session to get ITransactionLocal");
170
171 hr = ((ITransactionLocal*)*ppit)->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
172
173LExit:
174
175 return hr;
176}
177
178/********************************************************************
179 SqlEndTransaction - Ends the transaction
180
181 NOTE: if fCommit, will commit the transaction, otherwise rolls back
182*********************************************************************/
183extern "C" HRESULT DAPI SqlEndTransaction(
184 __in ITransaction* pit,
185 __in BOOL fCommit
186 )
187{
188 Assert(pit);
189
190 HRESULT hr = S_OK;
191
192 if (fCommit)
193 {
194 hr = pit->Commit(FALSE, XACTTC_SYNC, 0);
195 ExitOnFailure(hr, "commit of transaction failed");
196 }
197 else
198 {
199 hr = pit->Abort(NULL, FALSE, FALSE);
200 ExitOnFailure(hr, "abort of transaction failed");
201 }
202
203LExit:
204
205 return hr;
206}
207
208
209/********************************************************************
210 SqlDatabaseExists - determines if database exists
211
212 NOTE: wzInstance is optional
213 if fIntegratedAuth is set then wzUser and wzPassword are ignored
214 returns S_OK if database exist
215 returns S_FALSE if database does not exist
216 returns E_* on error
217********************************************************************/
218extern "C" HRESULT DAPI SqlDatabaseExists(
219 __in_z LPCWSTR wzServer,
220 __in_z LPCWSTR wzInstance,
221 __in_z LPCWSTR wzDatabase,
222 __in BOOL fIntegratedAuth,
223 __in_z LPCWSTR wzUser,
224 __in_z LPCWSTR wzPassword,
225 __out_opt BSTR* pbstrErrorDescription
226 )
227{
228 Assert(wzServer && wzDatabase && *wzDatabase);
229
230 HRESULT hr = S_OK;
231 IDBCreateSession* pidbSession = NULL;
232
233 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
234 ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
235
236 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
237
238LExit:
239 ReleaseObject(pidbSession);
240
241 return hr;
242}
243
244
245/********************************************************************
246 SqlSessionDatabaseExists - determines if database exists
247
248 NOTE: pidbSession must be connected to master database
249 returns S_OK if database exist
250 returns S_FALSE if database does not exist
251 returns E_* on error
252********************************************************************/
253extern "C" HRESULT DAPI SqlSessionDatabaseExists(
254 __in IDBCreateSession* pidbSession,
255 __in_z LPCWSTR wzDatabase,
256 __out_opt BSTR* pbstrErrorDescription
257 )
258{
259 Assert(pidbSession && wzDatabase && *wzDatabase);
260
261 HRESULT hr = S_OK;
262
263 LPWSTR pwzQuery = NULL;
264 IRowset* pirs = NULL;
265
266 DBCOUNTITEM cRows = 0;
267 HROW rghRows[1];
268 HROW* prow = rghRows;
269
270 //
271 // query to see if the database exists
272 //
273 hr = StrAllocFormatted(&pwzQuery, L"SELECT name FROM sysdatabases WHERE name='%s'", wzDatabase);
274 ExitOnFailure(hr, "failed to allocate query string to ensure database exists");
275
276 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, &pirs, NULL, pbstrErrorDescription);
277 ExitOnFailure(hr, "failed to get database list from 'master' database");
278 Assert(pirs);
279
280 //
281 // check to see if the database was returned
282 //
283 hr = pirs->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &prow);
284 ExitOnFailure(hr, "failed to get row with database name");
285
286 // succeeded but no database
287 if ((DB_S_ENDOFROWSET == hr) || (0 == cRows))
288 {
289 hr = S_FALSE;
290 }
291
292LExit:
293 ReleaseObject(pirs);
294 ReleaseStr(pwzQuery);
295
296 return hr;
297}
298
299
300/********************************************************************
301 SqlDatabaseEnsureExists - creates a database if it does not exist
302
303 NOTE: wzInstance is optional
304 if fIntegratedAuth is set then wzUser and wzPassword are ignored
305********************************************************************/
306extern "C" HRESULT DAPI SqlDatabaseEnsureExists(
307 __in_z LPCWSTR wzServer,
308 __in_z LPCWSTR wzInstance,
309 __in_z LPCWSTR wzDatabase,
310 __in BOOL fIntegratedAuth,
311 __in_z LPCWSTR wzUser,
312 __in_z LPCWSTR wzPassword,
313 __in_opt const SQL_FILESPEC* psfDatabase,
314 __in_opt const SQL_FILESPEC* psfLog,
315 __out_opt BSTR* pbstrErrorDescription
316 )
317{
318 Assert(wzServer && wzDatabase && *wzDatabase);
319
320 HRESULT hr = S_OK;
321 IDBCreateSession* pidbSession = NULL;
322
323 //
324 // connect to the master database to create the new database
325 //
326 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
327 ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
328
329 hr = SqlSessionDatabaseEnsureExists(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
330 ExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
331
332 Assert(S_OK == hr);
333LExit:
334 ReleaseObject(pidbSession);
335
336 return hr;
337}
338
339
340/********************************************************************
341 SqlSessionDatabaseEnsureExists - creates a database if it does not exist
342
343 NOTE: pidbSession must be connected to the master database
344********************************************************************/
345extern "C" HRESULT DAPI SqlSessionDatabaseEnsureExists(
346 __in IDBCreateSession* pidbSession,
347 __in_z LPCWSTR wzDatabase,
348 __in_opt const SQL_FILESPEC* psfDatabase,
349 __in_opt const SQL_FILESPEC* psfLog,
350 __out_opt BSTR* pbstrErrorDescription
351 )
352{
353 Assert(pidbSession && wzDatabase && *wzDatabase);
354
355 HRESULT hr = S_OK;
356
357 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
358 ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
359
360 if (S_FALSE == hr)
361 {
362 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
363 ExitOnFailure(hr, "failed to create database: %1", wzDatabase);
364 }
365 // else database already exists, return S_FALSE
366
367 Assert(S_OK == hr);
368LExit:
369
370 return hr;
371}
372
373
374/********************************************************************
375 SqlCreateDatabase - creates a database on the server
376
377 NOTE: wzInstance is optional
378 if fIntegratedAuth is set then wzUser and wzPassword are ignored
379********************************************************************/
380extern "C" HRESULT DAPI SqlCreateDatabase(
381 __in_z LPCWSTR wzServer,
382 __in_z LPCWSTR wzInstance,
383 __in_z LPCWSTR wzDatabase,
384 __in BOOL fIntegratedAuth,
385 __in_z LPCWSTR wzUser,
386 __in_z LPCWSTR wzPassword,
387 __in_opt const SQL_FILESPEC* psfDatabase,
388 __in_opt const SQL_FILESPEC* psfLog,
389 __out_opt BSTR* pbstrErrorDescription
390 )
391{
392 Assert(wzServer && wzDatabase && *wzDatabase);
393
394 HRESULT hr = S_OK;
395 IDBCreateSession* pidbSession = NULL;
396
397 //
398 // connect to the master database to create the new database
399 //
400 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
401 ExitOnFailure(hr, "failed to connect to 'master' database on server %ls", wzServer);
402
403 hr = SqlSessionCreateDatabase(pidbSession, wzDatabase, psfDatabase, psfLog, pbstrErrorDescription);
404 ExitOnFailure(hr, "failed to create database: %ls", wzDatabase);
405
406 Assert(S_OK == hr);
407LExit:
408 ReleaseObject(pidbSession);
409
410 return hr;
411}
412
413
414/********************************************************************
415 SqlSessionCreateDatabase - creates a database on the server
416
417 NOTE: pidbSession must be connected to the master database
418********************************************************************/
419extern "C" HRESULT DAPI SqlSessionCreateDatabase(
420 __in IDBCreateSession* pidbSession,
421 __in_z LPCWSTR wzDatabase,
422 __in_opt const SQL_FILESPEC* psfDatabase,
423 __in_opt const SQL_FILESPEC* psfLog,
424 __out_opt BSTR* pbstrErrorDescription
425 )
426{
427 HRESULT hr = S_OK;
428 LPWSTR pwzDbFile = NULL;
429 LPWSTR pwzLogFile = NULL;
430 LPWSTR pwzQuery = NULL;
431 LPWSTR pwzDatabaseEscaped = NULL;
432
433 if (psfDatabase)
434 {
435 hr = FileSpecToString(psfDatabase, &pwzDbFile);
436 ExitOnFailure(hr, "failed to convert db filespec to string");
437 }
438
439 if (psfLog)
440 {
441 hr = FileSpecToString(psfLog, &pwzLogFile);
442 ExitOnFailure(hr, "failed to convert log filespec to string");
443 }
444
445 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
446 ExitOnFailure(hr, "failed to escape database string");
447
448 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"");
449 ExitOnFailure(hr, "failed to allocate query to create database: %ls", pwzDatabaseEscaped);
450
451 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
452 ExitOnFailure(hr, "failed to create database: %ls, Query: %ls", pwzDatabaseEscaped, pwzQuery);
453
454LExit:
455 ReleaseStr(pwzQuery);
456 ReleaseStr(pwzLogFile);
457 ReleaseStr(pwzDbFile);
458 ReleaseStr(pwzDatabaseEscaped);
459
460 return hr;
461}
462
463
464/********************************************************************
465 SqlDropDatabase - removes a database from a server if it exists
466
467 NOTE: wzInstance is optional
468 if fIntegratedAuth is set then wzUser and wzPassword are ignored
469********************************************************************/
470extern "C" HRESULT DAPI SqlDropDatabase(
471 __in_z LPCWSTR wzServer,
472 __in_z LPCWSTR wzInstance,
473 __in_z LPCWSTR wzDatabase,
474 __in BOOL fIntegratedAuth,
475 __in_z LPCWSTR wzUser,
476 __in_z LPCWSTR wzPassword,
477 __out_opt BSTR* pbstrErrorDescription
478 )
479{
480 Assert(wzServer && wzDatabase && *wzDatabase);
481
482 HRESULT hr = S_OK;
483 IDBCreateSession* pidbSession = NULL;
484
485 //
486 // connect to the master database to search for wzDatabase
487 //
488 hr = SqlConnectDatabase(wzServer, wzInstance, L"master", fIntegratedAuth, wzUser, wzPassword, &pidbSession);
489 ExitOnFailure(hr, "Failed to connect to 'master' database");
490
491 hr = SqlSessionDropDatabase(pidbSession, wzDatabase, pbstrErrorDescription);
492
493LExit:
494 ReleaseObject(pidbSession);
495
496 return hr;
497}
498
499
500/********************************************************************
501 SqlSessionDropDatabase - removes a database from a server if it exists
502
503 NOTE: pidbSession must be connected to the master database
504********************************************************************/
505extern "C" HRESULT DAPI SqlSessionDropDatabase(
506 __in IDBCreateSession* pidbSession,
507 __in_z LPCWSTR wzDatabase,
508 __out_opt BSTR* pbstrErrorDescription
509 )
510{
511 Assert(pidbSession && wzDatabase && *wzDatabase);
512
513 HRESULT hr = S_OK;
514 LPWSTR pwzQuery = NULL;
515 LPWSTR pwzDatabaseEscaped = NULL;
516
517 hr = SqlSessionDatabaseExists(pidbSession, wzDatabase, pbstrErrorDescription);
518 ExitOnFailure(hr, "failed to determine if exists, database: %ls", wzDatabase);
519
520 hr = EscapeSqlIdentifier(wzDatabase, &pwzDatabaseEscaped);
521 ExitOnFailure(hr, "failed to escape database string");
522
523 if (S_OK == hr)
524 {
525 hr = StrAllocFormatted(&pwzQuery, L"DROP DATABASE %s", pwzDatabaseEscaped);
526 ExitOnFailure(hr, "failed to allocate query to drop database: %ls", pwzDatabaseEscaped);
527
528 hr = SqlSessionExecuteQuery(pidbSession, pwzQuery, NULL, NULL, pbstrErrorDescription);
529 ExitOnFailure(hr, "Failed to drop database");
530 }
531
532LExit:
533 ReleaseStr(pwzQuery);
534 ReleaseStr(pwzDatabaseEscaped);
535
536 return hr;
537}
538
539
540/********************************************************************
541 SqlSessionExecuteQuery - executes a query and returns the results if desired
542
543 NOTE: ppirs and pcRoes and pbstrErrorDescription are optional
544********************************************************************/
545extern "C" HRESULT DAPI SqlSessionExecuteQuery(
546 __in IDBCreateSession* pidbSession,
547 __in __sql_command LPCWSTR wzSql,
548 __out_opt IRowset** ppirs,
549 __out_opt DBROWCOUNT* pcRows,
550 __out_opt BSTR* pbstrErrorDescription
551 )
552{
553 Assert(pidbSession);
554
555 HRESULT hr = S_OK;
556 IDBCreateCommand* pidbCommand = NULL;
557 ICommandText* picmdText = NULL;
558 ICommand* picmd = NULL;
559 DBROWCOUNT cRows = 0;
560
561 if (pcRows)
562 {
563 *pcRows = NULL;
564 }
565
566 //
567 // create the command
568 //
569 hr = pidbSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**)&pidbCommand);
570 ExitOnFailure(hr, "failed to create database session");
571 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
572 ExitOnFailure(hr, "failed to create command to execute session");
573
574 //
575 // set the sql text into the command
576 //
577 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
578 ExitOnFailure(hr, "failed to get command text object for command");
579 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
580 ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
581
582 //
583 // execute the command
584 //
585 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
586 ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
587
588 if (DB_S_ERRORSOCCURRED == hr)
589 {
590 hr = E_FAIL;
591 }
592
593 if (pcRows)
594 {
595 *pcRows = cRows;
596 }
597
598LExit:
599
600 if (FAILED(hr) && picmd && pbstrErrorDescription)
601 {
602 HRESULT hrGetErrors = SqlGetErrorInfo(picmd, IID_ICommandText, 0x409, NULL, pbstrErrorDescription); // TODO: use current locale instead of always American-English
603 if (FAILED(hrGetErrors))
604 {
605 ReleaseBSTR(*pbstrErrorDescription);
606 }
607 }
608
609 ReleaseObject(picmd);
610 ReleaseObject(picmdText);
611 ReleaseObject(pidbCommand);
612
613 return hr;
614}
615
616
617/********************************************************************
618 SqlCommandExecuteQuery - executes a SQL command and returns the results if desired
619
620 NOTE: ppirs and pcRoes are optional
621********************************************************************/
622extern "C" HRESULT DAPI SqlCommandExecuteQuery(
623 __in IDBCreateCommand* pidbCommand,
624 __in __sql_command LPCWSTR wzSql,
625 __out IRowset** ppirs,
626 __out DBROWCOUNT* pcRows
627 )
628{
629 Assert(pidbCommand);
630
631 HRESULT hr = S_OK;
632 ICommandText* picmdText = NULL;
633 ICommand* picmd = NULL;
634 DBROWCOUNT cRows = 0;
635
636 if (pcRows)
637 {
638 *pcRows = NULL;
639 }
640
641 //
642 // create the command
643 //
644 hr = pidbCommand->CreateCommand(NULL, IID_ICommand, (IUnknown**)&picmd);
645 ExitOnFailure(hr, "failed to create command to execute session");
646
647 //
648 // set the sql text into the command
649 //
650 hr = picmd->QueryInterface(IID_ICommandText, (LPVOID*)&picmdText);
651 ExitOnFailure(hr, "failed to get command text object for command");
652 hr = picmdText->SetCommandText(DBGUID_DEFAULT , wzSql);
653 ExitOnFailure(hr, "failed to set SQL string: %ls", wzSql);
654
655 //
656 // execute the command
657 //
658 hr = picmd->Execute(NULL, (ppirs) ? IID_IRowset : IID_NULL, NULL, &cRows, reinterpret_cast<IUnknown**>(ppirs));
659 ExitOnFailure(hr, "failed to execute SQL string: %ls", wzSql);
660
661 if (DB_S_ERRORSOCCURRED == hr)
662 {
663 hr = E_FAIL;
664 }
665
666 if (pcRows)
667 {
668 *pcRows = cRows;
669 }
670
671LExit:
672 ReleaseObject(picmd);
673 ReleaseObject(picmdText);
674
675 return hr;
676}
677
678
679/********************************************************************
680 SqlGetErrorInfo - gets error information from the last SQL function call
681
682 NOTE: pbstrErrorSource and pbstrErrorDescription are optional
683********************************************************************/
684extern "C" HRESULT DAPI SqlGetErrorInfo(
685 __in IUnknown* pObjectWithError,
686 __in REFIID IID_InterfaceWithError,
687 __in DWORD dwLocaleId,
688 __out_opt BSTR* pbstrErrorSource,
689 __out_opt BSTR* pbstrErrorDescription
690 )
691{
692 HRESULT hr = S_OK;
693 Assert(pObjectWithError);
694
695 // interfaces needed to extract error information out
696 ISupportErrorInfo* pISupportErrorInfo = NULL;
697 IErrorInfo* pIErrorInfoAll = NULL;
698 IErrorRecords* pIErrorRecords = NULL;
699 IErrorInfo* pIErrorInfoRecord = NULL;
700
701 // only ask for error information if the interface supports it.
702 hr = pObjectWithError->QueryInterface(IID_ISupportErrorInfo,(void**)&pISupportErrorInfo);
703 ExitOnFailure(hr, "No error information was found for object.");
704
705 hr = pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError);
706 ExitOnFailure(hr, "InterfaceWithError is not supported for object with error");
707
708 // ignore the return of GetErrorInfo it can succeed and return a NULL pointer in pIErrorInfoAll anyway
709 hr = ::GetErrorInfo(0, &pIErrorInfoAll);
710 ExitOnFailure(hr, "failed to get error info");
711
712 if (S_OK == hr && pIErrorInfoAll)
713 {
714 // see if it's a valid OLE DB IErrorInfo interface that exposes a list of records
715 hr = pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
716 if (SUCCEEDED(hr))
717 {
718 ULONG cErrors = 0;
719 pIErrorRecords->GetRecordCount(&cErrors);
720
721 // get the error information for each record
722 for (ULONG i = 0; i < cErrors; ++i)
723 {
724 hr = pIErrorRecords->GetErrorInfo(i, dwLocaleId, &pIErrorInfoRecord);
725 if (SUCCEEDED(hr))
726 {
727 if (pbstrErrorSource)
728 {
729 pIErrorInfoRecord->GetSource(pbstrErrorSource);
730 }
731 if (pbstrErrorDescription)
732 {
733 pIErrorInfoRecord->GetDescription(pbstrErrorDescription);
734 }
735
736 ReleaseNullObject(pIErrorInfoRecord);
737
738 break; // TODO: return more than one error in the future!
739 }
740 }
741
742 ReleaseNullObject(pIErrorRecords);
743 }
744 else // we have a simple error record
745 {
746 if (pbstrErrorSource)
747 {
748 pIErrorInfoAll->GetSource(pbstrErrorSource);
749 }
750 if (pbstrErrorDescription)
751 {
752 pIErrorInfoAll->GetDescription(pbstrErrorDescription);
753 }
754 }
755 }
756 else
757 {
758 hr = E_NOMOREITEMS;
759 }
760
761LExit:
762 ReleaseObject(pIErrorInfoRecord);
763 ReleaseObject(pIErrorRecords);
764 ReleaseObject(pIErrorInfoAll);
765 ReleaseObject(pISupportErrorInfo);
766
767 return hr;
768}
769
770
771//
772// private
773//
774
775/********************************************************************
776 FileSpecToString
777
778*********************************************************************/
779static HRESULT FileSpecToString(
780 __in const SQL_FILESPEC* psf,
781 __out LPWSTR* ppwz
782 )
783{
784 Assert(psf && ppwz);
785
786 HRESULT hr = S_OK;
787 LPWSTR pwz = NULL;
788
789 hr = StrAllocString(&pwz, L"(", 1024);
790 ExitOnFailure(hr, "failed to allocate string for database file info");
791
792 ExitOnNull(*psf->wzName, hr, E_INVALIDARG, "logical name not specified in database file info");
793 ExitOnNull(*psf->wzFilename, hr, E_INVALIDARG, "filename not specified in database file info");
794
795 hr = StrAllocFormatted(&pwz, L"%sNAME=%s", pwz, psf->wzName);
796 ExitOnFailure(hr, "failed to format database file info name: %ls", psf->wzName);
797
798 hr = StrAllocFormatted(&pwz, L"%s, FILENAME='%s'", pwz, psf->wzFilename);
799 ExitOnFailure(hr, "failed to format database file info filename: %ls", psf->wzFilename);
800
801 if (0 != psf->wzSize[0])
802 {
803 hr = StrAllocFormatted(&pwz, L"%s, SIZE=%s", pwz, psf->wzSize);
804 ExitOnFailure(hr, "failed to format database file info size: %s", psf->wzSize);
805 }
806
807 if (0 != psf->wzMaxSize[0])
808 {
809 hr = StrAllocFormatted(&pwz, L"%s, MAXSIZE=%s", pwz, psf->wzMaxSize);
810 ExitOnFailure(hr, "failed to format database file info maxsize: %s", psf->wzMaxSize);
811 }
812
813 if (0 != psf->wzGrow[0])
814 {
815 hr = StrAllocFormatted(&pwz, L"%s, FILEGROWTH=%s", pwz, psf->wzGrow);
816 ExitOnFailure(hr, "failed to format database file info growth: %s", psf->wzGrow);
817 }
818
819 hr = StrAllocFormatted(&pwz, L"%s)", pwz);
820 ExitOnFailure(hr, "failed to allocate string for file spec");
821
822 *ppwz = pwz;
823 pwz = NULL; // null here so it doesn't get freed below
824
825LExit:
826 ReleaseStr(pwz);
827 return hr;
828}
829
830static HRESULT EscapeSqlIdentifier(
831 __in_z LPCWSTR wzIdentifier,
832 __deref_out_z LPWSTR* ppwz
833 )
834{
835 Assert(ppwz);
836
837 HRESULT hr = S_OK;
838 LPWSTR pwz = NULL;
839
840 if (wzIdentifier == NULL)
841 {
842 //Just ignore a NULL identifier and clear out the result
843 ReleaseNullStr(*ppwz);
844 ExitFunction();
845 }
846
847 int cchIdentifier = lstrlenW(wzIdentifier);
848
849 //If an empty string or already escaped just copy
850 if (cchIdentifier == 0 || (wzIdentifier[0] == '[' && wzIdentifier[cchIdentifier-1] == ']'))
851 {
852 hr = StrAllocString(&pwz, wzIdentifier, 0);
853 ExitOnFailure(hr, "failed to format database name: %ls", wzIdentifier);
854 }
855 else
856 {
857 //escape it
858 hr = StrAllocFormatted(&pwz, L"[%s]", wzIdentifier);
859 ExitOnFailure(hr, "failed to format escaped database name: %ls", wzIdentifier);
860 }
861
862 *ppwz = pwz;
863 pwz = NULL; // null here so it doesn't get freed below
864
865LExit:
866 ReleaseStr(pwz);
867 return hr;
868}