diff options
Diffstat (limited to 'src/ca/scasqlstr.cpp')
| -rw-r--r-- | src/ca/scasqlstr.cpp | 728 |
1 files changed, 728 insertions, 0 deletions
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 | } | ||
