main
cpp 2,487 lines 87.1 KB
Raw
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 "sceutil.h"
6
7 // Limit is documented as 4 GB, but for some reason the API's don't let us specify anything above 4091 MB.
8 #define MAX_SQLCE_DATABASE_SIZE 4091
9
10 // In case of some older versions of sqlce_oledb.h don't have these definitions, define some types.
11 #ifndef DBTYPEFOR_DBLENGTH
12 #ifdef _WIN64
13 #define SKIP_SCE_COMPILE
14 #else
15 #define SCE_32BIT_ONLY
16 typedef DWORD DBLENGTH;
17 typedef LONG DBROWOFFSET;
18 typedef LONG DBROWCOUNT;
19 typedef DWORD DBCOUNTITEM;
20 typedef DWORD DBORDINAL;
21 typedef LONG DB_LORDINAL;
22 typedef DWORD DBBKMARK;
23 typedef DWORD DBBYTEOFFSET;
24 typedef DWORD DBREFCOUNT;
25 typedef DWORD DB_UPARAMS;
26 typedef LONG DB_LPARAMS;
27 typedef DWORD DBHASHVALUE;
28 typedef DWORD DB_DWRESERVE;
29 typedef LONG DB_LRESERVE;
30 typedef DWORD DB_URESERVE;
31 #endif
32
33 #endif
34
35 #ifndef SKIP_SCE_COMPILE // If the sce headers don't support 64-bit, don't build for 64-bit
36
37 // structs
38 struct SCE_DATABASE_INTERNAL
39 {
40 // In case we call DllGetClassObject on a specific file
41 HMODULE hSqlCeDll;
42
43 volatile LONG dwTransactionRefcount;
44 IDBInitialize *pIDBInitialize;
45 IDBCreateSession *pIDBCreateSession;
46 ITransactionLocal *pITransactionLocal;
47 IDBProperties *pIDBProperties;
48 IOpenRowset *pIOpenRowset;
49 ISessionProperties *pISessionProperties;
50
51 BOOL fChanges; // This database has changed
52 BOOL fPendingChanges; // Some changes are pending, upon transaction commit
53 BOOL fRollbackTransaction; // If this flag is true, the current transaction was requested to be rolled back
54 BOOL fTransactionBadState; // If this flag is true, we were unable to get out of a transaction, so starting a new transaction should fail
55
56 // If the database was opened as read-only, we copied it here - so delete it on close
57 LPWSTR sczTempDbFile;
58 };
59
60 struct SCE_ROW
61 {
62 SCE_DATABASE_INTERNAL *pDatabaseInternal;
63
64 SCE_TABLE_SCHEMA *pTableSchema;
65 IRowset *pIRowset;
66 HROW hRow;
67 BOOL fInserting;
68
69 DWORD dwBindingIndex;
70 DBBINDING *rgBinding;
71 SIZE_T cbOffset;
72 BYTE *pbData;
73 };
74
75 struct SCE_QUERY
76 {
77 SCE_TABLE_SCHEMA *pTableSchema;
78 SCE_INDEX_SCHEMA *pIndexSchema;
79 SCE_DATABASE_INTERNAL *pDatabaseInternal;
80
81 // Accessor build-up members
82 DWORD dwBindingIndex;
83 DBBINDING *rgBinding;
84 SIZE_T cbOffset;
85 BYTE *pbData;
86 };
87
88 struct SCE_QUERY_RESULTS
89 {
90 SCE_DATABASE_INTERNAL *pDatabaseInternal;
91 IRowset *pIRowset;
92 SCE_TABLE_SCHEMA *pTableSchema;
93 };
94
95 extern const int SCE_ROW_HANDLE_BYTES = sizeof(SCE_ROW);
96 extern const int SCE_QUERY_HANDLE_BYTES = sizeof(SCE_QUERY);
97 extern const int SCE_QUERY_RESULTS_HANDLE_BYTES = sizeof(SCE_QUERY_RESULTS);
98
99 // The following is the internal Sce-maintained table to tell the identifier and version of the schema
100 const SCE_COLUMN_SCHEMA SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA[] =
101 {
102 {
103 L"AppIdentifier",
104 DBTYPE_WSTR,
105 0,
106 FALSE,
107 TRUE,
108 FALSE,
109 NULL,
110 0,
111 0
112 },
113 {
114 L"Version",
115 DBTYPE_I4,
116 0,
117 FALSE,
118 FALSE,
119 FALSE,
120 NULL,
121 0,
122 0
123 }
124 };
125
126 const SCE_TABLE_SCHEMA SCE_INTERNAL_VERSION_TABLE_SCHEMA[] =
127 {
128 L"SceSchemaTablev1",
129 _countof(SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA),
130 (SCE_COLUMN_SCHEMA *)SCE_INTERNAL_VERSION_TABLE_VERSION_COLUMN_SCHEMA,
131 0,
132 NULL,
133 NULL,
134 NULL
135 };
136
137 // internal function declarations
138
139 // Creates an instance of SQL Server CE object, returning IDBInitialize object
140 // If a file path is provided in wzSqlCeDllPath parameter, it calls DllGetClassObject
141 // on that file specifically. Otherwise it calls CoCreateInstance
142 static HRESULT CreateSqlCe(
143 __in_z_opt LPCWSTR wzSqlCeDllPath,
144 __out IDBInitialize **ppIDBInitialize,
145 __out_opt HMODULE *phSqlCeDll
146 );
147 static HRESULT RunQuery(
148 __in BOOL fRange,
149 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
150 __out SCE_QUERY_RESULTS **ppsqrhHandle
151 );
152 static HRESULT FillOutColumnDescFromSchema(
153 __in const SCE_COLUMN_SCHEMA *pSchema,
154 __out DBCOLUMNDESC pColumnDesc
155 );
156 static HRESULT EnsureSchema(
157 __in SCE_DATABASE *pDatabase,
158 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
159 );
160 static HRESULT OpenSchema(
161 __in SCE_DATABASE *pDatabase,
162 __in SCE_DATABASE_SCHEMA *pdsSchema
163 );
164 static HRESULT SetColumnValue(
165 __in const SCE_TABLE_SCHEMA *pTableSchema,
166 __in DWORD dwColumnIndex,
167 __in_bcount_opt(cbSize) const BYTE *pbData,
168 __in SIZE_T cbSize,
169 __inout DBBINDING *pBinding,
170 __inout SIZE_T *pcbOffset,
171 __inout BYTE **ppbBuffer
172 );
173 static HRESULT GetColumnValue(
174 __in SCE_ROW *pRow,
175 __in DWORD dwColumnIndex,
176 __out_opt BYTE **ppbData,
177 __out SIZE_T *pcbSize
178 );
179 static HRESULT GetColumnValueFixed(
180 __in SCE_ROW *pRow,
181 __in DWORD dwColumnIndex,
182 __in DWORD cbSize,
183 __out BYTE *pbData
184 );
185 static HRESULT EnsureLocalColumnConstraints(
186 __in ITableDefinition *pTableDefinition,
187 __in DBID *pTableID,
188 __in SCE_TABLE_SCHEMA *pTableSchema
189 );
190 static HRESULT EnsureForeignColumnConstraints(
191 __in ITableDefinition *pTableDefinition,
192 __in DBID *pTableID,
193 __in SCE_TABLE_SCHEMA *pTableSchema,
194 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
195 );
196 static HRESULT SetSessionProperties(
197 __in ISessionProperties *pISessionProperties
198 );
199 static HRESULT GetDatabaseSchemaInfo(
200 __in SCE_DATABASE *pDatabase,
201 __out LPWSTR *psczSchemaType,
202 __out DWORD *pdwVersion
203 );
204 static HRESULT SetDatabaseSchemaInfo(
205 __in SCE_DATABASE *pDatabase,
206 __in LPCWSTR wzSchemaType,
207 __in DWORD dwVersion
208 );
209 static void ReleaseDatabase(
210 SCE_DATABASE *pDatabase
211 );
212 static void ReleaseDatabaseInternal(
213 SCE_DATABASE_INTERNAL *pDatabaseInternal
214 );
215
216 // function definitions
217 extern "C" HRESULT DAPI SceCreateDatabase(
218 __in_z LPCWSTR sczFile,
219 __in_z_opt LPCWSTR wzSqlCeDllPath,
220 __out SCE_DATABASE **ppDatabase
221 )
222 {
223 HRESULT hr = S_OK;
224 LPWSTR sczDirectory = NULL;
225 SCE_DATABASE *pNewSceDatabase = NULL;
226 SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
227 IDBDataSourceAdmin *pIDBDataSourceAdmin = NULL;
228 DBPROPSET rgdbpDataSourcePropSet[2] = { };
229 DBPROP rgdbpDataSourceProp[2] = { };
230 DBPROP rgdbpDataSourceSsceProp[1] = { };
231
232 pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
233 ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
234
235 pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
236 ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
237
238 pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
239
240 hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
241 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
242
243 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBDataSourceAdmin, reinterpret_cast<void **>(&pIDBDataSourceAdmin));
244 ExitOnFailure(hr, "Failed to get IDBDataSourceAdmin interface");
245
246 hr = PathGetDirectory(sczFile, &sczDirectory);
247 ExitOnFailure(hr, "Failed to get directory portion of path: %ls", sczFile);
248
249 hr = DirEnsureExists(sczDirectory, NULL);
250 ExitOnFailure(hr, "Failed to ensure directory exists: %ls", sczDirectory);
251
252 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
253 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
254 rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
255 rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(sczFile);
256
257 rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
258 rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
259 rgdbpDataSourceProp[1].vValue.vt = VT_I4;
260 rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
261
262 // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
263 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
264 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
265 rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
266
267 rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
268 rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
269 rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
270 rgdbpDataSourceSsceProp[0].vValue.intVal = MAX_SQLCE_DATABASE_SIZE;
271
272 rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
273 rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
274 rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
275
276 hr = pIDBDataSourceAdmin->CreateDataSource(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet, NULL, IID_IUnknown, NULL);
277 ExitOnFailure(hr, "Failed to create data source");
278
279 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
280 ExitOnFailure(hr, "Failed to get IDBProperties interface");
281
282 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
283 ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
284
285 hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
286 ExitOnFailure(hr, "Failed to get ISessionProperties interface");
287
288 hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
289 ExitOnFailure(hr, "Failed to set session properties");
290
291 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
292 ExitOnFailure(hr, "Failed to get IOpenRowset interface");
293
294 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
295 ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
296
297 *ppDatabase = pNewSceDatabase;
298 pNewSceDatabase = NULL;
299
300 LExit:
301 ReleaseStr(sczDirectory);
302 ReleaseObject(pIDBDataSourceAdmin);
303 ReleaseDatabase(pNewSceDatabase);
304 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
305
306 return hr;
307 }
308
309 extern "C" HRESULT DAPI SceOpenDatabase(
310 __in_z LPCWSTR sczFile,
311 __in_z_opt LPCWSTR wzSqlCeDllPath,
312 __in LPCWSTR wzExpectedSchemaType,
313 __in DWORD dwExpectedVersion,
314 __out SCE_DATABASE **ppDatabase,
315 __in BOOL fReadOnly
316 )
317 {
318 HRESULT hr = S_OK;
319 DWORD dwVersionFound = 0;
320 LPWSTR sczTempDbFile = NULL;
321 LPCWSTR wzPathToOpen = NULL;
322 LPWSTR sczSchemaType = NULL;
323 SCE_DATABASE *pNewSceDatabase = NULL;
324 SCE_DATABASE_INTERNAL *pNewSceDatabaseInternal = NULL;
325 DBPROPSET rgdbpDataSourcePropSet[2] = { };
326 DBPROP rgdbpDataSourceProp[2] = { };
327 DBPROP rgdbpDataSourceSsceProp[1] = { };
328
329 pNewSceDatabase = reinterpret_cast<SCE_DATABASE *>(MemAlloc(sizeof(SCE_DATABASE), TRUE));
330 ExitOnNull(pNewSceDatabase, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE struct");
331
332 pNewSceDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(MemAlloc(sizeof(SCE_DATABASE_INTERNAL), TRUE));
333 ExitOnNull(pNewSceDatabaseInternal, hr, E_OUTOFMEMORY, "Failed to allocate SCE_DATABASE_INTERNAL struct");
334
335 pNewSceDatabase->sdbHandle = reinterpret_cast<void *>(pNewSceDatabaseInternal);
336
337 hr = CreateSqlCe(wzSqlCeDllPath, &pNewSceDatabaseInternal->pIDBInitialize, &pNewSceDatabaseInternal->hSqlCeDll);
338 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
339
340 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBProperties, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBProperties));
341 ExitOnFailure(hr, "Failed to get IDBProperties interface");
342
343 // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now.
344 if (fReadOnly)
345 {
346 hr = DirCreateTempPath(PathFile(sczFile), &sczTempDbFile);
347 ExitOnFailure(hr, "Failed to get temp path");
348
349 hr = FileEnsureCopy(sczFile, sczTempDbFile, TRUE);
350 ExitOnFailure(hr, "Failed to copy file to temp path");
351
352 hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, sczTempDbFile, 0);
353 ExitOnFailure(hr, "Failed to copy temp db file path");
354
355 wzPathToOpen = sczTempDbFile;
356 }
357 else
358 {
359 wzPathToOpen = sczFile;
360 }
361
362 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
363 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
364 rgdbpDataSourceProp[0].vValue.vt = VT_BSTR;
365 rgdbpDataSourceProp[0].vValue.bstrVal = ::SysAllocString(wzPathToOpen);
366
367 rgdbpDataSourceProp[1].dwPropertyID = DBPROP_INIT_MODE;
368 rgdbpDataSourceProp[1].dwOptions = DBPROPOPTIONS_REQUIRED;
369 rgdbpDataSourceProp[1].vValue.vt = VT_I4;
370 rgdbpDataSourceProp[1].vValue.lVal = DB_MODE_SHARE_DENY_NONE;
371
372 // SQL CE doesn't seem to allow us to specify DBPROP_INIT_PROMPT if we include any properties from DBPROPSET_SSCE_DBINIT
373 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_DBINIT;
374 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
375 rgdbpDataSourcePropSet[0].cProperties = _countof(rgdbpDataSourceProp);
376
377 rgdbpDataSourceSsceProp[0].dwPropertyID = DBPROP_SSCE_MAX_DATABASE_SIZE;
378 rgdbpDataSourceSsceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
379 rgdbpDataSourceSsceProp[0].vValue.vt = VT_I4;
380 rgdbpDataSourceSsceProp[0].vValue.lVal = MAX_SQLCE_DATABASE_SIZE;
381
382 rgdbpDataSourcePropSet[1].guidPropertySet = DBPROPSET_SSCE_DBINIT;
383 rgdbpDataSourcePropSet[1].rgProperties = rgdbpDataSourceSsceProp;
384 rgdbpDataSourcePropSet[1].cProperties = _countof(rgdbpDataSourceSsceProp);
385
386 hr = pNewSceDatabaseInternal->pIDBProperties->SetProperties(_countof(rgdbpDataSourcePropSet), rgdbpDataSourcePropSet);
387 ExitOnFailure(hr, "Failed to set initial properties to open database");
388
389 hr = pNewSceDatabaseInternal->pIDBInitialize->Initialize();
390 ExitOnFailure(hr, "Failed to open database: %ls", sczFile);
391
392 hr = pNewSceDatabaseInternal->pIDBInitialize->QueryInterface(IID_IDBCreateSession, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIDBCreateSession));
393 ExitOnFailure(hr, "Failed to get IDBCreateSession interface");
394
395 hr = pNewSceDatabaseInternal->pIDBCreateSession->CreateSession(NULL, IID_ISessionProperties, reinterpret_cast<IUnknown **>(&pNewSceDatabaseInternal->pISessionProperties));
396 ExitOnFailure(hr, "Failed to get ISessionProperties interface");
397
398 hr = SetSessionProperties(pNewSceDatabaseInternal->pISessionProperties);
399 ExitOnFailure(hr, "Failed to set session properties");
400
401 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_IOpenRowset, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pIOpenRowset));
402 ExitOnFailure(hr, "Failed to get IOpenRowset interface");
403
404 hr = pNewSceDatabaseInternal->pISessionProperties->QueryInterface(IID_ITransactionLocal, reinterpret_cast<void **>(&pNewSceDatabaseInternal->pITransactionLocal));
405 ExitOnFailure(hr, "Failed to get ITransactionLocal interface");
406
407 hr = GetDatabaseSchemaInfo(pNewSceDatabase, &sczSchemaType, &dwVersionFound);
408 ExitOnFailure(hr, "Failed to find schema version of database");
409
410 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczSchemaType, -1, wzExpectedSchemaType, -1))
411 {
412 hr = HRESULT_FROM_WIN32(ERROR_BAD_FILE_TYPE);
413 ExitOnRootFailure(hr, "Tried to open wrong database type - expected type %ls, found type %ls", wzExpectedSchemaType, sczSchemaType);
414 }
415 else if (dwVersionFound != dwExpectedVersion)
416 {
417 hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
418 ExitOnRootFailure(hr, "Tried to open wrong database schema version - expected version %u, found version %u", dwExpectedVersion, dwVersionFound);
419 }
420
421 *ppDatabase = pNewSceDatabase;
422 pNewSceDatabase = NULL;
423
424 LExit:
425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 ReleaseStr(sczSchemaType);
427 ReleaseStr(sczTempDbFile);
428 ReleaseDatabase(pNewSceDatabase);
429
430 return hr;
431 }
432
433 extern "C" HRESULT DAPI SceEnsureDatabase(
434 __in_z LPCWSTR sczFile,
435 __in_z_opt LPCWSTR wzSqlCeDllPath,
436 __in LPCWSTR wzSchemaType,
437 __in DWORD dwExpectedVersion,
438 __in SCE_DATABASE_SCHEMA *pdsSchema,
439 __out SCE_DATABASE **ppDatabase
440 )
441 {
442 HRESULT hr = S_OK;
443 SCE_DATABASE *pDatabase = NULL;
444
445 if (FileExistsEx(sczFile, NULL))
446 {
447 hr = SceOpenDatabase(sczFile, wzSqlCeDllPath, wzSchemaType, dwExpectedVersion, &pDatabase, FALSE);
448 ExitOnFailure(hr, "Failed to open database while ensuring database exists: %ls", sczFile);
449 }
450 else
451 {
452 hr = SceCreateDatabase(sczFile, wzSqlCeDllPath, &pDatabase);
453 ExitOnFailure(hr, "Failed to create database while ensuring database exists: %ls", sczFile);
454
455 hr = SetDatabaseSchemaInfo(pDatabase, wzSchemaType, dwExpectedVersion);
456 ExitOnFailure(hr, "Failed to set schema version of database");
457 }
458
459 hr = EnsureSchema(pDatabase, pdsSchema);
460 ExitOnFailure(hr, "Failed to ensure schema is correct in database: %ls", sczFile);
461
462 // Keep a pointer to the schema in the SCE_DATABASE object for future reference
463 pDatabase->pdsSchema = pdsSchema;
464
465 *ppDatabase = pDatabase;
466 pDatabase = NULL;
467
468 LExit:
469 ReleaseDatabase(pDatabase);
470
471 return hr;
472 }
473
474 extern "C" HRESULT DAPI SceIsTableEmpty(
475 __in SCE_DATABASE *pDatabase,
476 __in DWORD dwTableIndex,
477 __out BOOL *pfEmpty
478 )
479 {
480 HRESULT hr = S_OK;
481 SCE_ROW_HANDLE row = NULL;
482
483 hr = SceGetFirstRow(pDatabase, dwTableIndex, &row);
484 if (E_NOTFOUND == hr)
485 {
486 *pfEmpty = TRUE;
487 ExitFunction1(hr = S_OK);
488 }
489 ExitOnFailure(hr, "Failed to get first row while checking if table is empty");
490
491 *pfEmpty = FALSE;
492
493 LExit:
494 ReleaseSceRow(row);
495
496 return hr;
497 }
498
499 extern "C" HRESULT DAPI SceGetFirstRow(
500 __in SCE_DATABASE *pDatabase,
501 __in DWORD dwTableIndex,
502 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
503 )
504 {
505 HRESULT hr = S_OK;
506 DBCOUNTITEM cRowsObtained = 0;
507 HROW hRow = DB_NULL_HROW;
508 HROW *phRow = &hRow;
509 SCE_ROW *pRow = NULL;
510 SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
511
512 hr = pTable->pIRowset->RestartPosition(DB_NULL_HCHAPTER);
513 ExitOnFailure(hr, "Failed to reset IRowset position to beginning");
514
515 hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
516 if (DB_S_ENDOFROWSET == hr)
517 {
518 ExitFunction1(hr = E_NOTFOUND);
519 }
520 ExitOnFailure(hr, "Failed to get next first row");
521
522 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
523 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
524
525 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
526 pRow->hRow = hRow;
527 pRow->pTableSchema = pTable;
528 pRow->pIRowset = pTable->pIRowset;
529 pRow->pIRowset->AddRef();
530
531 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
532
533 LExit:
534 return hr;
535 }
536
537 HRESULT DAPI SceGetNextRow(
538 __in SCE_DATABASE *pDatabase,
539 __in DWORD dwTableIndex,
540 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
541 )
542 {
543 HRESULT hr = S_OK;
544 DBCOUNTITEM cRowsObtained = 0;
545 HROW hRow = DB_NULL_HROW;
546 HROW *phRow = &hRow;
547 SCE_ROW *pRow = NULL;
548 SCE_TABLE_SCHEMA *pTable = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
549
550 hr = pTable->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
551 if (DB_S_ENDOFROWSET == hr)
552 {
553 ExitFunction1(hr = E_NOTFOUND);
554 }
555 ExitOnFailure(hr, "Failed to get next first row");
556
557 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
558 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
559
560 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
561 pRow->hRow = hRow;
562 pRow->pTableSchema = pTable;
563 pRow->pIRowset = pTable->pIRowset;
564 pRow->pIRowset->AddRef();
565
566 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
567
568 LExit:
569 return hr;
570 }
571
572 extern "C" HRESULT DAPI SceBeginTransaction(
573 __in SCE_DATABASE *pDatabase
574 )
575 {
576 HRESULT hr = S_OK;
577 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
578
579 if (pDatabaseInternal->fTransactionBadState)
580 {
581 // We're in a hosed transaction state - we can't start a new one
582 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_RECOVERY_FAILURE));
583 }
584
585 ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
586
587 if (1 == pDatabaseInternal->dwTransactionRefcount)
588 {
589 hr = pDatabaseInternal->pITransactionLocal->StartTransaction(ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL);
590 ExitOnFailure(hr, "Failed to start transaction");
591 }
592
593 LExit:
594 if (FAILED(hr))
595 {
596 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
597 }
598
599 return hr;
600 }
601
602 extern "C" HRESULT DAPI SceCommitTransaction(
603 __in SCE_DATABASE *pDatabase
604 )
605 {
606 HRESULT hr = S_OK;
607 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
608 Assert(0 < pDatabaseInternal->dwTransactionRefcount);
609
610 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
611
612 if (0 == pDatabaseInternal->dwTransactionRefcount)
613 {
614 if (pDatabaseInternal->fRollbackTransaction)
615 {
616 hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
617 ExitOnFailure(hr, "Failed to abort transaction");
618 }
619 else
620 {
621 hr = pDatabaseInternal->pITransactionLocal->Commit(FALSE, XACTTC_SYNC, 0);
622 ExitOnFailure(hr, "Failed to commit transaction");
623 }
624
625 if (pDatabaseInternal->fPendingChanges)
626 {
627 pDatabaseInternal->fPendingChanges = FALSE;
628 pDatabaseInternal->fChanges = TRUE;
629 }
630
631 pDatabaseInternal->fRollbackTransaction = FALSE;
632 }
633
634 LExit:
635 // If we tried to commit and failed, the caller should subsequently call rollback
636 if (FAILED(hr))
637 {
638 ::InterlockedIncrement(&pDatabaseInternal->dwTransactionRefcount);
639 }
640
641 return hr;
642 }
643
644 extern "C" HRESULT DAPI SceRollbackTransaction(
645 __in SCE_DATABASE *pDatabase
646 )
647 {
648 HRESULT hr = S_OK;
649 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
650 Assert(0 < pDatabaseInternal->dwTransactionRefcount);
651
652 ::InterlockedDecrement(&pDatabaseInternal->dwTransactionRefcount);
653
654 if (0 == pDatabaseInternal->dwTransactionRefcount)
655 {
656 hr = pDatabaseInternal->pITransactionLocal->Abort(NULL, FALSE, FALSE);
657 ExitOnFailure(hr, "Failed to abort transaction");
658 pDatabaseInternal->fPendingChanges = FALSE;
659
660 pDatabaseInternal->fRollbackTransaction = FALSE;
661 }
662 else
663 {
664 pDatabaseInternal->fRollbackTransaction = TRUE;
665 }
666
667 LExit:
668 // We're just in a bad state now. Don't increment the transaction refcount (what is the user going to do - call us again?)
669 // but mark the database as bad so the user gets an error if they try to start a new transaction.
670 if (FAILED(hr))
671 {
672 TraceError(hr, "Failed to rollback transaction");
673 pDatabaseInternal->fTransactionBadState = TRUE;
674 }
675
676 return hr;
677 }
678
679 extern "C" HRESULT DAPI SceDeleteRow(
680 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
681 )
682 {
683 HRESULT hr = S_OK;
684 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(*pRowHandle);
685 IRowsetChange *pIRowsetChange = NULL;
686 DBROWSTATUS rowStatus = DBROWSTATUS_S_OK;
687
688 hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
689 ExitOnFailure(hr, "Failed to get IRowsetChange interface");
690
691 hr = pIRowsetChange->DeleteRows(DB_NULL_HCHAPTER, 1, &pRow->hRow, &rowStatus);
692 ExitOnFailure(hr, "Failed to delete row with status: %u", rowStatus);
693
694 ReleaseNullSceRow(*pRowHandle);
695
696 LExit:
697 ReleaseObject(pIRowsetChange);
698
699 return hr;
700 }
701
702 extern "C" HRESULT DAPI ScePrepareInsert(
703 __in SCE_DATABASE *pDatabase,
704 __in DWORD dwTableIndex,
705 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
706 )
707 {
708 HRESULT hr = S_OK;
709 SCE_ROW *pRow = NULL;
710
711 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
712 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
713
714 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
715 pRow->hRow = DB_NULL_HROW;
716 pRow->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
717 pRow->pIRowset = pRow->pTableSchema->pIRowset;
718 pRow->pIRowset->AddRef();
719 pRow->fInserting = TRUE;
720
721 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
722 pRow = NULL;
723
724 LExit:
725 ReleaseSceRow(pRow);
726
727 return hr;
728 }
729
730 extern "C" HRESULT DAPI SceFinishUpdate(
731 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
732 )
733 {
734 HRESULT hr = S_OK;
735 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
736 IAccessor *pIAccessor = NULL;
737 IRowsetChange *pIRowsetChange = NULL;
738 DBBINDSTATUS *rgBindStatus = NULL;
739 HACCESSOR hAccessor = DB_NULL_HACCESSOR;
740 HROW hRow = DB_NULL_HROW;
741
742 hr = pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
743 ExitOnFailure(hr, "Failed to get IAccessor interface");
744
745 // This can be used when stepping through the debugger to see bind failures
746 #ifdef DEBUG
747 if (0 < pRow->dwBindingIndex)
748 {
749 hr = MemEnsureArraySize(reinterpret_cast<void **>(&rgBindStatus), pRow->dwBindingIndex, sizeof(DBBINDSTATUS), 0);
750 ExitOnFailure(hr, "Failed to ensure binding status array size");
751 }
752 #endif
753
754 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pRow->dwBindingIndex, pRow->rgBinding, 0, &hAccessor, rgBindStatus);
755 ExitOnFailure(hr, "Failed to create accessor");
756
757 hr = pRow->pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pIRowsetChange));
758 ExitOnFailure(hr, "Failed to get IRowsetChange interface");
759
760 if (pRow->fInserting)
761 {
762 hr = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, hAccessor, pRow->pbData, &hRow);
763 ExitOnFailure(hr, "Failed to insert new row");
764
765 pRow->hRow = hRow;
766 ReleaseNullObject(pRow->pIRowset);
767 pRow->pIRowset = pRow->pTableSchema->pIRowset;
768 pRow->pIRowset->AddRef();
769 }
770 else
771 {
772 hr = pIRowsetChange->SetData(pRow->hRow, hAccessor, pRow->pbData);
773 ExitOnFailure(hr, "Failed to update existing row");
774 }
775
776 if (0 < pRow->pDatabaseInternal->dwTransactionRefcount)
777 {
778 pRow->pDatabaseInternal->fPendingChanges = TRUE;
779 }
780 else
781 {
782 pRow->pDatabaseInternal->fChanges = TRUE;
783 }
784
785 LExit:
786 if (DB_NULL_HACCESSOR != hAccessor)
787 {
788 pIAccessor->ReleaseAccessor(hAccessor, NULL);
789 }
790 ReleaseMem(rgBindStatus);
791 ReleaseObject(pIAccessor);
792 ReleaseObject(pIRowsetChange);
793
794 return hr;
795 }
796
797 extern "C" HRESULT DAPI SceSetColumnBinary(
798 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
799 __in DWORD dwColumnIndex,
800 __in_bcount(cbBuffer) const BYTE* pbBuffer,
801 __in SIZE_T cbBuffer
802 )
803 {
804 HRESULT hr = S_OK;
805 size_t cbAllocSize = 0;
806 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
807
808 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
809 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set binary, columns: %u", pRow->pTableSchema->cColumns);
810
811 if (NULL == pRow->rgBinding)
812 {
813 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
814 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
815 }
816
817 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, pbBuffer, cbBuffer, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
818 ExitOnFailure(hr, "Failed to set column value as binary");
819
820 LExit:
821 return hr;
822 }
823
824 extern "C" HRESULT DAPI SceSetColumnDword(
825 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
826 __in DWORD dwColumnIndex,
827 __in const DWORD dwValue
828 )
829 {
830 HRESULT hr = S_OK;
831 size_t cbAllocSize = 0;
832 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
833
834 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
835 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set dword, columns: %u", pRow->pTableSchema->cColumns);
836
837 if (NULL == pRow->rgBinding)
838 {
839 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
840 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
841 }
842
843 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dwValue), 4, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
844 ExitOnFailure(hr, "Failed to set column value as binary");
845
846 LExit:
847 return hr;
848 }
849
850 extern "C" HRESULT DAPI SceSetColumnQword(
851 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
852 __in DWORD dwColumnIndex,
853 __in const DWORD64 qwValue
854 )
855 {
856 HRESULT hr = S_OK;
857 size_t cbAllocSize = 0;
858 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
859
860 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
861 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set qword, columns: %u", pRow->pTableSchema->cColumns);
862
863 if (NULL == pRow->rgBinding)
864 {
865 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
866 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
867 }
868
869 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&qwValue), 8, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
870 ExitOnFailure(hr, "Failed to set column value as qword");
871
872 LExit:
873 return hr;
874 }
875
876 extern "C" HRESULT DAPI SceSetColumnBool(
877 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
878 __in DWORD dwColumnIndex,
879 __in const BOOL fValue
880 )
881 {
882 HRESULT hr = S_OK;
883 size_t cbAllocSize = 0;
884 short int sValue = fValue ? 0xFFFF : 0x0000;
885 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
886
887 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
888 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set bool, columns: %u", pRow->pTableSchema->cColumns);
889
890 if (NULL == pRow->rgBinding)
891 {
892 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
893 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
894 }
895
896 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&sValue), 2, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
897 ExitOnFailure(hr, "Failed to set column value as binary");
898
899 LExit:
900 return hr;
901 }
902
903 extern "C" HRESULT DAPI SceSetColumnString(
904 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
905 __in DWORD dwColumnIndex,
906 __in_z_opt LPCWSTR wzValue
907 )
908 {
909 HRESULT hr = S_OK;
910 size_t cbAllocSize = 0;
911 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
912 SIZE_T cbSize = (NULL == wzValue) ? 0 : ((lstrlenW(wzValue) + 1) * sizeof(WCHAR));
913
914 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
915 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set string, columns: %u", pRow->pTableSchema->cColumns);
916
917 if (NULL == pRow->rgBinding)
918 {
919 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
920 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
921 }
922
923 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(wzValue), cbSize, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
924 ExitOnFailure(hr, "Failed to set column value as string: %ls", wzValue);
925
926 LExit:
927 return hr;
928 }
929
930 extern "C" HRESULT DAPI SceSetColumnNull(
931 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
932 __in DWORD dwColumnIndex
933 )
934 {
935 HRESULT hr = S_OK;
936 size_t cbAllocSize = 0;
937 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
938
939 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
940 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set empty, columns: %u", pRow->pTableSchema->cColumns);
941
942 if (NULL == pRow->rgBinding)
943 {
944 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
945 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
946 }
947
948 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, NULL, 0, &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
949 ExitOnFailure(hr, "Failed to set column value as empty value");
950
951 LExit:
952 return hr;
953 }
954
955 extern "C" HRESULT DAPI SceSetColumnSystemTime(
956 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle,
957 __in DWORD dwColumnIndex,
958 __in const SYSTEMTIME *pst
959 )
960 {
961 HRESULT hr = S_OK;
962 size_t cbAllocSize = 0;
963 DBTIMESTAMP dbTimeStamp = { };
964
965 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
966
967 hr = ::SizeTMult(sizeof(DBBINDING), pRow->pTableSchema->cColumns, &cbAllocSize);
968 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to set systemtime, columns: %u", pRow->pTableSchema->cColumns);
969
970 if (NULL == pRow->rgBinding)
971 {
972 pRow->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
973 ExitOnNull(pRow->rgBinding, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for sce row writer");
974 }
975
976 dbTimeStamp.year = pst->wYear;
977 dbTimeStamp.month = pst->wMonth;
978 dbTimeStamp.day = pst->wDay;
979 dbTimeStamp.hour = pst->wHour;
980 dbTimeStamp.minute = pst->wMinute;
981 dbTimeStamp.second = pst->wSecond;
982 // Don't use .fraction because milliseconds are not reliable in SQL CE. They are rounded to the nearest 1/300th of a second,
983 // and it is not supported (or at least I can't figure out how) to query for an exact timestamp if milliseconds
984 // are involved (even when rounded the way SQL CE returns them).
985
986 hr = SetColumnValue(pRow->pTableSchema, dwColumnIndex, reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pRow->rgBinding[pRow->dwBindingIndex++], &pRow->cbOffset, &pRow->pbData);
987 ExitOnFailure(hr, "Failed to set column value as DBTIMESTAMP");
988
989 LExit:
990 return hr;
991 }
992
993 extern "C" HRESULT DAPI SceGetColumnBinary(
994 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
995 __in DWORD dwColumnIndex,
996 __out_opt BYTE **ppbBuffer,
997 __inout SIZE_T *pcbBuffer
998 )
999 {
1000 HRESULT hr = S_OK;
1001 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1002
1003 hr = GetColumnValue(pRow, dwColumnIndex, ppbBuffer, pcbBuffer);
1004 if (E_NOTFOUND == hr)
1005 {
1006 ExitFunction();
1007 }
1008 ExitOnFailure(hr, "Failed to get binary data out of column");
1009
1010 LExit:
1011 return hr;
1012 }
1013
1014 extern "C" HRESULT DAPI SceGetColumnDword(
1015 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1016 __in DWORD dwColumnIndex,
1017 __out DWORD *pdwValue
1018 )
1019 {
1020 HRESULT hr = S_OK;
1021 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1022
1023 hr = GetColumnValueFixed(pRow, dwColumnIndex, 4, reinterpret_cast<BYTE *>(pdwValue));
1024 if (E_NOTFOUND == hr)
1025 {
1026 ExitFunction();
1027 }
1028 ExitOnFailure(hr, "Failed to get dword data out of column");
1029
1030 LExit:
1031 return hr;
1032 }
1033
1034 extern "C" HRESULT DAPI SceGetColumnQword(
1035 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1036 __in DWORD dwColumnIndex,
1037 __in DWORD64 *pqwValue
1038 )
1039 {
1040 HRESULT hr = S_OK;
1041 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1042
1043 hr = GetColumnValueFixed(pRow, dwColumnIndex, 8, reinterpret_cast<BYTE *>(pqwValue));
1044 if (E_NOTFOUND == hr)
1045 {
1046 ExitFunction();
1047 }
1048 ExitOnFailure(hr, "Failed to get qword data out of column");
1049
1050 LExit:
1051 return hr;
1052 }
1053
1054 extern "C" HRESULT DAPI SceGetColumnBool(
1055 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1056 __in DWORD dwColumnIndex,
1057 __out BOOL *pfValue
1058 )
1059 {
1060 HRESULT hr = S_OK;
1061 short int sValue = 0;
1062 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1063
1064 hr = GetColumnValueFixed(pRow, dwColumnIndex, 2, reinterpret_cast<BYTE *>(&sValue));
1065 if (E_NOTFOUND == hr)
1066 {
1067 ExitFunction();
1068 }
1069 ExitOnFailure(hr, "Failed to get data out of column");
1070
1071 if (sValue == 0x0000)
1072 {
1073 *pfValue = FALSE;
1074 }
1075 else
1076 {
1077 *pfValue = TRUE;
1078 }
1079
1080 LExit:
1081 return hr;
1082 }
1083
1084 extern "C" HRESULT DAPI SceGetColumnString(
1085 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1086 __in DWORD dwColumnIndex,
1087 __out_z LPWSTR *psczValue
1088 )
1089 {
1090 HRESULT hr = S_OK;
1091 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1092 SIZE_T cbSize = 0;
1093
1094 hr = GetColumnValue(pRow, dwColumnIndex, reinterpret_cast<BYTE **>(psczValue), &cbSize);
1095 if (E_NOTFOUND == hr)
1096 {
1097 ExitFunction();
1098 }
1099 ExitOnFailure(hr, "Failed to get string data out of column");
1100
1101 LExit:
1102 return hr;
1103 }
1104
1105 extern "C" HRESULT DAPI SceGetColumnSystemTime(
1106 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowReadHandle,
1107 __in DWORD dwColumnIndex,
1108 __out SYSTEMTIME *pst
1109 )
1110 {
1111 HRESULT hr = S_OK;
1112 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowReadHandle);
1113 DBTIMESTAMP dbTimeStamp = { };
1114
1115 hr = GetColumnValueFixed(pRow, dwColumnIndex, sizeof(dbTimeStamp), reinterpret_cast<BYTE *>(&dbTimeStamp));
1116 if (E_NOTFOUND == hr)
1117 {
1118 ExitFunction();
1119 }
1120 ExitOnFailure(hr, "Failed to get string data out of column");
1121
1122 pst->wYear = dbTimeStamp.year;
1123 pst->wMonth = dbTimeStamp.month;
1124 pst->wDay = dbTimeStamp.day;
1125 pst->wHour = dbTimeStamp.hour;
1126 pst->wMinute = dbTimeStamp.minute;
1127 pst->wSecond = dbTimeStamp.second;
1128
1129 LExit:
1130 return hr;
1131 }
1132
1133 extern "C" void DAPI SceCloseTable(
1134 __in SCE_TABLE_SCHEMA *pTable
1135 )
1136 {
1137 ReleaseNullObject(pTable->pIRowsetChange);
1138 ReleaseNullObject(pTable->pIRowset);
1139 }
1140
1141 extern "C" BOOL DAPI SceDatabaseChanged(
1142 __in SCE_DATABASE *pDatabase
1143 )
1144 {
1145 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1146
1147 return pDatabaseInternal->fChanges;
1148 }
1149
1150 void DAPI SceResetDatabaseChanged(
1151 __in SCE_DATABASE *pDatabase
1152 )
1153 {
1154 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1155
1156 pDatabaseInternal->fChanges = FALSE;
1157 }
1158
1159 extern "C" HRESULT DAPI SceCloseDatabase(
1160 __in SCE_DATABASE *pDatabase
1161 )
1162 {
1163 HRESULT hr = S_OK;
1164
1165 ReleaseDatabase(pDatabase);
1166
1167 return hr;
1168 }
1169
1170 extern "C" HRESULT DAPI SceBeginQuery(
1171 __in SCE_DATABASE *pDatabase,
1172 __in DWORD dwTableIndex,
1173 __in DWORD dwIndex,
1174 __deref_out_bcount(SCE_QUERY_HANDLE_BYTES) SCE_QUERY_HANDLE *psqhHandle
1175 )
1176 {
1177 HRESULT hr = S_OK;
1178 size_t cbAllocSize = 0;
1179 SCE_QUERY *psq = static_cast<SCE_QUERY*>(MemAlloc(sizeof(SCE_QUERY), TRUE));
1180 ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate new sce query");
1181
1182 psq->pTableSchema = &(pDatabase->pdsSchema->rgTables[dwTableIndex]);
1183 psq->pIndexSchema = &(psq->pTableSchema->rgIndexes[dwIndex]);
1184 psq->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1185
1186 hr = ::SizeTMult(sizeof(DBBINDING), psq->pTableSchema->cColumns, &cbAllocSize);
1187 ExitOnFailure(hr, "Overflow while calculating allocation size for DBBINDING to begin query, columns: %u", psq->pTableSchema->cColumns);
1188
1189 psq->rgBinding = static_cast<DBBINDING *>(MemAlloc(cbAllocSize, TRUE));
1190 ExitOnNull(psq, hr, E_OUTOFMEMORY, "Failed to allocate DBBINDINGs for new sce query");
1191
1192 *psqhHandle = static_cast<SCE_QUERY_HANDLE>(psq);
1193 psq = NULL;
1194
1195 LExit:
1196 ReleaseSceQuery(psq);
1197
1198 return hr;
1199 }
1200
1201 HRESULT DAPI SceSetQueryColumnBinary(
1202 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1203 __in_bcount(cbBuffer) const BYTE* pbBuffer,
1204 __in SIZE_T cbBuffer
1205 )
1206 {
1207 HRESULT hr = S_OK;
1208 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1209
1210 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], pbBuffer, cbBuffer, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1211 ExitOnFailure(hr, "Failed to set query column value as binary of size: %u", cbBuffer);
1212
1213 ++(pQuery->dwBindingIndex);
1214
1215 LExit:
1216 return hr;
1217 }
1218
1219 HRESULT DAPI SceSetQueryColumnDword(
1220 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1221 __in const DWORD dwValue
1222 )
1223 {
1224 HRESULT hr = S_OK;
1225 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1226
1227 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dwValue), 4, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1228 ExitOnFailure(hr, "Failed to set query column value as dword");
1229
1230 ++(pQuery->dwBindingIndex);
1231
1232 LExit:
1233 return hr;
1234 }
1235
1236 HRESULT DAPI SceSetQueryColumnQword(
1237 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1238 __in const DWORD64 qwValue
1239 )
1240 {
1241 HRESULT hr = S_OK;
1242 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1243
1244 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&qwValue), 8, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1245 ExitOnFailure(hr, "Failed to set query column value as qword");
1246
1247 ++(pQuery->dwBindingIndex);
1248
1249 LExit:
1250 return hr;
1251 }
1252
1253 HRESULT DAPI SceSetQueryColumnBool(
1254 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1255 __in const BOOL fValue
1256 )
1257 {
1258 HRESULT hr = S_OK;
1259 short int sValue = fValue ? 1 : 0;
1260 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1261
1262 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&sValue), 2, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1263 ExitOnFailure(hr, "Failed to set query column value as boolean");
1264
1265 ++(pQuery->dwBindingIndex);
1266
1267 LExit:
1268 return hr;
1269 }
1270
1271 HRESULT DAPI SceSetQueryColumnString(
1272 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1273 __in_z_opt LPCWSTR wzString
1274 )
1275 {
1276 HRESULT hr = S_OK;
1277 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1278 SIZE_T cbSize = (NULL == wzString) ? 0 : ((lstrlenW(wzString) + 1) * sizeof(WCHAR));
1279
1280 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(wzString), cbSize, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1281 ExitOnFailure(hr, "Failed to set query column value as string");
1282
1283 ++(pQuery->dwBindingIndex);
1284
1285 LExit:
1286 return hr;
1287 }
1288
1289 HRESULT DAPI SceSetQueryColumnSystemTime(
1290 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle,
1291 __in const SYSTEMTIME *pst
1292 )
1293 {
1294 HRESULT hr = S_OK;
1295 DBTIMESTAMP dbTimeStamp = { };
1296 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1297
1298 dbTimeStamp.year = pst->wYear;
1299 dbTimeStamp.month = pst->wMonth;
1300 dbTimeStamp.day = pst->wDay;
1301 dbTimeStamp.hour = pst->wHour;
1302 dbTimeStamp.minute = pst->wMinute;
1303 dbTimeStamp.second = pst->wSecond;
1304
1305 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], reinterpret_cast<const BYTE *>(&dbTimeStamp), sizeof(dbTimeStamp), &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1306 ExitOnFailure(hr, "Failed to set query column value as DBTIMESTAMP");
1307
1308 ++(pQuery->dwBindingIndex);
1309
1310 LExit:
1311 return hr;
1312 }
1313
1314 HRESULT DAPI SceSetQueryColumnEmpty(
1315 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1316 )
1317 {
1318 HRESULT hr = S_OK;
1319 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1320
1321 hr = SetColumnValue(pQuery->pTableSchema, pQuery->pIndexSchema->rgColumns[pQuery->dwBindingIndex], NULL, 0, &pQuery->rgBinding[pQuery->dwBindingIndex], &pQuery->cbOffset, &pQuery->pbData);
1322 ExitOnFailure(hr, "Failed to set query column value as empty value");
1323
1324 ++(pQuery->dwBindingIndex);
1325
1326 LExit:
1327 return hr;
1328 }
1329
1330 HRESULT DAPI SceRunQueryExact(
1331 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1332 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1333 )
1334 {
1335 HRESULT hr = S_OK;
1336 SCE_QUERY_RESULTS *pQueryResults = NULL;
1337
1338 hr = RunQuery(FALSE, *psqhHandle, &pQueryResults);
1339 if (E_NOTFOUND == hr)
1340 {
1341 ExitFunction();
1342 }
1343 ExitOnFailure(hr, "Failed to run query exact");
1344
1345 hr = SceGetNextResultRow(reinterpret_cast<SCE_QUERY_RESULTS_HANDLE>(pQueryResults), pRowHandle);
1346 if (E_NOTFOUND == hr)
1347 {
1348 ExitFunction();
1349 }
1350 ExitOnFailure(hr, "Failed to get next row out of results");
1351
1352 LExit:
1353 ReleaseNullSceQuery(*psqhHandle);
1354 ReleaseSceQueryResults(pQueryResults);
1355
1356 return hr;
1357 }
1358
1359 extern "C" HRESULT DAPI SceRunQueryRange(
1360 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE *psqhHandle,
1361 __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE *psqrhHandle
1362 )
1363 {
1364 HRESULT hr = S_OK;
1365 SCE_QUERY_RESULTS **ppQueryResults = reinterpret_cast<SCE_QUERY_RESULTS **>(psqrhHandle);
1366
1367 hr = RunQuery(TRUE, *psqhHandle, ppQueryResults);
1368 if (E_NOTFOUND == hr)
1369 {
1370 ExitFunction();
1371 }
1372 ExitOnFailure(hr, "Failed to run query for range");
1373
1374 LExit:
1375 ReleaseNullSceQuery(*psqhHandle);
1376
1377 return hr;
1378 }
1379
1380 extern "C" HRESULT DAPI SceGetNextResultRow(
1381 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle,
1382 __out_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE *pRowHandle
1383 )
1384 {
1385 HRESULT hr = S_OK;
1386 HROW hRow = DB_NULL_HROW;
1387 HROW *phRow = &hRow;
1388 DBCOUNTITEM cRowsObtained = 0;
1389 SCE_ROW *pRow = NULL;
1390 SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1391
1392 Assert(pRowHandle && (*pRowHandle == NULL));
1393
1394 hr = pQueryResults->pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &phRow);
1395 if (DB_S_ENDOFROWSET == hr)
1396 {
1397 ExitFunction1(hr = E_NOTFOUND);
1398 }
1399 ExitOnFailure(hr, "Failed to get next first row");
1400
1401 pRow = reinterpret_cast<SCE_ROW *>(MemAlloc(sizeof(SCE_ROW), TRUE));
1402 ExitOnNull(pRow, hr, E_OUTOFMEMORY, "Failed to allocate SCE_ROW struct");
1403
1404 pRow->pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pQueryResults->pDatabaseInternal);
1405 pRow->hRow = hRow;
1406 pRow->pTableSchema = pQueryResults->pTableSchema;
1407 pRow->pIRowset = pQueryResults->pIRowset;
1408 pRow->pIRowset->AddRef();
1409
1410 *pRowHandle = reinterpret_cast<SCE_ROW_HANDLE>(pRow);
1411 pRow = NULL;
1412 hRow = DB_NULL_HROW;
1413
1414 LExit:
1415 if (DB_NULL_HROW != hRow)
1416 {
1417 pQueryResults->pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
1418 }
1419 ReleaseSceRow(pRow);
1420
1421 return hr;
1422 }
1423
1424 extern "C" void DAPI SceFreeRow(
1425 __in_bcount(SCE_ROW_HANDLE_BYTES) SCE_ROW_HANDLE rowHandle
1426 )
1427 {
1428 SCE_ROW *pRow = reinterpret_cast<SCE_ROW *>(rowHandle);
1429
1430 if (DB_NULL_HROW != pRow->hRow)
1431 {
1432 pRow->pIRowset->ReleaseRows(1, &pRow->hRow, NULL, NULL, NULL);
1433 }
1434 ReleaseObject(pRow->pIRowset);
1435 ReleaseMem(pRow->rgBinding);
1436 ReleaseMem(pRow->pbData);
1437 ReleaseMem(pRow);
1438 }
1439
1440 void DAPI SceFreeQuery(
1441 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_HANDLE sqhHandle
1442 )
1443 {
1444 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(sqhHandle);
1445
1446 ReleaseMem(pQuery->rgBinding);
1447 ReleaseMem(pQuery->pbData);
1448 ReleaseMem(pQuery);
1449 }
1450
1451 void DAPI SceFreeQueryResults(
1452 __in_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS_HANDLE sqrhHandle
1453 )
1454 {
1455 SCE_QUERY_RESULTS *pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(sqrhHandle);
1456
1457 ReleaseObject(pQueryResults->pIRowset);
1458 ReleaseMem(pQueryResults);
1459 }
1460
1461 // internal function definitions
1462 static HRESULT CreateSqlCe(
1463 __in_z_opt LPCWSTR wzSqlCeDllPath,
1464 __out IDBInitialize **ppIDBInitialize,
1465 __out_opt HMODULE *phSqlCeDll
1466 )
1467 {
1468 HRESULT hr = S_OK;
1469 LPWSTR sczPath = NULL;
1470 LPWSTR sczDirectory = NULL;
1471 LPWSTR sczDllFullPath = NULL;
1472
1473 if (NULL == wzSqlCeDllPath)
1474 {
1475 hr = CoCreateInstance(CLSID_SQLSERVERCE, 0, CLSCTX_INPROC_SERVER, IID_IDBInitialize, reinterpret_cast<void **>(ppIDBInitialize));
1476 ExitOnFailure(hr, "Failed to get IDBInitialize interface");
1477 }
1478 else
1479 {
1480 // First try loading DLL from the path of our EXE
1481 hr = PathForCurrentProcess(&sczPath, NULL);
1482 ExitOnFailure(hr, "Failed to get path for current process");
1483
1484 hr = PathGetDirectory(sczPath, &sczDirectory);
1485 ExitOnFailure(hr, "Failed to get directory of current process");
1486
1487 hr = PathConcat(sczDirectory, wzSqlCeDllPath, &sczDllFullPath);
1488 ExitOnFailure(hr, "Failed to concatenate current directory and DLL filename");
1489
1490 *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1491
1492 // If that failed, fallback to loading from current path
1493 if (NULL == *phSqlCeDll)
1494 {
1495 hr = PathGetFullPathName(wzSqlCeDllPath, &sczDllFullPath, NULL, NULL);
1496 ExitOnFailure(hr, "Failed to get full path for DLL");
1497
1498 *phSqlCeDll = ::LoadLibraryW(sczDllFullPath);
1499 ExitOnNullWithLastError(*phSqlCeDll, hr, "Failed to open Sql CE DLL: %ls", sczDllFullPath);
1500 }
1501
1502 HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
1503 pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(*phSqlCeDll, "DllGetClassObject");
1504
1505 IClassFactory* pFactory = NULL;
1506 hr = pfnGetFactory(CLSID_SQLSERVERCE, IID_IClassFactory, (void**)&pFactory);
1507 ExitOnFailure(hr, "Failed to get factory for IID_IDBInitialize from DLL: %ls", sczDllFullPath);
1508 ExitOnNull(pFactory, hr, E_UNEXPECTED, "GetFactory returned success, but pFactory was NULL");
1509
1510 hr = pFactory->CreateInstance(NULL, IID_IDBInitialize, (void**)ppIDBInitialize);
1511 pFactory->Release();
1512 }
1513
1514 LExit:
1515 ReleaseStr(sczPath);
1516 ReleaseStr(sczDirectory);
1517 ReleaseStr(sczDllFullPath);
1518
1519 return hr;
1520 }
1521
1522 static HRESULT RunQuery(
1523 __in BOOL fRange,
1524 __in_bcount(SCE_QUERY_BYTES) SCE_QUERY_HANDLE psqhHandle,
1525 __deref_out_bcount(SCE_QUERY_RESULTS_BYTES) SCE_QUERY_RESULTS **ppQueryResults
1526 )
1527 {
1528 HRESULT hr = S_OK;
1529 DBID tableID = { };
1530 DBID indexID = { };
1531 IAccessor *pIAccessor = NULL;
1532 IRowsetIndex *pIRowsetIndex = NULL;
1533 IRowset *pIRowset = NULL;
1534 HACCESSOR hAccessor = DB_NULL_HACCESSOR;
1535 SCE_QUERY *pQuery = reinterpret_cast<SCE_QUERY *>(psqhHandle);
1536 SCE_QUERY_RESULTS *pQueryResults = NULL;
1537 DBPROPSET rgdbpIndexPropSet[1];
1538 DBPROP rgdbpIndexProp[1];
1539
1540 rgdbpIndexPropSet[0].cProperties = 1;
1541 rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1542 rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1543
1544 rgdbpIndexProp[0].dwPropertyID = DBPROP_IRowsetIndex;
1545 rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1546 rgdbpIndexProp[0].colid = DB_NULLID;
1547 rgdbpIndexProp[0].vValue.vt = VT_BOOL;
1548 rgdbpIndexProp[0].vValue.boolVal = VARIANT_TRUE;
1549
1550 tableID.eKind = DBKIND_NAME;
1551 tableID.uName.pwszName = const_cast<WCHAR *>(pQuery->pTableSchema->wzName);
1552
1553 indexID.eKind = DBKIND_NAME;
1554 indexID.uName.pwszName = const_cast<WCHAR *>(pQuery->pIndexSchema->wzName);
1555
1556 hr = pQuery->pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, _countof(rgdbpIndexPropSet), rgdbpIndexPropSet, (IUnknown**) &pIRowsetIndex);
1557 ExitOnFailure(hr, "Failed to open IRowsetIndex");
1558
1559 hr = pIRowsetIndex->QueryInterface(IID_IRowset, reinterpret_cast<void **>(&pIRowset));
1560 ExitOnFailure(hr, "Failed to get IRowset interface from IRowsetIndex");
1561
1562 hr = pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
1563 ExitOnFailure(hr, "Failed to get IAccessor interface");
1564
1565 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pQuery->dwBindingIndex, pQuery->rgBinding, 0, &hAccessor, NULL);
1566 ExitOnFailure(hr, "Failed to create accessor");
1567
1568 if (!fRange)
1569 {
1570 hr = pIRowsetIndex->Seek(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, DBSEEK_FIRSTEQ);
1571 if (DB_E_NOTFOUND == hr)
1572 {
1573 ExitFunction1(hr = E_NOTFOUND);
1574 }
1575 ExitOnFailure(hr, "Failed to seek to record");
1576 }
1577 else
1578 {
1579 // If ALL columns in the index were specified, do a full key match
1580 if (pQuery->dwBindingIndex == pQuery->pIndexSchema->cColumns)
1581 {
1582 hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, 0, NULL, DBRANGE_MATCH);
1583 }
1584 else
1585 {
1586 // Otherwise, just match the specified keys.
1587 // We really want to use DBRANGE_MATCH_N_SHIFT here, but SQL CE doesn't appear to support it
1588 // So instead, we set the start and end to the same partial key, and then allow inclusive matching
1589 // This appears to accomplish the same thing
1590 hr = pIRowsetIndex->SetRange(hAccessor, pQuery->dwBindingIndex, pQuery->pbData, pQuery->dwBindingIndex, pQuery->pbData, 0);
1591 }
1592 if (DB_E_NOTFOUND == hr || E_NOTFOUND == hr)
1593 {
1594 ExitFunction1(hr = E_NOTFOUND);
1595 }
1596 ExitOnFailure(hr, "Failed to set range to find records");
1597 }
1598
1599 pQueryResults = reinterpret_cast<SCE_QUERY_RESULTS *>(MemAlloc(sizeof(SCE_QUERY_RESULTS), TRUE));
1600 ExitOnNull(pQueryResults, hr, E_OUTOFMEMORY, "Failed to allocate query results struct");
1601
1602 pQueryResults->pDatabaseInternal = pQuery->pDatabaseInternal;
1603 pQueryResults->pTableSchema = pQuery->pTableSchema;
1604 pQueryResults->pIRowset = pIRowset;
1605 pIRowset = NULL;
1606
1607 *ppQueryResults = pQueryResults;
1608 pQueryResults = NULL;
1609
1610 LExit:
1611 if (DB_NULL_HACCESSOR != hAccessor)
1612 {
1613 pIAccessor->ReleaseAccessor(hAccessor, NULL);
1614 }
1615 ReleaseObject(pIAccessor);
1616 ReleaseObject(pIRowset);
1617 ReleaseObject(pIRowsetIndex);
1618 ReleaseMem(pQueryResults);
1619 ReleaseSceQueryResults(pQueryResults);
1620
1621 return hr;
1622 }
1623
1624 static HRESULT FillOutColumnDescFromSchema(
1625 __in const SCE_COLUMN_SCHEMA *pColumnSchema,
1626 __out DBCOLUMNDESC *pColumnDesc
1627 )
1628 {
1629 HRESULT hr = S_OK;
1630 DWORD dwColumnProperties = 0;
1631 DWORD dwColumnPropertyIndex = 0;
1632 BOOL fFixedSize = FALSE;
1633
1634 pColumnDesc->dbcid.eKind = DBKIND_NAME;
1635 pColumnDesc->dbcid.uName.pwszName = (WCHAR *)pColumnSchema->wzName;
1636 pColumnDesc->wType = pColumnSchema->dbtColumnType;
1637 pColumnDesc->ulColumnSize = pColumnSchema->dwLength;
1638 if (0 == pColumnDesc->ulColumnSize && (DBTYPE_WSTR == pColumnDesc->wType || DBTYPE_BYTES == pColumnDesc->wType))
1639 {
1640 fFixedSize = FALSE;
1641 }
1642 else
1643 {
1644 fFixedSize = TRUE;
1645 }
1646
1647 dwColumnProperties = 1;
1648 if (pColumnSchema->fAutoIncrement)
1649 {
1650 ++dwColumnProperties;
1651 }
1652 if (!pColumnSchema->fNullable)
1653 {
1654 ++dwColumnProperties;
1655 }
1656
1657 if (0 < dwColumnProperties)
1658 {
1659 pColumnDesc->cPropertySets = 1;
1660 pColumnDesc->rgPropertySets = reinterpret_cast<DBPROPSET *>(MemAlloc(sizeof(DBPROPSET), TRUE));
1661 ExitOnNull(pColumnDesc->rgPropertySets, hr, E_OUTOFMEMORY, "Failed to allocate propset object while setting up column parameters");
1662
1663 pColumnDesc->rgPropertySets[0].cProperties = dwColumnProperties;
1664 pColumnDesc->rgPropertySets[0].guidPropertySet = DBPROPSET_COLUMN;
1665 pColumnDesc->rgPropertySets[0].rgProperties = reinterpret_cast<DBPROP *>(MemAlloc(sizeof(DBPROP) * dwColumnProperties, TRUE));
1666
1667 dwColumnPropertyIndex = 0;
1668 if (pColumnSchema->fAutoIncrement)
1669 {
1670 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_AUTOINCREMENT;
1671 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1672 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1673 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1674 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_TRUE;
1675 ++dwColumnPropertyIndex;
1676 }
1677 if (!pColumnSchema->fNullable)
1678 {
1679 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_NULLABLE;
1680 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1681 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1682 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1683 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = VARIANT_FALSE;
1684 ++dwColumnPropertyIndex;
1685 }
1686
1687 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwPropertyID = DBPROP_COL_FIXEDLENGTH;
1688 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].dwOptions = DBPROPOPTIONS_REQUIRED;
1689 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].colid = DB_NULLID;
1690 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.vt = VT_BOOL;
1691 pColumnDesc->rgPropertySets[0].rgProperties[dwColumnPropertyIndex].vValue.boolVal = fFixedSize ? VARIANT_TRUE : VARIANT_FALSE;
1692 ++dwColumnPropertyIndex;
1693 }
1694
1695 LExit:
1696 return hr;
1697 }
1698
1699 static HRESULT EnsureSchema(
1700 __in SCE_DATABASE *pDatabase,
1701 __in SCE_DATABASE_SCHEMA *pdsSchema
1702 )
1703 {
1704 HRESULT hr = S_OK;
1705 size_t cbAllocSize = 0;
1706 BOOL fInTransaction = FALSE;
1707 BOOL fSchemaNeedsSetup = TRUE;
1708 DBID tableID = { };
1709 DBID indexID = { };
1710 DBPROPSET rgdbpIndexPropSet[1];
1711 DBPROPSET rgdbpRowSetPropSet[1];
1712 DBPROP rgdbpIndexProp[1];
1713 DBPROP rgdbpRowSetProp[1];
1714 DBCOLUMNDESC *rgColumnDescriptions = NULL;
1715 DBINDEXCOLUMNDESC *rgIndexColumnDescriptions = NULL;
1716 DWORD cIndexColumnDescriptions = 0;
1717 DWORD dwTableColumnIndex = 0;
1718 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1719 ITableDefinition *pTableDefinition = NULL;
1720 IIndexDefinition *pIndexDefinition = NULL;
1721 IRowsetIndex *pIRowsetIndex = NULL;
1722
1723 rgdbpRowSetPropSet[0].cProperties = 1;
1724 rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1725 rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1726
1727 rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1728 rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1729 rgdbpRowSetProp[0].colid = DB_NULLID;
1730 rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1731 rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1732
1733 rgdbpIndexPropSet[0].cProperties = 1;
1734 rgdbpIndexPropSet[0].guidPropertySet = DBPROPSET_INDEX;
1735 rgdbpIndexPropSet[0].rgProperties = rgdbpIndexProp;
1736
1737 rgdbpIndexProp[0].dwPropertyID = DBPROP_INDEX_NULLS;
1738 rgdbpIndexProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1739 rgdbpIndexProp[0].colid = DB_NULLID;
1740 rgdbpIndexProp[0].vValue.vt = VT_I4;
1741 rgdbpIndexProp[0].vValue.lVal = DBPROPVAL_IN_DISALLOWNULL;
1742
1743 hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_ITableDefinition, reinterpret_cast<void **>(&pTableDefinition));
1744 ExitOnFailure(hr, "Failed to get ITableDefinition for table creation");
1745
1746 hr = pDatabaseInternal->pISessionProperties->QueryInterface(IID_IIndexDefinition, reinterpret_cast<void **>(&pIndexDefinition));
1747 ExitOnFailure(hr, "Failed to get IIndexDefinition for index creation");
1748
1749 hr = SceBeginTransaction(pDatabase);
1750 ExitOnFailure(hr, "Failed to start transaction for ensuring schema");
1751 fInTransaction = TRUE;
1752
1753 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1754 {
1755 tableID.eKind = DBKIND_NAME;
1756 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1757
1758 // Fill out each column description struct as appropriate, to be used for creating the table, or confirming the table's columns all exist
1759 rgColumnDescriptions = static_cast<DBCOLUMNDESC *>(MemAlloc(sizeof(DBCOLUMNDESC) * pdsSchema->rgTables[dwTable].cColumns, TRUE));
1760 ExitOnNull(rgColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate column description array while creating table");
1761
1762 for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1763 {
1764 hr = FillOutColumnDescFromSchema(pdsSchema->rgTables[dwTable].rgColumns + i, rgColumnDescriptions + i);
1765 ExitOnFailure(hr, "Failed to fill out column description from schema");
1766 }
1767
1768 // First try to open the table - or if it doesn't exist, create it
1769 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1770 if (DB_E_NOTABLE == hr)
1771 {
1772 // The table doesn't exist, so let's create it
1773 hr = pTableDefinition->CreateTable(NULL, &tableID, pdsSchema->rgTables[dwTable].cColumns, rgColumnDescriptions, IID_IUnknown, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, NULL, NULL);
1774 ExitOnFailure(hr, "Failed to create table: %ls", pdsSchema->rgTables[dwTable].wzName);
1775 }
1776 else
1777 {
1778 ExitOnFailure(hr, "Failed to open table %ls while ensuring schema", tableID.uName.pwszName);
1779
1780 // Close any rowset we opened
1781 ReleaseNullObject(pdsSchema->rgTables[dwTable].pIRowset);
1782
1783 // If it does exist, make sure all columns are in the table
1784 // Only nullable columns can be added to an existing table
1785 for (DWORD i = 1; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1786 {
1787 if (pdsSchema->rgTables[dwTable].rgColumns[i].fNullable)
1788 hr = pTableDefinition->AddColumn(&tableID, rgColumnDescriptions + i, NULL);
1789 if (DB_E_DUPLICATECOLUMNID == hr)
1790 {
1791 hr = S_OK;
1792 }
1793 ExitOnFailure(hr, "Failed to add column %ls", pdsSchema->rgTables[dwTable].rgColumns[i].wzName);
1794 }
1795 }
1796
1797 #pragma prefast(push)
1798 #pragma prefast(disable:26010)
1799 hr = EnsureLocalColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable);
1800 #pragma prefast(pop)
1801 ExitOnFailure(hr, "Failed to ensure local column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1802
1803 for (DWORD i = 0; i < pdsSchema->rgTables[dwTable].cColumns; ++i)
1804 {
1805 if (NULL != rgColumnDescriptions[i].rgPropertySets)
1806 {
1807 ReleaseMem(rgColumnDescriptions[i].rgPropertySets[0].rgProperties);
1808 ReleaseMem(rgColumnDescriptions[i].rgPropertySets);
1809 }
1810 }
1811
1812 ReleaseNullMem(rgColumnDescriptions);
1813 if (0 < pdsSchema->rgTables[dwTable].cIndexes)
1814 {
1815 // Now create indexes for the table
1816 for (DWORD dwIndex = 0; dwIndex < pdsSchema->rgTables[dwTable].cIndexes; ++dwIndex)
1817 {
1818 indexID.eKind = DBKIND_NAME;
1819 indexID.uName.pwszName = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName;
1820
1821 // Check if the index exists
1822 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, &indexID, IID_IRowsetIndex, 0, NULL, (IUnknown**) &pIRowsetIndex);
1823 if (SUCCEEDED(hr))
1824 {
1825 // TODO: If one with the same name exists, check if the schema actually matches
1826 ReleaseNullObject(pIRowsetIndex);
1827 continue;
1828 }
1829 hr = S_OK;
1830
1831 hr = ::SizeTMult(sizeof(DBINDEXCOLUMNDESC), pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns, &cbAllocSize);
1832 ExitOnFailure(hr, "Overflow while calculating allocation size for DBINDEXCOLUMNDESC, columns: %u", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns);
1833
1834 rgIndexColumnDescriptions = reinterpret_cast<DBINDEXCOLUMNDESC *>(MemAlloc(cbAllocSize, TRUE));
1835 ExitOnNull(rgIndexColumnDescriptions, hr, E_OUTOFMEMORY, "Failed to allocate structure to hold index column descriptions");
1836 cIndexColumnDescriptions = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns;
1837
1838 for (DWORD dwColumnIndex = 0; dwColumnIndex < cIndexColumnDescriptions; ++dwColumnIndex)
1839 {
1840 dwTableColumnIndex = pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].rgColumns[dwColumnIndex];
1841
1842 rgIndexColumnDescriptions[dwColumnIndex].pColumnID = reinterpret_cast<DBID *>(MemAlloc(sizeof(DBID), TRUE));
1843 rgIndexColumnDescriptions[dwColumnIndex].pColumnID->eKind = DBKIND_NAME;
1844 rgIndexColumnDescriptions[dwColumnIndex].pColumnID->uName.pwszName = const_cast<LPOLESTR>(pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].wzName);
1845 rgIndexColumnDescriptions[dwColumnIndex].eIndexColOrder = pdsSchema->rgTables[dwTable].rgColumns[dwTableColumnIndex].fDescending ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC;
1846 }
1847
1848 hr = pIndexDefinition->CreateIndex(&tableID, &indexID, static_cast<DBORDINAL>(pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].cColumns), rgIndexColumnDescriptions, 1, rgdbpIndexPropSet, NULL);
1849 if (DB_E_DUPLICATEINDEXID == hr)
1850 {
1851 // If the index already exists, no worries
1852 hr = S_OK;
1853 }
1854 ExitOnFailure(hr, "Failed to create index named %ls into table named %ls", pdsSchema->rgTables[dwTable].rgIndexes[dwIndex].wzName, pdsSchema->rgTables[dwTable].wzName);
1855
1856 for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1857 {
1858 ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1859 }
1860
1861 cIndexColumnDescriptions = 0;
1862 ReleaseNullMem(rgIndexColumnDescriptions);
1863 }
1864 }
1865 }
1866
1867 // Now once all tables have been created, create foreign key relationships
1868 if (fSchemaNeedsSetup)
1869 {
1870 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1871 {
1872 tableID.eKind = DBKIND_NAME;
1873 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1874
1875 // Setup any constraints for the table's columns
1876 hr = EnsureForeignColumnConstraints(pTableDefinition, &tableID, pdsSchema->rgTables + dwTable, pdsSchema);
1877 ExitOnFailure(hr, "Failed to ensure foreign column constraints for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1878 }
1879 }
1880
1881 hr = SceCommitTransaction(pDatabase);
1882 ExitOnFailure(hr, "Failed to commit transaction for ensuring schema");
1883 fInTransaction = FALSE;
1884
1885 hr = OpenSchema(pDatabase, pdsSchema);
1886 ExitOnFailure(hr, "Failed to open schema");
1887
1888 LExit:
1889 ReleaseObject(pTableDefinition);
1890 ReleaseObject(pIndexDefinition);
1891 ReleaseObject(pIRowsetIndex);
1892
1893 if (fInTransaction)
1894 {
1895 SceRollbackTransaction(pDatabase);
1896 }
1897
1898 for (DWORD i = 0; i < cIndexColumnDescriptions; ++i)
1899 {
1900 ReleaseMem(rgIndexColumnDescriptions[i].pColumnID);
1901 }
1902
1903 ReleaseMem(rgIndexColumnDescriptions);
1904 ReleaseMem(rgColumnDescriptions);
1905
1906 return hr;
1907 }
1908
1909 static HRESULT OpenSchema(
1910 __in SCE_DATABASE *pDatabase,
1911 __in SCE_DATABASE_SCHEMA *pdsSchema
1912 )
1913 {
1914 HRESULT hr = S_OK;
1915 SCE_DATABASE_INTERNAL *pDatabaseInternal = reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle);
1916 DBID tableID = { };
1917 DBPROPSET rgdbpRowSetPropSet[1];
1918 DBPROP rgdbpRowSetProp[1];
1919
1920 rgdbpRowSetPropSet[0].cProperties = 1;
1921 rgdbpRowSetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;
1922 rgdbpRowSetPropSet[0].rgProperties = rgdbpRowSetProp;
1923
1924 rgdbpRowSetProp[0].dwPropertyID = DBPROP_IRowsetChange;
1925 rgdbpRowSetProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
1926 rgdbpRowSetProp[0].colid = DB_NULLID;
1927 rgdbpRowSetProp[0].vValue.vt = VT_BOOL;
1928 rgdbpRowSetProp[0].vValue.boolVal = VARIANT_TRUE;
1929
1930 // Finally, open all tables
1931 for (DWORD dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
1932 {
1933 tableID.eKind = DBKIND_NAME;
1934 tableID.uName.pwszName = const_cast<WCHAR *>(pdsSchema->rgTables[dwTable].wzName);
1935
1936 // And finally, open the table's standard interfaces
1937 hr = pDatabaseInternal->pIOpenRowset->OpenRowset(NULL, &tableID, NULL, IID_IRowset, _countof(rgdbpRowSetPropSet), rgdbpRowSetPropSet, reinterpret_cast<IUnknown **>(&pdsSchema->rgTables[dwTable].pIRowset));
1938 ExitOnFailure(hr, "Failed to open table %u named %ls after ensuring all indexes and constraints are created", dwTable, pdsSchema->rgTables[dwTable].wzName);
1939
1940 hr = pdsSchema->rgTables[dwTable].pIRowset->QueryInterface(IID_IRowsetChange, reinterpret_cast<void **>(&pdsSchema->rgTables[dwTable].pIRowsetChange));
1941 ExitOnFailure(hr, "Failed to get IRowsetChange object for table: %ls", pdsSchema->rgTables[dwTable].wzName);
1942 }
1943
1944 LExit:
1945 return hr;
1946 }
1947
1948 static HRESULT SetColumnValue(
1949 __in const SCE_TABLE_SCHEMA *pTableSchema,
1950 __in DWORD dwColumnIndex,
1951 __in_bcount_opt(cbSize) const BYTE *pbData,
1952 __in SIZE_T cbSize,
1953 __inout DBBINDING *pBinding,
1954 __inout SIZE_T *pcbOffset,
1955 __inout BYTE **ppbBuffer
1956 )
1957 {
1958 HRESULT hr = S_OK;
1959 size_t cbNewOffset = *pcbOffset;
1960
1961 pBinding->iOrdinal = dwColumnIndex + 1; // Skip bookmark column
1962 pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
1963 pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
1964
1965 pBinding->obLength = cbNewOffset;
1966
1967 hr = ::SizeTAdd(cbNewOffset, sizeof(DBBYTEOFFSET), &cbNewOffset);
1968 ExitOnFailure(hr, "Failed to add sizeof(DBBYTEOFFSET) to alloc size while setting column value");
1969
1970 pBinding->obValue = cbNewOffset;
1971
1972 hr = ::SizeTAdd(cbNewOffset, cbSize, &cbNewOffset);
1973 ExitOnFailure(hr, "Failed to add %u to alloc size while setting column value", cbSize);
1974
1975 pBinding->obStatus = cbNewOffset;
1976 pBinding->eParamIO = DBPARAMIO_INPUT;
1977
1978 hr = ::SizeTAdd(cbNewOffset, sizeof(DBSTATUS), &cbNewOffset);
1979 ExitOnFailure(hr, "Failed to add sizeof(DBSTATUS) to alloc size while setting column value");
1980
1981 pBinding->wType = pTableSchema->rgColumns[dwColumnIndex].dbtColumnType;
1982 pBinding->cbMaxLen = static_cast<DBBYTEOFFSET>(cbSize);
1983
1984 if (NULL == *ppbBuffer)
1985 {
1986 *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbNewOffset, TRUE));
1987 ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer while setting row string");
1988 }
1989 else
1990 {
1991 *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, cbNewOffset, TRUE));
1992 ExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate buffer while setting row string");
1993 }
1994
1995 *(reinterpret_cast<DBBYTEOFFSET *>(*ppbBuffer + *pcbOffset)) = static_cast<DBBYTEOFFSET>(cbSize);
1996 *pcbOffset += sizeof(DBBYTEOFFSET);
1997 memcpy(*ppbBuffer + *pcbOffset, pbData, cbSize);
1998 *pcbOffset += cbSize;
1999 if (NULL == pbData)
2000 {
2001 *(reinterpret_cast<DBSTATUS *>(*ppbBuffer + *pcbOffset)) = DBSTATUS_S_ISNULL;
2002 }
2003 *pcbOffset += sizeof(DBSTATUS);
2004
2005 LExit:
2006 return hr;
2007 }
2008
2009 static HRESULT GetColumnValue(
2010 __in SCE_ROW *pRow,
2011 __in DWORD dwColumnIndex,
2012 __out_opt BYTE **ppbData,
2013 __out SIZE_T *pcbSize
2014 )
2015 {
2016 HRESULT hr = S_OK;
2017 const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2018 IAccessor *pIAccessor = NULL;
2019 HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2020 HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2021 DWORD dwDataSize = 0;
2022 void *pvRawData = NULL;
2023 DBBINDING dbBinding = { };
2024 DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2025
2026 dbBinding.iOrdinal = dwColumnIndex + 1;
2027 dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2028 dbBinding.dwPart = DBPART_LENGTH;
2029 dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2030
2031 pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2032 ExitOnFailure(hr, "Failed to get IAccessor interface");
2033
2034 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2035 ExitOnFailure(hr, "Failed to create accessor");
2036
2037 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2038 ExitOnFailure(hr, "Failed to get size of data");
2039
2040 // For variable-length columns, zero data returned means NULL
2041 if (0 == dwDataSize)
2042 {
2043 ExitFunction1(hr = E_NOTFOUND);
2044 }
2045
2046 if (NULL != ppbData)
2047 {
2048 dbBinding.dwPart = DBPART_VALUE;
2049 dbBinding.cbMaxLen = dwDataSize;
2050
2051 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2052 ExitOnFailure(hr, "Failed to create accessor");
2053
2054 if (DBBINDSTATUS_OK != dbBindStatus)
2055 {
2056 hr = E_INVALIDARG;
2057 ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2058 }
2059
2060 if (DBTYPE_WSTR == dbBinding.wType)
2061 {
2062 hr = StrAlloc(reinterpret_cast<LPWSTR *>(&pvRawData), dwDataSize / sizeof(WCHAR));
2063 ExitOnFailure(hr, "Failed to allocate space for string data while reading column %u", dwColumnIndex);
2064 }
2065 else
2066 {
2067 pvRawData = MemAlloc(dwDataSize, TRUE);
2068 ExitOnNull(pvRawData, hr, E_OUTOFMEMORY, "Failed to allocate space for data while reading column %u", dwColumnIndex);
2069 }
2070
2071 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, pvRawData);
2072 ExitOnFailure(hr, "Failed to read data value");
2073
2074 ReleaseMem(*ppbData);
2075 *ppbData = reinterpret_cast<BYTE *>(pvRawData);
2076 pvRawData = NULL;
2077 }
2078
2079 *pcbSize = dwDataSize;
2080
2081 LExit:
2082 ReleaseMem(pvRawData);
2083
2084 if (DB_NULL_HACCESSOR != hAccessorLength)
2085 {
2086 pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2087 }
2088 if (DB_NULL_HACCESSOR != hAccessorValue)
2089 {
2090 pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2091 }
2092 ReleaseObject(pIAccessor);
2093
2094 return hr;
2095 }
2096
2097 static HRESULT GetColumnValueFixed(
2098 __in SCE_ROW *pRow,
2099 __in DWORD dwColumnIndex,
2100 __in DWORD cbSize,
2101 __out BYTE *pbData
2102 )
2103 {
2104 HRESULT hr = S_OK;
2105 const SCE_TABLE_SCHEMA *pTable = pRow->pTableSchema;
2106 IAccessor *pIAccessor = NULL;
2107 HACCESSOR hAccessorLength = DB_NULL_HACCESSOR;
2108 HACCESSOR hAccessorValue = DB_NULL_HACCESSOR;
2109 DWORD dwDataSize = 0;
2110 DBBINDSTATUS dbBindStatus = DBBINDSTATUS_OK;
2111 DBBINDING dbBinding = { };
2112
2113 dbBinding.iOrdinal = dwColumnIndex + 1;
2114 dbBinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
2115 dbBinding.dwPart = DBPART_LENGTH;
2116 dbBinding.wType = pTable->rgColumns[dwColumnIndex].dbtColumnType;
2117
2118 pRow->pIRowset->QueryInterface(IID_IAccessor, reinterpret_cast<void **>(&pIAccessor));
2119 ExitOnFailure(hr, "Failed to get IAccessor interface");
2120
2121 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorLength, &dbBindStatus);
2122 ExitOnFailure(hr, "Failed to create accessor");
2123
2124 if (DBBINDSTATUS_OK != dbBindStatus)
2125 {
2126 hr = E_INVALIDARG;
2127 ExitOnFailure(hr, "Bad bind status while creating accessor to get length of value");
2128 }
2129
2130 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorLength, reinterpret_cast<void *>(&dwDataSize));
2131 ExitOnFailure(hr, "Failed to get size of data");
2132
2133 if (0 == dwDataSize)
2134 {
2135 ExitFunction1(hr = E_NOTFOUND);
2136 }
2137
2138 dbBinding.dwPart = DBPART_VALUE;
2139 dbBinding.cbMaxLen = cbSize;
2140
2141 hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, 0, &hAccessorValue, &dbBindStatus);
2142 ExitOnFailure(hr, "Failed to create accessor");
2143
2144 if (DBBINDSTATUS_OK != dbBindStatus)
2145 {
2146 hr = E_INVALIDARG;
2147 ExitOnFailure(hr, "Bad bind status while creating accessor to get value");
2148 }
2149
2150 hr = pRow->pIRowset->GetData(pRow->hRow, hAccessorValue, reinterpret_cast<void *>(pbData));
2151 ExitOnFailure(hr, "Failed to read data value");
2152
2153 LExit:
2154 if (DB_NULL_HACCESSOR != hAccessorLength)
2155 {
2156 pIAccessor->ReleaseAccessor(hAccessorLength, NULL);
2157 }
2158 if (DB_NULL_HACCESSOR != hAccessorValue)
2159 {
2160 pIAccessor->ReleaseAccessor(hAccessorValue, NULL);
2161 }
2162 ReleaseObject(pIAccessor);
2163
2164 return hr;
2165 }
2166
2167 static HRESULT EnsureLocalColumnConstraints(
2168 __in ITableDefinition *pTableDefinition,
2169 __in DBID *pTableID,
2170 __in SCE_TABLE_SCHEMA *pTableSchema
2171 )
2172 {
2173 HRESULT hr = S_OK;
2174 SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2175 DBCONSTRAINTDESC dbcdConstraint = { };
2176 DBID dbConstraintID = { };
2177 DBID dbLocalColumnID = { };
2178 ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2179
2180 hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2181 ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2182
2183 for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2184 {
2185 pCurrentColumn = pTableSchema->rgColumns + i;
2186
2187 // Add a primary key constraint for this column, if one exists
2188 if (pCurrentColumn->fPrimaryKey)
2189 {
2190 // Setup DBID for new constraint
2191 dbConstraintID.eKind = DBKIND_NAME;
2192 dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(L"PrimaryKey");
2193 dbcdConstraint.pConstraintID = &dbConstraintID;
2194
2195 dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_PRIMARYKEY;
2196
2197 dbLocalColumnID.eKind = DBKIND_NAME;
2198 dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2199 dbcdConstraint.cColumns = 1;
2200 dbcdConstraint.rgColumnList = &dbLocalColumnID;
2201
2202 dbcdConstraint.pReferencedTableID = NULL;
2203 dbcdConstraint.cForeignKeyColumns = 0;
2204 dbcdConstraint.rgForeignKeyColumnList = NULL;
2205 dbcdConstraint.pwszConstraintText = NULL;
2206 dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2207 dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2208 dbcdConstraint.MatchType = DBMATCHTYPE_NONE;
2209 dbcdConstraint.Deferrability = 0;
2210 dbcdConstraint.cReserved = 0;
2211 dbcdConstraint.rgReserved = NULL;
2212
2213 hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2214 if (DB_E_DUPLICATECONSTRAINTID == hr)
2215 {
2216 hr = S_OK;
2217 }
2218 ExitOnFailure(hr, "Failed to add primary key constraint for column %ls, table %ls", pCurrentColumn->wzName, pTableSchema->wzName);
2219 }
2220 }
2221
2222 LExit:
2223 ReleaseObject(pTableDefinitionWithConstraints);
2224
2225 return hr;
2226 }
2227
2228 static HRESULT EnsureForeignColumnConstraints(
2229 __in ITableDefinition *pTableDefinition,
2230 __in DBID *pTableID,
2231 __in SCE_TABLE_SCHEMA *pTableSchema,
2232 __in SCE_DATABASE_SCHEMA *pDatabaseSchema
2233 )
2234 {
2235 HRESULT hr = S_OK;
2236 SCE_COLUMN_SCHEMA *pCurrentColumn = NULL;
2237 DBCONSTRAINTDESC dbcdConstraint = { };
2238 DBID dbConstraintID = { };
2239 DBID dbLocalColumnID = { };
2240 DBID dbForeignTableID = { };
2241 DBID dbForeignColumnID = { };
2242 ITableDefinitionWithConstraints *pTableDefinitionWithConstraints = NULL;
2243
2244 hr = pTableDefinition->QueryInterface(IID_ITableDefinitionWithConstraints, reinterpret_cast<void **>(&pTableDefinitionWithConstraints));
2245 ExitOnFailure(hr, "Failed to query for ITableDefinitionWithConstraints interface in order to create column constraints");
2246
2247 for (DWORD i = 0; i < pTableSchema->cColumns; ++i)
2248 {
2249 pCurrentColumn = pTableSchema->rgColumns + i;
2250
2251 // Add a foreign key constraint for this column, if one exists
2252 if (NULL != pCurrentColumn->wzRelationName)
2253 {
2254 // Setup DBID for new constraint
2255 dbConstraintID.eKind = DBKIND_NAME;
2256 dbConstraintID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzRelationName);
2257 dbcdConstraint.pConstraintID = &dbConstraintID;
2258
2259 dbcdConstraint.ConstraintType = DBCONSTRAINTTYPE_FOREIGNKEY;
2260
2261 dbForeignColumnID.eKind = DBKIND_NAME;
2262 dbForeignColumnID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].rgColumns[pCurrentColumn->dwForeignKeyColumn].wzName);
2263 dbcdConstraint.cColumns = 1;
2264 dbcdConstraint.rgColumnList = &dbForeignColumnID;
2265
2266 dbForeignTableID.eKind = DBKIND_NAME;
2267 dbForeignTableID.uName.pwszName = const_cast<LPOLESTR>(pDatabaseSchema->rgTables[pCurrentColumn->dwForeignKeyTable].wzName);
2268 dbcdConstraint.pReferencedTableID = &dbForeignTableID;
2269
2270 dbLocalColumnID.eKind = DBKIND_NAME;
2271 dbLocalColumnID.uName.pwszName = const_cast<LPOLESTR>(pCurrentColumn->wzName);
2272 dbcdConstraint.cForeignKeyColumns = 1;
2273 dbcdConstraint.rgForeignKeyColumnList = &dbLocalColumnID;
2274
2275 dbcdConstraint.pwszConstraintText = NULL;
2276 dbcdConstraint.UpdateRule = DBUPDELRULE_NOACTION;
2277 dbcdConstraint.DeleteRule = DBUPDELRULE_NOACTION;
2278 dbcdConstraint.MatchType = DBMATCHTYPE_FULL;
2279 dbcdConstraint.Deferrability = 0;
2280 dbcdConstraint.cReserved = 0;
2281 dbcdConstraint.rgReserved = NULL;
2282
2283 hr = pTableDefinitionWithConstraints->AddConstraint(pTableID, &dbcdConstraint);
2284 if (DB_E_DUPLICATECONSTRAINTID == hr)
2285 {
2286 hr = S_OK;
2287 }
2288 ExitOnFailure(hr, "Failed to add constraint named: %ls to table: %ls", pCurrentColumn->wzRelationName, pTableSchema->wzName);
2289 }
2290 }
2291
2292 LExit:
2293 ReleaseObject(pTableDefinitionWithConstraints);
2294
2295 return hr;
2296 }
2297
2298 static HRESULT SetSessionProperties(
2299 __in ISessionProperties *pISessionProperties
2300 )
2301 {
2302 HRESULT hr = S_OK;
2303 DBPROP rgdbpDataSourceProp[1];
2304 DBPROPSET rgdbpDataSourcePropSet[1];
2305
2306 rgdbpDataSourceProp[0].dwPropertyID = DBPROP_SSCE_TRANSACTION_COMMIT_MODE;
2307 rgdbpDataSourceProp[0].dwOptions = DBPROPOPTIONS_REQUIRED;
2308 rgdbpDataSourceProp[0].vValue.vt = VT_I4;
2309 rgdbpDataSourceProp[0].vValue.lVal = DBPROPVAL_SSCE_TCM_FLUSH;
2310
2311 rgdbpDataSourcePropSet[0].guidPropertySet = DBPROPSET_SSCE_SESSION;
2312 rgdbpDataSourcePropSet[0].rgProperties = rgdbpDataSourceProp;
2313 rgdbpDataSourcePropSet[0].cProperties = 1;
2314
2315 hr = pISessionProperties->SetProperties(1, rgdbpDataSourcePropSet);
2316 ExitOnFailure(hr, "Failed to set session properties");
2317
2318 LExit:
2319 return hr;
2320 }
2321
2322 static HRESULT GetDatabaseSchemaInfo(
2323 __in SCE_DATABASE *pDatabase,
2324 __out LPWSTR *psczSchemaType,
2325 __out DWORD *pdwVersion
2326 )
2327 {
2328 HRESULT hr = S_OK;
2329 LPWSTR sczSchemaType = NULL;
2330 DWORD dwVersionFound = 0;
2331 SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2332 SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2333 // Database object with our alternate schema
2334 SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2335 SCE_ROW_HANDLE sceRow = NULL;
2336
2337 hr = OpenSchema(pDatabase, &fullSchema);
2338 ExitOnFailure(hr, "Failed to ensure internal version schema");
2339
2340 hr = SceGetFirstRow(&database, 0, &sceRow);
2341 ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2342
2343 hr = SceGetColumnString(sceRow, 0, &sczSchemaType);
2344 ExitOnFailure(hr, "Failed to get internal schematype");
2345
2346 hr = SceGetColumnDword(sceRow, 1, &dwVersionFound);
2347 ExitOnFailure(hr, "Failed to get internal version");
2348
2349 *psczSchemaType = sczSchemaType;
2350 sczSchemaType = NULL;
2351 *pdwVersion = dwVersionFound;
2352
2353 LExit:
2354 SceCloseTable(&schemaTable); // ignore failure
2355 ReleaseStr(sczSchemaType);
2356 ReleaseSceRow(sceRow);
2357
2358 return hr;
2359 }
2360
2361 static HRESULT SetDatabaseSchemaInfo(
2362 __in SCE_DATABASE *pDatabase,
2363 __in LPCWSTR wzSchemaType,
2364 __in DWORD dwVersion
2365 )
2366 {
2367 HRESULT hr = S_OK;
2368 BOOL fInSceTransaction = FALSE;
2369 SCE_TABLE_SCHEMA schemaTable = SCE_INTERNAL_VERSION_TABLE_SCHEMA[0];
2370 SCE_DATABASE_SCHEMA fullSchema = { 1, &schemaTable};
2371 // Database object with our alternate schema
2372 SCE_DATABASE database = { pDatabase->sdbHandle, &fullSchema };
2373 SCE_ROW_HANDLE sceRow = NULL;
2374
2375 hr = EnsureSchema(pDatabase, &fullSchema);
2376 ExitOnFailure(hr, "Failed to ensure internal version schema");
2377
2378 hr = SceBeginTransaction(&database);
2379 ExitOnFailure(hr, "Failed to begin transaction");
2380 fInSceTransaction = TRUE;
2381
2382 hr = SceGetFirstRow(&database, 0, &sceRow);
2383 if (E_NOTFOUND == hr)
2384 {
2385 hr = ScePrepareInsert(&database, 0, &sceRow);
2386 ExitOnFailure(hr, "Failed to insert only row into internal version schema table");
2387 }
2388 else
2389 {
2390 ExitOnFailure(hr, "Failed to get first row in internal version schema table");
2391 }
2392
2393 hr = SceSetColumnString(sceRow, 0, wzSchemaType);
2394 ExitOnFailure(hr, "Failed to set internal schematype to: %ls", wzSchemaType);
2395
2396 hr = SceSetColumnDword(sceRow, 1, dwVersion);
2397 ExitOnFailure(hr, "Failed to set internal version to: %u", dwVersion);
2398
2399 hr = SceFinishUpdate(sceRow);
2400 ExitOnFailure(hr, "Failed to insert first row in internal version schema table");
2401
2402 hr = SceCommitTransaction(&database);
2403 ExitOnFailure(hr, "Failed to commit transaction");
2404 fInSceTransaction = FALSE;
2405
2406 LExit:
2407 SceCloseTable(&schemaTable); // ignore failure
2408 ReleaseSceRow(sceRow);
2409 if (fInSceTransaction)
2410 {
2411 SceRollbackTransaction(&database);
2412 }
2413
2414 return hr;
2415 }
2416
2417 static void ReleaseDatabase(
2418 SCE_DATABASE *pDatabase
2419 )
2420 {
2421 if (NULL != pDatabase)
2422 {
2423 if (NULL != pDatabase->pdsSchema)
2424 {
2425 for (DWORD i = 0; i < pDatabase->pdsSchema->cTables; ++i)
2426 {
2427 SceCloseTable(pDatabase->pdsSchema->rgTables + i);
2428 }
2429 }
2430
2431 if (NULL != pDatabase->sdbHandle)
2432 {
2433 ReleaseDatabaseInternal(reinterpret_cast<SCE_DATABASE_INTERNAL *>(pDatabase->sdbHandle));
2434 }
2435 }
2436 ReleaseMem(pDatabase);
2437 }
2438
2439 static void ReleaseDatabaseInternal(
2440 SCE_DATABASE_INTERNAL *pDatabaseInternal
2441 )
2442 {
2443 HRESULT hr = S_OK;
2444
2445 if (NULL != pDatabaseInternal)
2446 {
2447 ReleaseObject(pDatabaseInternal->pITransactionLocal);
2448 ReleaseObject(pDatabaseInternal->pIOpenRowset);
2449 ReleaseObject(pDatabaseInternal->pISessionProperties);
2450 ReleaseObject(pDatabaseInternal->pIDBCreateSession);
2451 ReleaseObject(pDatabaseInternal->pIDBProperties);
2452
2453 if (NULL != pDatabaseInternal->pIDBInitialize)
2454 {
2455 hr = pDatabaseInternal->pIDBInitialize->Uninitialize();
2456 if (FAILED(hr))
2457 {
2458 TraceError(hr, "Failed to call uninitialize on IDBInitialize");
2459 }
2460 ReleaseObject(pDatabaseInternal->pIDBInitialize);
2461 }
2462
2463 if (NULL != pDatabaseInternal->hSqlCeDll)
2464 {
2465 if (!::FreeLibrary(pDatabaseInternal->hSqlCeDll))
2466 {
2467 hr = HRESULT_FROM_WIN32(::GetLastError());
2468 TraceError(hr, "Failed to free sql ce dll");
2469 }
2470 }
2471 }
2472
2473 // If there was a temp file we copied to (for read-only databases), delete it after close
2474 if (NULL != pDatabaseInternal->sczTempDbFile)
2475 {
2476 hr = FileEnsureDelete(pDatabaseInternal->sczTempDbFile);
2477 if (FAILED(hr))
2478 {
2479 TraceError(hr, "Failed to delete temporary database file (copied here because the database was opened as read-only): %ls", pDatabaseInternal->sczTempDbFile);
2480 }
2481 ReleaseStr(pDatabaseInternal->sczTempDbFile);
2482 }
2483
2484 ReleaseMem(pDatabaseInternal);
2485 }
2486
2487 #endif // end SKIP_SCE_COMPILE