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