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-2020    */
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(_WIN32)
21 #if defined(__BORLANDC__)
22 #define __MFC_COMPAT__                   // To define min/max as macro
23 #endif   // __BORLANDC__
24 //#include <windows.h>
25 #else    // !_WIN32
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  // !_WIN32
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 /***********************************************************************/
MAPFAM(PDOSDEF tdp)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 
MAPFAM(PMAPFAM tmfp)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 /***********************************************************************/
Reset(void)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 /***********************************************************************/
GetFileLength(PGLOBAL g)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 /***********************************************************************/
OpenTableFile(PGLOBAL g)102 bool MAPFAM::OpenTableFile(PGLOBAL g)
103   {
104   char    filename[_MAX_PATH];
105   size_t  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         ? false : true;
174 //      ? PushWarning(g, Tdbp) : true; --> assert fails into MariaDB
175       } // endif hFile
176 
177     /*******************************************************************/
178     /*  Get the file size.                                             */
179     /*******************************************************************/
180 		len = (size_t)mm.lenL;
181 
182 		if (mm.lenH)
183 			len += ((size_t)mm.lenH * 0x000000001LL);
184 
185     Memory = (char *)mm.memory;
186 
187     if (!len) {              // Empty or deleted file
188       CloseFileHandle(hFile);
189       Tdbp->ResetSize();
190       return false;
191       } // endif len
192 
193     if (!Memory) {
194       CloseFileHandle(hFile);
195       sprintf(g->Message, MSG(MAP_VIEW_ERROR),
196                           filename, GetLastError());
197       return true;
198       } // endif Memory
199 
200 #if defined(_WIN32)
201     if (mode != MODE_DELETE) {
202 #else   // !_WIN32
203     if (mode == MODE_READ) {
204 #endif  // !_WIN32
205       CloseFileHandle(hFile);                    // Not used anymore
206       hFile = INVALID_HANDLE_VALUE;              // For Fblock
207       } // endif Mode
208 
209     /*******************************************************************/
210     /*  Link a Fblock. This make possible to reuse already opened maps */
211     /*  and also to automatically unmap them in case of error g->jump. */
212     /*  Note: block can already exist for previously closed file.      */
213     /*******************************************************************/
214     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
215     fp->Type = TYPE_FB_MAP;
216     fp->Fname = PlugDup(g, filename);
217     fp->Next = dbuserp->Openlist;
218     dbuserp->Openlist = fp;
219     fp->Count = 1;
220     fp->Length = len;
221     fp->Memory = Memory;
222     fp->Mode = mode;
223     fp->File = NULL;
224     fp->Handle = hFile;                // Used for Delete
225   } // endif fp
226 
227   To_Fb = fp;                               // Useful when closing
228 
229   /*********************************************************************/
230   /*  The pseudo "buffer" is here the entire file mapping view.        */
231   /*********************************************************************/
232   Fpos = Mempos = Memory;
233   Top = Memory + len;
234 
235   if (trace(1))
236     htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
237           fp, fp->Count, Memory, len, Top);
238 
239   return AllocateBuffer(g);          // Useful for DBF files
240   } // end of OpenTableFile
241 
242 /***********************************************************************/
243 /*  GetRowID: return the RowID of last read record.                    */
244 /***********************************************************************/
245 int MAPFAM::GetRowID(void)
246   {
247   return Rows;
248   } // end of GetRowID
249 
250 /***********************************************************************/
251 /*  GetPos: return the position of last read record.                   */
252 /***********************************************************************/
253 int MAPFAM::GetPos(void)
254   {
255   return (int)(Fpos - Memory);
256   } // end of GetPos
257 
258 /***********************************************************************/
259 /*  GetNextPos: return the position of next record.                    */
260 /***********************************************************************/
261 int MAPFAM::GetNextPos(void)
262   {
263   return (int)(Mempos - Memory);
264   } // end of GetNextPos
265 
266 /***********************************************************************/
267 /*  SetPos: Replace the table at the specified position.               */
268 /***********************************************************************/
269 bool MAPFAM::SetPos(PGLOBAL g, int pos)
270   {
271   Fpos = Mempos = Memory + pos;
272 
273   if (Mempos >= Top || Mempos < Memory) {
274     strcpy(g->Message, MSG(INV_MAP_POS));
275     return true;
276     } // endif Mempos
277 
278   Placed = true;
279   return false;
280   } // end of SetPos
281 
282 /***********************************************************************/
283 /*  Record file position in case of UPDATE or DELETE.                  */
284 /***********************************************************************/
285 bool MAPFAM::RecordPos(PGLOBAL)
286   {
287   Fpos = Mempos;
288   return false;
289   } // end of RecordPos
290 
291 /***********************************************************************/
292 /*  Initialize Fpos and Mempos for indexed DELETE.                     */
293 /***********************************************************************/
294 int MAPFAM::InitDelete(PGLOBAL, int fpos, int spos)
295   {
296   Fpos = Memory + (ptrdiff_t)fpos;
297   Mempos = Memory + (ptrdiff_t)spos;
298   return RC_OK;
299   } // end of InitDelete
300 
301 /***********************************************************************/
302 /*  Skip one record in file.                                           */
303 /***********************************************************************/
304 int MAPFAM::SkipRecord(PGLOBAL g, bool header)
305   {
306   PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
307 
308   // Skip this record
309   while (*Mempos++ != '\n')					 		// What about Unix ???
310 		if (Mempos == Top)
311       return RC_EF;
312 
313   // Update progress information
314   dup->ProgCur = GetPos();
315 
316   if (header)
317     Fpos = Tpos = Spos = Mempos;      // For Delete
318 
319   return RC_OK;
320   } // end of SkipRecord
321 
322 /***********************************************************************/
323 /*  ReadBuffer: Read one line for a mapped text file.                  */
324 /***********************************************************************/
325 int MAPFAM::ReadBuffer(PGLOBAL g)
326   {
327   int rc, len, n = 1;
328 
329   // Are we at the end of the memory
330 	if (Mempos >= Top) {
331 		if ((rc = GetNext(g)) != RC_OK)
332 			return rc;
333 		else if (Tdbp->GetAmType() == TYPE_AM_CSV && ((PTDBCSV)Tdbp)->Header)
334 			if ((rc = SkipRecord(g, true)) != RC_OK)
335 				return rc;
336 
337 	}	// endif Mempos
338 
339 
340   if (!Placed) {
341     /*******************************************************************/
342     /*  Record file position in case of UPDATE or DELETE.              */
343     /*******************************************************************/
344    next:
345     Fpos = Mempos;
346     CurBlk = (int)Rows++;
347 
348     /*******************************************************************/
349     /*  Check whether optimization on ROWID                            */
350     /*  can be done, as well as for join as for local filtering.       */
351     /*******************************************************************/
352     switch (Tdbp->TestBlock(g)) {
353       case RC_EF:
354 				if ((rc = GetNext(g)) != RC_OK)
355 					return rc;
356 
357       /* falls through */
358       case RC_NF:
359         // Skip this record
360         if ((rc = SkipRecord(g, false)) != RC_OK)
361           return rc;
362 
363         goto next;
364       } // endswitch rc
365 
366   } else
367     Placed = false;
368 
369   // Immediately calculate next position (Used by DeleteDB)
370   while (*Mempos++ != '\n')          // What about Unix ???
371 		if (Mempos == Top) {
372 			n = 0;
373 			break;
374 		}	// endif Mempos
375 
376   // Set caller line buffer
377   len = (int)(Mempos - Fpos) - n;
378 
379   // Don't rely on ENDING setting
380   if (len > 0 && *(Mempos - 2) == '\r')
381     len--;             // Line ends by CRLF
382 
383   memcpy(Tdbp->GetLine(), Fpos, len);
384   Tdbp->GetLine()[len] = '\0';
385   return RC_OK;
386   } // end of ReadBuffer
387 
388 /***********************************************************************/
389 /*  WriteBuffer: File write routine for MAP access method.             */
390 /***********************************************************************/
391 int MAPFAM::WriteBuffer(PGLOBAL g __attribute__((unused)))
392   {
393 #if defined(_DEBUG)
394   // Insert mode is no more handled using file mapping
395   if (Tdbp->GetMode() == MODE_INSERT) {
396     strcpy(g->Message, MSG(NO_MAP_INSERT));
397     return RC_FX;
398     } // endif
399 #endif   // _DEBUG
400 
401   /*********************************************************************/
402   /*  Copy the updated record back into the memory mapped file.        */
403   /*********************************************************************/
404   memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
405   return RC_OK;
406   } // end of WriteBuffer
407 
408 /***********************************************************************/
409 /*  Data Base delete line routine for MAP (and FIX?) access methods.   */
410 /*  Lines between deleted lines are moved in the mapfile view.         */
411 /***********************************************************************/
412 int MAPFAM::DeleteRecords(PGLOBAL g, int irc)
413   {
414   int    n;
415 
416   if (trace(1))
417     htrc("MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n",
418          irc, Mempos, To_Buf, Tpos, Spos);
419 
420   if (irc != RC_OK) {
421     /*******************************************************************/
422     /*  EOF: position Fpos at the top of map position.                 */
423     /*******************************************************************/
424     Fpos = Top;
425 
426     if (trace(1))
427       htrc("Fpos placed at file top=%p\n", Fpos);
428 
429     } // endif irc
430 
431   if (Tpos == Spos) {
432     /*******************************************************************/
433     /*  First line to delete. Move of eventual preceding lines is     */
434     /*  not required here, just setting of future Spos and Tpos.       */
435     /*******************************************************************/
436     Tpos = Spos = Fpos;
437   } else if ((n = (int)(Fpos - Spos)) > 0) {
438     /*******************************************************************/
439     /*  Non consecutive line to delete. Move intermediate lines.       */
440     /*******************************************************************/
441     memmove(Tpos, Spos, n);
442     Tpos += n;
443 
444     if (trace(1))
445       htrc("move %d bytes\n", n);
446 
447   } // endif n
448 
449   if (irc == RC_OK) {
450     Spos = Mempos;                               // New start position
451 
452     if (trace(1))
453       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
454 
455   } else if (To_Fb) {                 // Can be NULL for deleted files
456     /*******************************************************************/
457     /*  Last call after EOF has been reached.                          */
458     /*  We must firstly Unmap the view and use the saved file handle   */
459     /*  to put an EOF at the end of the copied part of the file.       */
460     /*******************************************************************/
461     PFBLOCK fp = To_Fb;
462 
463     CloseMemMap(fp->Memory, (size_t)fp->Length);
464     fp->Count = 0;                             // Avoid doing it twice
465 
466     if (!Abort) {
467       /*****************************************************************/
468       /*  Remove extra records.                                        */
469       /*****************************************************************/
470       n = (int)(Tpos - Memory);
471 
472 #if defined(_WIN32)
473       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
474 
475       if (drc == 0xFFFFFFFF) {
476         sprintf(g->Message, MSG(FUNCTION_ERROR),
477                             "SetFilePointer", GetLastError());
478         CloseHandle(fp->Handle);
479         return RC_FX;
480         } // endif
481 
482       if (trace(1))
483        htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
484 
485       if (!SetEndOfFile(fp->Handle)) {
486         sprintf(g->Message, MSG(FUNCTION_ERROR),
487                             "SetEndOfFile", GetLastError());
488         CloseHandle(fp->Handle);
489         return RC_FX;
490         } // endif
491 
492 #else    // UNIX
493       if (ftruncate(fp->Handle, (off_t)n)) {
494         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
495         close(fp->Handle);
496         return RC_FX;
497         } // endif
498 
499 #endif   // UNIX
500     } // endif Abort
501 
502 #if defined(_WIN32)
503   CloseHandle(fp->Handle);
504 #else    // UNIX
505   close(fp->Handle);
506 #endif   // UNIX
507   } // endif irc
508 
509   return RC_OK;                                      // All is correct
510   } // end of DeleteRecords
511 
512 /***********************************************************************/
513 /*  Table file close routine for MAP access method.                    */
514 /***********************************************************************/
515 void MAPFAM::CloseTableFile(PGLOBAL g, bool)
516   {
517   PlugCloseFile(g, To_Fb);
518 //To_Fb = NULL;              // To get correct file size in Cardinality
519 
520   if (trace(1))
521     htrc("MAP Close: closing %s count=%d\n",
522          To_File, (To_Fb) ? To_Fb->Count : 0);
523 
524   } // end of CloseTableFile
525 
526 /***********************************************************************/
527 /*  Rewind routine for MAP access method.                              */
528 /***********************************************************************/
529 void MAPFAM::Rewind(void)
530   {
531   Mempos = Memory;
532   } // end of Rewind
533 
534 /* --------------------------- Class MBKFAM -------------------------- */
535 
536 /***********************************************************************/
537 /*  Constructors.                                                      */
538 /***********************************************************************/
539 MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp)
540   {
541   Blocked = true;
542   Block = tdp->GetBlock();
543   Last = tdp->GetLast();
544   Nrec = tdp->GetElemt();
545   BlkPos = tdp->GetTo_Pos();
546   CurNum = Nrec;
547   } // end of MBKFAM standard constructor
548 
549 /***********************************************************************/
550 /*  Reset: reset position values at the beginning of file.             */
551 /***********************************************************************/
552 void MBKFAM::Reset(void)
553   {
554   MAPFAM::Reset();
555   CurNum = Nrec;              // To start by a new block
556   }  // end of Reset
557 
558 /***********************************************************************/
559 /*  Cardinality: returns table cardinality in number of rows.          */
560 /*  This function can be called with a null argument to test the       */
561 /*  availability of Cardinality implementation (1 yes, 0 no).          */
562 /***********************************************************************/
563 int MBKFAM::Cardinality(PGLOBAL g)
564   {
565   return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
566   } // end of Cardinality
567 
568 /***********************************************************************/
569 /*  Skip one record in file.                                           */
570 /***********************************************************************/
571 int MBKFAM::SkipRecord(PGLOBAL, bool)
572   {
573   return RC_OK;
574   } // end of SkipRecord
575 
576 /***********************************************************************/
577 /*  GetRowID: return the RowID of last read record.                    */
578 /***********************************************************************/
579 int MBKFAM::GetRowID(void)
580   {
581   return CurNum + Nrec * CurBlk + 1;
582   } // end of GetRowID
583 
584 /***********************************************************************/
585 /*  ReadBuffer: Read one line for a mapped Fix file.                   */
586 /***********************************************************************/
587 int MBKFAM::ReadBuffer(PGLOBAL g)
588   {
589   int rc, len;
590 
591   /*********************************************************************/
592   /*  Sequential block reading when Placed is not true.                */
593   /*********************************************************************/
594   if (Placed) {
595     Placed = false;
596   } else if (Mempos >= Top) {        // Are we at the end of the memory
597 		if ((rc = GetNext(g)) != RC_OK)
598 			return rc;
599 
600 	} else if (++CurNum < Nrec) {
601     Fpos = Mempos;
602   } else {
603     /*******************************************************************/
604     /*  New block.                                                     */
605     /*******************************************************************/
606     CurNum = 0;
607 
608    next:
609     if (++CurBlk >= Block)
610 			if ((rc = GetNext(g)) != RC_OK)
611 				return rc;
612 
613     /*******************************************************************/
614     /*  Before reading a new block, check whether block optimization   */
615     /*  can be done, as well as for join as for local filtering.       */
616     /*******************************************************************/
617     switch (Tdbp->TestBlock(g)) {
618       case RC_EF:
619 				if ((rc = GetNext(g)) != RC_OK)
620 					return rc;
621 
622 				break;
623 			case RC_NF:
624         goto next;
625       } // endswitch rc
626 
627     Fpos = Mempos = Memory + BlkPos[CurBlk];
628   } // endif's
629 
630   // Immediately calculate next position (Used by DeleteDB)
631 	while (*Mempos++ != '\n')          // What about Unix ???
632 		if (Mempos == Top)
633 			break;
634 
635   // Set caller line buffer
636   len = (int)(Mempos - Fpos) - Ending;
637   memcpy(Tdbp->GetLine(), Fpos, len);
638   Tdbp->GetLine()[len] = '\0';
639   return RC_OK;
640   } // end of ReadBuffer
641 
642 /***********************************************************************/
643 /*  Rewind routine for FIX MAP access method.                          */
644 /***********************************************************************/
645 void MBKFAM::Rewind(void)
646   {
647   Mempos = Memory + Headlen;
648   CurBlk = -1;
649   CurNum = Nrec;
650   } // end of Rewind
651 
652 /* --------------------------- Class MPXFAM -------------------------- */
653 
654 /***********************************************************************/
655 /*  Constructors.                                                      */
656 /***********************************************************************/
657 MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp)
658   {
659   Blksize = tdp->GetBlksize();
660   Padded = tdp->GetPadded();
661 
662   if (Padded && Blksize)
663     Nrec = Blksize / Lrecl;
664   else {
665     Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
666     Blksize = Nrec * Lrecl;
667     Padded = false;
668     } // endelse
669 
670   CurNum = Nrec;
671   } // end of MPXFAM standard constructor
672 
673 #if 0                 // MBKFAM routine is correct
674 /***********************************************************************/
675 /*  GetRowID: return the RowID of last read record.                    */
676 /***********************************************************************/
677 int MPXFAM::GetRowID(void)
678   {
679   return (Mempos - Memory - Headlen) / Lrecl;
680   } // end of GetRowID
681 #endif
682 
683 /***********************************************************************/
684 /*  GetPos: return the position of last read record.                   */
685 /***********************************************************************/
686 int MPXFAM::GetPos(void)
687   {
688   return (CurNum + Nrec * CurBlk);          // Computed file index
689   } // end of GetPos
690 
691 /***********************************************************************/
692 /*  SetPos: Replace the table at the specified position.               */
693 /***********************************************************************/
694 bool MPXFAM::SetPos(PGLOBAL g, int pos)
695   {
696   if (pos < 0) {
697     strcpy(g->Message, MSG(INV_REC_POS));
698     return true;
699     } // endif recpos
700 
701   CurBlk = pos / Nrec;
702   CurNum = pos % Nrec;
703   Fpos = Mempos = Memory + Headlen + pos * Lrecl;
704 
705   // Indicate the table position was externally set
706   Placed = true;
707   return false;
708   } // end of SetPos
709 
710 /***********************************************************************/
711 /*  Initialize CurBlk, CurNum, Mempos and Fpos for indexed DELETE.     */
712 /***********************************************************************/
713 int MPXFAM::InitDelete(PGLOBAL, int fpos, int)
714   {
715   Fpos = Memory + Headlen + (ptrdiff_t)fpos * Lrecl;
716   Mempos = Fpos + Lrecl;
717   return RC_OK;
718   } // end of InitDelete
719 
720 /***********************************************************************/
721 /*  ReadBuffer: Read one line for a mapped Fix file.                   */
722 /***********************************************************************/
723 int MPXFAM::ReadBuffer(PGLOBAL g)
724   {
725 	int rc;
726 
727   /*********************************************************************/
728   /*  Sequential block reading when Placed is not true.                */
729   /*********************************************************************/
730   if (Placed) {
731     Placed = false;
732   } else if (Mempos >= Top) {        // Are we at the end of the memory
733 		if ((rc = GetNext(g)) != RC_OK)
734 			return rc;
735 
736 	} else if (++CurNum < Nrec) {
737     Fpos = Mempos;
738   } else {
739     /*******************************************************************/
740     /*  New block.                                                     */
741     /*******************************************************************/
742     CurNum = 0;
743 
744    next:
745     if (++CurBlk >= Block)
746 			return GetNext(g);
747 
748     /*******************************************************************/
749     /*  Before reading a new block, check whether block optimization   */
750     /*  can be done, as well as for join as for local filtering.       */
751     /*******************************************************************/
752     switch (Tdbp->TestBlock(g)) {
753       case RC_EF:
754 				if ((rc = GetNext(g)) != RC_OK)
755 					return rc;
756 
757 				break;
758 			case RC_NF:
759         goto next;
760       } // endswitch rc
761 
762     Fpos = Mempos = Headlen + Memory + CurBlk * Blksize;
763   } // endif's
764 
765   Tdbp->SetLine(Mempos);
766 
767   // Immediately calculate next position (Used by DeleteDB)
768   Mempos += Lrecl;
769   return RC_OK;
770   } // end of ReadBuffer
771 
772 /***********************************************************************/
773 /*  WriteBuffer: File write routine for MAP access method.             */
774 /***********************************************************************/
775 int MPXFAM::WriteBuffer(PGLOBAL g __attribute__((unused)))
776   {
777 #if defined(_DEBUG)
778   // Insert mode is no more handled using file mapping
779   if (Tdbp->GetMode() == MODE_INSERT) {
780     strcpy(g->Message, MSG(NO_MAP_INSERT));
781     return RC_FX;
782     } // endif
783 #endif   // _DEBUG
784 
785   // In Update mode, file was modified in memory
786   return RC_OK;
787   } // end of WriteBuffer
788 
789