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