aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-16 13:53:48 -0600
committerSean Hall <r.sean.hall@gmail.com>2018-12-16 13:54:52 -0600
commit7d813eaad8eaca04a687d1bb942316232d1c54fd (patch)
tree8f5adb9da22b1f9fb79892fb6cf2de23fac32c71
parent1b7a9d3734119e658c91ebd9742ab5a3ce94cce4 (diff)
downloadwix-7d813eaad8eaca04a687d1bb942316232d1c54fd.tar.gz
wix-7d813eaad8eaca04a687d1bb942316232d1c54fd.tar.bz2
wix-7d813eaad8eaca04a687d1bb942316232d1c54fd.zip
Import implementation of SqlCA from old repo's scasched/scaexec.
-rw-r--r--src/ca/CustomMsiErrors.h10
-rw-r--r--src/ca/precomp.h14
-rw-r--r--src/ca/sca.h33
-rw-r--r--src/ca/scacost.h7
-rw-r--r--src/ca/scadb.cpp587
-rw-r--r--src/ca/scadb.h55
-rw-r--r--src/ca/scaexec.cpp393
-rw-r--r--src/ca/scasql.cpp113
-rw-r--r--src/ca/scasqlstr.cpp728
-rw-r--r--src/ca/scasqlstr.h51
-rw-r--r--src/ca/scauser.cpp82
-rw-r--r--src/ca/scauser.h40
-rw-r--r--src/ca/sqlca.def8
-rw-r--r--src/ca/sqlca.vcxproj11
14 files changed, 2131 insertions, 1 deletions
diff --git a/src/ca/CustomMsiErrors.h b/src/ca/CustomMsiErrors.h
new file mode 100644
index 00000000..b568d01c
--- /dev/null
+++ b/src/ca/CustomMsiErrors.h
@@ -0,0 +1,10 @@
1#pragma once
2// 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.
3
4#define msierrSQLFailedCreateDatabase 26201
5#define msierrSQLFailedDropDatabase 26202
6#define msierrSQLFailedConnectDatabase 26203
7#define msierrSQLFailedExecString 26204
8#define msierrSQLDatabaseAlreadyExists 26205
9
10//Last available is 26250 \ No newline at end of file
diff --git a/src/ca/precomp.h b/src/ca/precomp.h
index 3edad7ed..7a5074b3 100644
--- a/src/ca/precomp.h
+++ b/src/ca/precomp.h
@@ -2,12 +2,26 @@
2// 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// 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.
3 3
4 4
5#if _WIN32_MSI < 150
6#define _WIN32_MSI 150
7#endif
8
5#include <windows.h> 9#include <windows.h>
6#include <msiquery.h> 10#include <msiquery.h>
7 11
12#include <strsafe.h>
13
8#define MAXUINT USHRT_MAX 14#define MAXUINT USHRT_MAX
9#include <Setup.Configuration.h> 15#include <Setup.Configuration.h>
10 16
11#include "wcautil.h" 17#include "wcautil.h"
12#include "fileutil.h" 18#include "fileutil.h"
19#include "memutil.h"
13#include "strutil.h" 20#include "strutil.h"
21#include "wiutil.h"
22
23#include "CustomMsiErrors.h"
24
25#include "sca.h"
26#include "scacost.h"
27#include "scasqlstr.h"
diff --git a/src/ca/sca.h b/src/ca/sca.h
new file mode 100644
index 00000000..bc36344e
--- /dev/null
+++ b/src/ca/sca.h
@@ -0,0 +1,33 @@
1#pragma once
2// 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.
3
4// Generic action enum.
5enum SCA_ACTION
6{
7 SCA_ACTION_NONE,
8 SCA_ACTION_INSTALL,
9 SCA_ACTION_UNINSTALL
10};
11
12// sql database attributes definitions
13enum SCADB_ATTRIBUTES
14{
15 SCADB_CREATE_ON_INSTALL = 0x00000001,
16 SCADB_DROP_ON_UNINSTALL = 0x00000002,
17 SCADB_CONTINUE_ON_ERROR = 0x00000004,
18 SCADB_DROP_ON_INSTALL = 0x00000008,
19 SCADB_CREATE_ON_UNINSTALL = 0x00000010,
20 SCADB_CONFIRM_OVERWRITE = 0x00000020,
21 SCADB_CREATE_ON_REINSTALL = 0x00000040,
22 SCADB_DROP_ON_REINSTALL = 0x00000080,
23};
24
25// sql string/script attributes definitions
26enum SCASQL_ATTRIBUTES
27{
28 SCASQL_EXECUTE_ON_INSTALL = 0x00000001,
29 SCASQL_EXECUTE_ON_UNINSTALL = 0x00000002,
30 SCASQL_CONTINUE_ON_ERROR = 0x00000004,
31 SCASQL_ROLLBACK = 0x00000008,
32 SCASQL_EXECUTE_ON_REINSTALL = 0x00000010,
33};
diff --git a/src/ca/scacost.h b/src/ca/scacost.h
new file mode 100644
index 00000000..6ea7e465
--- /dev/null
+++ b/src/ca/scacost.h
@@ -0,0 +1,7 @@
1#pragma once
2// 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.
3
4const UINT COST_SQL_CREATEDB = 10000;
5const UINT COST_SQL_DROPDB = 5000;
6const UINT COST_SQL_CONNECTDB = 5000;
7const UINT COST_SQL_STRING = 5000;
diff --git a/src/ca/scadb.cpp b/src/ca/scadb.cpp
new file mode 100644
index 00000000..9f9efca2
--- /dev/null
+++ b/src/ca/scadb.cpp
@@ -0,0 +1,587 @@
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// sql queries
6LPCWSTR vcsSqlDatabaseQuery = L"SELECT `SqlDb`, `Server`, `Instance`, `Database`, "
7 L"`Component_`, `User_`, `FileSpec_`, `FileSpec_Log`, `Attributes` "
8 L"FROM `SqlDatabase`";
9enum eSqlDatabaseQuery { sdqSqlDb = 1, sdqServer, sdqInstance, sdqDatabase,
10 sdqComponent, sdqUser, sdqDbFileSpec, sdqLogFileSpec, sdqAttributes };
11
12LPCWSTR vcsSqlFileSpecQuery = L"SELECT `FileSpec`, `Name`, `Filename`, `Size`, "
13 L"`MaxSize`, `GrowthSize` FROM `SqlFileSpec` WHERE `FileSpec`=?";
14enum eSqlFileSpecQuery { sfsqFileSpec = 1, sfsqName, sfsqFilename, sfsqSize,
15 sfsqMaxSize, sfsqGrowth };
16
17
18// prototypes for private helper functions
19static HRESULT NewDb(
20 __out SCA_DB** ppsd
21 );
22
23static SCA_DB* AddDbToList(
24 __in SCA_DB* psdList,
25 __in SCA_DB* psd
26 );
27
28static HRESULT SchedCreateDatabase(
29 __in SCA_DB* psd
30 );
31
32static HRESULT SchedDropDatabase(
33 __in LPCWSTR wzKey, LPCWSTR wzServer,
34 __in LPCWSTR wzInstance,
35 __in LPCWSTR wzDatabase,
36 __in int iAttributes,
37 __in BOOL fIntegratedAuth,
38 __in LPCWSTR wzUser,
39 __in LPCWSTR wzPassword
40 );
41
42static HRESULT GetFileSpec(
43 __in MSIHANDLE hViewFileSpec,
44 __in LPCWSTR wzKey,
45 __in SQL_FILESPEC* psf
46 );
47
48
49HRESULT ScaDbsRead(
50 __inout SCA_DB** ppsdList,
51 __in SCA_ACTION saAction
52 )
53{
54 HRESULT hr = S_OK;
55 UINT er = ERROR_SUCCESS;
56 PMSIHANDLE hView;
57 PMSIHANDLE hRec;
58 PMSIHANDLE hViewFileSpec = NULL;
59
60 LPWSTR pwzData = NULL;
61 LPWSTR pwzId = NULL;
62 LPWSTR pwzComponent = NULL;
63
64 SCA_DB* psd = NULL;
65
66 if (S_OK != WcaTableExists(L"SqlDatabase"))
67 {
68 WcaLog(LOGMSG_VERBOSE, "Skipping ScaCreateDatabase() - SqlDatabase table not present");
69 ExitFunction1(hr = S_FALSE);
70 }
71
72 if (S_OK == WcaTableExists(L"SqlFileSpec"))
73 {
74 hr = WcaOpenView(vcsSqlFileSpecQuery, &hViewFileSpec);
75 ExitOnFailure(hr, "failed to open view on SqlFileSpec table");
76 }
77
78 // loop through all the sql databases
79 hr = WcaOpenExecuteView(vcsSqlDatabaseQuery, &hView);
80 ExitOnFailure(hr, "Failed to open view on SqlDatabase table");
81 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
82 {
83 BOOL fHasComponent = FALSE;
84 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
85 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
86
87 hr = WcaGetRecordString(hRec, sdqSqlDb, &pwzId);
88 ExitOnFailure(hr, "Failed to get SqlDatabase.SqlDb");
89
90 hr = WcaGetRecordString(hRec, sdqComponent, &pwzComponent);
91 ExitOnFailure(hr, "Failed to get Component for database: '%ls'", psd->wzKey);
92 if (pwzComponent && *pwzComponent)
93 {
94 fHasComponent = TRUE;
95
96 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction);
97 hr = HRESULT_FROM_WIN32(er);
98 ExitOnFailure(hr, "Failed to get state for component: %ls", pwzComponent);
99
100 // If we're doing install but the Component is not being installed or we're doing
101 // uninstall but the Component is not being uninstalled, skip it.
102 if ((WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL != saAction) ||
103 (WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL != saAction))
104 {
105 continue;
106 }
107 }
108
109 hr = NewDb(&psd);
110 ExitOnFailure(hr, "Failed to allocate memory for new database: %D", pwzId);
111
112 hr = ::StringCchCopyW(psd->wzKey, countof(psd->wzKey), pwzId);
113 ExitOnFailure(hr, "Failed to copy SqlDatabase.SqlDbL: %ls", pwzId);
114
115 hr = ::StringCchCopyW(psd->wzComponent, countof(psd->wzComponent), pwzComponent);
116 ExitOnFailure(hr, "Failed to copy SqlDatabase.Component_: %ls", pwzComponent);
117
118 psd->fHasComponent = fHasComponent;
119 psd->isInstalled = isInstalled;
120 psd->isAction = isAction;
121
122 hr = WcaGetRecordFormattedString(hRec, sdqServer, &pwzData);
123 ExitOnFailure(hr, "Failed to get Server for database: '%ls'", psd->wzKey);
124 hr = ::StringCchCopyW(psd->wzServer, countof(psd->wzServer), pwzData);
125 ExitOnFailure(hr, "Failed to copy server string to database object:%ls", pwzData);
126
127 hr = WcaGetRecordFormattedString(hRec, sdqInstance, &pwzData);
128 ExitOnFailure(hr, "Failed to get Instance for database: '%ls'", psd->wzKey);
129 hr = ::StringCchCopyW(psd->wzInstance, countof(psd->wzInstance), pwzData);
130 ExitOnFailure(hr, "Failed to copy instance string to database object:%ls", pwzData);
131
132 hr = WcaGetRecordFormattedString(hRec, sdqDatabase, &pwzData);
133 ExitOnFailure(hr, "Failed to get Database for database: '%ls'", psd->wzKey);
134 hr = ::StringCchCopyW(psd->wzDatabase, countof(psd->wzDatabase), pwzData);
135 ExitOnFailure(hr, "Failed to copy database string to database object:%ls", pwzData);
136
137 hr = WcaGetRecordInteger(hRec, sdqAttributes, &psd->iAttributes);
138 ExitOnFailure(hr, "Failed to get SqlDatabase.Attributes");
139
140 hr = WcaGetRecordFormattedString(hRec, sdqUser, &pwzData);
141 ExitOnFailure(hr, "Failed to get User record for database: '%ls'", psd->wzKey);
142
143 // if a user was specified
144 if (*pwzData)
145 {
146 psd->fUseIntegratedAuth = FALSE;
147 hr = ScaGetUser(pwzData, &psd->scau);
148 ExitOnFailure(hr, "Failed to get user information for database: '%ls'", psd->wzKey);
149 }
150 else
151 {
152 psd->fUseIntegratedAuth = TRUE;
153 // integrated authorization doesn't have a User record
154 }
155
156 hr = WcaGetRecordString(hRec, sdqDbFileSpec, &pwzData);
157 ExitOnFailure(hr, "Failed to get Database FileSpec for database: '%ls'", psd->wzKey);
158
159 // if a database filespec was specified
160 if (*pwzData)
161 {
162 hr = GetFileSpec(hViewFileSpec, pwzData, &psd->sfDb);
163 ExitOnFailure(hr, "failed to get FileSpec for: %ls", pwzData);
164 if (S_OK == hr)
165 {
166 psd->fHasDbSpec = TRUE;
167 }
168 }
169
170 hr = WcaGetRecordString(hRec, sdqLogFileSpec, &pwzData);
171 ExitOnFailure(hr, "Failed to get Log FileSpec for database: '%ls'", psd->wzKey);
172
173 // if a log filespec was specified
174 if (*pwzData)
175 {
176 hr = GetFileSpec(hViewFileSpec, pwzData, &psd->sfLog);
177 ExitOnFailure(hr, "failed to get FileSpec for: %ls", pwzData);
178 if (S_OK == hr)
179 {
180 psd->fHasLogSpec = TRUE;
181 }
182 }
183
184 *ppsdList = AddDbToList(*ppsdList, psd);
185 psd = NULL; // set the db NULL so it doesn't accidentally get freed below
186 }
187
188 if (E_NOMOREITEMS == hr)
189 {
190 hr = S_OK;
191 }
192 ExitOnFailure(hr, "Failure occured while processing SqlDatabase table");
193
194LExit:
195 if (psd)
196 {
197 ScaDbsFreeList(psd);
198 }
199
200 ReleaseStr(pwzComponent);
201 ReleaseStr(pwzId);
202 ReleaseStr(pwzData);
203 return hr;
204}
205
206
207SCA_DB* ScaDbsFindDatabase(
208 __in LPCWSTR wzSqlDb,
209 __in SCA_DB* psdList
210 )
211{
212 SCA_DB* psd = NULL;
213
214 for (psd = psdList; psd; psd = psd->psdNext)
215 {
216 if (0 == lstrcmpW(wzSqlDb, psd->wzKey))
217 {
218 break;
219 }
220 }
221
222 return psd;
223}
224
225
226HRESULT ScaDbsInstall(
227 __in SCA_DB* psdList
228 )
229{
230 HRESULT hr = S_FALSE; // assume nothing will be done
231 SCA_DB* psd = NULL;
232
233 for (psd = psdList; psd; psd = psd->psdNext)
234 {
235 if (psd->fHasComponent)
236 {
237 // if we need to drop, do that first
238 if (((psd->iAttributes & SCADB_DROP_ON_INSTALL) && WcaIsInstalling(psd->isInstalled, psd->isAction) && !WcaIsReInstalling(psd->isInstalled, psd->isAction)) ||
239 ((psd->iAttributes & SCADB_DROP_ON_REINSTALL) && WcaIsReInstalling(psd->isInstalled, psd->isAction)))
240 {
241 hr = SchedDropDatabase(psd->wzKey, psd->wzServer, psd->wzInstance, psd->wzDatabase, psd->iAttributes, psd->fUseIntegratedAuth, psd->scau.wzName, psd->scau.wzPassword);
242 ExitOnFailure(hr, "Failed to drop database %ls", psd->wzKey);
243 }
244
245 // if installing this component
246 if (((psd->iAttributes & SCADB_CREATE_ON_INSTALL) && WcaIsInstalling(psd->isInstalled, psd->isAction) && !WcaIsReInstalling(psd->isInstalled, psd->isAction)) ||
247 ((psd->iAttributes & SCADB_CREATE_ON_REINSTALL) && WcaIsReInstalling(psd->isInstalled, psd->isAction)))
248 {
249 hr = SchedCreateDatabase(psd);
250 ExitOnFailure(hr, "Failed to ensure database %ls exists", psd->wzKey);
251 }
252 }
253 }
254
255LExit:
256 return hr;
257}
258
259
260HRESULT ScaDbsUninstall(
261 __in SCA_DB* psdList
262 )
263{
264 HRESULT hr = S_FALSE; // assume nothing will be done
265 SCA_DB* psd = NULL;
266
267 for (psd = psdList; psd; psd = psd->psdNext)
268 {
269 if (psd->fHasComponent)
270 {
271 // if we need to drop do that first
272 if ((psd->iAttributes & SCADB_DROP_ON_UNINSTALL) && WcaIsUninstalling(psd->isInstalled, psd->isAction))
273 {
274 hr = SchedDropDatabase(psd->wzKey, psd->wzServer, psd->wzInstance, psd->wzDatabase, psd->iAttributes, psd->fUseIntegratedAuth, psd->scau.wzName, psd->scau.wzPassword);
275 ExitOnFailure(hr, "Failed to drop database %ls", psd->wzKey);
276 }
277
278 // install the db
279 if ((psd->iAttributes & SCADB_CREATE_ON_UNINSTALL) && WcaIsUninstalling(psd->isInstalled, psd->isAction))
280 {
281 hr = SchedCreateDatabase(psd);
282 ExitOnFailure(hr, "Failed to ensure database %ls exists", psd->wzKey);
283 }
284 }
285 }
286
287LExit:
288 return hr;
289}
290
291
292void ScaDbsFreeList(
293 __in SCA_DB* psdList
294 )
295{
296 SCA_DB* psdDelete = psdList;
297 while (psdList)
298 {
299 psdDelete = psdList;
300 psdList = psdList->psdNext;
301
302 MemFree(psdDelete);
303 }
304}
305
306
307// private helper functions
308
309static HRESULT NewDb(
310 __out SCA_DB** ppsd
311 )
312{
313 HRESULT hr = S_OK;
314 SCA_DB* psd = static_cast<SCA_DB*>(MemAlloc(sizeof(SCA_DB), TRUE));
315 ExitOnNull(psd, hr, E_OUTOFMEMORY, "failed to allocate memory for new database element");
316
317 *ppsd = psd;
318
319LExit:
320 return hr;
321}
322
323
324static SCA_DB* AddDbToList(
325 __in SCA_DB* psdList,
326 __in SCA_DB* psd
327 )
328{
329 if (psdList)
330 {
331 SCA_DB* psdT = psdList;
332 while (psdT->psdNext)
333 {
334 psdT = psdT->psdNext;
335 }
336
337 psdT->psdNext = psd;
338 }
339 else
340 {
341 psdList = psd;
342 }
343
344 return psdList;
345}
346
347
348static HRESULT SchedCreateDatabase(
349 __in SCA_DB* psd
350 )
351{
352 HRESULT hr = S_OK;
353 WCHAR* pwzCustomActionData = NULL;
354
355 hr = WcaWriteStringToCaData(psd->wzKey, &pwzCustomActionData);
356 ExitOnFailure(hr, "failed to add DBKey to CustomActionData");
357
358 hr = WcaWriteStringToCaData(psd->wzServer, &pwzCustomActionData);
359 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
360
361 hr = WcaWriteStringToCaData(psd->wzInstance, &pwzCustomActionData);
362 ExitOnFailure(hr, "Failed to add server instance to CustomActionData");
363
364 hr = WcaWriteStringToCaData(psd->wzDatabase, &pwzCustomActionData);
365 ExitOnFailure(hr, "Failed to add database name to CustomActionData");
366
367 hr = WcaWriteIntegerToCaData(psd->iAttributes, &pwzCustomActionData);
368 ExitOnFailure(hr, "Failed to add Sql attributes to CustomActionData");
369
370 hr = WcaWriteStringToCaData(psd->fUseIntegratedAuth ? L"1" : L"0", &pwzCustomActionData);
371 ExitOnFailure(hr, "Failed to add if integrated connection to CustomActionData");
372
373 hr = WcaWriteStringToCaData(psd->scau.wzName, &pwzCustomActionData);
374 ExitOnFailure(hr, "Failed to add server user to CustomActionData");
375
376 hr = WcaWriteStringToCaData(psd->scau.wzPassword, &pwzCustomActionData);
377 ExitOnFailure(hr, "Failed to add user password to CustomActionData");
378
379 // Check to see if the database exists, if it does not then schedule a rollback
380 // so we clean up after ourselves if the creation of the database fails or is
381 // aborted. It is interesting to note that we can do this check here because the
382 // deferred CustomActions are Impersonated. That means this scheduling action and
383 // the execution actions all run with the same user context, so it is safe to
384 // to do the check.
385 hr = SqlDatabaseExists(psd->wzServer, psd->wzInstance, psd->wzDatabase, psd->fUseIntegratedAuth, psd->scau.wzName, psd->scau.wzPassword, NULL);
386 if (S_FALSE == hr)
387 {
388 hr = WcaDoDeferredAction(L"RollbackCreateDatabase", pwzCustomActionData, COST_SQL_CREATEDB);
389 ExitOnFailure(hr, "Failed to schedule RollbackCreateDatabase action");
390 }
391
392 // database filespec
393 if (psd->fHasDbSpec)
394 {
395 hr = WcaWriteStringToCaData(L"1", &pwzCustomActionData);
396 ExitOnFailure(hr, "failed to specify that do have db.filespec to CustomActionData");
397
398 hr = WcaWriteStringToCaData(psd->sfDb.wzName, &pwzCustomActionData);
399 ExitOnFailure(hr, "failed to add FileSpec.Name to CustomActionData");
400
401 hr = WcaWriteStringToCaData(psd->sfDb.wzFilename, &pwzCustomActionData);
402 ExitOnFailure(hr, "failed to add FileSpec.Filename to CustomActionData");
403
404 hr = WcaWriteStringToCaData(psd->sfDb.wzSize, &pwzCustomActionData);
405 ExitOnFailure(hr, "Failed to add FileSpec.Size to CustomActionData");
406
407 hr = WcaWriteStringToCaData(psd->sfDb.wzMaxSize, &pwzCustomActionData);
408 ExitOnFailure(hr, "Failed to add FileSpec.MaxSize to CustomActionData");
409
410 hr = WcaWriteStringToCaData(psd->sfDb.wzGrow, &pwzCustomActionData);
411 ExitOnFailure(hr, "Failed to add FileSpec.GrowthSize to CustomActionData");
412 }
413 else
414 {
415 hr = WcaWriteStringToCaData(L"0", &pwzCustomActionData);
416 ExitOnFailure(hr, "failed to specify that do not have db.filespec to CustomActionData");
417 }
418
419 // log filespec
420 if (psd->fHasLogSpec)
421 {
422 hr = WcaWriteStringToCaData(L"1", &pwzCustomActionData);
423 ExitOnFailure(hr, "failed to specify that do have log.filespec to CustomActionData");
424
425 hr = WcaWriteStringToCaData(psd->sfLog.wzName, &pwzCustomActionData);
426 ExitOnFailure(hr, "failed to add FileSpec.Name to CustomActionData");
427
428 hr = WcaWriteStringToCaData(psd->sfLog.wzFilename, &pwzCustomActionData);
429 ExitOnFailure(hr, "failed to add FileSpec.Filename to CustomActionData");
430
431 hr = WcaWriteStringToCaData(psd->sfLog.wzSize, &pwzCustomActionData);
432 ExitOnFailure(hr, "Failed to add FileSpec.Size to CustomActionData");
433
434 hr = WcaWriteStringToCaData(psd->sfLog.wzMaxSize, &pwzCustomActionData);
435 ExitOnFailure(hr, "Failed to add FileSpec.MaxSize to CustomActionData");
436
437 hr = WcaWriteStringToCaData(psd->sfLog.wzGrow, &pwzCustomActionData);
438 ExitOnFailure(hr, "Failed to add FileSpec.GrowthSize to CustomActionData");
439 }
440 else
441 {
442 hr = WcaWriteStringToCaData(L"0", &pwzCustomActionData);
443 ExitOnFailure(hr, "failed to specify that do not have log.filespec to CustomActionData");
444 }
445
446 // schedule the CreateDatabase action
447 hr = WcaDoDeferredAction(L"CreateDatabase", pwzCustomActionData, COST_SQL_CREATEDB);
448 ExitOnFailure(hr, "Failed to schedule CreateDatabase action");
449
450LExit:
451 ReleaseStr(pwzCustomActionData);
452 return hr;
453}
454
455
456HRESULT SchedDropDatabase(
457 __in LPCWSTR wzKey,
458 __in LPCWSTR wzServer,
459 __in LPCWSTR wzInstance,
460 __in LPCWSTR wzDatabase,
461 __in int iAttributes,
462 __in BOOL fIntegratedAuth,
463 __in LPCWSTR wzUser,
464 __in LPCWSTR wzPassword
465 )
466{
467 HRESULT hr = S_OK;
468 WCHAR* pwzCustomActionData = NULL;
469
470 hr = WcaWriteStringToCaData(wzKey, &pwzCustomActionData);
471 ExitOnFailure(hr, "failed to add DBKey to CustomActionData");
472
473 hr = WcaWriteStringToCaData(wzServer, &pwzCustomActionData);
474 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
475
476 hr = WcaWriteStringToCaData(wzInstance, &pwzCustomActionData);
477 ExitOnFailure(hr, "Failed to add server instance to CustomActionData");
478
479 hr = WcaWriteStringToCaData(wzDatabase, &pwzCustomActionData);
480 ExitOnFailure(hr, "Failed to add database name to CustomActionData");
481
482 hr = WcaWriteIntegerToCaData(iAttributes, &pwzCustomActionData);
483 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
484
485 hr = WcaWriteStringToCaData(fIntegratedAuth ? L"1" : L"0", &pwzCustomActionData);
486 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
487
488 hr = WcaWriteStringToCaData(wzUser, &pwzCustomActionData);
489 ExitOnFailure(hr, "Failed to add server user to CustomActionData");
490
491 hr = WcaWriteStringToCaData(wzPassword, &pwzCustomActionData);
492 ExitOnFailure(hr, "Failed to add user password to CustomActionData");
493
494 hr = WcaDoDeferredAction(L"DropDatabase", pwzCustomActionData, COST_SQL_DROPDB);
495 ExitOnFailure(hr, "Failed to schedule DropDatabase action");
496
497LExit:
498 ReleaseStr(pwzCustomActionData);
499 return hr;
500}
501
502
503HRESULT GetFileSpec(
504 __in MSIHANDLE hViewFileSpec,
505 __in LPCWSTR wzKey,
506 __in SQL_FILESPEC* psf
507 )
508{
509 HRESULT hr = S_OK;
510 PMSIHANDLE hRecFileSpec, hRec;
511 LPWSTR pwzData = NULL;
512
513 // create a record to do the fetch
514 hRecFileSpec = ::MsiCreateRecord(1);
515 if (!hRecFileSpec)
516 {
517 ExitOnFailure(hr = E_UNEXPECTED, "failed to create record for filespec: %ls", wzKey);
518 }
519 hr = WcaSetRecordString(hRecFileSpec, 1, wzKey);
520 ExitOnFailure(hr, "failed to set record string for filespec: %ls", wzKey);
521
522 // get the FileSpec record
523 hr = WcaExecuteView(hViewFileSpec, hRecFileSpec);
524 ExitOnFailure(hr, "failed to execute view on SqlFileSpec table for filespec: %ls", wzKey);
525 hr = WcaFetchSingleRecord(hViewFileSpec, &hRec);
526 ExitOnFailure(hr, "failed to get record for filespec: %ls", wzKey);
527
528 // read the data out of the filespec record
529 hr = WcaGetRecordFormattedString(hRec, sfsqName, &pwzData);
530 ExitOnFailure(hr, "Failed to get SqlFileSpec.Name for filespec: %ls", wzKey);
531 hr = ::StringCchCopyW(psf->wzName, countof(psf->wzName), pwzData);
532 ExitOnFailure(hr, "Failed to copy SqlFileSpec.Name string: %ls", pwzData);
533
534 hr = WcaGetRecordFormattedString(hRec, sfsqFilename, &pwzData);
535 ExitOnFailure(hr, "Failed to get SqlFileSpec.Filename for filespec: %ls", wzKey);
536 if (*pwzData)
537 {
538 hr = ::StringCchCopyW(psf->wzFilename, countof(psf->wzFilename), pwzData);
539 ExitOnFailure(hr, "Failed to copy filename to filespec object: %ls", pwzData);
540 }
541 else // if there is no file, skip this FILESPEC
542 {
543 WcaLog(LOGMSG_VERBOSE, "No filename specified, skipping FileSpec: %ls", psf->wzName);
544 ExitFunction1(hr = S_FALSE);
545 }
546
547 hr = WcaGetRecordFormattedString(hRec, sfsqSize, &pwzData);
548 ExitOnFailure(hr, "Failed to get SqlFileSpec.Size for filespec: %ls", wzKey);
549 if (*pwzData)
550 {
551 hr = ::StringCchCopyW(psf->wzSize, countof(psf->wzSize), pwzData);
552 ExitOnFailure(hr, "Failed to copy size to filespec object: %ls", pwzData);
553 }
554 else
555 {
556 psf->wzSize[0] = 0;
557 }
558
559 hr = WcaGetRecordFormattedString(hRec, sfsqMaxSize, &pwzData);
560 ExitOnFailure(hr, "Failed to get SqlFileSpec.MaxSize for filespec: %ls", wzKey);
561 if (*pwzData)
562 {
563 hr = ::StringCchCopyW(psf->wzMaxSize, countof(psf->wzMaxSize), pwzData);
564 ExitOnFailure(hr, "Failed to copy max size to filespec object: %ls", pwzData);
565 }
566 else
567 {
568 psf->wzMaxSize[0] = 0;
569 }
570
571 hr = WcaGetRecordFormattedString(hRec, sfsqGrowth, &pwzData);
572 ExitOnFailure(hr, "Failed to get SqlFileSpec.GrowthSize for filespec: %ls", wzKey);
573 if (*pwzData)
574 {
575 hr = ::StringCchCopyW(psf->wzGrow, countof(psf->wzGrow), pwzData);
576 ExitOnFailure(hr, "Failed to copy growth size to filespec object: %ls", pwzData);
577 }
578 else
579 {
580 psf->wzGrow[0] = 0;
581 }
582
583 hr = S_OK;
584LExit:
585 ReleaseStr(pwzData);
586 return hr;
587}
diff --git a/src/ca/scadb.h b/src/ca/scadb.h
new file mode 100644
index 00000000..885e84c2
--- /dev/null
+++ b/src/ca/scadb.h
@@ -0,0 +1,55 @@
1#pragma once
2// 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.
3
4
5#include "scauser.h"
6#include "sqlutil.h"
7
8struct SCA_DB
9{
10 // darwin information
11 WCHAR wzKey[MAX_DARWIN_KEY + 1];
12 BOOL fHasComponent;
13 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
14 INSTALLSTATE isInstalled, isAction;
15
16 WCHAR wzServer[MAX_DARWIN_COLUMN + 1];
17 WCHAR wzInstance[MAX_DARWIN_COLUMN + 1];
18 WCHAR wzDatabase[MAX_DARWIN_COLUMN + 1];
19
20 int iAttributes;
21
22 BOOL fUseIntegratedAuth;
23 SCA_USER scau;
24
25 BOOL fHasDbSpec;
26 SQL_FILESPEC sfDb;
27 BOOL fHasLogSpec;
28 SQL_FILESPEC sfLog;
29
30 SCA_DB* psdNext;
31};
32
33
34// prototypes
35HRESULT ScaDbsRead(
36 __inout SCA_DB** ppsdList,
37 __in SCA_ACTION saAction
38 );
39
40SCA_DB* ScaDbsFindDatabase(
41 __in LPCWSTR wzSqlDb,
42 __in SCA_DB* psdList
43 );
44
45HRESULT ScaDbsInstall(
46 __in SCA_DB* psdList
47 );
48
49HRESULT ScaDbsUninstall(
50 __in SCA_DB* psdList
51 );
52
53void ScaDbsFreeList(
54 __in SCA_DB* psdList
55 );
diff --git a/src/ca/scaexec.cpp b/src/ca/scaexec.cpp
new file mode 100644
index 00000000..7a30f52a
--- /dev/null
+++ b/src/ca/scaexec.cpp
@@ -0,0 +1,393 @@
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
6/********************************************************************
7 * CreateDatabase - CUSTOM ACTION ENTRY POINT for creating databases
8 *
9 * Input: deferred CustomActionData - DbKey\tServer\tInstance\tDatabase\tAttributes\tIntegratedAuth\tUser\tPassword
10 * ****************************************************************/
11extern "C" UINT __stdcall CreateDatabase(MSIHANDLE hInstall)
12{
13//AssertSz(FALSE, "debug CreateDatabase here");
14 UINT er = ERROR_SUCCESS;
15 HRESULT hr = S_OK;
16
17 LPWSTR pwzData = NULL;
18 IDBCreateSession* pidbSession = NULL;
19 BSTR bstrErrorDescription = NULL;
20 LPWSTR pwz = NULL;
21 LPWSTR pwzDatabaseKey = NULL;
22 LPWSTR pwzServer = NULL;
23 LPWSTR pwzInstance = NULL;
24 LPWSTR pwzDatabase = NULL;
25 LPWSTR pwzTemp = NULL;
26 int iAttributes;
27 BOOL fIntegratedAuth;
28 LPWSTR pwzUser = NULL;
29 LPWSTR pwzPassword = NULL;
30 BOOL fHaveDbFileSpec = FALSE;
31 SQL_FILESPEC sfDb;
32 BOOL fHaveLogFileSpec = FALSE;
33 SQL_FILESPEC sfLog;
34 BOOL fInitializedCom = FALSE;
35
36 memset(&sfDb, 0, sizeof(sfDb));
37 memset(&sfLog, 0, sizeof(sfLog));
38
39 hr = WcaInitialize(hInstall, "CreateDatabase");
40 ExitOnFailure(hr, "failed to initialize");
41
42 hr = ::CoInitialize(NULL);
43 ExitOnFailure(hr, "failed to intialize COM");
44 fInitializedCom = TRUE;
45
46 hr = WcaGetProperty( L"CustomActionData", &pwzData);
47 ExitOnFailure(hr, "failed to get CustomActionData");
48
49 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
50
51 pwz = pwzData;
52 hr = WcaReadStringFromCaData(&pwz, &pwzDatabaseKey); // SQL Server
53 ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
54 hr = WcaReadStringFromCaData(&pwz, &pwzServer); // SQL Server
55 ExitOnFailure(hr, "failed to read server from custom action data: %ls", pwz);
56 hr = WcaReadStringFromCaData(&pwz, &pwzInstance); // SQL Server Instance
57 ExitOnFailure(hr, "failed to read server instance from custom action data: %ls", pwz);
58 hr = WcaReadStringFromCaData(&pwz, &pwzDatabase); // SQL Database
59 ExitOnFailure(hr, "failed to read server instance from custom action data: %ls", pwz);
60 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
61 ExitOnFailure(hr, "failed to read attributes from custom action data: %ls", pwz);
62 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth)); // Integrated Windows Authentication?
63 ExitOnFailure(hr, "failed to read integrated auth flag from custom action data: %ls", pwz);
64 hr = WcaReadStringFromCaData(&pwz, &pwzUser); // SQL User
65 ExitOnFailure(hr, "failed to read user from custom action data: %ls", pwz);
66 hr = WcaReadStringFromCaData(&pwz, &pwzPassword); // SQL User Password
67 ExitOnFailure(hr, "failed to read user from custom action data: %ls", pwz);
68
69 // db file spec
70 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fHaveDbFileSpec));
71 ExitOnFailure(hr, "failed to read db file spec from custom action data: %ls", pwz);
72
73 if (fHaveDbFileSpec)
74 {
75 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
76 ExitOnFailure(hr, "failed to read db file spec name from custom action data: %ls", pwz);
77 hr = ::StringCchCopyW(sfDb.wzName, countof(sfDb.wzName), pwzTemp);
78 ExitOnFailure(hr, "failed to copy db file spec name: %ls", pwzTemp);
79
80 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
81 ExitOnFailure(hr, "failed to read db file spec filename from custom action data: %ls", pwz);
82 hr = ::StringCchCopyW(sfDb.wzFilename, countof(sfDb.wzFilename), pwzTemp);
83 ExitOnFailure(hr, "failed to copy db file spec filename: %ls", pwzTemp);
84
85 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
86 ExitOnFailure(hr, "failed to read db file spec size from custom action data: %ls", pwz);
87 hr = ::StringCchCopyW(sfDb.wzSize, countof(sfDb.wzSize), pwzTemp);
88 ExitOnFailure(hr, "failed to copy db file spec size value: %ls", pwzTemp);
89
90 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
91 ExitOnFailure(hr, "failed to read db file spec max size from custom action data: %ls", pwz);
92 hr = ::StringCchCopyW(sfDb.wzMaxSize, countof(sfDb.wzMaxSize), pwzTemp);
93 ExitOnFailure(hr, "failed to copy db file spec max size: %ls", pwzTemp);
94
95 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
96 ExitOnFailure(hr, "failed to read db file spec grow from custom action data: %ls", pwz);
97 hr = ::StringCchCopyW(sfDb.wzGrow, countof(sfDb.wzGrow), pwzTemp);
98 ExitOnFailure(hr, "failed to copy db file spec grow value: %ls", pwzTemp);
99 }
100
101 // log file spec
102 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fHaveLogFileSpec));
103 ExitOnFailure(hr, "failed to read log file spec from custom action data: %ls", pwz);
104 if (fHaveLogFileSpec)
105 {
106 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
107 ExitOnFailure(hr, "failed to read log file spec name from custom action data: %ls", pwz);
108 hr = ::StringCchCopyW(sfLog.wzName, countof(sfDb.wzName), pwzTemp);
109 ExitOnFailure(hr, "failed to copy log file spec name: %ls", pwzTemp);
110
111 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
112 ExitOnFailure(hr, "failed to read log file spec filename from custom action data: %ls", pwz);
113 hr = ::StringCchCopyW(sfLog.wzFilename, countof(sfDb.wzFilename), pwzTemp);
114 ExitOnFailure(hr, "failed to copy log file spec filename: %ls", pwzTemp);
115
116 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
117 ExitOnFailure(hr, "failed to read log file spec size from custom action data: %ls", pwz);
118 hr = ::StringCchCopyW(sfLog.wzSize, countof(sfDb.wzSize), pwzTemp);
119 ExitOnFailure(hr, "failed to copy log file spec size value: %ls", pwzTemp);
120
121 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
122 ExitOnFailure(hr, "failed to read log file spec max size from custom action data: %ls", pwz);
123 hr = ::StringCchCopyW(sfLog.wzMaxSize, countof(sfDb.wzMaxSize), pwzTemp);
124 ExitOnFailure(hr, "failed to copy log file spec max size: %ls", pwzTemp);
125
126 hr = WcaReadStringFromCaData(&pwz, &pwzTemp);
127 ExitOnFailure(hr, "failed to read log file spec grow from custom action data: %ls", pwz);
128 hr = ::StringCchCopyW(sfLog.wzGrow, countof(sfDb.wzGrow), pwzTemp);
129 ExitOnFailure(hr, "failed to copy log file spec grow value: %ls", pwzTemp);
130 }
131
132 if (iAttributes & SCADB_CONFIRM_OVERWRITE)
133 {
134 // Check if the database already exists
135 hr = SqlDatabaseExists(pwzServer, pwzInstance, pwzDatabase, fIntegratedAuth, pwzUser, pwzPassword, &bstrErrorDescription);
136 MessageExitOnFailure(hr, msierrSQLFailedCreateDatabase, "failed to check if database exists: '%ls', error: %ls", pwzDatabase, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription);
137
138 if (S_OK == hr) // found an existing database, confirm that they don't want to stop before it gets trampled, in no UI case just continue anyways
139 {
140 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
141 if (IDNO == WcaErrorMessage(msierrSQLDatabaseAlreadyExists, hr, MB_YESNO, 1, pwzDatabase))
142 ExitOnFailure(hr, "failed to initialize");
143 }
144 }
145
146 hr = SqlDatabaseEnsureExists(pwzServer, pwzInstance, pwzDatabase, fIntegratedAuth, pwzUser, pwzPassword, fHaveDbFileSpec ? &sfDb : NULL, fHaveLogFileSpec ? &sfLog : NULL, &bstrErrorDescription);
147 if ((iAttributes & SCADB_CONTINUE_ON_ERROR) && FAILED(hr))
148 {
149 WcaLog(LOGMSG_STANDARD, "Error 0x%x: failed to create SQL database but continuing, error: %ls, Database: %ls", hr, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription, pwzDatabase);
150 hr = S_OK;
151 }
152 MessageExitOnFailure(hr, msierrSQLFailedCreateDatabase, "failed to create to database: '%ls', error: %ls", pwzDatabase, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription);
153
154 hr = WcaProgressMessage(COST_SQL_CONNECTDB, FALSE);
155LExit:
156 ReleaseStr(pwzDatabaseKey);
157 ReleaseStr(pwzServer);
158 ReleaseStr(pwzInstance);
159 ReleaseStr(pwzDatabase);
160 ReleaseStr(pwzUser);
161 ReleaseStr(pwzPassword);
162 ReleaseObject(pidbSession);
163 ReleaseBSTR(bstrErrorDescription);
164
165 if (fInitializedCom)
166 {
167 ::CoUninitialize();
168 }
169
170 if (FAILED(hr))
171 {
172 er = ERROR_INSTALL_FAILURE;
173 }
174 return WcaFinalize(er);
175}
176
177
178/********************************************************************
179 DropDatabase - CUSTOM ACTION ENTRY POINT for removing databases
180
181 Input: deferred CustomActionData - DbKey\tServer\tInstance\tDatabase\tAttributes\tIntegratedAuth\tUser\tPassword
182 * ****************************************************************/
183extern "C" UINT __stdcall DropDatabase(MSIHANDLE hInstall)
184{
185//Assert(FALSE);
186 UINT er = ERROR_SUCCESS;
187 HRESULT hr = S_OK;
188
189 LPWSTR pwzData = NULL;
190 IDBCreateSession* pidbSession = NULL;
191 BSTR bstrErrorDescription = NULL;
192 LPWSTR pwz = NULL;
193 LPWSTR pwzDatabaseKey = NULL;
194 LPWSTR pwzServer = NULL;
195 LPWSTR pwzInstance = NULL;
196 LPWSTR pwzDatabase = NULL;
197 long lAttributes;
198 BOOL fIntegratedAuth;
199 LPWSTR pwzUser = NULL;
200 LPWSTR pwzPassword = NULL;
201 BOOL fInitializedCom = TRUE;
202
203 hr = WcaInitialize(hInstall, "DropDatabase");
204 ExitOnFailure(hr, "failed to initialize");
205
206 hr = ::CoInitialize(NULL);
207 ExitOnFailure(hr, "failed to intialize COM");
208 fInitializedCom = TRUE;
209
210 hr = WcaGetProperty( L"CustomActionData", &pwzData);
211 ExitOnFailure(hr, "failed to get CustomActionData");
212
213 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
214
215 pwz = pwzData;
216 hr = WcaReadStringFromCaData(&pwz, &pwzDatabaseKey);
217 ExitOnFailure(hr, "failed to read database key");
218 hr = WcaReadStringFromCaData(&pwz, &pwzServer);
219 ExitOnFailure(hr, "failed to read server");
220 hr = WcaReadStringFromCaData(&pwz, &pwzInstance);
221 ExitOnFailure(hr, "failed to read instance");
222 hr = WcaReadStringFromCaData(&pwz, &pwzDatabase);
223 ExitOnFailure(hr, "failed to read database");
224 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&lAttributes));
225 ExitOnFailure(hr, "failed to read attributes");
226 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth)); // Integrated Windows Authentication?
227 ExitOnFailure(hr, "failed to read integrated auth flag");
228 hr = WcaReadStringFromCaData(&pwz, &pwzUser);
229 ExitOnFailure(hr, "failed to read user");
230 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
231 ExitOnFailure(hr, "failed to read password");
232
233 hr = SqlDropDatabase(pwzServer, pwzInstance, pwzDatabase, fIntegratedAuth, pwzUser, pwzPassword, &bstrErrorDescription);
234 if ((lAttributes & SCADB_CONTINUE_ON_ERROR) && FAILED(hr))
235 {
236 WcaLog(LOGMSG_STANDARD, "Error 0x%x: failed to drop SQL database but continuing, error: %ls, Database: %ls", hr, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription, pwzDatabase);
237 hr = S_OK;
238 }
239 MessageExitOnFailure(hr, msierrSQLFailedDropDatabase, "failed to drop to database: '%ls', error: %ls", pwzDatabase, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription);
240
241 hr = WcaProgressMessage(COST_SQL_CONNECTDB, FALSE);
242
243LExit:
244 ReleaseStr(pwzDatabaseKey);
245 ReleaseStr(pwzServer);
246 ReleaseStr(pwzInstance);
247 ReleaseStr(pwzDatabase);
248 ReleaseStr(pwzUser);
249 ReleaseStr(pwzPassword);
250 ReleaseStr(pwzData);
251 ReleaseObject(pidbSession);
252 ReleaseBSTR(bstrErrorDescription);
253
254 if (fInitializedCom)
255 {
256 ::CoUninitialize();
257 }
258
259 if (FAILED(hr))
260 {
261 er = ERROR_INSTALL_FAILURE;
262 }
263 return WcaFinalize(er);
264}
265
266
267/********************************************************************
268 ExecuteSqlStrings - CUSTOM ACTION ENTRY POINT for running SQL strings
269
270 Input: deferred CustomActionData - DbKey\tServer\tInstance\tDatabase\tAttributes\tIntegratedAuth\tUser\tPassword\tSQLKey1\tSQLString1\tSQLKey2\tSQLString2\tSQLKey3\tSQLString3\t...
271 rollback CustomActionData - same as above
272 * ****************************************************************/
273extern "C" UINT __stdcall ExecuteSqlStrings(MSIHANDLE hInstall)
274{
275//Assert(FALSE);
276 UINT er = ERROR_SUCCESS;
277 HRESULT hr = S_OK;
278 HRESULT hrDB = S_OK;
279
280 LPWSTR pwzData = NULL;
281 IDBCreateSession* pidbSession = NULL;
282 BSTR bstrErrorDescription = NULL;
283
284 LPWSTR pwz = NULL;
285 LPWSTR pwzDatabaseKey = NULL;
286 LPWSTR pwzServer = NULL;
287 LPWSTR pwzInstance = NULL;
288 LPWSTR pwzDatabase = NULL;
289 int iAttributesDB;
290 int iAttributesSQL;
291 BOOL fIntegratedAuth;
292 LPWSTR pwzUser = NULL;
293 LPWSTR pwzPassword = NULL;
294 LPWSTR pwzSqlKey = NULL;
295 LPWSTR pwzSql = NULL;
296 BOOL fInitializedCom = FALSE;
297
298 hr = WcaInitialize(hInstall, "ExecuteSqlStrings");
299 ExitOnFailure(hr, "failed to initialize");
300
301 hr = ::CoInitialize(NULL);
302 ExitOnFailure(hr, "failed to intialize COM");
303 fInitializedCom = TRUE;
304
305 hr = WcaGetProperty( L"CustomActionData", &pwzData);
306 ExitOnFailure(hr, "failed to get CustomActionData");
307
308 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
309
310 pwz = pwzData;
311 hr = WcaReadStringFromCaData(&pwz, &pwzDatabaseKey);
312 ExitOnFailure(hr, "failed to read database key");
313 hr = WcaReadStringFromCaData(&pwz, &pwzServer);
314 ExitOnFailure(hr, "failed to read server");
315 hr = WcaReadStringFromCaData(&pwz, &pwzInstance);
316 ExitOnFailure(hr, "failed to read instance");
317 hr = WcaReadStringFromCaData(&pwz, &pwzDatabase);
318 ExitOnFailure(hr, "failed to read database");
319 hr = WcaReadIntegerFromCaData(&pwz, &iAttributesDB);
320 ExitOnFailure(hr, "failed to read attributes");
321 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth)); // Integrated Windows Authentication?
322 ExitOnFailure(hr, "failed to read integrated auth flag");
323 hr = WcaReadStringFromCaData(&pwz, &pwzUser);
324 ExitOnFailure(hr, "failed to read user");
325 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
326 ExitOnFailure(hr, "failed to read password");
327
328 // Store off the result of the connect, only exit if we don't care if the database connection succeeds
329 // Wait to fail until later to see if we actually have work to do that is not set to continue on error
330 hrDB = SqlConnectDatabase(pwzServer, pwzInstance, pwzDatabase, fIntegratedAuth, pwzUser, pwzPassword, &pidbSession);
331 if ((iAttributesDB & SCADB_CONTINUE_ON_ERROR) && FAILED(hrDB))
332 {
333 WcaLog(LOGMSG_STANDARD, "Error 0x%x: continuing after failure to connect to database: %ls", hrDB, pwzDatabase);
334 ExitFunction1(hr = S_OK);
335 }
336
337 while (S_OK == hr && S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzSqlKey)))
338 {
339 hr = WcaReadIntegerFromCaData(&pwz, &iAttributesSQL);
340 ExitOnFailure(hr, "failed to read attributes for SQL string: %ls", pwzSqlKey);
341
342 hr = WcaReadStringFromCaData(&pwz, &pwzSql);
343 ExitOnFailure(hr, "failed to read SQL string for key: %ls", pwzSqlKey);
344
345 // If the SqlString row is set to continue on error and the DB connection failed, skip attempting to execute
346 if ((iAttributesSQL & SCASQL_CONTINUE_ON_ERROR) && FAILED(hrDB))
347 {
348 WcaLog(LOGMSG_STANDARD, "Error 0x%x: continuing after failure to connect to database: %ls", hrDB, pwzDatabase);
349 continue;
350 }
351
352 // Now check if the DB connection succeeded
353 MessageExitOnFailure(hr = hrDB, msierrSQLFailedConnectDatabase, "failed to connect to database: '%ls'", pwzDatabase);
354
355 WcaLog(LOGMSG_VERBOSE, "Executing SQL string: %ls", pwzSql);
356 hr = SqlSessionExecuteQuery(pidbSession, pwzSql, NULL, NULL, &bstrErrorDescription);
357 if ((iAttributesSQL & SCASQL_CONTINUE_ON_ERROR) && FAILED(hr))
358 {
359 WcaLog(LOGMSG_STANDARD, "Error 0x%x: failed to execute SQL string but continuing, error: %ls, SQL key: %ls SQL string: %ls", hr, NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription, pwzSqlKey, pwzSql);
360 hr = S_OK;
361 }
362 MessageExitOnFailure(hr, msierrSQLFailedExecString, "failed to execute SQL string, error: %ls, SQL key: %ls SQL string: %ls", NULL == bstrErrorDescription ? L"unknown error" : bstrErrorDescription, pwzSqlKey, pwzSql);
363
364 WcaProgressMessage(COST_SQL_STRING, FALSE);
365 }
366 if (E_NOMOREITEMS == hr)
367 {
368 hr = S_OK;
369 }
370
371LExit:
372 ReleaseStr(pwzDatabaseKey);
373 ReleaseStr(pwzServer);
374 ReleaseStr(pwzInstance);
375 ReleaseStr(pwzDatabase);
376 ReleaseStr(pwzUser);
377 ReleaseStr(pwzPassword);
378 ReleaseStr(pwzData);
379
380 ReleaseBSTR(bstrErrorDescription);
381 ReleaseObject(pidbSession);
382
383 if (fInitializedCom)
384 {
385 ::CoUninitialize();
386 }
387
388 if (FAILED(hr))
389 {
390 er = ERROR_INSTALL_FAILURE;
391 }
392 return WcaFinalize(er);
393}
diff --git a/src/ca/scasql.cpp b/src/ca/scasql.cpp
new file mode 100644
index 00000000..5e3edd1c
--- /dev/null
+++ b/src/ca/scasql.cpp
@@ -0,0 +1,113 @@
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// prototypes
6static HRESULT ConfigureSqlData(
7 __in SCA_ACTION saAction
8 );
9
10
11/********************************************************************
12InstallSqlData - CUSTOM ACTION ENTRY POINT for installing
13 SQL data
14
15********************************************************************/
16extern "C" UINT __stdcall InstallSqlData(
17 __in MSIHANDLE hInstall
18 )
19{
20 HRESULT hr = S_OK;
21 UINT er = ERROR_SUCCESS;
22
23 // initialize
24 hr = WcaInitialize(hInstall, "InstallSqlData");
25 ExitOnFailure(hr, "Failed to initialize");
26
27 hr = ConfigureSqlData(SCA_ACTION_INSTALL);
28
29LExit:
30 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
31 return WcaFinalize(er);
32}
33
34
35/********************************************************************
36UninstallSqlData - CUSTOM ACTION ENTRY POINT for uninstalling
37 SQL data
38
39********************************************************************/
40extern "C" UINT __stdcall UninstallSqlData(
41 __in MSIHANDLE hInstall
42 )
43{
44 HRESULT hr = S_OK;
45 UINT er = ERROR_SUCCESS;
46
47 // initialize
48 hr = WcaInitialize(hInstall, "UninstallCertificates");
49 ExitOnFailure(hr, "Failed to initialize");
50
51 hr = ConfigureSqlData(SCA_ACTION_UNINSTALL);
52
53LExit:
54 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
55 return WcaFinalize(er);
56}
57
58
59static HRESULT ConfigureSqlData(
60 __in SCA_ACTION saAction
61 )
62{
63 //AssertSz(FALSE, "debug ConfigureSqlData()");
64 HRESULT hr = S_OK;
65
66 SCA_DB* psdList = NULL;
67 SCA_SQLSTR* psssList = NULL;
68
69 // check for the prerequsite tables
70 if (S_OK != WcaTableExists(L"SqlDatabase"))
71 {
72 WcaLog(LOGMSG_VERBOSE, "skipping SQL CustomAction, no SqlDatabase table");
73 ExitFunction1(hr = S_FALSE);
74 }
75
76 // read tables
77 hr = ScaDbsRead(&psdList, saAction);
78 ExitOnFailure(hr, "failed to read SqlDatabase table");
79
80 hr = ScaSqlStrsRead(&psssList, saAction);
81 ExitOnFailure(hr, "failed to read SqlStrings table");
82
83 hr = ScaSqlStrsReadScripts(&psssList, saAction);
84 ExitOnFailure(hr, "failed to read SqlScripts table");
85
86 if (SCA_ACTION_UNINSTALL == saAction)
87 {
88 // do uninstall actions (order is important!)
89 hr = ScaSqlStrsUninstall(psdList, psssList);
90 ExitOnFailure(hr, "failed to execute uninstall SQL strings");
91
92 hr = ScaDbsUninstall(psdList);
93 ExitOnFailure(hr, "failed to uninstall databases");
94 }
95 else
96 {
97 // do install actions (order is important!)
98 hr = ScaDbsInstall(psdList);
99 ExitOnFailure(hr, "failed to install databases");
100
101 hr = ScaSqlStrsInstall(psdList, psssList);
102 ExitOnFailure(hr, "failed to execute install SQL strings, length may be too long, try add GO to break up");
103 }
104
105LExit:
106 if (psssList)
107 ScaSqlStrsFreeList(psssList);
108
109 if (psdList)
110 ScaDbsFreeList(psdList);
111
112 return hr;
113}
diff --git a/src/ca/scasqlstr.cpp b/src/ca/scasqlstr.cpp
new file mode 100644
index 00000000..3108e307
--- /dev/null
+++ b/src/ca/scasqlstr.cpp
@@ -0,0 +1,728 @@
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// sql queries
6LPCWSTR vcsSqlStringQuery = L"SELECT `String`, `SqlDb_`, `Component_`,`SQL`,`User_`,`Attributes`,`Sequence` "
7L"FROM `SqlString` ORDER BY `SqlDb_`,`Sequence`";
8enum eSqlStringQuery { ssqSqlString = 1, ssqSqlDb, ssqComponent, ssqSQL, ssqUser, ssqAttributes, ssqSequence };
9
10LPCWSTR vcsSqlScriptQuery = L"SELECT `ScriptBinary_`,`Script`, `SqlDb_`, `Component_`,`User_`,`Attributes`,`Sequence` "
11L"FROM `SqlScript` ORDER BY `SqlDb_`,`Sequence`";
12enum eSqlScriptQuery { sscrqScriptBinary=1, sscrqSqlScript, sscrqSqlDb, sscrqComponent, sscrqUser, sscrqAttributes, sscrqSequence };
13
14LPCWSTR vcsSqlBinaryScriptQuery = L"SELECT `Data` FROM `Binary` WHERE `Name`=?";
15enum eSqlBinaryScriptQuery { ssbsqData = 1 };
16
17
18// prototypes for private helper functions
19static HRESULT NewSqlStr(
20 __out SCA_SQLSTR** ppsss
21 );
22static SCA_SQLSTR* AddSqlStrToList(
23 __in SCA_SQLSTR* psssList,
24 __in SCA_SQLSTR* psss
25 );
26static HRESULT ExecuteStrings(
27 __in SCA_DB* psdList,
28 __in SCA_SQLSTR* psssList,
29 __in BOOL fInstall
30 );
31
32HRESULT ScaSqlStrsRead(
33 __inout SCA_SQLSTR** ppsssList,
34 __in SCA_ACTION saAction
35 )
36{
37 HRESULT hr = S_OK;
38 UINT er = ERROR_SUCCESS;
39 PMSIHANDLE hView, hRec;
40 PMSIHANDLE hViewUser, hRecUser;
41
42 LPWSTR pwzComponent = NULL;
43 LPWSTR pwzData = NULL;
44
45 SCA_SQLSTR* psss = NULL;
46
47 if (S_OK != WcaTableExists(L"SqlString") || S_OK != WcaTableExists(L"SqlDatabase"))
48 {
49 WcaLog(LOGMSG_VERBOSE, "Skipping ScaSqlStrsRead() - SqlString and/or SqlDatabase table not present");
50 ExitFunction1(hr = S_FALSE);
51 }
52
53 // loop through all the sql strings
54 hr = WcaOpenExecuteView(vcsSqlStringQuery, &hView);
55 ExitOnFailure(hr, "Failed to open view on SqlString table");
56 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
57 {
58 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
59 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
60
61 hr = WcaGetRecordString(hRec, ssqComponent, &pwzComponent);
62 ExitOnFailure(hr, "Failed to get Component for SQL String.");
63
64 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction);
65 hr = HRESULT_FROM_WIN32(er);
66 ExitOnFailure(hr, "Failed to get state for component: %ls", pwzComponent);
67
68 // If we're doing install but the Component is not being installed or we're doing
69 // uninstall but the Component is not being uninstalled, skip it.
70 if ((WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL != saAction) ||
71 (WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL != saAction))
72 {
73 continue;
74 }
75
76 hr = NewSqlStr(&psss);
77 ExitOnFailure(hr, "failed to allocation new sql string element");
78
79 psss->isInstalled = isInstalled;
80 psss->isAction = isAction;
81
82 hr = WcaGetRecordString(hRec, ssqSqlString, &pwzData);
83 ExitOnFailure(hr, "Failed to get SqlString.String");
84 hr = ::StringCchCopyW(psss->wzKey, countof(psss->wzKey), pwzData);
85 ExitOnFailure(hr, "Failed to copy SqlString.String: %ls", pwzData);
86
87 // find the database information for this string
88 hr = WcaGetRecordString(hRec, ssqSqlDb, &pwzData);
89 ExitOnFailure(hr, "Failed to get SqlString.SqlDb_ for SqlString '%ls'", psss->wzKey);
90 hr = ::StringCchCopyW(psss->wzSqlDb, countof(psss->wzSqlDb), pwzData);
91 ExitOnFailure(hr, "Failed to copy SqlString.SqlDb_: %ls", pwzData);
92
93 hr = WcaGetRecordInteger(hRec, ssqAttributes, &psss->iAttributes);
94 ExitOnFailure(hr, "Failed to get SqlString.Attributes for SqlString '%ls'", psss->wzKey);
95
96 //get the sequence number for the string (note that this will be sequenced with scripts too)
97 hr = WcaGetRecordInteger(hRec, ssqSequence, &psss->iSequence);
98 ExitOnFailure(hr, "Failed to get SqlString.Sequence for SqlString '%ls'", psss->wzKey);
99
100 // execute SQL
101 hr = WcaGetRecordFormattedString(hRec, ssqSQL, &pwzData);
102 ExitOnFailure(hr, "Failed to get SqlString.SQL for SqlString '%ls'", psss->wzKey);
103
104 Assert(!psss->pwzSql);
105 hr = StrAllocString(&psss->pwzSql, pwzData, 0);
106 ExitOnFailure(hr, "Failed to alloc string for SqlString '%ls'", psss->wzKey);
107
108 *ppsssList = AddSqlStrToList(*ppsssList, psss);
109 psss = NULL; // set the sss to NULL so it doesn't get freed below
110 }
111
112 if (E_NOMOREITEMS == hr)
113 {
114 hr = S_OK;
115 }
116 ExitOnFailure(hr, "Failure occured while reading SqlString table");
117
118LExit:
119 // if anything was left over after an error clean it all up
120 if (psss)
121 {
122 ScaSqlStrsFreeList(psss);
123 }
124
125 ReleaseStr(pwzData);
126 ReleaseStr(pwzComponent);
127
128 return hr;
129}
130
131
132HRESULT ScaSqlStrsReadScripts(
133 __inout SCA_SQLSTR** ppsssList,
134 __in SCA_ACTION saAction
135 )
136{
137 HRESULT hr = S_OK;
138 UINT er = ERROR_SUCCESS;
139
140 PMSIHANDLE hView, hRec;
141 PMSIHANDLE hViewBinary, hRecBinary;
142 PMSIHANDLE hViewUser, hRecUser;
143
144 LPWSTR pwzComponent = NULL;
145 LPWSTR pwzData = NULL;
146
147 BYTE* pbScript = NULL;
148 DWORD cbRead = 0;
149 DWORD cbScript = 0;
150 DWORD cchScript = 0;
151
152 LPWSTR pwzScriptBuffer = NULL;
153 WCHAR* pwzScript = NULL;
154 WCHAR* pwz;
155 DWORD cch = 0;
156
157 SCA_SQLSTR sss;
158 SCA_SQLSTR* psss = NULL;
159
160 if (S_OK != WcaTableExists(L"SqlScript") || S_OK != WcaTableExists(L"SqlDatabase") || S_OK != WcaTableExists(L"Binary"))
161 {
162 WcaLog(LOGMSG_VERBOSE, "Skipping ScaSqlStrsReadScripts() - SqlScripts and/or SqlDatabase table not present");
163 ExitFunction1(hr = S_FALSE);
164 }
165
166 // open a view on the binary table
167 hr = WcaOpenView(vcsSqlBinaryScriptQuery, &hViewBinary);
168 ExitOnFailure(hr, "Failed to open view on Binary table for SqlScripts");
169
170 // loop through all the sql scripts
171 hr = WcaOpenExecuteView(vcsSqlScriptQuery, &hView);
172 ExitOnFailure(hr, "Failed to open view on SqlScript table");
173 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
174 {
175 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
176 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
177
178 hr = WcaGetRecordString(hRec, sscrqComponent, &pwzComponent);
179 ExitOnFailure(hr, "Failed to get Component for SQL Script.");
180
181 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction);
182 hr = HRESULT_FROM_WIN32(er);
183 ExitOnFailure(hr, "Failed to get state for component: %ls", pwzComponent);
184
185 // If we're doing install but the Component is not being installed or we're doing
186 // uninstall but the Component is not being uninstalled, skip it.
187 if ((WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL != saAction) ||
188 (WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL != saAction))
189 {
190 continue;
191 }
192
193 ::ZeroMemory(&sss, sizeof(sss));
194
195 sss.isInstalled = isInstalled;
196 sss.isAction = isAction;
197
198 hr = WcaGetRecordString(hRec, sscrqSqlScript, &pwzData);
199 ExitOnFailure(hr, "Failed to get SqlScript.Script");
200 hr = ::StringCchCopyW(sss.wzKey, countof(sss.wzKey), pwzData);
201 ExitOnFailure(hr, "Failed to copy SqlScript.Script: %ls", pwzData);
202
203 // find the database information for this string
204 hr = WcaGetRecordString(hRec, sscrqSqlDb, &pwzData);
205 ExitOnFailure(hr, "Failed to get SqlScript.SqlDb_ for SqlScript '%ls'", sss.wzKey);
206 hr = ::StringCchCopyW(sss.wzSqlDb, countof(sss.wzSqlDb), pwzData);
207 ExitOnFailure(hr, "Failed to copy SqlScritp.SqlDbb: %ls", pwzData);
208
209 hr = WcaGetRecordInteger(hRec, sscrqAttributes, &sss.iAttributes);
210 ExitOnFailure(hr, "Failed to get SqlScript.Attributes for SqlScript '%ls'", sss.wzKey);
211
212 hr = WcaGetRecordInteger(hRec, sscrqSequence, &sss.iSequence);
213 ExitOnFailure(hr, "Failed to get SqlScript.Sequence for SqlScript '%ls'", sss.wzKey);
214
215 // get the sql script out of the binary stream
216 hr = WcaExecuteView(hViewBinary, hRec);
217 ExitOnFailure(hr, "Failed to open SqlScript.BinaryScript_ for SqlScript '%ls'", sss.wzKey);
218 hr = WcaFetchSingleRecord(hViewBinary, &hRecBinary);
219 ExitOnFailure(hr, "Failed to fetch SqlScript.BinaryScript_ for SqlScript '%ls'", sss.wzKey);
220
221 // Note: We need to allocate an extra character on the stream to NULL terminate the SQL script.
222 // The WcaGetRecordStream() function won't let us add extra space on the end of the stream
223 // so we'll read the stream "the old fashioned way".
224 //hr = WcaGetRecordStream(hRecBinary, ssbsqData, (BYTE**)&pbScript, &cbScript);
225 //ExitOnFailure(hr, "Failed to read SqlScript.BinaryScript_ for SqlScript '%ls'", sss.wzKey);
226 er = ::MsiRecordReadStream(hRecBinary, ssbsqData, NULL, &cbRead);
227 hr = HRESULT_FROM_WIN32(er);
228 ExitOnFailure(hr, "failed to get size of stream");
229
230 cbScript = cbRead + sizeof(WCHAR); // we may have an ANSI SQL script but leave enough to even NULL terminate a WCHAR string
231 hr = WcaAllocStream(&pbScript, cbScript); // this will allocate a fully zeroed out buffer so our string will be NULL terminated
232 ExitOnFailure(hr, "failed to allocate data for stream");
233
234 er = ::MsiRecordReadStream(hRecBinary, ssbsqData, reinterpret_cast<char*>(pbScript), &cbRead); //read the buffer but leave the space for the NULL terminator
235 hr = HRESULT_FROM_WIN32(er);
236 ExitOnFailure(hr, "failed to read from stream");
237
238 Assert(cbRead + sizeof(WCHAR) == cbScript);
239
240 // Check for the UNICODE BOM file marker.
241 if ((0xFF == *pbScript) && (0xFE == *(pbScript + 1)))
242 {
243 // Copy the UNICODE string after the BOM marker (subtract one because we'll skip the BOM marker).
244 cchScript = (cbScript / sizeof(WCHAR)) - 1;
245
246 hr = StrAllocString(&pwzScriptBuffer, reinterpret_cast<LPWSTR>(pbScript) + 1, 0);
247 ExitOnFailure(hr, "Failed to allocate WCHAR string of size '%d'", cchScript);
248 }
249 else
250 {
251 // We have an ANSI string so convert it to UNICODE.
252 cchScript = cbScript;
253
254 hr = StrAllocStringAnsi(&pwzScriptBuffer, reinterpret_cast<LPCSTR>(pbScript), 0, CP_ACP);
255 ExitOnFailure(hr, "Failed to allocate WCHAR string of size '%d'", cchScript);
256 }
257
258 // Free the byte buffer since it has been converted to a new UNICODE string, one way or another.
259 if (pbScript)
260 {
261 WcaFreeStream(pbScript);
262 pbScript = NULL;
263 }
264
265 // Process the SQL script stripping out unnecessary stuff (like comments) and looking for "GO" statements.
266 pwzScript = pwzScriptBuffer;
267 while (cchScript && pwzScript && *pwzScript)
268 {
269 // strip off leading whitespace
270 while (cchScript && *pwzScript && iswspace(*pwzScript))
271 {
272 ++pwzScript;
273 --cchScript;
274 }
275
276 Assert(0 <= cchScript);
277
278 // if there is a SQL comment remove it
279 while (cchScript && L'/' == *pwzScript && L'*' == *(pwzScript + 1))
280 {
281 // go until end of comment
282 while (cchScript && *pwzScript && *(pwzScript + 1) && !(L'*' == *pwzScript && L'/' == *(pwzScript + 1)))
283 {
284 ++pwzScript;
285 --cchScript;
286 }
287
288 Assert(2 <= cchScript);
289
290 if (2 <= cchScript)
291 {
292 // to account for */ at end
293 pwzScript+=2;
294 cchScript-=2;
295 }
296
297 Assert(0 <= cchScript);
298
299 // strip off any new leading whitespace
300 while (cchScript && *pwzScript && iswspace(*pwzScript))
301 {
302 ++pwzScript;
303 --cchScript;
304 }
305 }
306
307 while (cchScript && L'-' == *pwzScript && L'-' == *(pwzScript + 1))
308 {
309 // go past the new line character
310 while (cchScript && *pwzScript && L'\n' != *pwzScript)
311 {
312 ++pwzScript;
313 --cchScript;
314 }
315
316 Assert(0 <= cchScript);
317
318 if (cchScript && L'\n' == *pwzScript)
319 {
320 ++pwzScript;
321 --cchScript;
322 }
323
324 Assert(0 <= cchScript);
325
326 // strip off any new leading whitespace
327 while (cchScript && *pwzScript && iswspace(*pwzScript))
328 {
329 ++pwzScript;
330 --cchScript;
331 }
332 }
333
334 Assert(0 <= cchScript);
335
336 // try to isolate a "GO" keyword and count the characters in the SQL string
337 pwz = pwzScript;
338 cch = 0;
339 while (cchScript && *pwz)
340 {
341 //skip past comment lines that might have "go" in them
342 //note that these comments are "in the middle" of our function,
343 //or possibly at the end of a line
344 if (cchScript && L'-' == *pwz && L'-' == *(pwz + 1))
345 {
346 // skip past chars until the new line character
347 while (cchScript && *pwz && (L'\n' != *pwz))
348 {
349 ++pwz;
350 ++cch;
351 --cchScript;
352 }
353 }
354
355 //skip past comment lines of form /* to */ that might have "go" in them
356 //note that these comments are "in the middle" of our function,
357 //or possibly at the end of a line
358 if (cchScript && L'/' == *pwz && L'*' == *(pwz + 1))
359 {
360 // skip past chars until the new line character
361 while (cchScript && *pwz && *(pwz + 1) && !((L'*' == *pwz) && (L'/' == *(pwz + 1))))
362 {
363 ++pwz;
364 ++cch;
365 --cchScript;
366 }
367
368 if (2 <= cchScript)
369 {
370 // to account for */ at end
371 pwz+=2;
372 cch+=2;
373 cchScript-=2;
374 }
375 }
376
377 // Skip past strings that may be part of the SQL statement that might have a "go" in them
378 if ( cchScript && L'\'' == *pwz )
379 {
380 ++pwz;
381 ++cch;
382 --cchScript;
383
384 // Skip past chars until the end of the string
385 while ( cchScript && *pwz && !(L'\'' == *pwz) )
386 {
387 ++pwz;
388 ++cch;
389 --cchScript;
390 }
391 }
392
393 // Skip past strings that may be part of the SQL statement that might have a "go" in them
394 if ( cchScript && L'\"' == *pwz )
395 {
396 ++pwz;
397 ++cch;
398 --cchScript;
399
400 // Skip past chars until the end of the string
401 while ( cchScript && *pwz && !(L'\"' == *pwz) )
402 {
403 ++pwz;
404 ++cch;
405 --cchScript;
406 }
407 }
408
409 // if "GO" is isolated
410 if ((pwzScript == pwz || iswspace(*(pwz - 1))) &&
411 (L'G' == *pwz || L'g' == *pwz) &&
412 (L'O' == *(pwz + 1) || L'o' == *(pwz + 1)) &&
413 (0 == *(pwz + 2) || iswspace(*(pwz + 2))))
414 {
415 *pwz = 0; // null terminate the SQL string on the "G"
416 pwz += 2;
417 cchScript -= 2;
418 break; // found "GO" now add SQL string to list
419 }
420
421 ++pwz;
422 ++cch;
423 --cchScript;
424 }
425
426 Assert(0 <= cchScript);
427
428 if (0 < cch) //don't process if there's nothing to process
429 {
430 // replace tabs with spaces
431 for (LPWSTR pwzTab = wcsstr(pwzScript, L"\t"); pwzTab; pwzTab = wcsstr(pwzTab, L"\t"))
432 *pwzTab = ' ';
433
434 // strip off whitespace at the end of the script string
435 for (LPWSTR pwzErase = pwzScript + cch - 1; pwzScript < pwzErase && iswspace(*pwzErase); pwzErase--)
436 {
437 *(pwzErase) = 0;
438 cch--;
439 }
440 }
441
442 if (0 < cch)
443 {
444 hr = NewSqlStr(&psss);
445 ExitOnFailure(hr, "failed to allocate new sql string element");
446
447 // copy everything over
448 hr = ::StringCchCopyW(psss->wzKey, countof(psss->wzKey), sss.wzKey);
449 ExitOnFailure(hr, "Failed to copy key string to sqlstr object");
450 hr = ::StringCchCopyW(psss->wzSqlDb, countof(psss->wzSqlDb), sss.wzSqlDb);
451 ExitOnFailure(hr, "Failed to copy DB string to sqlstr object");
452 hr = ::StringCchCopyW(psss->wzComponent, countof(psss->wzComponent), sss.wzComponent);
453 ExitOnFailure(hr, "Failed to copy component string to sqlstr object");
454 psss->isInstalled = sss.isInstalled;
455 psss->isAction = sss.isAction;
456 psss->iAttributes = sss.iAttributes;
457 psss->iSequence = sss.iSequence;
458
459 // cchRequired includes the NULL terminating char
460 hr = StrAllocString(&psss->pwzSql, pwzScript, 0);
461 ExitOnFailure(hr, "Failed to allocate string for SQL script: '%ls'", psss->wzKey);
462
463 *ppsssList = AddSqlStrToList(*ppsssList, psss);
464 psss = NULL; // set the db NULL so it doesn't accidentally get freed below
465 }
466
467 pwzScript = pwz;
468 }
469 }
470
471 if (E_NOMOREITEMS == hr)
472 {
473 hr = S_OK;
474 }
475 ExitOnFailure(hr, "Failure occured while reading SqlString table");
476
477LExit:
478 // if anything was left over after an error clean it all up
479 if (psss)
480 {
481 ScaSqlStrsFreeList(psss);
482 }
483
484 if (pbScript)
485 {
486 WcaFreeStream(pbScript);
487 }
488
489 ReleaseStr(pwzScriptBuffer);
490 ReleaseStr(pwzData);
491 ReleaseStr(pwzComponent);
492
493 return hr;
494}
495
496
497HRESULT ScaSqlStrsInstall(
498 __in SCA_DB* psdList,
499 __in SCA_SQLSTR* psssList
500 )
501{
502 HRESULT hr = ExecuteStrings(psdList, psssList, TRUE);
503
504 return hr;
505}
506
507
508HRESULT ScaSqlStrsUninstall(
509 __in SCA_DB* psdList,
510 __in SCA_SQLSTR* psssList
511 )
512{
513 HRESULT hr = ExecuteStrings(psdList, psssList, FALSE);
514
515 return hr;
516}
517
518
519void ScaSqlStrsFreeList(
520 __in SCA_SQLSTR* psssList
521 )
522{
523 SCA_SQLSTR* psssDelete = psssList;
524 while (psssList)
525 {
526 psssDelete = psssList;
527 psssList = psssList->psssNext;
528
529 if (psssDelete->pwzSql)
530 {
531 ReleaseStr(psssDelete->pwzSql);
532 }
533
534 MemFree(psssDelete);
535 }
536}
537
538
539// private helper functions
540
541static HRESULT NewSqlStr(
542 __out SCA_SQLSTR** ppsss
543 )
544{
545 HRESULT hr = S_OK;
546 SCA_SQLSTR* psss = static_cast<SCA_SQLSTR*>(MemAlloc(sizeof(SCA_SQLSTR), TRUE));
547 ExitOnNull(psss, hr, E_OUTOFMEMORY, "failed to allocate memory for new sql string element");
548
549 *ppsss = psss;
550
551LExit:
552 return hr;
553}
554
555
556static SCA_SQLSTR* AddSqlStrToList(
557 __in SCA_SQLSTR* psssList,
558 __in SCA_SQLSTR* psss
559 )
560{
561 Assert(psss); //just checking
562
563 //make certain we have a valid sequence number; note that negatives are technically valid
564 if (MSI_NULL_INTEGER == psss->iSequence)
565 {
566 psss->iSequence = 0;
567 }
568
569 if (psssList)
570 {
571 //list already exists, so insert psss into the list in Sequence order
572
573 //see if we need to change the head, otherwise figure out where in the order it fits
574 if (psss->iSequence < psssList->iSequence)
575 {
576 psss->psssNext = psssList;
577 psssList = psss;
578 }
579 else
580 {
581 SCA_SQLSTR* psssT = psssList;
582 //note that if Sequence numbers are duplicated, as in the case of a sqlscript,
583 //we need to insert them "at the end" of the group so the sqlfile stays in order
584 while (psssT->psssNext && (psssT->psssNext->iSequence <= psss->iSequence))
585 {
586 psssT = psssT->psssNext;
587 }
588
589 //insert our new psss AFTER psssT
590 psss->psssNext = psssT->psssNext;
591 psssT->psssNext = psss;
592 }
593 }
594 else
595 {
596 psssList = psss;
597 }
598
599 return psssList;
600}
601
602
603static HRESULT ExecuteStrings(
604 __in SCA_DB* psdList,
605 __in SCA_SQLSTR* psssList,
606 __in BOOL fInstall
607 )
608{
609 HRESULT hr = S_FALSE; // assume nothing will be done
610
611 int iRollback = -1;
612 int iOldRollback = iRollback;
613
614 LPCWSTR wzOldDb = NULL;
615 UINT uiCost = 0;
616 WCHAR* pwzCustomActionData = NULL;
617 WCHAR wzNumber[64];
618
619 // loop through all sql strings
620 for (SCA_SQLSTR* psss = psssList; psss; psss = psss->psssNext)
621 {
622 // if installing this component
623 if ((fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_INSTALL) && WcaIsInstalling(psss->isInstalled, psss->isAction) && !WcaIsReInstalling(psss->isInstalled, psss->isAction)) ||
624 (fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_REINSTALL) && WcaIsReInstalling(psss->isInstalled, psss->isAction)) ||
625 (!fInstall && (psss->iAttributes & SCASQL_EXECUTE_ON_UNINSTALL) && WcaIsUninstalling(psss->isInstalled, psss->isAction)))
626 {
627 // determine if this is a rollback scheduling or normal deferred scheduling
628 if (psss->iAttributes & SCASQL_ROLLBACK)
629 {
630 iRollback = 1;
631 }
632 else
633 {
634 iRollback = 0;
635 }
636
637 // if we need to create a connection to a new server\database
638 if (!wzOldDb || 0 != lstrcmpW(wzOldDb, psss->wzSqlDb) || iOldRollback != iRollback)
639 {
640 const SCA_DB* psd = ScaDbsFindDatabase(psss->wzSqlDb, psdList);
641 if (!psd)
642 {
643 ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "failed to find data for Database: %ls", psss->wzSqlDb);
644 }
645
646 if (-1 == iOldRollback)
647 {
648 iOldRollback = iRollback;
649 }
650 Assert(0 == iOldRollback || 1 == iOldRollback);
651
652 // if there was custom action data before, schedule the action to write it
653 if (pwzCustomActionData && *pwzCustomActionData)
654 {
655 Assert(pwzCustomActionData && *pwzCustomActionData && uiCost);
656
657 hr = WcaDoDeferredAction(1 == iOldRollback ? L"RollbackExecuteSqlStrings" : L"ExecuteSqlStrings", pwzCustomActionData, uiCost);
658 ExitOnFailure(hr, "failed to schedule ExecuteSqlStrings action, rollback: %d", iOldRollback);
659 iOldRollback = iRollback;
660
661 *pwzCustomActionData = L'\0';
662 uiCost = 0;
663 }
664
665 Assert(!pwzCustomActionData || (pwzCustomActionData && 0 == *pwzCustomActionData) && 0 == uiCost);
666
667 hr = WcaWriteStringToCaData(psd->wzKey, &pwzCustomActionData);
668 ExitOnFailure(hr, "Failed to add SQL Server Database String to CustomActionData for Database String: %ls", psd->wzKey);
669
670 hr = WcaWriteStringToCaData(psd->wzServer, &pwzCustomActionData);
671 ExitOnFailure(hr, "Failed to add SQL Server to CustomActionData for Database String: %ls", psd->wzKey);
672
673 hr = WcaWriteStringToCaData(psd->wzInstance, &pwzCustomActionData);
674 ExitOnFailure(hr, "Failed to add SQL Instance to CustomActionData for Database String: %ls", psd->wzKey);
675
676 hr = WcaWriteStringToCaData(psd->wzDatabase, &pwzCustomActionData);
677 ExitOnFailure(hr, "Failed to add SQL Database to CustomActionData for Database String: %ls", psd->wzKey);
678
679 hr = ::StringCchPrintfW(wzNumber, countof(wzNumber), L"%d", psd->iAttributes);
680 ExitOnFailure(hr, "Failed to format attributes integer value to string");
681 hr = WcaWriteStringToCaData(wzNumber, &pwzCustomActionData);
682 ExitOnFailure(hr, "Failed to add SQL Attributes to CustomActionData for Database String: %ls", psd->wzKey);
683
684 hr = ::StringCchPrintfW(wzNumber, countof(wzNumber), L"%d", psd->fUseIntegratedAuth);
685 ExitOnFailure(hr, "Failed to format UseIntegratedAuth integer value to string");
686 hr = WcaWriteStringToCaData(wzNumber, &pwzCustomActionData);
687 ExitOnFailure(hr, "Failed to add SQL IntegratedAuth flag to CustomActionData for Database String: %ls", psd->wzKey);
688
689 hr = WcaWriteStringToCaData(psd->scau.wzName, &pwzCustomActionData);
690 ExitOnFailure(hr, "Failed to add SQL UserName to CustomActionData for Database String: %ls", psd->wzKey);
691
692 hr = WcaWriteStringToCaData(psd->scau.wzPassword, &pwzCustomActionData);
693 ExitOnFailure(hr, "Failed to add SQL Password to CustomActionData for Database String: %ls", psd->wzKey);
694
695 uiCost += COST_SQL_CONNECTDB;
696
697 wzOldDb = psss->wzSqlDb;
698 }
699
700 WcaLog(LOGMSG_VERBOSE, "Scheduling SQL string: %ls", psss->pwzSql);
701
702 hr = WcaWriteStringToCaData(psss->wzKey, &pwzCustomActionData);
703 ExitOnFailure(hr, "Failed to add SQL Key to CustomActionData for SQL string: %ls", psss->wzKey);
704
705 hr = WcaWriteIntegerToCaData(psss->iAttributes, &pwzCustomActionData);
706 ExitOnFailure(hr, "failed to add attributes to CustomActionData for SQL string: %ls", psss->wzKey);
707
708 hr = WcaWriteStringToCaData(psss->pwzSql, &pwzCustomActionData);
709 ExitOnFailure(hr, "Failed to to add SQL Query to CustomActionData for SQL string: %ls", psss->wzKey);
710 uiCost += COST_SQL_STRING;
711 }
712 }
713
714 if (pwzCustomActionData && *pwzCustomActionData)
715 {
716 Assert(pwzCustomActionData && *pwzCustomActionData && uiCost);
717 hr = WcaDoDeferredAction(1 == iRollback ? L"RollbackExecuteSqlStrings" : L"ExecuteSqlStrings", pwzCustomActionData, uiCost);
718 ExitOnFailure(hr, "Failed to schedule ExecuteSqlStrings action");
719
720 *pwzCustomActionData = L'\0';
721 uiCost = 0;
722 }
723
724LExit:
725 ReleaseStr(pwzCustomActionData);
726
727 return hr;
728}
diff --git a/src/ca/scasqlstr.h b/src/ca/scasqlstr.h
new file mode 100644
index 00000000..a6f6df1c
--- /dev/null
+++ b/src/ca/scasqlstr.h
@@ -0,0 +1,51 @@
1#pragma once
2// 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.
3
4
5#include "scadb.h"
6
7struct SCA_SQLSTR
8{
9 // darwin information
10 WCHAR wzKey[MAX_DARWIN_KEY + 1];
11 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
12 INSTALLSTATE isInstalled, isAction;
13
14 WCHAR wzSqlDb[MAX_DARWIN_COLUMN + 1];
15
16 BOOL fHasUser;
17 SCA_USER scau;
18
19 LPWSTR pwzSql;
20 int iAttributes;
21 int iSequence; //used to sequence SqlString and SqlScript tables together
22
23 SCA_SQLSTR* psssNext;
24};
25
26
27// prototypes
28HRESULT ScaSqlStrsRead(
29 __inout SCA_SQLSTR** ppsssList,
30 __in SCA_ACTION saAction
31 );
32
33HRESULT ScaSqlStrsReadScripts(
34 __inout SCA_SQLSTR** ppsssList,
35 __in SCA_ACTION saAction
36 );
37
38HRESULT ScaSqlStrsInstall(
39 __in SCA_DB* psdList,
40 __in SCA_SQLSTR* psssList
41 );
42
43HRESULT ScaSqlStrsUninstall(
44 __in SCA_DB* psdList,
45 __in SCA_SQLSTR* psssList
46 );
47
48void ScaSqlStrsFreeList(
49 __in SCA_SQLSTR* psssList
50 );
51
diff --git a/src/ca/scauser.cpp b/src/ca/scauser.cpp
new file mode 100644
index 00000000..4d74e4d4
--- /dev/null
+++ b/src/ca/scauser.cpp
@@ -0,0 +1,82 @@
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
5LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?";
6enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7
8
9HRESULT __stdcall ScaGetUser(
10 __in LPCWSTR wzUser,
11 __out SCA_USER* pscau
12 )
13{
14 if (!wzUser || !pscau)
15 {
16 return E_INVALIDARG;
17 }
18
19 HRESULT hr = S_OK;
20 PMSIHANDLE hView, hRec;
21
22 LPWSTR pwzData = NULL;
23
24 // clear struct and bail right away if no user key was passed to search for
25 ::ZeroMemory(pscau, sizeof(*pscau));
26 if (!*wzUser)
27 {
28 ExitFunction1(hr = S_OK);
29 }
30
31 hRec = ::MsiCreateRecord(1);
32 hr = WcaSetRecordString(hRec, 1, wzUser);
33 ExitOnFailure(hr, "Failed to look up User");
34
35 hr = WcaOpenView(vcsUserQuery, &hView);
36 ExitOnFailure(hr, "Failed to open view on User table");
37 hr = WcaExecuteView(hView, hRec);
38 ExitOnFailure(hr, "Failed to execute view on User table");
39
40 hr = WcaFetchSingleRecord(hView, &hRec);
41 if (S_OK == hr)
42 {
43 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
44 ExitOnFailure(hr, "Failed to get User.User");
45 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
46 ExitOnFailure(hr, "Failed to copy key string to user object");
47
48 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
49 ExitOnFailure(hr, "Failed to get User.Component_");
50 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
51 ExitOnFailure(hr, "Failed to copy component string to user object");
52
53 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
54 ExitOnFailure(hr, "Failed to get User.Name");
55 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
56 ExitOnFailure(hr, "Failed to copy name string to user object");
57
58 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
59 ExitOnFailure(hr, "Failed to get User.Domain");
60 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
61 ExitOnFailure(hr, "Failed to copy domain string to user object");
62
63 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
64 ExitOnFailure(hr, "Failed to get User.Password");
65 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
66 ExitOnFailure(hr, "Failed to copy password string to user object");
67 }
68 else if (E_NOMOREITEMS == hr)
69 {
70 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate User.User='%ls'", wzUser);
71 hr = E_FAIL;
72 }
73 else
74 {
75 ExitOnFailure(hr, "Error or found multiple matching User rows");
76 }
77
78LExit:
79 ReleaseStr(pwzData);
80
81 return hr;
82}
diff --git a/src/ca/scauser.h b/src/ca/scauser.h
new file mode 100644
index 00000000..20e561f2
--- /dev/null
+++ b/src/ca/scauser.h
@@ -0,0 +1,40 @@
1#pragma once
2// 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.
3
4
5
6// structs
7struct SCA_GROUP
8{
9 WCHAR wzKey[MAX_DARWIN_KEY + 1];
10 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
11
12 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
13 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
14
15 SCA_GROUP *psgNext;
16};
17
18struct SCA_USER
19{
20 WCHAR wzKey[MAX_DARWIN_KEY + 1];
21 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
22 INSTALLSTATE isInstalled;
23 INSTALLSTATE isAction;
24
25 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
26 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
27 WCHAR wzPassword[MAX_DARWIN_COLUMN + 1];
28 INT iAttributes;
29
30 SCA_GROUP *psgGroups;
31
32 SCA_USER *psuNext;
33};
34
35
36// prototypes
37HRESULT __stdcall ScaGetUser(
38 __in LPCWSTR wzUser,
39 __out SCA_USER* pscau
40 );
diff --git a/src/ca/sqlca.def b/src/ca/sqlca.def
index e16626b3..e899d560 100644
--- a/src/ca/sqlca.def
+++ b/src/ca/sqlca.def
@@ -4,4 +4,10 @@
4LIBRARY "sqlca" 4LIBRARY "sqlca"
5 5
6EXPORTS 6EXPORTS
7 7;scaexec.cpp
8 CreateDatabase
9 DropDatabase
10 ExecuteSqlStrings
11;scasql.cpp
12 InstallSqlData
13 UninstallSqlData
diff --git a/src/ca/sqlca.vcxproj b/src/ca/sqlca.vcxproj
index 3d638f6e..b1f7dafa 100644
--- a/src/ca/sqlca.vcxproj
+++ b/src/ca/sqlca.vcxproj
@@ -45,11 +45,22 @@
45 <ClCompile Include="dllmain.cpp"> 45 <ClCompile Include="dllmain.cpp">
46 <PrecompiledHeader>Create</PrecompiledHeader> 46 <PrecompiledHeader>Create</PrecompiledHeader>
47 </ClCompile> 47 </ClCompile>
48 <ClCompile Include="scadb.cpp" />
49 <ClCompile Include="scaexec.cpp" />
50 <ClCompile Include="scasql.cpp" />
51 <ClCompile Include="scasqlstr.cpp" />
52 <ClCompile Include="scauser.cpp" />
48 <ClCompile Include="sqlca.cpp" /> 53 <ClCompile Include="sqlca.cpp" />
49 </ItemGroup> 54 </ItemGroup>
50 55
51 <ItemGroup> 56 <ItemGroup>
57 <ClInclude Include="CustomMsiErrors.h" />
52 <ClInclude Include="precomp.h" /> 58 <ClInclude Include="precomp.h" />
59 <ClInclude Include="sca.h" />
60 <ClInclude Include="scacost.h" />
61 <ClInclude Include="scadb.h" />
62 <ClInclude Include="scasqlstr.h" />
63 <ClInclude Include="scauser.h" />
53 </ItemGroup> 64 </ItemGroup>
54 65
55 <ItemGroup> 66 <ItemGroup>