| 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 | #include <sqlce_oledb.h> |
| 6 | #include <sceutil.h> |
| 7 | |
| 8 | using namespace System; |
| 9 | using namespace Xunit; |
| 10 | using namespace WixTest; |
| 11 | |
| 12 | #define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);}; |
| 13 | |
| 14 | namespace DutilTests |
| 15 | { |
| 16 | enum TABLES |
| 17 | { |
| 18 | TABLE_A, |
| 19 | TABLE_COUNT |
| 20 | }; |
| 21 | |
| 22 | enum TABLE_A_COLUMNS |
| 23 | { |
| 24 | TABLE_A_KEY, |
| 25 | TABLE_A_BINARY, |
| 26 | TABLE_A_DWORD, |
| 27 | TABLE_A_QWORD, |
| 28 | TABLE_A_BOOL, |
| 29 | TABLE_A_STRING, |
| 30 | TABLE_A_DWORD_NULLABLE, |
| 31 | TABLE_A_INITIAL_COLUMNS, |
| 32 | |
| 33 | TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS, |
| 34 | TABLE_A_FINAL_COLUMNS |
| 35 | }; |
| 36 | |
| 37 | struct TableARowValue |
| 38 | { |
| 39 | DWORD dwAutoGenKey; |
| 40 | |
| 41 | BYTE *pbBinary; |
| 42 | DWORD cBinary; |
| 43 | |
| 44 | DWORD dw; |
| 45 | DWORD64 qw; |
| 46 | BOOL f; |
| 47 | LPWSTR scz; |
| 48 | |
| 49 | BOOL fNullablePresent; |
| 50 | DWORD dwNullable; |
| 51 | |
| 52 | BOOL fSchemaV2; |
| 53 | LPWSTR sczExtra; |
| 54 | }; |
| 55 | |
| 56 | public ref class SceUtil |
| 57 | { |
| 58 | public: |
| 59 | void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema) |
| 60 | { |
| 61 | DWORD dwTable; |
| 62 | |
| 63 | for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable) |
| 64 | { |
| 65 | ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns); |
| 66 | ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes); |
| 67 | } |
| 68 | |
| 69 | ReleaseMem(pdsSchema->rgTables); |
| 70 | |
| 71 | return; |
| 72 | } |
| 73 | |
| 74 | void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended) |
| 75 | { |
| 76 | pSchema->cTables = TABLE_COUNT; |
| 77 | pSchema->rgTables = static_cast<SCE_TABLE_SCHEMA*>(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE)); |
| 78 | NativeAssert::True(pSchema->rgTables != NULL); |
| 79 | |
| 80 | pSchema->rgTables[TABLE_A].wzName = L"TableA"; |
| 81 | pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS; |
| 82 | pSchema->rgTables[TABLE_A].cIndexes = 2; |
| 83 | |
| 84 | for (DWORD i = 0; i < pSchema->cTables; ++i) |
| 85 | { |
| 86 | pSchema->rgTables[i].rgColumns = static_cast<SCE_COLUMN_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE)); |
| 87 | NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL); |
| 88 | |
| 89 | pSchema->rgTables[i].rgIndexes = static_cast<SCE_INDEX_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE)); |
| 90 | NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL); |
| 91 | } |
| 92 | |
| 93 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key"; |
| 94 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4; |
| 95 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE; |
| 96 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE; |
| 97 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary"; |
| 98 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES; |
| 99 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword"; |
| 100 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4; |
| 101 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword"; |
| 102 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8; |
| 103 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool"; |
| 104 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL; |
| 105 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String"; |
| 106 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR; |
| 107 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable"; |
| 108 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4; |
| 109 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE; |
| 110 | |
| 111 | if (fIncludeExtended) |
| 112 | { |
| 113 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString"; |
| 114 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR; |
| 115 | pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE; |
| 116 | } |
| 117 | |
| 118 | static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD }; |
| 119 | static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING }; |
| 120 | |
| 121 | ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword"); |
| 122 | ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String"); |
| 123 | } |
| 124 | |
| 125 | void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra) |
| 126 | { |
| 127 | pValue->pbBinary = pbBinary; |
| 128 | pValue->cBinary = cBinary; |
| 129 | pValue->dw = dw; |
| 130 | pValue->qw = qw; |
| 131 | pValue->f = f; |
| 132 | pValue->scz = scz; |
| 133 | |
| 134 | if (pdw) |
| 135 | { |
| 136 | pValue->fNullablePresent = TRUE; |
| 137 | pValue->dwNullable = *pdw; |
| 138 | } |
| 139 | else |
| 140 | { |
| 141 | pValue->fNullablePresent = FALSE; |
| 142 | } |
| 143 | |
| 144 | if (sczExtra) |
| 145 | { |
| 146 | pValue->fSchemaV2 = TRUE; |
| 147 | pValue->sczExtra = sczExtra; |
| 148 | } |
| 149 | else |
| 150 | { |
| 151 | pValue->fSchemaV2 = FALSE; |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther) |
| 156 | { |
| 157 | NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary); |
| 158 | NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary)); |
| 159 | |
| 160 | NativeAssert::Equal(pValueExpected->dw, pValueOther->dw); |
| 161 | NativeAssert::Equal(pValueExpected->qw, pValueOther->qw); |
| 162 | NativeAssert::Equal(pValueExpected->f, pValueOther->f); |
| 163 | NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz)); |
| 164 | |
| 165 | NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent); |
| 166 | if (pValueExpected->fNullablePresent) |
| 167 | { |
| 168 | NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable); |
| 169 | } |
| 170 | |
| 171 | NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2); |
| 172 | if (pValueExpected->fSchemaV2) |
| 173 | { |
| 174 | NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra)); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback) |
| 179 | { |
| 180 | HRESULT hr = S_OK; |
| 181 | SCE_ROW_HANDLE sceRow = NULL; |
| 182 | |
| 183 | hr = SceBeginTransaction(pDatabase); |
| 184 | NativeAssert::Succeeded(hr, "Failed to begin transaction"); |
| 185 | |
| 186 | hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow); |
| 187 | NativeAssert::Succeeded(hr, "Failed to prepare to insert row"); |
| 188 | |
| 189 | hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary); |
| 190 | NativeAssert::Succeeded(hr, "Failed to set binary value"); |
| 191 | |
| 192 | hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw); |
| 193 | NativeAssert::Succeeded(hr, "Failed to set dword value"); |
| 194 | |
| 195 | hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw); |
| 196 | NativeAssert::Succeeded(hr, "Failed to set qword value"); |
| 197 | |
| 198 | hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f); |
| 199 | NativeAssert::Succeeded(hr, "Failed to set bool value"); |
| 200 | |
| 201 | hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz); |
| 202 | NativeAssert::Succeeded(hr, "Failed to set string value"); |
| 203 | |
| 204 | if (pValue->fNullablePresent) |
| 205 | { |
| 206 | hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable); |
| 207 | NativeAssert::Succeeded(hr, "Failed to set dword value"); |
| 208 | } |
| 209 | else |
| 210 | { |
| 211 | hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE); |
| 212 | NativeAssert::Succeeded(hr, "Failed to set null value"); |
| 213 | } |
| 214 | |
| 215 | if (pValue->fSchemaV2) |
| 216 | { |
| 217 | hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra); |
| 218 | NativeAssert::Succeeded(hr, "Failed to set extra string value"); |
| 219 | } |
| 220 | |
| 221 | hr = SceFinishUpdate(sceRow); |
| 222 | NativeAssert::Succeeded(hr, "Failed to finish insert"); |
| 223 | |
| 224 | if (fRollback) |
| 225 | { |
| 226 | hr = SceRollbackTransaction(pDatabase); |
| 227 | NativeAssert::Succeeded(hr, "Failed to rollback transaction"); |
| 228 | } |
| 229 | else |
| 230 | { |
| 231 | hr = SceCommitTransaction(pDatabase); |
| 232 | NativeAssert::Succeeded(hr, "Failed to commit transaction"); |
| 233 | |
| 234 | hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey); |
| 235 | NativeAssert::Succeeded(hr, "Failed to get autogen key after insert"); |
| 236 | |
| 237 | NativeAssert::True(pValue->dwAutoGenKey != 0); |
| 238 | } |
| 239 | |
| 240 | ReleaseSceRow(sceRow); |
| 241 | } |
| 242 | |
| 243 | void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow) |
| 244 | { |
| 245 | HRESULT hr = S_OK; |
| 246 | TableARowValue value = {}; |
| 247 | |
| 248 | hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary); |
| 249 | NativeAssert::Succeeded(hr, "Failed to get binary value from result row"); |
| 250 | |
| 251 | hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw); |
| 252 | NativeAssert::Succeeded(hr, "Failed to get dword value from result row"); |
| 253 | |
| 254 | hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw); |
| 255 | NativeAssert::Succeeded(hr, "Failed to get qword value from result row"); |
| 256 | |
| 257 | hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f); |
| 258 | NativeAssert::Succeeded(hr, "Failed to get bool value from result row"); |
| 259 | |
| 260 | hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz); |
| 261 | NativeAssert::Succeeded(hr, "Failed to get string value from result row"); |
| 262 | |
| 263 | hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable); |
| 264 | if (hr == E_NOTFOUND) |
| 265 | { |
| 266 | value.fNullablePresent = FALSE; |
| 267 | hr = S_OK; |
| 268 | } |
| 269 | else |
| 270 | { |
| 271 | NativeAssert::Succeeded(hr, "Failed to get string value from result row"); |
| 272 | value.fNullablePresent = TRUE; |
| 273 | } |
| 274 | |
| 275 | if (pExpectedValue->fSchemaV2) |
| 276 | { |
| 277 | value.fSchemaV2 = TRUE; |
| 278 | hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra); |
| 279 | NativeAssert::Succeeded(hr, "Failed to get extra string value from result row"); |
| 280 | } |
| 281 | |
| 282 | AssertStructValuesSame(pExpectedValue, &value); |
| 283 | |
| 284 | ReleaseNullMem(value.pbBinary); |
| 285 | ReleaseNullStr(value.scz); |
| 286 | } |
| 287 | |
| 288 | void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults) |
| 289 | { |
| 290 | HRESULT hr = S_OK; |
| 291 | SCE_ROW_HANDLE sceRow = NULL; |
| 292 | |
| 293 | for (DWORD i = 0; i < cExpectedValues; ++i) |
| 294 | { |
| 295 | hr = SceGetNextResultRow(queryResults, &sceRow); |
| 296 | NativeAssert::Succeeded(hr, "Failed to get next result row"); |
| 297 | |
| 298 | VerifyRow(rgExpectedValues[i], sceRow); |
| 299 | ReleaseNullSceRow(sceRow); |
| 300 | } |
| 301 | |
| 302 | // No more results |
| 303 | NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow))); |
| 304 | } |
| 305 | |
| 306 | void TestIndex(SCE_DATABASE *pDatabase) |
| 307 | { |
| 308 | HRESULT hr = S_OK; |
| 309 | BYTE binary1[50] = { 0x80, 0x70 }; |
| 310 | BYTE binary2[40] = { 0x90, 0xAB }; |
| 311 | BYTE binary3[40] = { 0x85, 0x88 }; |
| 312 | DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888; |
| 313 | TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {}; |
| 314 | SCE_QUERY_HANDLE query = NULL; |
| 315 | SCE_QUERY_RESULTS_HANDLE results = NULL; |
| 316 | |
| 317 | SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL); |
| 318 | SetStructValues(&value2, static_cast<BYTE *>(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL); |
| 319 | SetStructValues(&value3, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL); |
| 320 | SetStructValues(&value4, static_cast<BYTE *>(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL); |
| 321 | SetStructValues(&value5, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL); |
| 322 | |
| 323 | // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards |
| 324 | InsertRow(pDatabase, &value1, TRUE); |
| 325 | |
| 326 | InsertRow(pDatabase, &value1, FALSE); |
| 327 | InsertRow(pDatabase, &value2, FALSE); |
| 328 | InsertRow(pDatabase, &value3, FALSE); |
| 329 | InsertRow(pDatabase, &value4, FALSE); |
| 330 | InsertRow(pDatabase, &value5, FALSE); |
| 331 | |
| 332 | NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey); |
| 333 | |
| 334 | // Test setting 1 column |
| 335 | hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); |
| 336 | NativeAssert::Succeeded(hr, "Failed to begin query"); |
| 337 | |
| 338 | hr = SceSetQueryColumnDword(query, 3); |
| 339 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 340 | |
| 341 | hr = SceRunQueryRange(&query, &results); |
| 342 | NativeAssert::Succeeded(hr, "Failed to run query"); |
| 343 | NativeAssert::True(query == NULL); |
| 344 | |
| 345 | TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 }; |
| 346 | VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results); |
| 347 | ReleaseNullSceQueryResults(results); |
| 348 | |
| 349 | // Test setting 2 columns, third column is unspecified so results are sorted by it |
| 350 | hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); |
| 351 | NativeAssert::Succeeded(hr, "Failed to begin query"); |
| 352 | |
| 353 | hr = SceSetQueryColumnDword(query, 3); |
| 354 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 355 | |
| 356 | hr = SceSetQueryColumnString(query, L"yyy"); |
| 357 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 358 | |
| 359 | hr = SceRunQueryRange(&query, &results); |
| 360 | NativeAssert::Succeeded(hr, "Failed to run query"); |
| 361 | NativeAssert::True(query == NULL); |
| 362 | |
| 363 | TableARowValue *sortedAfterQuery2[] = { &value5, &value2 }; |
| 364 | VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results); |
| 365 | ReleaseNullSceQueryResults(results); |
| 366 | |
| 367 | // Test setting 2 columns, third column of index is unspecified so results are sorted by it |
| 368 | hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); |
| 369 | NativeAssert::Succeeded(hr, "Failed to begin query"); |
| 370 | |
| 371 | hr = SceSetQueryColumnDword(query, 3); |
| 372 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 373 | |
| 374 | hr = SceSetQueryColumnString(query, L"yyy"); |
| 375 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 376 | |
| 377 | hr = SceRunQueryRange(&query, &results); |
| 378 | NativeAssert::Succeeded(hr, "Failed to run query"); |
| 379 | NativeAssert::True(query == NULL); |
| 380 | |
| 381 | TableARowValue *sortedAfterQuery3[] = { &value5, &value2 }; |
| 382 | VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results); |
| 383 | ReleaseNullSceQueryResults(results); |
| 384 | |
| 385 | // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by |
| 386 | hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query); |
| 387 | NativeAssert::Succeeded(hr, "Failed to begin query"); |
| 388 | |
| 389 | hr = SceSetQueryColumnDword(query, 3); |
| 390 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 391 | |
| 392 | hr = SceSetQueryColumnString(query, L"yyy"); |
| 393 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 394 | |
| 395 | hr = SceRunQueryRange(&query, &results); |
| 396 | NativeAssert::Succeeded(hr, "Failed to run query"); |
| 397 | NativeAssert::True(query == NULL); |
| 398 | |
| 399 | TableARowValue *sortedAfterQuery4[] = { &value2, &value5 }; |
| 400 | VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results); |
| 401 | ReleaseNullSceQueryResults(results); |
| 402 | } |
| 403 | |
| 404 | void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase) |
| 405 | { |
| 406 | HRESULT hr = S_OK; |
| 407 | BYTE binary1[40] = { 0x55, 0x44 }; |
| 408 | DWORD dwValue1 = 58; |
| 409 | TableARowValue value1 = {}; |
| 410 | SCE_QUERY_HANDLE query = NULL; |
| 411 | SCE_ROW_HANDLE row = NULL; |
| 412 | |
| 413 | SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring"); |
| 414 | |
| 415 | InsertRow(pDatabase, &value1, FALSE); |
| 416 | |
| 417 | // Test setting 1 column |
| 418 | hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query); |
| 419 | NativeAssert::Succeeded(hr, "Failed to begin query"); |
| 420 | |
| 421 | hr = SceSetQueryColumnDword(query, 5); |
| 422 | NativeAssert::Succeeded(hr, "Failed to set query column dword"); |
| 423 | |
| 424 | hr = SceRunQueryExact(&query, &row); |
| 425 | NativeAssert::Succeeded(hr, "Failed to run query exact"); |
| 426 | |
| 427 | VerifyRow(&value1, row); |
| 428 | } |
| 429 | |
| 430 | [Fact] |
| 431 | void SceUtilTest() |
| 432 | { |
| 433 | HRESULT hr = S_OK; |
| 434 | BOOL fComInitialized = FALSE; |
| 435 | LPWSTR sczDbPath = NULL; |
| 436 | SCE_DATABASE *pDatabase = NULL; |
| 437 | SCE_DATABASE_SCHEMA schema1 = {}; |
| 438 | SCE_DATABASE_SCHEMA schema2 = {}; |
| 439 | |
| 440 | try |
| 441 | { |
| 442 | hr = ::CoInitialize(0); |
| 443 | NativeAssert::Succeeded(hr, "Failed to initialize COM"); |
| 444 | fComInitialized = TRUE; |
| 445 | |
| 446 | SetupSchema(&schema1, FALSE); |
| 447 | SetupSchema(&schema2, TRUE); |
| 448 | |
| 449 | hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT); |
| 450 | NativeAssert::Succeeded(hr, "Failed to get path to test database"); |
| 451 | |
| 452 | FileEnsureDelete(sczDbPath); |
| 453 | |
| 454 | hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase); |
| 455 | NativeAssert::Succeeded(hr, "Failed to ensure database schema"); |
| 456 | |
| 457 | TestIndex(pDatabase); |
| 458 | |
| 459 | hr = SceCloseDatabase(pDatabase); |
| 460 | pDatabase = NULL; |
| 461 | NativeAssert::Succeeded(hr, "Failed to close database"); |
| 462 | |
| 463 | // Add column to schema |
| 464 | hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase); |
| 465 | NativeAssert::Succeeded(hr, "Failed to ensure database schema"); |
| 466 | |
| 467 | TestReadWriteSchemaV2(pDatabase); |
| 468 | } |
| 469 | finally |
| 470 | { |
| 471 | ReleaseSceSchema(&schema1); |
| 472 | ReleaseSceSchema(&schema2); |
| 473 | |
| 474 | if (NULL != pDatabase) |
| 475 | { |
| 476 | hr = SceCloseDatabase(pDatabase); |
| 477 | NativeAssert::Succeeded(hr, "Failed to close database"); |
| 478 | } |
| 479 | ReleaseStr(sczDbPath); |
| 480 | |
| 481 | if (fComInitialized) |
| 482 | { |
| 483 | ::CoUninitialize(); |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | }; |
| 488 | } |