aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/eseutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/eseutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/eseutil.cpp1340
1 files changed, 1340 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/eseutil.cpp b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp
new file mode 100644
index 00000000..b9455d4b
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/eseutil.cpp
@@ -0,0 +1,1340 @@
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
6// Exit macros
7#define EseExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
8#define EseExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
9#define EseExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
10#define EseExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
11#define EseExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
12#define EseExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__)
13#define EseExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__)
14#define EseExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__)
15#define EseExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_ESEUTIL, p, x, e, s, __VA_ARGS__)
16#define EseExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_ESEUTIL, p, x, s, __VA_ARGS__)
17#define EseExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_ESEUTIL, e, x, s, __VA_ARGS__)
18#define EseExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_ESEUTIL, g, x, s, __VA_ARGS__)
19
20struct ESE_QUERY
21{
22 ESE_QUERY_TYPE qtQueryType;
23 BOOL fIndexRangeSet;
24
25 JET_SESID jsSession;
26 JET_TABLEID jtTable;
27
28 DWORD dwColumns;
29 void *pvData[10]; // The data queried for for this column
30 DWORD cbData[10]; // One for each column, describes the size of the corresponding entry in ppvData
31};
32
33// Todo: convert more JET_ERR to HRESULTS here
34HRESULT HresultFromJetError(JET_ERR jEr)
35{
36 HRESULT hr = S_OK;
37
38 switch (jEr)
39 {
40 case JET_errSuccess:
41 return S_OK;
42
43 case JET_wrnNyi:
44 return E_NOTIMPL;
45 break;
46
47 case JET_errOutOfMemory:
48 hr = E_OUTOFMEMORY;
49 break;
50
51 case JET_errInvalidParameter: __fallthrough;
52 case JET_errInvalidInstance:
53 hr = E_INVALIDARG;
54 break;
55
56 case JET_errDatabaseInUse:
57 hr = HRESULT_FROM_WIN32(ERROR_DEVICE_IN_USE);
58 break;
59
60 case JET_errKeyDuplicate:
61 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
62 break;
63
64 case JET_errInvalidSystemPath: __fallthrough;
65 case JET_errInvalidLogDirectory: __fallthrough;
66 case JET_errInvalidPath: __fallthrough;
67 case JET_errDatabaseInvalidPath:
68 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
69 break;
70
71 case JET_errDatabaseLocked:
72 hr = HRESULT_FROM_WIN32(ERROR_FILE_CHECKED_OUT);
73 break;
74
75 case JET_errInvalidDatabase:
76 hr = HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION);
77 break;
78
79 case JET_errCallbackNotResolved:
80 hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
81 break;
82
83 case JET_errNoCurrentRecord: __fallthrough;
84 case JET_errRecordNotFound: __fallthrough;
85 case JET_errFileNotFound: __fallthrough;
86 case JET_errObjectNotFound:
87 hr = E_NOTFOUND;
88 break;
89
90 case JET_wrnBufferTruncated:
91 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
92 break;
93
94 case JET_errFileAccessDenied:
95 hr = E_ACCESSDENIED;
96 break;
97
98 default:
99 hr = E_FAIL;
100 }
101
102 // Log the actual Jet error code so we have record of it before it's morphed into an HRESULT to be compatible with the rest of our code
103 ExitTraceSource(DUTIL_SOURCE_ESEUTIL, hr, "Encountered Jet Error: 0x%08x", jEr);
104
105 return hr;
106}
107
108#define ExitOnJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }}
109#define ExitOnRootJetFailure(e, x, s, ...) { x = HresultFromJetError(e); if (S_OK != x) { Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(DUTIL_SOURCE_ESEUTIL, x, s, __VA_ARGS__); goto LExit; }}
110
111HRESULT DAPI EseBeginSession(
112 __out JET_INSTANCE *pjiInstance,
113 __out JET_SESID *pjsSession,
114 __in_z LPCWSTR pszInstance,
115 __in_z LPCWSTR pszPath
116 )
117{
118 HRESULT hr = S_OK;
119 JET_ERR jEr = JET_errSuccess;
120 LPSTR pszAnsiInstance = NULL;
121 LPSTR pszAnsiPath = NULL;
122
123 hr = DirEnsureExists(pszPath, NULL);
124 EseExitOnFailure(hr, "Failed to ensure database directory exists");
125
126 // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
127 // likely breaking everyone with unicode characters in their path.
128 hr = StrAnsiAllocString(&pszAnsiInstance, pszInstance, 0, CP_ACP);
129 EseExitOnFailure(hr, "Failed converting instance name to ansi");
130
131 hr = StrAnsiAllocString(&pszAnsiPath, pszPath, 0, CP_ACP);
132 EseExitOnFailure(hr, "Failed converting session path name to ansi");
133
134 jEr = JetCreateInstanceA(pjiInstance, pszAnsiInstance);
135 ExitOnJetFailure(jEr, hr, "Failed to create instance");
136
137 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramSystemPath, NULL, pszAnsiPath);
138 ExitOnJetFailure(jEr, hr, "Failed to set jet system path to: %s", pszAnsiPath);
139
140 // This makes sure log files that are created are created next to the database, not next to our EXE (note they last after execution)
141 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramLogFilePath, NULL, pszAnsiPath);
142 ExitOnJetFailure(jEr, hr, "Failed to set jet log file path to: %s", pszAnsiPath);
143
144 jEr = JetSetSystemParameter(pjiInstance, NULL, JET_paramMaxOpenTables, 10, NULL);
145 ExitOnJetFailure(jEr, hr, "Failed to set jet max open tables parameter");
146
147 // TODO: Use callback hooks so that Jet Engine uses our memory allocation methods, etc.? (search docs for "JET_PFNREALLOC" - there are other callbacks too)
148
149 jEr = JetInit(pjiInstance);
150 ExitOnJetFailure(jEr, hr, "Failed to initialize jet engine instance");
151
152 jEr = JetBeginSession(*pjiInstance, pjsSession, NULL, NULL);
153 ExitOnJetFailure(jEr, hr, "Failed to begin jet session");
154
155LExit:
156 ReleaseStr(pszAnsiInstance);
157 ReleaseStr(pszAnsiPath);
158
159 return hr;
160}
161
162HRESULT DAPI EseEndSession(
163 __in JET_INSTANCE jiInstance,
164 __in JET_SESID jsSession
165 )
166{
167 HRESULT hr = S_OK;
168 JET_ERR jEr = JET_errSuccess;
169
170 jEr = JetEndSession(jsSession, 0);
171 ExitOnJetFailure(jEr, hr, "Failed to end jet session");
172
173 jEr = JetTerm(jiInstance);
174 ExitOnJetFailure(jEr, hr, "Failed to uninitialize jet engine instance");
175
176LExit:
177 return hr;
178}
179
180// Utility function used by EnsureSchema()
181HRESULT AllocColumnCreateStruct(
182 __in const ESE_TABLE_SCHEMA *ptsSchema,
183 __deref_out JET_COLUMNCREATE **ppjccColumnCreate
184 )
185{
186 HRESULT hr = S_OK;
187 DWORD_PTR i;
188 size_t cbAllocSize = 0;
189
190 hr = ::SizeTMult(ptsSchema->dwColumns, sizeof(JET_COLUMNCREATE), &(cbAllocSize));
191 EseExitOnFailure(hr, "Maximum allocation exceeded.");
192
193 *ppjccColumnCreate = static_cast<JET_COLUMNCREATE*>(MemAlloc(cbAllocSize, TRUE));
194 EseExitOnNull(*ppjccColumnCreate, hr, E_OUTOFMEMORY, "Failed to allocate column create structure for database");
195
196 for (i = 0; i < ptsSchema->dwColumns; ++i)
197 {
198 (*ppjccColumnCreate)[i].cbStruct = sizeof(JET_COLUMNCREATE);
199
200 hr = StrAnsiAllocString(&(*ppjccColumnCreate)[i].szColumnName, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
201 EseExitOnFailure(hr, "Failed to allocate ansi column name: %ls", ptsSchema->pcsColumns[i].pszName);
202
203 (*ppjccColumnCreate)[i].coltyp = ptsSchema->pcsColumns[i].jcColumnType;
204
205 if (JET_coltypText == (*ppjccColumnCreate)[i].coltyp)
206 {
207 (*ppjccColumnCreate)[i].cbMax = 256;
208 }
209 else if (JET_coltypLongText == (*ppjccColumnCreate)[i].coltyp)
210 {
211 (*ppjccColumnCreate)[i].cbMax = 2147483648;
212 (*ppjccColumnCreate)[i].grbit = JET_bitColumnTagged; // LongText columns must be tagged
213 ptsSchema->pcsColumns[i].fNullable = TRUE;
214 }
215 else if (JET_coltypLong == (*ppjccColumnCreate)[i].coltyp)
216 {
217 (*ppjccColumnCreate)[i].cbMax = 4;
218
219 if (ptsSchema->pcsColumns[i].fAutoIncrement)
220 {
221 (*ppjccColumnCreate)[i].grbit |= JET_bitColumnAutoincrement;
222 }
223 }
224
225 if (!(ptsSchema->pcsColumns[i].fNullable))
226 {
227 (*ppjccColumnCreate)[i].grbit |= JET_bitColumnNotNULL;
228 }
229
230 (*ppjccColumnCreate)[i].pvDefault = NULL;
231 (*ppjccColumnCreate)[i].cbDefault = 0;
232 (*ppjccColumnCreate)[i].cp = 1200;
233 (*ppjccColumnCreate)[i].columnid = 0;
234 (*ppjccColumnCreate)[i].err = 0;
235 }
236
237LExit:
238 return hr;
239}
240
241HRESULT FreeColumnCreateStruct(
242 __in_ecount(dwColumns) JET_COLUMNCREATE *pjccColumnCreate,
243 __in DWORD dwColumns
244 )
245{
246 HRESULT hr = S_OK;
247 DWORD i;
248
249 for (i = 0; i < dwColumns; ++i)
250 {
251 ReleaseStr((pjccColumnCreate[i]).szColumnName);
252 }
253
254 hr = MemFree(pjccColumnCreate);
255 EseExitOnFailure(hr, "Failed to release core column create struct");
256
257LExit:
258 return hr;
259}
260
261// Utility function used by EnsureSchema()
262HRESULT AllocIndexCreateStruct(
263 __in const ESE_TABLE_SCHEMA *ptsSchema,
264 __deref_out JET_INDEXCREATE **ppjicIndexCreate
265 )
266{
267 HRESULT hr = S_OK;
268 LPSTR pszMultiSzKeys = NULL;
269 LPSTR pszIndexName = NULL;
270 LPSTR pszTempString = NULL;
271 BOOL fKeyColumns = FALSE;
272 DWORD_PTR i;
273
274 for (i=0; i < ptsSchema->dwColumns; ++i)
275 {
276 if (ptsSchema->pcsColumns[i].fKey)
277 {
278 hr = StrAnsiAllocString(&pszTempString, ptsSchema->pcsColumns[i].pszName, 0, CP_ACP);
279 EseExitOnFailure(hr, "Failed to convert string to ansi: %ls", ptsSchema->pcsColumns[i].pszName);
280
281 hr = StrAnsiAllocConcat(&pszMultiSzKeys, "+", 0);
282 EseExitOnFailure(hr, "Failed to append plus sign to multisz string: %s", pszTempString);
283
284 hr = StrAnsiAllocConcat(&pszMultiSzKeys, pszTempString, 0);
285 EseExitOnFailure(hr, "Failed to append column name to multisz string: %s", pszTempString);
286
287 ReleaseNullStr(pszTempString);
288
289 // All question marks will be converted to null characters later; this is just to trick dutil
290 // into letting us create an ansi, double-null-terminated list of single-null-terminated strings
291 hr = StrAnsiAllocConcat(&pszMultiSzKeys, "?", 0);
292 EseExitOnFailure(hr, "Failed to append placeholder character to multisz string: %hs", pszMultiSzKeys);
293
294 // Record that at least one key column was found
295 fKeyColumns = TRUE;
296 }
297 }
298
299 // If no key columns were found, don't create an index - just return
300 if (!fKeyColumns)
301 {
302 ExitFunction1(hr = S_OK);
303 }
304
305 hr = StrAnsiAllocString(&pszIndexName, ptsSchema->pszName, 0, CP_ACP);
306 EseExitOnFailure(hr, "Failed to allocate ansi string version of %ls", ptsSchema->pszName);
307
308 hr = StrAnsiAllocConcat(&pszIndexName, "_Index", 0);
309 EseExitOnFailure(hr, "Failed to append table name string version of %ls", ptsSchema->pszName);
310
311 *ppjicIndexCreate = static_cast<JET_INDEXCREATE*>(MemAlloc(sizeof(JET_INDEXCREATE), TRUE));
312 EseExitOnNull(*ppjicIndexCreate, hr, E_OUTOFMEMORY, "Failed to allocate index create structure for database");
313
314 // Record the size including both null terminators - the struct requires this
315 size_t cchSize = 0;
316 hr = ::StringCchLengthA(pszMultiSzKeys, STRSAFE_MAX_LENGTH, &cchSize);
317 EseExitOnRootFailure(hr, "Failed to get size of keys string");
318
319 ++cchSize; // add 1 to include null character at the end
320
321 // At this point convert all question marks to null characters
322 for (i = 0; i < cchSize; ++i)
323 {
324 if ('?' == pszMultiSzKeys[i])
325 {
326 pszMultiSzKeys[i] = '\0';
327 }
328 }
329
330 (*ppjicIndexCreate)->cbStruct = sizeof(JET_INDEXCREATE);
331 (*ppjicIndexCreate)->szIndexName = pszIndexName;
332 (*ppjicIndexCreate)->szKey = pszMultiSzKeys;
333 (*ppjicIndexCreate)->cbKey = (DWORD)cchSize;
334 (*ppjicIndexCreate)->grbit = JET_bitIndexUnique | JET_bitIndexPrimary;
335 (*ppjicIndexCreate)->ulDensity = 80;
336 (*ppjicIndexCreate)->lcid = 1033;
337 (*ppjicIndexCreate)->pidxunicode = NULL;
338 (*ppjicIndexCreate)->cbVarSegMac = 0;
339 (*ppjicIndexCreate)->rgconditionalcolumn = NULL;
340 (*ppjicIndexCreate)->cConditionalColumn = 0;
341 (*ppjicIndexCreate)->err = 0;
342
343LExit:
344 ReleaseStr(pszTempString);
345
346 return hr;
347}
348
349HRESULT EnsureSchema(
350 __in JET_DBID jdbDb,
351 __in JET_SESID jsSession,
352 __in ESE_DATABASE_SCHEMA *pdsSchema
353 )
354{
355 HRESULT hr = S_OK;
356 JET_ERR jEr = JET_errSuccess;
357 BOOL fTransaction = FALSE;
358 DWORD dwTable;
359 DWORD dwColumn;
360 JET_TABLECREATE jtTableCreate = { };
361
362 // Set parameters which apply to all tables here
363 jtTableCreate.cbStruct = sizeof(jtTableCreate);
364 jtTableCreate.ulPages = 100;
365 jtTableCreate.ulDensity = 0; // per the docs, 0 means "use the default value"
366 jtTableCreate.cIndexes = 1;
367
368 hr = EseBeginTransaction(jsSession);
369 EseExitOnFailure(hr, "Failed to begin transaction to create tables");
370 fTransaction = TRUE;
371
372 for (dwTable = 0;dwTable < pdsSchema->dwTables; ++dwTable)
373 {
374 // Don't free this pointer - it's just a shortcut to the current table's name within the struct
375 LPCWSTR pwzTableName = pdsSchema->ptsTables[dwTable].pszName;
376
377 // Ensure table exists
378 hr = EseOpenTable(jsSession, jdbDb, pwzTableName, &pdsSchema->ptsTables[dwTable].jtTable);
379 if (E_NOTFOUND == hr) // if the table is missing, create it
380 {
381 // Fill out the JET_TABLECREATE struct
382 hr = StrAnsiAllocString(&jtTableCreate.szTableName, pdsSchema->ptsTables[dwTable].pszName, 0, CP_ACP);
383 EseExitOnFailure(hr, "Failed converting table name to ansi");
384
385 hr = AllocColumnCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgcolumncreate);
386 EseExitOnFailure(hr, "Failed to allocate column create struct");
387
388 hr = AllocIndexCreateStruct(&(pdsSchema->ptsTables[dwTable]), &jtTableCreate.rgindexcreate);
389 EseExitOnFailure(hr, "Failed to allocate index create struct");
390
391 jtTableCreate.cColumns = pdsSchema->ptsTables[dwTable].dwColumns;
392 jtTableCreate.tableid = NULL;
393
394 // TODO: Investigate why we can't create a table without a key column?
395 // Actually create the table using our JET_TABLECREATE struct
396 jEr = JetCreateTableColumnIndex(jsSession, jdbDb, &jtTableCreate);
397 ExitOnJetFailure(jEr, hr, "Failed to create %ls table", pwzTableName);
398
399 // Record the table ID in our cache
400 pdsSchema->ptsTables[dwTable].jtTable = jtTableCreate.tableid;
401
402 // Record the column IDs in our cache
403 for (dwColumn = 0; dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
404 {
405 pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn].jcColumn = jtTableCreate.rgcolumncreate[dwColumn].columnid;
406 }
407
408 // Free and NULL things we allocated in this struct
409 ReleaseNullStr(jtTableCreate.szTableName);
410
411 hr = FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
412 EseExitOnFailure(hr, "Failed to free column create struct");
413 jtTableCreate.rgcolumncreate = NULL;
414 }
415 else
416 {
417 // If the table already exists, grab the column ids and put them into our cache
418 for (dwColumn = 0;dwColumn < pdsSchema->ptsTables[dwTable].dwColumns; ++dwColumn)
419 {
420 // Don't free this - it's just a shortcut to the current column within the struct
421 ESE_COLUMN_SCHEMA *pcsColumn = &(pdsSchema->ptsTables[dwTable].pcsColumns[dwColumn]);
422 ULONG ulColumnSize = 0;
423 BOOL fNullable = pcsColumn->fNullable;
424
425 // Todo: this code is nearly duplicated from AllocColumnCreateStruct - factor it out!
426 if (JET_coltypText == pcsColumn->jcColumnType)
427 {
428 ulColumnSize = 256;
429 }
430 else if (JET_coltypLongText == pcsColumn->jcColumnType)
431 {
432 ulColumnSize = 2147483648;
433 fNullable = TRUE;
434 }
435 else if (JET_coltypLong == pcsColumn->jcColumnType)
436 {
437 ulColumnSize = 4;
438 fNullable = TRUE;
439 }
440
441 hr = EseEnsureColumn(jsSession, pdsSchema->ptsTables[dwTable].jtTable, pcsColumn->pszName, pcsColumn->jcColumnType, ulColumnSize, pcsColumn->fFixed, fNullable, &pcsColumn->jcColumn);
442 EseExitOnFailure(hr, "Failed to create column %u of %ls table", dwColumn, pwzTableName);
443 }
444 }
445 }
446
447LExit:
448 ReleaseStr(jtTableCreate.szTableName);
449
450 if (NULL != jtTableCreate.rgcolumncreate)
451 {
452 // Don't record the HRESULT here or it will override the return value of this function
453 FreeColumnCreateStruct(jtTableCreate.rgcolumncreate, jtTableCreate.cColumns);
454 }
455
456 if (fTransaction)
457 {
458 EseCommitTransaction(jsSession);
459 }
460
461 return hr;
462}
463
464// Todo: support overwrite flag? Unfortunately, requires WinXP and up
465// Todo: Add version parameter, and a built-in dutil table that stores the version of the database schema on disk - then allow overriding the "migrate to new schema" functionality with a callback
466HRESULT DAPI EseEnsureDatabase(
467 __in JET_SESID jsSession,
468 __in_z LPCWSTR pszFile,
469 __in ESE_DATABASE_SCHEMA *pdsSchema,
470 __out JET_DBID* pjdbDb,
471 __in BOOL fExclusive,
472 __in BOOL fReadonly
473 )
474{
475 HRESULT hr = S_OK;
476 JET_ERR jEr = JET_errSuccess;
477 JET_GRBIT jgrOptions = 0;
478 LPWSTR pszDir = NULL;
479 LPSTR pszAnsiFile = NULL;
480
481 // Sigh. JETblue requires Vista and up for the wide character version of this function, so we'll convert to ANSI before calling,
482 // likely breaking all those with unicode characters in their path.
483 hr = StrAnsiAllocString(&pszAnsiFile, pszFile, 0, CP_ACP);
484 EseExitOnFailure(hr, "Failed converting database name to ansi");
485
486 hr = PathGetDirectory(pszFile, &pszDir);
487 EseExitOnFailure(hr, "Failed to get directory that will contain database file");
488
489 hr = DirEnsureExists(pszDir, NULL);
490 EseExitOnFailure(hr, "Failed to ensure directory exists for database: %ls", pszDir);
491
492 if (FileExistsEx(pszFile, NULL))
493 {
494 if (fReadonly)
495 {
496 jgrOptions = jgrOptions | JET_bitDbReadOnly;
497 }
498
499 jEr = JetAttachDatabaseA(jsSession, pszAnsiFile, jgrOptions);
500 ExitOnJetFailure(jEr, hr, "Failed to attach to database %s", pszAnsiFile);
501
502 // This flag doesn't apply to attach, only applies to Open, so only set it after the attach
503 if (fExclusive)
504 {
505 jgrOptions = jgrOptions | JET_bitDbExclusive;
506 }
507
508 jEr = JetOpenDatabaseA(jsSession, pszAnsiFile, NULL, pjdbDb, jgrOptions);
509 ExitOnJetFailure(jEr, hr, "Failed to open database %s", pszAnsiFile);
510 }
511 else
512 {
513 jEr = JetCreateDatabase2A(jsSession, pszAnsiFile, 0, pjdbDb, 0);
514 ExitOnJetFailure(jEr, hr, "Failed to create database %ls", pszFile);
515 }
516
517 hr = EnsureSchema(*pjdbDb, jsSession, pdsSchema);
518 EseExitOnFailure(hr, "Failed to ensure database schema matches expectations");
519
520LExit:
521 ReleaseStr(pszDir);
522 ReleaseStr(pszAnsiFile);
523
524 return hr;
525}
526
527HRESULT DAPI EseCloseDatabase(
528 __in JET_SESID jsSession,
529 __in JET_DBID jdbDb
530 )
531{
532 HRESULT hr = S_OK;
533 JET_ERR jEr = JET_errSuccess;
534 JET_GRBIT jgrOptions = 0;
535
536 jEr = JetCloseDatabase(jsSession, jdbDb, jgrOptions);
537 ExitOnJetFailure(jEr, hr, "Failed to close database");
538
539LExit:
540 return hr;
541}
542
543HRESULT DAPI EseCreateTable(
544 __in JET_SESID jsSession,
545 __in JET_DBID jdbDb,
546 __in_z LPCWSTR pszTable,
547 __out JET_TABLEID *pjtTable
548 )
549{
550 HRESULT hr = S_OK;
551 JET_ERR jEr = JET_errSuccess;
552 LPSTR pszAnsiTable = NULL;
553
554 hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
555 EseExitOnFailure(hr, "Failed converting table name to ansi");
556
557 jEr = JetCreateTableA(jsSession, jdbDb, pszAnsiTable, 100, 0, pjtTable);
558 ExitOnJetFailure(jEr, hr, "Failed to create table %s", pszAnsiTable);
559
560LExit:
561 ReleaseStr(pszAnsiTable);
562
563 return hr;
564}
565
566HRESULT DAPI EseOpenTable(
567 __in JET_SESID jsSession,
568 __in JET_DBID jdbDb,
569 __in_z LPCWSTR pszTable,
570 __out JET_TABLEID *pjtTable
571 )
572{
573 HRESULT hr = S_OK;
574 JET_ERR jEr = JET_errSuccess;
575 LPSTR pszAnsiTable = NULL;
576
577 hr = StrAnsiAllocString(&pszAnsiTable, pszTable, 0, CP_ACP);
578 EseExitOnFailure(hr, "Failed converting table name to ansi");
579
580 jEr = JetOpenTableA(jsSession, jdbDb, pszAnsiTable, NULL, 0, 0, pjtTable);
581 ExitOnJetFailure(jEr, hr, "Failed to open table %s", pszAnsiTable);
582
583LExit:
584 ReleaseStr(pszAnsiTable);
585
586 return hr;
587}
588
589HRESULT DAPI EseCloseTable(
590 __in JET_SESID jsSession,
591 __in JET_TABLEID jtTable
592 )
593{
594 HRESULT hr = S_OK;
595 JET_ERR jEr = JET_errSuccess;
596
597 jEr = JetCloseTable(jsSession, jtTable);
598 ExitOnJetFailure(jEr, hr, "Failed to close table");
599
600LExit:
601 return hr;
602}
603
604HRESULT DAPI EseEnsureColumn(
605 __in JET_SESID jsSession,
606 __in JET_TABLEID jtTable,
607 __in_z LPCWSTR pszColumnName,
608 __in JET_COLTYP jcColumnType,
609 __in ULONG ulColumnSize,
610 __in BOOL fFixed,
611 __in BOOL fNullable,
612 __out_opt JET_COLUMNID *pjcColumn
613 )
614{
615 HRESULT hr = S_OK;
616 JET_ERR jEr = JET_errSuccess;
617 LPSTR pszAnsiColumnName = NULL;
618 JET_COLUMNDEF jcdColumnDef = { sizeof(JET_COLUMNDEF) };
619 JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
620
621 hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
622 EseExitOnFailure(hr, "Failed converting column name to ansi");
623
624 jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
625 if (JET_errSuccess == jEr)
626 {
627 // Return the found columnID
628 if (NULL != pjcColumn)
629 {
630 *pjcColumn = jcdTempBase.columnid;
631 }
632
633 ExitFunction1(hr = S_OK);
634 }
635 else if (JET_errColumnNotFound == jEr)
636 {
637 jEr = JET_errSuccess;
638 }
639 ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
640
641 jcdColumnDef.columnid = 0;
642 jcdColumnDef.coltyp = jcColumnType;
643 jcdColumnDef.wCountry = 0;
644 jcdColumnDef.langid = 0;
645 jcdColumnDef.cp = 1200;
646 jcdColumnDef.wCollate = 0;
647 jcdColumnDef.cbMax = ulColumnSize;
648 jcdColumnDef.grbit = 0;
649
650 if (fFixed)
651 {
652 jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnFixed;
653 }
654 if (!fNullable)
655 {
656 jcdColumnDef.grbit = jcdColumnDef.grbit | JET_bitColumnNotNULL;
657 }
658
659 jEr = JetAddColumnA(jsSession, jtTable, pszAnsiColumnName, &jcdColumnDef, NULL, 0, pjcColumn);
660 ExitOnJetFailure(jEr, hr, "Failed to add column %ls", pszColumnName);
661
662LExit:
663 ReleaseStr(pszAnsiColumnName);
664
665 return hr;
666}
667
668HRESULT DAPI EseGetColumn(
669 __in JET_SESID jsSession,
670 __in JET_TABLEID jtTable,
671 __in_z LPCWSTR pszColumnName,
672 __out JET_COLUMNID *pjcColumn
673 )
674{
675 HRESULT hr = S_OK;
676 JET_ERR jEr = JET_errSuccess;
677 LPSTR pszAnsiColumnName = NULL;
678 JET_COLUMNBASE jcdTempBase = { sizeof(JET_COLUMNBASE) };
679
680 hr = StrAnsiAllocString(&pszAnsiColumnName, pszColumnName, 0, CP_ACP);
681 EseExitOnFailure(hr, "Failed converting column name to ansi");
682
683 jEr = JetGetTableColumnInfoA(jsSession, jtTable, pszAnsiColumnName, &jcdTempBase, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
684 if (JET_errSuccess == jEr)
685 {
686 // Return the found columnID
687 if (NULL != pjcColumn)
688 {
689 *pjcColumn = jcdTempBase.columnid;
690 }
691
692 ExitFunction1(hr = S_OK);
693 }
694 ExitOnJetFailure(jEr, hr, "Failed to check if column exists: %s", pszAnsiColumnName);
695
696LExit:
697 ReleaseStr(pszAnsiColumnName);
698
699 return hr;
700}
701
702HRESULT DAPI EseMoveCursor(
703 __in JET_SESID jsSession,
704 __in JET_TABLEID jtTable,
705 __in LONG lRow
706 )
707{
708 HRESULT hr = S_OK;
709 JET_ERR jEr = JET_errSuccess;
710
711 jEr = JetMove(jsSession, jtTable, lRow, 0);
712 ExitOnJetFailure(jEr, hr, "Failed to move jet cursor by amount: %d", lRow);
713
714LExit:
715 return hr;
716}
717
718HRESULT DAPI EseDeleteRow(
719 __in JET_SESID jsSession,
720 __in JET_TABLEID jtTable
721 )
722{
723 HRESULT hr = S_OK;
724 JET_ERR jEr = JET_errSuccess;
725
726 jEr = JetDelete(jsSession, jtTable);
727 ExitOnJetFailure(jEr, hr, "Failed to delete row");
728
729LExit:
730 return hr;
731}
732
733HRESULT DAPI EseBeginTransaction(
734 __in JET_SESID jsSession
735 )
736{
737 HRESULT hr = S_OK;
738 JET_ERR jEr = JET_errSuccess;
739
740 jEr = JetBeginTransaction(jsSession);
741 ExitOnJetFailure(jEr, hr, "Failed to begin transaction");
742
743LExit:
744 return hr;
745}
746
747HRESULT DAPI EseRollbackTransaction(
748 __in JET_SESID jsSession,
749 __in BOOL fAll
750 )
751{
752 HRESULT hr = S_OK;
753 JET_ERR jEr = JET_errSuccess;
754
755 jEr = JetRollback(jsSession, fAll ? JET_bitRollbackAll : 0);
756 ExitOnJetFailure(jEr, hr, "Failed to rollback transaction");
757
758LExit:
759 return hr;
760}
761
762HRESULT DAPI EseCommitTransaction(
763 __in JET_SESID jsSession
764 )
765{
766 HRESULT hr = S_OK;
767 JET_ERR jEr = JET_errSuccess;
768
769 jEr = JetCommitTransaction(jsSession, 0);
770 ExitOnJetFailure(jEr, hr, "Failed to commit transaction");
771
772LExit:
773 return hr;
774}
775
776HRESULT DAPI EsePrepareUpdate(
777 __in JET_SESID jsSession,
778 __in JET_TABLEID jtTable,
779 __in ULONG ulPrep
780 )
781{
782 HRESULT hr = S_OK;
783 JET_ERR jEr = JET_errSuccess;
784
785 jEr = JetPrepareUpdate(jsSession, jtTable, ulPrep);
786 ExitOnJetFailure(jEr, hr, "Failed to prepare for update of type: %ul", ulPrep);
787
788LExit:
789 return hr;
790}
791
792HRESULT DAPI EseFinishUpdate(
793 __in JET_SESID jsSession,
794 __in JET_TABLEID jtTable,
795 __in BOOL fSeekToInsertedRecord
796 )
797{
798 HRESULT hr = S_OK;
799 JET_ERR jEr = JET_errSuccess;
800 unsigned char rgbBookmark[JET_cbBookmarkMost + 1];
801 DWORD cbBookmark;
802
803 if (fSeekToInsertedRecord)
804 {
805 jEr = JetUpdate(jsSession, jtTable, rgbBookmark, sizeof(rgbBookmark), &cbBookmark);
806 ExitOnJetFailure(jEr, hr, "Failed to run update and retrieve bookmark");
807
808 jEr = JetGotoBookmark(jsSession, jtTable, rgbBookmark, cbBookmark);
809 ExitOnJetFailure(jEr, hr, "Failed to seek to recently updated record using bookmark");
810 }
811 else
812 {
813 jEr = JetUpdate(jsSession, jtTable, NULL, 0, NULL);
814 ExitOnJetFailure(jEr, hr, "Failed to run update (without retrieving bookmark)");
815 }
816
817LExit:
818 // If we fail, the caller won't expect that the update wasn't finished, so we'll cancel their entire update to leave them in a good state
819 if (FAILED(hr))
820 {
821 JetPrepareUpdate(jsSession, jtTable, JET_prepCancel);
822 }
823
824 return hr;
825}
826
827HRESULT DAPI EseSetColumnBinary(
828 __in JET_SESID jsSession,
829 __in ESE_TABLE_SCHEMA tsTable,
830 __in DWORD dwColumn,
831 __in_bcount(cbBuffer) const BYTE* pbBuffer,
832 __in SIZE_T cbBuffer
833 )
834{
835 HRESULT hr = S_OK;
836 JET_ERR jEr = JET_errSuccess;
837
838 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pbBuffer, static_cast<unsigned long>(cbBuffer), 0, NULL);
839 ExitOnJetFailure(jEr, hr, "Failed to set binary value into column of database");
840
841LExit:
842 return hr;
843}
844
845HRESULT DAPI EseSetColumnDword(
846 __in JET_SESID jsSession,
847 __in ESE_TABLE_SCHEMA tsTable,
848 __in DWORD dwColumn,
849 __in DWORD dwValue
850 )
851{
852 HRESULT hr = S_OK;
853 JET_ERR jEr = JET_errSuccess;
854
855 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &dwValue, sizeof(DWORD), 0, NULL);
856 ExitOnJetFailure(jEr, hr, "Failed to set dword value into column of database: %u", dwValue);
857
858LExit:
859 return hr;
860}
861
862HRESULT DAPI EseSetColumnBool(
863 __in JET_SESID jsSession,
864 __in ESE_TABLE_SCHEMA tsTable,
865 __in DWORD dwColumn,
866 __in BOOL fValue
867 )
868{
869 HRESULT hr = S_OK;
870 JET_ERR jEr = JET_errSuccess;
871 BYTE bValue = fValue ? 0xFF : 0x00;
872
873 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, 0, NULL);
874 ExitOnJetFailure(jEr, hr, "Failed to set bool value into column of database");
875
876LExit:
877 return hr;
878}
879
880HRESULT DAPI EseSetColumnString(
881 __in JET_SESID jsSession,
882 __in ESE_TABLE_SCHEMA tsTable,
883 __in DWORD dwColumn,
884 __in_z LPCWSTR pwzValue
885 )
886{
887 HRESULT hr = S_OK;
888 JET_ERR jEr = JET_errSuccess;
889 size_t cchValue = 0;
890 ULONG cbValueSize = 0;
891
892 if (pwzValue)
893 {
894 hr = ::StringCchLengthW(pwzValue, STRSAFE_MAX_LENGTH, &cchValue);
895 EseExitOnRootFailure(hr, "Failed to get string length: %ls", pwzValue);
896 }
897
898 cbValueSize = static_cast<ULONG>((cchValue + 1) * sizeof(WCHAR)); // add 1 for null character, then multiply by size of WCHAR to get bytes
899
900 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pwzValue, cbValueSize, 0, NULL);
901 ExitOnJetFailure(jEr, hr, "Failed to set string value into column of database: %ls", pwzValue);
902
903LExit:
904 return hr;
905}
906
907HRESULT DAPI EseSetColumnEmpty(
908 __in JET_SESID jsSession,
909 __in ESE_TABLE_SCHEMA tsTable,
910 __in DWORD dwColumn
911 )
912{
913 HRESULT hr = S_OK;
914 JET_ERR jEr = JET_errSuccess;
915
916 jEr = JetSetColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, 0, NULL);
917 ExitOnJetFailure(jEr, hr, "Failed to set empty value into column of database");
918
919LExit:
920 return hr;
921}
922
923HRESULT DAPI EseGetColumnBinary(
924 __in JET_SESID jsSession,
925 __in ESE_TABLE_SCHEMA tsTable,
926 __in DWORD dwColumn,
927 __deref_inout_bcount(*piBuffer) BYTE** ppbBuffer,
928 __inout SIZE_T* piBuffer
929 )
930{
931 HRESULT hr = S_OK;
932 JET_ERR jEr = JET_errSuccess;
933 ULONG ulActualSize = 0;
934
935 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
936 if (JET_wrnBufferTruncated == jEr)
937 {
938 jEr = JET_errSuccess;
939 }
940 ExitOnJetFailure(jEr, hr, "Failed to check size of binary value from record");
941
942 if (NULL == *ppbBuffer)
943 {
944 *ppbBuffer = reinterpret_cast<BYTE *>(MemAlloc(ulActualSize, FALSE));
945 EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for reading binary value column");
946 }
947 else
948 {
949 *ppbBuffer = reinterpret_cast<BYTE *>(MemReAlloc(*ppbBuffer, ulActualSize, FALSE));
950 EseExitOnNull(*ppbBuffer, hr, E_OUTOFMEMORY, "Failed to reallocate memory for reading binary value column");
951 }
952
953 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppbBuffer, ulActualSize, NULL, 0, NULL);
954 ExitOnJetFailure(jEr, hr, "Failed to retrieve binary value from record");
955
956 *piBuffer = static_cast<SIZE_T>(ulActualSize);
957
958LExit:
959 if (FAILED(hr))
960 {
961 ReleaseNullMem(*ppbBuffer);
962 }
963
964 return hr;
965}
966
967HRESULT DAPI EseGetColumnDword(
968 __in JET_SESID jsSession,
969 __in ESE_TABLE_SCHEMA tsTable,
970 __in DWORD dwColumn,
971 __out DWORD *pdwValue
972 )
973{
974 HRESULT hr = S_OK;
975 JET_ERR jEr = JET_errSuccess;
976
977 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, pdwValue, sizeof(DWORD), NULL, 0, NULL);
978 ExitOnJetFailure(jEr, hr, "Failed to retrieve dword value from record");
979
980LExit:
981 return hr;
982}
983
984HRESULT DAPI EseGetColumnBool(
985 __in JET_SESID jsSession,
986 __in ESE_TABLE_SCHEMA tsTable,
987 __in DWORD dwColumn,
988 __out BOOL *pfValue
989 )
990{
991 HRESULT hr = S_OK;
992 JET_ERR jEr = JET_errSuccess;
993 BYTE bValue = 0;
994
995 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, &bValue, 1, NULL, 0, NULL);
996 ExitOnJetFailure(jEr, hr, "Failed to retrieve bool value from record");
997
998 if (bValue == 0)
999 {
1000 *pfValue = FALSE;
1001 }
1002 else
1003 {
1004 *pfValue = TRUE;
1005 }
1006
1007LExit:
1008 return hr;
1009}
1010
1011HRESULT DAPI EseGetColumnString(
1012 __in JET_SESID jsSession,
1013 __in ESE_TABLE_SCHEMA tsTable,
1014 __in DWORD dwColumn,
1015 __out LPWSTR *ppszValue
1016 )
1017{
1018 HRESULT hr = S_OK;
1019 JET_ERR jEr = JET_errSuccess;
1020 ULONG ulActualSize = 0;
1021
1022 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, NULL, 0, &ulActualSize, 0, NULL);
1023 if (JET_wrnBufferTruncated == jEr)
1024 {
1025 jEr = JET_errSuccess;
1026 }
1027 ExitOnJetFailure(jEr, hr, "Failed to check size of string value from record");
1028
1029 hr = StrAlloc(ppszValue, ulActualSize);
1030 EseExitOnFailure(hr, "Failed to allocate string while retrieving column value");
1031
1032 jEr = JetRetrieveColumn(jsSession, tsTable.jtTable, tsTable.pcsColumns[dwColumn].jcColumn, *ppszValue, ulActualSize, NULL, 0, NULL);
1033 ExitOnJetFailure(jEr, hr, "Failed to retrieve string value from record");
1034
1035LExit:
1036 return hr;
1037}
1038
1039HRESULT DAPI EseBeginQuery(
1040 __in JET_SESID jsSession,
1041 __in JET_TABLEID jtTable,
1042 __in ESE_QUERY_TYPE qtQueryType,
1043 __out ESE_QUERY_HANDLE *peqhHandle
1044 )
1045{
1046 UNREFERENCED_PARAMETER(jsSession);
1047 UNREFERENCED_PARAMETER(jtTable);
1048
1049 HRESULT hr = S_OK;
1050
1051 *peqhHandle = static_cast<ESE_QUERY*>(MemAlloc(sizeof(ESE_QUERY), TRUE));
1052 EseExitOnNull(*peqhHandle, hr, E_OUTOFMEMORY, "Failed to allocate new query");
1053
1054 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(*peqhHandle);
1055 peqHandle->qtQueryType = qtQueryType;
1056 peqHandle->jsSession = jsSession;
1057 peqHandle->jtTable = jtTable;
1058
1059LExit:
1060 return hr;
1061}
1062
1063// Utility function used by other functions to set a query column
1064HRESULT DAPI SetQueryColumn(
1065 __in ESE_QUERY_HANDLE eqhHandle,
1066 __in_bcount(cbData) const void *pvData,
1067 __in DWORD cbData,
1068 __in JET_GRBIT jGrb
1069 )
1070{
1071 HRESULT hr = S_OK;
1072 JET_ERR jEr = JET_errSuccess;
1073
1074 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1075
1076 if (peqHandle->dwColumns == countof(peqHandle->pvData))
1077 {
1078 hr = E_NOTIMPL;
1079 EseExitOnFailure(hr, "Dutil hasn't implemented support for queries of more than %d columns", countof(peqHandle->pvData));
1080 }
1081
1082 if (0 == peqHandle->dwColumns) // If it's the first column, start a new key
1083 {
1084 jGrb = jGrb | JET_bitNewKey;
1085 }
1086
1087 jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, pvData, cbData, jGrb);
1088 ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1089
1090 // If the query is wildcard, setup the cached copy of pvData
1091 if (ESE_QUERY_EXACT != peqHandle->qtQueryType)
1092 {
1093 peqHandle->pvData[peqHandle->dwColumns] = MemAlloc(cbData, FALSE);
1094 EseExitOnNull(peqHandle->pvData[peqHandle->dwColumns], hr, E_OUTOFMEMORY, "Failed to allocate memory");
1095
1096 memcpy(peqHandle->pvData[peqHandle->dwColumns], pvData, cbData);
1097
1098 peqHandle->cbData[peqHandle->dwColumns] = cbData;
1099 }
1100
1101 // Increment the number of total columns
1102 ++peqHandle->dwColumns;
1103
1104LExit:
1105 return hr;
1106}
1107
1108HRESULT DAPI EseSetQueryColumnBinary(
1109 __in ESE_QUERY_HANDLE eqhHandle,
1110 __in_bcount(cbBuffer) const BYTE* pbBuffer,
1111 __in SIZE_T cbBuffer,
1112 __in BOOL fFinal // If this is true, all other key columns in the query will be set to "*"
1113 )
1114{
1115 HRESULT hr = S_OK;
1116 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1117 JET_GRBIT jGrb = 0;
1118
1119 if (cbBuffer > DWORD_MAX)
1120 {
1121 ExitFunction1(hr = E_INVALIDARG);
1122 }
1123
1124 if (fFinal)
1125 {
1126 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1127 {
1128 jGrb = jGrb | JET_bitFullColumnStartLimit;
1129 }
1130 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1131 {
1132 jGrb = jGrb | JET_bitFullColumnEndLimit;
1133 }
1134 }
1135
1136 hr = SetQueryColumn(eqhHandle, reinterpret_cast<const void *>(pbBuffer), static_cast<DWORD>(cbBuffer), jGrb);
1137 EseExitOnFailure(hr, "Failed to set value of query colum (as binary) to:");
1138
1139LExit:
1140 return hr;
1141}
1142
1143HRESULT DAPI EseSetQueryColumnDword(
1144 __in ESE_QUERY_HANDLE eqhHandle,
1145 __in DWORD dwData,
1146 __in BOOL fFinal
1147 )
1148{
1149 HRESULT hr = S_OK;
1150 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1151 JET_GRBIT jGrb = 0;
1152
1153 if (fFinal)
1154 {
1155 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1156 {
1157 jGrb = jGrb | JET_bitFullColumnStartLimit;
1158 }
1159 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1160 {
1161 jGrb = jGrb | JET_bitFullColumnEndLimit;
1162 }
1163 }
1164
1165 hr = SetQueryColumn(eqhHandle, (const void *)&dwData, sizeof(DWORD), jGrb);
1166 EseExitOnFailure(hr, "Failed to set value of query colum (as dword) to: %u", dwData);
1167
1168LExit:
1169 return hr;
1170}
1171
1172HRESULT DAPI EseSetQueryColumnBool(
1173 __in ESE_QUERY_HANDLE eqhHandle,
1174 __in BOOL fValue,
1175 __in BOOL fFinal
1176 )
1177{
1178 HRESULT hr = S_OK;
1179 BYTE bByte = fValue ? 0xFF : 0x00;
1180 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1181 JET_GRBIT jGrb = 0;
1182
1183 if (fFinal)
1184 {
1185 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1186 {
1187 jGrb = jGrb | JET_bitFullColumnStartLimit;
1188 }
1189 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1190 {
1191 jGrb = jGrb | JET_bitFullColumnEndLimit;
1192 }
1193 }
1194
1195 hr = SetQueryColumn(eqhHandle, (const void *)&bByte, 1, jGrb);
1196 EseExitOnFailure(hr, "Failed to set value of query colum (as bool) to: %s", fValue ? "TRUE" : "FALSE");
1197
1198LExit:
1199 return hr;
1200}
1201
1202HRESULT DAPI EseSetQueryColumnString(
1203 __in ESE_QUERY_HANDLE eqhHandle,
1204 __in_z LPCWSTR pszString,
1205 __in BOOL fFinal
1206 )
1207{
1208 HRESULT hr = S_OK;
1209 DWORD dwStringSize = 0;
1210 size_t cchString = 0;
1211 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1212 JET_GRBIT jGrb = 0;
1213
1214 if (pszString)
1215 {
1216 hr = ::StringCchLengthW(pszString, STRSAFE_MAX_LENGTH, &cchString);
1217 EseExitOnRootFailure(hr, "Failed to get size of column string");
1218 }
1219
1220 dwStringSize = static_cast<DWORD>(sizeof(WCHAR) * (cchString + 1)); // Add 1 for null terminator
1221
1222 if (fFinal)
1223 {
1224 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1225 {
1226 jGrb = jGrb | JET_bitFullColumnStartLimit;
1227 }
1228 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1229 {
1230 jGrb = jGrb | JET_bitFullColumnEndLimit;
1231 }
1232 }
1233
1234 hr = SetQueryColumn(eqhHandle, (const void *)pszString, dwStringSize, jGrb);
1235 EseExitOnFailure(hr, "Failed to set value of query colum (as string) to: %ls", pszString);
1236
1237LExit:
1238 return hr;
1239}
1240
1241HRESULT DAPI EseFinishQuery(
1242 __in ESE_QUERY_HANDLE eqhHandle
1243 )
1244{
1245 HRESULT hr = S_OK;
1246 JET_ERR jEr = JET_errSuccess;
1247
1248 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1249
1250 if (peqHandle->fIndexRangeSet)
1251 {
1252 jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeRemove);
1253 ExitOnJetFailure(jEr, hr, "Failed to release index range");
1254
1255 peqHandle->fIndexRangeSet = FALSE;
1256 }
1257
1258 for (int i=0; i < countof(peqHandle->pvData); ++i)
1259 {
1260 ReleaseMem(peqHandle->pvData[i]);
1261 }
1262
1263 ReleaseMem(peqHandle);
1264
1265LExit:
1266 return hr;
1267}
1268
1269HRESULT DAPI EseRunQuery(
1270 __in ESE_QUERY_HANDLE eqhHandle
1271 )
1272{
1273 HRESULT hr = S_OK;
1274 JET_ERR jEr = JET_errSuccess;
1275 JET_GRBIT jGrb = 0;
1276 JET_GRBIT jGrbSeekType = 0;
1277 DWORD i;
1278
1279 ESE_QUERY *peqHandle = static_cast<ESE_QUERY *>(eqhHandle);
1280
1281 if (ESE_QUERY_EXACT == peqHandle->qtQueryType)
1282 {
1283 jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, JET_bitSeekEQ);
1284 ExitOnJetFailure(jEr, hr, "Failed to seek EQ within jet table");
1285 }
1286 else
1287 {
1288 if (ESE_QUERY_FROM_TOP == peqHandle->qtQueryType)
1289 {
1290 jGrbSeekType = JET_bitSeekGE;
1291 }
1292 else if (ESE_QUERY_FROM_BOTTOM == peqHandle->qtQueryType)
1293 {
1294 jGrbSeekType = JET_bitSeekLE;
1295 }
1296
1297 jEr = JetSeek(peqHandle->jsSession, peqHandle->jtTable, jGrbSeekType);
1298 if (jEr == JET_wrnSeekNotEqual)
1299 {
1300 jEr = JET_errSuccess;
1301 }
1302
1303 // At this point we've already set our cursor to the beginning of the range of records to select.
1304 // Now we'll make a key pointing to the end of the range of records to select, so we can call JetSetIndexRange()
1305 // For a semi-explanation, see this doc page: http://msdn.microsoft.com/en-us/library/aa964799%28EXCHG.10%29.aspx
1306 for (i = 0; i < peqHandle->dwColumns; ++i)
1307 {
1308 if (i == 0)
1309 {
1310 jGrb = JET_bitNewKey;
1311 }
1312 else
1313 {
1314 jGrb = 0;
1315 }
1316
1317 // On the last iteration
1318 if (i == peqHandle->dwColumns - 1)
1319 {
1320 jGrb |= JET_bitFullColumnEndLimit;
1321 }
1322
1323 jEr = JetMakeKey(peqHandle->jsSession, peqHandle->jtTable, peqHandle->pvData[i], peqHandle->cbData[i], jGrb);
1324 ExitOnJetFailure(jEr, hr, "Failed to begin new query");
1325 }
1326
1327 jEr = JetSetIndexRange(peqHandle->jsSession, peqHandle->jtTable, JET_bitRangeUpperLimit);
1328 ExitOnJetFailure(jEr, hr, "Failed to set index range");
1329
1330 peqHandle->fIndexRangeSet = TRUE;
1331
1332 // Sometimes JetBlue doesn't check if there is a current record when calling the above function (and sometimes it does)
1333 // So, let's check if there is a current record before returning (by reading the first byte of one).
1334 jEr = JetMove(peqHandle->jsSession, peqHandle->jtTable, 0, 0);
1335 ExitOnJetFailure(jEr, hr, "Failed to check if there is a current record after query");
1336 }
1337
1338LExit:
1339 return hr;
1340}