| 1 | /*********** File AM Map C++ Program Source Code File (.CPP) ***********/ |
| 2 | /* PROGRAM NAME: FILAMAP */ |
| 3 | /* ------------- */ |
| 4 | /* Version 1.6 */ |
| 5 | /* */ |
| 6 | /* COPYRIGHT: */ |
| 7 | /* ---------- */ |
| 8 | /* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ |
| 9 | /* */ |
| 10 | /* WHAT THIS PROGRAM DOES: */ |
| 11 | /* ----------------------- */ |
| 12 | /* This program are the MAP file access method classes. */ |
| 13 | /* */ |
| 14 | /***********************************************************************/ |
| 15 | |
| 16 | /***********************************************************************/ |
| 17 | /* Include relevant sections of the System header files. */ |
| 18 | /***********************************************************************/ |
| 19 | #include "my_global.h" |
| 20 | #if defined(__WIN__) |
| 21 | #if defined(__BORLANDC__) |
| 22 | #define __MFC_COMPAT__ // To define min/max as macro |
| 23 | #endif // __BORLANDC__ |
| 24 | //#include <windows.h> |
| 25 | #else // !__WIN__ |
| 26 | #if defined(UNIX) |
| 27 | #include <errno.h> |
| 28 | #include <unistd.h> |
| 29 | #else // !UNIX |
| 30 | #include <io.h> |
| 31 | #endif // !UNIX |
| 32 | #include <fcntl.h> |
| 33 | #endif // !__WIN__ |
| 34 | |
| 35 | /***********************************************************************/ |
| 36 | /* Include application header files: */ |
| 37 | /* global.h is header containing all global declarations. */ |
| 38 | /* plgdbsem.h is header containing the DB application declarations. */ |
| 39 | /* filamtxt.h is header containing the file AM classes declarations. */ |
| 40 | /* Note: these files are included inside the include files below. */ |
| 41 | /***********************************************************************/ |
| 42 | #include "global.h" |
| 43 | #include "plgdbsem.h" |
| 44 | #include "osutil.h" |
| 45 | #include "maputil.h" |
| 46 | #include "filamap.h" |
| 47 | #include "tabdos.h" |
| 48 | #include "tabfmt.h" |
| 49 | |
| 50 | /* --------------------------- Class MAPFAM -------------------------- */ |
| 51 | |
| 52 | /***********************************************************************/ |
| 53 | /* Constructors. */ |
| 54 | /***********************************************************************/ |
| 55 | MAPFAM::MAPFAM(PDOSDEF tdp) : TXTFAM(tdp) |
| 56 | { |
| 57 | Memory = NULL; |
| 58 | Mempos = NULL; |
| 59 | Tpos = NULL; |
| 60 | Fpos = NULL; |
| 61 | Spos = NULL; |
| 62 | Top = NULL; |
| 63 | } // end of MAPFAM standard constructor |
| 64 | |
| 65 | MAPFAM::MAPFAM(PMAPFAM tmfp) : TXTFAM(tmfp) |
| 66 | { |
| 67 | Memory = tmfp->Memory; |
| 68 | Mempos = tmfp->Mempos; |
| 69 | Fpos = tmfp->Fpos; |
| 70 | Spos = tmfp->Spos; |
| 71 | Tpos = tmfp->Tpos; |
| 72 | Top = tmfp->Top; |
| 73 | } // end of MAPFAM copy constructor |
| 74 | |
| 75 | /***********************************************************************/ |
| 76 | /* Reset: reset position values at the beginning of file. */ |
| 77 | /***********************************************************************/ |
| 78 | void MAPFAM::Reset(void) |
| 79 | { |
| 80 | TXTFAM::Reset(); |
| 81 | Fpos = Tpos = Spos = NULL; |
| 82 | } // end of Reset |
| 83 | |
| 84 | /***********************************************************************/ |
| 85 | /* MAP GetFileLength: returns file size in number of bytes. */ |
| 86 | /***********************************************************************/ |
| 87 | int MAPFAM::GetFileLength(PGLOBAL g) |
| 88 | { |
| 89 | int len; |
| 90 | |
| 91 | len = (To_Fb && To_Fb->Count) ? To_Fb->Length : TXTFAM::GetFileLength(g); |
| 92 | |
| 93 | if (trace(1)) |
| 94 | htrc("Mapped file length=%d\n" , len); |
| 95 | |
| 96 | return len; |
| 97 | } // end of GetFileLength |
| 98 | |
| 99 | /***********************************************************************/ |
| 100 | /* OpenTableFile: Open a DOS/UNIX table file as a mapped file. */ |
| 101 | /***********************************************************************/ |
| 102 | bool MAPFAM::OpenTableFile(PGLOBAL g) |
| 103 | { |
| 104 | char filename[_MAX_PATH]; |
| 105 | int len; |
| 106 | MODE mode = Tdbp->GetMode(); |
| 107 | PFBLOCK fp; |
| 108 | PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; |
| 109 | |
| 110 | #if defined(_DEBUG) |
| 111 | // Insert mode is no more handled using file mapping |
| 112 | assert(mode != MODE_INSERT); |
| 113 | #endif // _DEBUG |
| 114 | |
| 115 | /*********************************************************************/ |
| 116 | /* We used the file name relative to recorded datapath. */ |
| 117 | /*********************************************************************/ |
| 118 | PlugSetPath(filename, To_File, Tdbp->GetPath()); |
| 119 | |
| 120 | /*********************************************************************/ |
| 121 | /* Under Win32 the whole file will be mapped so we can use it as */ |
| 122 | /* if it were entirely read into virtual memory. */ |
| 123 | /* Firstly we check whether this file have been already mapped. */ |
| 124 | /*********************************************************************/ |
| 125 | if (mode == MODE_READ) { |
| 126 | for (fp = dbuserp->Openlist; fp; fp = fp->Next) |
| 127 | if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) |
| 128 | && fp->Count && fp->Mode == mode) |
| 129 | break; |
| 130 | |
| 131 | if (trace(1)) |
| 132 | htrc("Mapping file, fp=%p\n" , fp); |
| 133 | |
| 134 | } else |
| 135 | fp = NULL; |
| 136 | |
| 137 | if (fp) { |
| 138 | /*******************************************************************/ |
| 139 | /* File already mapped. Just increment use count and get pointer. */ |
| 140 | /*******************************************************************/ |
| 141 | fp->Count++; |
| 142 | Memory = fp->Memory; |
| 143 | len = fp->Length; |
| 144 | } else { |
| 145 | /*******************************************************************/ |
| 146 | /* If required, delete the whole file if no filtering is implied. */ |
| 147 | /*******************************************************************/ |
| 148 | bool del; |
| 149 | HANDLE hFile; |
| 150 | MEMMAP mm; |
| 151 | |
| 152 | del = mode == MODE_DELETE && !Tdbp->GetNext(); |
| 153 | |
| 154 | if (del) |
| 155 | DelRows = Cardinality(g); |
| 156 | |
| 157 | /*******************************************************************/ |
| 158 | /* Create the mapping file object. */ |
| 159 | /*******************************************************************/ |
| 160 | hFile = CreateFileMap(g, filename, &mm, mode, del); |
| 161 | |
| 162 | if (hFile == INVALID_HANDLE_VALUE) { |
| 163 | DWORD rc = GetLastError(); |
| 164 | |
| 165 | if (!(*g->Message)) |
| 166 | sprintf(g->Message, MSG(OPEN_MODE_ERROR), |
| 167 | "map" , (int) rc, filename); |
| 168 | |
| 169 | if (trace(1)) |
| 170 | htrc("CreateFileMap: %s\n" , g->Message); |
| 171 | |
| 172 | return (mode == MODE_READ && rc == ENOENT) |
| 173 | ? PushWarning(g, Tdbp) : true; |
| 174 | } // endif hFile |
| 175 | |
| 176 | /*******************************************************************/ |
| 177 | /* Get the file size (assuming file is smaller than 4 GB) */ |
| 178 | /*******************************************************************/ |
| 179 | len = mm.lenL; |
| 180 | Memory = (char *)mm.memory; |
| 181 | |
| 182 | if (!len) { // Empty or deleted file |
| 183 | CloseFileHandle(hFile); |
| 184 | Tdbp->ResetSize(); |
| 185 | return false; |
| 186 | } // endif len |
| 187 | |
| 188 | if (!Memory) { |
| 189 | CloseFileHandle(hFile); |
| 190 | sprintf(g->Message, MSG(MAP_VIEW_ERROR), |
| 191 | filename, GetLastError()); |
| 192 | return true; |
| 193 | } // endif Memory |
| 194 | |
| 195 | #if defined(__WIN__) |
| 196 | if (mode != MODE_DELETE) { |
| 197 | #else // !__WIN__ |
| 198 | if (mode == MODE_READ) { |
| 199 | #endif // !__WIN__ |
| 200 | CloseFileHandle(hFile); // Not used anymore |
| 201 | hFile = INVALID_HANDLE_VALUE; // For Fblock |
| 202 | } // endif Mode |
| 203 | |
| 204 | /*******************************************************************/ |
| 205 | /* Link a Fblock. This make possible to reuse already opened maps */ |
| 206 | /* and also to automatically unmap them in case of error g->jump. */ |
| 207 | /* Note: block can already exist for previously closed file. */ |
| 208 | /*******************************************************************/ |
| 209 | fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); |
| 210 | fp->Type = TYPE_FB_MAP; |
| 211 | fp->Fname = PlugDup(g, filename); |
| 212 | fp->Next = dbuserp->Openlist; |
| 213 | dbuserp->Openlist = fp; |
| 214 | fp->Count = 1; |
| 215 | fp->Length = len; |
| 216 | fp->Memory = Memory; |
| 217 | fp->Mode = mode; |
| 218 | fp->File = NULL; |
| 219 | fp->Handle = hFile; // Used for Delete |
| 220 | } // endif fp |
| 221 | |
| 222 | To_Fb = fp; // Useful when closing |
| 223 | |
| 224 | /*********************************************************************/ |
| 225 | /* The pseudo "buffer" is here the entire file mapping view. */ |
| 226 | /*********************************************************************/ |
| 227 | Fpos = Mempos = Memory; |
| 228 | Top = Memory + len; |
| 229 | |
| 230 | if (trace(1)) |
| 231 | htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n" , |
| 232 | fp, fp->Count, Memory, len, Top); |
| 233 | |
| 234 | return AllocateBuffer(g); // Useful for DBF files |
| 235 | } // end of OpenTableFile |
| 236 | |
| 237 | /***********************************************************************/ |
| 238 | /* GetRowID: return the RowID of last read record. */ |
| 239 | /***********************************************************************/ |
| 240 | int MAPFAM::GetRowID(void) |
| 241 | { |
| 242 | return Rows; |
| 243 | } // end of GetRowID |
| 244 | |
| 245 | /***********************************************************************/ |
| 246 | /* GetPos: return the position of last read record. */ |
| 247 | /***********************************************************************/ |
| 248 | int MAPFAM::GetPos(void) |
| 249 | { |
| 250 | return (int)(Fpos - Memory); |
| 251 | } // end of GetPos |
| 252 | |
| 253 | /***********************************************************************/ |
| 254 | /* GetNextPos: return the position of next record. */ |
| 255 | /***********************************************************************/ |
| 256 | int MAPFAM::GetNextPos(void) |
| 257 | { |
| 258 | return (int)(Mempos - Memory); |
| 259 | } // end of GetNextPos |
| 260 | |
| 261 | /***********************************************************************/ |
| 262 | /* SetPos: Replace the table at the specified position. */ |
| 263 | /***********************************************************************/ |
| 264 | bool MAPFAM::SetPos(PGLOBAL g, int pos) |
| 265 | { |
| 266 | Fpos = Mempos = Memory + pos; |
| 267 | |
| 268 | if (Mempos >= Top || Mempos < Memory) { |
| 269 | strcpy(g->Message, MSG(INV_MAP_POS)); |
| 270 | return true; |
| 271 | } // endif Mempos |
| 272 | |
| 273 | Placed = true; |
| 274 | return false; |
| 275 | } // end of SetPos |
| 276 | |
| 277 | /***********************************************************************/ |
| 278 | /* Record file position in case of UPDATE or DELETE. */ |
| 279 | /***********************************************************************/ |
| 280 | bool MAPFAM::RecordPos(PGLOBAL) |
| 281 | { |
| 282 | Fpos = Mempos; |
| 283 | return false; |
| 284 | } // end of RecordPos |
| 285 | |
| 286 | /***********************************************************************/ |
| 287 | /* Initialize Fpos and Mempos for indexed DELETE. */ |
| 288 | /***********************************************************************/ |
| 289 | int MAPFAM::InitDelete(PGLOBAL, int fpos, int spos) |
| 290 | { |
| 291 | Fpos = Memory + (ptrdiff_t)fpos; |
| 292 | Mempos = Memory + (ptrdiff_t)spos; |
| 293 | return RC_OK; |
| 294 | } // end of InitDelete |
| 295 | |
| 296 | /***********************************************************************/ |
| 297 | /* Skip one record in file. */ |
| 298 | /***********************************************************************/ |
| 299 | int MAPFAM::SkipRecord(PGLOBAL g, bool ) |
| 300 | { |
| 301 | PDBUSER dup = (PDBUSER)g->Activityp->Aptr; |
| 302 | |
| 303 | // Skip this record |
| 304 | while (*Mempos++ != '\n') // What about Unix ??? |
| 305 | if (Mempos == Top) |
| 306 | return RC_EF; |
| 307 | |
| 308 | // Update progress information |
| 309 | dup->ProgCur = GetPos(); |
| 310 | |
| 311 | if (header) |
| 312 | Fpos = Tpos = Spos = Mempos; // For Delete |
| 313 | |
| 314 | return RC_OK; |
| 315 | } // end of SkipRecord |
| 316 | |
| 317 | /***********************************************************************/ |
| 318 | /* ReadBuffer: Read one line for a mapped text file. */ |
| 319 | /***********************************************************************/ |
| 320 | int MAPFAM::ReadBuffer(PGLOBAL g) |
| 321 | { |
| 322 | int rc, len, n = 1; |
| 323 | |
| 324 | // Are we at the end of the memory |
| 325 | if (Mempos >= Top) { |
| 326 | if ((rc = GetNext(g)) != RC_OK) |
| 327 | return rc; |
| 328 | else if (Tdbp->GetAmType() == TYPE_AM_CSV && ((PTDBCSV)Tdbp)->Header) |
| 329 | if ((rc = SkipRecord(g, true)) != RC_OK) |
| 330 | return rc; |
| 331 | |
| 332 | } // endif Mempos |
| 333 | |
| 334 | |
| 335 | if (!Placed) { |
| 336 | /*******************************************************************/ |
| 337 | /* Record file position in case of UPDATE or DELETE. */ |
| 338 | /*******************************************************************/ |
| 339 | next: |
| 340 | Fpos = Mempos; |
| 341 | CurBlk = (int)Rows++; |
| 342 | |
| 343 | /*******************************************************************/ |
| 344 | /* Check whether optimization on ROWID */ |
| 345 | /* can be done, as well as for join as for local filtering. */ |
| 346 | /*******************************************************************/ |
| 347 | switch (Tdbp->TestBlock(g)) { |
| 348 | case RC_EF: |
| 349 | if ((rc = GetNext(g)) != RC_OK) |
| 350 | return rc; |
| 351 | |
| 352 | case RC_NF: |
| 353 | // Skip this record |
| 354 | if ((rc = SkipRecord(g, false)) != RC_OK) |
| 355 | return rc; |
| 356 | |
| 357 | goto next; |
| 358 | } // endswitch rc |
| 359 | |
| 360 | } else |
| 361 | Placed = false; |
| 362 | |
| 363 | // Immediately calculate next position (Used by DeleteDB) |
| 364 | while (*Mempos++ != '\n') // What about Unix ??? |
| 365 | if (Mempos == Top) { |
| 366 | n = 0; |
| 367 | break; |
| 368 | } // endif Mempos |
| 369 | |
| 370 | // Set caller line buffer |
| 371 | len = (int)(Mempos - Fpos) - n; |
| 372 | |
| 373 | // Don't rely on ENDING setting |
| 374 | if (len > 0 && *(Mempos - 2) == '\r') |
| 375 | len--; // Line ends by CRLF |
| 376 | |
| 377 | memcpy(Tdbp->GetLine(), Fpos, len); |
| 378 | Tdbp->GetLine()[len] = '\0'; |
| 379 | return RC_OK; |
| 380 | } // end of ReadBuffer |
| 381 | |
| 382 | /***********************************************************************/ |
| 383 | /* WriteBuffer: File write routine for MAP access method. */ |
| 384 | /***********************************************************************/ |
| 385 | int MAPFAM::WriteBuffer(PGLOBAL g __attribute__((unused))) |
| 386 | { |
| 387 | #if defined(_DEBUG) |
| 388 | // Insert mode is no more handled using file mapping |
| 389 | if (Tdbp->GetMode() == MODE_INSERT) { |
| 390 | strcpy(g->Message, MSG(NO_MAP_INSERT)); |
| 391 | return RC_FX; |
| 392 | } // endif |
| 393 | #endif // _DEBUG |
| 394 | |
| 395 | /*********************************************************************/ |
| 396 | /* Copy the updated record back into the memory mapped file. */ |
| 397 | /*********************************************************************/ |
| 398 | memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine())); |
| 399 | return RC_OK; |
| 400 | } // end of WriteBuffer |
| 401 | |
| 402 | /***********************************************************************/ |
| 403 | /* Data Base delete line routine for MAP (and FIX?) access methods. */ |
| 404 | /* Lines between deleted lines are moved in the mapfile view. */ |
| 405 | /***********************************************************************/ |
| 406 | int MAPFAM::DeleteRecords(PGLOBAL g, int irc) |
| 407 | { |
| 408 | int n; |
| 409 | |
| 410 | if (trace(1)) |
| 411 | htrc("MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n" , |
| 412 | irc, Mempos, To_Buf, Tpos, Spos); |
| 413 | |
| 414 | if (irc != RC_OK) { |
| 415 | /*******************************************************************/ |
| 416 | /* EOF: position Fpos at the top of map position. */ |
| 417 | /*******************************************************************/ |
| 418 | Fpos = Top; |
| 419 | |
| 420 | if (trace(1)) |
| 421 | htrc("Fpos placed at file top=%p\n" , Fpos); |
| 422 | |
| 423 | } // endif irc |
| 424 | |
| 425 | if (Tpos == Spos) { |
| 426 | /*******************************************************************/ |
| 427 | /* First line to delete. Move of eventual preceding lines is */ |
| 428 | /* not required here, just setting of future Spos and Tpos. */ |
| 429 | /*******************************************************************/ |
| 430 | Tpos = Spos = Fpos; |
| 431 | } else if ((n = (int)(Fpos - Spos)) > 0) { |
| 432 | /*******************************************************************/ |
| 433 | /* Non consecutive line to delete. Move intermediate lines. */ |
| 434 | /*******************************************************************/ |
| 435 | memmove(Tpos, Spos, n); |
| 436 | Tpos += n; |
| 437 | |
| 438 | if (trace(1)) |
| 439 | htrc("move %d bytes\n" , n); |
| 440 | |
| 441 | } // endif n |
| 442 | |
| 443 | if (irc == RC_OK) { |
| 444 | Spos = Mempos; // New start position |
| 445 | |
| 446 | if (trace(1)) |
| 447 | htrc("after: Tpos=%p Spos=%p\n" , Tpos, Spos); |
| 448 | |
| 449 | } else if (To_Fb) { // Can be NULL for deleted files |
| 450 | /*******************************************************************/ |
| 451 | /* Last call after EOF has been reached. */ |
| 452 | /* We must firstly Unmap the view and use the saved file handle */ |
| 453 | /* to put an EOF at the end of the copied part of the file. */ |
| 454 | /*******************************************************************/ |
| 455 | PFBLOCK fp = To_Fb; |
| 456 | |
| 457 | CloseMemMap(fp->Memory, (size_t)fp->Length); |
| 458 | fp->Count = 0; // Avoid doing it twice |
| 459 | |
| 460 | if (!Abort) { |
| 461 | /*****************************************************************/ |
| 462 | /* Remove extra records. */ |
| 463 | /*****************************************************************/ |
| 464 | n = (int)(Tpos - Memory); |
| 465 | |
| 466 | #if defined(__WIN__) |
| 467 | DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); |
| 468 | |
| 469 | if (drc == 0xFFFFFFFF) { |
| 470 | sprintf(g->Message, MSG(FUNCTION_ERROR), |
| 471 | "SetFilePointer" , GetLastError()); |
| 472 | CloseHandle(fp->Handle); |
| 473 | return RC_FX; |
| 474 | } // endif |
| 475 | |
| 476 | if (trace(1)) |
| 477 | htrc("done, Tpos=%p newsize=%d drc=%d\n" , Tpos, n, drc); |
| 478 | |
| 479 | if (!SetEndOfFile(fp->Handle)) { |
| 480 | sprintf(g->Message, MSG(FUNCTION_ERROR), |
| 481 | "SetEndOfFile" , GetLastError()); |
| 482 | CloseHandle(fp->Handle); |
| 483 | return RC_FX; |
| 484 | } // endif |
| 485 | |
| 486 | #else // UNIX |
| 487 | if (ftruncate(fp->Handle, (off_t)n)) { |
| 488 | sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); |
| 489 | close(fp->Handle); |
| 490 | return RC_FX; |
| 491 | } // endif |
| 492 | |
| 493 | #endif // UNIX |
| 494 | } // endif Abort |
| 495 | |
| 496 | #if defined(__WIN__) |
| 497 | CloseHandle(fp->Handle); |
| 498 | #else // UNIX |
| 499 | close(fp->Handle); |
| 500 | #endif // UNIX |
| 501 | } // endif irc |
| 502 | |
| 503 | return RC_OK; // All is correct |
| 504 | } // end of DeleteRecords |
| 505 | |
| 506 | /***********************************************************************/ |
| 507 | /* Table file close routine for MAP access method. */ |
| 508 | /***********************************************************************/ |
| 509 | void MAPFAM::CloseTableFile(PGLOBAL g, bool) |
| 510 | { |
| 511 | PlugCloseFile(g, To_Fb); |
| 512 | //To_Fb = NULL; // To get correct file size in Cardinality |
| 513 | |
| 514 | if (trace(1)) |
| 515 | htrc("MAP Close: closing %s count=%d\n" , |
| 516 | To_File, (To_Fb) ? To_Fb->Count : 0); |
| 517 | |
| 518 | } // end of CloseTableFile |
| 519 | |
| 520 | /***********************************************************************/ |
| 521 | /* Rewind routine for MAP access method. */ |
| 522 | /***********************************************************************/ |
| 523 | void MAPFAM::Rewind(void) |
| 524 | { |
| 525 | Mempos = Memory; |
| 526 | } // end of Rewind |
| 527 | |
| 528 | /* --------------------------- Class MBKFAM -------------------------- */ |
| 529 | |
| 530 | /***********************************************************************/ |
| 531 | /* Constructors. */ |
| 532 | /***********************************************************************/ |
| 533 | MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp) |
| 534 | { |
| 535 | Blocked = true; |
| 536 | Block = tdp->GetBlock(); |
| 537 | Last = tdp->GetLast(); |
| 538 | Nrec = tdp->GetElemt(); |
| 539 | BlkPos = tdp->GetTo_Pos(); |
| 540 | CurNum = Nrec; |
| 541 | } // end of MBKFAM standard constructor |
| 542 | |
| 543 | /***********************************************************************/ |
| 544 | /* Reset: reset position values at the beginning of file. */ |
| 545 | /***********************************************************************/ |
| 546 | void MBKFAM::Reset(void) |
| 547 | { |
| 548 | MAPFAM::Reset(); |
| 549 | CurNum = Nrec; // To start by a new block |
| 550 | } // end of Reset |
| 551 | |
| 552 | /***********************************************************************/ |
| 553 | /* Cardinality: returns table cardinality in number of rows. */ |
| 554 | /* This function can be called with a null argument to test the */ |
| 555 | /* availability of Cardinality implementation (1 yes, 0 no). */ |
| 556 | /***********************************************************************/ |
| 557 | int MBKFAM::Cardinality(PGLOBAL g) |
| 558 | { |
| 559 | return (g) ? (int)((Block - 1) * Nrec + Last) : 1; |
| 560 | } // end of Cardinality |
| 561 | |
| 562 | /***********************************************************************/ |
| 563 | /* Skip one record in file. */ |
| 564 | /***********************************************************************/ |
| 565 | int MBKFAM::SkipRecord(PGLOBAL, bool) |
| 566 | { |
| 567 | return RC_OK; |
| 568 | } // end of SkipRecord |
| 569 | |
| 570 | /***********************************************************************/ |
| 571 | /* GetRowID: return the RowID of last read record. */ |
| 572 | /***********************************************************************/ |
| 573 | int MBKFAM::GetRowID(void) |
| 574 | { |
| 575 | return CurNum + Nrec * CurBlk + 1; |
| 576 | } // end of GetRowID |
| 577 | |
| 578 | /***********************************************************************/ |
| 579 | /* ReadBuffer: Read one line for a mapped Fix file. */ |
| 580 | /***********************************************************************/ |
| 581 | int MBKFAM::ReadBuffer(PGLOBAL g) |
| 582 | { |
| 583 | int rc, len; |
| 584 | |
| 585 | /*********************************************************************/ |
| 586 | /* Sequential block reading when Placed is not true. */ |
| 587 | /*********************************************************************/ |
| 588 | if (Placed) { |
| 589 | Placed = false; |
| 590 | } else if (Mempos >= Top) { // Are we at the end of the memory |
| 591 | if ((rc = GetNext(g)) != RC_OK) |
| 592 | return rc; |
| 593 | |
| 594 | } else if (++CurNum < Nrec) { |
| 595 | Fpos = Mempos; |
| 596 | } else { |
| 597 | /*******************************************************************/ |
| 598 | /* New block. */ |
| 599 | /*******************************************************************/ |
| 600 | CurNum = 0; |
| 601 | |
| 602 | next: |
| 603 | if (++CurBlk >= Block) |
| 604 | if ((rc = GetNext(g)) != RC_OK) |
| 605 | return rc; |
| 606 | |
| 607 | /*******************************************************************/ |
| 608 | /* Before reading a new block, check whether block optimization */ |
| 609 | /* can be done, as well as for join as for local filtering. */ |
| 610 | /*******************************************************************/ |
| 611 | switch (Tdbp->TestBlock(g)) { |
| 612 | case RC_EF: |
| 613 | if ((rc = GetNext(g)) != RC_OK) |
| 614 | return rc; |
| 615 | |
| 616 | break; |
| 617 | case RC_NF: |
| 618 | goto next; |
| 619 | } // endswitch rc |
| 620 | |
| 621 | Fpos = Mempos = Memory + BlkPos[CurBlk]; |
| 622 | } // endif's |
| 623 | |
| 624 | // Immediately calculate next position (Used by DeleteDB) |
| 625 | while (*Mempos++ != '\n') // What about Unix ??? |
| 626 | if (Mempos == Top) |
| 627 | break; |
| 628 | |
| 629 | // Set caller line buffer |
| 630 | len = (int)(Mempos - Fpos) - Ending; |
| 631 | memcpy(Tdbp->GetLine(), Fpos, len); |
| 632 | Tdbp->GetLine()[len] = '\0'; |
| 633 | return RC_OK; |
| 634 | } // end of ReadBuffer |
| 635 | |
| 636 | /***********************************************************************/ |
| 637 | /* Rewind routine for FIX MAP access method. */ |
| 638 | /***********************************************************************/ |
| 639 | void MBKFAM::Rewind(void) |
| 640 | { |
| 641 | Mempos = Memory + Headlen; |
| 642 | CurBlk = -1; |
| 643 | CurNum = Nrec; |
| 644 | } // end of Rewind |
| 645 | |
| 646 | /* --------------------------- Class MPXFAM -------------------------- */ |
| 647 | |
| 648 | /***********************************************************************/ |
| 649 | /* Constructors. */ |
| 650 | /***********************************************************************/ |
| 651 | MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp) |
| 652 | { |
| 653 | Blksize = tdp->GetBlksize(); |
| 654 | Padded = tdp->GetPadded(); |
| 655 | |
| 656 | if (Padded && Blksize) |
| 657 | Nrec = Blksize / Lrecl; |
| 658 | else { |
| 659 | Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; |
| 660 | Blksize = Nrec * Lrecl; |
| 661 | Padded = false; |
| 662 | } // endelse |
| 663 | |
| 664 | CurNum = Nrec; |
| 665 | } // end of MPXFAM standard constructor |
| 666 | |
| 667 | #if 0 // MBKFAM routine is correct |
| 668 | /***********************************************************************/ |
| 669 | /* GetRowID: return the RowID of last read record. */ |
| 670 | /***********************************************************************/ |
| 671 | int MPXFAM::GetRowID(void) |
| 672 | { |
| 673 | return (Mempos - Memory - Headlen) / Lrecl; |
| 674 | } // end of GetRowID |
| 675 | #endif |
| 676 | |
| 677 | /***********************************************************************/ |
| 678 | /* GetPos: return the position of last read record. */ |
| 679 | /***********************************************************************/ |
| 680 | int MPXFAM::GetPos(void) |
| 681 | { |
| 682 | return (CurNum + Nrec * CurBlk); // Computed file index |
| 683 | } // end of GetPos |
| 684 | |
| 685 | /***********************************************************************/ |
| 686 | /* SetPos: Replace the table at the specified position. */ |
| 687 | /***********************************************************************/ |
| 688 | bool MPXFAM::SetPos(PGLOBAL g, int pos) |
| 689 | { |
| 690 | if (pos < 0) { |
| 691 | strcpy(g->Message, MSG(INV_REC_POS)); |
| 692 | return true; |
| 693 | } // endif recpos |
| 694 | |
| 695 | CurBlk = pos / Nrec; |
| 696 | CurNum = pos % Nrec; |
| 697 | Fpos = Mempos = Memory + Headlen + pos * Lrecl; |
| 698 | |
| 699 | // Indicate the table position was externally set |
| 700 | Placed = true; |
| 701 | return false; |
| 702 | } // end of SetPos |
| 703 | |
| 704 | /***********************************************************************/ |
| 705 | /* Initialize CurBlk, CurNum, Mempos and Fpos for indexed DELETE. */ |
| 706 | /***********************************************************************/ |
| 707 | int MPXFAM::InitDelete(PGLOBAL, int fpos, int) |
| 708 | { |
| 709 | Fpos = Memory + Headlen + (ptrdiff_t)fpos * Lrecl; |
| 710 | Mempos = Fpos + Lrecl; |
| 711 | return RC_OK; |
| 712 | } // end of InitDelete |
| 713 | |
| 714 | /***********************************************************************/ |
| 715 | /* ReadBuffer: Read one line for a mapped Fix file. */ |
| 716 | /***********************************************************************/ |
| 717 | int MPXFAM::ReadBuffer(PGLOBAL g) |
| 718 | { |
| 719 | int rc; |
| 720 | |
| 721 | /*********************************************************************/ |
| 722 | /* Sequential block reading when Placed is not true. */ |
| 723 | /*********************************************************************/ |
| 724 | if (Placed) { |
| 725 | Placed = false; |
| 726 | } else if (Mempos >= Top) { // Are we at the end of the memory |
| 727 | if ((rc = GetNext(g)) != RC_OK) |
| 728 | return rc; |
| 729 | |
| 730 | } else if (++CurNum < Nrec) { |
| 731 | Fpos = Mempos; |
| 732 | } else { |
| 733 | /*******************************************************************/ |
| 734 | /* New block. */ |
| 735 | /*******************************************************************/ |
| 736 | CurNum = 0; |
| 737 | |
| 738 | next: |
| 739 | if (++CurBlk >= Block) |
| 740 | return GetNext(g); |
| 741 | |
| 742 | /*******************************************************************/ |
| 743 | /* Before reading a new block, check whether block optimization */ |
| 744 | /* can be done, as well as for join as for local filtering. */ |
| 745 | /*******************************************************************/ |
| 746 | switch (Tdbp->TestBlock(g)) { |
| 747 | case RC_EF: |
| 748 | if ((rc = GetNext(g)) != RC_OK) |
| 749 | return rc; |
| 750 | |
| 751 | break; |
| 752 | case RC_NF: |
| 753 | goto next; |
| 754 | } // endswitch rc |
| 755 | |
| 756 | Fpos = Mempos = Headlen + Memory + CurBlk * Blksize; |
| 757 | } // endif's |
| 758 | |
| 759 | Tdbp->SetLine(Mempos); |
| 760 | |
| 761 | // Immediately calculate next position (Used by DeleteDB) |
| 762 | Mempos += Lrecl; |
| 763 | return RC_OK; |
| 764 | } // end of ReadBuffer |
| 765 | |
| 766 | /***********************************************************************/ |
| 767 | /* WriteBuffer: File write routine for MAP access method. */ |
| 768 | /***********************************************************************/ |
| 769 | int MPXFAM::WriteBuffer(PGLOBAL g __attribute__((unused))) |
| 770 | { |
| 771 | #if defined(_DEBUG) |
| 772 | // Insert mode is no more handled using file mapping |
| 773 | if (Tdbp->GetMode() == MODE_INSERT) { |
| 774 | strcpy(g->Message, MSG(NO_MAP_INSERT)); |
| 775 | return RC_FX; |
| 776 | } // endif |
| 777 | #endif // _DEBUG |
| 778 | |
| 779 | // In Update mode, file was modified in memory |
| 780 | return RC_OK; |
| 781 | } // end of WriteBuffer |
| 782 | |
| 783 | |