1 /*********** File AM Fix C++ Program Source Code File (.CPP) ***********/
2 /* PROGRAM NAME: FILAMFIX                                              */
3 /* -------------                                                       */
4 /*  Version 1.6                                                        */
5 /*                                                                     */
6 /* COPYRIGHT:                                                          */
7 /* ----------                                                          */
8 /*  (C) Copyright to the author Olivier BERTRAND          2005-2015    */
9 /*                                                                     */
10 /* WHAT THIS PROGRAM DOES:                                             */
11 /* -----------------------                                             */
12 /*  This program are the FIX/BIN 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 #include <io.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #if defined(__BORLANDC__)
25 #define __MFC_COMPAT__                   // To define min/max as macro
26 #endif   // __BORLANDC__
27 //#include <windows.h>
28 #else   // !_WIN32
29 #if defined(UNIX)
30 #include <errno.h>
31 #include <unistd.h>
32 #else   // !UNIX
33 #include <io.h>
34 #endif  // !UNIX
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #endif  // !_WIN32
38 
39 /***********************************************************************/
40 /*  Include application header files:                                  */
41 /*  global.h    is header containing all global declarations.          */
42 /*  plgdbsem.h  is header containing the DB application declarations.  */
43 /*  filamfix.h  is header containing the file AM classes declarations. */
44 /***********************************************************************/
45 #include "global.h"
46 #include "plgdbsem.h"
47 #include "filamfix.h"
48 #include "tabdos.h"
49 #include "tabfix.h"
50 #include "osutil.h"
51 
52 #ifndef INVALID_SET_FILE_POINTER
53 #define INVALID_SET_FILE_POINTER  0xFFFFFFFF
54 #endif
55 
56 extern int num_read, num_there, num_eq[2];               // Statistics
57 
58 /* --------------------------- Class FIXFAM -------------------------- */
59 
60 /***********************************************************************/
61 /*  Constructors.                                                      */
62 /***********************************************************************/
FIXFAM(PDOSDEF tdp)63 FIXFAM::FIXFAM(PDOSDEF tdp) : BLKFAM(tdp)
64   {
65   Blksize = tdp->GetBlksize();
66   Padded = tdp->GetPadded();
67 
68   if (Padded && Blksize)
69     Nrec = Blksize / Lrecl;
70   else {
71     Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
72     Blksize = Nrec * Lrecl;
73     Padded = false;
74     } // endelse
75 
76   } // end of FIXFAM standard constructor
77 
FIXFAM(PFIXFAM txfp)78 FIXFAM::FIXFAM(PFIXFAM txfp) : BLKFAM(txfp)
79   {
80   } // end of FIXFAM copy constructor
81 
82 /***********************************************************************/
83 /*  SetPos: Replace the table at the specified position.               */
84 /***********************************************************************/
SetPos(PGLOBAL g,int pos)85 bool FIXFAM::SetPos(PGLOBAL g, int pos)
86   {
87   if (pos < 0) {
88     strcpy(g->Message, MSG(INV_REC_POS));
89     return true;
90     } // endif recpos
91 
92   CurBlk = pos / Nrec;
93   CurNum = pos % Nrec;
94 #if defined(_DEBUG)
95   num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
96 #endif
97 
98   // Indicate the table position was externally set
99   Placed = true;
100   return false;
101   } // end of SetPos
102 
103 /***********************************************************************/
104 /*  Initialize CurBlk and CurNum for indexed DELETE.                   */
105 /***********************************************************************/
InitDelete(PGLOBAL,int fpos,int)106 int FIXFAM::InitDelete(PGLOBAL, int fpos, int)
107   {
108   CurBlk = fpos / Nrec;
109   CurNum = fpos % Nrec;
110   return RC_OK;
111   } // end of InitDelete
112 
113 /***********************************************************************/
114 /*  Allocate the block buffer for the table.                           */
115 /***********************************************************************/
AllocateBuffer(PGLOBAL g)116 bool FIXFAM::AllocateBuffer(PGLOBAL g)
117   {
118   Buflen = Blksize;
119   To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
120 
121   if (UseTemp || Tdbp->GetMode() == MODE_DELETE) {
122     if (Padded) {
123       strcpy(g->Message, MSG(NO_MODE_PADDED));
124       return true;
125       } // endif Padded
126 
127     // Allocate a separate buffer so block reading can be kept
128     Dbflen = Nrec;
129     DelBuf = PlugSubAlloc(g, NULL, Blksize);
130   } else if (Tdbp->GetMode() == MODE_INSERT) {
131     /*******************************************************************/
132     /*  For Insert the buffer must be prepared.                        */
133     /*******************************************************************/
134     if (Tdbp->GetFtype() == RECFM_BIN) {
135       // The buffer must be prepared depending on column types
136       int     n = 0;
137       bool    b = false;
138       PBINCOL colp;
139 
140       // Prepare the first line of the buffer
141       memset(To_Buf, 0, Buflen);
142 
143 #if 0
144       for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) {
145         if (!IsTypeNum(cdp->GetType())) {
146           memset(To_Buf + cdp->GetOffset(), ' ', cdp->GetClen());
147           b = true;
148           } // endif not num
149 
150         n = MY_MAX(n, cdp->GetOffset() + cdp->GetClen());
151         } // endfor cdp
152 #endif // 0
153 
154       for (colp = (PBINCOL)Tdbp->GetColumns(); colp;
155            colp = (PBINCOL)colp->GetNext())
156         if (!colp->IsSpecial()) {
157           if (!IsTypeNum(colp->GetResultType())) {
158             memset(To_Buf + colp->GetDeplac(), ' ', colp->GetLength());
159             b = true;
160             } // endif not num
161 
162           n = MY_MAX(n, colp->GetDeplac() + colp->GetFileSize());
163           } // endif !special
164 
165       // We do this for binary table because the lrecl can have been
166       // specified with additional space to include line ending.
167       if (n < Lrecl && Ending) {
168         To_Buf[Lrecl - 1] = '\n';
169 
170         if (n < Lrecl - 1 && Ending == 2)
171           To_Buf[Lrecl - 2] = '\r';
172 
173         } // endif n
174 
175       if (b)
176         // Now repeat this for the whole buffer
177         for (int len = Lrecl; len <= Buflen - Lrecl; len += Lrecl)
178           memcpy(To_Buf + len, To_Buf, Lrecl);
179 
180     } else {
181       memset(To_Buf, ' ', Buflen);
182 
183       if (!Padded)
184         // The file is physically a text file.
185         for (int len = Lrecl; len <= Buflen; len += Lrecl) {
186           if (Ending == 2)
187             To_Buf[len - 2] = '\r';
188 
189           To_Buf[len - 1] = '\n';
190           } // endfor len
191 
192     } // endif Ftype
193 
194     Rbuf = Nrec;                     // To be used by WriteDB
195     } // endif Insert
196 
197   return false;
198   } // end of AllocateBuffer
199 
200 /***********************************************************************/
201 /*  Reset buffer access according to indexing and to mode.             */
202 /*  >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
203 /***********************************************************************/
ResetBuffer(PGLOBAL g)204 void FIXFAM::ResetBuffer(PGLOBAL g)
205   {
206   /*********************************************************************/
207   /*  If access is random, performances can be much better when the    */
208   /*  reads are done on only one row, except for small tables that can */
209   /*  be entirely read in one block.                                   */
210   /*********************************************************************/
211   if (Tdbp->GetKindex() && ReadBlks != 1 && !Padded) {
212     Nrec = 1;                       // Better for random access
213     Rbuf = 0;
214     Blksize = Lrecl;
215     OldBlk = -2;                    // Has no meaning anymore
216     Block = Tdbp->Cardinality(g);   // Blocks are one line now
217     } // endif Mode
218 
219   } // end of ResetBuffer
220 
221 /***********************************************************************/
222 /*  WriteModifiedBlock: Used when updating.                            */
223 /***********************************************************************/
WriteModifiedBlock(PGLOBAL g)224 int FIXFAM::WriteModifiedBlock(PGLOBAL g)
225   {
226   /*********************************************************************/
227   /*  The old block was modified in Update mode.                       */
228   /*  In Update mode we simply rewrite the old block on itself.        */
229   /*********************************************************************/
230   int  rc = RC_OK;
231   bool moved = false;
232 
233   // Using temp copy any intermediate lines.
234   if (UseTemp && MoveIntermediateLines(g, &moved))
235     rc = RC_FX;
236 
237   // Fpos is last position, Headlen is DBF file header length
238   else if (!moved && fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
239     sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
240     rc = RC_FX;
241   } else if (fwrite(To_Buf, Lrecl, Rbuf, T_Stream) != (size_t)Rbuf) {
242     sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
243     rc = RC_FX;
244   } else
245     Spos = Fpos + Nrec;           // + Rbuf ???
246 
247   if (Closing || rc != RC_OK) {   // Error or called from CloseDB
248     Closing = true;               // To tell CloseDB about error
249     return rc;
250     } // endif Closing
251 
252   // NOTE: Next line was added to avoid a very strange fread bug.
253   // When the fseek is not executed (even the file has the good
254   // pointer position) the next read can happen anywhere in the file.
255   OldBlk = -2;            // This will force fseek to be executed
256   Modif = 0;
257   return rc;
258   } // end of WriteModifiedBlock
259 
260 /***********************************************************************/
261 /*  ReadBuffer: Read one line for a FIX file.                          */
262 /***********************************************************************/
ReadBuffer(PGLOBAL g)263 int FIXFAM::ReadBuffer(PGLOBAL g)
264   {
265   int n, rc = RC_OK;
266 
267   /*********************************************************************/
268   /*  Sequential reading when Placed is not true.                      */
269   /*********************************************************************/
270   if (Placed) {
271     Tdbp->SetLine(To_Buf + CurNum * Lrecl);
272     Placed = false;
273   } else if (++CurNum < Rbuf) {
274     Tdbp->IncLine(Lrecl);                // Used by DOSCOL functions
275     return RC_OK;
276   } else if (Rbuf < Nrec && CurBlk != -1) {
277     return RC_EF;
278   } else {
279     /*******************************************************************/
280     /*  New block.                                                     */
281     /*******************************************************************/
282     CurNum = 0;
283     Tdbp->SetLine(To_Buf);
284 
285  next:
286     if (++CurBlk >= Block)
287       return RC_EF;
288 
289     /*******************************************************************/
290     /*  Before reading a new block, check whether block indexing       */
291     /*  can be done, as well as for join as for local filtering.       */
292     /*******************************************************************/
293     switch (Tdbp->TestBlock(g)) {
294       case RC_EF:
295         return RC_EF;
296       case RC_NF:
297         goto next;
298       } // endswitch rc
299    } // endif's
300 
301   if (OldBlk == CurBlk) {
302     IsRead = true;            // Was read indeed
303     return RC_OK;             // Block is already there
304     } // endif OldBlk
305 
306   // Write modified block in mode UPDATE
307   if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
308     return rc;
309 
310   // This could be done only for new block. However note that FPOS
311   // is used as block position when updating and as line position
312   // when deleting so this has to be carefully checked.
313   Fpos = CurBlk * Nrec;         // Fpos is new line position
314 
315   // fseek is required only in non sequential reading
316   if (CurBlk != OldBlk + 1)
317     // Note: Headlen is for DBF tables
318     if (fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
319       sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
320       return RC_FX;
321       } // endif fseek
322 
323   if (trace(2))
324     htrc("File position is now %d\n", ftell(Stream));
325 
326   if (Padded)
327     n = fread(To_Buf, (size_t)Blksize, 1, Stream);
328   else
329     n = fread(To_Buf, (size_t)Lrecl, (size_t)Nrec, Stream);
330 
331   if (n) {
332     rc = RC_OK;
333     Rbuf = (Padded) ? n * Nrec : n;
334     ReadBlks++;
335     num_read++;
336   } else if (feof(Stream)) {
337     rc = RC_EF;
338   } else {
339 #if defined(_WIN32)
340     sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
341 #else
342     sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
343 #endif
344 
345     if (trace(1))
346       htrc("%s\n", g->Message);
347 
348     return RC_FX;
349   } // endelse
350 
351   OldBlk = CurBlk;              // Last block actually read
352   IsRead = true;                // Is read indeed
353   return rc;
354   } // end of ReadBuffer
355 
356 /***********************************************************************/
357 /*  WriteBuffer: File write routine for FIX access method.             */
358 /*  Updates are written into the (Temp) file in ReadBuffer.            */
359 /***********************************************************************/
WriteBuffer(PGLOBAL g)360 int FIXFAM::WriteBuffer(PGLOBAL g)
361   {
362   if (trace(2))
363     htrc("FIX WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n",
364          Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum);
365 
366   if (Tdbp->GetMode() == MODE_INSERT) {
367     /*******************************************************************/
368     /*  In Insert mode, blocs are added sequentialy to the file end.   */
369     /*******************************************************************/
370     if (++CurNum != Rbuf) {
371       Tdbp->IncLine(Lrecl);            // Used by DOSCOL functions
372       return RC_OK;                    // We write only full blocks
373       } // endif CurNum
374 
375     if (trace(2))
376       htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf);
377 
378     //  Now start the writing process.
379     if (fwrite(To_Buf, Lrecl, Rbuf, Stream) != (size_t)Rbuf) {
380       sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
381       Closing = true;      // To tell CloseDB about a Write error
382       return RC_FX;
383       } // endif size
384 
385     CurBlk++;
386     CurNum = 0;
387     Tdbp->SetLine(To_Buf);
388 
389     if (trace(2))
390       htrc("write done\n");
391 
392   } else {                           // Mode == MODE_UPDATE
393     // T_Stream is the temporary stream or the table file stream itself
394     if (!T_Stream) {
395       if (UseTemp) {
396         if (OpenTempFile(g))
397           return RC_FX;
398         else if (CopyHeader(g))           // For DBF tables
399           return RC_FX;
400 
401       } else
402         T_Stream = Stream;
403 
404       } // endif T_Stream
405 
406     if (Nrec > 1)
407       Modif++;                         // Modified line in blocked mode
408     else if (WriteModifiedBlock(g))    // Indexed update
409       return RC_FX;
410 
411   } // endif Mode
412 
413   return RC_OK;
414   } // end of WriteBuffer
415 
416 /***********************************************************************/
417 /*  Data Base delete line routine for FIXFAM access method.            */
418 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)419 int FIXFAM::DeleteRecords(PGLOBAL g, int irc)
420   {
421   bool moved;
422 
423   /*********************************************************************/
424   /*  There is an alternative here:                                    */
425   /*  1 - use a temporary file in which are copied all not deleted     */
426   /*      lines, at the end the original file will be deleted and      */
427   /*      the temporary file renamed to the original file name.        */
428   /*  2 - directly move the not deleted lines inside the original      */
429   /*      file, and at the end erase all trailing records.             */
430   /*  This will be experimented.                                       */
431   /*********************************************************************/
432   if (trace(2))
433     htrc("DOS DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
434          irc, UseTemp, Fpos, Tpos, Spos);
435 
436   if (irc != RC_OK) {
437     /*******************************************************************/
438     /*  EOF: position Fpos at the end-of-file position.                */
439     /*******************************************************************/
440     Fpos = Tdbp->Cardinality(g);
441 
442     if (trace(2))
443       htrc("Fpos placed at file end=%d\n", Fpos);
444 
445   } else    // Fpos is the deleted line position
446     Fpos = CurBlk * Nrec + CurNum;
447 
448   if (Tpos == Spos) {
449     /*******************************************************************/
450     /*  First line to delete.                                          */
451     /*******************************************************************/
452     if (UseTemp) {
453       /*****************************************************************/
454       /*  Open temporary file, lines before this will be moved.        */
455       /*****************************************************************/
456       if (OpenTempFile(g))
457         return RC_FX;
458 
459     } else {
460       /*****************************************************************/
461       /*  Move of eventual preceding lines is not required here.      */
462       /*  Set the target file as being the source file itself.         */
463       /*  Set the future Tpos, and give Spos a value to block moving.  */
464       /*****************************************************************/
465       T_Stream = Stream;
466       Spos = Tpos = Fpos;
467     } // endif UseTemp
468 
469     } // endif Tpos == Spos
470 
471   /*********************************************************************/
472   /*  Move any intermediate lines.                                     */
473   /*********************************************************************/
474   if (MoveIntermediateLines(g, &moved))
475     return RC_FX;
476 
477   if (irc == RC_OK) {
478     /*******************************************************************/
479     /*  Reposition the file pointer and set Spos.                      */
480     /*******************************************************************/
481     Spos = Fpos + 1;          // New start position is on next line
482 
483     if (moved) {
484       if (fseek(Stream, Spos * Lrecl, SEEK_SET)) {
485         sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
486         return RC_FX;
487         } // endif fseek
488 
489       OldBlk = -2;  // To force fseek to be executed on next block
490       } // endif moved
491 
492     if (trace(2))
493       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
494 
495   } else {
496     /*******************************************************************/
497     /*  Last call after EOF has been reached.                          */
498     /*******************************************************************/
499     if (UseTemp) {
500       /*****************************************************************/
501       /*  Ok, now delete old file and rename new temp file.            */
502       /*****************************************************************/
503       if (RenameTempFile(g))
504         return RC_FX;
505 
506     } else {
507       /*****************************************************************/
508       /*  Because the chsize functionality is only accessible with a   */
509       /*  system call we must close the file and reopen it with the    */
510       /*  open function (_fopen for MS ??) this is still to be checked */
511       /*  for compatibility with Text files and other OS's.            */
512       /*****************************************************************/
513       char filename[_MAX_PATH];
514       int  h;
515 
516       /*rc= */PlugCloseFile(g, To_Fb);
517       PlugSetPath(filename, To_File, Tdbp->GetPath());
518 
519       if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
520         return RC_FX;
521 
522       /*****************************************************************/
523       /*  Remove extra records.                                        */
524       /*****************************************************************/
525 #if defined(UNIX)
526       if (ftruncate(h, (off_t)(Tpos * Lrecl))) {
527         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
528         close(h);
529         return RC_FX;
530         } // endif
531 #else
532       if (chsize(h, Tpos * Lrecl)) {
533         sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
534         close(h);
535         return RC_FX;
536         } // endif
537 #endif
538 
539       close(h);
540 
541     if (trace(2))
542       htrc("done, h=%d irc=%d\n", h, irc);
543 
544     } // endif UseTemp
545 
546   } // endif irc
547 
548   return RC_OK;                                      // All is correct
549   } // end of DeleteRecords
550 
551 /***********************************************************************/
552 /*  Move intermediate deleted or updated lines.                        */
553 /*  This works only for file open in binary mode.                      */
554 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool * b)555 bool FIXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
556   {
557   int   n;
558   size_t req, len;
559 
560   for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
561     /*******************************************************************/
562     /*  Non consecutive line to delete. Move intermediate lines.       */
563     /*******************************************************************/
564     if (!UseTemp || !*b)
565       if (fseek(Stream, Headlen + Spos * Lrecl, SEEK_SET)) {
566         sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
567         return true;
568         } // endif
569 
570     req = (size_t)MY_MIN(n, Dbflen);
571     len = fread(DelBuf, Lrecl, req, Stream);
572 
573     if (trace(2))
574       htrc("after read req=%d len=%d\n", req, len);
575 
576     if (len != req) {
577       sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
578       return true;
579       } // endif len
580 
581     if (!UseTemp)         // Delete mode, cannot be a DBF file
582       if (fseek(T_Stream, Tpos * Lrecl, SEEK_SET)) {
583         sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
584         return true;
585         } // endif
586 
587     if ((len = fwrite(DelBuf, Lrecl, req, T_Stream)) != req) {
588       sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
589       return true;
590       } // endif
591 
592     if (trace(2))
593       htrc("after write pos=%d\n", ftell(Stream));
594 
595     Tpos += (int)req;
596     Spos += (int)req;
597 
598     if (trace(2))
599       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
600 
601     *b = true;
602     } // endfor n
603 
604   return false;
605   } // end of MoveIntermediate Lines
606 
607 /***********************************************************************/
608 /*  Table file close routine for FIX access method.                    */
609 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)610 void FIXFAM::CloseTableFile(PGLOBAL g, bool abort)
611   {
612   int rc = RC_OK, wrc = RC_OK;
613   MODE mode = Tdbp->GetMode();
614 
615   Abort = abort;
616 
617   // Closing is True if last Write was in error
618   if (mode == MODE_INSERT && CurNum && !Closing) {
619     // Some more inserted lines remain to be written
620     Rbuf = CurNum--;
621     wrc = WriteBuffer(g);
622   } else if (mode == MODE_UPDATE) {
623     if (Modif && !Closing) {
624       // Last updated block remains to be written
625       Closing = true;               // ???
626       wrc = WriteModifiedBlock(g);
627       } // endif Modif
628 
629     if (UseTemp && T_Stream && wrc == RC_OK) {
630       if (!Abort) {
631         // Copy any remaining lines
632         bool b;
633 
634         Fpos = Tdbp->Cardinality(g);
635         Abort = MoveIntermediateLines(g, &b) != RC_OK;
636         } // endif Abort
637 
638       // Delete the old file and rename the new temp file.
639       RenameTempFile(g);
640       goto fin;
641       } // endif UseTemp
642 
643   } // endif's mode
644 
645   // Finally close the file
646   rc = PlugCloseFile(g, To_Fb);
647 
648  fin:
649   if (trace(1))
650     htrc("FIX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
651          To_File, mode, wrc, rc);
652 
653   Stream = NULL;           // So we can know whether table is open
654   } // end of CloseTableFile
655 
656 /* ------------------------- Class BGXFAM ---------------------------- */
657 
658 /***********************************************************************/
659 /*  Implementation of the BGXFAM class.                                */
660 /*  This is the FAM class for FIX tables of more than 2 gigabytes.     */
661 /***********************************************************************/
BGXFAM(PDOSDEF tdp)662 BGXFAM::BGXFAM(PDOSDEF tdp) : FIXFAM(tdp)
663   {
664   Hfile = INVALID_HANDLE_VALUE;
665   Tfile = INVALID_HANDLE_VALUE;
666   } // end of BGXFAM constructor
667 
BGXFAM(PBGXFAM txfp)668 BGXFAM::BGXFAM(PBGXFAM txfp) : FIXFAM(txfp)
669   {
670   Hfile = txfp->Hfile;
671   Tfile = txfp->Tfile;
672   } // end of BGXFAM copy constructor
673 
674 /***********************************************************************/
675 /*  Set current position in a big file.                                */
676 /***********************************************************************/
BigSeek(PGLOBAL g,HANDLE h,BIGINT pos,int org)677 bool BGXFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, int org)
678   {
679 #if defined(_WIN32)
680   char          buf[256];
681   DWORD         drc;
682   LARGE_INTEGER of;
683 
684   of.QuadPart = pos;
685   of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, org);
686 
687   if (of.LowPart == INVALID_SET_FILE_POINTER &&
688            (drc = GetLastError()) != NO_ERROR) {
689     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
690                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
691                   (LPTSTR)buf, sizeof(buf), NULL);
692     sprintf(g->Message, MSG(SFP_ERROR), buf);
693     return true;
694     } // endif
695 #else   // !_WIN32
696   if (lseek64(h, pos, org) < 0) {
697 //  sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
698     sprintf(g->Message, "lseek64: %s", strerror(errno));
699     printf("%s\n", g->Message);
700     return true;
701     } // endif
702 #endif  // !_WIN32
703 
704   return false;
705   } // end of BigSeek
706 
707 /***********************************************************************/
708 /*  Read from a big file.                                              */
709 /***********************************************************************/
BigRead(PGLOBAL g,HANDLE h,void * inbuf,int req)710 int BGXFAM::BigRead(PGLOBAL g __attribute__((unused)),
711                     HANDLE h, void *inbuf, int req)
712   {
713   int rc;
714 
715 #if defined(_WIN32)
716   DWORD nbr, drc, len = (DWORD)req;
717   bool  brc = ReadFile(h, inbuf, len, &nbr, NULL);
718 
719   if (trace(2))
720     htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
721 
722   if (!brc) {
723     char buf[256];  // , *fn = (h == Hfile) ? To_File : "Tempfile";
724 
725     drc = GetLastError();
726     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
727                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
728                   (LPTSTR)buf, sizeof(buf), NULL);
729     sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
730 
731     if (trace(2))
732       htrc("BIGREAD: %s\n", g->Message);
733 
734     rc = -1;
735   } else
736     rc = (int)nbr;
737 #else   // !_WIN32
738   size_t  len = (size_t)req;
739   ssize_t nbr = read(h, inbuf, len);
740 
741   rc = (int)nbr;
742 #endif  // !_WIN32
743 
744   return rc;
745   } // end of BigRead
746 
747 /***********************************************************************/
748 /*  Write into a big file.                                             */
749 /***********************************************************************/
BigWrite(PGLOBAL g,HANDLE h,void * inbuf,int req)750 bool BGXFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
751   {
752   bool rc = false;
753 
754 #if defined(_WIN32)
755   DWORD nbw, drc, len = (DWORD)req;
756   bool  brc = WriteFile(h, inbuf, len, &nbw, NULL);
757 
758   if (trace(2))
759     htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
760 
761   if (!brc || nbw != len) {
762 		char buf[256];
763 		PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
764 
765     if (brc)
766       strcpy(buf, MSG(BAD_BYTE_NUM));
767     else {
768       drc = GetLastError();
769       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
770                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
771                     (LPTSTR)buf, sizeof(buf), NULL);
772       } // endelse brc
773 
774     sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
775 
776     if (trace(2))
777       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
778            nbw, len, drc, g->Message);
779 
780     rc = true;
781     } // endif brc || nbw
782 #else   // !_WIN32
783   size_t  len = (size_t)req;
784   ssize_t nbw = write(h, inbuf, len);
785 
786   if (nbw != (ssize_t)len) {
787     const char *fn = (h == Hfile) ? To_File : "Tempfile";
788 
789     sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
790 
791     if (trace(2))
792       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
793            nbw, len, errno, g->Message);
794 
795     rc = true;
796     } // endif nbr
797 #endif  // !_WIN32
798 
799   return rc;
800   } // end of BigWrite
801 
802 #if 0
803 /***********************************************************************/
804 /*  Reset: reset position values at the beginning of file.             */
805 /***********************************************************************/
806 void BGXFAM::Reset(void)
807   {
808   FIXFAM::Reset();
809   Xpos = 0;
810   } // end of Reset
811 #endif // 0
812 
813 /***********************************************************************/
814 /*  OpenTableFile: opens a huge file using Windows/Unix API's.         */
815 /***********************************************************************/
OpenTableFile(PGLOBAL g)816 bool BGXFAM::OpenTableFile(PGLOBAL g)
817   {
818   char    filename[_MAX_PATH];
819   MODE    mode = Tdbp->GetMode();
820   PDBUSER dbuserp = PlgGetUser(g);
821 
822   if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
823     sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
824     return true;
825     } // endif
826 
827   PlugSetPath(filename, To_File, Tdbp->GetPath());
828 
829   if (trace(1))
830     htrc("OpenTableFile: filename=%s mode=%d\n", filename, mode);
831 
832 #if defined(_WIN32)
833   DWORD rc, access, creation, share = 0;
834 
835   /*********************************************************************/
836   /*  Create the file object according to access mode                  */
837   /*********************************************************************/
838   switch (mode) {
839     case MODE_READ:
840       access = GENERIC_READ;
841       share = FILE_SHARE_READ;
842       creation = OPEN_EXISTING;
843       break;
844     case MODE_DELETE:
845       if (!Tdbp->GetNext()) {
846         // Store the number of deleted rows
847         DelRows = Cardinality(g);
848 
849         // This will delete the whole file and provoque ReadDB to
850         // return immediately.
851         access = GENERIC_READ | GENERIC_WRITE;
852         creation = TRUNCATE_EXISTING;
853         Tdbp->ResetSize();
854         Headlen = 0;
855         break;
856         } // endif
857 
858       // Selective delete, pass thru
859     case MODE_UPDATE:
860       if ((UseTemp = Tdbp->IsUsingTemp(g)))
861         access = GENERIC_READ;
862       else
863         access = GENERIC_READ | GENERIC_WRITE;
864 
865       creation = OPEN_EXISTING;
866       break;
867     case MODE_INSERT:
868       access = GENERIC_WRITE;
869       creation = OPEN_ALWAYS;
870       break;
871     default:
872       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
873       return true;
874     } // endswitch
875 
876   Hfile = CreateFile(filename, access, share, NULL, creation,
877                                FILE_ATTRIBUTE_NORMAL, NULL);
878 
879   if (Hfile == INVALID_HANDLE_VALUE) {
880     rc = GetLastError();
881     sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
882     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
883                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
884                   (LPTSTR)filename, sizeof(filename), NULL);
885     strcat(g->Message, filename);
886   } else
887     rc = 0;
888 
889   if (trace(2))
890     htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
891          rc, access, share, creation, Hfile, filename);
892 
893   if (mode == MODE_INSERT)
894     /*******************************************************************/
895     /* In Insert mode we must position the cursor at end of file.      */
896     /*******************************************************************/
897     if (BigSeek(g, Hfile, (BIGINT)0, FILE_END))
898       return true;
899 
900 #else   // UNIX
901   int    rc = 0;
902   int    oflag = O_LARGEFILE;         // Enable file size > 2G
903   mode_t tmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
904 
905   /*********************************************************************/
906   /*  Create the file object according to access mode                  */
907   /*********************************************************************/
908   switch (mode) {
909     case MODE_READ:
910       oflag |= O_RDONLY;
911       break;
912     case MODE_DELETE:
913       if (!Tdbp->GetNext()) {
914         // This will delete the whole file and provoque ReadDB to
915         // return immediately.
916         oflag |= (O_RDWR | O_TRUNC);
917         Tdbp->ResetSize();
918         break;
919         } // endif
920 
921       // Selective delete
922       /* fall through */
923     case MODE_UPDATE:
924       UseTemp = Tdbp->IsUsingTemp(g);
925       oflag |= (UseTemp) ? O_RDONLY : O_RDWR;
926       break;
927     case MODE_INSERT:
928       oflag |= (O_WRONLY | O_CREAT | O_APPEND);
929  //   tmode = S_IREAD | S_IWRITE;
930       break;
931     default:
932       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
933       return true;
934     } // endswitch
935 
936   Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, tmode);
937 
938   if (Hfile == INVALID_HANDLE_VALUE) {
939     rc = errno;
940   } else
941     rc = 0;
942 
943   if (trace(2))
944     htrc(" rc=%d oflag=%p tmode=%p handle=%p fn=%s\n",
945          rc, oflag, tmode, Hfile, filename);
946 
947 #endif  // UNIX
948 
949   if (!rc) {
950     if (!To_Fb) {
951       To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
952       To_Fb->Fname = To_File;
953       To_Fb->Type = TYPE_FB_HANDLE;
954       To_Fb->Memory = NULL;
955       To_Fb->Length = 0;
956       To_Fb->Mode = mode;
957       To_Fb->File = NULL;
958       To_Fb->Next = dbuserp->Openlist;
959       dbuserp->Openlist = To_Fb;
960       } // endif To_Fb
961 
962     To_Fb->Count = 1;
963     To_Fb->Mode = mode;
964     To_Fb->Handle = Hfile;
965 
966     /*******************************************************************/
967     /*  Allocate the block buffer.                                     */
968     /*******************************************************************/
969     return AllocateBuffer(g);
970   } else
971     return (mode == MODE_READ && rc == ENOENT)
972             ? PushWarning(g, Tdbp) : true;
973 
974   } // end of OpenTableFile
975 
976 /***********************************************************************/
977 /*  BIGFIX Cardinality: returns table cardinality in number of rows.   */
978 /*  This function can be called with a null argument to test the       */
979 /*  availability of Cardinality implementation (1 yes, 0 no).          */
980 /***********************************************************************/
Cardinality(PGLOBAL g)981 int BGXFAM::Cardinality(PGLOBAL g)
982   {
983   if (g) {
984     char   filename[_MAX_PATH];
985     int   card = -1;
986     BIGINT fsize;
987 
988     PlugSetPath(filename, To_File, Tdbp->GetPath());
989 
990 #if defined(_WIN32)  // OB
991     LARGE_INTEGER len;
992     DWORD         rc = 0;
993 
994     len.QuadPart = -1;
995 
996     if (Hfile == INVALID_HANDLE_VALUE) {
997       HANDLE h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
998                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
999 
1000       if (h == INVALID_HANDLE_VALUE)
1001         if ((rc = GetLastError()) != ERROR_FILE_NOT_FOUND) {
1002           sprintf(g->Message, MSG(OPEN_ERROR), rc, 10, filename);
1003           FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1004                         FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
1005                         (LPTSTR)filename, sizeof(filename), NULL);
1006           strcat(g->Message, filename);
1007           return -1;
1008         } else
1009           return 0;                     // File does not exist
1010 
1011       // Get the size of the file (can be greater than 4 GB)
1012       len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
1013       CloseHandle(h);
1014     } else
1015       len.LowPart = GetFileSize(Hfile, (LPDWORD)&len.HighPart);
1016 
1017     if (len.LowPart == 0xFFFFFFFF && (rc = GetLastError()) != NO_ERROR) {
1018       sprintf(g->Message, MSG(FILELEN_ERROR), "GetFileSize", filename);
1019       return -2;
1020     } else
1021       fsize = len.QuadPart;
1022 
1023 #else    // UNIX
1024     if (Hfile == INVALID_HANDLE_VALUE) {
1025       int h = open64(filename, O_RDONLY, 0);
1026 
1027       if (trace(1))
1028         htrc(" h=%d\n", h);
1029 
1030       if (h == INVALID_HANDLE_VALUE) {
1031         if (trace(1))
1032           htrc("  errno=%d ENOENT=%d\n", errno, ENOENT);
1033 
1034         if (errno != ENOENT) {
1035           sprintf(g->Message, MSG(OPEN_ERROR_IS),
1036                               filename, strerror(errno));
1037           return -1;
1038         } else
1039           return 0;                      // File does not exist
1040 
1041         } // endif h
1042 
1043       // Get the size of the file (can be greater than 4 GB)
1044       fsize = lseek64(h, 0, SEEK_END);
1045       close(h);
1046     } else {
1047       BIGINT curpos = lseek64(Hfile, 0, SEEK_CUR);
1048 
1049       fsize = lseek64(Hfile, 0, SEEK_END);
1050       lseek64(Hfile, curpos, SEEK_SET);
1051     } // endif Hfile
1052 
1053     if (fsize < 0) {
1054       sprintf(g->Message, MSG(FILELEN_ERROR), "lseek64", filename);
1055       return -2;
1056       } // endif fsize
1057 
1058 #endif   // UNIX
1059 
1060     // Check the real size of the file
1061     if (Padded && Blksize) {
1062       if (fsize % (BIGINT)Blksize) {
1063         sprintf(g->Message, MSG(NOT_FIXED_LEN),
1064                             filename, (int)fsize, Lrecl);
1065         return -3;
1066       } else
1067         card = (int)(fsize / (BIGINT)Blksize) * Nrec;
1068 
1069     } else if (fsize % (BIGINT)Lrecl) {
1070       sprintf(g->Message, MSG(NOT_FIXED_LEN), filename, (int)fsize, Lrecl);
1071       return -3;
1072     } else
1073       card = (int)(fsize / (BIGINT)Lrecl); // Fixed length file
1074 
1075     if (trace(1))
1076       htrc(" Computed max_K=%d fsize=%lf lrecl=%d\n",
1077            card, (double)fsize, Lrecl);
1078 
1079     // Set number of blocks for later use
1080     Block = (card + Nrec - 1) / Nrec;
1081     return card;
1082   } else
1083     return -1;
1084 
1085   } // end of Cardinality
1086 
1087 /***********************************************************************/
1088 /*  WriteModifiedBlock: Used when updating.                            */
1089 /***********************************************************************/
WriteModifiedBlock(PGLOBAL g)1090 int BGXFAM::WriteModifiedBlock(PGLOBAL g)
1091   {
1092   /*********************************************************************/
1093   /*  The old block was modified in Update mode.                       */
1094   /*  In Update mode we simply rewrite the old block on itself.        */
1095   /*********************************************************************/
1096   int  rc = RC_OK;
1097   bool moved = false;
1098 
1099   if (UseTemp)                // Copy any intermediate lines.
1100     if (MoveIntermediateLines(g, &moved))
1101       rc = RC_FX;
1102 
1103   if (rc == RC_OK) {
1104     // Set file position to OldBlk position (Fpos)
1105     if (!moved && BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
1106       rc = RC_FX;
1107     else if (BigWrite(g, Tfile, To_Buf, Lrecl * Rbuf))
1108       rc = RC_FX;
1109 
1110     Spos = Fpos + Nrec;       // + Rbuf ???
1111     } // endif rc
1112 
1113   if (Closing || rc != RC_OK) // Error or called from CloseDB
1114     return rc;
1115 
1116   // NOTE: Next line was added to avoid a very  strange fread bug.
1117   // When the fseek is not executed (even the file has the good
1118   // pointer position) the next read can happen anywhere in the file.
1119   OldBlk = CurBlk;       // This will force fseek to be executed
1120   Modif = 0;
1121   return rc;
1122   } // end of WriteModifiedBlock
1123 
1124 /***********************************************************************/
1125 /*  ReadBuffer: Read Nrec lines for a big fixed/binary file.           */
1126 /***********************************************************************/
ReadBuffer(PGLOBAL g)1127 int BGXFAM::ReadBuffer(PGLOBAL g)
1128   {
1129   int nbr, rc = RC_OK;
1130 
1131   /*********************************************************************/
1132   /*  Sequential reading when Placed is not true.                      */
1133   /*********************************************************************/
1134   if (Placed) {
1135     Tdbp->SetLine(To_Buf + CurNum * Lrecl);
1136     Placed = false;
1137   } else if (++CurNum < Rbuf) {
1138     Tdbp->IncLine(Lrecl);                // Used by DOSCOL functions
1139     return RC_OK;
1140   } else if (Rbuf < Nrec && CurBlk != -1) {
1141     return RC_EF;
1142   } else {
1143     /*******************************************************************/
1144     /*  New block.                                                     */
1145     /*******************************************************************/
1146     CurNum = 0;
1147     Tdbp->SetLine(To_Buf);
1148 
1149  next:
1150     if (++CurBlk >= Block)
1151       return RC_EF;
1152 
1153     /*******************************************************************/
1154     /*  Before reading a new block, check whether block optimization   */
1155     /*  can be done, as well as for join as for local filtering.       */
1156     /*******************************************************************/
1157     switch (Tdbp->TestBlock(g)) {
1158       case RC_EF:
1159         return RC_EF;
1160       case RC_NF:
1161         goto next;
1162       } // endswitch rc
1163 
1164    } // endif's
1165 
1166   if (OldBlk == CurBlk) {
1167     IsRead = true;       // Was read indeed
1168     return RC_OK;        // Block is already there
1169     } // endif OldBlk
1170 
1171   // Write modified block in mode UPDATE
1172   if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
1173     return rc;
1174 
1175   Fpos = CurBlk * Nrec;
1176 
1177   // Setting file pointer is required only in non sequential reading
1178   if (CurBlk != OldBlk + 1)
1179     if (BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
1180       return RC_FX;
1181 
1182   if (trace(2))
1183     htrc("File position is now %d\n", Fpos);
1184 
1185   nbr = BigRead(g, Hfile, To_Buf, (Padded) ? Blksize : Lrecl * Nrec);
1186 
1187   if (nbr > 0) {
1188     Rbuf = (Padded) ? Nrec : nbr / Lrecl;
1189     rc = RC_OK;
1190     ReadBlks++;
1191     num_read++;
1192   } else
1193     rc = (nbr == 0) ? RC_EF : RC_FX;
1194 
1195   OldBlk = CurBlk;             // Last block actually read
1196   IsRead = true;               // Is read indeed
1197   return rc;
1198   } // end of ReadBuffer
1199 
1200 /***********************************************************************/
1201 /*  WriteBuffer: File write routine for BGXFAM access method.          */
1202 /*  Updates are written into the (Temp) file in ReadBuffer.            */
1203 /***********************************************************************/
WriteBuffer(PGLOBAL g)1204 int BGXFAM::WriteBuffer(PGLOBAL g)
1205   {
1206   if (trace(2))
1207     htrc("BIG WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n",
1208          Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum);
1209 
1210   if (Tdbp->GetMode() == MODE_INSERT) {
1211     /*******************************************************************/
1212     /*  In Insert mode, blocks are added sequentialy to the file end.  */
1213     /*******************************************************************/
1214     if (++CurNum != Rbuf) {
1215       Tdbp->IncLine(Lrecl);            // Used by DOSCOL functions
1216       return RC_OK;                    // We write only full blocks
1217       } // endif CurNum
1218 
1219     if (trace(2))
1220       htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf);
1221 
1222     //  Now start the writing process.
1223     if (BigWrite(g, Hfile, To_Buf, Lrecl * Rbuf))
1224       return RC_FX;
1225 
1226     CurBlk++;
1227     CurNum = 0;
1228     Tdbp->SetLine(To_Buf);
1229 
1230     if (trace(2))
1231       htrc("write done\n");
1232 
1233   } else {                           // Mode == MODE_UPDATE
1234     // Tfile is the temporary file or the table file handle itself
1235     if (Tfile == INVALID_HANDLE_VALUE) {
1236       if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
1237         if (OpenTempFile(g))
1238           return RC_FX;
1239 
1240       } else
1241         Tfile = Hfile;
1242 
1243       } // endif Tfile
1244 
1245     if (Nrec > 1)
1246       Modif++;                         // Modified line in blocked mode
1247     else if (WriteModifiedBlock(g))    // Indexed update
1248       return RC_FX;
1249 
1250   } // endif Mode
1251 
1252   return RC_OK;
1253   } // end of WriteBuffer
1254 
1255 /***********************************************************************/
1256 /*  Data Base delete line routine for BGXFAM access method.            */
1257 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)1258 int BGXFAM::DeleteRecords(PGLOBAL g, int irc)
1259   {
1260   bool moved;
1261 
1262   /*********************************************************************/
1263   /*  There is an alternative here:                                    */
1264   /*  1 - use a temporary file in which are copied all not deleted     */
1265   /*      lines, at the end the original file will be deleted and      */
1266   /*      the temporary file renamed to the original file name.        */
1267   /*  2 - directly move the not deleted lines inside the original      */
1268   /*      file, and at the end erase all trailing records.             */
1269   /*  This will be experimented.                                       */
1270   /*********************************************************************/
1271   if (trace(2))
1272     htrc("BGX DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
1273          irc, UseTemp, Fpos, Tpos, Spos);
1274 
1275   if (irc != RC_OK) {
1276     /*******************************************************************/
1277     /*  EOF: position Fpos at the end-of-file position.                */
1278     /*******************************************************************/
1279     Fpos = Tdbp->Cardinality(g);
1280 
1281     if (trace(2))
1282       htrc("Fpos placed at file end=%d\n", Fpos);
1283 
1284   } else    // Fpos is the deleted line position
1285     Fpos = CurBlk * Nrec + CurNum;
1286 
1287   if (Tpos == Spos) {
1288     /*******************************************************************/
1289     /*  First line to delete. Move of eventual preceding lines is     */
1290     /*  not required here if a temporary file is not used, just the    */
1291     /*  setting of future Spos and Tpos.                               */
1292     /*******************************************************************/
1293     if (UseTemp) {
1294       /*****************************************************************/
1295       /*  Open the temporary file, Spos is at the beginning of file.   */
1296       /*****************************************************************/
1297       if (OpenTempFile(g))
1298         return RC_FX;
1299 
1300     } else {
1301       /*****************************************************************/
1302       /*  Move of eventual preceding lines is not required here.      */
1303       /*  Set the target file as being the source file itself.         */
1304       /*  Set the future Tpos, and give Spos a value to block copying. */
1305       /*****************************************************************/
1306       Tfile = Hfile;
1307       Spos = Tpos = Fpos;
1308     } // endif UseTemp
1309 
1310     } // endif Tpos == Spos
1311 
1312   /*********************************************************************/
1313   /*  Move any intermediate lines.                                     */
1314   /*********************************************************************/
1315   if (MoveIntermediateLines(g, &moved))
1316     return RC_FX;
1317 
1318   if (irc == RC_OK) {
1319     if (trace(1))
1320       assert(Spos == Fpos);
1321 
1322     Spos++;          // New start position is on next line
1323 
1324     if (moved) {
1325       if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl))
1326         return RC_FX;
1327 
1328       OldBlk = -2;  // To force fseek to be executed on next block
1329       } // endif moved
1330 
1331     if (trace(2))
1332       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
1333 
1334   } else if (irc != RC_OK) {
1335     /*******************************************************************/
1336     /*  Last call after EOF has been reached.                          */
1337     /*******************************************************************/
1338     if (UseTemp) {
1339       /*****************************************************************/
1340       /*  Ok, now delete old file and rename new temp file.            */
1341       /*****************************************************************/
1342       if (RenameTempFile(g))
1343         return RC_FX;
1344 
1345     } else {
1346       /*****************************************************************/
1347       /*  Remove extra records.                                        */
1348       /*****************************************************************/
1349 #if defined(_WIN32)
1350       if (BigSeek(g, Hfile, (BIGINT)Tpos * (BIGINT)Lrecl))
1351         return RC_FX;
1352 
1353       if (!SetEndOfFile(Hfile)) {
1354         DWORD drc = GetLastError();
1355 
1356         sprintf(g->Message, MSG(SETEOF_ERROR), drc);
1357         return RC_FX;
1358         } // endif error
1359 #else   // !_WIN32
1360       if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
1361         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1362         return RC_FX;
1363         } // endif
1364 #endif  // !_WIN32
1365 
1366     } // endif UseTemp
1367 
1368   } // endif irc
1369 
1370   return RC_OK;                                      // All is correct
1371   } // end of DeleteRecords
1372 
1373 /***********************************************************************/
1374 /*  Open a temporary file used while updating or deleting.             */
1375 /***********************************************************************/
OpenTempFile(PGLOBAL g)1376 bool BGXFAM::OpenTempFile(PGLOBAL g)
1377   {
1378   char   *tempname;
1379   PDBUSER dup = PlgGetUser(g);
1380 
1381   /*********************************************************************/
1382   /*  Open the temporary file, Spos is at the beginning of file.       */
1383   /*********************************************************************/
1384   tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1385   PlugSetPath(tempname, To_File, Tdbp->GetPath());
1386   strcat(PlugRemoveType(tempname, tempname), ".t");
1387   remove(tempname);       // Be sure it does not exist yet
1388 
1389 #if defined(_WIN32)
1390   Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
1391                      CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1392 
1393   if (Tfile == INVALID_HANDLE_VALUE) {
1394     DWORD rc = GetLastError();
1395     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
1396     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1397               FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
1398               (LPTSTR)tempname, _MAX_PATH, NULL);
1399     strcat(g->Message, tempname);
1400     return true;
1401     } // endif Tfile
1402 #else    // UNIX
1403   Tfile = open64(tempname, O_WRONLY | O_TRUNC, S_IWRITE);
1404 
1405   if (Tfile == INVALID_HANDLE_VALUE) {
1406     int rc = errno;
1407     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
1408     strcat(g->Message, strerror(errno));
1409     return true;
1410     } //endif Tfile
1411 #endif   // UNIX
1412 
1413   To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1414   To_Fbt->Fname = tempname;
1415   To_Fbt->Type = TYPE_FB_HANDLE;
1416   To_Fbt->Memory = NULL;
1417   To_Fbt->Length = 0;
1418   To_Fbt->File = NULL;
1419   To_Fbt->Next = dup->Openlist;
1420   To_Fbt->Count = 1;
1421   To_Fbt->Mode = MODE_INSERT;
1422   To_Fbt->Handle = Tfile;
1423   dup->Openlist = To_Fbt;
1424   return false;
1425   } // end of OpenTempFile
1426 
1427 /***********************************************************************/
1428 /*  Move intermediate deleted or updated lines.                        */
1429 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool * b)1430 bool BGXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
1431   {
1432   int n, req, nbr;
1433 
1434   for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
1435     /*******************************************************************/
1436     /*  Non consecutive line to delete. Move intermediate lines.       */
1437     /*******************************************************************/
1438     if (!UseTemp || !*b)
1439       if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl))
1440         return true;
1441 
1442     req = MY_MIN(n, Dbflen) * Lrecl;
1443 
1444     if ((nbr = BigRead(g, Hfile, DelBuf, req)) != req) {
1445       sprintf(g->Message, MSG(DEL_READ_ERROR), req, nbr);
1446       return true;
1447       } // endif nbr
1448 
1449     if (!UseTemp)
1450       if (BigSeek(g, Tfile, (BIGINT)Tpos * (BIGINT)Lrecl))
1451         return true;
1452 
1453     if (BigWrite(g, Tfile, DelBuf, req))
1454       return true;
1455 
1456     req /= Lrecl;
1457     Tpos += (int)req;
1458     Spos += (int)req;
1459 
1460     if (trace(2))
1461       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
1462 
1463     *b = true;
1464     } // endfor n
1465 
1466   return false;
1467   } // end of MoveIntermediateLines
1468 
1469 /***********************************************************************/
1470 /*  Data Base close routine for BIGFIX access method.                  */
1471 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)1472 void BGXFAM::CloseTableFile(PGLOBAL g, bool abort)
1473   {
1474   int rc = RC_OK, wrc = RC_OK;
1475   MODE mode = Tdbp->GetMode();
1476 
1477   Abort = abort;
1478 
1479   // Closing is True if last Write was in error
1480   if (mode == MODE_INSERT && CurNum && !Closing) {
1481     // Some more inserted lines remain to be written
1482     Rbuf = CurNum--;
1483     wrc = WriteBuffer(g);
1484   } else if (mode == MODE_UPDATE) {
1485     if (Modif && !Closing) {
1486       // Last updated block remains to be written
1487       Closing = true;
1488       wrc = WriteModifiedBlock(g);
1489       } // endif Modif
1490 
1491     if (UseTemp && Tfile && wrc == RC_OK) {
1492       if (!Abort) {
1493         // Copy any remaining lines
1494         bool b;
1495 
1496         Fpos = Tdbp->Cardinality(g);
1497         Abort = MoveIntermediateLines(g, &b) != RC_OK;
1498         } // endif Abort
1499 
1500       // Delete the old file and rename the new temp file.
1501       RenameTempFile(g);
1502       goto fin;
1503       } // endif UseTemp
1504 
1505   } // endif's mode
1506 
1507   // Finally close the file
1508   rc = PlugCloseFile(g, To_Fb);
1509 
1510  fin:
1511   if (trace(1))
1512     htrc("BGX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
1513          To_File, mode, wrc, rc);
1514 
1515   Hfile = INVALID_HANDLE_VALUE; // So we can know whether table is open
1516   } // end of CloseTableFile
1517 
1518 /***********************************************************************/
1519 /*  Rewind routine for huge FIX access method.                         */
1520 /*  Note: commenting out OldBlk = -1 has two advantages:               */
1521 /*  1 - It forces fseek on  first block, thus suppressing the need to  */
1522 /*      rewind the file, anyway unuseful when second pass if indexed.  */
1523 /*  2 - It permit to avoid re-reading small tables having only 1 block.*/
1524 /*      (even very unlikely for huge files!)                           */
1525 /***********************************************************************/
Rewind(void)1526 void BGXFAM::Rewind(void)
1527   {
1528 #if 0    // This is probably unuseful because file is accessed directly
1529 #if defined(_WIN32)  //OB
1530   SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
1531 #else    // UNIX
1532   lseek64(Hfile, 0, SEEK_SET);
1533 #endif   // UNIX
1534 #endif // 0
1535   CurBlk = -1;
1536   CurNum = Rbuf;
1537 //OldBlk = -1;
1538 //Rbuf = 0;        commented out in case we reuse last read block
1539   Fpos = 0;
1540   } // end of Rewind
1541