| 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 | |