1 | /***************** Xindex C++ Class Xindex Code (.CPP) *****************/ |
2 | /* Name: XINDEX.CPP Version 3.0 */ |
3 | /* */ |
4 | /* (C) Copyright to the author Olivier BERTRAND 2004-2017 */ |
5 | /* */ |
6 | /* This file contains the class XINDEX implementation code. */ |
7 | /***********************************************************************/ |
8 | |
9 | /***********************************************************************/ |
10 | /* Include relevant sections of the System header files. */ |
11 | /***********************************************************************/ |
12 | #include "my_global.h" |
13 | #if defined(__WIN__) |
14 | #include <io.h> |
15 | #include <fcntl.h> |
16 | #include <errno.h> |
17 | //#include <windows.h> |
18 | #else // !__WIN__ |
19 | #if defined(UNIX) |
20 | #include <sys/types.h> |
21 | #include <sys/stat.h> |
22 | #include <errno.h> |
23 | #include <unistd.h> |
24 | #else // !UNIX |
25 | #include <io.h> |
26 | #endif // !UNIX |
27 | #include <fcntl.h> |
28 | #endif // !__WIN__ |
29 | |
30 | /***********************************************************************/ |
31 | /* Include required application header files */ |
32 | /* global.h is header containing all global Plug declarations. */ |
33 | /* plgdbsem.h is header containing the DB applic. declarations. */ |
34 | /* kindex.h is header containing the KINDEX class definition. */ |
35 | /***********************************************************************/ |
36 | #include "global.h" |
37 | #include "plgdbsem.h" |
38 | #include "osutil.h" |
39 | #include "maputil.h" |
40 | //nclude "filter.h" |
41 | #include "tabcol.h" |
42 | #include "xindex.h" |
43 | #include "xobject.h" |
44 | //nclude "scalfnc.h" |
45 | //nclude "array.h" |
46 | #include "filamtxt.h" |
47 | #include "tabdos.h" |
48 | #if defined(VCT_SUPPORT) |
49 | #include "tabvct.h" |
50 | #endif // VCT_SUPPORT |
51 | |
52 | /***********************************************************************/ |
53 | /* Macro or external routine definition */ |
54 | /***********************************************************************/ |
55 | #define NZ 8 |
56 | #define NW 5 |
57 | #define MAX_INDX 10 |
58 | #ifndef INVALID_SET_FILE_POINTER |
59 | #define INVALID_SET_FILE_POINTER 0xFFFFFFFF |
60 | #endif |
61 | |
62 | /***********************************************************************/ |
63 | /* DB external variables. */ |
64 | /***********************************************************************/ |
65 | extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ |
66 | #if defined(XMAP) |
67 | extern my_bool xmap; |
68 | #endif // XMAP |
69 | |
70 | /***********************************************************************/ |
71 | /* Last two parameters are true to enable type checking, and last one */ |
72 | /* to have rows filled by blanks to be compatible with QRY blocks. */ |
73 | /***********************************************************************/ |
74 | PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, |
75 | bool check = true, bool blank = true, bool un = false); |
76 | |
77 | /***********************************************************************/ |
78 | /* Check whether we have to create/update permanent indexes. */ |
79 | /***********************************************************************/ |
80 | int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add) |
81 | { |
82 | int rc; |
83 | PTABLE tablep; |
84 | PTDB tdbp; |
85 | PCATLG cat = PlgGetCatalog(g, true); |
86 | |
87 | /*********************************************************************/ |
88 | /* Open a new table in mode read and with only the keys columns. */ |
89 | /*********************************************************************/ |
90 | tablep = new(g) XTAB(name); |
91 | |
92 | if (!(tdbp = cat->GetTable(g, tablep))) |
93 | rc = RC_NF; |
94 | else if (!tdbp->GetDef()->Indexable()) { |
95 | sprintf(g->Message, MSG(TABLE_NO_INDEX), name); |
96 | rc = RC_NF; |
97 | } else if ((rc = ((PTDBASE)tdbp)->MakeIndex(g, pxdf, add)) == RC_INFO) |
98 | rc = RC_OK; // No or remote index |
99 | |
100 | return rc; |
101 | } // end of PlgMakeIndex |
102 | |
103 | /* -------------------------- Class INDEXDEF ------------------------- */ |
104 | |
105 | /***********************************************************************/ |
106 | /* INDEXDEF Constructor. */ |
107 | /***********************************************************************/ |
108 | INDEXDEF::INDEXDEF(char *name, bool uniq, int n) |
109 | { |
110 | //To_Def = NULL; |
111 | Next = NULL; |
112 | ToKeyParts = NULL; |
113 | Name = name; |
114 | Unique = uniq; |
115 | Invalid = false; |
116 | AutoInc = false; |
117 | Dynamic = false; |
118 | Mapped = false; |
119 | Nparts = 0; |
120 | ID = n; |
121 | //Offset = 0; |
122 | //Offhigh = 0; |
123 | //Size = 0; |
124 | MaxSame = 1; |
125 | } // end of INDEXDEF constructor |
126 | |
127 | /***********************************************************************/ |
128 | /* Set the max same values for each colum after making the index. */ |
129 | /***********************************************************************/ |
130 | void INDEXDEF::SetMxsame(PXINDEX x) |
131 | { |
132 | PKPDEF kdp; |
133 | PXCOL xcp; |
134 | |
135 | for (kdp = ToKeyParts, xcp = x->To_KeyCol; |
136 | kdp && xcp; kdp = kdp->Next, xcp = xcp->Next) |
137 | kdp->Mxsame = xcp->Mxs; |
138 | } // end of SetMxsame |
139 | |
140 | /* -------------------------- Class KPARTDEF ------------------------- */ |
141 | |
142 | /***********************************************************************/ |
143 | /* KPARTDEF Constructor. */ |
144 | /***********************************************************************/ |
145 | KPARTDEF::KPARTDEF(PSZ name, int n) |
146 | { |
147 | Next = NULL; |
148 | Name = name; |
149 | Mxsame = 0; |
150 | Ncol = n; |
151 | Klen = 0; |
152 | } // end of KPARTDEF constructor |
153 | |
154 | /* -------------------------- XXBASE Class --------------------------- */ |
155 | |
156 | /***********************************************************************/ |
157 | /* XXBASE public constructor. */ |
158 | /***********************************************************************/ |
159 | XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b), |
160 | To_Rec((int*&)Record.Memp) |
161 | { |
162 | Tbxp = tbxp; |
163 | Record = Nmblk; |
164 | Cur_K = -1; |
165 | Old_K = -1; |
166 | Num_K = 0; |
167 | Ndif = 0; |
168 | Bot = Top = Inf = Sup = 0; |
169 | Op = OP_EQ; |
170 | To_KeyCol = NULL; |
171 | Mul = false; |
172 | Srtd = false; |
173 | Dynamic = false; |
174 | Val_K = -1; |
175 | Nblk = Sblk = 0; |
176 | Thresh = 7; |
177 | ID = -1; |
178 | Nth = 0; |
179 | } // end of XXBASE constructor |
180 | |
181 | /***********************************************************************/ |
182 | /* Make file output of XINDEX contents. */ |
183 | /***********************************************************************/ |
184 | void XXBASE::Printf(PGLOBAL, FILE *f, uint n) |
185 | { |
186 | char m[64]; |
187 | |
188 | memset(m, ' ', n); // Make margin string |
189 | m[n] = '\0'; |
190 | fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n" , m, Tbxp, Num_K); |
191 | } // end of Printf |
192 | |
193 | /***********************************************************************/ |
194 | /* Make string output of XINDEX contents. */ |
195 | /***********************************************************************/ |
196 | void XXBASE::Prints(PGLOBAL, char *ps, uint z) |
197 | { |
198 | *ps = '\0'; |
199 | strncat(ps, "Xindex" , z); |
200 | } // end of Prints |
201 | |
202 | /* -------------------------- XINDEX Class --------------------------- */ |
203 | |
204 | /***********************************************************************/ |
205 | /* XINDEX public constructor. */ |
206 | /***********************************************************************/ |
207 | XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k) |
208 | : XXBASE(tdbp, !xdp->IsUnique()) |
209 | { |
210 | Xdp = xdp; |
211 | ID = xdp->GetID(); |
212 | Tdbp = tdbp; |
213 | X = pxp; |
214 | To_LastCol = NULL; |
215 | To_LastVal = NULL; |
216 | To_Cols = cp; |
217 | To_Vals = xp; |
218 | Mul = !xdp->IsUnique(); |
219 | Srtd = false; |
220 | Nk = xdp->GetNparts(); |
221 | Nval = (k) ? k : Nk; |
222 | Incr = 0; |
223 | //Defoff = xdp->GetOffset(); |
224 | //Defhigh = xdp->GetOffhigh(); |
225 | //Size = xdp->GetSize(); |
226 | MaxSame = xdp->GetMaxSame(); |
227 | } // end of XINDEX constructor |
228 | |
229 | /***********************************************************************/ |
230 | /* XINDEX Reset: re-initialize a Xindex block. */ |
231 | /***********************************************************************/ |
232 | void XINDEX::Reset(void) |
233 | { |
234 | for (PXCOL kp = To_KeyCol; kp; kp = kp->Next) |
235 | kp->Val_K = kp->Ndf; |
236 | |
237 | Cur_K = Num_K; |
238 | Old_K = -1; // Needed to avoid not setting CurBlk for Update |
239 | Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST : |
240 | (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ; |
241 | Nth = 0; |
242 | } // end of Reset |
243 | |
244 | /***********************************************************************/ |
245 | /* XINDEX Close: terminate index and free all allocated data. */ |
246 | /* Do not reset values that are used at return to make. */ |
247 | /***********************************************************************/ |
248 | void XINDEX::Close(void) |
249 | { |
250 | // Close file or view of file |
251 | if (X) |
252 | X->Close(); |
253 | |
254 | // De-allocate data |
255 | PlgDBfree(Record); |
256 | PlgDBfree(Index); |
257 | PlgDBfree(Offset); |
258 | |
259 | for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next) { |
260 | // Column values cannot be retrieved from key anymore |
261 | if (kcp->Colp) |
262 | kcp->Colp->SetKcol(NULL); |
263 | |
264 | // De-allocate Key data |
265 | kcp->FreeData(); |
266 | } // endfor kcp |
267 | |
268 | } // end of Close |
269 | |
270 | /***********************************************************************/ |
271 | /* XINDEX compare routine for C Quick/Insertion sort. */ |
272 | /***********************************************************************/ |
273 | int XINDEX::Qcompare(int *i1, int *i2) |
274 | { |
275 | register int k; |
276 | register PXCOL kcp; |
277 | |
278 | for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next) |
279 | if ((k = kcp->Compare(*i1, *i2))) |
280 | break; |
281 | |
282 | //num_comp++; |
283 | return k; |
284 | } // end of Qcompare |
285 | |
286 | /***********************************************************************/ |
287 | /* AddColumns: here we try to determine whether it is worthwhile to */ |
288 | /* add to the keys the values of the columns selected for this table. */ |
289 | /* Sure enough, it is done while records are read and permit to avoid */ |
290 | /* reading the table while doing the join (Dynamic index only) */ |
291 | /***********************************************************************/ |
292 | bool XINDEX::AddColumns(void) |
293 | { |
294 | if (!Dynamic) |
295 | return false; // Not applying to static index |
296 | else if (IsMul()) |
297 | return false; // Not done yet for multiple index |
298 | #if defined(VCT_SUPPORT) |
299 | else if (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit()) |
300 | return false; // This would require to read additional files |
301 | #endif // VCT_SUPPORT |
302 | else |
303 | return true; |
304 | |
305 | } // end of AddColumns |
306 | |
307 | /***********************************************************************/ |
308 | /* Make: Make and index on key column(s). */ |
309 | /***********************************************************************/ |
310 | bool XINDEX::Make(PGLOBAL g, PIXDEF sxp) |
311 | { |
312 | /*********************************************************************/ |
313 | /* Table can be accessed through an index. */ |
314 | /*********************************************************************/ |
315 | int k, nk = Nk, rc = RC_OK; |
316 | int *bof, i, j, n, ndf, nkey; |
317 | PKPDEF kdfp = Xdp->GetToKeyParts(); |
318 | bool brc = false; |
319 | PCOL colp; |
320 | PFIL filp = Tdbp->GetFilter(); |
321 | PXCOL kp, addcolp, prev = NULL, kcp = NULL; |
322 | //PDBUSER dup = (PDBUSER)g->Activityp->Aptr; |
323 | |
324 | #if defined(_DEBUG) |
325 | assert(X || Nk == 1); |
326 | #endif // _DEBUG |
327 | |
328 | /*********************************************************************/ |
329 | /* Allocate the storage that will contain the keys and the file */ |
330 | /* positions corresponding to them. */ |
331 | /*********************************************************************/ |
332 | if ((n = Tdbp->GetMaxSize(g)) < 0) |
333 | return true; |
334 | else if (!n) { |
335 | Num_K = Ndif = 0; |
336 | MaxSame = 1; |
337 | |
338 | // The if condition was suppressed because this may be an existing |
339 | // index that is now void because all table lines were deleted. |
340 | // if (sxp) |
341 | goto nox; // Truncate eventually existing index file |
342 | // else |
343 | // return false; |
344 | |
345 | } // endif n |
346 | |
347 | if (trace(1)) |
348 | htrc("XINDEX Make: n=%d\n" , n); |
349 | |
350 | // File position must be stored |
351 | Record.Size = n * sizeof(int); |
352 | |
353 | if (!PlgDBalloc(g, NULL, Record)) { |
354 | sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index" , n); |
355 | goto err; // Error |
356 | } // endif |
357 | |
358 | /*********************************************************************/ |
359 | /* Allocate the KXYCOL blocks used to store column values. */ |
360 | /*********************************************************************/ |
361 | for (k = 0; k < Nk; k++) { |
362 | colp = To_Cols[k]; |
363 | |
364 | if (!kdfp) { |
365 | sprintf(g->Message, MSG(INT_COL_ERROR), |
366 | (colp) ? colp->GetName() : "???" ); |
367 | goto err; // Error |
368 | } // endif kdfp |
369 | |
370 | kcp = new(g) KXYCOL(this); |
371 | |
372 | if (kcp->Init(g, colp, n, true, kdfp->Klen)) |
373 | goto err; // Error |
374 | |
375 | if (prev) { |
376 | kcp->Previous = prev; |
377 | prev->Next = kcp; |
378 | } else |
379 | To_KeyCol = kcp; |
380 | |
381 | prev = kcp; |
382 | kdfp = kdfp->Next; |
383 | } // endfor k |
384 | |
385 | To_LastCol = prev; |
386 | |
387 | if (AddColumns()) { |
388 | PCOL kolp = To_Cols[0]; // Temporary while imposing Nk = 1 |
389 | |
390 | i = 0; |
391 | |
392 | // Allocate the accompanying |
393 | for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) { |
394 | // Count how many columns to add |
395 | // for (k = 0; k < Nk; k++) |
396 | // if (colp == To_Cols[k]) |
397 | // break; |
398 | |
399 | // if (k == nk) |
400 | if (colp != kolp) |
401 | i++; |
402 | |
403 | } // endfor colp |
404 | |
405 | if (i && i < 10) // Should be a parameter |
406 | for (colp = Tbxp->GetColumns(); colp; colp = colp->GetNext()) { |
407 | // for (k = 0; k < Nk; k++) |
408 | // if (colp == To_Cols[k]) |
409 | // break; |
410 | |
411 | // if (k < nk) |
412 | if (colp == kolp) |
413 | continue; // This is a key column |
414 | |
415 | kcp = new(g) KXYCOL(this); |
416 | |
417 | if (kcp->Init(g, colp, n, true, 0)) |
418 | return true; |
419 | |
420 | if (trace(1)) |
421 | htrc("Adding colp=%p Buf_Type=%d size=%d\n" , |
422 | colp, colp->GetResultType(), n); |
423 | |
424 | nk++; |
425 | prev->Next = kcp; |
426 | prev = kcp; |
427 | } // endfor colp |
428 | |
429 | } // endif AddColumns |
430 | |
431 | #if 0 |
432 | /*********************************************************************/ |
433 | /* Get the starting information for progress. */ |
434 | /*********************************************************************/ |
435 | dup->Step = (char*)PlugSubAlloc(g, NULL, 128); |
436 | sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name); |
437 | dup->ProgMax = Tdbp->GetProgMax(g); |
438 | dup->ProgCur = 0; |
439 | #endif // 0 |
440 | |
441 | /*********************************************************************/ |
442 | /* Standard init: read the file and construct the index table. */ |
443 | /* Note: reading will be sequential as To_Kindex is not set. */ |
444 | /*********************************************************************/ |
445 | for (i = nkey = 0; rc != RC_EF; i++) { |
446 | #if 0 |
447 | if (!dup->Step) { |
448 | strcpy(g->Message, MSG(QUERY_CANCELLED)); |
449 | throw 99; |
450 | } // endif Step |
451 | #endif // 0 |
452 | |
453 | /*******************************************************************/ |
454 | /* Read a valid record from table file. */ |
455 | /*******************************************************************/ |
456 | rc = Tdbp->ReadDB(g); |
457 | |
458 | // Update progress information |
459 | // dup->ProgCur = Tdbp->GetProgCur(); |
460 | |
461 | // Check return code and do whatever must be done according to it |
462 | switch (rc) { |
463 | case RC_OK: |
464 | if (ApplyFilter(g, filp)) |
465 | break; |
466 | |
467 | // fall through |
468 | case RC_NF: |
469 | continue; |
470 | case RC_EF: |
471 | goto end_of_file; |
472 | default: |
473 | sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name); |
474 | goto err; |
475 | } // endswitch rc |
476 | |
477 | /*******************************************************************/ |
478 | /* Get and Store the file position of the last read record for */ |
479 | /* future direct access. */ |
480 | /*******************************************************************/ |
481 | if (nkey == n) { |
482 | sprintf(g->Message, MSG(TOO_MANY_KEYS), nkey); |
483 | return true; |
484 | } else |
485 | To_Rec[nkey] = Tdbp->GetRecpos(); |
486 | |
487 | if (trace(2)) |
488 | htrc("Make: To_Rec[%d]=%d\n" , nkey, To_Rec[nkey]); |
489 | |
490 | /*******************************************************************/ |
491 | /* Get the keys and place them in the key blocks. */ |
492 | /*******************************************************************/ |
493 | for (k = 0, kcp = To_KeyCol; |
494 | k < nk && kcp; |
495 | k++, kcp = kcp->Next) { |
496 | // colp = To_Cols[k]; |
497 | colp = kcp->Colp; |
498 | |
499 | if (!colp->GetStatus(BUF_READ)) |
500 | colp->ReadColumn(g); |
501 | else |
502 | colp->Reset(); |
503 | |
504 | kcp->SetValue(colp, nkey); |
505 | } // endfor k |
506 | |
507 | nkey++; // A new valid key was found |
508 | } // endfor i |
509 | |
510 | end_of_file: |
511 | |
512 | // Update progress information |
513 | //dup->ProgCur = Tdbp->GetProgMax(g); |
514 | |
515 | /*********************************************************************/ |
516 | /* Record the Index size and eventually resize memory allocation. */ |
517 | /*********************************************************************/ |
518 | if ((Num_K = nkey) < n) { |
519 | PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int)); |
520 | |
521 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
522 | kcp->ReAlloc(g, Num_K); |
523 | |
524 | } // endif Num_K |
525 | |
526 | /*********************************************************************/ |
527 | /* Sort the index so we can use an optimized Find algorithm. */ |
528 | /* Note: for a unique index we use the non conservative sort */ |
529 | /* version because normally all index values are different. */ |
530 | /* This was set at CSORT class construction. */ |
531 | /* For all indexes, an offset array is made so we can check the */ |
532 | /* uniqueness of unique indexes. */ |
533 | /*********************************************************************/ |
534 | Index.Size = Num_K * sizeof(int); |
535 | |
536 | if (!PlgDBalloc(g, NULL, Index)) { |
537 | sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index" , Num_K); |
538 | goto err; // Error |
539 | } // endif alloc |
540 | |
541 | Offset.Size = (Num_K + 1) * sizeof(int); |
542 | |
543 | if (!PlgDBalloc(g, NULL, Offset)) { |
544 | sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset" , Num_K + 1); |
545 | goto err; // Error |
546 | } // endif alloc |
547 | |
548 | // We must separate keys and added columns before sorting |
549 | addcolp = To_LastCol->Next; |
550 | To_LastCol->Next = NULL; |
551 | |
552 | // Call the sort program, it returns the number of distinct values |
553 | if ((Ndif = Qsort(g, Num_K)) < 0) |
554 | goto err; // Error during sort |
555 | |
556 | if (trace(1)) |
557 | htrc("Make: Nk=%d n=%d Num_K=%d Ndif=%d addcolp=%p BlkFil=%p X=%p\n" , |
558 | Nk, n, Num_K, Ndif, addcolp, Tdbp->To_BlkFil, X); |
559 | |
560 | // Check whether the unique index is unique indeed |
561 | if (!Mul) |
562 | if (Ndif < Num_K) { |
563 | strcpy(g->Message, MSG(INDEX_NOT_UNIQ)); |
564 | brc = true; |
565 | goto err; |
566 | } else |
567 | PlgDBfree(Offset); // Not used anymore |
568 | |
569 | // Restore kcp list |
570 | To_LastCol->Next = addcolp; |
571 | |
572 | // Use the index to physically reorder the xindex |
573 | Srtd = Reorder(g); |
574 | |
575 | if (Ndif < Num_K) { |
576 | // Resize the offset array |
577 | PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int)); |
578 | |
579 | // Initial value of MaxSame |
580 | MaxSame = Pof[1] - Pof[0]; |
581 | |
582 | // Resize the Key array by only keeping the distinct values |
583 | for (i = 1; i < Ndif; i++) { |
584 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
585 | kcp->Move(i, Pof[i]); |
586 | |
587 | MaxSame = MY_MAX(MaxSame, Pof[i + 1] - Pof[i]); |
588 | } // endfor i |
589 | |
590 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
591 | kcp->ReAlloc(g, Ndif); |
592 | |
593 | } else { |
594 | Mul = false; // Current index is unique |
595 | PlgDBfree(Offset); // Not used anymore |
596 | MaxSame = 1; // Reset it when remaking an index |
597 | } // endif Ndif |
598 | |
599 | /*********************************************************************/ |
600 | /* Now do the reduction of the index. Indeed a multi-column index */ |
601 | /* can be used for only some of the first columns. For instance if */ |
602 | /* an index is defined for column A, B, C PlugDB can use it for */ |
603 | /* only the column A or the columns A, B. */ |
604 | /* What we do here is to reduce the data so column A will contain */ |
605 | /* only the sorted distinct values of A, B will contain data such */ |
606 | /* as only distinct values of A,B are stored etc. */ |
607 | /* This implies that for each column set an offset array is made */ |
608 | /* except if the subset originally contains unique values. */ |
609 | /*********************************************************************/ |
610 | // Update progress information |
611 | //dup->Step = STEP(REDUCE_INDEX); |
612 | |
613 | ndf = Ndif; |
614 | To_LastCol->Mxs = MaxSame; |
615 | |
616 | for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) { |
617 | if (!(bof = kcp->MakeOffset(g, ndf))) |
618 | goto err; |
619 | else |
620 | *bof = 0; |
621 | |
622 | for (n = 0, i = j = 1; i < ndf; i++) |
623 | for (kp = kcp; kp; kp = kp->Previous) |
624 | if (kp->Compare(n, i)) { |
625 | // Values are not equal to last ones |
626 | bof[j++] = n = i; |
627 | break; |
628 | } // endif Compare |
629 | |
630 | if (j < ndf) { |
631 | // Sub-index is multiple |
632 | bof[j] = ndf; |
633 | ndf = j; // New number of distinct values |
634 | |
635 | // Resize the Key array by only keeping the distinct values |
636 | for (kp = kcp; kp; kp = kp->Previous) { |
637 | for (i = 1; i < ndf; i++) |
638 | kp->Move(i, bof[i]); |
639 | |
640 | kp->ReAlloc(g, ndf); |
641 | } // endif kcp |
642 | |
643 | // Resize the offset array |
644 | kcp->MakeOffset(g, ndf); |
645 | |
646 | // Calculate the max same value for this column |
647 | kcp->Mxs = ColMaxSame(kcp); |
648 | } else { |
649 | // Current sub-index is unique |
650 | kcp->MakeOffset(g, 0); // The offset is not used anymore |
651 | kcp->Mxs = 1; // Unique |
652 | } // endif j |
653 | |
654 | } // endfor kcp |
655 | |
656 | /*********************************************************************/ |
657 | /* For sorted columns and fixed record size, file position can be */ |
658 | /* calculated, so the Record array can be discarted. */ |
659 | /* Not true for DBF tables because of eventual soft deleted lines. */ |
660 | /* Note: for Num_K = 1 any non null value is Ok. */ |
661 | /*********************************************************************/ |
662 | if (Srtd && !filp && Tdbp->Ftype != RECFM_VAR |
663 | && Tdbp->Txfp->GetAmType() != TYPE_AM_DBF) { |
664 | Incr = (Num_K > 1) ? To_Rec[1] : Num_K; |
665 | PlgDBfree(Record); |
666 | } // endif Srtd |
667 | |
668 | /*********************************************************************/ |
669 | /* Check whether a two-tier find algorithm can be implemented. */ |
670 | /* It is currently implemented only for single key indexes. */ |
671 | /*********************************************************************/ |
672 | if (Nk == 1 && ndf >= 65536) { |
673 | // Implement a two-tier find algorithm |
674 | for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ; |
675 | |
676 | Nblk = (ndf -1) / Sblk + 1; |
677 | |
678 | if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk)) |
679 | goto err; // Error |
680 | |
681 | } // endif Num_K |
682 | |
683 | nox: |
684 | /*********************************************************************/ |
685 | /* No valid record read yet for secondary file. */ |
686 | /*********************************************************************/ |
687 | Cur_K = Num_K; |
688 | |
689 | /*********************************************************************/ |
690 | /* Save the xindex so it has not to be recalculated. */ |
691 | /*********************************************************************/ |
692 | if (X) { |
693 | if (SaveIndex(g, sxp)) |
694 | brc = true; |
695 | |
696 | } else { // Dynamic index |
697 | // Indicate that key column values can be found from KEYCOL's |
698 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
699 | kcp->Colp->SetKcol(kcp); |
700 | |
701 | Tdbp->SetFilter(NULL); // Not used anymore |
702 | } // endif X |
703 | |
704 | err: |
705 | // We don't need the index anymore |
706 | if (X || brc) |
707 | Close(); |
708 | |
709 | if (brc) |
710 | printf("%s\n" , g->Message); |
711 | |
712 | return brc; |
713 | } // end of Make |
714 | |
715 | /***********************************************************************/ |
716 | /* Return the max size of the intermediate column. */ |
717 | /***********************************************************************/ |
718 | int XINDEX::ColMaxSame(PXCOL kp) |
719 | { |
720 | int *kof, i, ck1, ck2, ckn = 1; |
721 | PXCOL kcp; |
722 | |
723 | // Calculate the max same value for this column |
724 | for (i = 0; i < kp->Ndf; i++) { |
725 | ck1 = i; |
726 | ck2 = i + 1; |
727 | |
728 | for (kcp = kp; kcp; kcp = kcp->Next) { |
729 | if (!(kof = (kcp->Next) ? kcp->Kof : Pof)) |
730 | break; |
731 | |
732 | ck1 = kof[ck1]; |
733 | ck2 = kof[ck2]; |
734 | } // endfor kcp |
735 | |
736 | ckn = MY_MAX(ckn, ck2 - ck1); |
737 | } // endfor i |
738 | |
739 | return ckn; |
740 | } // end of ColMaxSame |
741 | |
742 | /***********************************************************************/ |
743 | /* Reorder: use the sort index to reorder the data in storage so */ |
744 | /* it will be physically sorted and sort index can be removed. */ |
745 | /***********************************************************************/ |
746 | bool XINDEX::Reorder(PGLOBAL g __attribute__((unused))) |
747 | { |
748 | register int i, j, k, n; |
749 | bool sorted = true; |
750 | PXCOL kcp; |
751 | #if 0 |
752 | PDBUSER dup = (PDBUSER)g->Activityp->Aptr; |
753 | |
754 | if (Num_K > 500000) { |
755 | // Update progress information |
756 | dup->Step = STEP(REORDER_INDEX); |
757 | dup->ProgMax = Num_K; |
758 | dup->ProgCur = 0; |
759 | } else |
760 | dup = NULL; |
761 | #endif // 0 |
762 | |
763 | if (!Pex) |
764 | return Srtd; |
765 | |
766 | for (i = 0; i < Num_K; i++) { |
767 | if (Pex[i] == Num_K) { // Already moved |
768 | continue; |
769 | } else if (Pex[i] == i) { // Already placed |
770 | // if (dup) |
771 | // dup->ProgCur++; |
772 | |
773 | continue; |
774 | } // endif's Pex |
775 | |
776 | sorted = false; |
777 | |
778 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
779 | kcp->Save(i); |
780 | |
781 | n = To_Rec[i]; |
782 | |
783 | for (j = i;; j = k) { |
784 | k = Pex[j]; |
785 | Pex[j] = Num_K; // Mark position as set |
786 | |
787 | if (k == i) { |
788 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
789 | kcp->Restore(j); |
790 | |
791 | To_Rec[j] = n; |
792 | break; // end of loop |
793 | } else { |
794 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
795 | kcp->Move(j, k); // Move k to j |
796 | |
797 | To_Rec[j] = To_Rec[k]; |
798 | } // endif k |
799 | |
800 | // if (dup) |
801 | // dup->ProgCur++; |
802 | |
803 | } // endfor j |
804 | |
805 | } // endfor i |
806 | |
807 | // The index is not used anymore |
808 | PlgDBfree(Index); |
809 | return sorted; |
810 | } // end of Reorder |
811 | |
812 | /***********************************************************************/ |
813 | /* Save the index values for this table. */ |
814 | /* The problem here is to avoid name duplication, because more than */ |
815 | /* one data file can have the same name (but different types) and/or */ |
816 | /* the same data file can be used with different block sizes. This is */ |
817 | /* why we use Ofn that defaults to the file name but can be set to a */ |
818 | /* different name if necessary. */ |
819 | /***********************************************************************/ |
820 | bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp) |
821 | { |
822 | PCSZ ftype; |
823 | char fn[_MAX_PATH]; |
824 | int n[NZ], nof = (Mul) ? (Ndif + 1) : 0; |
825 | int id = -1, size = 0; |
826 | bool sep, rc = false; |
827 | PXCOL kcp = To_KeyCol; |
828 | PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; |
829 | //PDBUSER dup = PlgGetUser(g); |
830 | |
831 | //dup->Step = STEP(SAVING_INDEX); |
832 | //dup->ProgMax = 15 + 16 * Nk; |
833 | //dup->ProgCur = 0; |
834 | |
835 | switch (Tdbp->Ftype) { |
836 | case RECFM_VAR: ftype = ".dnx" ; break; |
837 | case RECFM_FIX: ftype = ".fnx" ; break; |
838 | case RECFM_BIN: ftype = ".bnx" ; break; |
839 | case RECFM_VCT: ftype = ".vnx" ; break; |
840 | case RECFM_DBF: ftype = ".dbx" ; break; |
841 | default: |
842 | sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); |
843 | return true; |
844 | } // endswitch Ftype |
845 | |
846 | if ((sep = defp->GetBoolCatInfo("SepIndex" , false))) { |
847 | // Index is saved in a separate file |
848 | #if defined(__WIN__) |
849 | char drive[_MAX_DRIVE]; |
850 | #else |
851 | char *drive = NULL; |
852 | #endif |
853 | char direc[_MAX_DIR]; |
854 | char fname[_MAX_FNAME]; |
855 | |
856 | _splitpath(defp->GetOfn(), drive, direc, fname, NULL); |
857 | strcat(strcat(fname, "_" ), Xdp->GetName()); |
858 | _makepath(fn, drive, direc, fname, ftype); |
859 | sxp = NULL; |
860 | } else { |
861 | id = ID; |
862 | strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); |
863 | } // endif sep |
864 | |
865 | PlugSetPath(fn, fn, Tdbp->GetPath()); |
866 | |
867 | if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) { |
868 | printf("%s\n" , g->Message); |
869 | return true; |
870 | } // endif Open |
871 | |
872 | if (!Ndif) |
873 | goto end; // Void index |
874 | |
875 | /*********************************************************************/ |
876 | /* Write the index values on the index file. */ |
877 | /*********************************************************************/ |
878 | n[0] = ID + MAX_INDX; // To check validity |
879 | n[1] = Nk; // The number of indexed columns |
880 | n[2] = nof; // The offset array size or 0 |
881 | n[3] = Num_K; // The index size |
882 | n[4] = Incr; // Increment of record positions |
883 | n[5] = Nblk; n[6] = Sblk; |
884 | n[7] = Srtd ? 1 : 0; // Values are sorted in the file |
885 | |
886 | if (trace(1)) { |
887 | htrc("Saving index %s\n" , Xdp->GetName()); |
888 | htrc("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d Srtd=%d\n" , |
889 | ID, Nk, nof, Num_K, Incr, Nblk, Sblk, Srtd); |
890 | } // endif trace |
891 | |
892 | size = X->Write(g, n, NZ, sizeof(int), rc); |
893 | //dup->ProgCur = 1; |
894 | |
895 | if (Mul) // Write the offset array |
896 | size += X->Write(g, Pof, nof, sizeof(int), rc); |
897 | |
898 | //dup->ProgCur = 5; |
899 | |
900 | if (!Incr) // Write the record position array(s) |
901 | size += X->Write(g, To_Rec, Num_K, sizeof(int), rc); |
902 | |
903 | //dup->ProgCur = 15; |
904 | |
905 | for (; kcp; kcp = kcp->Next) { |
906 | n[0] = kcp->Ndf; // Number of distinct sub-values |
907 | n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique |
908 | n[2] = (kcp == To_KeyCol) ? Nblk : 0; |
909 | n[3] = kcp->Klen; // To be checked later |
910 | n[4] = kcp->Type; // To be checked later |
911 | |
912 | size += X->Write(g, n, NW, sizeof(int), rc); |
913 | // dup->ProgCur += 1; |
914 | |
915 | if (n[2]) |
916 | size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc); |
917 | |
918 | // dup->ProgCur += 5; |
919 | |
920 | size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc); |
921 | // dup->ProgCur += 5; |
922 | |
923 | if (n[1]) |
924 | size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc); |
925 | |
926 | // dup->ProgCur += 5; |
927 | } // endfor kcp |
928 | |
929 | if (trace(1)) |
930 | htrc("Index %s saved, Size=%d\n" , Xdp->GetName(), size); |
931 | |
932 | end: |
933 | X->Close(fn, id); |
934 | return rc; |
935 | } // end of SaveIndex |
936 | |
937 | /***********************************************************************/ |
938 | /* Init: Open and Initialize a Key Index. */ |
939 | /***********************************************************************/ |
940 | bool XINDEX::Init(PGLOBAL g) |
941 | { |
942 | #if defined(XMAP) |
943 | if (xmap) |
944 | return MapInit(g); |
945 | #endif // XMAP |
946 | |
947 | /*********************************************************************/ |
948 | /* Table will be accessed through an index table. */ |
949 | /* If sorting is required, this will be done later. */ |
950 | /*********************************************************************/ |
951 | PCSZ ftype; |
952 | char fn[_MAX_PATH]; |
953 | int k, n, nv[NZ], id = -1; |
954 | bool estim = false; |
955 | PCOL colp; |
956 | PXCOL prev = NULL, kcp = NULL; |
957 | PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; |
958 | |
959 | /*********************************************************************/ |
960 | /* Get the estimated table size. */ |
961 | /* Note: for fixed tables we must use cardinality to avoid the call */ |
962 | /* to MaxBlkSize that could reduce the cardinality value. */ |
963 | /*********************************************************************/ |
964 | if (Tdbp->Cardinality(NULL)) { |
965 | // For DBF tables, Cardinality includes bad or soft deleted lines |
966 | // that are not included in the index, and can be larger then the |
967 | // index size. |
968 | estim = (Tdbp->Ftype == RECFM_DBF || Tdbp->Txfp->GetAmType() == TYPE_AM_ZIP); |
969 | n = Tdbp->Cardinality(g); // n is exact table size |
970 | } else { |
971 | // Variable table not optimized |
972 | estim = true; // n is an estimate of the size |
973 | n = Tdbp->GetMaxSize(g); |
974 | } // endif Cardinality |
975 | |
976 | if (n <= 0) |
977 | return !(n == 0); // n < 0 error, n = 0 void table |
978 | |
979 | /*********************************************************************/ |
980 | /* Get the first key column. */ |
981 | /*********************************************************************/ |
982 | if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) { |
983 | strcpy(g->Message, MSG(NO_KEY_COL)); |
984 | return true; // Error |
985 | } else |
986 | colp = To_Cols[0]; |
987 | |
988 | switch (Tdbp->Ftype) { |
989 | case RECFM_VAR: ftype = ".dnx" ; break; |
990 | case RECFM_FIX: ftype = ".fnx" ; break; |
991 | case RECFM_BIN: ftype = ".bnx" ; break; |
992 | case RECFM_VCT: ftype = ".vnx" ; break; |
993 | case RECFM_DBF: ftype = ".dbx" ; break; |
994 | default: |
995 | sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); |
996 | return true; |
997 | } // endswitch Ftype |
998 | |
999 | if (defp->SepIndex()) { |
1000 | // Index was saved in a separate file |
1001 | #if defined(__WIN__) |
1002 | char drive[_MAX_DRIVE]; |
1003 | #else |
1004 | char *drive = NULL; |
1005 | #endif |
1006 | char direc[_MAX_DIR]; |
1007 | char fname[_MAX_FNAME]; |
1008 | |
1009 | _splitpath(defp->GetOfn(), drive, direc, fname, NULL); |
1010 | strcat(strcat(fname, "_" ), Xdp->GetName()); |
1011 | _makepath(fn, drive, direc, fname, ftype); |
1012 | } else { |
1013 | id = ID; |
1014 | strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); |
1015 | } // endif sep |
1016 | |
1017 | PlugSetPath(fn, fn, Tdbp->GetPath()); |
1018 | |
1019 | if (trace(1)) |
1020 | htrc("Index %s file: %s\n" , Xdp->GetName(), fn); |
1021 | |
1022 | /*********************************************************************/ |
1023 | /* Open the index file and check its validity. */ |
1024 | /*********************************************************************/ |
1025 | if (X->Open(g, fn, id, MODE_READ)) |
1026 | goto err; // No saved values |
1027 | |
1028 | // Now start the reading process. |
1029 | if (X->Read(g, nv, NZ - 1, sizeof(int))) |
1030 | goto err; |
1031 | |
1032 | if (nv[0] >= MAX_INDX) { |
1033 | // New index format |
1034 | if (X->Read(g, nv + 7, 1, sizeof(int))) |
1035 | goto err; |
1036 | |
1037 | Srtd = nv[7] != 0; |
1038 | nv[0] -= MAX_INDX; |
1039 | } else |
1040 | Srtd = false; |
1041 | |
1042 | if (trace(1)) |
1043 | htrc("nv=%d %d %d %d %d %d %d (%d)\n" , |
1044 | nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd); |
1045 | |
1046 | // The test on ID was suppressed because MariaDB can change an index ID |
1047 | // when other indexes are added or deleted |
1048 | if (/*nv[0] != ID ||*/ nv[1] != Nk) { |
1049 | sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); |
1050 | |
1051 | if (trace(1)) |
1052 | htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n" , nv[0], ID, nv[1], Nk); |
1053 | |
1054 | goto err; |
1055 | } // endif |
1056 | |
1057 | if (nv[2]) { |
1058 | Mul = true; |
1059 | Ndif = nv[2]; |
1060 | |
1061 | // Allocate the storage that will contain the offset array |
1062 | Offset.Size = Ndif * sizeof(int); |
1063 | |
1064 | if (!PlgDBalloc(g, NULL, Offset)) { |
1065 | sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset" , Ndif); |
1066 | goto err; |
1067 | } // endif |
1068 | |
1069 | if (X->Read(g, Pof, Ndif, sizeof(int))) |
1070 | goto err; |
1071 | |
1072 | Ndif--; // nv[2] is offset size, equal to Ndif + 1 |
1073 | } else { |
1074 | Mul = false; |
1075 | Ndif = nv[3]; |
1076 | } // endif nv[2] |
1077 | |
1078 | if (nv[3] < n && estim) |
1079 | n = nv[3]; // n was just an evaluated max value |
1080 | |
1081 | if (nv[3] != n) { |
1082 | sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); |
1083 | goto err; |
1084 | } // endif |
1085 | |
1086 | Num_K = nv[3]; |
1087 | Incr = nv[4]; |
1088 | Nblk = nv[5]; |
1089 | Sblk = nv[6]; |
1090 | |
1091 | if (!Incr) { |
1092 | /*******************************************************************/ |
1093 | /* Allocate the storage that will contain the file positions. */ |
1094 | /*******************************************************************/ |
1095 | Record.Size = Num_K * sizeof(int); |
1096 | |
1097 | if (!PlgDBalloc(g, NULL, Record)) { |
1098 | sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index" , Num_K); |
1099 | goto err; |
1100 | } // endif |
1101 | |
1102 | if (X->Read(g, To_Rec, Num_K, sizeof(int))) |
1103 | goto err; |
1104 | |
1105 | } else |
1106 | Srtd = true; // Sorted positions can be calculated |
1107 | |
1108 | /*********************************************************************/ |
1109 | /* Allocate the KXYCOL blocks used to store column values. */ |
1110 | /*********************************************************************/ |
1111 | for (k = 0; k < Nk; k++) { |
1112 | if (k == Nval) |
1113 | To_LastVal = prev; |
1114 | |
1115 | if (X->Read(g, nv, NW, sizeof(int))) |
1116 | goto err; |
1117 | |
1118 | colp = To_Cols[k]; |
1119 | |
1120 | if (nv[4] != colp->GetResultType() || !colp->GetValue() || |
1121 | (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { |
1122 | sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); |
1123 | goto err; // Error |
1124 | } // endif GetKey |
1125 | |
1126 | kcp = new(g) KXYCOL(this); |
1127 | |
1128 | if (kcp->Init(g, colp, nv[0], true, (int)nv[3])) |
1129 | goto err; // Error |
1130 | |
1131 | /*******************************************************************/ |
1132 | /* Read the index values from the index file. */ |
1133 | /*******************************************************************/ |
1134 | if (k == 0 && Nblk) { |
1135 | if (kcp->MakeBlockArray(g, Nblk, 0)) |
1136 | goto err; |
1137 | |
1138 | // Read block values |
1139 | if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen)) |
1140 | goto err; |
1141 | |
1142 | } // endif Nblk |
1143 | |
1144 | // Read the entire (small) index |
1145 | if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen)) |
1146 | goto err; |
1147 | |
1148 | if (nv[1]) { |
1149 | if (!kcp->MakeOffset(g, nv[1] - 1)) |
1150 | goto err; |
1151 | |
1152 | // Read the offset array |
1153 | if (X->Read(g, kcp->Kof, nv[1], sizeof(int))) |
1154 | goto err; |
1155 | |
1156 | } // endif n[1] |
1157 | |
1158 | if (!kcp->Prefix) |
1159 | // Indicate that the key column value can be found from KXYCOL |
1160 | colp->SetKcol(kcp); |
1161 | |
1162 | if (prev) { |
1163 | kcp->Previous = prev; |
1164 | prev->Next = kcp; |
1165 | } else |
1166 | To_KeyCol = kcp; |
1167 | |
1168 | prev = kcp; |
1169 | } // endfor k |
1170 | |
1171 | To_LastCol = prev; |
1172 | |
1173 | if (Mul && prev) { |
1174 | // Last key offset is the index offset |
1175 | kcp->Koff = Offset; |
1176 | kcp->Koff.Sub = true; |
1177 | } // endif Mul |
1178 | |
1179 | X->Close(); |
1180 | |
1181 | /*********************************************************************/ |
1182 | /* No valid record read yet for secondary file. */ |
1183 | /*********************************************************************/ |
1184 | Cur_K = Num_K; |
1185 | return false; |
1186 | |
1187 | err: |
1188 | Close(); |
1189 | return true; |
1190 | } // end of Init |
1191 | |
1192 | #if defined(XMAP) |
1193 | /***********************************************************************/ |
1194 | /* Init: Open and Initialize a Key Index. */ |
1195 | /***********************************************************************/ |
1196 | bool XINDEX::MapInit(PGLOBAL g) |
1197 | { |
1198 | /*********************************************************************/ |
1199 | /* Table will be accessed through an index table. */ |
1200 | /* If sorting is required, this will be done later. */ |
1201 | /*********************************************************************/ |
1202 | const char *ftype; |
1203 | BYTE *mbase; |
1204 | char fn[_MAX_PATH]; |
1205 | int *nv, nv0, k, n, id = -1; |
1206 | bool estim; |
1207 | PCOL colp; |
1208 | PXCOL prev = NULL, kcp = NULL; |
1209 | PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; |
1210 | PDBUSER dup = PlgGetUser(g); |
1211 | |
1212 | /*********************************************************************/ |
1213 | /* Get the estimated table size. */ |
1214 | /* Note: for fixed tables we must use cardinality to avoid the call */ |
1215 | /* to MaxBlkSize that could reduce the cardinality value. */ |
1216 | /*********************************************************************/ |
1217 | if (Tdbp->Cardinality(NULL)) { |
1218 | // For DBF tables, Cardinality includes bad or soft deleted lines |
1219 | // that are not included in the index, and can be larger then the |
1220 | // index size. |
1221 | estim = (Tdbp->Ftype == RECFM_DBF); |
1222 | n = Tdbp->Cardinality(g); // n is exact table size |
1223 | } else { |
1224 | // Variable table not optimized |
1225 | estim = true; // n is an estimate of the size |
1226 | n = Tdbp->GetMaxSize(g); |
1227 | } // endif Cardinality |
1228 | |
1229 | if (n <= 0) |
1230 | return !(n == 0); // n < 0 error, n = 0 void table |
1231 | |
1232 | /*********************************************************************/ |
1233 | /* Get the first key column. */ |
1234 | /*********************************************************************/ |
1235 | if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) { |
1236 | strcpy(g->Message, MSG(NO_KEY_COL)); |
1237 | return true; // Error |
1238 | } else |
1239 | colp = To_Cols[0]; |
1240 | |
1241 | switch (Tdbp->Ftype) { |
1242 | case RECFM_VAR: ftype = ".dnx" ; break; |
1243 | case RECFM_FIX: ftype = ".fnx" ; break; |
1244 | case RECFM_BIN: ftype = ".bnx" ; break; |
1245 | case RECFM_VCT: ftype = ".vnx" ; break; |
1246 | case RECFM_DBF: ftype = ".dbx" ; break; |
1247 | default: |
1248 | sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); |
1249 | return true; |
1250 | } // endswitch Ftype |
1251 | |
1252 | if (defp->SepIndex()) { |
1253 | // Index was save in a separate file |
1254 | #if defined(__WIN__) |
1255 | char drive[_MAX_DRIVE]; |
1256 | #else |
1257 | char *drive = NULL; |
1258 | #endif |
1259 | char direc[_MAX_DIR]; |
1260 | char fname[_MAX_FNAME]; |
1261 | |
1262 | _splitpath(defp->GetOfn(), drive, direc, fname, NULL); |
1263 | strcat(strcat(fname, "_" ), Xdp->GetName()); |
1264 | _makepath(fn, drive, direc, fname, ftype); |
1265 | } else { |
1266 | id = ID; |
1267 | strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); |
1268 | } // endif SepIndex |
1269 | |
1270 | PlugSetPath(fn, fn, Tdbp->GetPath()); |
1271 | |
1272 | if (trace(1)) |
1273 | htrc("Index %s file: %s\n" , Xdp->GetName(), fn); |
1274 | |
1275 | /*********************************************************************/ |
1276 | /* Get a view on the part of the index file containing this index. */ |
1277 | /*********************************************************************/ |
1278 | if (!(mbase = (BYTE*)X->FileView(g, fn))) |
1279 | goto err; |
1280 | |
1281 | if (id >= 0) { |
1282 | // Get offset from the header |
1283 | IOFF *noff = (IOFF*)mbase; |
1284 | |
1285 | // Position the memory base at the offset of this index |
1286 | mbase += noff[id].v.Low; |
1287 | } // endif id |
1288 | |
1289 | // Now start the mapping process. |
1290 | nv = (int*)mbase; |
1291 | |
1292 | if (nv[0] >= MAX_INDX) { |
1293 | // New index format |
1294 | Srtd = nv[7] != 0; |
1295 | nv0 = nv[0] - MAX_INDX; |
1296 | mbase += NZ * sizeof(int); |
1297 | } else { |
1298 | Srtd = false; |
1299 | mbase += (NZ - 1) * sizeof(int); |
1300 | nv0 = nv[0]; |
1301 | } // endif nv |
1302 | |
1303 | if (trace(1)) |
1304 | htrc("nv=%d %d %d %d %d %d %d %d\n" , |
1305 | nv0, nv[1], nv[2], nv[3], nv[4], nv[5], nv[6], Srtd); |
1306 | |
1307 | // The test on ID was suppressed because MariaDB can change an index ID |
1308 | // when other indexes are added or deleted |
1309 | if (/*nv0 != ID ||*/ nv[1] != Nk) { |
1310 | // Not this index |
1311 | sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); |
1312 | |
1313 | if (trace(1)) |
1314 | htrc("nv0=%d ID=%d nv[1]=%d Nk=%d\n" , nv0, ID, nv[1], Nk); |
1315 | |
1316 | goto err; |
1317 | } // endif nv |
1318 | |
1319 | if (nv[2]) { |
1320 | // Set the offset array memory block |
1321 | Offset.Memp = mbase; |
1322 | Offset.Size = nv[2] * sizeof(int); |
1323 | Offset.Sub = true; |
1324 | Mul = true; |
1325 | Ndif = nv[2] - 1; |
1326 | mbase += Offset.Size; |
1327 | } else { |
1328 | Mul = false; |
1329 | Ndif = nv[3]; |
1330 | } // endif nv[2] |
1331 | |
1332 | if (nv[3] < n && estim) |
1333 | n = nv[3]; // n was just an evaluated max value |
1334 | |
1335 | if (nv[3] != n) { |
1336 | sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); |
1337 | goto err; |
1338 | } // endif |
1339 | |
1340 | Num_K = nv[3]; |
1341 | Incr = nv[4]; |
1342 | Nblk = nv[5]; |
1343 | Sblk = nv[6]; |
1344 | |
1345 | if (!Incr) { |
1346 | /*******************************************************************/ |
1347 | /* Point to the storage that contains the file positions. */ |
1348 | /*******************************************************************/ |
1349 | Record.Size = Num_K * sizeof(int); |
1350 | Record.Memp = mbase; |
1351 | Record.Sub = true; |
1352 | mbase += Record.Size; |
1353 | } else |
1354 | Srtd = true; // Sorted positions can be calculated |
1355 | |
1356 | /*********************************************************************/ |
1357 | /* Allocate the KXYCOL blocks used to store column values. */ |
1358 | /*********************************************************************/ |
1359 | for (k = 0; k < Nk; k++) { |
1360 | if (k == Nval) |
1361 | To_LastVal = prev; |
1362 | |
1363 | nv = (int*)mbase; |
1364 | mbase += (NW * sizeof(int)); |
1365 | |
1366 | colp = To_Cols[k]; |
1367 | |
1368 | if (nv[4] != colp->GetResultType() || !colp->GetValue() || |
1369 | (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { |
1370 | sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); |
1371 | goto err; // Error |
1372 | } // endif GetKey |
1373 | |
1374 | kcp = new(g) KXYCOL(this); |
1375 | |
1376 | if (!(mbase = kcp->MapInit(g, colp, nv, mbase))) |
1377 | goto err; |
1378 | |
1379 | if (!kcp->Prefix) |
1380 | // Indicate that the key column value can be found from KXYCOL |
1381 | colp->SetKcol(kcp); |
1382 | |
1383 | if (prev) { |
1384 | kcp->Previous = prev; |
1385 | prev->Next = kcp; |
1386 | } else |
1387 | To_KeyCol = kcp; |
1388 | |
1389 | prev = kcp; |
1390 | } // endfor k |
1391 | |
1392 | To_LastCol = prev; |
1393 | |
1394 | if (Mul && prev) |
1395 | // Last key offset is the index offset |
1396 | kcp->Koff = Offset; |
1397 | |
1398 | /*********************************************************************/ |
1399 | /* No valid record read yet for secondary file. */ |
1400 | /*********************************************************************/ |
1401 | Cur_K = Num_K; |
1402 | return false; |
1403 | |
1404 | err: |
1405 | Close(); |
1406 | return true; |
1407 | } // end of MapInit |
1408 | #endif // XMAP |
1409 | |
1410 | /***********************************************************************/ |
1411 | /* Get Ndif and Num_K from the index file. */ |
1412 | /***********************************************************************/ |
1413 | bool XINDEX::GetAllSizes(PGLOBAL g,/* int &ndif,*/ int &numk) |
1414 | { |
1415 | PCSZ ftype; |
1416 | char fn[_MAX_PATH]; |
1417 | int nv[NZ], id = -1; // n |
1418 | //bool estim = false; |
1419 | bool rc = true; |
1420 | PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; |
1421 | |
1422 | // ndif = numk = 0; |
1423 | numk = 0; |
1424 | |
1425 | #if 0 |
1426 | /*********************************************************************/ |
1427 | /* Get the estimated table size. */ |
1428 | /* Note: for fixed tables we must use cardinality to avoid the call */ |
1429 | /* to MaxBlkSize that could reduce the cardinality value. */ |
1430 | /*********************************************************************/ |
1431 | if (Tdbp->Cardinality(NULL)) { |
1432 | // For DBF tables, Cardinality includes bad or soft deleted lines |
1433 | // that are not included in the index, and can be larger then the |
1434 | // index size. |
1435 | estim = (Tdbp->Ftype == RECFM_DBF); |
1436 | n = Tdbp->Cardinality(g); // n is exact table size |
1437 | } else { |
1438 | // Variable table not optimized |
1439 | estim = true; // n is an estimate of the size |
1440 | n = Tdbp->GetMaxSize(g); |
1441 | } // endif Cardinality |
1442 | |
1443 | if (n <= 0) |
1444 | return !(n == 0); // n < 0 error, n = 0 void table |
1445 | |
1446 | /*********************************************************************/ |
1447 | /* Check the key part number. */ |
1448 | /*********************************************************************/ |
1449 | if (!Nk) { |
1450 | strcpy(g->Message, MSG(NO_KEY_COL)); |
1451 | return true; // Error |
1452 | } // endif Nk |
1453 | #endif // 0 |
1454 | |
1455 | switch (Tdbp->Ftype) { |
1456 | case RECFM_VAR: ftype = ".dnx" ; break; |
1457 | case RECFM_FIX: ftype = ".fnx" ; break; |
1458 | case RECFM_BIN: ftype = ".bnx" ; break; |
1459 | case RECFM_VCT: ftype = ".vnx" ; break; |
1460 | case RECFM_DBF: ftype = ".dbx" ; break; |
1461 | default: |
1462 | sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); |
1463 | return true; |
1464 | } // endswitch Ftype |
1465 | |
1466 | if (defp->SepIndex()) { |
1467 | // Index was saved in a separate file |
1468 | #if defined(__WIN__) |
1469 | char drive[_MAX_DRIVE]; |
1470 | #else |
1471 | char *drive = NULL; |
1472 | #endif |
1473 | char direc[_MAX_DIR]; |
1474 | char fname[_MAX_FNAME]; |
1475 | |
1476 | _splitpath(defp->GetOfn(), drive, direc, fname, NULL); |
1477 | strcat(strcat(fname, "_" ), Xdp->GetName()); |
1478 | _makepath(fn, drive, direc, fname, ftype); |
1479 | } else { |
1480 | id = ID; |
1481 | strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); |
1482 | } // endif sep |
1483 | |
1484 | PlugSetPath(fn, fn, Tdbp->GetPath()); |
1485 | |
1486 | if (trace(1)) |
1487 | htrc("Index %s file: %s\n" , Xdp->GetName(), fn); |
1488 | |
1489 | /*********************************************************************/ |
1490 | /* Open the index file and check its validity. */ |
1491 | /*********************************************************************/ |
1492 | if (X->Open(g, fn, id, MODE_READ)) |
1493 | goto err; // No saved values |
1494 | |
1495 | // Get offset from XDB file |
1496 | //if (X->Seek(g, Defoff, Defhigh, SEEK_SET)) |
1497 | // goto err; |
1498 | |
1499 | // Now start the reading process. |
1500 | if (X->Read(g, nv, NZ, sizeof(int))) |
1501 | goto err; |
1502 | |
1503 | if (trace(1)) |
1504 | htrc("nv=%d %d %d %d\n" , nv[0], nv[1], nv[2], nv[3]); |
1505 | |
1506 | // The test on ID was suppressed because MariaDB can change an index ID |
1507 | // when other indexes are added or deleted |
1508 | if (/*nv[0] != ID ||*/ nv[1] != Nk) { |
1509 | sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); |
1510 | |
1511 | if (trace(1)) |
1512 | htrc("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n" , nv[0], ID, nv[1], Nk); |
1513 | |
1514 | goto err; |
1515 | } // endif |
1516 | |
1517 | #if 0 |
1518 | if (nv[2]) { |
1519 | Mul = true; |
1520 | Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1 |
1521 | } else { |
1522 | Mul = false; |
1523 | Ndif = nv[3]; |
1524 | } // endif nv[2] |
1525 | |
1526 | if (nv[3] < n && estim) |
1527 | n = nv[3]; // n was just an evaluated max value |
1528 | |
1529 | if (nv[3] != n) { |
1530 | sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); |
1531 | goto err; |
1532 | } // endif |
1533 | #endif // 0 |
1534 | |
1535 | Num_K = nv[3]; |
1536 | |
1537 | #if 0 |
1538 | if (Nk > 1) { |
1539 | if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR)) |
1540 | goto err; |
1541 | |
1542 | if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR)) |
1543 | goto err; |
1544 | |
1545 | if (X->Read(g, nv, NW, sizeof(int))) |
1546 | goto err; |
1547 | |
1548 | PCOL colp = *To_Cols; |
1549 | |
1550 | if (nv[4] != colp->GetResultType() || |
1551 | (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { |
1552 | sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); |
1553 | goto err; // Error |
1554 | } // endif GetKey |
1555 | |
1556 | Ndif = nv[0]; |
1557 | } // endif Nk |
1558 | #endif // 0 |
1559 | |
1560 | /*********************************************************************/ |
1561 | /* Set size values. */ |
1562 | /*********************************************************************/ |
1563 | //ndif = Ndif; |
1564 | numk = Num_K; |
1565 | rc = false; |
1566 | |
1567 | err: |
1568 | X->Close(); |
1569 | return rc; |
1570 | } // end of GetAllSizes |
1571 | |
1572 | /***********************************************************************/ |
1573 | /* RANGE: Tell how many records exist for a given value, for an array */ |
1574 | /* of values, or in a given value range. */ |
1575 | /***********************************************************************/ |
1576 | int XINDEX::Range(PGLOBAL g, int limit, bool incl) |
1577 | { |
1578 | int i, k, n = 0; |
1579 | PXOB *xp = To_Vals; |
1580 | PXCOL kp = To_KeyCol; |
1581 | OPVAL op = Op; |
1582 | |
1583 | switch (limit) { |
1584 | case 1: Op = (incl) ? OP_GE : OP_GT; break; |
1585 | case 2: Op = (incl) ? OP_GT : OP_GE; break; |
1586 | default: return 0; |
1587 | } // endswitch limit |
1588 | |
1589 | /*********************************************************************/ |
1590 | /* Currently only range of constant values with an EQ operator is */ |
1591 | /* implemented. Find the number of rows for each given values. */ |
1592 | /*********************************************************************/ |
1593 | if (xp[0]->GetType() == TYPE_CONST) { |
1594 | for (i = 0; kp; kp = kp->Next) { |
1595 | kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix); |
1596 | if (++i == Nval) break; |
1597 | } // endfor kp |
1598 | |
1599 | if ((k = FastFind()) < Num_K) |
1600 | n = k; |
1601 | // if (limit) |
1602 | // n = (Mul) ? k : kp->Val_K; |
1603 | // else |
1604 | // n = (Mul) ? Pof[kp->Val_K + 1] - k : 1; |
1605 | |
1606 | } else { |
1607 | strcpy(g->Message, MSG(RANGE_NO_JOIN)); |
1608 | n = -1; // Logical error |
1609 | } // endif'f Type |
1610 | |
1611 | Op = op; |
1612 | return n; |
1613 | } // end of Range |
1614 | |
1615 | /***********************************************************************/ |
1616 | /* Return the size of the group (equal values) of the current value. */ |
1617 | /***********************************************************************/ |
1618 | int XINDEX::GroupSize(void) |
1619 | { |
1620 | #if defined(_DEBUG) |
1621 | assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif); |
1622 | #endif // _DEBUG |
1623 | |
1624 | if (Nval == Nk) |
1625 | return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K] |
1626 | : 1; |
1627 | |
1628 | #if defined(_DEBUG) |
1629 | assert(To_LastVal); |
1630 | #endif // _DEBUG |
1631 | |
1632 | // Index whose only some columns are used |
1633 | int ck1, ck2; |
1634 | |
1635 | ck1 = To_LastVal->Val_K; |
1636 | ck2 = ck1 + 1; |
1637 | |
1638 | #if defined(_DEBUG) |
1639 | assert(ck1 >= 0 && ck1 < To_LastVal->Ndf); |
1640 | #endif // _DEBUG |
1641 | |
1642 | for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) { |
1643 | ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1; |
1644 | ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2; |
1645 | } // endfor kcp |
1646 | |
1647 | return ck2 - ck1; |
1648 | } // end of GroupSize |
1649 | |
1650 | /***********************************************************************/ |
1651 | /* Find Cur_K and Val_K's of the next distinct value of the index. */ |
1652 | /* Returns false if Ok, true if there are no more different values. */ |
1653 | /***********************************************************************/ |
1654 | bool XINDEX::NextValDif(void) |
1655 | { |
1656 | int curk; |
1657 | PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol; |
1658 | |
1659 | if (++kcp->Val_K < kcp->Ndf) { |
1660 | Cur_K = curk = kcp->Val_K; |
1661 | |
1662 | // (Cur_K return is currently not used by SQLGBX) |
1663 | for (PXCOL kp = kcp; kp; kp = kp->Next) |
1664 | Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K; |
1665 | |
1666 | } else |
1667 | return true; |
1668 | |
1669 | for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) { |
1670 | if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1]) |
1671 | break; // all previous columns have same value |
1672 | |
1673 | curk = ++kcp->Val_K; // This is a break, get new column value |
1674 | } // endfor kcp |
1675 | |
1676 | return false; |
1677 | } // end of NextValDif |
1678 | |
1679 | /***********************************************************************/ |
1680 | /* XINDEX: Find Cur_K and Val_K's of next index entry. */ |
1681 | /* If eq is true next values must be equal to last ones up to Nval. */ |
1682 | /* Returns false if Ok, true if there are no more (equal) values. */ |
1683 | /***********************************************************************/ |
1684 | bool XINDEX::NextVal(bool eq) |
1685 | { |
1686 | int n, neq = Nk + 1, curk; |
1687 | PXCOL kcp; |
1688 | |
1689 | if (Cur_K == Num_K) |
1690 | return true; |
1691 | else |
1692 | curk = ++Cur_K; |
1693 | |
1694 | for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) { |
1695 | if (kcp->Kof) { |
1696 | if (curk == kcp->Kof[kcp->Val_K + 1]) |
1697 | neq = n; |
1698 | |
1699 | } else { |
1700 | #ifdef _DEBUG |
1701 | assert(curk == kcp->Val_K + 1); |
1702 | #endif // _DEBUG |
1703 | neq = n; |
1704 | } // endif Kof |
1705 | |
1706 | #ifdef _DEBUG |
1707 | assert(kcp->Val_K < kcp->Ndf); |
1708 | #endif // _DEBUG |
1709 | |
1710 | // If this is not a break... |
1711 | if (neq > n) |
1712 | break; // all previous columns have same value |
1713 | |
1714 | curk = ++kcp->Val_K; // This is a break, get new column value |
1715 | } // endfor kcp |
1716 | |
1717 | // Return true if no more values or, in case of "equal" values, |
1718 | // if the last used column value has changed |
1719 | return (Cur_K == Num_K || (eq && neq <= Nval)); |
1720 | } // end of NextVal |
1721 | |
1722 | /***********************************************************************/ |
1723 | /* XINDEX: Find Cur_K and Val_K's of previous index entry. */ |
1724 | /* Returns false if Ok, true if there are no more values. */ |
1725 | /***********************************************************************/ |
1726 | bool XINDEX::PrevVal(void) |
1727 | { |
1728 | int n, neq = Nk + 1, curk; |
1729 | PXCOL kcp; |
1730 | |
1731 | if (Cur_K == 0) |
1732 | return true; |
1733 | else |
1734 | curk = --Cur_K; |
1735 | |
1736 | for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) { |
1737 | if (kcp->Kof) { |
1738 | if (curk < kcp->Kof[kcp->Val_K]) |
1739 | neq = n; |
1740 | |
1741 | } else { |
1742 | #ifdef _DEBUG |
1743 | assert(curk == kcp->Val_K -1); |
1744 | #endif // _DEBUG |
1745 | neq = n; |
1746 | } // endif Kof |
1747 | |
1748 | #ifdef _DEBUG |
1749 | assert(kcp->Val_K >= 0); |
1750 | #endif // _DEBUG |
1751 | |
1752 | // If this is not a break... |
1753 | if (neq > n) |
1754 | break; // all previous columns have same value |
1755 | |
1756 | curk = --kcp->Val_K; // This is a break, get new column value |
1757 | } // endfor kcp |
1758 | |
1759 | return false; |
1760 | } // end of PrevVal |
1761 | |
1762 | /***********************************************************************/ |
1763 | /* XINDEX: Fetch a physical or logical record. */ |
1764 | /***********************************************************************/ |
1765 | int XINDEX::Fetch(PGLOBAL g) |
1766 | { |
1767 | int n; |
1768 | PXCOL kp; |
1769 | |
1770 | if (Num_K == 0) |
1771 | return -1; // means end of file |
1772 | |
1773 | if (trace(2)) |
1774 | htrc("XINDEX Fetch: Op=%d\n" , Op); |
1775 | |
1776 | /*********************************************************************/ |
1777 | /* Table read through a sorted index. */ |
1778 | /*********************************************************************/ |
1779 | switch (Op) { |
1780 | case OP_NEXT: // Read next |
1781 | if (NextVal(false)) |
1782 | return -1; // End of indexed file |
1783 | |
1784 | break; |
1785 | case OP_FIRST: // Read first |
1786 | for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next) |
1787 | kp->Val_K = 0; |
1788 | |
1789 | Op = OP_NEXT; |
1790 | break; |
1791 | case OP_SAME: // Read next same |
1792 | // Logically the key values should be the same as before |
1793 | if (NextVal(true)) { |
1794 | Op = OP_EQ; |
1795 | return -2; // no more equal values |
1796 | } // endif NextVal |
1797 | |
1798 | break; |
1799 | case OP_NXTDIF: // Read next dif |
1800 | // while (!NextVal(true)) ; |
1801 | |
1802 | // if (Cur_K >= Num_K) |
1803 | // return -1; // End of indexed file |
1804 | if (NextValDif()) |
1805 | return -1; // End of indexed file |
1806 | |
1807 | break; |
1808 | case OP_FSTDIF: // Read first diff |
1809 | for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next) |
1810 | kp->Val_K = 0; |
1811 | |
1812 | Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT; |
1813 | break; |
1814 | case OP_LAST: // Read last key |
1815 | for (Cur_K = Num_K - 1, kp = To_KeyCol; kp; kp = kp->Next) |
1816 | kp->Val_K = kp->Kblp->GetNval() - 1; |
1817 | |
1818 | Op = OP_NEXT; |
1819 | break; |
1820 | case OP_PREV: // Read previous |
1821 | if (PrevVal()) |
1822 | return -1; // End of indexed file |
1823 | |
1824 | break; |
1825 | default: // Should be OP_EQ |
1826 | // if (Tbxp->Key_Rank < 0) { |
1827 | /***************************************************************/ |
1828 | /* Look for the first key equal to the link column values */ |
1829 | /* and return its rank whithin the index table. */ |
1830 | /***************************************************************/ |
1831 | for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next) |
1832 | if (kp->InitFind(g, To_Vals[n])) |
1833 | return -1; // No more constant values |
1834 | |
1835 | Nth++; |
1836 | |
1837 | if (trace(2)) |
1838 | htrc("Fetch: Looking for new value Nth=%d\n" , Nth); |
1839 | |
1840 | Cur_K = FastFind(); |
1841 | |
1842 | if (Cur_K >= Num_K) |
1843 | /*************************************************************/ |
1844 | /* Rank not whithin index table, signal record not found. */ |
1845 | /*************************************************************/ |
1846 | return -2; |
1847 | |
1848 | else if (Mul || Nval < Nk) |
1849 | Op = OP_SAME; |
1850 | |
1851 | } // endswitch Op |
1852 | |
1853 | /*********************************************************************/ |
1854 | /* If rank is equal to stored rank, record is already there. */ |
1855 | /*********************************************************************/ |
1856 | if (Cur_K == Old_K) |
1857 | return -3; // Means record already there |
1858 | else |
1859 | Old_K = Cur_K; // Store rank of newly read record |
1860 | |
1861 | /*********************************************************************/ |
1862 | /* Return the position of the required record. */ |
1863 | /*********************************************************************/ |
1864 | return (Incr) ? Cur_K * Incr : To_Rec[Cur_K]; |
1865 | } // end of Fetch |
1866 | |
1867 | /***********************************************************************/ |
1868 | /* FastFind: Returns the index of matching record in a join using an */ |
1869 | /* optimized algorithm based on dichotomie and optimized comparing. */ |
1870 | /***********************************************************************/ |
1871 | int XINDEX::FastFind(void) |
1872 | { |
1873 | register int curk, sup, inf, i= 0, k, n = 2; |
1874 | register PXCOL kp, kcp; |
1875 | |
1876 | //assert((int)nv == Nval); |
1877 | |
1878 | if (Nblk && Op == OP_EQ) { |
1879 | // Look in block values to find in which block to search |
1880 | sup = Nblk; |
1881 | inf = -1; |
1882 | |
1883 | while (n && sup - inf > 1) { |
1884 | i = (inf + sup) >> 1; |
1885 | |
1886 | n = To_KeyCol->CompBval(i); |
1887 | |
1888 | if (n < 0) |
1889 | sup = i; |
1890 | else |
1891 | inf = i; |
1892 | |
1893 | } // endwhile |
1894 | |
1895 | if (inf < 0) |
1896 | return Num_K; |
1897 | |
1898 | // i = inf; |
1899 | inf *= Sblk; |
1900 | |
1901 | if ((sup = inf + Sblk) > To_KeyCol->Ndf) |
1902 | sup = To_KeyCol->Ndf; |
1903 | |
1904 | inf--; |
1905 | } else { |
1906 | inf = -1; |
1907 | sup = To_KeyCol->Ndf; |
1908 | } // endif Nblk |
1909 | |
1910 | if (trace(4)) |
1911 | htrc("XINDEX FastFind: Nblk=%d Op=%d inf=%d sup=%d\n" , |
1912 | Nblk, Op, inf, sup); |
1913 | |
1914 | for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) { |
1915 | while (sup - inf > 1) { |
1916 | i = (inf + sup) >> 1; |
1917 | |
1918 | n = kcp->CompVal(i); |
1919 | |
1920 | if (n < 0) |
1921 | sup = i; |
1922 | else if (n > 0) |
1923 | inf = i; |
1924 | else |
1925 | break; |
1926 | |
1927 | } // endwhile |
1928 | |
1929 | if (n) { |
1930 | if (Op != OP_EQ) { |
1931 | // Currently only OP_GT or OP_GE |
1932 | kcp->Val_K = curk = sup; |
1933 | |
1934 | // Check for value changes in previous key parts |
1935 | for (kp = kcp->Previous; kp; kp = kp->Previous) |
1936 | if (kp->Kof && curk < kp->Kof[kp->Val_K + 1]) |
1937 | break; |
1938 | else |
1939 | curk = ++kp->Val_K; |
1940 | |
1941 | n = 0; |
1942 | } // endif Op |
1943 | |
1944 | break; |
1945 | } // endif n |
1946 | |
1947 | kcp->Val_K = i; |
1948 | |
1949 | if (++k == Nval) { |
1950 | if (Op == OP_GT) { // n is always 0 |
1951 | curk = ++kcp->Val_K; // Increment value by 1 |
1952 | |
1953 | // Check for value changes in previous key parts |
1954 | for (kp = kcp->Previous; kp; kp = kp->Previous) |
1955 | if (kp->Kof && curk < kp->Kof[kp->Val_K + 1]) |
1956 | break; // Not changed |
1957 | else |
1958 | curk = ++kp->Val_K; |
1959 | |
1960 | } // endif Op |
1961 | |
1962 | break; // So kcp remains pointing the last tested block |
1963 | } // endif k |
1964 | |
1965 | if (kcp->Kof) { |
1966 | inf = kcp->Kof[i] - 1; |
1967 | sup = kcp->Kof[i + 1]; |
1968 | } else { |
1969 | inf = i - 1; |
1970 | sup = i + 1; |
1971 | } // endif Kof |
1972 | |
1973 | } // endfor k, kcp |
1974 | |
1975 | if (n) { |
1976 | // Record not found |
1977 | for (kcp = To_KeyCol; kcp; kcp = kcp->Next) |
1978 | kcp->Val_K = kcp->Ndf; // Not a valid value |
1979 | |
1980 | return Num_K; |
1981 | } // endif n |
1982 | |
1983 | for (curk = kcp->Val_K; kcp; kcp = kcp->Next) { |
1984 | kcp->Val_K = curk; |
1985 | curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K; |
1986 | } // endfor kcp |
1987 | |
1988 | if (trace(4)) |
1989 | htrc("XINDEX FastFind: curk=%d\n" , curk); |
1990 | |
1991 | return curk; |
1992 | } // end of FastFind |
1993 | |
1994 | /* -------------------------- XINDXS Class --------------------------- */ |
1995 | |
1996 | /***********************************************************************/ |
1997 | /* XINDXS public constructor. */ |
1998 | /***********************************************************************/ |
1999 | XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp) |
2000 | : XINDEX(tdbp, xdp, pxp, cp, xp) |
2001 | { |
2002 | Srtd = To_Cols[0]->GetOpt() == 2; |
2003 | } // end of XINDXS constructor |
2004 | |
2005 | /***********************************************************************/ |
2006 | /* XINDXS compare routine for C Quick/Insertion sort. */ |
2007 | /***********************************************************************/ |
2008 | int XINDXS::Qcompare(int *i1, int *i2) |
2009 | { |
2010 | //num_comp++; |
2011 | return To_KeyCol->Compare(*i1, *i2); |
2012 | } // end of Qcompare |
2013 | |
2014 | /***********************************************************************/ |
2015 | /* Range: Tell how many records exist for given value(s): */ |
2016 | /* If limit=0 return range for these values. */ |
2017 | /* If limit=1 return the start of range. */ |
2018 | /* If limit=2 return the end of range. */ |
2019 | /***********************************************************************/ |
2020 | int XINDXS::Range(PGLOBAL g, int limit, bool incl) |
2021 | { |
2022 | int k, n = 0; |
2023 | PXOB xp = To_Vals[0]; |
2024 | PXCOL kp = To_KeyCol; |
2025 | OPVAL op = Op; |
2026 | |
2027 | switch (limit) { |
2028 | case 1: Op = (incl) ? OP_GE : OP_GT; break; |
2029 | case 2: Op = (incl) ? OP_GT : OP_GE; break; |
2030 | default: Op = OP_EQ; |
2031 | } // endswitch limit |
2032 | |
2033 | /*********************************************************************/ |
2034 | /* Currently only range of constant values with an EQ operator is */ |
2035 | /* implemented. Find the number of rows for each given values. */ |
2036 | /*********************************************************************/ |
2037 | if (xp->GetType() == TYPE_CONST) { |
2038 | kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix); |
2039 | k = FastFind(); |
2040 | |
2041 | if (k < Num_K || Op != OP_EQ) |
2042 | if (limit) |
2043 | n = (Mul) ? k : kp->Val_K; |
2044 | else |
2045 | n = (Mul) ? Pof[kp->Val_K + 1] - k : 1; |
2046 | |
2047 | } else { |
2048 | strcpy(g->Message, MSG(RANGE_NO_JOIN)); |
2049 | n = -1; // Logical error |
2050 | } // endif'f Type |
2051 | |
2052 | Op = op; |
2053 | return n; |
2054 | } // end of Range |
2055 | |
2056 | /***********************************************************************/ |
2057 | /* Return the size of the group (equal values) of the current value. */ |
2058 | /***********************************************************************/ |
2059 | int XINDXS::GroupSize(void) |
2060 | { |
2061 | #if defined(_DEBUG) |
2062 | assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif); |
2063 | #endif // _DEBUG |
2064 | return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K] : 1; |
2065 | } // end of GroupSize |
2066 | |
2067 | /***********************************************************************/ |
2068 | /* XINDXS: Find Cur_K and Val_K of previous index value. */ |
2069 | /* Returns false if Ok, true if there are no more values. */ |
2070 | /***********************************************************************/ |
2071 | bool XINDXS::PrevVal(void) |
2072 | { |
2073 | if (--Cur_K < 0) |
2074 | return true; |
2075 | |
2076 | if (Mul) { |
2077 | if (Cur_K < Pof[To_KeyCol->Val_K]) |
2078 | To_KeyCol->Val_K--; |
2079 | |
2080 | } else |
2081 | To_KeyCol->Val_K = Cur_K; |
2082 | |
2083 | return false; |
2084 | } // end of PrevVal |
2085 | |
2086 | /***********************************************************************/ |
2087 | /* XINDXS: Find Cur_K and Val_K of next index value. */ |
2088 | /* If b is true next value must be equal to last one. */ |
2089 | /* Returns false if Ok, true if there are no more (equal) values. */ |
2090 | /***********************************************************************/ |
2091 | bool XINDXS::NextVal(bool eq) |
2092 | { |
2093 | bool rc; |
2094 | |
2095 | if (To_KeyCol->Val_K == Ndif) |
2096 | return true; |
2097 | |
2098 | if (Mul) { |
2099 | int limit = Pof[To_KeyCol->Val_K + 1]; |
2100 | |
2101 | #ifdef _DEBUG |
2102 | assert(Cur_K < limit); |
2103 | assert(To_KeyCol->Val_K < Ndif); |
2104 | #endif // _DEBUG |
2105 | |
2106 | if (++Cur_K == limit) { |
2107 | To_KeyCol->Val_K++; |
2108 | rc = (eq || limit == Num_K); |
2109 | } else |
2110 | rc = false; |
2111 | |
2112 | } else |
2113 | rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq; |
2114 | |
2115 | return rc; |
2116 | } // end of NextVal |
2117 | |
2118 | /***********************************************************************/ |
2119 | /* XINDXS: Fetch a physical or logical record. */ |
2120 | /***********************************************************************/ |
2121 | int XINDXS::Fetch(PGLOBAL g) |
2122 | { |
2123 | if (Num_K == 0) |
2124 | return -1; // means end of file |
2125 | |
2126 | if (trace(2)) |
2127 | htrc("XINDXS Fetch: Op=%d\n" , Op); |
2128 | |
2129 | /*********************************************************************/ |
2130 | /* Table read through a sorted index. */ |
2131 | /*********************************************************************/ |
2132 | switch (Op) { |
2133 | case OP_NEXT: // Read next |
2134 | if (NextVal(false)) |
2135 | return -1; // End of indexed file |
2136 | |
2137 | break; |
2138 | case OP_FIRST: // Read first |
2139 | To_KeyCol->Val_K = Cur_K = 0; |
2140 | Op = OP_NEXT; |
2141 | break; |
2142 | case OP_SAME: // Read next same |
2143 | if (!Mul || NextVal(true)) { |
2144 | Op = OP_EQ; |
2145 | return -2; // No more equal values |
2146 | } // endif Mul |
2147 | |
2148 | break; |
2149 | case OP_NXTDIF: // Read next dif |
2150 | if (++To_KeyCol->Val_K == Ndif) |
2151 | return -1; // End of indexed file |
2152 | |
2153 | Cur_K = Pof[To_KeyCol->Val_K]; |
2154 | break; |
2155 | case OP_FSTDIF: // Read first diff |
2156 | To_KeyCol->Val_K = Cur_K = 0; |
2157 | Op = (Mul) ? OP_NXTDIF : OP_NEXT; |
2158 | break; |
2159 | case OP_LAST: // Read first |
2160 | Cur_K = Num_K - 1; |
2161 | To_KeyCol->Val_K = Ndif - 1; |
2162 | Op = OP_PREV; |
2163 | break; |
2164 | case OP_PREV: // Read previous |
2165 | if (PrevVal()) |
2166 | return -1; // End of indexed file |
2167 | |
2168 | break; |
2169 | default: // Should be OP_EQ |
2170 | /*****************************************************************/ |
2171 | /* Look for the first key equal to the link column values */ |
2172 | /* and return its rank whithin the index table. */ |
2173 | /*****************************************************************/ |
2174 | if (To_KeyCol->InitFind(g, To_Vals[0])) |
2175 | return -1; // No more constant values |
2176 | else |
2177 | Nth++; |
2178 | |
2179 | if (trace(2)) |
2180 | htrc("Fetch: Looking for new value Nth=%d\n" , Nth); |
2181 | |
2182 | Cur_K = FastFind(); |
2183 | |
2184 | if (Cur_K >= Num_K) |
2185 | // Rank not whithin index table, signal record not found |
2186 | return -2; |
2187 | else if (Mul) |
2188 | Op = OP_SAME; |
2189 | |
2190 | } // endswitch Op |
2191 | |
2192 | /*********************************************************************/ |
2193 | /* If rank is equal to stored rank, record is already there. */ |
2194 | /*********************************************************************/ |
2195 | if (Cur_K == Old_K) |
2196 | return -3; // Means record already there |
2197 | else |
2198 | Old_K = Cur_K; // Store rank of newly read record |
2199 | |
2200 | /*********************************************************************/ |
2201 | /* Return the position of the required record. */ |
2202 | /*********************************************************************/ |
2203 | return (Incr) ? Cur_K * Incr : To_Rec[Cur_K]; |
2204 | } // end of Fetch |
2205 | |
2206 | /***********************************************************************/ |
2207 | /* FastFind: Returns the index of matching indexed record using an */ |
2208 | /* optimized algorithm based on dichotomie and optimized comparing. */ |
2209 | /***********************************************************************/ |
2210 | int XINDXS::FastFind(void) |
2211 | { |
2212 | register int sup, inf, i= 0, n = 2; |
2213 | register PXCOL kcp = To_KeyCol; |
2214 | |
2215 | if (Nblk && Op == OP_EQ) { |
2216 | // Look in block values to find in which block to search |
2217 | sup = Nblk; |
2218 | inf = -1; |
2219 | |
2220 | while (n && sup - inf > 1) { |
2221 | i = (inf + sup) >> 1; |
2222 | |
2223 | n = kcp->CompBval(i); |
2224 | |
2225 | if (n < 0) |
2226 | sup = i; |
2227 | else |
2228 | inf = i; |
2229 | |
2230 | } // endwhile |
2231 | |
2232 | if (inf < 0) |
2233 | return Num_K; |
2234 | |
2235 | inf *= Sblk; |
2236 | |
2237 | if ((sup = inf + Sblk) > Ndif) |
2238 | sup = Ndif; |
2239 | |
2240 | inf--; |
2241 | } else { |
2242 | inf = -1; |
2243 | sup = Ndif; |
2244 | } // endif Nblk |
2245 | |
2246 | if (trace(4)) |
2247 | htrc("XINDXS FastFind: Nblk=%d Op=%d inf=%d sup=%d\n" , |
2248 | Nblk, Op, inf, sup); |
2249 | |
2250 | while (sup - inf > 1) { |
2251 | i = (inf + sup) >> 1; |
2252 | |
2253 | n = kcp->CompVal(i); |
2254 | |
2255 | if (n < 0) |
2256 | sup = i; |
2257 | else if (n > 0) |
2258 | inf = i; |
2259 | else |
2260 | break; |
2261 | |
2262 | } // endwhile |
2263 | |
2264 | if (!n && Op == OP_GT) { |
2265 | ++i; |
2266 | } else if (n && Op != OP_EQ) { |
2267 | // Currently only OP_GT or OP_GE |
2268 | i = sup; |
2269 | n = 0; |
2270 | } // endif sup |
2271 | |
2272 | if (trace(4)) |
2273 | htrc("XINDXS FastFind: n=%d i=%d\n" , n, i); |
2274 | |
2275 | // Loop on kcp because of dynamic indexing |
2276 | for (; kcp; kcp = kcp->Next) |
2277 | kcp->Val_K = i; // Used by FillValue |
2278 | |
2279 | return ((n) ? Num_K : (Mul) ? Pof[i] : i); |
2280 | } // end of FastFind |
2281 | |
2282 | /* -------------------------- XLOAD Class --------------------------- */ |
2283 | |
2284 | /***********************************************************************/ |
2285 | /* XLOAD constructor. */ |
2286 | /***********************************************************************/ |
2287 | XLOAD::XLOAD(void) |
2288 | { |
2289 | Hfile = INVALID_HANDLE_VALUE; |
2290 | NewOff.Val = 0LL; |
2291 | } // end of XLOAD constructor |
2292 | |
2293 | /***********************************************************************/ |
2294 | /* Close the index huge file. */ |
2295 | /***********************************************************************/ |
2296 | void XLOAD::Close(void) |
2297 | { |
2298 | if (Hfile != INVALID_HANDLE_VALUE) { |
2299 | CloseFileHandle(Hfile); |
2300 | Hfile = INVALID_HANDLE_VALUE; |
2301 | } // endif Hfile |
2302 | |
2303 | } // end of Close |
2304 | |
2305 | /* --------------------------- XFILE Class --------------------------- */ |
2306 | |
2307 | /***********************************************************************/ |
2308 | /* XFILE constructor. */ |
2309 | /***********************************************************************/ |
2310 | XFILE::XFILE(void) : XLOAD() |
2311 | { |
2312 | Xfile = NULL; |
2313 | #if defined(XMAP) |
2314 | Mmp = NULL; |
2315 | #endif // XMAP |
2316 | } // end of XFILE constructor |
2317 | |
2318 | /***********************************************************************/ |
2319 | /* Xopen function: opens a file using native API's. */ |
2320 | /***********************************************************************/ |
2321 | bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode) |
2322 | { |
2323 | PCSZ pmod; |
2324 | bool rc; |
2325 | IOFF noff[MAX_INDX]; |
2326 | |
2327 | /*********************************************************************/ |
2328 | /* Open the index file according to mode. */ |
2329 | /*********************************************************************/ |
2330 | switch (mode) { |
2331 | case MODE_READ: pmod = "rb" ; break; |
2332 | case MODE_WRITE: pmod = "wb" ; break; |
2333 | case MODE_INSERT: pmod = "ab" ; break; |
2334 | default: |
2335 | sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen" , mode); |
2336 | return true; |
2337 | } // endswitch mode |
2338 | |
2339 | if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) { |
2340 | if (trace(1)) |
2341 | htrc("Open: %s\n" , g->Message); |
2342 | |
2343 | return true; |
2344 | } // endif Xfile |
2345 | |
2346 | if (mode == MODE_INSERT) { |
2347 | /*******************************************************************/ |
2348 | /* Position the cursor at end of file so ftell returns file size. */ |
2349 | /*******************************************************************/ |
2350 | if (fseek(Xfile, 0, SEEK_END)) { |
2351 | sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek" ); |
2352 | return true; |
2353 | } // endif |
2354 | |
2355 | NewOff.v.Low = (int)ftell(Xfile); |
2356 | |
2357 | if (trace(1)) |
2358 | htrc("XFILE Open: NewOff.v.Low=%d\n" , NewOff.v.Low); |
2359 | |
2360 | } else if (mode == MODE_WRITE) { |
2361 | if (id >= 0) { |
2362 | // New not sep index file. Write the header. |
2363 | memset(noff, 0, sizeof(noff)); |
2364 | Write(g, noff, sizeof(IOFF), MAX_INDX, rc); |
2365 | fseek(Xfile, 0, SEEK_END); |
2366 | NewOff.v.Low = (int)ftell(Xfile); |
2367 | |
2368 | if (trace(1)) |
2369 | htrc("XFILE Open: NewOff.v.Low=%d\n" , NewOff.v.Low); |
2370 | |
2371 | } // endif id |
2372 | |
2373 | } else if (mode == MODE_READ && id >= 0) { |
2374 | // Get offset from the header |
2375 | if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) { |
2376 | sprintf(g->Message, MSG(XFILE_READERR), errno); |
2377 | return true; |
2378 | } // endif MAX_INDX |
2379 | |
2380 | if (trace(1)) |
2381 | htrc("XFILE Open: noff[%d].v.Low=%d\n" , id, noff[id].v.Low); |
2382 | |
2383 | // Position the cursor at the offset of this index |
2384 | if (fseek(Xfile, noff[id].v.Low, SEEK_SET)) { |
2385 | sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek" ); |
2386 | return true; |
2387 | } // endif |
2388 | |
2389 | } // endif mode |
2390 | |
2391 | return false; |
2392 | } // end of Open |
2393 | |
2394 | /***********************************************************************/ |
2395 | /* Move into an index file. */ |
2396 | /***********************************************************************/ |
2397 | bool XFILE::Seek(PGLOBAL g, int low, int high __attribute__((unused)), |
2398 | int origin) |
2399 | { |
2400 | #if defined(_DEBUG) |
2401 | assert(high == 0); |
2402 | #endif // !_DEBUG |
2403 | |
2404 | if (fseek(Xfile, low, origin)) { |
2405 | sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek" ); |
2406 | return true; |
2407 | } // endif |
2408 | |
2409 | return false; |
2410 | } // end of Seek |
2411 | |
2412 | /***********************************************************************/ |
2413 | /* Read from the index file. */ |
2414 | /***********************************************************************/ |
2415 | bool XFILE::Read(PGLOBAL g, void *buf, int n, int size) |
2416 | { |
2417 | if (fread(buf, size, n, Xfile) != (size_t)n) { |
2418 | sprintf(g->Message, MSG(XFILE_READERR), errno); |
2419 | return true; |
2420 | } // endif size |
2421 | |
2422 | return false; |
2423 | } // end of Read |
2424 | |
2425 | /***********************************************************************/ |
2426 | /* Write on index file, set rc and return the number of bytes written */ |
2427 | /***********************************************************************/ |
2428 | int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc) |
2429 | { |
2430 | int niw = (int)fwrite(buf, size, n, Xfile); |
2431 | |
2432 | if (niw != n) { |
2433 | sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno)); |
2434 | rc = true; |
2435 | } // endif size |
2436 | |
2437 | return niw * size; |
2438 | } // end of Write |
2439 | |
2440 | /***********************************************************************/ |
2441 | /* Update the file header and close the index file. */ |
2442 | /***********************************************************************/ |
2443 | void XFILE::Close(char *fn, int id) |
2444 | { |
2445 | if (id >= 0 && fn && Xfile) { |
2446 | fclose(Xfile); |
2447 | |
2448 | if ((Xfile = fopen(fn, "r+b" ))) |
2449 | if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET)) |
2450 | fwrite(&NewOff, sizeof(int), 2, Xfile); |
2451 | |
2452 | } // endif id |
2453 | |
2454 | Close(); |
2455 | } // end of Close |
2456 | |
2457 | /***********************************************************************/ |
2458 | /* Close the index file. */ |
2459 | /***********************************************************************/ |
2460 | void XFILE::Close(void) |
2461 | { |
2462 | XLOAD::Close(); |
2463 | |
2464 | if (Xfile) { |
2465 | fclose(Xfile); |
2466 | Xfile = NULL; |
2467 | } // endif Xfile |
2468 | |
2469 | #if defined(XMAP) |
2470 | if (Mmp && CloseMemMap(Mmp->memory, Mmp->lenL)) |
2471 | printf("Error closing mapped index\n" ); |
2472 | #endif // XMAP |
2473 | } // end of Close |
2474 | |
2475 | #if defined(XMAP) |
2476 | /*********************************************************************/ |
2477 | /* Map the entire index file. */ |
2478 | /*********************************************************************/ |
2479 | void *XFILE::FileView(PGLOBAL g, char *fn) |
2480 | { |
2481 | HANDLE h; |
2482 | |
2483 | Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP)); |
2484 | h = CreateFileMap(g, fn, Mmp, MODE_READ, false); |
2485 | |
2486 | if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) { |
2487 | if (!(*g->Message)) |
2488 | strcpy(g->Message, MSG(FILE_MAP_ERR)); |
2489 | |
2490 | CloseFileHandle(h); // Not used anymore |
2491 | return NULL; // No saved values |
2492 | } // endif h |
2493 | |
2494 | CloseFileHandle(h); // Not used anymore |
2495 | return Mmp->memory; |
2496 | } // end of FileView |
2497 | #endif // XMAP |
2498 | |
2499 | /* -------------------------- XHUGE Class --------------------------- */ |
2500 | |
2501 | /***********************************************************************/ |
2502 | /* Xopen function: opens a file using native API's. */ |
2503 | /***********************************************************************/ |
2504 | bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode) |
2505 | { |
2506 | IOFF noff[MAX_INDX]; |
2507 | |
2508 | if (Hfile != INVALID_HANDLE_VALUE) { |
2509 | sprintf(g->Message, MSG(FILE_OPEN_YET), filename); |
2510 | return true; |
2511 | } // endif |
2512 | |
2513 | if (trace(1)) |
2514 | htrc(" Xopen: filename=%s id=%d mode=%d\n" , filename, id, mode); |
2515 | |
2516 | #if defined(__WIN__) |
2517 | LONG high = 0; |
2518 | DWORD rc, drc, access, share, creation; |
2519 | |
2520 | /*********************************************************************/ |
2521 | /* Create the file object according to access mode */ |
2522 | /*********************************************************************/ |
2523 | switch (mode) { |
2524 | case MODE_READ: |
2525 | access = GENERIC_READ; |
2526 | share = FILE_SHARE_READ; |
2527 | creation = OPEN_EXISTING; |
2528 | break; |
2529 | case MODE_WRITE: |
2530 | access = GENERIC_WRITE; |
2531 | share = 0; |
2532 | creation = CREATE_ALWAYS; |
2533 | break; |
2534 | case MODE_INSERT: |
2535 | access = GENERIC_WRITE; |
2536 | share = 0; |
2537 | creation = OPEN_EXISTING; |
2538 | break; |
2539 | default: |
2540 | sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen" , mode); |
2541 | return true; |
2542 | } // endswitch |
2543 | |
2544 | Hfile = CreateFile(filename, access, share, NULL, creation, |
2545 | FILE_ATTRIBUTE_NORMAL, NULL); |
2546 | |
2547 | if (Hfile == INVALID_HANDLE_VALUE) { |
2548 | rc = GetLastError(); |
2549 | sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); |
2550 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | |
2551 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, |
2552 | (LPTSTR)filename, sizeof(filename), NULL); |
2553 | strcat(g->Message, filename); |
2554 | return true; |
2555 | } // endif Hfile |
2556 | |
2557 | if (trace(1)) |
2558 | htrc(" access=%p share=%p creation=%d handle=%p fn=%s\n" , |
2559 | access, share, creation, Hfile, filename); |
2560 | |
2561 | if (mode == MODE_INSERT) { |
2562 | /*******************************************************************/ |
2563 | /* In Insert mode we must position the cursor at end of file. */ |
2564 | /*******************************************************************/ |
2565 | rc = SetFilePointer(Hfile, 0, &high, FILE_END); |
2566 | |
2567 | if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) { |
2568 | sprintf(g->Message, MSG(ERROR_IN_SFP), drc); |
2569 | CloseHandle(Hfile); |
2570 | Hfile = INVALID_HANDLE_VALUE; |
2571 | return true; |
2572 | } // endif |
2573 | |
2574 | NewOff.v.Low = (int)rc; |
2575 | NewOff.v.High = (int)high; |
2576 | } else if (mode == MODE_WRITE) { |
2577 | if (id >= 0) { |
2578 | // New not sep index file. Write the header. |
2579 | memset(noff, 0, sizeof(noff)); |
2580 | rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL); |
2581 | NewOff.v.Low = (int)drc; |
2582 | } // endif id |
2583 | |
2584 | } else if (mode == MODE_READ && id >= 0) { |
2585 | // Get offset from the header |
2586 | rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL); |
2587 | |
2588 | if (!rc) { |
2589 | sprintf(g->Message, MSG(XFILE_READERR), GetLastError()); |
2590 | return true; |
2591 | } // endif rc |
2592 | |
2593 | // Position the cursor at the offset of this index |
2594 | rc = SetFilePointer(Hfile, noff[id].v.Low, |
2595 | (PLONG)&noff[id].v.High, FILE_BEGIN); |
2596 | |
2597 | if (rc == INVALID_SET_FILE_POINTER) { |
2598 | sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer" ); |
2599 | return true; |
2600 | } // endif |
2601 | |
2602 | } // endif Mode |
2603 | |
2604 | #else // UNIX |
2605 | int oflag = O_LARGEFILE; // Enable file size > 2G |
2606 | mode_t pmod = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
2607 | |
2608 | /*********************************************************************/ |
2609 | /* Create the file object according to access mode */ |
2610 | /*********************************************************************/ |
2611 | switch (mode) { |
2612 | case MODE_READ: |
2613 | oflag |= O_RDONLY; |
2614 | break; |
2615 | case MODE_WRITE: |
2616 | oflag |= O_WRONLY | O_CREAT | O_TRUNC; |
2617 | // pmod = S_IREAD | S_IWRITE; |
2618 | break; |
2619 | case MODE_INSERT: |
2620 | oflag |= (O_WRONLY | O_APPEND); |
2621 | break; |
2622 | default: |
2623 | sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen" , mode); |
2624 | return true; |
2625 | } // endswitch |
2626 | |
2627 | Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod); |
2628 | |
2629 | if (Hfile == INVALID_HANDLE_VALUE) { |
2630 | /*rc = errno;*/ |
2631 | if (trace(1)) |
2632 | htrc("Open: %s\n" , g->Message); |
2633 | |
2634 | return true; |
2635 | } // endif Hfile |
2636 | |
2637 | if (trace(1)) |
2638 | htrc(" oflag=%p mode=%d handle=%d fn=%s\n" , |
2639 | oflag, mode, Hfile, filename); |
2640 | |
2641 | if (mode == MODE_INSERT) { |
2642 | /*******************************************************************/ |
2643 | /* Position the cursor at end of file so ftell returns file size. */ |
2644 | /*******************************************************************/ |
2645 | if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) { |
2646 | sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek" ); |
2647 | return true; |
2648 | } // endif |
2649 | |
2650 | if (trace(1)) |
2651 | htrc("INSERT: NewOff=%lld\n" , NewOff.Val); |
2652 | |
2653 | } else if (mode == MODE_WRITE) { |
2654 | if (id >= 0) { |
2655 | // New not sep index file. Write the header. |
2656 | memset(noff, 0, sizeof(noff)); |
2657 | NewOff.v.Low = write(Hfile, &noff, sizeof(noff)); |
2658 | } // endif id |
2659 | |
2660 | if (trace(1)) |
2661 | htrc("WRITE: NewOff=%lld\n" , NewOff.Val); |
2662 | |
2663 | } else if (mode == MODE_READ && id >= 0) { |
2664 | // Get offset from the header |
2665 | if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) { |
2666 | sprintf(g->Message, MSG(READ_ERROR), "Index file" , strerror(errno)); |
2667 | return true; |
2668 | } // endif read |
2669 | |
2670 | if (trace(1)) |
2671 | htrc("noff[%d]=%lld\n" , id, noff[id].Val); |
2672 | |
2673 | // Position the cursor at the offset of this index |
2674 | if (lseek64(Hfile, noff[id].Val, SEEK_SET) < 0) { |
2675 | sprintf(g->Message, "(XHUGE)lseek64: %s (%lld)" , strerror(errno), noff[id].Val); |
2676 | printf("%s\n" , g->Message); |
2677 | // sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek"); |
2678 | return true; |
2679 | } // endif lseek64 |
2680 | |
2681 | } // endif mode |
2682 | #endif // UNIX |
2683 | |
2684 | return false; |
2685 | } // end of Open |
2686 | |
2687 | /***********************************************************************/ |
2688 | /* Go to position in a huge file. */ |
2689 | /***********************************************************************/ |
2690 | bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin) |
2691 | { |
2692 | #if defined(__WIN__) |
2693 | LONG hi = high; |
2694 | DWORD rc = SetFilePointer(Hfile, low, &hi, origin); |
2695 | |
2696 | if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { |
2697 | sprintf(g->Message, MSG(FUNC_ERROR), "Xseek" ); |
2698 | return true; |
2699 | } // endif |
2700 | |
2701 | #else // UNIX |
2702 | off64_t pos = (off64_t)low |
2703 | + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000); |
2704 | |
2705 | if (lseek64(Hfile, pos, origin) < 0) { |
2706 | sprintf(g->Message, MSG(ERROR_IN_LSK), errno); |
2707 | |
2708 | if (trace(1)) |
2709 | htrc("lseek64 error %d\n" , errno); |
2710 | |
2711 | return true; |
2712 | } // endif lseek64 |
2713 | |
2714 | if (trace(1)) |
2715 | htrc("Seek: low=%d high=%d\n" , low, high); |
2716 | #endif // UNIX |
2717 | |
2718 | return false; |
2719 | } // end of Seek |
2720 | |
2721 | /***********************************************************************/ |
2722 | /* Read from a huge index file. */ |
2723 | /***********************************************************************/ |
2724 | bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size) |
2725 | { |
2726 | bool rc = false; |
2727 | |
2728 | #if defined(__WIN__) |
2729 | bool brc; |
2730 | DWORD nbr, count = (DWORD)(n * size); |
2731 | |
2732 | brc = ReadFile(Hfile, buf, count, &nbr, NULL); |
2733 | |
2734 | if (brc) { |
2735 | if (nbr != count) { |
2736 | strcpy(g->Message, MSG(EOF_INDEX_FILE)); |
2737 | rc = true; |
2738 | } // endif nbr |
2739 | |
2740 | } else { |
2741 | char buf[256]; |
2742 | DWORD drc = GetLastError(); |
2743 | |
2744 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | |
2745 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, |
2746 | (LPTSTR)buf, sizeof(buf), NULL); |
2747 | sprintf(g->Message, MSG(READ_ERROR), "index file" , buf); |
2748 | rc = true; |
2749 | } // endif brc |
2750 | #else // UNIX |
2751 | ssize_t count = (ssize_t)(n * size); |
2752 | |
2753 | if (trace(1)) |
2754 | htrc("Hfile=%d n=%d size=%d count=%d\n" , Hfile, n, size, count); |
2755 | |
2756 | if (read(Hfile, buf, count) != count) { |
2757 | sprintf(g->Message, MSG(READ_ERROR), "Index file" , strerror(errno)); |
2758 | |
2759 | if (trace(1)) |
2760 | htrc("read error %d\n" , errno); |
2761 | |
2762 | rc = true; |
2763 | } // endif nbr |
2764 | #endif // UNIX |
2765 | |
2766 | return rc; |
2767 | } // end of Read |
2768 | |
2769 | /***********************************************************************/ |
2770 | /* Write on a huge index file. */ |
2771 | /***********************************************************************/ |
2772 | int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc) |
2773 | { |
2774 | #if defined(__WIN__) |
2775 | bool brc; |
2776 | DWORD nbw, count = (DWORD)n * (DWORD) size; |
2777 | |
2778 | brc = WriteFile(Hfile, buf, count, &nbw, NULL); |
2779 | |
2780 | if (!brc) { |
2781 | char msg[256]; |
2782 | DWORD drc = GetLastError(); |
2783 | |
2784 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | |
2785 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, |
2786 | (LPTSTR)msg, sizeof(msg), NULL); |
2787 | sprintf(g->Message, MSG(WRITING_ERROR), "index file" , msg); |
2788 | rc = true; |
2789 | } // endif size |
2790 | |
2791 | return (int)nbw; |
2792 | #else // UNIX |
2793 | ssize_t nbw; |
2794 | size_t count = (size_t)n * (size_t)size; |
2795 | |
2796 | nbw = write(Hfile, buf, count); |
2797 | |
2798 | if (nbw != (signed)count) { |
2799 | sprintf(g->Message, MSG(WRITING_ERROR), |
2800 | "index file" , strerror(errno)); |
2801 | rc = true; |
2802 | } // endif nbw |
2803 | |
2804 | return (int)nbw; |
2805 | #endif // UNIX |
2806 | } // end of Write |
2807 | |
2808 | /***********************************************************************/ |
2809 | /* Update the file header and close the index file. */ |
2810 | /***********************************************************************/ |
2811 | void XHUGE::Close(char *fn, int id) |
2812 | { |
2813 | if (trace(1)) |
2814 | htrc("XHUGE::Close: fn=%s id=%d NewOff=%lld\n" , fn, id, NewOff.Val); |
2815 | |
2816 | #if defined(__WIN__) |
2817 | if (id >= 0 && fn) { |
2818 | CloseFileHandle(Hfile); |
2819 | Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
2820 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
2821 | |
2822 | if (Hfile != INVALID_HANDLE_VALUE) |
2823 | if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN) |
2824 | != INVALID_SET_FILE_POINTER) { |
2825 | DWORD nbw; |
2826 | |
2827 | WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL); |
2828 | } // endif SetFilePointer |
2829 | |
2830 | } // endif id |
2831 | #else // !__WIN__ |
2832 | if (id >= 0 && fn) { |
2833 | if (Hfile != INVALID_HANDLE_VALUE) { |
2834 | if (lseek64(Hfile, id * sizeof(IOFF), SEEK_SET) >= 0) { |
2835 | ssize_t nbw = write(Hfile, &NewOff, sizeof(IOFF)); |
2836 | |
2837 | if (nbw != (signed)sizeof(IOFF)) |
2838 | htrc("Error writing index file header: %s\n" , strerror(errno)); |
2839 | |
2840 | } else |
2841 | htrc("(XHUGE::Close)lseek64: %s (%d)\n" , strerror(errno), id); |
2842 | |
2843 | } else |
2844 | htrc("(XHUGE)error reopening %s: %s\n" , fn, strerror(errno)); |
2845 | |
2846 | } // endif id |
2847 | #endif // !__WIN__ |
2848 | |
2849 | XLOAD::Close(); |
2850 | } // end of Close |
2851 | |
2852 | #if defined(XMAP) |
2853 | /***********************************************************************/ |
2854 | /* Don't know whether this is possible for huge files. */ |
2855 | /***********************************************************************/ |
2856 | void *XHUGE::FileView(PGLOBAL g, char *) |
2857 | { |
2858 | strcpy(g->Message, MSG(NO_PART_MAP)); |
2859 | return NULL; |
2860 | } // end of FileView |
2861 | #endif // XMAP |
2862 | |
2863 | /* -------------------------- XXROW Class --------------------------- */ |
2864 | |
2865 | /***********************************************************************/ |
2866 | /* XXROW Public Constructor. */ |
2867 | /***********************************************************************/ |
2868 | XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false) |
2869 | { |
2870 | Srtd = true; |
2871 | Tdbp = tdbp; |
2872 | Valp = NULL; |
2873 | } // end of XXROW constructor |
2874 | |
2875 | /***********************************************************************/ |
2876 | /* XXROW Reset: re-initialize a Kindex block. */ |
2877 | /***********************************************************************/ |
2878 | void XXROW::Reset(void) |
2879 | { |
2880 | #if defined(_DEBUG) |
2881 | assert(Tdbp->GetLink()); // This a join index |
2882 | #endif // _DEBUG |
2883 | } // end of Reset |
2884 | |
2885 | /***********************************************************************/ |
2886 | /* Init: Open and Initialize a Key Index. */ |
2887 | /***********************************************************************/ |
2888 | bool XXROW::Init(PGLOBAL g) |
2889 | { |
2890 | /*********************************************************************/ |
2891 | /* Table will be accessed through an index table. */ |
2892 | /* To_Link should not be NULL. */ |
2893 | /*********************************************************************/ |
2894 | if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1) |
2895 | return true; |
2896 | |
2897 | if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) { |
2898 | strcpy(g->Message, MSG(TYPE_MISMATCH)); |
2899 | return true; |
2900 | } else |
2901 | Valp = (*Tdbp->GetLink())->GetValue(); |
2902 | |
2903 | if ((Num_K = Tbxp->Cardinality(g)) < 0) |
2904 | return true; // Not a fixed file |
2905 | |
2906 | /*********************************************************************/ |
2907 | /* The entire table is indexed, no need to construct the index. */ |
2908 | /*********************************************************************/ |
2909 | Cur_K = Num_K; |
2910 | return false; |
2911 | } // end of Init |
2912 | |
2913 | /***********************************************************************/ |
2914 | /* RANGE: Tell how many record exist in a given value range. */ |
2915 | /***********************************************************************/ |
2916 | int XXROW::Range(PGLOBAL, int limit, bool incl) |
2917 | { |
2918 | int n = Valp->GetIntValue(); |
2919 | |
2920 | switch (limit) { |
2921 | case 1: n += ((incl) ? 0 : 1); break; |
2922 | case 2: n += ((incl) ? 1 : 0); break; |
2923 | default: n = 1; |
2924 | } // endswitch limit |
2925 | |
2926 | return n; |
2927 | } // end of Range |
2928 | |
2929 | /***********************************************************************/ |
2930 | /* XXROW: Fetch a physical or logical record. */ |
2931 | /***********************************************************************/ |
2932 | int XXROW::Fetch(PGLOBAL) |
2933 | { |
2934 | if (Num_K == 0) |
2935 | return -1; // means end of file |
2936 | |
2937 | /*********************************************************************/ |
2938 | /* Look for a key equal to the link column of previous table, */ |
2939 | /* and return its rank whithin the index table. */ |
2940 | /*********************************************************************/ |
2941 | Cur_K = FastFind(); |
2942 | |
2943 | if (Cur_K >= Num_K) |
2944 | /*******************************************************************/ |
2945 | /* Rank not whithin index table, signal record not found. */ |
2946 | /*******************************************************************/ |
2947 | return -2; // Means record not found |
2948 | |
2949 | /*********************************************************************/ |
2950 | /* If rank is equal to stored rank, record is already there. */ |
2951 | /*********************************************************************/ |
2952 | if (Cur_K == Old_K) |
2953 | return -3; // Means record already there |
2954 | else |
2955 | Old_K = Cur_K; // Store rank of newly read record |
2956 | |
2957 | return Cur_K; |
2958 | } // end of Fetch |
2959 | |
2960 | /***********************************************************************/ |
2961 | /* FastFind: Returns the index of matching record in a join. */ |
2962 | /***********************************************************************/ |
2963 | int XXROW::FastFind(void) |
2964 | { |
2965 | int n = Valp->GetIntValue(); |
2966 | |
2967 | if (n < 0) |
2968 | return (Op == OP_EQ) ? (-1) : 0; |
2969 | else if (n > Num_K) |
2970 | return Num_K; |
2971 | else |
2972 | return (Op == OP_GT) ? n : (n - 1); |
2973 | |
2974 | } // end of FastFind |
2975 | |
2976 | /* ------------------------- KXYCOL Classes -------------------------- */ |
2977 | |
2978 | /***********************************************************************/ |
2979 | /* KXYCOL public constructor. */ |
2980 | /***********************************************************************/ |
2981 | KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp), |
2982 | To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp) |
2983 | { |
2984 | Next = NULL; |
2985 | Previous = NULL; |
2986 | Kxp = kp; |
2987 | Colp = NULL; |
2988 | IsSorted = false; |
2989 | Asc = true; |
2990 | Keys = Nmblk; |
2991 | Kblp = NULL; |
2992 | Bkeys = Nmblk; |
2993 | Blkp = NULL; |
2994 | Valp = NULL; |
2995 | Klen = 0; |
2996 | Kprec = 0; |
2997 | Type = TYPE_ERROR; |
2998 | Prefix = false; |
2999 | Koff = Nmblk; |
3000 | Val_K = 0; |
3001 | Ndf = 0; |
3002 | Mxs = 0; |
3003 | } // end of KXYCOL constructor |
3004 | |
3005 | /***********************************************************************/ |
3006 | /* KXYCOL Init: initialize and allocate storage. */ |
3007 | /* Key length kln can be smaller than column length for CHAR columns. */ |
3008 | /***********************************************************************/ |
3009 | bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln) |
3010 | { |
3011 | int len = colp->GetLength(), prec = colp->GetScale(); |
3012 | bool un = colp->IsUnsigned(); |
3013 | |
3014 | // Currently no indexing on NULL columns |
3015 | if (colp->IsNullable() && kln) { |
3016 | sprintf(g->Message, "Cannot index nullable column %s" , colp->GetName()); |
3017 | return true; |
3018 | } // endif nullable |
3019 | |
3020 | if (kln && len > kln && colp->GetResultType() == TYPE_STRING) { |
3021 | len = kln; |
3022 | Prefix = true; |
3023 | } // endif kln |
3024 | |
3025 | if (trace(1)) |
3026 | htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n" , |
3027 | this, colp->GetName(), n, colp->GetResultType(), sm); |
3028 | |
3029 | // Allocate the Value object used when moving items |
3030 | Type = colp->GetResultType(); |
3031 | |
3032 | if (!(Valp = AllocateValue(g, Type, len, prec, un))) |
3033 | return true; |
3034 | |
3035 | Klen = Valp->GetClen(); |
3036 | Keys.Size = (size_t)n * (size_t)Klen; |
3037 | |
3038 | if (!PlgDBalloc(g, NULL, Keys)) { |
3039 | sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n); |
3040 | return true; // Error |
3041 | } // endif |
3042 | |
3043 | // Allocate the Valblock. The last parameter is to have rows filled |
3044 | // by blanks (if true) or keep the zero ending char (if false). |
3045 | // Currently we set it to true to be compatible with QRY blocks, |
3046 | // and the one before last is to enable length/type checking, set to |
3047 | // true if not a prefix key. |
3048 | Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true, un); |
3049 | Asc = sm; // Sort mode: Asc=true Desc=false |
3050 | Ndf = n; |
3051 | |
3052 | // Store this information to avoid sorting when already done |
3053 | if (Asc) |
3054 | IsSorted = colp->GetOpt() == 2; |
3055 | |
3056 | //SetNulls(colp->IsNullable()); for when null columns will be indexable |
3057 | Colp = colp; |
3058 | return false; |
3059 | } // end of Init |
3060 | |
3061 | #if defined(XMAP) |
3062 | /***********************************************************************/ |
3063 | /* KXYCOL MapInit: initialize and address storage. */ |
3064 | /* Key length kln can be smaller than column length for CHAR columns. */ |
3065 | /***********************************************************************/ |
3066 | BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m) |
3067 | { |
3068 | int len = colp->GetLength(), prec = colp->GetScale(); |
3069 | bool un = colp->IsUnsigned(); |
3070 | |
3071 | if (n[3] && colp->GetLength() > n[3] |
3072 | && colp->GetResultType() == TYPE_STRING) { |
3073 | len = n[3]; |
3074 | Prefix = true; |
3075 | } // endif kln |
3076 | |
3077 | Type = colp->GetResultType(); |
3078 | |
3079 | if (trace(1)) |
3080 | htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n" , |
3081 | this, colp, Type, n[0], len, m); |
3082 | |
3083 | // Allocate the Value object used when moving items |
3084 | Valp = AllocateValue(g, Type, len, prec, un); |
3085 | Klen = Valp->GetClen(); |
3086 | |
3087 | if (n[2]) { |
3088 | Bkeys.Size = n[2] * Klen; |
3089 | Bkeys.Memp = m; |
3090 | Bkeys.Sub = true; |
3091 | |
3092 | // Allocate the Valblk containing initial block key values |
3093 | Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true, un); |
3094 | } // endif nb |
3095 | |
3096 | Keys.Size = n[0] * Klen; |
3097 | Keys.Memp = m + Bkeys.Size; |
3098 | Keys.Sub = true; |
3099 | |
3100 | // Allocate the Valblock. Last two parameters are to have rows filled |
3101 | // by blanks (if true) or keep the zero ending char (if false). |
3102 | // Currently we set it to true to be compatible with QRY blocks, |
3103 | // and last one to enable type checking (no conversion). |
3104 | Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, !Prefix, true, un); |
3105 | |
3106 | if (n[1]) { |
3107 | Koff.Size = n[1] * sizeof(int); |
3108 | Koff.Memp = m + Bkeys.Size + Keys.Size; |
3109 | Koff.Sub = true; |
3110 | } // endif n[1] |
3111 | |
3112 | Ndf = n[0]; |
3113 | //IsSorted = colp->GetOpt() < 0; |
3114 | IsSorted = false; |
3115 | Colp = colp; |
3116 | return m + Bkeys.Size + Keys.Size + Koff.Size; |
3117 | } // end of MapInit |
3118 | #endif // XMAP |
3119 | |
3120 | /***********************************************************************/ |
3121 | /* Allocate the offset block used by intermediate key columns. */ |
3122 | /***********************************************************************/ |
3123 | int *KXYCOL::MakeOffset(PGLOBAL g, int n) |
3124 | { |
3125 | if (!Kof) { |
3126 | // Calculate the initial size of the offset |
3127 | Koff.Size = (n + 1) * sizeof(int); |
3128 | |
3129 | // Allocate the required memory |
3130 | if (!PlgDBalloc(g, NULL, Koff)) { |
3131 | strcpy(g->Message, MSG(KEY_ALLOC_ERR)); |
3132 | return NULL; // Error |
3133 | } // endif |
3134 | |
3135 | } else if (n) { |
3136 | // This is a reallocation call |
3137 | PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int)); |
3138 | } else |
3139 | PlgDBfree(Koff); |
3140 | |
3141 | return (int*)Kof; |
3142 | } // end of MakeOffset |
3143 | |
3144 | /***********************************************************************/ |
3145 | /* Make a front end array of key values that are the first value of */ |
3146 | /* each blocks (of size n). This to reduce paging in FastFind. */ |
3147 | /***********************************************************************/ |
3148 | bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size) |
3149 | { |
3150 | int i, k; |
3151 | |
3152 | // Calculate the size of the block array in the index |
3153 | Bkeys.Size = nb * Klen; |
3154 | |
3155 | // Allocate the required memory |
3156 | if (!PlgDBalloc(g, NULL, Bkeys)) { |
3157 | sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb); |
3158 | return true; // Error |
3159 | } // endif |
3160 | |
3161 | // Allocate the Valblk used to contains initial block key values |
3162 | Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec); |
3163 | |
3164 | // Populate the array with values |
3165 | for (i = k = 0; i < nb; i++, k += size) |
3166 | Blkp->SetValue(Kblp, i, k); |
3167 | |
3168 | return false; |
3169 | } // end of MakeBlockArray |
3170 | |
3171 | /***********************************************************************/ |
3172 | /* KXYCOL SetValue: read column value for nth array element. */ |
3173 | /***********************************************************************/ |
3174 | void KXYCOL::SetValue(PCOL colp, int i) |
3175 | { |
3176 | #if defined(_DEBUG) |
3177 | assert (Kblp != NULL); |
3178 | #endif |
3179 | |
3180 | Kblp->SetValue(colp->GetValue(), i); |
3181 | } // end of SetValue |
3182 | |
3183 | /***********************************************************************/ |
3184 | /* InitFind: initialize finding the rank of column value in index. */ |
3185 | /***********************************************************************/ |
3186 | bool KXYCOL::InitFind(PGLOBAL g, PXOB xp) |
3187 | { |
3188 | if (xp->GetType() == TYPE_CONST) { |
3189 | if (Kxp->Nth) |
3190 | return true; |
3191 | |
3192 | Valp->SetValue_pval(xp->GetValue(), !Prefix); |
3193 | } else { |
3194 | xp->Reset(); |
3195 | xp->Eval(g); |
3196 | Valp->SetValue_pval(xp->GetValue(), false); |
3197 | } // endif Type |
3198 | |
3199 | if (trace(2)) { |
3200 | char buf[32]; |
3201 | |
3202 | htrc("KCOL InitFind: value=%s\n" , Valp->GetCharString(buf)); |
3203 | } // endif trace |
3204 | |
3205 | return false; |
3206 | } // end of InitFind |
3207 | |
3208 | #if 0 |
3209 | /***********************************************************************/ |
3210 | /* InitBinFind: initialize Value to the value pointed by vp. */ |
3211 | /***********************************************************************/ |
3212 | void KXYCOL::InitBinFind(void *vp) |
3213 | { |
3214 | Valp->SetBinValue(vp); |
3215 | } // end of InitBinFind |
3216 | #endif // 0 |
3217 | |
3218 | /***********************************************************************/ |
3219 | /* KXYCOL FillValue: called by COLBLK::Eval when a column value is */ |
3220 | /* already in storage in the corresponding KXYCOL. */ |
3221 | /***********************************************************************/ |
3222 | void KXYCOL::FillValue(PVAL valp) |
3223 | { |
3224 | valp->SetValue_pvblk(Kblp, Val_K); |
3225 | |
3226 | // Set null when applicable (NIY) |
3227 | //if (valp->GetNullable()) |
3228 | // valp->SetNull(valp->IsZero()); |
3229 | |
3230 | } // end of FillValue |
3231 | |
3232 | /***********************************************************************/ |
3233 | /* KXYCOL: Compare routine for one numeric value. */ |
3234 | /***********************************************************************/ |
3235 | int KXYCOL::Compare(int i1, int i2) |
3236 | { |
3237 | // Do the actual comparison between values. |
3238 | register int k = Kblp->CompVal(i1, i2); |
3239 | |
3240 | if (trace(4)) |
3241 | htrc("Compare done result=%d\n" , k); |
3242 | |
3243 | return (Asc) ? k : -k; |
3244 | } // end of Compare |
3245 | |
3246 | /***********************************************************************/ |
3247 | /* KXYCOL: Compare the ith key to the stored Value. */ |
3248 | /***********************************************************************/ |
3249 | int KXYCOL::CompVal(int i) |
3250 | { |
3251 | // Do the actual comparison between numerical values. |
3252 | if (trace(4)) { |
3253 | register int k = (int)Kblp->CompVal(Valp, (int)i); |
3254 | |
3255 | htrc("Compare done result=%d\n" , k); |
3256 | return k; |
3257 | } else |
3258 | return Kblp->CompVal(Valp, i); |
3259 | |
3260 | } // end of CompVal |
3261 | |
3262 | /***********************************************************************/ |
3263 | /* KXYCOL: Compare the key to the stored block value. */ |
3264 | /***********************************************************************/ |
3265 | int KXYCOL::CompBval(int i) |
3266 | { |
3267 | // Do the actual comparison between key values. |
3268 | return Blkp->CompVal(Valp, i); |
3269 | } // end of CompBval |
3270 | |
3271 | /***********************************************************************/ |
3272 | /* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */ |
3273 | /***********************************************************************/ |
3274 | void KXYCOL::ReAlloc(PGLOBAL g, int n) |
3275 | { |
3276 | PlgDBrealloc(g, NULL, Keys, n * Klen); |
3277 | Kblp->ReAlloc(To_Keys, n); |
3278 | Ndf = n; |
3279 | } // end of ReAlloc |
3280 | |
3281 | /***********************************************************************/ |
3282 | /* KXYCOL FreeData: Free To_Keys if it is not suballocated. */ |
3283 | /***********************************************************************/ |
3284 | void KXYCOL::FreeData(void) |
3285 | { |
3286 | PlgDBfree(Keys); |
3287 | Kblp = NULL; |
3288 | PlgDBfree(Bkeys); |
3289 | Blkp = NULL; |
3290 | PlgDBfree(Koff); |
3291 | Ndf = 0; |
3292 | } // end of FreeData |
3293 | |