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