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