1 /*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
2 /* PROGRAM NAME: FILAMVCT                                              */
3 /* -------------                                                       */
4 /*  Version 2.6                                                        */
5 /*                                                                     */
6 /* COPYRIGHT:                                                          */
7 /* ----------                                                          */
8 /*  (C) Copyright to the author Olivier BERTRAND          2005-2020    */
9 /*                                                                     */
10 /* WHAT THIS PROGRAM DOES:                                             */
11 /* -----------------------                                             */
12 /*  This program are the VCT file access method classes.               */
13 /*  Added in version 2:                                                */
14 /*  - Split Vec format.                                                */
15 /*  - Partial delete.                                                  */
16 /*  - Use of tempfile for update.                                      */
17 /*                                                                     */
18 /***********************************************************************/
19 
20 /***********************************************************************/
21 /*  Include relevant MariaDB header file.                              */
22 /***********************************************************************/
23 #include "my_global.h"
24 #if defined(_WIN32)
25 #include <io.h>
26 #include <fcntl.h>
27 #if defined(__BORLANDC__)
28 #define __MFC_COMPAT__                   // To define min/max as macro
29 #endif   // __BORLAND__
30 //#include <windows.h>
31 #include <sys/stat.h>
32 #else   // !_WIN32
33 #if defined(UNIX)
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #define NO_ERROR 0
39 #else   // !UNIX
40 #include <io.h>
41 #endif  // !UNIX
42 #include <fcntl.h>
43 #endif  // !_WIN32
44 
45 /***********************************************************************/
46 /*  Include application header files:                                  */
47 /*  global.h    is header containing all global declarations.          */
48 /*  plgdbsem.h  is header containing the DB application declarations.  */
49 /*  tabdos.h    is header containing the TABDOS class declarations.    */
50 /***********************************************************************/
51 #include "global.h"
52 #include "osutil.h"            // Unuseful for WINDOWS
53 #include "plgdbsem.h"
54 #include "valblk.h"
55 #include "filamfix.h"
56 #include "tabdos.h"
57 #include "tabvct.h"
58 #include "maputil.h"
59 #include "filamvct.h"
60 
61 #ifndef INVALID_SET_FILE_POINTER
62 #define INVALID_SET_FILE_POINTER  ((DWORD)-1)
63 #endif
64 
65 extern int num_read, num_there;                          // Statistics
66 static int num_write;
67 
68 /***********************************************************************/
69 /*  Header containing block info for not split VEC tables.             */
70 /*  Block and last values can be calculated from NumRec and Nrec.      */
71 /*  This is better than directly storing Block and Last because it     */
72 /*  make possible to use the same file with tables having a different  */
73 /*  block size value (Element -> Nrec)                                 */
74 /*  Note: can be in a separate file if header=1 or a true header (2)   */
75 /***********************************************************************/
76 typedef struct _vecheader {
77 //int Block;              /* The number of used blocks                 */
78 //int Last;               /* The number of used records in last block  */
79   int MaxRec;             /* Max number of records (True vector format)*/
80   int NumRec;             /* Number of valid records in the table      */
81   } VECHEADER;
82 
83 /***********************************************************************/
84 /*  Char VCT column blocks are right filled with blanks (blank = true) */
85 /*  Conversion of block values allowed conditionally for insert only.  */
86 /***********************************************************************/
87 PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
88                     bool check = true, bool blank = true, bool un = false);
89 
90 /* -------------------------- Class VCTFAM --------------------------- */
91 
92 /***********************************************************************/
93 /*  Implementation of the VCTFAM class.                                */
94 /***********************************************************************/
VCTFAM(PVCTDEF tdp)95 VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp)
96   {
97   Last = tdp->GetLast();
98   MaxBlk = (tdp->GetEstimate() > 0) ?
99           ((tdp->GetEstimate() - 1) / Nrec + 1) : 0;
100   NewBlock = NULL;
101   AddBlock = false;
102   Split = false;
103 
104   if ((Header = (MaxBlk) ? tdp->Header : 0))
105     Block = Last = -1;
106 
107   Bsize = Nrec;
108   CurNum = Nrec - 1;
109   Colfn = NULL;
110   Tempat = NULL;
111   Clens = NULL;
112   Deplac = NULL;
113   Isnum = NULL;
114   Ncol = 0;
115   } // end of VCTFAM standard constructor
116 
VCTFAM(PVCTFAM txfp)117 VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp)
118   {
119   MaxBlk = txfp->MaxBlk;
120   NewBlock = NULL;
121   AddBlock = false;
122   Split = txfp->Split;
123   Header = txfp->Header;
124   Bsize = txfp->Bsize;
125   Colfn = txfp->Colfn;
126   Tempat = txfp->Tempat;
127   Clens = txfp->Clens;
128   Deplac = txfp->Deplac;
129   Isnum = txfp->Isnum;
130   Ncol = txfp->Ncol;
131   } // end of VCTFAM copy constructor
132 
133 /***********************************************************************/
134 /*  VCT GetFileLength: returns file size in number of bytes.           */
135 /*  This function is here to be accessible by VECFAM and VMPFAM.       */
136 /***********************************************************************/
GetFileLength(PGLOBAL g)137 int VCTFAM::GetFileLength(PGLOBAL g)
138   {
139   if (Split) {
140     // Get the total file length
141     char filename[_MAX_PATH];
142     PCSZ savfile = To_File;
143     int  i, len = 0;
144 
145     //  Initialize the array of file structures
146     if (!Colfn) {
147       // Prepare the column file name pattern and set Ncol
148       Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
149       Ncol = ((PVCTDEF)Tdbp->GetDef())->MakeFnPattern(Colfn);
150       } // endif Colfn
151 
152     To_File = filename;
153 
154     for (i = 0; i < Ncol; i++) {
155       sprintf(filename, Colfn, i+1);
156       len += TXTFAM::GetFileLength(g);
157       } // endfor i
158 
159     To_File = savfile;
160     return len;
161   } else
162     return TXTFAM::GetFileLength(g);
163 
164   } // end of GetFileLength
165 
166 /***********************************************************************/
167 /*  Reset read/write position values.                                  */
168 /***********************************************************************/
Reset(void)169 void VCTFAM::Reset(void)
170   {
171   FIXFAM::Reset();
172   NewBlock = NULL;
173   AddBlock = false;
174   CurNum = Nrec - 1;
175   } // end of Reset
176 
177 /***********************************************************************/
178 /*  Get the Headlen, Block and Last info from the file header.         */
179 /***********************************************************************/
GetBlockInfo(PGLOBAL g)180 int VCTFAM::GetBlockInfo(PGLOBAL g)
181   {
182   char      filename[_MAX_PATH];
183   int       h, k, n;
184   VECHEADER vh;
185 
186   if (Header < 1 || Header > 3 || !MaxBlk) {
187     sprintf(g->Message, "Invalid header value %d", Header);
188     return -1;
189   } else
190     n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
191 
192   PlugSetPath(filename, To_File, Tdbp->GetPath());
193 
194   if (Header == 2)
195     strcat(PlugRemoveType(filename, filename), ".blk");
196 
197   if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
198       || !_filelength(h)) {
199     // Consider this is a void table
200     Last = Nrec;
201     Block = 0;
202 
203     if (h != -1)
204       close(h);
205 
206     return n;
207   } else if (Header == 3)
208     k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END);
209 
210   if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) {
211     sprintf(g->Message, "Error reading header file %s", filename);
212     n = -1;
213   } else if (MaxBlk * Nrec != vh.MaxRec) {
214     sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
215                         vh.MaxRec, MaxBlk, Nrec);
216     n = -1;
217   } else {
218     Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
219     Last  = (vh.NumRec + Nrec - 1) % Nrec + 1;
220   } // endif s
221 
222   close(h);
223   return n;
224   } // end of GetBlockInfo
225 
226 /***********************************************************************/
227 /*  Get the Headlen, Block and Last info from the file header.         */
228 /***********************************************************************/
SetBlockInfo(PGLOBAL g)229 bool VCTFAM::SetBlockInfo(PGLOBAL g)
230   {
231   char      filename[_MAX_PATH];
232   bool      rc = false;
233   size_t    n;
234   VECHEADER vh;
235   FILE     *s;
236 
237   PlugSetPath(filename, To_File, Tdbp->GetPath());
238 
239   if (Header != 2) {
240     if (Stream) {
241       s = Stream;
242 
243       if (Header == 1)
244         /*k =*/ fseek(s, 0, SEEK_SET);
245 
246     } else
247       s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b");
248 
249   } else {      // Header == 2
250     strcat(PlugRemoveType(filename, filename), ".blk");
251     s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
252   } // endif Header
253 
254   if (!s) {
255     sprintf(g->Message, "Error opening header file %s", filename);
256     return true;
257   } else if (Header == 3)
258     /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END);
259 
260   vh.MaxRec = MaxBlk * Bsize;
261   vh.NumRec = (Block - 1) * Nrec + Last;
262 
263   if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) {
264     sprintf(g->Message, "Error writing header file %s", filename);
265     rc = true;
266     } // endif fread
267 
268   if (Header == 2 || !Stream)
269     fclose(s);
270 
271   return rc;
272   } // end of SetBlockInfo
273 
274 /***********************************************************************/
275 /*  Use BlockTest to reduce the table estimated size.                  */
276 /***********************************************************************/
MaxBlkSize(PGLOBAL g,int)277 int VCTFAM::MaxBlkSize(PGLOBAL g, int)
278   {
279   int rc = RC_OK, savcur = CurBlk;
280   int size;
281 
282   // Roughly estimate the table size as the sum of blocks
283   // that can contain good rows
284   for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
285     if ((rc = Tdbp->TestBlock(g)) == RC_OK)
286       size += (CurBlk == Block - 1) ? Last : Nrec;
287     else if (rc == RC_EF)
288       break;
289 
290   CurBlk = savcur;
291   return size;
292   } // end of MaxBlkSize
293 
294 /***********************************************************************/
295 /*  VCT Cardinality: returns table cardinality in number of rows.      */
296 /*  This function can be called with a null argument to test the       */
297 /*  availability of Cardinality implementation (1 yes, 0 no).          */
298 /***********************************************************************/
Cardinality(PGLOBAL g)299 int VCTFAM::Cardinality(PGLOBAL g)
300   {
301   if (!g)
302     return 1;
303 
304   if (Block < 0)
305   {
306     if (Split) {
307       // Separate column files and no pre setting of Block and Last
308       // This allows to see a table modified externally, but Block
309       // and Last must be set from the file cardinality.
310       // Only happens when called by sub classes.
311       char    filename[_MAX_PATH];
312       PCSZ    savfn = To_File;
313       int     len, clen, card = -1;
314       PCOLDEF cdp = Tdbp->GetDef()->GetCols();
315 
316       if (!Colfn) {
317         // Prepare the column file name pattern
318         Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
319         Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
320         } // endif Colfn
321 
322       // Use the first column file to calculate the cardinality
323       clen = cdp->GetClen();
324       sprintf(filename, Colfn, 1);
325       To_File = filename;
326       len = TXTFAM::GetFileLength(g);
327       To_File = savfn;
328 
329       if (len >= 0) {
330         if (!(len % clen))
331           card = len / clen;           // Fixed length file
332         else
333           sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen);
334 
335       if (trace(1))
336         htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen);
337 
338       } else
339         card = 0;
340 
341       // Set number of blocks for later use
342       Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
343       Last = (card + Nrec - 1) % Nrec + 1;
344       return card;
345     } else {
346       // Vector table having Block and Last info in a Header (file)
347       if ((Headlen = GetBlockInfo(g)) < 0)
348         return -1;             // Error
349 
350     } // endif split
351   }
352   return (Block) ? ((Block - 1) * Nrec + Last) : 0;
353   } // end of Cardinality
354 
355 /***********************************************************************/
356 /*  GetRowID: return the RowID of last read record.                    */
357 /***********************************************************************/
GetRowID(void)358 int VCTFAM::GetRowID(void)
359   {
360   return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk
361                                : (Block - 1) * Nrec + Last);
362   } // end of GetRowID
363 
364 /***********************************************************************/
365 /*  VCT Create an empty file for Vector formatted tables.              */
366 /***********************************************************************/
MakeEmptyFile(PGLOBAL g,PCSZ fn)367 bool VCTFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
368   {
369   // Vector formatted file: this will create an empty file of the
370   // required length if it does not exists yet.
371   char filename[_MAX_PATH], c = 0;
372   int  h, n;
373 
374   PlugSetPath(filename, fn, Tdbp->GetPath());
375 #if defined(_WIN32)
376   h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE);
377 #else   // !_WIN32
378   h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename,  O_CREAT |  O_WRONLY, S_IREAD | S_IWRITE);
379 #endif  // !_WIN32
380 
381   if (h == -1)
382     return true;
383 
384   n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
385 
386   if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) < 0)
387     goto err;
388 
389   // This actually fills the empty file
390   if (write(h, &c, 1) < 0)
391     goto err;
392 
393   close(h);
394   return false;
395 
396  err:
397   sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
398   close(h);
399   return true;
400   } // end of MakeEmptyFile
401 
402 /***********************************************************************/
403 /*  VCT Access Method opening routine.                                 */
404 /*  New method now that this routine is called recursively (last table */
405 /*  first in reverse order): index blocks are immediately linked to    */
406 /*  join block of next table if it exists or else are discarted.       */
407 /***********************************************************************/
OpenTableFile(PGLOBAL g)408 bool VCTFAM::OpenTableFile(PGLOBAL g)
409   {
410   char    opmode[4], filename[_MAX_PATH];
411   MODE    mode = Tdbp->GetMode();
412   PDBUSER dbuserp = PlgGetUser(g);
413 
414   /*********************************************************************/
415   /*  Update block info if necessary.                                  */
416   /*********************************************************************/
417   if (Block < 0)
418     if ((Headlen = GetBlockInfo(g)) < 0)
419       return true;
420 
421   /*********************************************************************/
422   /*  Open according to input/output mode required.                    */
423   /*********************************************************************/
424   switch (mode) {
425     case MODE_READ:
426       strcpy(opmode, "rb");
427       break;
428     case MODE_DELETE:
429       if (!Tdbp->GetNext()) {
430         // Store the number of deleted lines
431         DelRows = Cardinality(g);
432 
433         // This will delete the whole file
434         strcpy(opmode, "wb");
435         break;
436         } // endif
437 
438       // Selective delete, pass thru
439       /* fall through */
440     case MODE_UPDATE:
441       UseTemp = Tdbp->IsUsingTemp(g);
442       strcpy(opmode, (UseTemp) ? "rb" : "r+b");
443       break;
444     case MODE_INSERT:
445       if (MaxBlk) {
446         if (!Block)
447           if (MakeEmptyFile(g, To_File))
448             return true;
449 
450         strcpy(opmode, "r+b");   // Required to update empty blocks
451       } else if (!Block || Last == Nrec)
452         strcpy(opmode, "ab");
453       else
454         strcpy(opmode, "r+b");   // Required to update the last block
455 
456       break;
457     default:
458       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
459       return true;
460     } // endswitch Mode
461 
462   /*********************************************************************/
463   /*  Use conventionnal input/output functions.                        */
464   /*********************************************************************/
465   PlugSetPath(filename, To_File, Tdbp->GetPath());
466 
467   if (!(Stream = PlugOpenFile(g, filename, opmode))) {
468     if (trace(1))
469       htrc("%s\n", g->Message);
470 
471     return (mode == MODE_READ && errno == ENOENT)
472             ? PushWarning(g, Tdbp) : true;
473     } // endif Stream
474 
475   if (trace(1))
476     htrc("File %s is open in mode %s\n", filename, opmode);
477 
478   To_Fb = dbuserp->Openlist;     // Keep track of File block
479 
480   if (!strcmp(opmode, "wb"))
481     // This will stop the process by
482     // causing GetProgMax to return 0.
483     return ResetTableSize(g, 0, Nrec);
484 
485   num_read = num_there = num_write = 0;
486 
487   //  Allocate the table and column block buffer
488   return AllocateBuffer(g);
489   } // end of OpenTableFile
490 
491 /***********************************************************************/
492 /*  Allocate the block buffers for columns used in the query.          */
493 /***********************************************************************/
AllocateBuffer(PGLOBAL g)494 bool VCTFAM::AllocateBuffer(PGLOBAL g)
495   {
496   MODE    mode = Tdbp->GetMode();
497   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
498   PCOLDEF cdp;
499   PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
500 
501   if (mode == MODE_INSERT) {
502     bool chk = PlgGetUser(g)->Check & CHK_TYPE;
503 
504     NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
505 
506     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
507       memset(NewBlock + Nrec * cdp->GetPoff(),
508             (IsTypeNum(cdp->GetType()) ? 0 : ' '),
509                         Nrec * cdp->GetClen());
510 
511     for (; cp; cp = (PVCTCOL)cp->Next)
512       cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
513                               cp->Buf_Type, Nrec, cp->Format.Length,
514                               cp->Format.Prec, chk, true,
515 				                      cp->IsUnsigned());
516 
517     return InitInsert(g);    // Initialize inserting
518   } else {
519     if (UseTemp || mode == MODE_DELETE) {
520       // Allocate all that is needed to move lines
521       int i = 0, n = (MaxBlk) ? MaxBlk : 1;
522 
523       if (!Ncol)
524         for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
525           Ncol++;
526 
527       Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
528       Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
529       Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
530 
531       for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
532         Clens[i] = cdp->GetClen();
533         Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec;
534         Isnum[i] = IsTypeNum(cdp->GetType());
535         Buflen = MY_MAX(Buflen, cdp->GetClen());
536         } // endfor cdp
537 
538       if (!UseTemp || MaxBlk) {
539         Buflen *= Nrec;
540         To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
541       } else
542         NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
543 
544       } // endif mode
545 
546     for (; cp; cp = (PVCTCOL)cp->Next)
547       if (!cp->IsSpecial())            // Not a pseudo column
548         cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
549                                 cp->Format.Length, cp->Format.Prec,
550 				                        true, true, cp->IsUnsigned());
551 
552   } //endif mode
553 
554   return false;
555   } // end of AllocateBuffer
556 
557 /***********************************************************************/
558 /*  Do initial action when inserting.                                  */
559 /***********************************************************************/
InitInsert(PGLOBAL g)560 bool VCTFAM::InitInsert(PGLOBAL g)
561 {
562 	bool rc = false;
563 
564   // We come here in MODE_INSERT only
565   if (Last == Nrec) {
566     CurBlk = Block;
567     CurNum = 0;
568     AddBlock = !MaxBlk;
569   } else {
570     PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
571 
572     // The starting point must be at the end of file as for append.
573     CurBlk = Block - 1;
574     CurNum = Last;
575 
576 		try {
577 			// Last block must be updated by new values
578 			for (; cp; cp = (PVCTCOL)cp->Next)
579 				cp->ReadBlock(g);
580 
581 		} catch (int n) {
582 			if (trace(1))
583 				htrc("Exception %d: %s\n", n, g->Message);
584 			rc = true;
585 		} catch (const char *msg) {
586 			strcpy(g->Message, msg);
587 			rc = true;
588 		} // end catch
589 
590   } // endif Last
591 
592 	if (!rc)
593     // We are not currently using a temporary file for Insert
594     T_Stream = Stream;
595 
596   return rc;
597   } // end of InitInsert
598 
599 /***********************************************************************/
600 /*  ReadBuffer: Read one line for a VCT file.                          */
601 /***********************************************************************/
ReadBuffer(PGLOBAL g)602 int VCTFAM::ReadBuffer(PGLOBAL g)
603   {
604   int  rc = RC_OK;
605   MODE mode = Tdbp->GetMode();
606 
607   if (Placed)
608     Placed = false;
609   else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) {
610     /*******************************************************************/
611     /*  New block.                                                     */
612     /*******************************************************************/
613     CurNum = 0;
614 
615    next:
616     if (++CurBlk == Block)
617       return RC_EF;                        // End of file
618 
619     /*******************************************************************/
620     /*  Before reading a new block, check whether block optimizing     */
621     /*  can be done, as well as for join as for local filtering.       */
622     /*******************************************************************/
623     switch (Tdbp->TestBlock(g)) {
624       case RC_EF:
625         return RC_EF;
626       case RC_NF:
627         goto next;
628       } // endswitch rc
629 
630     num_there++;
631     } // endif CurNum
632 
633   if (OldBlk != CurBlk) {
634     if (mode == MODE_UPDATE) {
635       /*****************************************************************/
636       /*  Flush the eventually modified column buffers in old blocks   */
637       /*  and read the blocks to modify attached to Set columns.       */
638       /*****************************************************************/
639       if (MoveLines(g))               // For VECFAM
640         return RC_FX;
641 
642       for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols();
643                    colp; colp = (PVCTCOL)colp->Next) {
644         colp->WriteBlock(g);
645         colp->ReadBlock(g);
646         } // endfor colp
647 
648       } // endif mode
649 
650     OldBlk = CurBlk;             // Last block actually read
651     } // endif oldblk
652 
653   if (trace(1))
654     htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK);
655 
656   return rc;
657   } // end of ReadBuffer
658 
659 /***********************************************************************/
660 /*  Data Base write routine for VCT access method.                     */
661 /***********************************************************************/
WriteBuffer(PGLOBAL g)662 int VCTFAM::WriteBuffer(PGLOBAL g)
663   {
664   if (trace(1))
665     htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
666           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
667 
668   if (Tdbp->GetMode() == MODE_UPDATE) {
669     // Mode Update is done in ReadDB, we just initialize it here
670     if (!T_Stream) {
671       if (UseTemp) {
672         if (OpenTempFile(g))
673           return RC_FX;
674 
675         // Most of the time, not all table columns are updated.
676         // This why we must completely pre-fill the temporary file.
677         Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
678                         : Block * Nrec;    // To write last lock
679 
680         if (MoveIntermediateLines(g))
681           return RC_FX;
682 
683       } else
684         T_Stream = Stream;
685 
686       } // endif T_Stream
687 
688   } else {
689     // Mode Insert
690     if (MaxBlk && CurBlk == MaxBlk) {
691       strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
692       return RC_EF;       // Too many lines for vector formatted table
693       } // endif MaxBlk
694 
695     if (Closing || ++CurNum == Nrec) {
696       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
697 
698       if (!AddBlock) {
699         // Write back the updated last block values
700         for (; cp; cp = (PVCTCOL)cp->Next)
701           cp->WriteBlock(g);
702 
703         if (!Closing && !MaxBlk) {
704           // For VCT tables, future blocks must be added
705           char filename[_MAX_PATH];
706 
707           // Close the file and reopen it in mode Insert
708           fclose(Stream);
709           PlugSetPath(filename, To_File, Tdbp->GetPath());
710 
711           if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) {
712             Closing = true;          // Tell CloseDB of error
713             return RC_FX;
714             } // endif Stream
715 
716           AddBlock = true;
717           } // endif Closing
718 
719       } else {
720         // Here we must add a new block to the file
721         if (Closing)
722           // Reset the overwritten columns for last block extra records
723           for (; cp; cp = (PVCTCOL)cp->Next)
724             memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
725                    (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
726                    (Nrec - Last) * cp->Clen);
727 
728         if ((size_t)Nrec !=
729              fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) {
730           sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
731           return RC_FX;
732           } // endif
733 
734       } // endif AddBlock
735 
736       if (!Closing) {
737         CurBlk++;
738         CurNum = 0;
739         } // endif Closing
740 
741       } // endif Closing || CurNum
742 
743   } // endif Mode
744 
745   return RC_OK;
746   } // end of WriteBuffer
747 
748 /***********************************************************************/
749 /*  Data Base delete line routine for VCT access method.               */
750 /*  Note: lines are moved directly in the files (ooops...)             */
751 /*  Using temp file depends on the Check setting, false by default.    */
752 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)753 int VCTFAM::DeleteRecords(PGLOBAL g, int irc)
754   {
755   bool eof = false;
756 
757   if (trace(1))
758     htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
759           irc, UseTemp, Fpos, Tpos, Spos);
760 
761   if (irc != RC_OK) {
762     /*******************************************************************/
763     /*  EOF: position Fpos at the end-of-file position.                */
764     /*******************************************************************/
765     Fpos = (Block - 1) * Nrec + Last;
766 
767     if (trace(1))
768       htrc("Fpos placed at file end=%d\n", Fpos);
769 
770     eof = UseTemp && !MaxBlk;
771   } else     // Fpos is the Deleted line position
772     Fpos = CurBlk * Nrec + CurNum;
773 
774   if (Tpos == Spos) {
775     if (UseTemp) {
776       /*****************************************************************/
777       /*  Open the temporary file, Spos is at the beginning of file.   */
778       /*****************************************************************/
779       if (OpenTempFile(g))
780         return RC_FX;
781 
782     } else {
783       /*****************************************************************/
784       /*  First line to delete. Move of eventual preceding lines is   */
785       /*  not required here, just the setting of future Spos and Tpos. */
786       /*****************************************************************/
787       T_Stream = Stream;
788       Spos = Tpos = Fpos;
789     } // endif UseTemp
790 
791     } // endif Tpos == Spos
792 
793   /*********************************************************************/
794   /*  Move any intermediate lines.                                     */
795   /*********************************************************************/
796   if (MoveIntermediateLines(g, &eof))
797     return RC_FX;
798 
799   if (irc == RC_OK) {
800     /*******************************************************************/
801     /*  Reposition the file pointer and set Spos.                      */
802     /*******************************************************************/
803 #ifdef _DEBUG
804     assert(Spos == Fpos);
805 #endif
806     Spos++;          // New start position is on next line
807 
808     if (trace(1))
809       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
810 
811   } else {
812     /*******************************************************************/
813     /*  Last call after EOF has been reached.                          */
814     /*  Update the Block and Last values.                              */
815     /*******************************************************************/
816     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
817     Last = (Tpos + Nrec - 1) % Nrec + 1;
818 
819     if (!UseTemp) {   // The UseTemp case is treated in CloseTableFile
820       if (!MaxBlk) {
821         /***************************************************************/
822         /*  Because the chsize functionality is only accessible with a */
823         /*  system call we must close the file and reopen it with the  */
824         /*  open function (_fopen for MS ??) this is still to be       */
825         /*  checked for compatibility with Text files and other OS's.  */
826         /***************************************************************/
827         char filename[_MAX_PATH];
828         int h;
829 
830         /*rc =*/ CleanUnusedSpace(g);           // Clean last block
831         /*rc =*/ PlugCloseFile(g, To_Fb);
832         Stream = NULL;                      // For SetBlockInfo
833         PlugSetPath(filename, To_File, Tdbp->GetPath());
834 
835         if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
836           return RC_FX;
837 
838         /***************************************************************/
839         /*  Remove extra blocks.                                       */
840         /***************************************************************/
841 #if defined(UNIX)
842         if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) {
843           sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
844           close(h);
845           return RC_FX;
846           } // endif
847 #else
848         if (chsize(h, Headlen + Block * Blksize)) {
849           sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
850           close(h);
851           return RC_FX;
852           } // endif
853 #endif
854 
855         close(h);
856 
857         if (trace(1))
858           htrc("done, h=%d irc=%d\n", h, irc);
859 
860       } else
861         // Clean the unused space in the file, this is required when
862         // inserting again with a partial column list.
863         if (CleanUnusedSpace(g))
864           return RC_FX;
865 
866       if (ResetTableSize(g, Block, Last))
867         return RC_FX;
868 
869       } // endif UseTemp
870 
871   } // endif irc
872 
873   return RC_OK;                                      // All is correct
874   } // end of DeleteRecords
875 
876 /***********************************************************************/
877 /*  Open a temporary file used while updating or deleting.             */
878 /***********************************************************************/
OpenTempFile(PGLOBAL g)879 bool VCTFAM::OpenTempFile(PGLOBAL g)
880   {
881   PCSZ  opmode;
882 	char  tempname[_MAX_PATH];
883 	bool  rc = false;
884 
885   /*********************************************************************/
886   /*  Open the temporary file, Spos is at the beginning of file.       */
887   /*********************************************************************/
888   PlugSetPath(tempname, To_File, Tdbp->GetPath());
889   strcat(PlugRemoveType(tempname, tempname), ".t");
890 
891   if (MaxBlk) {
892     if (MakeEmptyFile(g, tempname))
893       return true;
894 
895     opmode = "r+b";
896   } else
897     opmode = "wb";
898 
899   if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) {
900     if (trace(1))
901       htrc("%s\n", g->Message);
902 
903     rc = true;
904   } else
905     To_Fbt = PlgGetUser(g)->Openlist;
906 
907   return rc;
908   } // end of OpenTempFile
909 
910 /***********************************************************************/
911 /*  Move intermediate deleted or updated lines.                        */
912 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool * b)913 bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
914   {
915   int    i, dep, off;
916   int    n;
917   bool   eof = (b) ? *b : false;
918   size_t req, len;
919 
920   for (n = Fpos - Spos; n > 0 || eof; n -= req) {
921     /*******************************************************************/
922     /*  Non consecutive line to delete. Move intermediate lines.       */
923     /*******************************************************************/
924     if (!MaxBlk)
925       req = (size_t)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
926     else
927       req = (size_t)MY_MIN(n, Nrec);
928 
929     if (req) for (i = 0; i < Ncol; i++) {
930       if (MaxBlk) {
931         dep = Deplac[i];
932         off = Spos * Clens[i];
933       } else {
934         if (UseTemp)
935           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
936 
937         dep = Deplac[i] + (Spos / Nrec) * Blksize;
938         off = (Spos % Nrec) * Clens[i];
939       } // endif MaxBlk
940 
941       if (fseek(Stream, dep + off, SEEK_SET)) {
942         sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
943         return true;
944         } // endif
945 
946       len = fread(To_Buf, Clens[i], req, Stream);
947 
948       if (trace(1))
949         htrc("after read req=%d len=%d\n", req, len);
950 
951       if (len != req) {
952         sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
953         return true;
954         } // endif len
955 
956       if (!UseTemp || MaxBlk) {
957         if (MaxBlk) {
958           dep = Deplac[i];
959           off = Tpos * Clens[i];
960         } else {
961           dep = Deplac[i] + (Tpos / Nrec) * Blksize;
962           off = (Tpos % Nrec) * Clens[i];
963         } // endif MaxBlk
964 
965         if (fseek(T_Stream, dep + off, SEEK_SET)) {
966           sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
967           return true;
968           } // endif
969 
970         if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
971           sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
972           return true;
973           } // endif
974 
975         } // endif UseTemp
976 
977       if (trace(1))
978         htrc("after write pos=%d\n", ftell(Stream));
979 
980       } // endfor i
981 
982     Tpos += (int)req;
983     Spos += (int)req;
984 
985     if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) {
986       // Write the full or last block to the temporary file
987       if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
988         // Clean the last block in case of future insert,
989         // must be done here because T_Stream was open in write only.
990         for (i = 0; i < Ncol; i++) {
991           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
992           memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
993           } // endfor i
994 
995       // Write a new block in the temporary file
996       len = (size_t)Blksize;
997 
998       if (fwrite(NewBlock, 1, len, T_Stream) != len) {
999         sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1000         return true;
1001         } // endif
1002 
1003       if (Spos == Fpos)
1004         eof = false;
1005 
1006       } // endif UseTemp
1007 
1008     if (trace(1))
1009       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
1010 
1011     } // endfor n
1012 
1013   return false;
1014   } // end of MoveIntermediateLines
1015 
1016 /***********************************************************************/
1017 /*  Clean deleted space in a VCT or Vec table file.                    */
1018 /***********************************************************************/
CleanUnusedSpace(PGLOBAL g)1019 bool VCTFAM::CleanUnusedSpace(PGLOBAL g)
1020   {
1021   int     i, dep;
1022   int    n;
1023   size_t  req, len;
1024 
1025   if (!MaxBlk) {
1026     /*******************************************************************/
1027     /*  Clean last block of the VCT table file.                        */
1028     /*******************************************************************/
1029     assert(!UseTemp);
1030 
1031     if (!(n = Nrec - Last))
1032       return false;
1033 
1034     dep = (Block - 1) * Blksize;
1035     req = (size_t)n;
1036 
1037     for (i = 0; i < Ncol; i++) {
1038       memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1039 
1040       if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) {
1041         sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
1042         return true;
1043         } // endif
1044 
1045       if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) {
1046         sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1047         return true;
1048         } // endif
1049 
1050       } // endfor i
1051 
1052   } else for (n = Fpos - Tpos; n > 0; n -= req) {
1053     /*******************************************************************/
1054     /*  Fill VEC file remaining lines with 0's.                        */
1055     /*  Note: this seems to work even column blocks have been made     */
1056     /*  with Blanks = true. Perhaps should it be set to false for VEC. */
1057     /*******************************************************************/
1058     req = (size_t)MY_MIN(n, Nrec);
1059     memset(To_Buf, 0, Buflen);
1060 
1061     for (i = 0; i < Ncol; i++) {
1062       if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) {
1063         sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
1064         return true;
1065         } // endif
1066 
1067       if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
1068         sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1069         return true;
1070         } // endif
1071 
1072       } // endfor i
1073 
1074     Tpos += (int)req;
1075     } // endfor n
1076 
1077   return false;
1078   } // end of CleanUnusedSpace
1079 
1080 /***********************************************************************/
1081 /*  Data Base close routine for VCT access method.                     */
1082 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)1083 void VCTFAM::CloseTableFile(PGLOBAL g, bool abort)
1084   {
1085   int  rc = 0, wrc = RC_OK;
1086   MODE mode = Tdbp->GetMode();
1087 
1088   Abort = abort;
1089 
1090   if (mode == MODE_INSERT) {
1091     if (Closing)
1092       wrc = RC_FX;                  // Last write was in error
1093     else
1094       if (CurNum) {
1095         // Some more inserted lines remain to be written
1096         Last = CurNum;
1097         Block = CurBlk + 1;
1098         Closing = true;
1099         wrc = WriteBuffer(g);
1100       } else {
1101         Last = Nrec;
1102         Block = CurBlk;
1103         wrc = RC_OK;
1104       } // endif CurNum
1105 
1106     if (wrc != RC_FX) {
1107       rc = ResetTableSize(g, Block, Last);
1108     } else if (AddBlock) {
1109       // Last block was not written
1110       rc = ResetTableSize(g, CurBlk, Nrec);
1111 			throw 44;
1112     } // endif
1113 
1114   } else if (mode == MODE_UPDATE) {
1115     // Write back to file any pending modifications
1116     for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
1117                  colp; colp = (PVCTCOL)colp->Next)
1118       colp->WriteBlock(g);
1119 
1120     if (UseTemp && T_Stream) {
1121       rc = RenameTempFile(g);
1122 
1123       if (Header) {
1124         // Header must be set because it was not set in temp file
1125         Stream = T_Stream = NULL;      // For SetBlockInfo
1126         rc = SetBlockInfo(g);
1127         } // endif Header
1128 
1129       } // endif UseTemp
1130 
1131   } else if (mode == MODE_DELETE && UseTemp && T_Stream) {
1132     if (MaxBlk)
1133       rc = CleanUnusedSpace(g);
1134 
1135     if ((rc = RenameTempFile(g)) != RC_FX) {
1136       Stream = T_Stream = NULL;      // For SetBlockInfo
1137       rc = ResetTableSize(g, Block, Last);
1138       } // endif rc
1139 
1140   } // endif's mode
1141 
1142   if (!(UseTemp && T_Stream))
1143     rc = PlugCloseFile(g, To_Fb);
1144 
1145   if (trace(1))
1146     htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n",
1147           To_File, wrc, rc);
1148 
1149   Stream = NULL;
1150   } // end of CloseTableFile
1151 
1152 /***********************************************************************/
1153 /*  Data Base close routine for VCT access method.                     */
1154 /***********************************************************************/
ResetTableSize(PGLOBAL g,int block,int last)1155 bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last)
1156   {
1157   bool rc = false;
1158 
1159   // Set Block and Last values for TDBVCT::MakeBlockValues
1160   Block = block;
1161   Last = last;
1162 
1163   if (!Split) {
1164     if (!Header) {
1165       // Update catalog values for Block and Last
1166       PVCTDEF defp = (PVCTDEF)Tdbp->GetDef();
1167 
1168       defp->SetBlock(Block);
1169       defp->SetLast(Last);
1170 
1171       if (!defp->SetIntCatInfo("Blocks", Block) ||
1172           !defp->SetIntCatInfo("Last", Last)) {
1173         sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
1174         rc = true;
1175         } // endif
1176 
1177     } else
1178       rc = SetBlockInfo(g);
1179 
1180     } // endif Split
1181 
1182   Tdbp->ResetSize();
1183   return rc;
1184   } // end of ResetTableSize
1185 
1186 /***********************************************************************/
1187 /*  Rewind routine for VCT access method.                              */
1188 /***********************************************************************/
Rewind(void)1189 void VCTFAM::Rewind(void)
1190   {
1191   // In mode update we need to read Set Column blocks
1192   if (Tdbp->GetMode() == MODE_UPDATE)
1193     OldBlk = -1;
1194 
1195   // Initialize so block optimization is called for 1st block
1196   CurBlk = -1;
1197   CurNum = Nrec - 1;
1198 //rewind(Stream);             will be placed by fseek
1199   } // end of Rewind
1200 
1201 /***********************************************************************/
1202 /*  ReadBlock: Read column values from current block.                  */
1203 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)1204 bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
1205   {
1206   int     len;
1207   size_t  n;
1208 
1209   /*********************************************************************/
1210   /*  Calculate the offset and size of the block to read.              */
1211   /*********************************************************************/
1212   if (MaxBlk)                                 // True vector format
1213     len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk);
1214   else                                        // Blocked vector format
1215     len = Nrec * (colp->Deplac + Lrecl * CurBlk);
1216 
1217   if (trace(1))
1218     htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n",
1219           len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
1220 
1221   if (fseek(Stream, len, SEEK_SET)) {
1222     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1223     return true;
1224     } // endif
1225 
1226   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
1227                                         (size_t)Nrec, Stream);
1228 
1229   if (n != (size_t)Nrec) {
1230     if (errno == NO_ERROR)
1231       sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
1232     else
1233       sprintf(g->Message, MSG(READ_ERROR),
1234               To_File, strerror(errno));
1235 
1236     if (trace(1))
1237       htrc(" Read error: %s\n", g->Message);
1238 
1239     return true;
1240     } // endif
1241 
1242   if (trace(1))
1243     num_read++;
1244 
1245   return false;
1246   } // end of ReadBlock
1247 
1248 /***********************************************************************/
1249 /*  WriteBlock: Write back current column values for one block.        */
1250 /*  Note: the test of Status is meant to prevent physical writing of   */
1251 /*  the block during the checking loop in mode Update. It is set to    */
1252 /*  BUF_EMPTY when reopening the table between the two loops.          */
1253 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)1254 bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
1255   {
1256   int   len;
1257   size_t n;
1258 
1259   /*********************************************************************/
1260   /*  Calculate the offset and size of the block to write.             */
1261   /*********************************************************************/
1262   if (MaxBlk)                               // File has Vector format
1263     len = Headlen
1264         + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
1265   else                                      // Old VCT format
1266     len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
1267 
1268   if (trace(1))
1269     htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
1270           Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
1271 
1272   if (fseek(T_Stream, len, SEEK_SET)) {
1273     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1274     return true;
1275     } // endif
1276 
1277   // Here Nrec was changed to CurNum in mode Insert,
1278   // this is the true number of records to write,
1279   // this also avoid writing garbage in the file for true vector tables.
1280   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec;
1281 
1282   if (n != fwrite(colp->Blk->GetValPointer(),
1283                             (size_t)colp->Clen, n, T_Stream)) {
1284     sprintf(g->Message, MSG(WRITE_STRERROR),
1285             (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
1286 
1287     if (trace(1))
1288       htrc("Write error: %s\n", strerror(errno));
1289 
1290     return true;
1291     } // endif
1292 
1293 #if defined(UNIX)
1294   fflush(T_Stream); //NGC
1295 #endif
1296 
1297 #ifdef _DEBUG
1298   num_write++;
1299 #endif
1300 
1301   return false;
1302   } // end of WriteBlock
1303 
1304 /* -------------------------- Class VCMFAM --------------------------- */
1305 
1306 /***********************************************************************/
1307 /*  Implementation of the VCMFAM class.                                */
1308 /***********************************************************************/
VCMFAM(PVCTDEF tdp)1309 VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1310   {
1311   Memory = NULL;
1312   Memcol = NULL;
1313   } // end of VCMFAM standard constructor
1314 
VCMFAM(PVCMFAM txfp)1315 VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp)
1316   {
1317   Memory = txfp->Memory;
1318   Memcol = txfp->Memcol;
1319   } // end of VCMFAM copy constructor
1320 
1321 /***********************************************************************/
1322 /*  Mapped VCT Access Method opening routine.                          */
1323 /*  New method now that this routine is called recursively (last table */
1324 /*  first in reverse order): index blocks are immediately linked to    */
1325 /*  join block of next table if it exists or else are discarted.       */
1326 /***********************************************************************/
OpenTableFile(PGLOBAL g)1327 bool VCMFAM::OpenTableFile(PGLOBAL g)
1328   {
1329   char    filename[_MAX_PATH];
1330   size_t  len;
1331   MODE    mode = Tdbp->GetMode();
1332   PFBLOCK fp = NULL;
1333   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1334 
1335   /*********************************************************************/
1336   /*  Update block info if necessary.                                  */
1337   /*********************************************************************/
1338   if (Block < 0)
1339     if ((Headlen = GetBlockInfo(g)) < 0)
1340       return true;
1341 
1342   /*********************************************************************/
1343   /*  We used the file name relative to recorded datapath.             */
1344   /*********************************************************************/
1345   PlugSetPath(filename, To_File, Tdbp->GetPath());
1346 
1347   /*********************************************************************/
1348   /*  The whole file will be mapped so we can use it as if it were     */
1349   /*  entirely read into virtual memory.                               */
1350   /*  Firstly we check whether this file have been already mapped.     */
1351   /*********************************************************************/
1352   if (mode == MODE_READ) {
1353     for (fp = dbuserp->Openlist; fp; fp = fp->Next)
1354       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
1355                      && fp->Count && fp->Mode == mode)
1356         break;
1357 
1358     if (trace(1))
1359       htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count);
1360 
1361   } else
1362     fp = NULL;
1363 
1364   if (fp) {
1365     /*******************************************************************/
1366     /*  File already mapped. Just increment use count and get pointer. */
1367     /*******************************************************************/
1368     fp->Count++;
1369     Memory = fp->Memory;
1370     len = fp->Length;
1371   } else {
1372     /*******************************************************************/
1373     /*  If required, delete the whole file if no filtering is implied. */
1374     /*******************************************************************/
1375     bool   del;
1376     HANDLE hFile;
1377     MEMMAP mm;
1378     MODE   mapmode = mode;
1379 
1380     if (mode == MODE_INSERT) {
1381       if (MaxBlk) {
1382         if (!Block)
1383           if (MakeEmptyFile(g, To_File))
1384             return true;
1385 
1386         // Inserting will be like updating the file
1387         mapmode = MODE_UPDATE;
1388       } else {
1389         strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
1390         return true;
1391       } // endif MaxBlk
1392 
1393       } // endif mode
1394 
1395     del = mode == MODE_DELETE && !Tdbp->GetNext();
1396 
1397     if (del) {
1398       DelRows = Cardinality(g);
1399 
1400       // This will stop the process by causing GetProgMax to return 0.
1401 //    ResetTableSize(g, 0, Nrec);          must be done later
1402       } // endif del
1403 
1404     /*******************************************************************/
1405     /*  Create the mapping file object.                                */
1406     /*******************************************************************/
1407     hFile = CreateFileMap(g, filename, &mm, mapmode, del);
1408 
1409     if (hFile == INVALID_HANDLE_VALUE) {
1410       DWORD rc = GetLastError();
1411 
1412       if (!(*g->Message))
1413         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1414                 "map", (int) rc, filename);
1415 
1416       if (trace(1))
1417         htrc("%s\n", g->Message);
1418 
1419       return (mode == MODE_READ && rc == ENOENT)
1420               ? PushWarning(g, Tdbp) : true;
1421       } // endif hFile
1422 
1423     /*******************************************************************/
1424     /*  Get the file size.                                             */
1425     /*******************************************************************/
1426 		len = (size_t)mm.lenL;
1427 
1428 		if (mm.lenH)
1429 			len += ((size_t)mm.lenH * 0x000000001LL);
1430 
1431 		Memory = (char *)mm.memory;
1432 
1433     if (!len) {             // Empty or deleted file
1434       CloseFileHandle(hFile);
1435       bool rc = ResetTableSize(g, 0, Nrec);
1436       return (mapmode == MODE_UPDATE) ? true : rc;
1437       } // endif len
1438 
1439     if (!Memory) {
1440       CloseFileHandle(hFile);
1441       sprintf(g->Message, MSG(MAP_VIEW_ERROR),
1442                           filename, GetLastError());
1443       return true;
1444       } // endif Memory
1445 
1446     if (mode != MODE_DELETE) {
1447       CloseFileHandle(hFile);                    // Not used anymore
1448       hFile = INVALID_HANDLE_VALUE;              // For Fblock
1449       } // endif Mode
1450 
1451     /*******************************************************************/
1452     /*  Link a Fblock. This make possible to reuse already opened maps */
1453     /*  and also to automatically unmap them in case of error g->jump. */
1454     /*  Note: block can already exist for previously closed file.      */
1455     /*******************************************************************/
1456     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1457     fp->Type = TYPE_FB_MAP;
1458     fp->Fname = PlugDup(g, filename);
1459     fp->Next = dbuserp->Openlist;
1460     dbuserp->Openlist = fp;
1461     fp->Count = 1;
1462     fp->Length = len;
1463     fp->Memory = Memory;
1464     fp->Mode = mode;
1465     fp->File = NULL;
1466     fp->Handle = hFile;                // Used for Delete
1467   } // endif fp
1468 
1469   To_Fb = fp;                               // Useful when closing
1470 
1471   if (trace(1))
1472     htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
1473           fp, fp->Count, Memory, len);
1474 
1475   return AllocateBuffer(g);
1476   } // end of OpenTableFile
1477 
1478 /***********************************************************************/
1479 /*  Allocate the block buffers for columns used in the query.          */
1480 /*  Give a dummy value (1) to prevent allocating the value block.     */
1481 /*  It will be set pointing into the memory map of the file.           */
1482 /*  Note: Memcol must be set for all columns because it can be used    */
1483 /*  for set columns in Update. Clens values are used only in Delete.   */
1484 /***********************************************************************/
AllocateBuffer(PGLOBAL g)1485 bool VCMFAM::AllocateBuffer(PGLOBAL g)
1486   {
1487   int     m, i = 0;
1488   bool    b = Tdbp->GetMode() == MODE_DELETE;
1489   PVCTCOL cp;
1490   PCOLDEF cdp;
1491   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1492 
1493   // Calculate the number of columns
1494   if (!Ncol)
1495     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
1496       Ncol++;
1497 
1498   // To store the start position of each column
1499   Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*));
1500   m = (MaxBlk) ? MaxBlk : 1;
1501 
1502   // We will need all column sizes and type for Delete
1503   if (b) {
1504     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
1505     Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
1506     } // endif b
1507 
1508   for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) {
1509     if (b) {
1510       Clens[i] = cdp->GetClen();
1511       Isnum[i] = IsTypeNum(cdp->GetType());
1512       } // endif b
1513 
1514     Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec;
1515     } // endfor cdp
1516 
1517   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1518     if (!cp->IsSpecial()) {            // Not a pseudo column
1519       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
1520                               cp->Format.Length, cp->Format.Prec,
1521 				                      true, true, cp->IsUnsigned());
1522       cp->AddStatus(BUF_MAPPED);
1523       } // endif IsSpecial
1524 
1525   if (Tdbp->GetMode() == MODE_INSERT)
1526     return InitInsert(g);
1527 
1528   return false;
1529   } // end of AllocateBuffer
1530 
1531 /***********************************************************************/
1532 /*  Do initial action when inserting.                                  */
1533 /***********************************************************************/
InitInsert(PGLOBAL g)1534 bool VCMFAM::InitInsert(PGLOBAL g)
1535 {
1536   bool     rc = false;
1537   volatile PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1538 
1539   // We come here in MODE_INSERT only
1540   if (Last == Nrec) {
1541     CurBlk = Block;
1542     CurNum = 0;
1543     AddBlock = !MaxBlk;
1544   } else {
1545     // The starting point must be at the end of file as for append.
1546     CurBlk = Block - 1;
1547     CurNum = Last;
1548   } // endif Last
1549 
1550 	try {
1551 		// Initialize the column block pointer
1552 		for (; cp; cp = (PVCTCOL)cp->Next)
1553 			cp->ReadBlock(g);
1554 
1555 	} catch (int n) {
1556 	  if (trace(1))
1557 		  htrc("Exception %d: %s\n", n, g->Message);
1558 		rc = true;
1559   } catch (const char *msg) {
1560 	  strcpy(g->Message, msg);
1561 		rc = true;
1562   } // end catch
1563 
1564   return rc;
1565 } // end of InitInsert
1566 
1567 /***********************************************************************/
1568 /*  Data Base write routine for VMP access method.                     */
1569 /***********************************************************************/
WriteBuffer(PGLOBAL g)1570 int VCMFAM::WriteBuffer(PGLOBAL g)
1571   {
1572   if (trace(1))
1573     htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
1574           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
1575 
1576   // Mode Update being done in ReadDB we process here Insert mode only.
1577   if (Tdbp->GetMode() == MODE_INSERT) {
1578     if (CurBlk == MaxBlk) {
1579       strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
1580       return RC_EF;       // Too many lines for vector formatted table
1581       } // endif MaxBlk
1582 
1583     if (Closing || ++CurNum == Nrec) {
1584       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1585 
1586       // Write back the updated last block values
1587       for (; cp; cp = (PVCTCOL)cp->Next)
1588         cp->WriteBlock(g);
1589 
1590       if (!Closing) {
1591         CurBlk++;
1592         CurNum = 0;
1593 
1594         // Re-initialize the column block pointer
1595         for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1596           cp->ReadBlock(g);
1597 
1598         } // endif Closing
1599 
1600       } // endif Closing || CurNum
1601 
1602     } // endif Mode
1603 
1604   return RC_OK;
1605   } // end of WriteBuffer
1606 
1607 /***********************************************************************/
1608 /*  Data Base delete line routine for VMP access method.               */
1609 /*  Lines between deleted lines are moved in the mapfile view.         */
1610 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)1611 int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
1612   {
1613   if (trace(1))
1614     htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
1615                         irc, To_Buf, Tpos, Spos);
1616 
1617   if (irc != RC_OK) {
1618     /*******************************************************************/
1619     /*  EOF: position Fpos at the top of map position.                 */
1620     /*******************************************************************/
1621     Fpos = (Block - 1) * Nrec + Last;
1622 
1623     if (trace(1))
1624       htrc("Fpos placed at file top=%p\n", Fpos);
1625 
1626   } else     // Fpos is the Deleted line position
1627     Fpos = CurBlk * Nrec + CurNum;
1628 
1629   if (Tpos == Spos) {
1630     /*******************************************************************/
1631     /*  First line to delete. Move of eventual preceding lines is     */
1632     /*  not required here, just setting of future Spos and Tpos.       */
1633     /*******************************************************************/
1634     Tpos = Spos = Fpos;
1635   } else
1636     (void)MoveIntermediateLines(g);
1637 
1638   if (irc == RC_OK) {
1639     Spos = Fpos + 1;                               // New start position
1640 
1641     if (trace(1))
1642       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
1643 
1644   } else {
1645     /*******************************************************************/
1646     /*  Last call after EOF has been reached.                          */
1647     /*******************************************************************/
1648     int i, m, n;
1649 
1650     /*******************************************************************/
1651     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
1652     /*******************************************************************/
1653     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
1654     Last = (Tpos + Nrec - 1) % Nrec + 1;
1655 
1656     if (!MaxBlk) {
1657       PFBLOCK fp = To_Fb;
1658 
1659       // Clean the unused part of the last block
1660       m = (Block - 1) * Blksize;
1661       n = Nrec - Last;
1662 
1663       for (i = 0; i < Ncol; i++)
1664         memset(Memcol[i] + m + Last * Clens[i],
1665             (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1666 
1667       // We must Unmap the view and use the saved file handle
1668       // to put an EOF at the end of the last block of the file.
1669       CloseMemMap(fp->Memory, (size_t)fp->Length);
1670       fp->Count = 0;                            // Avoid doing it twice
1671 
1672       // Remove extra blocks
1673       n = Block * Blksize;
1674 
1675 #if defined(_WIN32)
1676       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
1677 
1678       if (drc == 0xFFFFFFFF) {
1679         sprintf(g->Message, MSG(FUNCTION_ERROR),
1680                             "SetFilePointer", GetLastError());
1681         CloseHandle(fp->Handle);
1682         return RC_FX;
1683         } // endif
1684 
1685       if (trace(1))
1686         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
1687 
1688       if (!SetEndOfFile(fp->Handle)) {
1689         sprintf(g->Message, MSG(FUNCTION_ERROR),
1690                             "SetEndOfFile", GetLastError());
1691         CloseHandle(fp->Handle);
1692         return RC_FX;
1693         } // endif
1694 
1695       CloseHandle(fp->Handle);
1696 #else    // UNIX
1697       if (ftruncate(fp->Handle, (off_t)n)) {
1698         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1699         close(fp->Handle);
1700         return RC_FX;
1701         } // endif
1702 
1703       close(fp->Handle);
1704 #endif   // UNIX
1705     } else
1706       // True vector table, Table file size does not change.
1707       // Just clean the unused part of the file.
1708       for (n = Fpos - Tpos, i = 0; i < Ncol; i++)
1709         memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]);
1710 
1711     // Reset Last and Block values in the catalog
1712     PlugCloseFile(g, To_Fb);      // in case of Header
1713     ResetTableSize(g, Block, Last);
1714   } // endif irc
1715 
1716   return RC_OK;                                      // All is correct
1717   } // end of DeleteRecords
1718 
1719 /***********************************************************************/
1720 /*  Move intermediate deleted or updated lines.                        */
1721 /***********************************************************************/
MoveIntermediateLines(PGLOBAL,bool *)1722 bool VCMFAM::MoveIntermediateLines(PGLOBAL, bool *)
1723   {
1724   int i, m, n;
1725 
1726   if ((n = Fpos - Spos) > 0) {
1727     /*******************************************************************/
1728     /*  Non consecutive line to delete. Move intermediate lines.       */
1729     /*******************************************************************/
1730     if (!MaxBlk) {
1731       // Old VCT format, moving must respect block limits
1732       char *ps, *pt;
1733       int   req, soff, toff;
1734 
1735       for (; n > 0; n -= req) {
1736         soff = Spos % Nrec;
1737         toff = Tpos % Nrec;
1738         req = (size_t)MY_MIN(n, Nrec - MY_MAX(soff, toff));
1739 
1740         for (i = 0; i < Ncol; i++) {
1741           ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i];
1742           pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i];
1743           memmove(pt, ps, req * Clens[i]);
1744           } // endfor i
1745 
1746         Tpos += req;
1747         Spos += req;
1748         } // endfor n
1749 
1750     } else {
1751       // True vector format, all is simple...
1752       for (i = 0; i < Ncol; i++) {
1753         m = Clens[i];
1754         memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m);
1755         } // endfor i
1756 
1757       Tpos += n;
1758     } // endif MaxBlk
1759 
1760     if (trace(1))
1761       htrc("move %d bytes\n", n);
1762 
1763     } // endif n
1764 
1765   return false;
1766   } // end of MoveIntermediate Lines
1767 
1768 /***********************************************************************/
1769 /*  Data Base close routine for VMP access method.                     */
1770 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)1771 void VCMFAM::CloseTableFile(PGLOBAL g, bool)
1772   {
1773   int  wrc = RC_OK;
1774   MODE mode = Tdbp->GetMode();
1775 
1776   if (mode == MODE_INSERT) {
1777     if (!Closing) {
1778       if (CurNum) {
1779         // Some more inserted lines remain to be written
1780         Last = CurNum;
1781         Block = CurBlk + 1;
1782         Closing = true;
1783         wrc = WriteBuffer(g);
1784       } else {
1785         Last = Nrec;
1786         Block = CurBlk;
1787         wrc = RC_OK;
1788       } // endif CurNum
1789 
1790     } else
1791       wrc = RC_FX;                  // Last write was in error
1792 
1793     PlugCloseFile(g, To_Fb);
1794 
1795     if (wrc != RC_FX)
1796       /*rc =*/ ResetTableSize(g, Block, Last);
1797 
1798   } else if (mode != MODE_DELETE || Abort)
1799     PlugCloseFile(g, To_Fb);
1800 
1801   } // end of CloseTableFile
1802 
1803 /***********************************************************************/
1804 /*  ReadBlock: Read column values from current block.                  */
1805 /***********************************************************************/
ReadBlock(PGLOBAL,PVCTCOL colp)1806 bool VCMFAM::ReadBlock(PGLOBAL, PVCTCOL colp)
1807   {
1808   char *mempos;
1809   int   i = colp->Index - 1;
1810   int  n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl);
1811 
1812   /*********************************************************************/
1813   /*  Calculate the start position of the column block to read.        */
1814   /*********************************************************************/
1815   mempos = Memcol[i] + n * CurBlk;
1816 
1817   if (trace(1))
1818     htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n",
1819           mempos, i, Nrec, colp->Clen, CurBlk);
1820 
1821   if (colp->GetStatus(BUF_MAPPED))
1822     colp->Blk->SetValPointer(mempos);
1823 
1824   if (trace(1))
1825     num_read++;
1826 
1827   return false;
1828   } // end of ReadBlock
1829 
1830 /***********************************************************************/
1831 /*  WriteBlock: Write back current column values for one block.        */
1832 /*  Note: there is nothing to do because we are working directly into  */
1833 /*  the mapped file, except when checking for Update but in this case  */
1834 /*  we do not want to write back the modifications either.             */
1835 /***********************************************************************/
WriteBlock(PGLOBAL,PVCTCOL colp)1836 bool VCMFAM::WriteBlock(PGLOBAL, PVCTCOL colp __attribute__((unused)))
1837   {
1838 #if defined(_DEBUG)
1839   char *mempos;
1840   int   i = colp->Index - 1;
1841   int   n = Nrec * colp->Clen;
1842 
1843   /*********************************************************************/
1844   /*  Calculate the offset and size of the block to write.             */
1845   /*********************************************************************/
1846   mempos = Memcol[i] + n * CurBlk;
1847 
1848   if (trace(1))
1849     htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n",
1850           Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk);
1851 
1852 #endif // _DEBUG
1853 
1854   return false;
1855   } // end of WriteBlock
1856 
1857 /* -------------------------- Class VECFAM --------------------------- */
1858 
1859 /***********************************************************************/
1860 /*  Implementation of the VECFAM class.                                */
1861 /***********************************************************************/
VECFAM(PVCTDEF tdp)1862 VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1863   {
1864   Streams = NULL;
1865   To_Fbs = NULL;
1866   To_Bufs = NULL;
1867   Split = true;
1868   Block = Last = -1;
1869   InitUpdate = false;
1870   } // end of VECFAM standard constructor
1871 
VECFAM(PVECFAM txfp)1872 VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp)
1873   {
1874   Streams = txfp->Streams;
1875   To_Fbs = txfp->To_Fbs;
1876   Clens = txfp->Clens;
1877   To_Bufs = txfp->To_Bufs;
1878   InitUpdate = txfp->InitUpdate;
1879   } // end of VECFAM copy constructor
1880 
1881 /***********************************************************************/
1882 /*  VEC Access Method opening routine.                                 */
1883 /*  New method now that this routine is called recursively (last table */
1884 /*  first in reverse order): index blocks are immediately linked to    */
1885 /*  join block of next table if it exists or else are discarted.       */
1886 /***********************************************************************/
OpenTableFile(PGLOBAL g)1887 bool VECFAM::OpenTableFile(PGLOBAL g)
1888   {
1889   char    opmode[4];
1890   int     i;
1891   bool    b= false;
1892   PCOLDEF cdp;
1893   PVCTCOL cp;
1894   MODE    mode = Tdbp->GetMode();
1895   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1896 
1897   /*********************************************************************/
1898   /*  Call Cardinality to set Block and Last values in case it was not */
1899   /*  already called (this happens indeed in test xmode)               */
1900   /*********************************************************************/
1901   Cardinality(g);
1902 
1903   /*********************************************************************/
1904   /*  Open according to input/output mode required.                    */
1905   /*********************************************************************/
1906   switch (mode) {
1907     case MODE_READ:
1908       strcpy(opmode, "rb");
1909       break;
1910     case MODE_DELETE:
1911       if (!Tdbp->GetNext()) {
1912         // Store the number of deleted lines
1913         DelRows = Cardinality(g);
1914 
1915         // This will delete the whole file
1916         strcpy(opmode, "wb");
1917 
1918         // This will stop the process by causing GetProgMax to return 0.
1919         ResetTableSize(g, 0, Nrec);
1920         break;
1921         } // endif filter
1922 
1923       // Selective delete, pass thru
1924       /* fall through */
1925     case MODE_UPDATE:
1926       UseTemp = Tdbp->IsUsingTemp(g);
1927       strcpy(opmode, (UseTemp) ? "rb": "r+b");
1928       break;
1929     case MODE_INSERT:
1930       strcpy(opmode, "ab");
1931       break;
1932     default:
1933       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
1934       return true;
1935     } // endswitch Mode
1936 
1937   /*********************************************************************/
1938   /*  Initialize the array of file structures.                         */
1939   /*********************************************************************/
1940   if (!Colfn) {
1941     // Prepare the column file name pattern and set Ncol
1942     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1943     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
1944     } // endif Colfn
1945 
1946   Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
1947   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
1948 
1949   for (i = 0; i < Ncol; i++) {
1950     Streams[i] = NULL;
1951     To_Fbs[i] = NULL;
1952     } // endif i
1953 
1954   /*********************************************************************/
1955   /*  Open the files corresponding to columns used in the query.       */
1956   /*********************************************************************/
1957   if (mode == MODE_INSERT || mode == MODE_DELETE) {
1958     // All columns must be written or deleted
1959     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
1960       if (OpenColumnFile(g, opmode, i))
1961         return true;
1962 
1963     // Check for void table or missing columns
1964     for (b = !Streams[0], i = 1; i < Ncol; i++)
1965       if (b != !Streams[i])
1966         return true;
1967 
1968   } else {
1969     /*******************************************************************/
1970     /*  Open the files corresponding to updated columns of the query.  */
1971     /*******************************************************************/
1972     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
1973          cp = (PVCTCOL)cp->Next)
1974       if (OpenColumnFile(g, opmode, cp->Index - 1))
1975         return true;
1976 
1977     // Open in read only mode the used columns not already open
1978     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1979       if (!cp->IsSpecial() && !Streams[cp->Index - 1])
1980         if (OpenColumnFile(g, "rb", cp->Index - 1))
1981           return true;
1982 
1983     // Check for void table or missing columns
1984     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
1985                 cp = (PVCTCOL)cp->Next)
1986       if (!cp->IsSpecial()) {
1987         if (!i++)
1988           b = !Streams[cp->Index - 1];
1989         else if (b != !Streams[cp->Index - 1])
1990           return true;
1991 
1992         } // endif Special
1993 
1994   } // endif mode
1995 
1996   /*********************************************************************/
1997   /*  Allocate the table and column block buffer.                      */
1998   /*********************************************************************/
1999   return (b) ? false : AllocateBuffer(g);
2000   } // end of OpenTableFile
2001 
2002 /***********************************************************************/
2003 /*  Open the file corresponding to one column.                         */
2004 /***********************************************************************/
OpenColumnFile(PGLOBAL g,PCSZ opmode,int i)2005 bool VECFAM::OpenColumnFile(PGLOBAL g, PCSZ opmode, int i)
2006   {
2007   char    filename[_MAX_PATH];
2008   PDBUSER dup = PlgGetUser(g);
2009 
2010   sprintf(filename, Colfn, i+1);
2011 
2012   if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
2013     if (trace(1))
2014       htrc("%s\n", g->Message);
2015 
2016     return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
2017             ? PushWarning(g, Tdbp) : true;
2018     } // endif Streams
2019 
2020   if (trace(1))
2021     htrc("File %s is open in mode %s\n", filename, opmode);
2022 
2023   To_Fbs[i] = dup->Openlist;       // Keep track of File blocks
2024   return false;
2025   } // end of OpenColumnFile
2026 
2027 /***********************************************************************/
2028 /*  Allocate the block buffers for columns used in the query.          */
2029 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2030 bool VECFAM::AllocateBuffer(PGLOBAL g)
2031   {
2032   int     i;
2033   PVCTCOL cp;
2034   PCOLDEF cdp;
2035   PTDBVCT tdbp = (PTDBVCT)Tdbp;
2036   MODE    mode = tdbp->GetMode();
2037   PDOSDEF defp = (PDOSDEF)tdbp->GetDef();
2038 
2039   if (mode != MODE_READ) {
2040     // Allocate what is needed by all modes except Read
2041     T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
2042     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2043 
2044     // Give default values
2045     for (i = 0; i < Ncol; i++) {
2046       T_Streams[i] = Streams[i];
2047       Clens[i] = 0;
2048       } // endfor i
2049 
2050     } // endif mode
2051 
2052   if (mode == MODE_INSERT) {
2053     bool    chk = PlgGetUser(g)->Check & CHK_TYPE;
2054 
2055     To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*));
2056     cdp = defp->GetCols();
2057 
2058     for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2059       Clens[i] = cdp->GetClen();
2060       To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]);
2061 
2062       if (cdp->GetType() == TYPE_STRING)
2063         memset(To_Bufs[i], ' ', Nrec * Clens[i]);
2064       else
2065         memset(To_Bufs[i],   0, Nrec * Clens[i]);
2066 
2067       } // endfor cdp
2068 
2069     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2070       cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1],
2071                               cp->Buf_Type, Nrec, cp->Format.Length,
2072                               cp->Format.Prec, chk, true, cp->IsUnsigned());
2073 
2074     return InitInsert(g);
2075   } else {
2076     if (UseTemp || mode == MODE_DELETE) {
2077       // Allocate all that is needed to move lines and make Temp
2078       if (UseTemp) {
2079         Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2080         strcpy(Tempat, Colfn);
2081         PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
2082         strcat(PlugRemoveType(Tempat, Tempat), ".t");
2083         T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2084         } // endif UseTemp
2085 
2086       if (UseTemp)
2087         for (i = 0; i < Ncol; i++) {
2088           T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL;
2089           T_Fbs[i] = NULL;
2090           } // endfor i
2091 
2092       if (mode == MODE_DELETE) {  // All columns are moved
2093         cdp = defp->GetCols();
2094 
2095         for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2096           Clens[i] = cdp->GetClen();
2097           Buflen = MY_MAX(Buflen, cdp->GetClen());
2098           } // endfor cdp
2099 
2100       } else {  // Mode Update, only some columns are updated
2101         for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) {
2102           i = cp->Index -1;
2103 
2104           if (UseTemp)
2105             T_Streams[i] = NULL;   // Mark the streams to open
2106 
2107           Clens[i] = cp->Clen;
2108           Buflen = MY_MAX(Buflen, cp->Clen);
2109           } // endfor cp
2110 
2111         InitUpdate = true;         // To be initialized
2112       } // endif mode
2113 
2114       To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec);
2115       } // endif mode
2116 
2117     // Finally allocate column buffers for all modes
2118     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2119       if (!cp->IsSpecial())            // Not a pseudo column
2120           cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
2121                                   cp->Format.Length, cp->Format.Prec,
2122 						                      true, true, cp->IsUnsigned());
2123 
2124   } // endif mode
2125 
2126   return false;
2127   } // end of AllocateBuffer
2128 
2129 /***********************************************************************/
2130 /*  Do initial action when inserting.                                  */
2131 /***********************************************************************/
InitInsert(PGLOBAL)2132 bool VECFAM::InitInsert(PGLOBAL)
2133   {
2134   // We come here in MODE_INSERT only
2135   CurBlk = 0;
2136   CurNum = 0;
2137   AddBlock = true;
2138   return false;
2139   } // end of InitInsert
2140 
2141 /***********************************************************************/
2142 /*  Reset buffer access according to indexing and to mode.             */
2143 /*  >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
2144 /***********************************************************************/
ResetBuffer(PGLOBAL g)2145 void VECFAM::ResetBuffer(PGLOBAL g)
2146   {
2147   /*********************************************************************/
2148   /*  If access is random, performances can be much better when the    */
2149   /*  reads are done on only one row, except for small tables that can */
2150   /*  be entirely read in one block. If the index is just used as a    */
2151   /*  bitmap filter, as for Update or Delete, reading will be          */
2152   /*  sequential and we better keep block reading.                     */
2153   /*********************************************************************/
2154   if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) {
2155     Nrec = 1;                       // Better for random access
2156     Rbuf = 0;
2157     OldBlk = -2;                    // Has no meaning anymore
2158     Block = Tdbp->Cardinality(g);   // Blocks are one line now
2159     Last = 1;                       // Probably unuseful
2160     } // endif Mode
2161 
2162   } // end of ResetBuffer
2163 
2164 /***********************************************************************/
2165 /*  Data Base write routine for VCT access method.                     */
2166 /***********************************************************************/
WriteBuffer(PGLOBAL g)2167 int VECFAM::WriteBuffer(PGLOBAL g)
2168   {
2169   if (trace(1))
2170     htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
2171           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
2172 
2173   if (Tdbp->GetMode() == MODE_INSERT) {
2174     if (Closing || ++CurNum == Nrec) {
2175       // Here we must add a new blocks to the files
2176       int    i;
2177       size_t n = (size_t)CurNum;
2178 
2179       for (i = 0; i < Ncol; i++)
2180         if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) {
2181           sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
2182           return RC_FX;
2183           } // endif
2184 
2185       if (!Closing) {
2186         CurBlk++;
2187         CurNum = 0;
2188         } // endif Closing
2189 
2190       } // endif Closing || CurNum
2191 
2192   } else              // Mode Update
2193     // Writing updates being done in ReadDB we do initialization only.
2194     if (InitUpdate) {
2195       if (OpenTempFile(g))
2196         return RC_FX;
2197 
2198       InitUpdate = false;                 // Done
2199       } // endif InitUpdate
2200 
2201   return RC_OK;
2202   } // end of WriteBuffer
2203 
2204 /***********************************************************************/
2205 /*  Data Base delete line routine for split vertical access methods.   */
2206 /*  Note: lines are moved directly in the files (ooops...)             */
2207 /*  Using temp file depends on the Check setting, false by default.    */
2208 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2209 int VECFAM::DeleteRecords(PGLOBAL g, int irc)
2210   {
2211   if (trace(1))
2212     htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
2213           irc, UseTemp, Fpos, Tpos, Spos);
2214 
2215   if (irc != RC_OK) {
2216     /*******************************************************************/
2217     /*  EOF: position Fpos at the end-of-file position.                */
2218     /*******************************************************************/
2219     Fpos = Cardinality(g);
2220 
2221     if (trace(1))
2222       htrc("Fpos placed at file end=%d\n", Fpos);
2223 
2224   } else     // Fpos is the Deleted line position
2225     Fpos = CurBlk * Nrec + CurNum;
2226 
2227   if (Tpos == Spos) {
2228     // First line to delete
2229     if (UseTemp) {
2230       /*****************************************************************/
2231       /*  Open the temporary files, Spos is at the beginning of file.  */
2232       /*****************************************************************/
2233       if (OpenTempFile(g))
2234         return RC_FX;
2235 
2236     } else
2237       /*****************************************************************/
2238       /*  Move of eventual preceding lines is not required here.      */
2239       /*  Set the future Tpos, and give Spos a value to block copying. */
2240       /*****************************************************************/
2241       Spos = Tpos = Fpos;
2242 
2243     } // endif Tpos == Spos
2244 
2245   /*********************************************************************/
2246   /*  Move any intermediate lines.                                     */
2247   /*********************************************************************/
2248   if (MoveIntermediateLines(g))
2249     return RC_FX;
2250 
2251   if (irc == RC_OK) {
2252 #ifdef _DEBUG
2253     assert(Spos == Fpos);
2254 #endif
2255     Spos++;          // New start position is on next line
2256 
2257     if (trace(1))
2258       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
2259 
2260   } else {
2261     /*******************************************************************/
2262     /*  Last call after EOF has been reached.                          */
2263     /*******************************************************************/
2264     if (!UseTemp) {
2265       /*****************************************************************/
2266       /* Because the chsize functionality is only accessible with a    */
2267       /* system call we must close the files and reopen them with the  */
2268       /* open function (_fopen for MS??) this is still to be checked   */
2269       /* for compatibility with other OS's.                            */
2270       /*****************************************************************/
2271       char filename[_MAX_PATH];
2272       int  h;                            // File handle, return code
2273 
2274       for (int i = 0; i < Ncol; i++) {
2275         sprintf(filename, Colfn, i + 1);
2276         /*rc =*/ PlugCloseFile(g, To_Fbs[i]);
2277 
2278         if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
2279           return RC_FX;
2280 
2281         /***************************************************************/
2282         /*  Remove extra records.                                      */
2283         /***************************************************************/
2284 #if defined(UNIX)
2285         if (ftruncate(h, (off_t)(Tpos * Clens[i]))) {
2286           sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2287           close(h);
2288           return RC_FX;
2289           } // endif
2290 #else
2291         if (chsize(h, Tpos * Clens[i])) {
2292           sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
2293           close(h);
2294           return RC_FX;
2295           } // endif
2296 #endif
2297 
2298         close(h);
2299 
2300         if (trace(1))
2301           htrc("done, h=%d irc=%d\n", h, irc);
2302 
2303         } // endfor i
2304 
2305     } else        // UseTemp
2306       // Ok, now delete old files and rename new temp files
2307       if (RenameTempFile(g) == RC_FX)
2308         return RC_FX;
2309 
2310     // Reset these values for TDBVCT::MakeBlockValues
2311     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2312     Last = (Tpos + Nrec - 1) % Nrec + 1;
2313 
2314     if (ResetTableSize(g, Block, Last))
2315       return RC_FX;
2316 
2317   } // endif irc
2318 
2319   return RC_OK;                                      // All is correct
2320   } // end of DeleteRecords
2321 
2322 /***********************************************************************/
2323 /*  Open temporary files used while updating or deleting.              */
2324 /*  Note: the files not updated have been given a T_Stream value of 1. */
2325 /***********************************************************************/
OpenTempFile(PGLOBAL g)2326 bool VECFAM::OpenTempFile(PGLOBAL g)
2327   {
2328   char tempname[_MAX_PATH];
2329 
2330   for (int i = 0; i < Ncol; i++)
2331     if (!T_Streams[i]) {
2332       /*****************************************************************/
2333       /*  Open the temporary file, Spos is at the beginning of file.   */
2334       /*****************************************************************/
2335       sprintf(tempname, Tempat, i+1);
2336 
2337       if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
2338         if (trace(1))
2339           htrc("%s\n", g->Message);
2340 
2341         return true;
2342       } else
2343         T_Fbs[i] = PlgGetUser(g)->Openlist;
2344 
2345     } else       // This is a column that is not updated
2346       T_Streams[i] = NULL;        // For RenameTempFile
2347 
2348   return false;
2349   } // end of OpenTempFile
2350 
2351 /***********************************************************************/
2352 /*  Move intermediate updated lines before writing blocks.             */
2353 /***********************************************************************/
MoveLines(PGLOBAL g)2354 bool VECFAM::MoveLines(PGLOBAL g)
2355   {
2356   if (UseTemp && !InitUpdate) {    // Don't do it in check pass
2357     Fpos = OldBlk * Nrec;
2358 
2359     if (MoveIntermediateLines(g)) {
2360       Closing = true;           // ???
2361       return true;
2362       } // endif UseTemp
2363 
2364 //  Spos = Fpos + Nrec;
2365     } // endif UseTemp
2366   return false;
2367 
2368   } // end of MoveLines
2369 
2370 /***********************************************************************/
2371 /*  Move intermediate deleted or updated lines.                        */
2372 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool *)2373 bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *)
2374   {
2375   int    i, n;
2376   bool   b = false;
2377   size_t req, len;
2378 
2379   for (n = Fpos - Spos; n > 0; n -= Nrec) {
2380     /*******************************************************************/
2381     /*  Non consecutive line to delete. Move intermediate lines.       */
2382     /*******************************************************************/
2383     req = (size_t)MY_MIN(n, Nrec);
2384 
2385     for (i = 0; i < Ncol; i++) {
2386       if (!T_Streams[i])
2387         continue;             // Non updated column
2388 
2389       if (!UseTemp || !b)
2390         if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) {
2391           sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
2392           return true;
2393           } // endif
2394 
2395       len = fread(To_Buf, Clens[i], req, Streams[i]);
2396 
2397       if (trace(1))
2398         htrc("after read req=%d len=%d\n", req, len);
2399 
2400       if (len != req) {
2401         sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
2402         return true;
2403         } // endif len
2404 
2405       if (!UseTemp)
2406         if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) {
2407           sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
2408           return true;
2409           } // endif
2410 
2411       if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
2412         sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
2413         return true;
2414         } // endif
2415 
2416       if (trace(1))
2417         htrc("after write pos=%d\n", ftell(Streams[i]));
2418 
2419       } // endfor i
2420 
2421     Tpos += (int)req;
2422     Spos += (int)req;
2423 
2424     if (trace(1))
2425       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
2426 
2427     b = true;
2428     } // endfor n
2429 
2430   return false;
2431   } // end of MoveIntermediate Lines
2432 
2433 /***********************************************************************/
2434 /*  Delete the old files and rename the new temporary files.           */
2435 /***********************************************************************/
RenameTempFile(PGLOBAL g)2436 int VECFAM::RenameTempFile(PGLOBAL g)
2437   {
2438   char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
2439   int   rc = RC_OK;
2440 
2441   // Close all files.
2442   // This loop is necessary because, in case of join,
2443   // the table files can have been open several times.
2444   for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
2445     rc = PlugCloseFile(g, fb);
2446 
2447   for (int i = 0; i < Ncol && rc == RC_OK; i++) {
2448     if (!T_Fbs[i])
2449       continue;
2450 
2451     tempname = (char*)T_Fbs[i]->Fname;
2452 
2453     if (!Abort) {
2454       sprintf(filename, Colfn, i+1);
2455       PlugSetPath(filename, filename, Tdbp->GetPath());
2456       strcat(PlugRemoveType(filetemp, filename), ".ttt");
2457       remove(filetemp);   // May still be there from previous error
2458 
2459       if (rename(filename, filetemp)) {    // Save file for security
2460         snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2461                 filename, filetemp, strerror(errno));
2462         rc = RC_FX;
2463       } else if (rename(tempname, filename)) {
2464         snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2465                 tempname, filename, strerror(errno));
2466         rc = rename(filetemp, filename);   // Restore saved file
2467         rc = RC_FX;
2468       } else if (remove(filetemp)) {
2469         sprintf(g->Message, MSG(REMOVE_ERROR),
2470                 filetemp, strerror(errno));
2471         rc = RC_INFO;                      // Acceptable
2472       } // endif's
2473 
2474     } else
2475       remove(tempname);
2476 
2477     } // endfor i
2478 
2479   return rc;
2480   } // end of RenameTempFile
2481 
2482 /***********************************************************************/
2483 /*  Data Base close routine for VEC access method.                     */
2484 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)2485 void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
2486   {
2487   int  rc = 0, wrc = RC_OK;
2488   MODE mode = Tdbp->GetMode();
2489 
2490   Abort = abort;
2491 
2492   if (mode == MODE_INSERT) {
2493     if (Closing)
2494       wrc = RC_FX;                  // Last write was in error
2495     else
2496       if (CurNum) {
2497         // Some more inserted lines remain to be written
2498         Last += (CurBlk * Nrec + CurNum -1);
2499         Block += (Last / Nrec);
2500         Last = Last % Nrec + 1;
2501         Closing = true;
2502         wrc = WriteBuffer(g);
2503       } else {
2504         Block += CurBlk;
2505         wrc = RC_OK;
2506       } // endif CurNum
2507 
2508     if (wrc != RC_FX)
2509       rc = ResetTableSize(g, Block, Last);
2510     else
2511 			throw 44;
2512 
2513   } else if (mode == MODE_UPDATE) {
2514     if (UseTemp && !InitUpdate && !Abort) {
2515       // Write any intermediate lines to temp file
2516       Fpos = OldBlk * Nrec;
2517       Abort = MoveIntermediateLines(g) != RC_OK;
2518 //    Spos = Fpos + Nrec;
2519       } // endif UseTemp
2520 
2521     // Write back to file any pending modifications
2522     if (wrc == RC_OK)
2523       for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
2524                    colp; colp = (PVCTCOL)colp->Next)
2525         colp->WriteBlock(g);
2526 
2527     if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
2528       // Write any intermediate lines to temp file
2529       Fpos = (Block - 1) * Nrec + Last;
2530       Abort = MoveIntermediateLines(g) != RC_OK;
2531       } // endif UseTemp
2532 
2533   } // endif's mode
2534 
2535   if (UseTemp && !InitUpdate) {
2536     // If they are errors, leave files unchanged
2537     rc = RenameTempFile(g);
2538 
2539   } else if (Streams)
2540     for (int i = 0; i < Ncol; i++)
2541       if (Streams[i]) {
2542         rc = PlugCloseFile(g, To_Fbs[i]);
2543         Streams[i] = NULL;
2544         To_Fbs[i] = NULL;
2545         } // endif Streams
2546 
2547   if (trace(1))
2548     htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc);
2549 
2550   } // end of CloseTableFile
2551 
2552 /***********************************************************************/
2553 /*  ReadBlock: Read column values from current block.                  */
2554 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)2555 bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
2556   {
2557   int     i, len;
2558   size_t  n;
2559 
2560   /*********************************************************************/
2561   /*  Calculate the offset and size of the block to read.              */
2562   /*********************************************************************/
2563   len = Nrec * colp->Clen * CurBlk;
2564   i = colp->Index - 1;
2565 
2566   if (trace(1))
2567     htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n",
2568           len, i, Nrec, colp->Deplac, Lrecl, CurBlk);
2569 
2570   if (fseek(Streams[i], len, SEEK_SET)) {
2571     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2572     return true;
2573     } // endif
2574 
2575   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
2576                                         (size_t)Nrec, Streams[i]);
2577 
2578   if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) {
2579     char fn[_MAX_PATH];
2580 
2581     sprintf(fn, Colfn, colp->Index);
2582 #if defined(_WIN32)
2583     if (feof(Streams[i]))
2584 #else   // !_WIN32
2585     if (errno == NO_ERROR)
2586 #endif  // !_WIN32
2587       sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
2588     else
2589       sprintf(g->Message, MSG(READ_ERROR),
2590               fn, strerror(errno));
2591 
2592     if (trace(1))
2593       htrc(" Read error: %s\n", g->Message);
2594 
2595     return true;
2596     } // endif
2597 
2598   if (trace(1))
2599     num_read++;
2600 
2601   return false;
2602   } // end of ReadBlock
2603 
2604 /***********************************************************************/
2605 /*  WriteBlock: Write back current column values for one block.        */
2606 /*  Note: the test of Status is meant to prevent physical writing of   */
2607 /*  the block during the checking loop in mode Update. It is set to    */
2608 /*  BUF_EMPTY when reopening the table between the two loops.          */
2609 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)2610 bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
2611   {
2612   int   i, len;
2613   size_t n;
2614 
2615   /*********************************************************************/
2616   /*  Calculate the offset and size of the block to write.             */
2617   /*********************************************************************/
2618   len = Nrec * colp->Clen * colp->ColBlk;
2619   i = colp->Index - 1;
2620 
2621   if (trace(1))
2622     htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
2623           Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
2624 
2625   if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp)
2626     if (fseek(T_Streams[i], len, SEEK_SET)) {
2627       sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2628       return true;
2629       } // endif
2630 
2631   // Here Nrec was changed to CurNum in mode Insert,
2632   // this is the true number of records to write,
2633   // this also avoid writing garbage in the file for true vector tables.
2634   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum
2635     : (colp->ColBlk == Block - 1) ? Last : Nrec;
2636 
2637   if (n != fwrite(colp->Blk->GetValPointer(),
2638                             (size_t)colp->Clen, n, T_Streams[i])) {
2639     char fn[_MAX_PATH];
2640 
2641     sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
2642     sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
2643 
2644     if (trace(1))
2645       htrc("Write error: %s\n", strerror(errno));
2646 
2647     return true;
2648   } else
2649     Spos = Fpos + n;
2650 
2651 #if defined(UNIX)
2652   fflush(Streams[i]); //NGC
2653 #endif
2654   return false;
2655   } // end of WriteBlock
2656 
2657 /* -------------------------- Class VMPFAM --------------------------- */
2658 
2659 /***********************************************************************/
2660 /*  Implementation of the VMPFAM class.                                */
2661 /***********************************************************************/
VMPFAM(PVCTDEF tdp)2662 VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp)
2663   {
2664   To_Fbs = NULL;
2665   Split = true;
2666   Block = Last = -1;
2667   } // end of VMPFAM standard constructor
2668 
VMPFAM(PVMPFAM txfp)2669 VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
2670   {
2671   To_Fbs = txfp->To_Fbs;
2672   } // end of VMPFAM copy constructor
2673 
2674 /***********************************************************************/
2675 /*  VCT Access Method opening routine.                                 */
2676 /*  New method now that this routine is called recursively (last table */
2677 /*  first in reverse order): index blocks are immediately linked to    */
2678 /*  join block of next table if it exists or else are discarted.       */
2679 /***********************************************************************/
OpenTableFile(PGLOBAL g)2680 bool VMPFAM::OpenTableFile(PGLOBAL g)
2681   {
2682   int     i;
2683   bool    b = false;
2684   MODE    mode = Tdbp->GetMode();
2685   PCOLDEF cdp;
2686   PVCTCOL cp;
2687   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
2688 
2689   if (mode == MODE_DELETE && !Tdbp->GetNext()) {
2690     DelRows = Cardinality(g);
2691 
2692     // This will stop the process by causing GetProgMax to return 0.
2693     ResetTableSize(g, 0, Nrec);
2694   } else
2695     Cardinality(g);        // See comment in VECFAM::OpenTbleFile
2696 
2697 
2698   /*********************************************************************/
2699   /*  Prepare the filename pattern for column files and set Ncol.      */
2700   /*********************************************************************/
2701   if (!Colfn) {
2702     // Prepare the column file name pattern
2703     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2704     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
2705     } // endif Colfn
2706 
2707   /*********************************************************************/
2708   /*  Initialize the array of file structures.                         */
2709   /*********************************************************************/
2710   Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *));
2711   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2712 
2713   for (i = 0; i < Ncol; i++) {
2714     Memcol[i] = NULL;
2715     To_Fbs[i] = NULL;
2716     } // endif i
2717 
2718   /*********************************************************************/
2719   /*  Open the files corresponding to columns used in the query.       */
2720   /*********************************************************************/
2721   if (mode == MODE_DELETE) {
2722     // All columns are used in Delete mode
2723     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
2724       if (MapColumnFile(g, mode, i))
2725         return true;
2726 
2727   } else {
2728     /*******************************************************************/
2729     /*  Open the files corresponding to updated columns of the query.  */
2730     /*******************************************************************/
2731     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
2732          cp = (PVCTCOL)cp->Next)
2733       if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1))
2734         return true;
2735 
2736     /*******************************************************************/
2737     /* Open other non already open used columns (except pseudos)      */
2738     /*******************************************************************/
2739     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2740       if (!cp->IsSpecial() && !Memcol[cp->Index - 1])
2741         if (MapColumnFile(g, MODE_READ, cp->Index - 1))
2742           return true;
2743 
2744     // Check for void table or missing columns
2745     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
2746                 cp = (PVCTCOL)cp->Next)
2747       if (!cp->IsSpecial()) {
2748         if (!i++)
2749           b = !Memcol[cp->Index - 1];
2750         else if (b != !Memcol[cp->Index - 1])
2751           return true;
2752 
2753         } // endif Special
2754 
2755   } // endif mode
2756 
2757   /*********************************************************************/
2758   /*  Allocate the table and column block buffer.                      */
2759   /*********************************************************************/
2760   return (b) ? false : AllocateBuffer(g);
2761   } // end of OpenTableFile
2762 
2763 /***********************************************************************/
2764 /*  Open the file corresponding to one column.                         */
2765 /***********************************************************************/
MapColumnFile(PGLOBAL g,MODE mode,int i)2766 bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
2767   {
2768   char    filename[_MAX_PATH];
2769   size_t  len;
2770   HANDLE  hFile;
2771   MEMMAP  mm;
2772   PFBLOCK fp;
2773   PDBUSER dup = PlgGetUser(g);
2774 
2775   sprintf(filename, Colfn, i+1);
2776 
2777   /*********************************************************************/
2778   /*  The whole file will be mapped so we can use it as                */
2779   /*  if it were entirely read into virtual memory.                    */
2780   /*  Firstly we check whether this file have been already mapped.     */
2781   /*********************************************************************/
2782   if (mode == MODE_READ) {
2783     for (fp = dup->Openlist; fp; fp = fp->Next)
2784       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
2785                      && fp->Count && fp->Mode == mode)
2786         break;
2787 
2788     if (trace(1))
2789       htrc("Mapping file, fp=%p\n", fp);
2790 
2791   } else
2792     fp = NULL;
2793 
2794   if (fp) {
2795     /*******************************************************************/
2796     /*  File already mapped. Just increment use count and get pointer. */
2797     /*******************************************************************/
2798     fp->Count++;
2799     Memcol[i] = fp->Memory;
2800     len = fp->Length;
2801   } else {
2802     /*******************************************************************/
2803     /*  Create the mapping file object.                                */
2804     /*******************************************************************/
2805     hFile = CreateFileMap(g, filename, &mm, mode, DelRows);
2806 
2807     if (hFile == INVALID_HANDLE_VALUE) {
2808       DWORD rc = GetLastError();
2809 
2810       if (!(*g->Message))
2811         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
2812                 "map", (int) rc, filename);
2813       if (trace(1))
2814         htrc("%s\n", g->Message);
2815 
2816       return (mode == MODE_READ && rc == ENOENT)
2817               ? PushWarning(g, Tdbp) : true;
2818       } // endif hFile
2819 
2820     /*****************************************************************/
2821     /*  Get the file size (assuming file is smaller than 4 GB)       */
2822     /*****************************************************************/
2823 		len = (size_t)mm.lenL;
2824 
2825 		if (mm.lenH)
2826 			len += ((size_t)mm.lenH * 0x000000001LL);
2827 
2828 		Memcol[i] = (char *)mm.memory;
2829 
2830     if (!len) {             // Empty or deleted file
2831       CloseFileHandle(hFile);
2832       ResetTableSize(g, 0, Nrec);
2833       return false;
2834       } // endif len
2835 
2836     if (!Memcol[i]) {
2837       CloseFileHandle(hFile);
2838       sprintf(g->Message, MSG(MAP_VIEW_ERROR),
2839                           filename, GetLastError());
2840       return true;
2841       } // endif Memory
2842 
2843     if (mode != MODE_DELETE) {
2844       CloseFileHandle(hFile);                    // Not used anymore
2845       hFile = INVALID_HANDLE_VALUE;              // For Fblock
2846       } // endif Mode
2847 
2848     /*******************************************************************/
2849     /*  Link a Fblock. This make possible to reuse already opened maps */
2850     /*  and also to automatically unmap them in case of error g->jump. */
2851     /*  Note: block can already exist for previously closed file.      */
2852     /*******************************************************************/
2853     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
2854     fp->Type = TYPE_FB_MAP;
2855     fp->Fname = PlugDup(g, filename);
2856     fp->Next = dup->Openlist;
2857     dup->Openlist = fp;
2858     fp->Count = 1;
2859     fp->Length = len;
2860     fp->Memory = Memcol[i];
2861     fp->Mode = mode;
2862     fp->File = NULL;
2863     fp->Handle = hFile;                        // Used for Delete
2864   } // endif fp
2865 
2866   To_Fbs[i] = fp;                              // Useful when closing
2867 
2868   if (trace(1))
2869     htrc("fp=%p count=%d MapView=%p len=%d\n",
2870           fp, fp->Count, Memcol[i], len);
2871 
2872   return false;
2873   } // end of MapColumnFile
2874 
2875 /***********************************************************************/
2876 /*  Allocate the block buffers for columns used in the query.          */
2877 /*  Give a dummy value (1) to prevent allocating the value block.     */
2878 /*  It will be set pointing into the memory map of the file.           */
2879 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2880 bool VMPFAM::AllocateBuffer(PGLOBAL g)
2881   {
2882   PVCTCOL cp;
2883 
2884   if (Tdbp->GetMode() == MODE_DELETE) {
2885     PCOLDEF cdp = Tdbp->GetDef()->GetCols();
2886 
2887     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2888 
2889     for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext())
2890       Clens[i] = cdp->GetClen();
2891 
2892     } // endif mode
2893 
2894   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2895     if (!cp->IsSpecial()) {            // Not a pseudo column
2896       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
2897                               cp->Format.Length, cp->Format.Prec,
2898 				                      true, true, cp->IsUnsigned());
2899       cp->AddStatus(BUF_MAPPED);
2900       } // endif IsSpecial
2901 
2902   return false;
2903   } // end of AllocateBuffer
2904 
2905 /***********************************************************************/
2906 /*  Data Base delete line routine for VMP access method.               */
2907 /*  Lines between deleted lines are moved in the mapfile view.         */
2908 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2909 int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
2910   {
2911   int  i;
2912   int m, n;
2913 
2914   if (trace(1))
2915     htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
2916                         irc, To_Buf, Tpos, Spos);
2917 
2918   if (irc != RC_OK) {
2919     /*******************************************************************/
2920     /*  EOF: position Fpos at the top of map position.                 */
2921     /*******************************************************************/
2922     Fpos = (Block - 1) * Nrec + Last;
2923 
2924     if (trace(1))
2925       htrc("Fpos placed at file top=%p\n", Fpos);
2926 
2927   } else     // Fpos is the Deleted line position
2928     Fpos = CurBlk * Nrec + CurNum;
2929 
2930   if (Tpos == Spos) {
2931     /*******************************************************************/
2932     /*  First line to delete. Move of eventual preceding lines is     */
2933     /*  not required here, just setting of future Spos and Tpos.       */
2934     /*******************************************************************/
2935     Tpos = Fpos;                               // Spos is set below
2936   } else if ((n = Fpos - Spos) > 0) {
2937     /*******************************************************************/
2938     /*  Non consecutive line to delete. Move intermediate lines.       */
2939     /*******************************************************************/
2940     for (i = 0; i < Ncol; i++) {
2941       m = Clens[i];
2942       memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n);
2943       } // endif i
2944 
2945     Tpos += n;
2946 
2947     if (trace(1))
2948       htrc("move %d bytes\n", n);
2949 
2950     } // endif n
2951 
2952   if (irc == RC_OK) {
2953     Spos = Fpos + 1;                               // New start position
2954 
2955     if (trace(1))
2956       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
2957 
2958   } else {
2959     /*******************************************************************/
2960     /*  Last call after EOF has been reached.                          */
2961     /*  We must firstly Unmap the view and use the saved file handle   */
2962     /*  to put an EOF at the end of the copied part of the file.       */
2963     /*******************************************************************/
2964     PFBLOCK fp;
2965 
2966     /*******************************************************************/
2967     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
2968     /*******************************************************************/
2969 //  Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2970 //  Last = (Tpos + Nrec - 1) % Nrec + 1;
2971 
2972     for (i = 0; i < Ncol; i++) {
2973       fp = To_Fbs[i];
2974       CloseMemMap(fp->Memory, (size_t)fp->Length);
2975       fp->Count = 0;                             // Avoid doing it twice
2976 
2977       /*****************************************************************/
2978       /*  Remove extra records.                                        */
2979       /*****************************************************************/
2980       n = Tpos * Clens[i];
2981 
2982 #if defined(_WIN32)
2983       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
2984 
2985       if (drc == 0xFFFFFFFF) {
2986         sprintf(g->Message, MSG(FUNCTION_ERROR),
2987                             "SetFilePointer", GetLastError());
2988         CloseHandle(fp->Handle);
2989         return RC_FX;
2990         } // endif
2991 
2992       if (trace(1))
2993         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
2994 
2995       if (!SetEndOfFile(fp->Handle)) {
2996         sprintf(g->Message, MSG(FUNCTION_ERROR),
2997                             "SetEndOfFile", GetLastError());
2998         CloseHandle(fp->Handle);
2999         return RC_FX;
3000         } // endif
3001 
3002       CloseHandle(fp->Handle);
3003 #else    // UNIX
3004       if (ftruncate(fp->Handle, (off_t)n)) {
3005         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3006         close(fp->Handle);
3007         return RC_FX;
3008         } // endif
3009 
3010       close(fp->Handle);
3011 #endif   // UNIX
3012       } // endfor i
3013 
3014   } // endif irc
3015 
3016   return RC_OK;                                      // All is correct
3017   } // end of DeleteRecords
3018 
3019 /***********************************************************************/
3020 /*  Data Base close routine for VMP access method.                     */
3021 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)3022 void VMPFAM::CloseTableFile(PGLOBAL g, bool)
3023   {
3024   if (Tdbp->GetMode() == MODE_DELETE) {
3025     // Set Block and Nrec values for TDBVCT::MakeBlockValues
3026     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3027     Last = (Tpos + Nrec - 1) % Nrec + 1;
3028     ResetTableSize(g, Block, Last);
3029   } else if (Tdbp->GetMode() == MODE_INSERT)
3030     assert(false);
3031 
3032   for (int i = 0; i < Ncol; i++)
3033     PlugCloseFile(g, To_Fbs[i]);
3034 
3035   } // end of CloseTableFile
3036 
3037 /* -------------------------- Class BGVFAM --------------------------- */
3038 
3039 /***********************************************************************/
3040 /*  Implementation of the BGVFAM class.                                */
3041 /***********************************************************************/
3042 // Constructors
BGVFAM(PVCTDEF tdp)3043 BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp)
3044   {
3045   Hfile = INVALID_HANDLE_VALUE;
3046   Tfile = INVALID_HANDLE_VALUE;
3047   BigDep = NULL;
3048   } // end of BGVFAM constructor
3049 
BGVFAM(PBGVFAM txfp)3050 BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp)
3051   {
3052   Hfile = txfp->Hfile;
3053   Tfile = txfp->Tfile;
3054   BigDep= txfp->BigDep;
3055   } // end of BGVFAM copy constructor
3056 
3057 /***********************************************************************/
3058 /*  Set current position in a big file.                                */
3059 /***********************************************************************/
BigSeek(PGLOBAL g,HANDLE h,BIGINT pos,bool b)3060 bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b)
3061   {
3062 #if defined(_WIN32)
3063   char          buf[256];
3064   DWORD         drc, m = (b) ? FILE_END : FILE_BEGIN;
3065   LARGE_INTEGER of;
3066 
3067   of.QuadPart = pos;
3068   of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m);
3069 
3070   if (of.LowPart == INVALID_SET_FILE_POINTER &&
3071            (drc = GetLastError()) != NO_ERROR) {
3072     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3073                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3074                   (LPTSTR)buf, sizeof(buf), NULL);
3075     sprintf(g->Message, MSG(SFP_ERROR), buf);
3076     return true;
3077     } // endif
3078 #else   // !_WIN32
3079   if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
3080     sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
3081     return true;
3082     } // endif
3083 #endif  // !_WIN32
3084 
3085   return false;
3086   } // end of BigSeek
3087 
3088 /***********************************************************************/
3089 /*  Read from a big file.                                              */
3090 /***********************************************************************/
BigRead(PGLOBAL g,HANDLE h,void * inbuf,int req)3091 bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
3092   {
3093   bool rc = false;
3094 
3095 #if defined(_WIN32)
3096   DWORD nbr, drc, len = (DWORD)req;
3097   bool  brc = ReadFile(h, inbuf, len, &nbr, NULL);
3098 
3099   if (trace(1))
3100     htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
3101 
3102   if (!brc || nbr != len) {
3103     char buf[256];  // , *fn = (h == Hfile) ? To_File : "Tempfile";
3104 
3105     if (brc)
3106       strcpy(buf, MSG(BAD_BYTE_READ));
3107     else {
3108       drc = GetLastError();
3109       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3110                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3111                     (LPTSTR)buf, sizeof(buf), NULL);
3112       } // endelse brc
3113 
3114     sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
3115 
3116     if (trace(1))
3117       htrc("BIGREAD: %s\n", g->Message);
3118 
3119     rc = true;
3120     } // endif brc || nbr
3121 #else   // !_WIN32
3122   size_t  len = (size_t)req;
3123   ssize_t nbr = read(h, inbuf, len);
3124 
3125   if (nbr != (ssize_t)len) {
3126     const char *fn = (h == Hfile) ? To_File : "Tempfile";
3127 
3128     sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
3129 
3130     if (trace(1))
3131       htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n",
3132                      nbr, len, errno, g->Message);
3133 
3134     rc = true;
3135     } // endif nbr
3136 #endif  // !_WIN32
3137 
3138   return rc;
3139   } // end of BigRead
3140 
3141 /***********************************************************************/
3142 /*  Write into a big file.                                             */
3143 /***********************************************************************/
BigWrite(PGLOBAL g,HANDLE h,void * inbuf,int req)3144 bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
3145   {
3146   bool rc = false;
3147 
3148 #if defined(_WIN32)
3149   DWORD nbw, drc, len = (DWORD)req;
3150   bool  brc = WriteFile(h, inbuf, len, &nbw, NULL);
3151 
3152   if (trace(1))
3153     htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
3154 
3155   if (!brc || nbw != len) {
3156 		char buf[256];
3157 		PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
3158 
3159     if (brc)
3160       strcpy(buf, MSG(BAD_BYTE_NUM));
3161     else {
3162       drc = GetLastError();
3163       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3164                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3165                     (LPTSTR)buf, sizeof(buf), NULL);
3166       } // endelse brc
3167 
3168     sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
3169 
3170     if (trace(1))
3171       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3172                       nbw, len, drc, g->Message);
3173 
3174     rc = true;
3175     } // endif brc || nbw
3176 #else   // !_WIN32
3177   size_t  len = (size_t)req;
3178   ssize_t nbw = write(h, inbuf, len);
3179 
3180   if (nbw != (ssize_t)len) {
3181     const char *fn = (h == Hfile) ? To_File : "Tempfile";
3182 
3183     sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
3184 
3185     if (trace(1))
3186       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3187                       nbw, len, errno, g->Message);
3188 
3189     rc = true;
3190     } // endif nbr
3191 #endif  // !_WIN32
3192 
3193   return rc;
3194   } // end of BigWrite
3195 
3196 /***********************************************************************/
3197 /*  Get the Headlen, Block and Last info from the file header.         */
3198 /***********************************************************************/
GetBlockInfo(PGLOBAL g)3199 int BGVFAM::GetBlockInfo(PGLOBAL g)
3200   {
3201   char      filename[_MAX_PATH];
3202   int       n;
3203   VECHEADER vh;
3204   HANDLE    h;
3205 
3206   if (Header < 1 || Header > 3 || !MaxBlk) {
3207     sprintf(g->Message, "Invalid header value %d", Header);
3208     return -1;
3209   } else
3210     n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
3211 
3212   PlugSetPath(filename, To_File, Tdbp->GetPath());
3213 
3214   if (Header == 2)
3215     strcat(PlugRemoveType(filename, filename), ".blk");
3216 
3217 #if defined(_WIN32)
3218   LARGE_INTEGER len;
3219 
3220   h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
3221                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3222 
3223   if (h != INVALID_HANDLE_VALUE) {
3224     // Get the size of the file (can be greater than 4 GB)
3225     len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
3226     } // endif h
3227 
3228   if (h == INVALID_HANDLE_VALUE || !len.QuadPart) {
3229 #else   // !_WIN32
3230   h = open64(filename, O_RDONLY, 0);
3231 
3232   if (h == INVALID_HANDLE_VALUE || !_filelength(h)) {
3233 #endif  // !_WIN32
3234     // Consider this is a void table
3235     if (trace(1))
3236       htrc("Void table h=%d\n", h);
3237 
3238     Last = Nrec;
3239     Block = 0;
3240 
3241     if (h != INVALID_HANDLE_VALUE)
3242       CloseFileHandle(h);
3243 
3244     return n;
3245   } else if (Header == 3)
3246     /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3247 
3248   if (BigRead(g, h, &vh, sizeof(vh))) {
3249     sprintf(g->Message, "Error reading header file %s", filename);
3250     n = -1;
3251   } else if (MaxBlk * Nrec != vh.MaxRec) {
3252     sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
3253                         vh.MaxRec, MaxBlk, Nrec);
3254     n = -1;
3255   } else {
3256     Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
3257     Last  = (vh.NumRec + Nrec - 1) % Nrec + 1;
3258 
3259     if (trace(1))
3260       htrc("Block=%d Last=%d\n", Block, Last);
3261 
3262   } // endif's
3263 
3264   CloseFileHandle(h);
3265   return n;
3266   } // end of GetBlockInfo
3267 
3268 /***********************************************************************/
3269 /*  Set the MaxRec and NumRec info in the file header.                 */
3270 /***********************************************************************/
3271 bool BGVFAM::SetBlockInfo(PGLOBAL g)
3272   {
3273   char      filename[_MAX_PATH];
3274   bool      b = false, rc = false;
3275   VECHEADER vh;
3276   HANDLE    h = INVALID_HANDLE_VALUE;
3277 
3278   PlugSetPath(filename, To_File, Tdbp->GetPath());
3279 
3280   if (Header != 2) {
3281     if (Hfile != INVALID_HANDLE_VALUE) {
3282       h = Hfile;
3283 
3284       if (Header == 1)
3285         /*bk =*/ BigSeek(g, h, (BIGINT)0);
3286 
3287     } else
3288       b = true;
3289 
3290   } else       // Header == 2
3291     strcat(PlugRemoveType(filename, filename), ".blk");
3292 
3293   if (h == INVALID_HANDLE_VALUE) {
3294 #if defined(_WIN32)
3295     DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING;
3296 
3297     h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0,
3298                    NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
3299 
3300 #else   // !_WIN32
3301     int oflag = (b) ?  O_RDWR : O_RDWR | O_TRUNC;
3302 
3303     h = open64(filename, oflag, 0);
3304 #endif  // !_WIN32
3305 
3306     if (h == INVALID_HANDLE_VALUE) {
3307       sprintf(g->Message, "Error opening header file %s", filename);
3308       return true;
3309       } // endif h
3310 
3311     } // endif h
3312 
3313   if (Header == 3)
3314     /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3315 
3316   vh.MaxRec = MaxBlk * Bsize;
3317   vh.NumRec = (Block - 1) * Nrec + Last;
3318 
3319   if (BigWrite(g, h, &vh, sizeof(vh))) {
3320     sprintf(g->Message, "Error writing header file %s", filename);
3321     rc = true;
3322     } // endif fread
3323 
3324   if (Header == 2 || Hfile == INVALID_HANDLE_VALUE)
3325     CloseFileHandle(h);
3326 
3327   return rc;
3328   } // end of SetBlockInfo
3329 
3330 /***********************************************************************/
3331 /*  VEC Create an empty file for new Vector formatted tables.          */
3332 /***********************************************************************/
3333 bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
3334   {
3335   // Vector formatted file this will create an empty file of the
3336   // required length if it does not exists yet.
3337   char          filename[_MAX_PATH], c = 0;
3338   int           n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
3339 
3340   PlugSetPath(filename, fn, Tdbp->GetPath());
3341 
3342 #if defined(_WIN32)
3343   PCSZ          p;
3344   DWORD         rc;
3345   bool          brc;
3346   LARGE_INTEGER of;
3347   HANDLE        h;
3348 
3349   h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3350                            FILE_ATTRIBUTE_NORMAL, NULL);
3351 
3352   if (h == INVALID_HANDLE_VALUE) {
3353     p = MSG(OPENING);
3354     goto err;
3355     } // endif h
3356 
3357   of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3358 
3359   if (trace(1))
3360     htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n",
3361                of.QuadPart, n, MaxBlk, Blksize);
3362 
3363   of.LowPart = SetFilePointer(h, of.LowPart,
3364                                 &of.HighPart, FILE_BEGIN);
3365 
3366   if (of.LowPart == INVALID_SET_FILE_POINTER &&
3367            GetLastError() != NO_ERROR) {
3368     p = MSG(MAKING);
3369     goto err;
3370     } // endif
3371 
3372   brc = WriteFile(h, &c, 1, &rc, NULL);
3373 
3374   if (!brc || rc != 1) {
3375     p = MSG(WRITING);
3376     goto err;
3377     } // endif
3378 
3379   CloseHandle(h);
3380   return false;
3381 
3382  err:
3383   rc = GetLastError();
3384   sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
3385   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3386                 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3387                 (LPTSTR)filename, sizeof(filename), NULL);
3388   strcat(g->Message, filename);
3389 
3390   if (h != INVALID_HANDLE_VALUE)
3391     CloseHandle(h);
3392 
3393   return true;
3394 #else   // !_WIN32
3395   int    h;
3396   BIGINT pos;
3397 
3398   h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
3399 
3400   if (h == -1)
3401     return true;
3402 
3403   pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3404 
3405   if (trace(1))
3406     htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
3407                pos, n, MaxBlk, Blksize);
3408 
3409   if (lseek64(h, pos, SEEK_SET) < 0)
3410     goto err;
3411 
3412   // This actually fills the empty file
3413   if (write(h, &c, 1) < 0)
3414     goto err;
3415 
3416   close(h);
3417   return false;
3418 
3419  err:
3420   sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
3421   close(h);
3422   return true;
3423 #endif  // !_WIN32
3424   } // end of MakeEmptyFile
3425 
3426 /***********************************************************************/
3427 /*  Vopen function: opens a file using Windows or Unix API's.          */
3428 /***********************************************************************/
3429 bool BGVFAM::OpenTableFile(PGLOBAL g)
3430   {
3431   char    filename[_MAX_PATH];
3432   bool    del = false;
3433   MODE    mode = Tdbp->GetMode();
3434   PDBUSER dbuserp = PlgGetUser(g);
3435 
3436   if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
3437     sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
3438     return true;
3439     } // endif
3440 
3441   /*********************************************************************/
3442   /*  Update block info if necessary.                                  */
3443   /*********************************************************************/
3444   if (Block < 0)
3445     if ((Headlen = GetBlockInfo(g)) < 0)
3446       return true;
3447 
3448   PlugSetPath(filename, To_File, Tdbp->GetPath());
3449 
3450   if (trace(1))
3451     htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
3452                          filename, mode, Last);
3453 
3454 #if defined(_WIN32)
3455   DWORD access, creation, share = 0, rc = 0;
3456 
3457   /*********************************************************************/
3458   /*  Create the file object according to access mode                  */
3459   /*********************************************************************/
3460   switch (mode) {
3461     case MODE_READ:
3462       access = GENERIC_READ;
3463       share = FILE_SHARE_READ;
3464       creation = OPEN_EXISTING;
3465       break;
3466     case MODE_INSERT:
3467       if (MaxBlk) {
3468         if (!Block)
3469           if (MakeEmptyFile(g, To_File))
3470             return true;
3471 
3472         // Required to update empty blocks
3473         access = GENERIC_READ | GENERIC_WRITE;
3474       } else if (Last == Nrec)
3475         access = GENERIC_WRITE;
3476       else
3477         // Required to update the last block
3478         access = GENERIC_READ | GENERIC_WRITE;
3479 
3480       creation = OPEN_ALWAYS;
3481       break;
3482     case MODE_DELETE:
3483       if (!Tdbp->GetNext()) {
3484         // Store the number of deleted lines
3485         DelRows = Cardinality(g);
3486 
3487         // This will stop the process by
3488         // causing GetProgMax to return 0.
3489 //      ResetTableSize(g, 0, Nrec);     must be done later
3490         del = true;
3491 
3492         // This will delete the whole file
3493         access = GENERIC_READ | GENERIC_WRITE;
3494         creation = TRUNCATE_EXISTING;
3495         break;
3496         } // endif
3497 
3498       // Selective delete, pass thru
3499     case MODE_UPDATE:
3500       if ((UseTemp = Tdbp->IsUsingTemp(g)))
3501         access = GENERIC_READ;
3502       else
3503         access = GENERIC_READ | GENERIC_WRITE;
3504 
3505       creation = OPEN_EXISTING;
3506       break;
3507     default:
3508       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3509       return true;
3510     } // endswitch
3511 
3512   /*********************************************************************/
3513   /*  Use specific Windows API functions.                              */
3514   /*********************************************************************/
3515   Hfile = CreateFile(filename, access, share, NULL, creation,
3516                                FILE_ATTRIBUTE_NORMAL, NULL);
3517 
3518   if (Hfile == INVALID_HANDLE_VALUE) {
3519     rc = GetLastError();
3520     sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3521     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3522                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3523                   (LPTSTR)filename, sizeof(filename), NULL);
3524     strcat(g->Message, filename);
3525     } // endif Hfile
3526 
3527   if (trace(1))
3528     htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
3529            rc, access, share, creation, Hfile, filename);
3530 
3531   if (mode == MODE_INSERT) {
3532     /*******************************************************************/
3533     /* In Insert mode we must position the cursor at end of file.      */
3534     /*******************************************************************/
3535     LARGE_INTEGER of;
3536 
3537     of.QuadPart = (BIGINT)0;
3538     of.LowPart = SetFilePointer(Hfile, of.LowPart,
3539                                       &of.HighPart, FILE_END);
3540 
3541     if (of.LowPart == INVALID_SET_FILE_POINTER &&
3542        (rc = GetLastError()) != NO_ERROR) {
3543       sprintf(g->Message, MSG(ERROR_IN_SFP), rc);
3544       CloseHandle(Hfile);
3545       Hfile = INVALID_HANDLE_VALUE;
3546       } // endif
3547 
3548     } // endif Mode
3549 
3550 #else   // UNIX
3551   /*********************************************************************/
3552   /*  The open() function has a transitional interface for 64-bit      */
3553   /*  file  offsets. Note that using open64() is equivalent to using   */
3554   /*  open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp).  */
3555   /*********************************************************************/
3556   int    rc = 0;
3557   int    oflag;
3558   mode_t pmd = 0;
3559 
3560   /*********************************************************************/
3561   /*  Create the file object according to access mode                  */
3562   /*********************************************************************/
3563   switch (mode) {
3564     case MODE_READ:
3565       oflag = O_RDONLY;
3566       break;
3567     case MODE_INSERT:
3568       if (MaxBlk) {
3569         if (!Block)
3570           if (MakeEmptyFile(g, To_File))
3571             return true;
3572 
3573         // Required to update empty blocks
3574         oflag = O_RDWR;
3575       } else if (Last == Nrec)
3576         oflag = O_WRONLY | O_CREAT | O_APPEND;
3577       else
3578         // Required to update the last block
3579         oflag = O_RDWR | O_CREAT | O_APPEND;
3580 
3581       pmd = S_IREAD | S_IWRITE;
3582       break;
3583     case MODE_DELETE:
3584       // This is temporary until a partial delete is implemented
3585       if (!Tdbp->GetNext()) {
3586         // Store the number of deleted lines
3587         DelRows = Cardinality(g);
3588         del = true;
3589 
3590         // This will delete the whole file and provoque ReadDB to
3591         // return immediately.
3592         oflag = O_RDWR | O_TRUNC;
3593         strcpy(g->Message, MSG(NO_VCT_DELETE));
3594         break;
3595         } // endif
3596 
3597       // Selective delete, pass thru
3598       /* fall through */
3599     case MODE_UPDATE:
3600       UseTemp = Tdbp->IsUsingTemp(g);
3601       oflag = (UseTemp) ? O_RDONLY : O_RDWR;
3602       break;
3603     default:
3604       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3605       return true;
3606     } // endswitch
3607 
3608   Hfile = open64(filename, oflag, pmd); // Enable file size > 2G
3609 
3610   if (Hfile == INVALID_HANDLE_VALUE) {
3611     rc = errno;
3612     sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3613     strcat(g->Message, strerror(errno));
3614     } // endif Hfile
3615 
3616   if (trace(1))
3617     htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n",
3618            rc, oflag, mode, Hfile, filename);
3619 #endif  // UNIX
3620 
3621   if (!rc) {
3622     if (!To_Fb) {
3623       To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3624       To_Fb->Fname = To_File;
3625       To_Fb->Type = TYPE_FB_HANDLE;
3626       To_Fb->Memory = NULL;
3627       To_Fb->Length = 0;
3628       To_Fb->File = NULL;
3629       To_Fb->Next = dbuserp->Openlist;
3630       dbuserp->Openlist = To_Fb;
3631       } // endif To_Fb
3632 
3633     To_Fb->Count = 1;
3634     To_Fb->Mode = mode;
3635     To_Fb->Handle = Hfile;
3636 
3637     if (trace(1))
3638       htrc("File %s is open in mode %d\n", filename, mode);
3639 
3640     if (del)
3641       // This will stop the process by
3642       // causing GetProgMax to return 0.
3643       return ResetTableSize(g, 0, Nrec);
3644 
3645     /*********************************************************************/
3646     /*  Allocate the table and column block buffers.                     */
3647     /*********************************************************************/
3648     return AllocateBuffer(g);
3649   } else
3650     return (mode == MODE_READ && rc == ENOENT)
3651             ? PushWarning(g, Tdbp) : true;
3652 
3653   } // end of OpenTableFile
3654 
3655 /***********************************************************************/
3656 /*  Allocate the block buffers for columns used in the query.          */
3657 /***********************************************************************/
3658 bool BGVFAM::AllocateBuffer(PGLOBAL g)
3659   {
3660   MODE    mode = Tdbp->GetMode();
3661   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
3662   PCOLDEF cdp;
3663   PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3664 
3665   if (mode == MODE_INSERT) {
3666     if (!NewBlock) {
3667       // Not reopening after inserting the last block
3668       bool chk = PlgGetUser(g)->Check & CHK_TYPE;
3669 
3670       NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3671 
3672       for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3673         memset(NewBlock + Nrec * cdp->GetPoff(),
3674               (IsTypeNum(cdp->GetType()) ? 0 : ' '),
3675                           Nrec * cdp->GetClen());
3676 
3677       for (; cp; cp = (PVCTCOL)cp->Next)
3678         cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
3679                                 cp->Buf_Type, Nrec, cp->Format.Length,
3680                                 cp->Format.Prec, chk, true, cp->IsUnsigned());
3681 
3682       InitInsert(g);    // Initialize inserting
3683 
3684       // Currently we don't use a temporary file for inserting
3685       Tfile = Hfile;
3686       } // endif NewBlock
3687 
3688   } else {
3689     if (UseTemp || mode == MODE_DELETE) {
3690       // Allocate all that is needed to move lines
3691       int i = 0;
3692 
3693       if (!Ncol)
3694         for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3695           Ncol++;
3696 
3697       if (MaxBlk)
3698         BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT));
3699       else
3700         Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3701 
3702       Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3703       Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
3704 
3705       for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
3706         if (MaxBlk)
3707           BigDep[i] = (BIGINT)Headlen
3708                     + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk;
3709         else
3710           Deplac[i] = cdp->GetPoff() * Nrec;
3711 
3712         Clens[i] = cdp->GetClen();
3713         Isnum[i] = IsTypeNum(cdp->GetType());
3714         Buflen = MY_MAX(Buflen, cdp->GetClen());
3715         } // endfor cdp
3716 
3717       if (!UseTemp || MaxBlk) {
3718         Buflen *= Nrec;
3719         To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
3720       } else
3721         NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3722 
3723       } // endif mode
3724 
3725     for (; cp; cp = (PVCTCOL)cp->Next)
3726       if (!cp->IsSpecial())            // Not a pseudo column
3727         cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
3728                                 cp->Format.Length, cp->Format.Prec,
3729 					                      true, true, cp->IsUnsigned());
3730 
3731   } //endif mode
3732 
3733   return false;
3734   } // end of AllocateBuffer
3735 
3736 /***********************************************************************/
3737 /*  Data Base write routine for huge VCT access method.                */
3738 /***********************************************************************/
3739 int BGVFAM::WriteBuffer(PGLOBAL g)
3740   {
3741   if (trace(1))
3742     htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n",
3743           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
3744 
3745   if (Tdbp->GetMode() == MODE_UPDATE) {
3746     // Mode Update is done in ReadDB, we just initialize it here
3747     if (Tfile == INVALID_HANDLE_VALUE) {
3748       if (UseTemp) {
3749         if (OpenTempFile(g))
3750           return RC_FX;
3751 
3752         // Most of the time, not all table columns are updated.
3753         // This why we must completely pre-fill the temporary file.
3754         Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
3755                         : Block * Nrec;   // To write last lock
3756 
3757         if (MoveIntermediateLines(g))
3758           return RC_FX;
3759 
3760       } else
3761         Tfile = Hfile;
3762 
3763       } // endif Tfile
3764 
3765   } else {
3766     // Mode Insert
3767     if (MaxBlk && CurBlk == MaxBlk) {
3768       strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
3769       return RC_EF;       // Too many lines for a Vector formatted table
3770       } // endif MaxBlk
3771 
3772     if (Closing || ++CurNum == Nrec) {
3773       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3774 
3775       if (!AddBlock) {
3776         // Write back the updated last block values
3777         for (; cp; cp = (PVCTCOL)cp->Next)
3778           cp->WriteBlock(g);
3779 
3780         if (!Closing && !MaxBlk) {
3781           // Close the VCT file and reopen it in mode Insert
3782 //#if defined(_WIN32)  //OB
3783 //          CloseHandle(Hfile);
3784 //#else    // UNIX
3785 //          close(Hfile);
3786 //#endif   // UNIX
3787           CloseFileHandle(Hfile);
3788           Hfile = INVALID_HANDLE_VALUE;
3789           To_Fb->Count = 0;
3790           Last = Nrec;               // Tested in OpenTableFile
3791 
3792           if (OpenTableFile(g)) {
3793             Closing = true;          // Tell CloseDB of error
3794             return RC_FX;
3795             } // endif Vopen
3796 
3797           AddBlock = true;
3798           } // endif Closing
3799 
3800       } else {
3801         // Here we must add a new block to the VCT file
3802         if (Closing)
3803           // Reset the overwritten columns for last block extra records
3804           for (; cp; cp = (PVCTCOL)cp->Next)
3805             memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
3806                    (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
3807                    (Nrec - Last) * cp->Clen);
3808 
3809         if (BigWrite(g, Hfile, NewBlock, Blksize))
3810           return RC_FX;
3811 
3812       } // endif AddBlock
3813 
3814       if (!Closing) {
3815         CurBlk++;
3816         CurNum = 0;
3817         } // endif Closing
3818 
3819       } // endif
3820 
3821     } // endif Mode
3822 
3823   return RC_OK;
3824   } // end of WriteBuffer
3825 
3826 /***********************************************************************/
3827 /*  Data Base delete line routine for BGVFAM access method.            */
3828 /***********************************************************************/
3829 int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
3830   {
3831   bool eof = false;
3832 
3833   /*********************************************************************/
3834   /*  There is an alternative here depending on UseTemp:               */
3835   /*  1 - use a temporary file in which are copied all not deleted     */
3836   /*      lines, at the end the original file will be deleted and      */
3837   /*      the temporary file renamed to the original file name.        */
3838   /*  2 - directly move the not deleted lines inside the original      */
3839   /*      file, and at the end erase all trailing records.             */
3840   /*********************************************************************/
3841   if (trace(1))
3842     htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
3843                         irc, UseTemp, Fpos, Tpos, Spos);
3844 
3845   if (irc != RC_OK) {
3846     /*******************************************************************/
3847     /*  EOF: position Fpos at the end-of-file position.                */
3848     /*******************************************************************/
3849     Fpos = (Block - 1) * Nrec + Last;
3850 
3851     if (trace(1))
3852       htrc("Fpos placed at file end=%d\n", Fpos);
3853 
3854     eof = UseTemp && !MaxBlk;
3855   } else     // Fpos is the deleted line position
3856     Fpos = CurBlk * Nrec + CurNum;
3857 
3858   if (Tpos == Spos) {
3859     if (UseTemp) {
3860       /*****************************************************************/
3861       /*  Open the temporary file, Spos is at the beginning of file.   */
3862       /*****************************************************************/
3863       if (OpenTempFile(g))
3864         return RC_FX;
3865 
3866     } else {
3867       /*****************************************************************/
3868       /*  Move of eventual preceding lines is not required here.      */
3869       /*  Set the target file as being the source file itself.         */
3870       /*  Set the future Tpos, and give Spos a value to block copying. */
3871       /*****************************************************************/
3872       Tfile = Hfile;
3873       Spos = Tpos = Fpos;
3874     } // endif UseTemp
3875 
3876     } // endif Tpos == Spos
3877 
3878   /*********************************************************************/
3879   /*  Move any intermediate lines.                                     */
3880   /*********************************************************************/
3881   if (MoveIntermediateLines(g, &eof))
3882     return RC_FX;
3883 
3884   if (irc == RC_OK) {
3885 #ifdef _DEBUG
3886     assert(Spos == Fpos);
3887 #endif
3888     Spos++;          // New start position is on next line
3889 
3890     if (trace(1))
3891       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
3892 
3893   } else {
3894     /*******************************************************************/
3895     /*  Last call after EOF has been reached.                          */
3896     /*******************************************************************/
3897     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3898     Last = (Tpos + Nrec - 1) % Nrec + 1;
3899 
3900     if (!UseTemp) {    // The UseTemp case is treated in CloseTableFile
3901       if (!MaxBlk) {
3902         if (Last < Nrec)            // Clean last block
3903           if (CleanUnusedSpace(g))
3904             return RC_FX;
3905 
3906         /***************************************************************/
3907         /*  Remove extra records.                                      */
3908         /***************************************************************/
3909 #if defined(_WIN32)
3910         BIGINT pos = (BIGINT)Block * (BIGINT)Blksize;
3911 
3912         if (BigSeek(g, Hfile, pos))
3913           return RC_FX;
3914 
3915         if (!SetEndOfFile(Hfile)) {
3916           DWORD drc = GetLastError();
3917 
3918           sprintf(g->Message, MSG(SETEOF_ERROR), drc);
3919           return RC_FX;
3920           } // endif error
3921 #else   // !_WIN32
3922         if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
3923           sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3924           return RC_FX;
3925           } // endif
3926 #endif  // !_WIN32
3927       } else // MaxBlk
3928         // Clean the unused space in the file, this is required when
3929         // inserting again with a partial column list.
3930         if (CleanUnusedSpace(g))
3931           return RC_FX;
3932 
3933       if (ResetTableSize(g, Block, Last))
3934        return RC_FX;
3935 
3936       } // endif UseTemp
3937 
3938   } // endif irc
3939 
3940   return RC_OK;                                      // All is correct
3941   } // end of DeleteRecords
3942 
3943 /***********************************************************************/
3944 /*  Open a temporary file used while updating or deleting.             */
3945 /***********************************************************************/
3946 bool BGVFAM::OpenTempFile(PGLOBAL g)
3947   {
3948   char   *tempname;
3949   PDBUSER dup = PlgGetUser(g);
3950 
3951   /*********************************************************************/
3952   /*  Open the temporary file, Spos is at the beginning of file.       */
3953   /*********************************************************************/
3954   tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
3955   PlugSetPath(tempname, To_File, Tdbp->GetPath());
3956   strcat(PlugRemoveType(tempname, tempname), ".t");
3957 
3958   if (!MaxBlk)
3959     remove(tempname);       // Be sure it does not exist yet
3960   else if (MakeEmptyFile(g, tempname))
3961     return true;
3962 
3963 #if defined(_WIN32)
3964   DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW;
3965 
3966   Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
3967                      access, FILE_ATTRIBUTE_NORMAL, NULL);
3968 
3969   if (Tfile == INVALID_HANDLE_VALUE) {
3970     DWORD rc = GetLastError();
3971     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname);
3972     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3973               FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3974               (LPTSTR)tempname, _MAX_PATH, NULL);
3975     strcat(g->Message, tempname);
3976     return true;
3977     } // endif Tfile
3978 #else    // UNIX
3979   int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC;
3980 
3981   Tfile = open64(tempname, oflag, S_IWRITE);
3982 
3983   if (Tfile == INVALID_HANDLE_VALUE) {
3984     int rc = errno;
3985     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
3986     strcat(g->Message, strerror(errno));
3987     return true;
3988     } //endif Tfile
3989 #endif   // UNIX
3990 
3991   To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3992   To_Fbt->Fname = tempname;
3993   To_Fbt->Type = TYPE_FB_HANDLE;
3994   To_Fbt->Memory = NULL;
3995   To_Fbt->Length = 0;
3996   To_Fbt->File = NULL;
3997   To_Fbt->Next = dup->Openlist;
3998   To_Fbt->Count = 1;
3999   To_Fbt->Mode = MODE_INSERT;
4000   To_Fbt->Handle = Tfile;
4001   dup->Openlist = To_Fbt;
4002   return false;
4003   } // end of OpenTempFile
4004 
4005 /***********************************************************************/
4006 /*  Move intermediate deleted or updated lines.                        */
4007 /***********************************************************************/
4008 bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
4009   {
4010   int    i, n, req, dep;
4011   bool   eof = (b) ? *b : false;
4012   BIGINT pos;
4013 
4014   for (n = Fpos - Spos; n > 0 || eof; n -= req) {
4015     /*******************************************************************/
4016     /*  Non consecutive line to delete. Move intermediate lines.       */
4017     /*******************************************************************/
4018     if (!MaxBlk)
4019       req = (DWORD)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
4020     else
4021       req = (DWORD)MY_MIN(n, Nrec);
4022 
4023     if (req) for (i = 0; i < Ncol; i++) {
4024       if (!MaxBlk) {
4025         if (UseTemp)
4026           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4027 
4028         pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i])
4029             + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize;
4030       } else
4031         pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i];
4032 
4033       if (BigSeek(g, Hfile, pos))
4034         return true;
4035 
4036       if (BigRead(g, Hfile, To_Buf, req * Clens[i]))
4037         return true;
4038 
4039       if (!UseTemp || MaxBlk) {
4040         if (!MaxBlk)
4041           pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i])
4042               + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize;
4043         else
4044           pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4045 
4046         if (BigSeek(g, Tfile, pos))
4047           return true;
4048 
4049         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4050           return true;
4051 
4052         } // endif UseTemp
4053 
4054       } // endfor i
4055 
4056     Tpos += (int)req;
4057     Spos += (int)req;
4058 
4059     if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) {
4060       // Write the full or last block to the temporary file
4061       if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
4062         // Clean the last block in case of future insert, must be
4063         // done here because Tfile was open in write only.
4064         for (i = 0; i < Ncol; i++) {
4065           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4066           memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
4067           } // endfor i
4068 
4069       if (BigWrite(g, Tfile, NewBlock, Blksize))
4070         return true;
4071 
4072       if (Spos == Fpos)
4073         eof = false;
4074 
4075       } // endif Usetemp...
4076 
4077     if (trace(1))
4078       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
4079 
4080     } // endfor n
4081 
4082   return false;
4083   } // end of MoveIntermediateLines
4084 
4085 /***********************************************************************/
4086 /*  Clean deleted space in a huge VCT or Vec table file.               */
4087 /***********************************************************************/
4088 bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
4089   {
4090   int    i;
4091   int   n;
4092   BIGINT pos, dep;
4093 
4094   if (!MaxBlk) {
4095     /*******************************************************************/
4096     /*  Clean last block of the VCT table file.                        */
4097     /*******************************************************************/
4098     assert(!UseTemp); // This case is handled in MoveIntermediateLines
4099 
4100     if (!(n = Nrec - Last))
4101       return false;
4102 
4103     dep = (BIGINT)((Block - 1) * Blksize);
4104 
4105     for (i = 0; i < Ncol; i++) {
4106       memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
4107       pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]);
4108 
4109       if (BigSeek(g, Hfile, pos))
4110         return true;
4111 
4112       if (BigWrite(g, Hfile, To_Buf, n * Clens[i]))
4113         return true;
4114 
4115       } // endfor i
4116 
4117   } else {
4118     int  req;
4119 
4120     if (To_Buf)
4121       memset(To_Buf, 0, Buflen);
4122 
4123     for (n = Fpos - Tpos; n > 0; n -= req) {
4124       /*****************************************************************/
4125       /*  Fill VEC file remaining lines with 0's.                      */
4126       /*  This seems to work even column blocks have been made with    */
4127       /*  Blanks = true. Perhaps should it be set to false for VEC.    */
4128       /*****************************************************************/
4129       req = MY_MIN(n, Nrec);
4130 
4131       for (i = 0; i < Ncol; i++) {
4132         pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4133 
4134         if (BigSeek(g, Tfile, pos))
4135           return true;
4136 
4137         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4138           return true;
4139 
4140         } // endfor i
4141 
4142       Tpos += req;
4143       } // endfor n
4144 
4145   } // endif MaxBlk
4146 
4147   return false;
4148   } // end of CleanUnusedSpace
4149 
4150 /***********************************************************************/
4151 /*  Data Base close routine for huge VEC access method.                */
4152 /***********************************************************************/
4153 void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
4154   {
4155   int  rc = 0, wrc = RC_OK;
4156   MODE mode = Tdbp->GetMode();
4157 
4158   Abort = abort;
4159 
4160   if (mode == MODE_INSERT) {
4161     if (Closing)
4162       wrc = RC_FX;                  // Last write was in error
4163     else
4164       if (CurNum) {
4165         // Some more inserted lines remain to be written
4166         Last = CurNum;
4167         Block = CurBlk + 1;
4168         Closing = true;
4169         wrc = WriteBuffer(g);
4170       } else {
4171         Last = Nrec;
4172         Block = CurBlk;
4173         wrc = RC_OK;
4174       } // endif CurNum
4175 
4176     if (wrc != RC_FX) {
4177       rc = ResetTableSize(g, Block, Last);
4178     } else if (AddBlock) {
4179       // Last block was not written
4180       rc = ResetTableSize(g, CurBlk, Nrec);
4181 			throw 44;
4182 		} // endif
4183 
4184   } else if (mode == MODE_UPDATE) {
4185     // Write back to file any pending modifications
4186     for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols();
4187                  colp; colp = (PVCTCOL)colp->Next)
4188       colp->WriteBlock(g);
4189 
4190     if (UseTemp && Tfile) {
4191       rc = RenameTempFile(g);
4192       Hfile = Tfile = INVALID_HANDLE_VALUE;
4193 
4194       if (Header)
4195         // Header must be set because it was not set in temp file
4196         rc = SetBlockInfo(g);
4197 
4198       } // endif UseTemp
4199 
4200   } else if (mode == MODE_DELETE && UseTemp && Tfile) {
4201     if (MaxBlk)
4202       rc = CleanUnusedSpace(g);
4203 
4204     if ((rc = RenameTempFile(g)) != RC_FX) {
4205       Hfile = Tfile = INVALID_HANDLE_VALUE;    // For SetBlockInfo
4206       rc = ResetTableSize(g, Block, Last);
4207       } // endif rc
4208 
4209   } // endif's mode
4210 
4211   if (Hfile != INVALID_HANDLE_VALUE)
4212     rc = PlugCloseFile(g, To_Fb);
4213 
4214   if (trace(1))
4215     htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n",
4216           To_File, wrc, rc);
4217 
4218   Hfile = INVALID_HANDLE_VALUE;
4219   } // end of CloseDB
4220 
4221 /***********************************************************************/
4222 /*  Rewind routine for huge VCT access method.                         */
4223 /***********************************************************************/
4224 void BGVFAM::Rewind(void)
4225   {
4226   // In mode update we need to read Set Column blocks
4227   if (Tdbp->GetMode() == MODE_UPDATE)
4228     OldBlk = -1;
4229 
4230   // Initialize so block optimization is called for 1st block
4231   CurBlk = -1;
4232   CurNum = Nrec - 1;
4233 
4234 #if 0 // This is probably unuseful as the file is directly accessed
4235 #if defined(_WIN32)  //OB
4236   SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
4237 #else    // UNIX
4238   lseek64(Hfile, 0, SEEK_SET);
4239 #endif   // UNIX
4240 #endif // 0
4241   } // end of Rewind
4242 
4243 /***********************************************************************/
4244 /*  ReadBlock: Read column values from current block.                  */
4245 /***********************************************************************/
4246 bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
4247   {
4248   BIGINT pos;
4249 
4250   /*********************************************************************/
4251   /*  Calculate the offset and size of the block to read.              */
4252   /*********************************************************************/
4253   if (MaxBlk)                                 // File has Vector format
4254     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4255         + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen;
4256   else                                        // Old VCT format
4257     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4258         + (BIGINT)Lrecl * (BIGINT)CurBlk);
4259 
4260   if (trace(1))
4261     htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n",
4262           pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
4263 
4264   if (BigSeek(g, Hfile, pos))
4265     return true;
4266 
4267   if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec))
4268     return true;
4269 
4270   if (trace(1))
4271     num_read++;
4272 
4273   return false;
4274   } // end of ReadBlock
4275 
4276 /***********************************************************************/
4277 /*  WriteBlock: Write back current column values for one block.        */
4278 /*  Note: the test of Status is meant to prevent physical writing of   */
4279 /*  the block during the checking loop in mode Update. It is set to    */
4280 /*  BUF_EMPTY when reopening the table between the two loops.          */
4281 /***********************************************************************/
4282 bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
4283   {
4284   int    len;
4285   BIGINT pos;
4286 
4287   /*********************************************************************/
4288   /*  Calculate the offset and size of the block to write.             */
4289   /*********************************************************************/
4290   if (MaxBlk)                               // File has Vector format
4291     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4292         + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen;
4293   else                                      // Old VCT format
4294     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4295         + (BIGINT)Lrecl * (BIGINT)colp->ColBlk);
4296 
4297   if (trace(1))
4298     htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n",
4299           pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
4300 
4301   if (BigSeek(g, Tfile, pos))
4302     return true;
4303 
4304 //len = colp->Clen * Nrec;    see comment in VCTFAM
4305   len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec);
4306 
4307   if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len))
4308     return true;
4309 
4310   return false;
4311   } // end of WriteBlock
4312 
4313 /* ----------------------- End of FilAMVct --------------------------- */
4314