summaryrefslogtreecommitdiff
path: root/src/include/xbindex.h.nope
blob: b42f76e4bae35a5a0083a08593045850b998f87f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
/*  xbindex.h

XBase64 Software Library

Copyright (c) 1997,2003,2014, 2018, 2022 Gary A Kunkel

The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.

Email Contact:

    XDB-devel@lists.sourceforge.net
    XDB-users@lists.sourceforge.net

*/


#ifndef __XB_INDEX_H__
#define __XB_INDEX_H__

#ifdef XB_INDEX_SUPPORT


#define XB_ADD_KEY 1
#define XB_UPD_KEY 2
#define XB_DEL_KEY 3

namespace xb{



///@cond DOXYOFF
// structure for index nodes, each node contains information regarding one block
struct XBDLLEXPORT xbIxNode {
  xbIxNode *npPrev;                     // pointer to previous node in chain
  xbIxNode *npNext;                     // pointer to next node in chain
  xbUInt32 iCurKeyNo;                   // current key number in the node, 0 offset
  xbUInt32 ulBlockNo;                   // this block number
  xbUInt32 ulBufSize;                   // size of cpBlockData
  char     *cpBlockData;                // pointer to memory version of block data stored in file
};
///@endcond DOXYOFF


//! @brief Base class for handling dbf indices.
/*!

The xbIx class is used as a base class for accessing index files.
Each index file can have one or more tags.

Calls to the index routines to perform index updates are handled automatically by the dbf class. 
The application program does not need to be concerned with index updates.

If there is a production MDX index, it is opened automatically when the dbf file is opened.
If there is an ndx file, that has been associated with the dbf file with the metadata routines,
it will be opened automatically when the dbf file is opened.
If there are non prod ndx indices that are not associated with the dbf file, the application
program will need to open as appropriate.
The meta data association logic is specific to the Xbase64 library and is not applicable to
other available tools that handle ndx indices.
All index files are automatically closed when the dbf file is closed.


<br>
The class is designed to support the addition of additional indices with a minimal amount of effort
needed to integrate into the library.
If you are looking at adding an new index type to the library, create a derived class using xbIx as a 
base class and modify methods needed to support the new index file version. 
The xbDbf class (and derived classes) perform the needed calls to the index routines for updates.<br>
See the following for examples on how to start on this:<br>
xbIxNdx is a derived class and supports a single tag.<br>
xbIxMdx is a derived class and supports multiple tags.<br>



<br>
How data fields are stored in index files:
<table>
<tr><th>Field Type<th>Stored in DBF as<th>Stored in NDX as<th>Stored in MDX as</tr>
<tr><td>C<td>char<td>char<td>char
<tr><td>F<td>text numbers<td>xbDouble<td>xbBcd
<tr><td>N<td>text numbers<td>xbDouble<td>xbBcd
<tr><td>D<td>text CCYYMMDD<td>xbDouble - julian<td>xbDouble - julian
</table>


<br>
Pages Vs Blocks
<br>
For purposes of the Xbase index classes, a page is considered to be 512 bytes of data
and a block is made up of one or more 512 byte pages.
<br>Default block sixe of NDX indices is one 512 byte page.
<br>Default block size of MDX indices is two 512 byte pages or 1024 bytes.

<br>The WriteBlock and GetBlock functions calculate the physical position in the 
file based on a combination of Block Number and Block Size.  Block size is set at
time of index file creation, default is 1024 or two pages.

<br>Page numbers are stored in the physical file, but block reads and writes 
are performed.

<br>
Duplicate Keys
<br>
With the original DBase unique indexing option, if a table has multiple records with the
same key value, DBase would allow multiple records in the table, but only the first
record would be found in the index.
<br>
XBase64 can be configured to support the original DBase duplicate key implementation,
or can be configured to halt with a DUPLICATE_KEY error on the insertion of a record
with a duplicate key.
<br>
<table>
<tr><th>Option<th>Description</tr>
<tr><td>XB_HALT_ON_DUPKEY</td><td>Return error XB_KEY_NOT_UNIQUE when attempting to append record with duplicate key</td></tr>
<tr><td>XB_EMULATE_DBASE</td><td>Emulate DBase, allow duplicate records with the same key, only the first record is indexed</td></tr>
</table>
*/


class XBDLLEXPORT xbIx : public xbFile {
 public:
   xbIx( xbDbf * d );
   virtual ~xbIx();

   virtual xbInt16  CheckTagIntegrity( void *vpTag, xbInt16 iOpt ) = 0;
   virtual xbInt16  Close();
   virtual xbInt16  CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ) = 0;
   virtual xbInt16  FindKey( void *vpTag, const xbString &sKey, xbInt16 iRetrieveSw );
   virtual xbInt16  FindKey( void *vpTag, const char * cKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw );
   virtual xbInt16  FindKey( void *vpTag, const xbBcd &bcd, xbInt16 iRetrieveSw );
   virtual xbInt16  FindKey( void *vpTag, const xbDate &dtKey, xbInt16 iRetrieveSw );
   virtual xbInt16  FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw );
   virtual xbInt16  FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw ) = 0;
   virtual void     *GetCurTag() const;
   virtual xbDbf    *GetDbf() const;

   virtual xbString &GetKeyExpression( const void *vpTag ) const = 0;
   virtual xbString &GetKeyFilter( const void *vpTag ) const = 0;
   virtual char     GetKeyType( const void *vpTag ) const = 0;
   virtual xbBool   GetLocked() const;

   virtual xbInt16  GetFirstKey( void *vpTag, xbInt16 iRetrieveSw ) = 0;
   virtual xbInt16  GetFirstKey( void *vpTag );
   virtual xbInt16  GetFirstKey();

   virtual xbInt16  GetLastKey( void *vpTag, xbInt16 lRetrieveSw ) = 0;
   virtual xbInt16  GetLastKey( void *vpTag );
   virtual xbInt16  GetLastKey();

   virtual xbInt16  GetNextKey( void *vpTag, xbInt16 iRetrieveSw ) = 0;
   virtual xbInt16  GetNextKey( void *vpTag );
   virtual xbInt16  GetNextKey();

   virtual xbInt16  GetPrevKey( void *vpTag, xbInt16 iRetrieveSw ) = 0;
   virtual xbInt16  GetPrevKey( void *vpTag );
   virtual xbInt16  GetPrevKey();

   virtual void     *GetTag( xbInt16 iTagNo ) const = 0;
   virtual void     *GetTag( xbString &sTagName ) const = 0;
   virtual xbInt16  GetTagCount() const = 0;

   virtual xbString &GetTagName( void *vpTag ) const = 0;
   virtual const char * GetTagName( void *vpTag, xbInt16 iOpt ) const = 0;
   virtual void     GetTagName( void *vpTag, xbString &sTagName ) {};

   virtual xbBool   GetUnique( void *vpTag ) const = 0;
   virtual xbBool   GetSortOrder( void *vpTag ) const = 0;

   virtual xbInt16  Open( const xbString &sFileName );
   virtual xbInt16  Reindex( void **vpTag ) = 0;
   virtual xbInt16  SetCurTag( xbInt16 iTagNo ) = 0;
   virtual xbInt16  SetCurTag( xbString &sTagName ) = 0;
   virtual void     SetCurTag( void * vpCurTag );
   virtual void     SetLocked( xbBool bLocked );

   virtual void     TestStub( char *s, void *vpTag ) {};


   #ifdef XB_DEBUG_SUPPORT
   virtual xbInt16 DumpFreeBlocks( xbInt16 iOpt = 0 ) { return XB_NO_ERROR; }
   virtual xbInt16 DumpHeader( xbInt16 iDestOpt = 0, xbInt16 iFmtOpt = 0 ) = 0;
   virtual xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt ) = 0;
   virtual void    DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const = 0;
   virtual xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const;
   virtual xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL  ) = 0;
 
   #endif

 protected:
   friend  class    xbDbf;

   virtual xbInt16  AddKey( void *vpTag, xbUInt32 ulRecNo ) = 0;
   virtual xbInt16  AddKeys( xbUInt32 ulRecNo );
   virtual xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 );
   virtual xbInt16  BSearchBlock( char cKeyType, xbIxNode *npNode, xbInt32 lKeyLen, const void *vpKey, xbInt32 lSearchKeyLen, xbInt16 &iCompRc, xbBool bDescending = xbFalse ) const;
   virtual xbInt16  CheckForDupKeys();
   virtual xbInt16  CheckForDupKey( void *vpTag ) = 0;
   virtual xbInt16  CompareKey( char cKeyType, const void *v1, const void *v2, size_t lKeyLen ) const;
   virtual xbInt16  CreateKeys( xbInt16 iOpt );
   virtual xbInt16  CreateKey( void * vpTag, xbInt16 iOpt ) = 0;
   virtual xbInt16  DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo ) = 0;
//   virtual xbInt16  DeleteKeys( xbUInt32 ulRecNo );
   virtual xbInt16  DeleteKeys();
   virtual xbInt16  DeleteKey( void *vpTag ) = 0;

   virtual xbInt16  DeleteTag( void *vpTag ) = 0;


   virtual xbInt16  FindKeyForCurRec( void *vpTag ) = 0;
   virtual xbIxNode *FreeNodeChain( xbIxNode *np );
   virtual xbInt16  GetBlock( void *vpTag, xbUInt32 ulBlockNo, xbInt16 iOpt, xbUInt32 ulAddlBuf = 0 );
//   virtual xbBool   GetIndexUpdated() const = 0;
   virtual xbInt32  GetKeyCount( xbIxNode *npNode ) const;
   virtual char     *GetKeyData( xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iKeyItemLen ) const;
   virtual xbInt16  GetKeySts( void *vpTag ) const = 0;
   virtual xbInt16  GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 lRetrieveSw ) = 0;
   virtual xbInt16  InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr ) = 0;
   virtual xbInt16  InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0;
   virtual xbInt16  KeyExists( void * ) = 0;
//   virtual xbInt16  KeyUpdated( void *vpTag ) const = 0;
   virtual void     NodeFree( xbIxNode * ixNode );
   virtual xbInt16  ReadHeadBlock( xbInt16 iOpt = 0 ) = 0;
   virtual void     SetDbf( xbDbf *dbf );
   virtual xbInt16  SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr ) = 0;
   virtual xbInt16  SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr ) = 0;
   virtual xbInt16  UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 ) = 0;
   virtual xbInt16  WriteHeadBlock( xbInt16 iOption ) = 0;

   xbDbf *dbf;
   char  *cNodeBuf;               // pointer to memory for processing in a block of index data
   void  *vpCurTag;               // pointer to active tag.  Single index files have only one tag

 private:
   virtual void     AppendNodeChain( void *vpTag, xbIxNode *npNode ) = 0;
   virtual xbInt16  GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const = 0;
   virtual xbBool   IsLeaf( void *vpTag, xbIxNode *npNode ) const = 0;
   // virtual void     SetCurNode( void *vpTag, xbIxNode *npNode ) = 0;

   xbBool   bLocked;               // index file locked?
};

#ifdef XB_NDX_SUPPORT

#define XB_NDX_BLOCK_SIZE 512


///@cond DOXYOFF
struct XBDLLEXPORT xbNdxTag {

   // NDX File Header Fields
   xbUInt32 ulRootBlock;     // header node is 0
   xbUInt32 ulTotalBlocks;   // includes header node
   char     cKeyType;        // C = Char, F = Numeric, D = Date
   xbInt16  iKeyLen;         // length of key data
   xbInt16  iKeysPerBlock;   // max number keys per block  <=100
   xbInt16  iKeyType;        // 00 = Char, 01 = Numeric
   xbInt16  iKeyItemLen;     // KeyLen + 8 bytes
   char     cSerNo;          // rolling incrementing serial number +1 on each index update
   xbInt16  iUnique;         // True if unique
   xbString sKeyExpression;  // index expression
   // end of NDX Header field

   xbExp     *exp;           // pointer to expression for expression keys
   time_t    tNodeChainTs;   // node chain time stamp
   xbIxNode  *npNodeChain;
   xbIxNode  *npCurNode;
   char      *cpKeyBuf;     // key buffer, for searches and adds
   char      *cpKeyBuf2;    // key buffer, for deletes
   xbString  sTagName;      // tag name - is the file name without the extension
//   xbInt16   iKeyUpdated;   // key updated?  set in method KeyUpdated, checked in AddKey and DeleteKey routines
   xbBool    bFoundSts;     // key found?  used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey


   xbInt16   iKeySts;       // key updated?  set in method CreateKey, checked in AddKey and DeleteKey routines
                            //   old key filtered     new key filtered   iKeySts
                            //       Y                      Y              XB_UPD_KEY  2 - update key if changed (delete and add)
                            //       Y                      N              XB_DEL_KEY  3 - delete key
                            //       N                      Y              XB_ADD_KEY  1 - add key
                            //       N                      N                          0 - no update

};
///@endcond DOXYOFF

//! @brief Class for handling NDX single tag indices.
/*!

The xbIxNdx class is derived from the xbIx base class and is specific to handling NDX single tag index files.
Each NDX index file can have only one tag, but the methods are set up to take an argument for a specific tag.
This was done in order to provide a consistant interface across index types.

Calls to the ndx index routines to perform index updates are handled automatically be the dbf class after
the ndx file has been opened.

Xbase64 provides a mechanism to automatically open ndx files when a dbf file is opened.
If the ndx file has been associated with the dbf file with the metadata routines,
it will be opened automatically when the dbf file is opened.
If there are non prod ndx indices that are not associated with the dbf file, the application
program will need to open as appropriate.
The meta data association logic is specific to the Xbase64 library and is not applicable to
other available tools that handle ndx indices.

*/

class XBDLLEXPORT xbIxNdx : public xbIx {
 public:
   xbIxNdx( xbDbf * d );
   ~xbIxNdx();
   xbInt16  CheckTagIntegrity( void *vpTag, xbInt16 iOpt );
   xbInt16  CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag );
//  xbInt16  DeleteTag( void *vpTag );
   xbInt16  FindKey( void *vpTag, const void *vpKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw );
   xbInt16  GetFirstKey( void *vpTag, xbInt16 iRetrieveSw );

   xbInt16  GetLastKey( void *vpTag, xbInt16 iRetrieveSw = 1 );
   xbInt16  GetNextKey( void *vpTag, xbInt16 iRetrieveSw = 1 );
   xbInt16  GetPrevKey( void *vpTag, xbInt16 iRetrieveSw = 1 );
   xbInt32  GetKeyLen  ( const void *vpTag ) const;
   char     GetKeyType ( const void *vpTag ) const;
   xbString &GetKeyExpression( const void *vpTag ) const;
   xbString &GetKeyFilter( const void *vpTag ) const;
   void     *GetTag( xbInt16 iTagNo ) const;
   void     *GetTag( xbString &sTagName ) const;
   xbString &GetTagName( void *vpTag ) const;
   const char * GetTagName( void *vpTag, xbInt16 iOpt ) const;

   xbInt16  GetTagCount() const;
   xbBool   GetUnique( void *vpTag = NULL ) const;
   xbBool   GetSortOrder( void *vpTag ) const;
   xbInt16  Reindex( void **vpTag );
   xbInt16  SetCurTag( xbInt16 iTagNo );
   xbInt16  SetCurTag( xbString &sTagName );



   #ifdef XB_DEBUG_SUPPORT
   xbInt16 DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL  );
   xbInt16 DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmt = 0 );
   xbInt16 DumpIxForTag( void *vpTag, xbInt16 iOutputOpt );
   void    DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const;
   xbInt16 DumpNode( void * vpTag, xbIxNode * pNode, xbInt16 iOption ) const;
   #endif

 protected:
   friend   class xbDbf;
   xbInt16  AddKey( void *vpTag, xbUInt32 ulRecNo );
   xbIxNode *AllocateIxNode( xbUInt32 ulBufSize = 0, xbInt16 iOption = 0 );
   xbInt16  CheckForDupKey( void *vpTag );
   xbIxNode *CreateIxNode( xbUInt32 ulBufSize );
   xbInt16  CreateKey( void * vpTag, xbInt16 iOpt );
   xbInt16  DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo );
   xbInt16  DeleteKey( void *vpTag );

   xbInt16  DeleteTag( void *vpTag );

   xbInt16  FindKeyForCurRec( void *vpTag );
//   xbBool   GetIndexUpdated() const;
   xbInt16  GetKeyTypeN( const void *vpTag ) const;
   xbInt16  GetKeySts( void *vpTag ) const;
   xbInt16  GetLastKey( xbUInt32 ulNodeNo, void *vpTag, xbInt16 iRetrieveSw = 1 );
   xbInt16  InsertNodeI( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, xbUInt32 uiPtr );
   xbInt16  InsertNodeL( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo, char * cpKeyBuf, xbUInt32 uiPtr );
   xbInt16  KeyExists( void *vpTag = NULL );
//   xbBool   KeyFiltered( void *vpTag ) const;
//   xbInt16  KeyUpdated( void *vpTag ) const;
   xbInt16  ReadHeadBlock(xbInt16 iOpt);   // read the header node of the disk NDX file
   xbInt16  SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr );
   xbInt16  SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr );
   xbInt16  UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 );
   xbInt16  WriteHeadBlock( xbInt16 iOption );

 private:
   xbInt16  AddKeyNewRoot( xbNdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight );
   void     AppendNodeChain( void *vpTag, xbIxNode *npNode );
   xbInt16  GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const;
   xbInt16  GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const;
   xbInt16  GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf );
   xbBool   IsLeaf( void *vpTag, xbIxNode *npNode ) const;
   xbInt16  KeySetPosAdd( xbNdxTag *npTag, xbUInt32 ulAddKeyRecNo );
   xbInt16  KeySetPosDel( xbNdxTag *npTag );
   // void     SetCurNode( void *vpTag, xbIxNode *np );
   xbNdxTag *ndxTag;
};

#endif   /* XB_NDX_SUPPORT */


#ifdef XB_MDX_SUPPORT
//#define XB_MDX_BLOCK_SIZE 1024

struct XBDLLEXPORT xbMdxTag {

  // next 7 fields comprise the tag table entry
  xbUInt32      ulTagHdrPageNo;   // 512 byte page number, NOT block number
  char          cTagName[11];
  char          cKeyFmt;          // always 0x10 w/ DBase V7
  char          cLeftChild;       // cFwdTagThread
  char          cRightChild;      // cFwdTagThread2
  char          cParent;          // cBwdTagThread
  char          c2;
  char          cKeyType;         // C,D,N

  xbUInt32      ulRootPage;       // 512 byte page number, NOT block number
  xbUInt32      ulTagSize;        // Number of 512 byte pages allocated to the tag.   Tag size of two is a single 1024 block

  char          cKeyFmt2;         // 0x10 - base
                                  // 0x08 - descending
                                  // 0x40 - unique
  char          cKeyType2;
  // one unused byte fits here

  char          cTag11;          // dbase sets to 0x1B

  xbInt16       iKeyLen;
  xbInt16       iKeysPerBlock;
  xbInt16       iSecKeyType;
  xbInt16       iKeyItemLen;    // iKeyLen + 4

  char          cSerialNo;      // Increments +1 for each tag update
  char          cUnique;
  xbString      *sKeyExp;       // Key expression

  char          cHasFilter;     // 0x00 or 0x01
  char          cHasKeys;       // 0x00 or 0x01

  xbUInt32      ulLeftChild;    // dbase 7 sets this to the root page on tag creation
  xbUInt32      ulRightChild;   // dbase 7 sets this to the root page on tag creation

  char          cTagYY;
  char          cTagMM;
  char          cTagDD;

  char          cKeyFmt3;      // dbase 7 sets this 0x01 if discreet field or 0x00 if calculated or combination field key expression on tag creation

  xbString      *sFiltExp;     // Filter expression

  time_t        tNodeChainTs;
  xbIxNode      *npNodeChain;
  xbIxNode      *npCurNode;
  xbExp         *exp;          // pointer to expression for expression based tags
  xbExp         *filter;       // pointer to expression for index filter

  char          *cpKeyBuf;     // key buffer
  char          *cpKeyBuf2;    // key buffer
//  xbBool        iKeyUpdated;   // key updated?  set in method CreateKey, checked in AddKey and DeleteKey routines
                               // 0 - no update
                               // 1 - Add
                               // 2 - Update
                               // 3 - Delete
//  xbBool        bKeyFiltered;  // key filtered?  True if included key, False if excluded key.  Set in method CreateKey, checked in AddKey and DeleteKey routines
  xbString      *sTagName;     // string tag name


  xbMdxTag      *next;
  xbBool        bFoundSts;     // key found?  used to determine if new key should be added in XB_EMULATE_DBASE mode in AddKey


  xbInt16       iKeySts;       // key updated?  set in method CreateKey, checked in AddKey and DeleteKey routines
                               //   old key filtered     new key filtered   iKeySts
                               //       Y                      Y              XB_UPD_KEY  2 - update key if changed (delete and add)
                               //       Y                      N              XB_DEL_KEY  3 - delete key
                               //       N                      Y              XB_ADD_KEY  1 - add key
                               //       N                      N                          0 - no update


};


class XBDLLEXPORT xbIxMdx : public xbIx {
 public:
   xbIxMdx( xbDbf * d );
   ~xbIxMdx();
   xbInt16  CheckTagIntegrity( void *vpTag, xbInt16 iOpt );
   xbInt16  CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag );
//   xbInt16  DeleteTag( void *vpTag );
   xbInt16  FindKey( void *vpTag, const void *vKey, xbInt32 lKeyLen, xbInt16 iRetrieveSw );
   xbInt16  FindKey( void *vpTag, xbDouble dKey, xbInt16 iRetrieveSw );
   xbInt16  GetFirstKey( void *vpTag, xbInt16 lRetrieveSw );
   xbString &GetKeyExpression( const void *vpTag ) const;
   xbString &GetKeyFilter( const void *vpTag ) const;
   char     GetKeyType( const void *vpTag ) const;
   xbInt16  GetLastKey( void *vpTag, xbInt16 lRetrieveSw );
   xbInt16  GetNextKey( void *vpTag, xbInt16 lRetrieveSw );
   xbInt16  GetPrevKey( void *vpTag, xbInt16 lRetrieveSw );
   xbBool   GetReuseEmptyNodesSw() const;
   xbBool   GetSortOrder( void *vpTag ) const;
   void     *GetTag( xbInt16 iTagNo ) const;
   void     *GetTag( xbString &sTagName ) const;
   xbInt16  GetTagCount() const;

   xbString &GetTagName( void *vpTag ) const;
   const char * GetTagName( void *vpTag, xbInt16 iOpt ) const;
   void     GetTagName( void *vpTag, xbString &sTagName );

   xbInt16  GetUnique( void *vpTag ) const;
   xbInt16  Reindex( void **vpTag );
   xbInt16  SetCurTag( xbInt16 iTagNo );
   xbInt16  SetCurTag( xbString &sTagName );
   void     SetReuseEmptyNodesSw( xbBool bReuse );

   void     TestStub( char *s, void *vpTag );

 protected:
   friend   class xbDbf;

   xbInt16  AddKey( void *vpTag, xbUInt32 ulRecNo );
   xbIxNode *AllocateIxNode( xbMdxTag * mpTag, xbUInt32 ulBufSize, xbUInt32 ulBlock2 );
   xbInt16  CheckForDupKey( void *vpTag );
   xbInt16  Close();
   xbInt16  CreateKey( void * vpTag, xbInt16 iOpt );
   xbInt16  DeleteFromNode( void *vpTag, xbIxNode * npNode, xbInt16 iSlotNo );
   xbInt16  DeleteKey( void *vpTag );

   xbInt16  DeleteTag( void *vpTag );

   xbInt16  FindKeyForCurRec( void *vpTag );
   xbInt16  GetKeySts( void *vpTag ) const;
   xbInt16  GetLastKey( xbUInt32 ulBlockNo, void *vpTag, xbInt16 lRetrieveSw );
   void     *GetTagTblPtr() const;
   void     Init( xbInt16 iOpt = 0 );
   xbInt16  InsertNodeI( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, xbUInt32 uiPtr );
   xbInt16  InsertNodeL( void *vpTag, xbIxNode *npNode, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr );
   xbInt16  KeyExists( void * );
   xbInt16  LoadTagTable();
   xbInt16  ReadHeadBlock(xbInt16 iOpt);   // read the header node of the disk file
   xbInt16  SplitNodeI( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, xbUInt32 uiPtr );
   xbInt16  SplitNodeL( void *vpTag, xbIxNode * npLeft, xbIxNode *npRight, xbInt16 iSlotNo, char *cpKeyBuf, xbUInt32 uiPtr );
   xbInt16  UpdateTagKey( char cAction, void *vpTag, xbUInt32 ulRecNo = 0 );
   xbInt16  WriteHeadBlock( xbInt16 iOption );

   #ifdef XB_DEBUG_SUPPORT
   xbInt16  DumpTagBlocks( xbInt16 iOpt = 1, void *vpTag = NULL  );
   xbInt16  DumpFreeBlocks( xbInt16 iOpt = 0 );
   xbInt16  DumpHeader( xbInt16 iOpt = 0, xbInt16 iFmtOpt = 0 );
   xbInt16  DumpIxForTag( void *vpTag, xbInt16 iOutputOpt );
   void     DumpIxNodeChain( void *vpTag, xbInt16 iOutputOpt ) const;
   #endif

 private:
   xbInt16  AddKeyNewRoot( xbMdxTag *npTag, xbIxNode *npLeft, xbIxNode *npRight );
   void     AppendNodeChain( void *vpTag, xbIxNode *npNode );
   xbUInt32 BlockToPage( xbUInt32 ulBlockNo );
   void     CalcBtreePointers();
   char     CalcTagKeyFmt( xbExp &exp );
   char     CalcTagFwdThread1();
   char     CalcTagFwdThread2();
   xbMdxTag *ClearTagTable();
   xbInt16  DumpBlock( xbInt16 iOpt, xbUInt32 ulBlockNo, xbMdxTag * mpTag );
   xbInt16  GetDbfPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulDbfPtr ) const;
   xbInt16  GetKeyPtr( void *vpTag, xbInt16 iKeyNo, xbIxNode *npNode, xbUInt32 &ulKeyPtr ) const;
   xbInt16  GetLastKeyForBlockNo( void *vpTag, xbUInt32 ulBlockNo, char *cpKeyBuf );
   xbInt16  HarvestEmptyNode( xbMdxTag *mpTag, xbIxNode *npNode, xbInt16 iOpt, xbBool bHarvestRoot = xbFalse );
   xbInt16  HarvestTagNodes( xbMdxTag *mpTag, xbBool bRecycleRoot = xbFalse );
   xbBool   IsLeaf( void *vpTag, xbIxNode *npNode ) const;
   xbInt16  KeySetPosAdd( xbMdxTag *mpTag, xbUInt32 ulAddKeyRecNo );
   xbInt16  KeySetPosDel( xbMdxTag *mpTag );
   xbInt16  LoadTagDetail( xbInt16 iOption, xbMdxTag *tte );
   xbUInt32 PageToBlock( xbUInt32 ulPageNo );
   xbInt16  TagSerialNo( xbInt16 iOption, xbMdxTag *mpTag );
   xbInt16  UpdateTagSize( xbMdxTag *mpTag, xbUInt32 ulTagSz );

   #ifdef XB_DEBUG_SUPPORT
   xbInt16  PrintKey( void *vpTag, xbIxNode *npNode, xbInt16 iKeyNo, xbInt16 iDepth, char cType, xbInt16 iOutputOpt );
   #endif

   // MDX File Header Fields
   char     cVersion;
   char     cCreateYY;
   char     cCreateMM;
   char     cCreateDD;
   xbString sFileName;
   xbInt16  iBlockFactor;          // 1-32 #of 512 byte segments in a block

   // use file version
   // xbInt16  iBlockSize;         // Stored at the xbFile level

   char     cProdIxFlag;
   char     cTagEntryCnt;
   xbInt16  iTagLen;
   xbInt16  iTagUseCnt;
   char     cNextTag;             // byte 28 +1
   char     c1B;                  // always 0x1B
   xbUInt32 ulPageCnt;            // number of 512 byte pages in the mdx file
   xbUInt32 ulFirstFreePage;      // page number corresponding to the next free block
   xbUInt32 ulNoOfBlockAvail;     // might be improperly named??  not sure how it is used
   char     cUpdateYY;
   char     cUpdateMM;
   char     cUpdateDD;
   // end of MDX Header fields



   xbMdxTag *mdxTagTbl;

   xbBool    bReuseEmptyNodes;     // Reuese empty MDX nodes when all keys deleted?
                                   // DBase 7.x and MS ODBC drivers do not reuse empty nodes, leaves them stranded in the file
                                   // Codebase 6.x reuses empty nodes.
                                   // Setting this to True will reuse empty nodes in the same manner Codebase 6.x reuses them.

};
#endif   /* XB_MDX_SUPPORT */
 

}        /* namespace xb     */
#endif   /* XB_INDEX_SUPPORT */
#endif   /* __XB_INDEX_H__   */