diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-16 13:53:48 -0600 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2018-12-16 13:54:52 -0600 |
commit | 7d813eaad8eaca04a687d1bb942316232d1c54fd (patch) | |
tree | 8f5adb9da22b1f9fb79892fb6cf2de23fac32c71 | |
parent | 1b7a9d3734119e658c91ebd9742ab5a3ce94cce4 (diff) | |
download | wix-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.h | 10 | ||||
-rw-r--r-- | src/ca/precomp.h | 14 | ||||
-rw-r--r-- | src/ca/sca.h | 33 | ||||
-rw-r--r-- | src/ca/scacost.h | 7 | ||||
-rw-r--r-- | src/ca/scadb.cpp | 587 | ||||
-rw-r--r-- | src/ca/scadb.h | 55 | ||||
-rw-r--r-- | src/ca/scaexec.cpp | 393 | ||||
-rw-r--r-- | src/ca/scasql.cpp | 113 | ||||
-rw-r--r-- | src/ca/scasqlstr.cpp | 728 | ||||
-rw-r--r-- | src/ca/scasqlstr.h | 51 | ||||
-rw-r--r-- | src/ca/scauser.cpp | 82 | ||||
-rw-r--r-- | src/ca/scauser.h | 40 | ||||
-rw-r--r-- | src/ca/sqlca.def | 8 | ||||
-rw-r--r-- | src/ca/sqlca.vcxproj | 11 |
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. | ||
5 | enum SCA_ACTION | ||
6 | { | ||
7 | SCA_ACTION_NONE, | ||
8 | SCA_ACTION_INSTALL, | ||
9 | SCA_ACTION_UNINSTALL | ||
10 | }; | ||
11 | |||
12 | // sql database attributes definitions | ||
13 | enum 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 | ||
26 | enum 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 | |||
4 | const UINT COST_SQL_CREATEDB = 10000; | ||
5 | const UINT COST_SQL_DROPDB = 5000; | ||
6 | const UINT COST_SQL_CONNECTDB = 5000; | ||
7 | const 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 | ||
6 | LPCWSTR vcsSqlDatabaseQuery = L"SELECT `SqlDb`, `Server`, `Instance`, `Database`, " | ||
7 | L"`Component_`, `User_`, `FileSpec_`, `FileSpec_Log`, `Attributes` " | ||
8 | L"FROM `SqlDatabase`"; | ||
9 | enum eSqlDatabaseQuery { sdqSqlDb = 1, sdqServer, sdqInstance, sdqDatabase, | ||
10 | sdqComponent, sdqUser, sdqDbFileSpec, sdqLogFileSpec, sdqAttributes }; | ||
11 | |||
12 | LPCWSTR vcsSqlFileSpecQuery = L"SELECT `FileSpec`, `Name`, `Filename`, `Size`, " | ||
13 | L"`MaxSize`, `GrowthSize` FROM `SqlFileSpec` WHERE `FileSpec`=?"; | ||
14 | enum eSqlFileSpecQuery { sfsqFileSpec = 1, sfsqName, sfsqFilename, sfsqSize, | ||
15 | sfsqMaxSize, sfsqGrowth }; | ||
16 | |||
17 | |||
18 | // prototypes for private helper functions | ||
19 | static HRESULT NewDb( | ||
20 | __out SCA_DB** ppsd | ||
21 | ); | ||
22 | |||
23 | static SCA_DB* AddDbToList( | ||
24 | __in SCA_DB* psdList, | ||
25 | __in SCA_DB* psd | ||
26 | ); | ||
27 | |||
28 | static HRESULT SchedCreateDatabase( | ||
29 | __in SCA_DB* psd | ||
30 | ); | ||
31 | |||
32 | static 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 | |||
42 | static HRESULT GetFileSpec( | ||
43 | __in MSIHANDLE hViewFileSpec, | ||
44 | __in LPCWSTR wzKey, | ||
45 | __in SQL_FILESPEC* psf | ||
46 | ); | ||
47 | |||
48 | |||
49 | HRESULT 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 | |||
194 | LExit: | ||
195 | if (psd) | ||
196 | { | ||
197 | ScaDbsFreeList(psd); | ||
198 | } | ||
199 | |||
200 | ReleaseStr(pwzComponent); | ||
201 | ReleaseStr(pwzId); | ||
202 | ReleaseStr(pwzData); | ||
203 | return hr; | ||
204 | } | ||
205 | |||
206 | |||
207 | SCA_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 | |||
226 | HRESULT 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 | |||
255 | LExit: | ||
256 | return hr; | ||
257 | } | ||
258 | |||
259 | |||
260 | HRESULT 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 | |||
287 | LExit: | ||
288 | return hr; | ||
289 | } | ||
290 | |||
291 | |||
292 | void 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 | |||
309 | static 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 | |||
319 | LExit: | ||
320 | return hr; | ||
321 | } | ||
322 | |||
323 | |||
324 | static 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 | |||
348 | static 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 | |||
450 | LExit: | ||
451 | ReleaseStr(pwzCustomActionData); | ||
452 | return hr; | ||
453 | } | ||
454 | |||
455 | |||
456 | HRESULT 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 | |||
497 | LExit: | ||
498 | ReleaseStr(pwzCustomActionData); | ||
499 | return hr; | ||
500 | } | ||
501 | |||
502 | |||
503 | HRESULT 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; | ||
584 | LExit: | ||
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 | |||
8 | struct 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 | ||
35 | HRESULT ScaDbsRead( | ||
36 | __inout SCA_DB** ppsdList, | ||
37 | __in SCA_ACTION saAction | ||
38 | ); | ||
39 | |||
40 | SCA_DB* ScaDbsFindDatabase( | ||
41 | __in LPCWSTR wzSqlDb, | ||
42 | __in SCA_DB* psdList | ||
43 | ); | ||
44 | |||
45 | HRESULT ScaDbsInstall( | ||
46 | __in SCA_DB* psdList | ||
47 | ); | ||
48 | |||
49 | HRESULT ScaDbsUninstall( | ||
50 | __in SCA_DB* psdList | ||
51 | ); | ||
52 | |||
53 | void 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 | * ****************************************************************/ | ||
11 | extern "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); | ||
155 | LExit: | ||
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 | * ****************************************************************/ | ||
183 | extern "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 | |||
243 | LExit: | ||
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 | * ****************************************************************/ | ||
273 | extern "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 | |||
371 | LExit: | ||
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 | ||
6 | static HRESULT ConfigureSqlData( | ||
7 | __in SCA_ACTION saAction | ||
8 | ); | ||
9 | |||
10 | |||
11 | /******************************************************************** | ||
12 | InstallSqlData - CUSTOM ACTION ENTRY POINT for installing | ||
13 | SQL data | ||
14 | |||
15 | ********************************************************************/ | ||
16 | extern "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 | |||
29 | LExit: | ||
30 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; | ||
31 | return WcaFinalize(er); | ||
32 | } | ||
33 | |||
34 | |||
35 | /******************************************************************** | ||
36 | UninstallSqlData - CUSTOM ACTION ENTRY POINT for uninstalling | ||
37 | SQL data | ||
38 | |||
39 | ********************************************************************/ | ||
40 | extern "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 | |||
53 | LExit: | ||
54 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; | ||
55 | return WcaFinalize(er); | ||
56 | } | ||
57 | |||
58 | |||
59 | static 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 | |||
105 | LExit: | ||
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 | ||
6 | LPCWSTR vcsSqlStringQuery = L"SELECT `String`, `SqlDb_`, `Component_`,`SQL`,`User_`,`Attributes`,`Sequence` " | ||
7 | L"FROM `SqlString` ORDER BY `SqlDb_`,`Sequence`"; | ||
8 | enum eSqlStringQuery { ssqSqlString = 1, ssqSqlDb, ssqComponent, ssqSQL, ssqUser, ssqAttributes, ssqSequence }; | ||
9 | |||
10 | LPCWSTR vcsSqlScriptQuery = L"SELECT `ScriptBinary_`,`Script`, `SqlDb_`, `Component_`,`User_`,`Attributes`,`Sequence` " | ||
11 | L"FROM `SqlScript` ORDER BY `SqlDb_`,`Sequence`"; | ||
12 | enum eSqlScriptQuery { sscrqScriptBinary=1, sscrqSqlScript, sscrqSqlDb, sscrqComponent, sscrqUser, sscrqAttributes, sscrqSequence }; | ||
13 | |||
14 | LPCWSTR vcsSqlBinaryScriptQuery = L"SELECT `Data` FROM `Binary` WHERE `Name`=?"; | ||
15 | enum eSqlBinaryScriptQuery { ssbsqData = 1 }; | ||
16 | |||
17 | |||
18 | // prototypes for private helper functions | ||
19 | static HRESULT NewSqlStr( | ||
20 | __out SCA_SQLSTR** ppsss | ||
21 | ); | ||
22 | static SCA_SQLSTR* AddSqlStrToList( | ||
23 | __in SCA_SQLSTR* psssList, | ||
24 | __in SCA_SQLSTR* psss | ||
25 | ); | ||
26 | static HRESULT ExecuteStrings( | ||
27 | __in SCA_DB* psdList, | ||
28 | __in SCA_SQLSTR* psssList, | ||
29 | __in BOOL fInstall | ||
30 | ); | ||
31 | |||
32 | HRESULT 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 | |||
118 | LExit: | ||
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 | |||
132 | HRESULT 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 | |||
477 | LExit: | ||
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 | |||
497 | HRESULT 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 | |||
508 | HRESULT 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 | |||
519 | void 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 | |||
541 | static 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 | |||
551 | LExit: | ||
552 | return hr; | ||
553 | } | ||
554 | |||
555 | |||
556 | static 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 | |||
603 | static 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 | |||
724 | LExit: | ||
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 | |||
7 | struct 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 | ||
28 | HRESULT ScaSqlStrsRead( | ||
29 | __inout SCA_SQLSTR** ppsssList, | ||
30 | __in SCA_ACTION saAction | ||
31 | ); | ||
32 | |||
33 | HRESULT ScaSqlStrsReadScripts( | ||
34 | __inout SCA_SQLSTR** ppsssList, | ||
35 | __in SCA_ACTION saAction | ||
36 | ); | ||
37 | |||
38 | HRESULT ScaSqlStrsInstall( | ||
39 | __in SCA_DB* psdList, | ||
40 | __in SCA_SQLSTR* psssList | ||
41 | ); | ||
42 | |||
43 | HRESULT ScaSqlStrsUninstall( | ||
44 | __in SCA_DB* psdList, | ||
45 | __in SCA_SQLSTR* psssList | ||
46 | ); | ||
47 | |||
48 | void 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 | |||
5 | LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?"; | ||
6 | enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword }; | ||
7 | |||
8 | |||
9 | HRESULT __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 | |||
78 | LExit: | ||
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 | ||
7 | struct 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 | |||
18 | struct 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 | ||
37 | HRESULT __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 @@ | |||
4 | LIBRARY "sqlca" | 4 | LIBRARY "sqlca" |
5 | 5 | ||
6 | EXPORTS | 6 | EXPORTS |
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> |