| 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 `Wix4SqlString` 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 `Wix4SqlScript` 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"Wix4SqlString") || S_OK != WcaTableExists(L"Wix4SqlDatabase")) |
| 48 | { |
| 49 | WcaLog(LOGMSG_VERBOSE, "Skipping ScaSqlStrsRead() - Wix4SqlString and/or Wix4SqlDatabase 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 Wix4SqlString 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 Wix4SqlString.String"); |
| 84 | hr = ::StringCchCopyW(psss->wzKey, countof(psss->wzKey), pwzData); |
| 85 | ExitOnFailure(hr, "Failed to copy Wix4SqlString.String: %ls", pwzData); |
| 86 | |
| 87 | // find the database information for this string |
| 88 | hr = WcaGetRecordString(hRec, ssqSqlDb, &pwzData); |
| 89 | ExitOnFailure(hr, "Failed to get Wix4SqlString.SqlDb_ for SqlString '%ls'", psss->wzKey); |
| 90 | hr = ::StringCchCopyW(psss->wzSqlDb, countof(psss->wzSqlDb), pwzData); |
| 91 | ExitOnFailure(hr, "Failed to copy Wix4SqlString.SqlDb_: %ls", pwzData); |
| 92 | |
| 93 | hr = WcaGetRecordInteger(hRec, ssqAttributes, &psss->iAttributes); |
| 94 | ExitOnFailure(hr, "Failed to get Wix4SqlString.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 Wix4SqlString.Sequence for SqlString '%ls'", psss->wzKey); |
| 99 | |
| 100 | // execute SQL |
| 101 | hr = WcaGetRecordFormattedString(hRec, ssqSQL, &pwzData); |
| 102 | ExitOnFailure(hr, "Failed to get Wix4SqlString.SQL for SQL string '%ls'", psss->wzKey); |
| 103 | |
| 104 | Assert(!psss->pwzSql); |
| 105 | hr = StrAllocString(&psss->pwzSql, pwzData, 0); |
| 106 | ExitOnFailure(hr, "Failed to alloc string for SQL string '%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 Wix4SqlString 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"Wix4SqlScript") || S_OK != WcaTableExists(L"Wix4SqlDatabase") || S_OK != WcaTableExists(L"Binary")) |
| 161 | { |
| 162 | WcaLog(LOGMSG_VERBOSE, "Skipping ScaSqlStrsReadScripts() - Wix4SqlScript and/or Wix4SqlDatabase 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 SQL scripts"); |
| 169 | |
| 170 | // loop through all the sql scripts |
| 171 | hr = WcaOpenExecuteView(vcsSqlScriptQuery, &hView); |
| 172 | ExitOnFailure(hr, "Failed to open view on Wix4SqlScript 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 Wix4SqlScript.Script"); |
| 200 | hr = ::StringCchCopyW(sss.wzKey, countof(sss.wzKey), pwzData); |
| 201 | ExitOnFailure(hr, "Failed to copy Wix4SqlScript.Script: %ls", pwzData); |
| 202 | |
| 203 | // find the database information for this string |
| 204 | hr = WcaGetRecordString(hRec, sscrqSqlDb, &pwzData); |
| 205 | ExitOnFailure(hr, "Failed to get Wix4SqlScript.SqlDb_ for SqlScript '%ls'", sss.wzKey); |
| 206 | hr = ::StringCchCopyW(sss.wzSqlDb, countof(sss.wzSqlDb), pwzData); |
| 207 | ExitOnFailure(hr, "Failed to copy Wix4SqlScript.SqlDbb: %ls", pwzData); |
| 208 | |
| 209 | hr = WcaGetRecordInteger(hRec, sscrqAttributes, &sss.iAttributes); |
| 210 | ExitOnFailure(hr, "Failed to get Wix4SqlScript.Attributes for SqlScript '%ls'", sss.wzKey); |
| 211 | |
| 212 | hr = WcaGetRecordInteger(hRec, sscrqSequence, &sss.iSequence); |
| 213 | ExitOnFailure(hr, "Failed to get Wix4SqlScript.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 Wix4SqlScript.BinaryScript_ for SqlScript '%ls'", sss.wzKey); |
| 218 | hr = WcaFetchSingleRecord(hViewBinary, &hRecBinary); |
| 219 | ExitOnFailure(hr, "Failed to fetch Wix4SqlScript.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 Wix4SqlScript.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 Wix4SqlScript 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 ? CUSTOM_ACTION_DECORATION(L"RollbackExecuteSqlStrings") : CUSTOM_ACTION_DECORATION(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 ? CUSTOM_ACTION_DECORATION(L"RollbackExecuteSqlStrings") : CUSTOM_ACTION_DECORATION(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 | } |