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/***********************************************************************/
55MAPFAM::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
65MAPFAM::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/***********************************************************************/
78void 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/***********************************************************************/
87int 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/***********************************************************************/
102bool 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/***********************************************************************/
240int MAPFAM::GetRowID(void)
241 {
242 return Rows;
243 } // end of GetRowID
244
245/***********************************************************************/
246/* GetPos: return the position of last read record. */
247/***********************************************************************/
248int MAPFAM::GetPos(void)
249 {
250 return (int)(Fpos - Memory);
251 } // end of GetPos
252
253/***********************************************************************/
254/* GetNextPos: return the position of next record. */
255/***********************************************************************/
256int 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/***********************************************************************/
264bool 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/***********************************************************************/
280bool 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/***********************************************************************/
289int 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/***********************************************************************/
299int MAPFAM::SkipRecord(PGLOBAL g, bool header)
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/***********************************************************************/
320int 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/***********************************************************************/
385int 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/***********************************************************************/
406int 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/***********************************************************************/
509void 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/***********************************************************************/
523void MAPFAM::Rewind(void)
524 {
525 Mempos = Memory;
526 } // end of Rewind
527
528/* --------------------------- Class MBKFAM -------------------------- */
529
530/***********************************************************************/
531/* Constructors. */
532/***********************************************************************/
533MBKFAM::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/***********************************************************************/
546void 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/***********************************************************************/
557int 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/***********************************************************************/
565int 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/***********************************************************************/
573int 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/***********************************************************************/
581int 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/***********************************************************************/
639void 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/***********************************************************************/
651MPXFAM::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/***********************************************************************/
671int 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/***********************************************************************/
680int 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/***********************************************************************/
688bool 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/***********************************************************************/
707int 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/***********************************************************************/
717int 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/***********************************************************************/
769int 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