diff options
author | Rob Mensching <rob@firegiant.com> | 2017-09-03 11:22:38 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2017-09-03 13:33:33 -0700 |
commit | 5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch) | |
tree | a76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/sqlutil.cpp | |
parent | 8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff) | |
download | wix-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.cpp | 868 |
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 | ||
13 | static HRESULT FileSpecToString( | ||
14 | __in const SQL_FILESPEC* psf, | ||
15 | __out LPWSTR* ppwz | ||
16 | ); | ||
17 | |||
18 | static 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 | ********************************************************************/ | ||
30 | extern "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 | |||
137 | LExit: | ||
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 | *********************************************************************/ | ||
155 | extern "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 | |||
173 | LExit: | ||
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 | *********************************************************************/ | ||
183 | extern "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 | |||
203 | LExit: | ||
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 | ********************************************************************/ | ||
218 | extern "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 | |||
238 | LExit: | ||
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 | ********************************************************************/ | ||
253 | extern "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 | |||
292 | LExit: | ||
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 | ********************************************************************/ | ||
306 | extern "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); | ||
333 | LExit: | ||
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 | ********************************************************************/ | ||
345 | extern "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); | ||
368 | LExit: | ||
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 | ********************************************************************/ | ||
380 | extern "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); | ||
407 | LExit: | ||
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 | ********************************************************************/ | ||
419 | extern "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 | |||
454 | LExit: | ||
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 | ********************************************************************/ | ||
470 | extern "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 | |||
493 | LExit: | ||
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 | ********************************************************************/ | ||
505 | extern "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 | |||
532 | LExit: | ||
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 | ********************************************************************/ | ||
545 | extern "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 | |||
598 | LExit: | ||
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 | ********************************************************************/ | ||
622 | extern "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 | |||
671 | LExit: | ||
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 | ********************************************************************/ | ||
684 | extern "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 | |||
761 | LExit: | ||
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 | *********************************************************************/ | ||
779 | static 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 | |||
825 | LExit: | ||
826 | ReleaseStr(pwz); | ||
827 | return hr; | ||
828 | } | ||
829 | |||
830 | static 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 | |||
865 | LExit: | ||
866 | ReleaseStr(pwz); | ||
867 | return hr; | ||
868 | } | ||