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       LPCSTR  name __attribute__((unused))= Tdbp->GetName();
1168 
1169       defp->SetBlock(Block);
1170       defp->SetLast(Last);
1171 
1172       if (!defp->SetIntCatInfo("Blocks", Block) ||
1173           !defp->SetIntCatInfo("Last", Last)) {
1174         sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
1175         rc = true;
1176         } // endif
1177 
1178     } else
1179       rc = SetBlockInfo(g);
1180 
1181     } // endif Split
1182 
1183   Tdbp->ResetSize();
1184   return rc;
1185   } // end of ResetTableSize
1186 
1187 /***********************************************************************/
1188 /*  Rewind routine for VCT access method.                              */
1189 /***********************************************************************/
Rewind(void)1190 void VCTFAM::Rewind(void)
1191   {
1192   // In mode update we need to read Set Column blocks
1193   if (Tdbp->GetMode() == MODE_UPDATE)
1194     OldBlk = -1;
1195 
1196   // Initialize so block optimization is called for 1st block
1197   CurBlk = -1;
1198   CurNum = Nrec - 1;
1199 //rewind(Stream);             will be placed by fseek
1200   } // end of Rewind
1201 
1202 /***********************************************************************/
1203 /*  ReadBlock: Read column values from current block.                  */
1204 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)1205 bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
1206   {
1207   int     len;
1208   size_t  n;
1209 
1210   /*********************************************************************/
1211   /*  Calculate the offset and size of the block to read.              */
1212   /*********************************************************************/
1213   if (MaxBlk)                                 // True vector format
1214     len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk);
1215   else                                        // Blocked vector format
1216     len = Nrec * (colp->Deplac + Lrecl * CurBlk);
1217 
1218   if (trace(1))
1219     htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n",
1220           len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
1221 
1222   if (fseek(Stream, len, SEEK_SET)) {
1223     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1224     return true;
1225     } // endif
1226 
1227   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
1228                                         (size_t)Nrec, Stream);
1229 
1230   if (n != (size_t)Nrec) {
1231     if (errno == NO_ERROR)
1232       sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
1233     else
1234       sprintf(g->Message, MSG(READ_ERROR),
1235               To_File, strerror(errno));
1236 
1237     if (trace(1))
1238       htrc(" Read error: %s\n", g->Message);
1239 
1240     return true;
1241     } // endif
1242 
1243   if (trace(1))
1244     num_read++;
1245 
1246   return false;
1247   } // end of ReadBlock
1248 
1249 /***********************************************************************/
1250 /*  WriteBlock: Write back current column values for one block.        */
1251 /*  Note: the test of Status is meant to prevent physical writing of   */
1252 /*  the block during the checking loop in mode Update. It is set to    */
1253 /*  BUF_EMPTY when reopening the table between the two loops.          */
1254 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)1255 bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
1256   {
1257   int   len;
1258   size_t n;
1259 
1260   /*********************************************************************/
1261   /*  Calculate the offset and size of the block to write.             */
1262   /*********************************************************************/
1263   if (MaxBlk)                               // File has Vector format
1264     len = Headlen
1265         + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
1266   else                                      // Old VCT format
1267     len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
1268 
1269   if (trace(1))
1270     htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
1271           Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
1272 
1273   if (fseek(T_Stream, len, SEEK_SET)) {
1274     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1275     return true;
1276     } // endif
1277 
1278   // Here Nrec was changed to CurNum in mode Insert,
1279   // this is the true number of records to write,
1280   // this also avoid writing garbage in the file for true vector tables.
1281   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec;
1282 
1283   if (n != fwrite(colp->Blk->GetValPointer(),
1284                             (size_t)colp->Clen, n, T_Stream)) {
1285     sprintf(g->Message, MSG(WRITE_STRERROR),
1286             (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
1287 
1288     if (trace(1))
1289       htrc("Write error: %s\n", strerror(errno));
1290 
1291     return true;
1292     } // endif
1293 
1294 #if defined(UNIX)
1295   fflush(T_Stream); //NGC
1296 #endif
1297 
1298 #ifdef _DEBUG
1299   num_write++;
1300 #endif
1301 
1302   return false;
1303   } // end of WriteBlock
1304 
1305 /* -------------------------- Class VCMFAM --------------------------- */
1306 
1307 /***********************************************************************/
1308 /*  Implementation of the VCMFAM class.                                */
1309 /***********************************************************************/
VCMFAM(PVCTDEF tdp)1310 VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1311   {
1312   Memory = NULL;
1313   Memcol = NULL;
1314   } // end of VCMFAM standard constructor
1315 
VCMFAM(PVCMFAM txfp)1316 VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp)
1317   {
1318   Memory = txfp->Memory;
1319   Memcol = txfp->Memcol;
1320   } // end of VCMFAM copy constructor
1321 
1322 /***********************************************************************/
1323 /*  Mapped VCT Access Method opening routine.                          */
1324 /*  New method now that this routine is called recursively (last table */
1325 /*  first in reverse order): index blocks are immediately linked to    */
1326 /*  join block of next table if it exists or else are discarted.       */
1327 /***********************************************************************/
OpenTableFile(PGLOBAL g)1328 bool VCMFAM::OpenTableFile(PGLOBAL g)
1329   {
1330   char    filename[_MAX_PATH];
1331   size_t  len;
1332   MODE    mode = Tdbp->GetMode();
1333   PFBLOCK fp = NULL;
1334   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1335 
1336   /*********************************************************************/
1337   /*  Update block info if necessary.                                  */
1338   /*********************************************************************/
1339   if (Block < 0)
1340     if ((Headlen = GetBlockInfo(g)) < 0)
1341       return true;
1342 
1343   /*********************************************************************/
1344   /*  We used the file name relative to recorded datapath.             */
1345   /*********************************************************************/
1346   PlugSetPath(filename, To_File, Tdbp->GetPath());
1347 
1348   /*********************************************************************/
1349   /*  The whole file will be mapped so we can use it as if it were     */
1350   /*  entirely read into virtual memory.                               */
1351   /*  Firstly we check whether this file have been already mapped.     */
1352   /*********************************************************************/
1353   if (mode == MODE_READ) {
1354     for (fp = dbuserp->Openlist; fp; fp = fp->Next)
1355       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
1356                      && fp->Count && fp->Mode == mode)
1357         break;
1358 
1359     if (trace(1))
1360       htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count);
1361 
1362   } else
1363     fp = NULL;
1364 
1365   if (fp) {
1366     /*******************************************************************/
1367     /*  File already mapped. Just increment use count and get pointer. */
1368     /*******************************************************************/
1369     fp->Count++;
1370     Memory = fp->Memory;
1371     len = fp->Length;
1372   } else {
1373     /*******************************************************************/
1374     /*  If required, delete the whole file if no filtering is implied. */
1375     /*******************************************************************/
1376     bool   del;
1377     HANDLE hFile;
1378     MEMMAP mm;
1379     MODE   mapmode = mode;
1380 
1381     if (mode == MODE_INSERT) {
1382       if (MaxBlk) {
1383         if (!Block)
1384           if (MakeEmptyFile(g, To_File))
1385             return true;
1386 
1387         // Inserting will be like updating the file
1388         mapmode = MODE_UPDATE;
1389       } else {
1390         strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
1391         return true;
1392       } // endif MaxBlk
1393 
1394       } // endif mode
1395 
1396     del = mode == MODE_DELETE && !Tdbp->GetNext();
1397 
1398     if (del) {
1399       DelRows = Cardinality(g);
1400 
1401       // This will stop the process by causing GetProgMax to return 0.
1402 //    ResetTableSize(g, 0, Nrec);          must be done later
1403       } // endif del
1404 
1405     /*******************************************************************/
1406     /*  Create the mapping file object.                                */
1407     /*******************************************************************/
1408     hFile = CreateFileMap(g, filename, &mm, mapmode, del);
1409 
1410     if (hFile == INVALID_HANDLE_VALUE) {
1411       DWORD rc = GetLastError();
1412 
1413       if (!(*g->Message))
1414         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1415                 "map", (int) rc, filename);
1416 
1417       if (trace(1))
1418         htrc("%s\n", g->Message);
1419 
1420       return (mode == MODE_READ && rc == ENOENT)
1421               ? PushWarning(g, Tdbp) : true;
1422       } // endif hFile
1423 
1424     /*******************************************************************/
1425     /*  Get the file size.                                             */
1426     /*******************************************************************/
1427 		len = (size_t)mm.lenL;
1428 
1429 		if (mm.lenH)
1430 			len += ((size_t)mm.lenH * 0x000000001LL);
1431 
1432 		Memory = (char *)mm.memory;
1433 
1434     if (!len) {             // Empty or deleted file
1435       CloseFileHandle(hFile);
1436       bool rc = ResetTableSize(g, 0, Nrec);
1437       return (mapmode == MODE_UPDATE) ? true : rc;
1438       } // endif len
1439 
1440     if (!Memory) {
1441       CloseFileHandle(hFile);
1442       sprintf(g->Message, MSG(MAP_VIEW_ERROR),
1443                           filename, GetLastError());
1444       return true;
1445       } // endif Memory
1446 
1447     if (mode != MODE_DELETE) {
1448       CloseFileHandle(hFile);                    // Not used anymore
1449       hFile = INVALID_HANDLE_VALUE;              // For Fblock
1450       } // endif Mode
1451 
1452     /*******************************************************************/
1453     /*  Link a Fblock. This make possible to reuse already opened maps */
1454     /*  and also to automatically unmap them in case of error g->jump. */
1455     /*  Note: block can already exist for previously closed file.      */
1456     /*******************************************************************/
1457     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1458     fp->Type = TYPE_FB_MAP;
1459     fp->Fname = PlugDup(g, filename);
1460     fp->Next = dbuserp->Openlist;
1461     dbuserp->Openlist = fp;
1462     fp->Count = 1;
1463     fp->Length = len;
1464     fp->Memory = Memory;
1465     fp->Mode = mode;
1466     fp->File = NULL;
1467     fp->Handle = hFile;                // Used for Delete
1468   } // endif fp
1469 
1470   To_Fb = fp;                               // Useful when closing
1471 
1472   if (trace(1))
1473     htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
1474           fp, fp->Count, Memory, len);
1475 
1476   return AllocateBuffer(g);
1477   } // end of OpenTableFile
1478 
1479 /***********************************************************************/
1480 /*  Allocate the block buffers for columns used in the query.          */
1481 /*  Give a dummy value (1) to prevent allocating the value block.     */
1482 /*  It will be set pointing into the memory map of the file.           */
1483 /*  Note: Memcol must be set for all columns because it can be used    */
1484 /*  for set columns in Update. Clens values are used only in Delete.   */
1485 /***********************************************************************/
AllocateBuffer(PGLOBAL g)1486 bool VCMFAM::AllocateBuffer(PGLOBAL g)
1487   {
1488   int     m, i = 0;
1489   bool    b = Tdbp->GetMode() == MODE_DELETE;
1490   PVCTCOL cp;
1491   PCOLDEF cdp;
1492   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1493 
1494   // Calculate the number of columns
1495   if (!Ncol)
1496     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
1497       Ncol++;
1498 
1499   // To store the start position of each column
1500   Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*));
1501   m = (MaxBlk) ? MaxBlk : 1;
1502 
1503   // We will need all column sizes and type for Delete
1504   if (b) {
1505     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
1506     Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
1507     } // endif b
1508 
1509   for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) {
1510     if (b) {
1511       Clens[i] = cdp->GetClen();
1512       Isnum[i] = IsTypeNum(cdp->GetType());
1513       } // endif b
1514 
1515     Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec;
1516     } // endfor cdp
1517 
1518   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1519     if (!cp->IsSpecial()) {            // Not a pseudo column
1520       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
1521                               cp->Format.Length, cp->Format.Prec,
1522 				                      true, true, cp->IsUnsigned());
1523       cp->AddStatus(BUF_MAPPED);
1524       } // endif IsSpecial
1525 
1526   if (Tdbp->GetMode() == MODE_INSERT)
1527     return InitInsert(g);
1528 
1529   return false;
1530   } // end of AllocateBuffer
1531 
1532 /***********************************************************************/
1533 /*  Do initial action when inserting.                                  */
1534 /***********************************************************************/
InitInsert(PGLOBAL g)1535 bool VCMFAM::InitInsert(PGLOBAL g)
1536 {
1537   bool     rc = false;
1538   volatile PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1539 
1540   // We come here in MODE_INSERT only
1541   if (Last == Nrec) {
1542     CurBlk = Block;
1543     CurNum = 0;
1544     AddBlock = !MaxBlk;
1545   } else {
1546     // The starting point must be at the end of file as for append.
1547     CurBlk = Block - 1;
1548     CurNum = Last;
1549   } // endif Last
1550 
1551 	try {
1552 		// Initialize the column block pointer
1553 		for (; cp; cp = (PVCTCOL)cp->Next)
1554 			cp->ReadBlock(g);
1555 
1556 	} catch (int n) {
1557 	  if (trace(1))
1558 		  htrc("Exception %d: %s\n", n, g->Message);
1559 		rc = true;
1560   } catch (const char *msg) {
1561 	  strcpy(g->Message, msg);
1562 		rc = true;
1563   } // end catch
1564 
1565   return rc;
1566 } // end of InitInsert
1567 
1568 /***********************************************************************/
1569 /*  Data Base write routine for VMP access method.                     */
1570 /***********************************************************************/
WriteBuffer(PGLOBAL g)1571 int VCMFAM::WriteBuffer(PGLOBAL g)
1572   {
1573   if (trace(1))
1574     htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
1575           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
1576 
1577   // Mode Update being done in ReadDB we process here Insert mode only.
1578   if (Tdbp->GetMode() == MODE_INSERT) {
1579     if (CurBlk == MaxBlk) {
1580       strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
1581       return RC_EF;       // Too many lines for vector formatted table
1582       } // endif MaxBlk
1583 
1584     if (Closing || ++CurNum == Nrec) {
1585       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1586 
1587       // Write back the updated last block values
1588       for (; cp; cp = (PVCTCOL)cp->Next)
1589         cp->WriteBlock(g);
1590 
1591       if (!Closing) {
1592         CurBlk++;
1593         CurNum = 0;
1594 
1595         // Re-initialize the column block pointer
1596         for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1597           cp->ReadBlock(g);
1598 
1599         } // endif Closing
1600 
1601       } // endif Closing || CurNum
1602 
1603     } // endif Mode
1604 
1605   return RC_OK;
1606   } // end of WriteBuffer
1607 
1608 /***********************************************************************/
1609 /*  Data Base delete line routine for VMP access method.               */
1610 /*  Lines between deleted lines are moved in the mapfile view.         */
1611 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)1612 int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
1613   {
1614   if (trace(1))
1615     htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
1616                         irc, To_Buf, Tpos, Spos);
1617 
1618   if (irc != RC_OK) {
1619     /*******************************************************************/
1620     /*  EOF: position Fpos at the top of map position.                 */
1621     /*******************************************************************/
1622     Fpos = (Block - 1) * Nrec + Last;
1623 
1624     if (trace(1))
1625       htrc("Fpos placed at file top=%p\n", Fpos);
1626 
1627   } else     // Fpos is the Deleted line position
1628     Fpos = CurBlk * Nrec + CurNum;
1629 
1630   if (Tpos == Spos) {
1631     /*******************************************************************/
1632     /*  First line to delete. Move of eventual preceding lines is     */
1633     /*  not required here, just setting of future Spos and Tpos.       */
1634     /*******************************************************************/
1635     Tpos = Spos = Fpos;
1636   } else
1637     (void)MoveIntermediateLines(g);
1638 
1639   if (irc == RC_OK) {
1640     Spos = Fpos + 1;                               // New start position
1641 
1642     if (trace(1))
1643       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
1644 
1645   } else {
1646     /*******************************************************************/
1647     /*  Last call after EOF has been reached.                          */
1648     /*******************************************************************/
1649     int i, m, n;
1650 
1651     /*******************************************************************/
1652     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
1653     /*******************************************************************/
1654     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
1655     Last = (Tpos + Nrec - 1) % Nrec + 1;
1656 
1657     if (!MaxBlk) {
1658       PFBLOCK fp = To_Fb;
1659 
1660       // Clean the unused part of the last block
1661       m = (Block - 1) * Blksize;
1662       n = Nrec - Last;
1663 
1664       for (i = 0; i < Ncol; i++)
1665         memset(Memcol[i] + m + Last * Clens[i],
1666             (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1667 
1668       // We must Unmap the view and use the saved file handle
1669       // to put an EOF at the end of the last block of the file.
1670       CloseMemMap(fp->Memory, (size_t)fp->Length);
1671       fp->Count = 0;                            // Avoid doing it twice
1672 
1673       // Remove extra blocks
1674       n = Block * Blksize;
1675 
1676 #if defined(_WIN32)
1677       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
1678 
1679       if (drc == 0xFFFFFFFF) {
1680         sprintf(g->Message, MSG(FUNCTION_ERROR),
1681                             "SetFilePointer", GetLastError());
1682         CloseHandle(fp->Handle);
1683         return RC_FX;
1684         } // endif
1685 
1686       if (trace(1))
1687         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
1688 
1689       if (!SetEndOfFile(fp->Handle)) {
1690         sprintf(g->Message, MSG(FUNCTION_ERROR),
1691                             "SetEndOfFile", GetLastError());
1692         CloseHandle(fp->Handle);
1693         return RC_FX;
1694         } // endif
1695 
1696       CloseHandle(fp->Handle);
1697 #else    // UNIX
1698       if (ftruncate(fp->Handle, (off_t)n)) {
1699         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1700         close(fp->Handle);
1701         return RC_FX;
1702         } // endif
1703 
1704       close(fp->Handle);
1705 #endif   // UNIX
1706     } else
1707       // True vector table, Table file size does not change.
1708       // Just clean the unused part of the file.
1709       for (n = Fpos - Tpos, i = 0; i < Ncol; i++)
1710         memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]);
1711 
1712     // Reset Last and Block values in the catalog
1713     PlugCloseFile(g, To_Fb);      // in case of Header
1714     ResetTableSize(g, Block, Last);
1715   } // endif irc
1716 
1717   return RC_OK;                                      // All is correct
1718   } // end of DeleteRecords
1719 
1720 /***********************************************************************/
1721 /*  Move intermediate deleted or updated lines.                        */
1722 /***********************************************************************/
MoveIntermediateLines(PGLOBAL,bool *)1723 bool VCMFAM::MoveIntermediateLines(PGLOBAL, bool *)
1724   {
1725   int i, m, n;
1726 
1727   if ((n = Fpos - Spos) > 0) {
1728     /*******************************************************************/
1729     /*  Non consecutive line to delete. Move intermediate lines.       */
1730     /*******************************************************************/
1731     if (!MaxBlk) {
1732       // Old VCT format, moving must respect block limits
1733       char *ps, *pt;
1734       int   req, soff, toff;
1735 
1736       for (; n > 0; n -= req) {
1737         soff = Spos % Nrec;
1738         toff = Tpos % Nrec;
1739         req = (size_t)MY_MIN(n, Nrec - MY_MAX(soff, toff));
1740 
1741         for (i = 0; i < Ncol; i++) {
1742           ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i];
1743           pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i];
1744           memmove(pt, ps, req * Clens[i]);
1745           } // endfor i
1746 
1747         Tpos += req;
1748         Spos += req;
1749         } // endfor n
1750 
1751     } else {
1752       // True vector format, all is simple...
1753       for (i = 0; i < Ncol; i++) {
1754         m = Clens[i];
1755         memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m);
1756         } // endfor i
1757 
1758       Tpos += n;
1759     } // endif MaxBlk
1760 
1761     if (trace(1))
1762       htrc("move %d bytes\n", n);
1763 
1764     } // endif n
1765 
1766   return false;
1767   } // end of MoveIntermediate Lines
1768 
1769 /***********************************************************************/
1770 /*  Data Base close routine for VMP access method.                     */
1771 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)1772 void VCMFAM::CloseTableFile(PGLOBAL g, bool)
1773   {
1774   int  wrc = RC_OK;
1775   MODE mode = Tdbp->GetMode();
1776 
1777   if (mode == MODE_INSERT) {
1778     if (!Closing) {
1779       if (CurNum) {
1780         // Some more inserted lines remain to be written
1781         Last = CurNum;
1782         Block = CurBlk + 1;
1783         Closing = true;
1784         wrc = WriteBuffer(g);
1785       } else {
1786         Last = Nrec;
1787         Block = CurBlk;
1788         wrc = RC_OK;
1789       } // endif CurNum
1790 
1791     } else
1792       wrc = RC_FX;                  // Last write was in error
1793 
1794     PlugCloseFile(g, To_Fb);
1795 
1796     if (wrc != RC_FX)
1797       /*rc =*/ ResetTableSize(g, Block, Last);
1798 
1799   } else if (mode != MODE_DELETE || Abort)
1800     PlugCloseFile(g, To_Fb);
1801 
1802   } // end of CloseTableFile
1803 
1804 /***********************************************************************/
1805 /*  ReadBlock: Read column values from current block.                  */
1806 /***********************************************************************/
ReadBlock(PGLOBAL,PVCTCOL colp)1807 bool VCMFAM::ReadBlock(PGLOBAL, PVCTCOL colp)
1808   {
1809   char *mempos;
1810   int   i = colp->Index - 1;
1811   int  n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl);
1812 
1813   /*********************************************************************/
1814   /*  Calculate the start position of the column block to read.        */
1815   /*********************************************************************/
1816   mempos = Memcol[i] + n * CurBlk;
1817 
1818   if (trace(1))
1819     htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n",
1820           mempos, i, Nrec, colp->Clen, CurBlk);
1821 
1822   if (colp->GetStatus(BUF_MAPPED))
1823     colp->Blk->SetValPointer(mempos);
1824 
1825   if (trace(1))
1826     num_read++;
1827 
1828   return false;
1829   } // end of ReadBlock
1830 
1831 /***********************************************************************/
1832 /*  WriteBlock: Write back current column values for one block.        */
1833 /*  Note: there is nothing to do because we are working directly into  */
1834 /*  the mapped file, except when checking for Update but in this case  */
1835 /*  we do not want to write back the modifications either.             */
1836 /***********************************************************************/
WriteBlock(PGLOBAL,PVCTCOL colp)1837 bool VCMFAM::WriteBlock(PGLOBAL, PVCTCOL colp __attribute__((unused)))
1838   {
1839 #if defined(_DEBUG)
1840   char *mempos;
1841   int   i = colp->Index - 1;
1842   int   n = Nrec * colp->Clen;
1843 
1844   /*********************************************************************/
1845   /*  Calculate the offset and size of the block to write.             */
1846   /*********************************************************************/
1847   mempos = Memcol[i] + n * CurBlk;
1848 
1849   if (trace(1))
1850     htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n",
1851           Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk);
1852 
1853 #endif // _DEBUG
1854 
1855   return false;
1856   } // end of WriteBlock
1857 
1858 /* -------------------------- Class VECFAM --------------------------- */
1859 
1860 /***********************************************************************/
1861 /*  Implementation of the VECFAM class.                                */
1862 /***********************************************************************/
VECFAM(PVCTDEF tdp)1863 VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1864   {
1865   Streams = NULL;
1866   To_Fbs = NULL;
1867   To_Bufs = NULL;
1868   Split = true;
1869   Block = Last = -1;
1870   InitUpdate = false;
1871   } // end of VECFAM standard constructor
1872 
VECFAM(PVECFAM txfp)1873 VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp)
1874   {
1875   Streams = txfp->Streams;
1876   To_Fbs = txfp->To_Fbs;
1877   Clens = txfp->Clens;
1878   To_Bufs = txfp->To_Bufs;
1879   InitUpdate = txfp->InitUpdate;
1880   } // end of VECFAM copy constructor
1881 
1882 /***********************************************************************/
1883 /*  VEC Access Method opening routine.                                 */
1884 /*  New method now that this routine is called recursively (last table */
1885 /*  first in reverse order): index blocks are immediately linked to    */
1886 /*  join block of next table if it exists or else are discarted.       */
1887 /***********************************************************************/
OpenTableFile(PGLOBAL g)1888 bool VECFAM::OpenTableFile(PGLOBAL g)
1889   {
1890   char    opmode[4];
1891   int     i;
1892   bool    b= false;
1893   PCOLDEF cdp;
1894   PVCTCOL cp;
1895   MODE    mode = Tdbp->GetMode();
1896   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1897 
1898   /*********************************************************************/
1899   /*  Call Cardinality to set Block and Last values in case it was not */
1900   /*  already called (this happens indeed in test xmode)               */
1901   /*********************************************************************/
1902   Cardinality(g);
1903 
1904   /*********************************************************************/
1905   /*  Open according to input/output mode required.                    */
1906   /*********************************************************************/
1907   switch (mode) {
1908     case MODE_READ:
1909       strcpy(opmode, "rb");
1910       break;
1911     case MODE_DELETE:
1912       if (!Tdbp->GetNext()) {
1913         // Store the number of deleted lines
1914         DelRows = Cardinality(g);
1915 
1916         // This will delete the whole file
1917         strcpy(opmode, "wb");
1918 
1919         // This will stop the process by causing GetProgMax to return 0.
1920         ResetTableSize(g, 0, Nrec);
1921         break;
1922         } // endif filter
1923 
1924       // Selective delete, pass thru
1925       /* fall through */
1926     case MODE_UPDATE:
1927       UseTemp = Tdbp->IsUsingTemp(g);
1928       strcpy(opmode, (UseTemp) ? "rb": "r+b");
1929       break;
1930     case MODE_INSERT:
1931       strcpy(opmode, "ab");
1932       break;
1933     default:
1934       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
1935       return true;
1936     } // endswitch Mode
1937 
1938   /*********************************************************************/
1939   /*  Initialize the array of file structures.                         */
1940   /*********************************************************************/
1941   if (!Colfn) {
1942     // Prepare the column file name pattern and set Ncol
1943     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1944     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
1945     } // endif Colfn
1946 
1947   Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
1948   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
1949 
1950   for (i = 0; i < Ncol; i++) {
1951     Streams[i] = NULL;
1952     To_Fbs[i] = NULL;
1953     } // endif i
1954 
1955   /*********************************************************************/
1956   /*  Open the files corresponding to columns used in the query.       */
1957   /*********************************************************************/
1958   if (mode == MODE_INSERT || mode == MODE_DELETE) {
1959     // All columns must be written or deleted
1960     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
1961       if (OpenColumnFile(g, opmode, i))
1962         return true;
1963 
1964     // Check for void table or missing columns
1965     for (b = !Streams[0], i = 1; i < Ncol; i++)
1966       if (b != !Streams[i])
1967         return true;
1968 
1969   } else {
1970     /*******************************************************************/
1971     /*  Open the files corresponding to updated columns of the query.  */
1972     /*******************************************************************/
1973     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
1974          cp = (PVCTCOL)cp->Next)
1975       if (OpenColumnFile(g, opmode, cp->Index - 1))
1976         return true;
1977 
1978     // Open in read only mode the used columns not already open
1979     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1980       if (!cp->IsSpecial() && !Streams[cp->Index - 1])
1981         if (OpenColumnFile(g, "rb", cp->Index - 1))
1982           return true;
1983 
1984     // Check for void table or missing columns
1985     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
1986                 cp = (PVCTCOL)cp->Next)
1987       if (!cp->IsSpecial()) {
1988         if (!i++)
1989           b = !Streams[cp->Index - 1];
1990         else if (b != !Streams[cp->Index - 1])
1991           return true;
1992 
1993         } // endif Special
1994 
1995   } // endif mode
1996 
1997   /*********************************************************************/
1998   /*  Allocate the table and column block buffer.                      */
1999   /*********************************************************************/
2000   return (b) ? false : AllocateBuffer(g);
2001   } // end of OpenTableFile
2002 
2003 /***********************************************************************/
2004 /*  Open the file corresponding to one column.                         */
2005 /***********************************************************************/
OpenColumnFile(PGLOBAL g,PCSZ opmode,int i)2006 bool VECFAM::OpenColumnFile(PGLOBAL g, PCSZ opmode, int i)
2007   {
2008   char    filename[_MAX_PATH];
2009   PDBUSER dup = PlgGetUser(g);
2010 
2011   sprintf(filename, Colfn, i+1);
2012 
2013   if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
2014     if (trace(1))
2015       htrc("%s\n", g->Message);
2016 
2017     return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
2018             ? PushWarning(g, Tdbp) : true;
2019     } // endif Streams
2020 
2021   if (trace(1))
2022     htrc("File %s is open in mode %s\n", filename, opmode);
2023 
2024   To_Fbs[i] = dup->Openlist;       // Keep track of File blocks
2025   return false;
2026   } // end of OpenColumnFile
2027 
2028 /***********************************************************************/
2029 /*  Allocate the block buffers for columns used in the query.          */
2030 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2031 bool VECFAM::AllocateBuffer(PGLOBAL g)
2032   {
2033   int     i;
2034   PVCTCOL cp;
2035   PCOLDEF cdp;
2036   PTDBVCT tdbp = (PTDBVCT)Tdbp;
2037   MODE    mode = tdbp->GetMode();
2038   PDOSDEF defp = (PDOSDEF)tdbp->GetDef();
2039 
2040   if (mode != MODE_READ) {
2041     // Allocate what is needed by all modes except Read
2042     T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
2043     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2044 
2045     // Give default values
2046     for (i = 0; i < Ncol; i++) {
2047       T_Streams[i] = Streams[i];
2048       Clens[i] = 0;
2049       } // endfor i
2050 
2051     } // endif mode
2052 
2053   if (mode == MODE_INSERT) {
2054     bool    chk = PlgGetUser(g)->Check & CHK_TYPE;
2055 
2056     To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*));
2057     cdp = defp->GetCols();
2058 
2059     for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2060       Clens[i] = cdp->GetClen();
2061       To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]);
2062 
2063       if (cdp->GetType() == TYPE_STRING)
2064         memset(To_Bufs[i], ' ', Nrec * Clens[i]);
2065       else
2066         memset(To_Bufs[i],   0, Nrec * Clens[i]);
2067 
2068       } // endfor cdp
2069 
2070     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2071       cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1],
2072                               cp->Buf_Type, Nrec, cp->Format.Length,
2073                               cp->Format.Prec, chk, true, cp->IsUnsigned());
2074 
2075     return InitInsert(g);
2076   } else {
2077     if (UseTemp || mode == MODE_DELETE) {
2078       // Allocate all that is needed to move lines and make Temp
2079       if (UseTemp) {
2080         Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2081         strcpy(Tempat, Colfn);
2082         PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
2083         strcat(PlugRemoveType(Tempat, Tempat), ".t");
2084         T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2085         } // endif UseTemp
2086 
2087       if (UseTemp)
2088         for (i = 0; i < Ncol; i++) {
2089           T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL;
2090           T_Fbs[i] = NULL;
2091           } // endfor i
2092 
2093       if (mode == MODE_DELETE) {  // All columns are moved
2094         cdp = defp->GetCols();
2095 
2096         for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2097           Clens[i] = cdp->GetClen();
2098           Buflen = MY_MAX(Buflen, cdp->GetClen());
2099           } // endfor cdp
2100 
2101       } else {  // Mode Update, only some columns are updated
2102         for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) {
2103           i = cp->Index -1;
2104 
2105           if (UseTemp)
2106             T_Streams[i] = NULL;   // Mark the streams to open
2107 
2108           Clens[i] = cp->Clen;
2109           Buflen = MY_MAX(Buflen, cp->Clen);
2110           } // endfor cp
2111 
2112         InitUpdate = true;         // To be initialized
2113       } // endif mode
2114 
2115       To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec);
2116       } // endif mode
2117 
2118     // Finally allocate column buffers for all modes
2119     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2120       if (!cp->IsSpecial())            // Not a pseudo column
2121           cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
2122                                   cp->Format.Length, cp->Format.Prec,
2123 						                      true, true, cp->IsUnsigned());
2124 
2125   } // endif mode
2126 
2127   return false;
2128   } // end of AllocateBuffer
2129 
2130 /***********************************************************************/
2131 /*  Do initial action when inserting.                                  */
2132 /***********************************************************************/
InitInsert(PGLOBAL)2133 bool VECFAM::InitInsert(PGLOBAL)
2134   {
2135   // We come here in MODE_INSERT only
2136   CurBlk = 0;
2137   CurNum = 0;
2138   AddBlock = true;
2139   return false;
2140   } // end of InitInsert
2141 
2142 /***********************************************************************/
2143 /*  Reset buffer access according to indexing and to mode.             */
2144 /*  >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
2145 /***********************************************************************/
ResetBuffer(PGLOBAL g)2146 void VECFAM::ResetBuffer(PGLOBAL g)
2147   {
2148   /*********************************************************************/
2149   /*  If access is random, performances can be much better when the    */
2150   /*  reads are done on only one row, except for small tables that can */
2151   /*  be entirely read in one block. If the index is just used as a    */
2152   /*  bitmap filter, as for Update or Delete, reading will be          */
2153   /*  sequential and we better keep block reading.                     */
2154   /*********************************************************************/
2155   if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) {
2156     Nrec = 1;                       // Better for random access
2157     Rbuf = 0;
2158     OldBlk = -2;                    // Has no meaning anymore
2159     Block = Tdbp->Cardinality(g);   // Blocks are one line now
2160     Last = 1;                       // Probably unuseful
2161     } // endif Mode
2162 
2163   } // end of ResetBuffer
2164 
2165 /***********************************************************************/
2166 /*  Data Base write routine for VCT access method.                     */
2167 /***********************************************************************/
WriteBuffer(PGLOBAL g)2168 int VECFAM::WriteBuffer(PGLOBAL g)
2169   {
2170   if (trace(1))
2171     htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
2172           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
2173 
2174   if (Tdbp->GetMode() == MODE_INSERT) {
2175     if (Closing || ++CurNum == Nrec) {
2176       // Here we must add a new blocks to the files
2177       int    i;
2178       size_t n = (size_t)CurNum;
2179 
2180       for (i = 0; i < Ncol; i++)
2181         if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) {
2182           sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
2183           return RC_FX;
2184           } // endif
2185 
2186       if (!Closing) {
2187         CurBlk++;
2188         CurNum = 0;
2189         } // endif Closing
2190 
2191       } // endif Closing || CurNum
2192 
2193   } else              // Mode Update
2194     // Writing updates being done in ReadDB we do initialization only.
2195     if (InitUpdate) {
2196       if (OpenTempFile(g))
2197         return RC_FX;
2198 
2199       InitUpdate = false;                 // Done
2200       } // endif InitUpdate
2201 
2202   return RC_OK;
2203   } // end of WriteBuffer
2204 
2205 /***********************************************************************/
2206 /*  Data Base delete line routine for split vertical access methods.   */
2207 /*  Note: lines are moved directly in the files (ooops...)             */
2208 /*  Using temp file depends on the Check setting, false by default.    */
2209 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2210 int VECFAM::DeleteRecords(PGLOBAL g, int irc)
2211   {
2212   if (trace(1))
2213     htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
2214           irc, UseTemp, Fpos, Tpos, Spos);
2215 
2216   if (irc != RC_OK) {
2217     /*******************************************************************/
2218     /*  EOF: position Fpos at the end-of-file position.                */
2219     /*******************************************************************/
2220     Fpos = Cardinality(g);
2221 
2222     if (trace(1))
2223       htrc("Fpos placed at file end=%d\n", Fpos);
2224 
2225   } else     // Fpos is the Deleted line position
2226     Fpos = CurBlk * Nrec + CurNum;
2227 
2228   if (Tpos == Spos) {
2229     // First line to delete
2230     if (UseTemp) {
2231       /*****************************************************************/
2232       /*  Open the temporary files, Spos is at the beginning of file.  */
2233       /*****************************************************************/
2234       if (OpenTempFile(g))
2235         return RC_FX;
2236 
2237     } else
2238       /*****************************************************************/
2239       /*  Move of eventual preceding lines is not required here.      */
2240       /*  Set the future Tpos, and give Spos a value to block copying. */
2241       /*****************************************************************/
2242       Spos = Tpos = Fpos;
2243 
2244     } // endif Tpos == Spos
2245 
2246   /*********************************************************************/
2247   /*  Move any intermediate lines.                                     */
2248   /*********************************************************************/
2249   if (MoveIntermediateLines(g))
2250     return RC_FX;
2251 
2252   if (irc == RC_OK) {
2253 #ifdef _DEBUG
2254     assert(Spos == Fpos);
2255 #endif
2256     Spos++;          // New start position is on next line
2257 
2258     if (trace(1))
2259       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
2260 
2261   } else {
2262     /*******************************************************************/
2263     /*  Last call after EOF has been reached.                          */
2264     /*******************************************************************/
2265     if (!UseTemp) {
2266       /*****************************************************************/
2267       /* Because the chsize functionality is only accessible with a    */
2268       /* system call we must close the files and reopen them with the  */
2269       /* open function (_fopen for MS??) this is still to be checked   */
2270       /* for compatibility with other OS's.                            */
2271       /*****************************************************************/
2272       char filename[_MAX_PATH];
2273       int  h;                            // File handle, return code
2274 
2275       for (int i = 0; i < Ncol; i++) {
2276         sprintf(filename, Colfn, i + 1);
2277         /*rc =*/ PlugCloseFile(g, To_Fbs[i]);
2278 
2279         if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
2280           return RC_FX;
2281 
2282         /***************************************************************/
2283         /*  Remove extra records.                                      */
2284         /***************************************************************/
2285 #if defined(UNIX)
2286         if (ftruncate(h, (off_t)(Tpos * Clens[i]))) {
2287           sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2288           close(h);
2289           return RC_FX;
2290           } // endif
2291 #else
2292         if (chsize(h, Tpos * Clens[i])) {
2293           sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
2294           close(h);
2295           return RC_FX;
2296           } // endif
2297 #endif
2298 
2299         close(h);
2300 
2301         if (trace(1))
2302           htrc("done, h=%d irc=%d\n", h, irc);
2303 
2304         } // endfor i
2305 
2306     } else        // UseTemp
2307       // Ok, now delete old files and rename new temp files
2308       if (RenameTempFile(g) == RC_FX)
2309         return RC_FX;
2310 
2311     // Reset these values for TDBVCT::MakeBlockValues
2312     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2313     Last = (Tpos + Nrec - 1) % Nrec + 1;
2314 
2315     if (ResetTableSize(g, Block, Last))
2316       return RC_FX;
2317 
2318   } // endif irc
2319 
2320   return RC_OK;                                      // All is correct
2321   } // end of DeleteRecords
2322 
2323 /***********************************************************************/
2324 /*  Open temporary files used while updating or deleting.              */
2325 /*  Note: the files not updated have been given a T_Stream value of 1. */
2326 /***********************************************************************/
OpenTempFile(PGLOBAL g)2327 bool VECFAM::OpenTempFile(PGLOBAL g)
2328   {
2329   char tempname[_MAX_PATH];
2330 
2331   for (int i = 0; i < Ncol; i++)
2332     if (!T_Streams[i]) {
2333       /*****************************************************************/
2334       /*  Open the temporary file, Spos is at the beginning of file.   */
2335       /*****************************************************************/
2336       sprintf(tempname, Tempat, i+1);
2337 
2338       if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
2339         if (trace(1))
2340           htrc("%s\n", g->Message);
2341 
2342         return true;
2343       } else
2344         T_Fbs[i] = PlgGetUser(g)->Openlist;
2345 
2346     } else       // This is a column that is not updated
2347       T_Streams[i] = NULL;        // For RenameTempFile
2348 
2349   return false;
2350   } // end of OpenTempFile
2351 
2352 /***********************************************************************/
2353 /*  Move intermediate updated lines before writing blocks.             */
2354 /***********************************************************************/
MoveLines(PGLOBAL g)2355 bool VECFAM::MoveLines(PGLOBAL g)
2356   {
2357   if (UseTemp && !InitUpdate) {    // Don't do it in check pass
2358     Fpos = OldBlk * Nrec;
2359 
2360     if (MoveIntermediateLines(g)) {
2361       Closing = true;           // ???
2362       return true;
2363       } // endif UseTemp
2364 
2365 //  Spos = Fpos + Nrec;
2366     } // endif UseTemp
2367   return false;
2368 
2369   } // end of MoveLines
2370 
2371 /***********************************************************************/
2372 /*  Move intermediate deleted or updated lines.                        */
2373 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool *)2374 bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *)
2375   {
2376   int    i, n;
2377   bool   b = false;
2378   size_t req, len;
2379 
2380   for (n = Fpos - Spos; n > 0; n -= Nrec) {
2381     /*******************************************************************/
2382     /*  Non consecutive line to delete. Move intermediate lines.       */
2383     /*******************************************************************/
2384     req = (size_t)MY_MIN(n, Nrec);
2385 
2386     for (i = 0; i < Ncol; i++) {
2387       if (!T_Streams[i])
2388         continue;             // Non updated column
2389 
2390       if (!UseTemp || !b)
2391         if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) {
2392           sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
2393           return true;
2394           } // endif
2395 
2396       len = fread(To_Buf, Clens[i], req, Streams[i]);
2397 
2398       if (trace(1))
2399         htrc("after read req=%d len=%d\n", req, len);
2400 
2401       if (len != req) {
2402         sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
2403         return true;
2404         } // endif len
2405 
2406       if (!UseTemp)
2407         if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) {
2408           sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
2409           return true;
2410           } // endif
2411 
2412       if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
2413         sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
2414         return true;
2415         } // endif
2416 
2417       if (trace(1))
2418         htrc("after write pos=%d\n", ftell(Streams[i]));
2419 
2420       } // endfor i
2421 
2422     Tpos += (int)req;
2423     Spos += (int)req;
2424 
2425     if (trace(1))
2426       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
2427 
2428     b = true;
2429     } // endfor n
2430 
2431   return false;
2432   } // end of MoveIntermediate Lines
2433 
2434 /***********************************************************************/
2435 /*  Delete the old files and rename the new temporary files.           */
2436 /***********************************************************************/
RenameTempFile(PGLOBAL g)2437 int VECFAM::RenameTempFile(PGLOBAL g)
2438   {
2439   char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
2440   int   rc = RC_OK;
2441 
2442   // Close all files.
2443   // This loop is necessary because, in case of join,
2444   // the table files can have been open several times.
2445   for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
2446     rc = PlugCloseFile(g, fb);
2447 
2448   for (int i = 0; i < Ncol && rc == RC_OK; i++) {
2449     if (!T_Fbs[i])
2450       continue;
2451 
2452     tempname = (char*)T_Fbs[i]->Fname;
2453 
2454     if (!Abort) {
2455       sprintf(filename, Colfn, i+1);
2456       PlugSetPath(filename, filename, Tdbp->GetPath());
2457       strcat(PlugRemoveType(filetemp, filename), ".ttt");
2458       remove(filetemp);   // May still be there from previous error
2459 
2460       if (rename(filename, filetemp)) {    // Save file for security
2461         snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2462                 filename, filetemp, strerror(errno));
2463         rc = RC_FX;
2464       } else if (rename(tempname, filename)) {
2465         snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2466                 tempname, filename, strerror(errno));
2467         rc = rename(filetemp, filename);   // Restore saved file
2468         rc = RC_FX;
2469       } else if (remove(filetemp)) {
2470         sprintf(g->Message, MSG(REMOVE_ERROR),
2471                 filetemp, strerror(errno));
2472         rc = RC_INFO;                      // Acceptable
2473       } // endif's
2474 
2475     } else
2476       remove(tempname);
2477 
2478     } // endfor i
2479 
2480   return rc;
2481   } // end of RenameTempFile
2482 
2483 /***********************************************************************/
2484 /*  Data Base close routine for VEC access method.                     */
2485 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)2486 void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
2487   {
2488   int  rc = 0, wrc = RC_OK;
2489   MODE mode = Tdbp->GetMode();
2490 
2491   Abort = abort;
2492 
2493   if (mode == MODE_INSERT) {
2494     if (Closing)
2495       wrc = RC_FX;                  // Last write was in error
2496     else
2497       if (CurNum) {
2498         // Some more inserted lines remain to be written
2499         Last += (CurBlk * Nrec + CurNum -1);
2500         Block += (Last / Nrec);
2501         Last = Last % Nrec + 1;
2502         Closing = true;
2503         wrc = WriteBuffer(g);
2504       } else {
2505         Block += CurBlk;
2506         wrc = RC_OK;
2507       } // endif CurNum
2508 
2509     if (wrc != RC_FX)
2510       rc = ResetTableSize(g, Block, Last);
2511     else
2512 			throw 44;
2513 
2514   } else if (mode == MODE_UPDATE) {
2515     if (UseTemp && !InitUpdate && !Abort) {
2516       // Write any intermediate lines to temp file
2517       Fpos = OldBlk * Nrec;
2518       Abort = MoveIntermediateLines(g) != RC_OK;
2519 //    Spos = Fpos + Nrec;
2520       } // endif UseTemp
2521 
2522     // Write back to file any pending modifications
2523     if (wrc == RC_OK)
2524       for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
2525                    colp; colp = (PVCTCOL)colp->Next)
2526         colp->WriteBlock(g);
2527 
2528     if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
2529       // Write any intermediate lines to temp file
2530       Fpos = (Block - 1) * Nrec + Last;
2531       Abort = MoveIntermediateLines(g) != RC_OK;
2532       } // endif UseTemp
2533 
2534   } // endif's mode
2535 
2536   if (UseTemp && !InitUpdate) {
2537     // If they are errors, leave files unchanged
2538     rc = RenameTempFile(g);
2539 
2540   } else if (Streams)
2541     for (int i = 0; i < Ncol; i++)
2542       if (Streams[i]) {
2543         rc = PlugCloseFile(g, To_Fbs[i]);
2544         Streams[i] = NULL;
2545         To_Fbs[i] = NULL;
2546         } // endif Streams
2547 
2548   if (trace(1))
2549     htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc);
2550 
2551   } // end of CloseTableFile
2552 
2553 /***********************************************************************/
2554 /*  ReadBlock: Read column values from current block.                  */
2555 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)2556 bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
2557   {
2558   int     i, len;
2559   size_t  n;
2560 
2561   /*********************************************************************/
2562   /*  Calculate the offset and size of the block to read.              */
2563   /*********************************************************************/
2564   len = Nrec * colp->Clen * CurBlk;
2565   i = colp->Index - 1;
2566 
2567   if (trace(1))
2568     htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n",
2569           len, i, Nrec, colp->Deplac, Lrecl, CurBlk);
2570 
2571   if (fseek(Streams[i], len, SEEK_SET)) {
2572     sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2573     return true;
2574     } // endif
2575 
2576   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
2577                                         (size_t)Nrec, Streams[i]);
2578 
2579   if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) {
2580     char fn[_MAX_PATH];
2581 
2582     sprintf(fn, Colfn, colp->Index);
2583 #if defined(_WIN32)
2584     if (feof(Streams[i]))
2585 #else   // !_WIN32
2586     if (errno == NO_ERROR)
2587 #endif  // !_WIN32
2588       sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
2589     else
2590       sprintf(g->Message, MSG(READ_ERROR),
2591               fn, strerror(errno));
2592 
2593     if (trace(1))
2594       htrc(" Read error: %s\n", g->Message);
2595 
2596     return true;
2597     } // endif
2598 
2599   if (trace(1))
2600     num_read++;
2601 
2602   return false;
2603   } // end of ReadBlock
2604 
2605 /***********************************************************************/
2606 /*  WriteBlock: Write back current column values for one block.        */
2607 /*  Note: the test of Status is meant to prevent physical writing of   */
2608 /*  the block during the checking loop in mode Update. It is set to    */
2609 /*  BUF_EMPTY when reopening the table between the two loops.          */
2610 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)2611 bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
2612   {
2613   int   i, len;
2614   size_t n;
2615 
2616   /*********************************************************************/
2617   /*  Calculate the offset and size of the block to write.             */
2618   /*********************************************************************/
2619   len = Nrec * colp->Clen * colp->ColBlk;
2620   i = colp->Index - 1;
2621 
2622   if (trace(1))
2623     htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
2624           Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
2625 
2626   if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp)
2627     if (fseek(T_Streams[i], len, SEEK_SET)) {
2628       sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2629       return true;
2630       } // endif
2631 
2632   // Here Nrec was changed to CurNum in mode Insert,
2633   // this is the true number of records to write,
2634   // this also avoid writing garbage in the file for true vector tables.
2635   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum
2636     : (colp->ColBlk == Block - 1) ? Last : Nrec;
2637 
2638   if (n != fwrite(colp->Blk->GetValPointer(),
2639                             (size_t)colp->Clen, n, T_Streams[i])) {
2640     char fn[_MAX_PATH];
2641 
2642     sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
2643     sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
2644 
2645     if (trace(1))
2646       htrc("Write error: %s\n", strerror(errno));
2647 
2648     return true;
2649   } else
2650     Spos = Fpos + n;
2651 
2652 #if defined(UNIX)
2653   fflush(Streams[i]); //NGC
2654 #endif
2655   return false;
2656   } // end of WriteBlock
2657 
2658 /* -------------------------- Class VMPFAM --------------------------- */
2659 
2660 /***********************************************************************/
2661 /*  Implementation of the VMPFAM class.                                */
2662 /***********************************************************************/
VMPFAM(PVCTDEF tdp)2663 VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp)
2664   {
2665   To_Fbs = NULL;
2666   Split = true;
2667   Block = Last = -1;
2668   } // end of VMPFAM standard constructor
2669 
VMPFAM(PVMPFAM txfp)2670 VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
2671   {
2672   To_Fbs = txfp->To_Fbs;
2673   } // end of VMPFAM copy constructor
2674 
2675 /***********************************************************************/
2676 /*  VCT Access Method opening routine.                                 */
2677 /*  New method now that this routine is called recursively (last table */
2678 /*  first in reverse order): index blocks are immediately linked to    */
2679 /*  join block of next table if it exists or else are discarted.       */
2680 /***********************************************************************/
OpenTableFile(PGLOBAL g)2681 bool VMPFAM::OpenTableFile(PGLOBAL g)
2682   {
2683   int     i;
2684   bool    b = false;
2685   MODE    mode = Tdbp->GetMode();
2686   PCOLDEF cdp;
2687   PVCTCOL cp;
2688   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
2689 
2690   if (mode == MODE_DELETE && !Tdbp->GetNext()) {
2691     DelRows = Cardinality(g);
2692 
2693     // This will stop the process by causing GetProgMax to return 0.
2694     ResetTableSize(g, 0, Nrec);
2695   } else
2696     Cardinality(g);        // See comment in VECFAM::OpenTbleFile
2697 
2698 
2699   /*********************************************************************/
2700   /*  Prepare the filename pattern for column files and set Ncol.      */
2701   /*********************************************************************/
2702   if (!Colfn) {
2703     // Prepare the column file name pattern
2704     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2705     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
2706     } // endif Colfn
2707 
2708   /*********************************************************************/
2709   /*  Initialize the array of file structures.                         */
2710   /*********************************************************************/
2711   Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *));
2712   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2713 
2714   for (i = 0; i < Ncol; i++) {
2715     Memcol[i] = NULL;
2716     To_Fbs[i] = NULL;
2717     } // endif i
2718 
2719   /*********************************************************************/
2720   /*  Open the files corresponding to columns used in the query.       */
2721   /*********************************************************************/
2722   if (mode == MODE_DELETE) {
2723     // All columns are used in Delete mode
2724     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
2725       if (MapColumnFile(g, mode, i))
2726         return true;
2727 
2728   } else {
2729     /*******************************************************************/
2730     /*  Open the files corresponding to updated columns of the query.  */
2731     /*******************************************************************/
2732     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
2733          cp = (PVCTCOL)cp->Next)
2734       if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1))
2735         return true;
2736 
2737     /*******************************************************************/
2738     /* Open other non already open used columns (except pseudos)      */
2739     /*******************************************************************/
2740     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2741       if (!cp->IsSpecial() && !Memcol[cp->Index - 1])
2742         if (MapColumnFile(g, MODE_READ, cp->Index - 1))
2743           return true;
2744 
2745     // Check for void table or missing columns
2746     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
2747                 cp = (PVCTCOL)cp->Next)
2748       if (!cp->IsSpecial()) {
2749         if (!i++)
2750           b = !Memcol[cp->Index - 1];
2751         else if (b != !Memcol[cp->Index - 1])
2752           return true;
2753 
2754         } // endif Special
2755 
2756   } // endif mode
2757 
2758   /*********************************************************************/
2759   /*  Allocate the table and column block buffer.                      */
2760   /*********************************************************************/
2761   return (b) ? false : AllocateBuffer(g);
2762   } // end of OpenTableFile
2763 
2764 /***********************************************************************/
2765 /*  Open the file corresponding to one column.                         */
2766 /***********************************************************************/
MapColumnFile(PGLOBAL g,MODE mode,int i)2767 bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
2768   {
2769   char    filename[_MAX_PATH];
2770   size_t  len;
2771   HANDLE  hFile;
2772   MEMMAP  mm;
2773   PFBLOCK fp;
2774   PDBUSER dup = PlgGetUser(g);
2775 
2776   sprintf(filename, Colfn, i+1);
2777 
2778   /*********************************************************************/
2779   /*  The whole file will be mapped so we can use it as                */
2780   /*  if it were entirely read into virtual memory.                    */
2781   /*  Firstly we check whether this file have been already mapped.     */
2782   /*********************************************************************/
2783   if (mode == MODE_READ) {
2784     for (fp = dup->Openlist; fp; fp = fp->Next)
2785       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
2786                      && fp->Count && fp->Mode == mode)
2787         break;
2788 
2789     if (trace(1))
2790       htrc("Mapping file, fp=%p\n", fp);
2791 
2792   } else
2793     fp = NULL;
2794 
2795   if (fp) {
2796     /*******************************************************************/
2797     /*  File already mapped. Just increment use count and get pointer. */
2798     /*******************************************************************/
2799     fp->Count++;
2800     Memcol[i] = fp->Memory;
2801     len = fp->Length;
2802   } else {
2803     /*******************************************************************/
2804     /*  Create the mapping file object.                                */
2805     /*******************************************************************/
2806     hFile = CreateFileMap(g, filename, &mm, mode, DelRows);
2807 
2808     if (hFile == INVALID_HANDLE_VALUE) {
2809       DWORD rc = GetLastError();
2810 
2811       if (!(*g->Message))
2812         sprintf(g->Message, MSG(OPEN_MODE_ERROR),
2813                 "map", (int) rc, filename);
2814       if (trace(1))
2815         htrc("%s\n", g->Message);
2816 
2817       return (mode == MODE_READ && rc == ENOENT)
2818               ? PushWarning(g, Tdbp) : true;
2819       } // endif hFile
2820 
2821     /*****************************************************************/
2822     /*  Get the file size (assuming file is smaller than 4 GB)       */
2823     /*****************************************************************/
2824 		len = (size_t)mm.lenL;
2825 
2826 		if (mm.lenH)
2827 			len += ((size_t)mm.lenH * 0x000000001LL);
2828 
2829 		Memcol[i] = (char *)mm.memory;
2830 
2831     if (!len) {             // Empty or deleted file
2832       CloseFileHandle(hFile);
2833       ResetTableSize(g, 0, Nrec);
2834       return false;
2835       } // endif len
2836 
2837     if (!Memcol[i]) {
2838       CloseFileHandle(hFile);
2839       sprintf(g->Message, MSG(MAP_VIEW_ERROR),
2840                           filename, GetLastError());
2841       return true;
2842       } // endif Memory
2843 
2844     if (mode != MODE_DELETE) {
2845       CloseFileHandle(hFile);                    // Not used anymore
2846       hFile = INVALID_HANDLE_VALUE;              // For Fblock
2847       } // endif Mode
2848 
2849     /*******************************************************************/
2850     /*  Link a Fblock. This make possible to reuse already opened maps */
2851     /*  and also to automatically unmap them in case of error g->jump. */
2852     /*  Note: block can already exist for previously closed file.      */
2853     /*******************************************************************/
2854     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
2855     fp->Type = TYPE_FB_MAP;
2856     fp->Fname = PlugDup(g, filename);
2857     fp->Next = dup->Openlist;
2858     dup->Openlist = fp;
2859     fp->Count = 1;
2860     fp->Length = len;
2861     fp->Memory = Memcol[i];
2862     fp->Mode = mode;
2863     fp->File = NULL;
2864     fp->Handle = hFile;                        // Used for Delete
2865   } // endif fp
2866 
2867   To_Fbs[i] = fp;                              // Useful when closing
2868 
2869   if (trace(1))
2870     htrc("fp=%p count=%d MapView=%p len=%d\n",
2871           fp, fp->Count, Memcol[i], len);
2872 
2873   return false;
2874   } // end of MapColumnFile
2875 
2876 /***********************************************************************/
2877 /*  Allocate the block buffers for columns used in the query.          */
2878 /*  Give a dummy value (1) to prevent allocating the value block.     */
2879 /*  It will be set pointing into the memory map of the file.           */
2880 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2881 bool VMPFAM::AllocateBuffer(PGLOBAL g)
2882   {
2883   PVCTCOL cp;
2884 
2885   if (Tdbp->GetMode() == MODE_DELETE) {
2886     PCOLDEF cdp = Tdbp->GetDef()->GetCols();
2887 
2888     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2889 
2890     for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext())
2891       Clens[i] = cdp->GetClen();
2892 
2893     } // endif mode
2894 
2895   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2896     if (!cp->IsSpecial()) {            // Not a pseudo column
2897       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
2898                               cp->Format.Length, cp->Format.Prec,
2899 				                      true, true, cp->IsUnsigned());
2900       cp->AddStatus(BUF_MAPPED);
2901       } // endif IsSpecial
2902 
2903   return false;
2904   } // end of AllocateBuffer
2905 
2906 /***********************************************************************/
2907 /*  Data Base delete line routine for VMP access method.               */
2908 /*  Lines between deleted lines are moved in the mapfile view.         */
2909 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2910 int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
2911   {
2912   int  i;
2913   int m, n;
2914 
2915   if (trace(1))
2916     htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
2917                         irc, To_Buf, Tpos, Spos);
2918 
2919   if (irc != RC_OK) {
2920     /*******************************************************************/
2921     /*  EOF: position Fpos at the top of map position.                 */
2922     /*******************************************************************/
2923     Fpos = (Block - 1) * Nrec + Last;
2924 
2925     if (trace(1))
2926       htrc("Fpos placed at file top=%p\n", Fpos);
2927 
2928   } else     // Fpos is the Deleted line position
2929     Fpos = CurBlk * Nrec + CurNum;
2930 
2931   if (Tpos == Spos) {
2932     /*******************************************************************/
2933     /*  First line to delete. Move of eventual preceding lines is     */
2934     /*  not required here, just setting of future Spos and Tpos.       */
2935     /*******************************************************************/
2936     Tpos = Fpos;                               // Spos is set below
2937   } else if ((n = Fpos - Spos) > 0) {
2938     /*******************************************************************/
2939     /*  Non consecutive line to delete. Move intermediate lines.       */
2940     /*******************************************************************/
2941     for (i = 0; i < Ncol; i++) {
2942       m = Clens[i];
2943       memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n);
2944       } // endif i
2945 
2946     Tpos += n;
2947 
2948     if (trace(1))
2949       htrc("move %d bytes\n", n);
2950 
2951     } // endif n
2952 
2953   if (irc == RC_OK) {
2954     Spos = Fpos + 1;                               // New start position
2955 
2956     if (trace(1))
2957       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
2958 
2959   } else {
2960     /*******************************************************************/
2961     /*  Last call after EOF has been reached.                          */
2962     /*  We must firstly Unmap the view and use the saved file handle   */
2963     /*  to put an EOF at the end of the copied part of the file.       */
2964     /*******************************************************************/
2965     PFBLOCK fp;
2966 
2967     /*******************************************************************/
2968     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
2969     /*******************************************************************/
2970 //  Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2971 //  Last = (Tpos + Nrec - 1) % Nrec + 1;
2972 
2973     for (i = 0; i < Ncol; i++) {
2974       fp = To_Fbs[i];
2975       CloseMemMap(fp->Memory, (size_t)fp->Length);
2976       fp->Count = 0;                             // Avoid doing it twice
2977 
2978       /*****************************************************************/
2979       /*  Remove extra records.                                        */
2980       /*****************************************************************/
2981       n = Tpos * Clens[i];
2982 
2983 #if defined(_WIN32)
2984       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
2985 
2986       if (drc == 0xFFFFFFFF) {
2987         sprintf(g->Message, MSG(FUNCTION_ERROR),
2988                             "SetFilePointer", GetLastError());
2989         CloseHandle(fp->Handle);
2990         return RC_FX;
2991         } // endif
2992 
2993       if (trace(1))
2994         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
2995 
2996       if (!SetEndOfFile(fp->Handle)) {
2997         sprintf(g->Message, MSG(FUNCTION_ERROR),
2998                             "SetEndOfFile", GetLastError());
2999         CloseHandle(fp->Handle);
3000         return RC_FX;
3001         } // endif
3002 
3003       CloseHandle(fp->Handle);
3004 #else    // UNIX
3005       if (ftruncate(fp->Handle, (off_t)n)) {
3006         sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3007         close(fp->Handle);
3008         return RC_FX;
3009         } // endif
3010 
3011       close(fp->Handle);
3012 #endif   // UNIX
3013       } // endfor i
3014 
3015   } // endif irc
3016 
3017   return RC_OK;                                      // All is correct
3018   } // end of DeleteRecords
3019 
3020 /***********************************************************************/
3021 /*  Data Base close routine for VMP access method.                     */
3022 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)3023 void VMPFAM::CloseTableFile(PGLOBAL g, bool)
3024   {
3025   if (Tdbp->GetMode() == MODE_DELETE) {
3026     // Set Block and Nrec values for TDBVCT::MakeBlockValues
3027     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3028     Last = (Tpos + Nrec - 1) % Nrec + 1;
3029     ResetTableSize(g, Block, Last);
3030   } else if (Tdbp->GetMode() == MODE_INSERT)
3031     assert(false);
3032 
3033   for (int i = 0; i < Ncol; i++)
3034     PlugCloseFile(g, To_Fbs[i]);
3035 
3036   } // end of CloseTableFile
3037 
3038 /* -------------------------- Class BGVFAM --------------------------- */
3039 
3040 /***********************************************************************/
3041 /*  Implementation of the BGVFAM class.                                */
3042 /***********************************************************************/
3043 // Constructors
BGVFAM(PVCTDEF tdp)3044 BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp)
3045   {
3046   Hfile = INVALID_HANDLE_VALUE;
3047   Tfile = INVALID_HANDLE_VALUE;
3048   BigDep = NULL;
3049   } // end of BGVFAM constructor
3050 
BGVFAM(PBGVFAM txfp)3051 BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp)
3052   {
3053   Hfile = txfp->Hfile;
3054   Tfile = txfp->Tfile;
3055   BigDep= txfp->BigDep;
3056   } // end of BGVFAM copy constructor
3057 
3058 /***********************************************************************/
3059 /*  Set current position in a big file.                                */
3060 /***********************************************************************/
BigSeek(PGLOBAL g,HANDLE h,BIGINT pos,bool b)3061 bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b)
3062   {
3063 #if defined(_WIN32)
3064   char          buf[256];
3065   DWORD         drc, m = (b) ? FILE_END : FILE_BEGIN;
3066   LARGE_INTEGER of;
3067 
3068   of.QuadPart = pos;
3069   of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m);
3070 
3071   if (of.LowPart == INVALID_SET_FILE_POINTER &&
3072            (drc = GetLastError()) != NO_ERROR) {
3073     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3074                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3075                   (LPTSTR)buf, sizeof(buf), NULL);
3076     sprintf(g->Message, MSG(SFP_ERROR), buf);
3077     return true;
3078     } // endif
3079 #else   // !_WIN32
3080   if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
3081     sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
3082     return true;
3083     } // endif
3084 #endif  // !_WIN32
3085 
3086   return false;
3087   } // end of BigSeek
3088 
3089 /***********************************************************************/
3090 /*  Read from a big file.                                              */
3091 /***********************************************************************/
BigRead(PGLOBAL g,HANDLE h,void * inbuf,int req)3092 bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
3093   {
3094   bool rc = false;
3095 
3096 #if defined(_WIN32)
3097   DWORD nbr, drc, len = (DWORD)req;
3098   bool  brc = ReadFile(h, inbuf, len, &nbr, NULL);
3099 
3100   if (trace(1))
3101     htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
3102 
3103   if (!brc || nbr != len) {
3104     char buf[256];  // , *fn = (h == Hfile) ? To_File : "Tempfile";
3105 
3106     if (brc)
3107       strcpy(buf, MSG(BAD_BYTE_READ));
3108     else {
3109       drc = GetLastError();
3110       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3111                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3112                     (LPTSTR)buf, sizeof(buf), NULL);
3113       } // endelse brc
3114 
3115     sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
3116 
3117     if (trace(1))
3118       htrc("BIGREAD: %s\n", g->Message);
3119 
3120     rc = true;
3121     } // endif brc || nbr
3122 #else   // !_WIN32
3123   size_t  len = (size_t)req;
3124   ssize_t nbr = read(h, inbuf, len);
3125 
3126   if (nbr != (ssize_t)len) {
3127     const char *fn = (h == Hfile) ? To_File : "Tempfile";
3128 
3129     sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
3130 
3131     if (trace(1))
3132       htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n",
3133                      nbr, len, errno, g->Message);
3134 
3135     rc = true;
3136     } // endif nbr
3137 #endif  // !_WIN32
3138 
3139   return rc;
3140   } // end of BigRead
3141 
3142 /***********************************************************************/
3143 /*  Write into a big file.                                             */
3144 /***********************************************************************/
BigWrite(PGLOBAL g,HANDLE h,void * inbuf,int req)3145 bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
3146   {
3147   bool rc = false;
3148 
3149 #if defined(_WIN32)
3150   DWORD nbw, drc, len = (DWORD)req;
3151   bool  brc = WriteFile(h, inbuf, len, &nbw, NULL);
3152 
3153   if (trace(1))
3154     htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
3155 
3156   if (!brc || nbw != len) {
3157 		char buf[256];
3158 		PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
3159 
3160     if (brc)
3161       strcpy(buf, MSG(BAD_BYTE_NUM));
3162     else {
3163       drc = GetLastError();
3164       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3165                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3166                     (LPTSTR)buf, sizeof(buf), NULL);
3167       } // endelse brc
3168 
3169     sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
3170 
3171     if (trace(1))
3172       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3173                       nbw, len, drc, g->Message);
3174 
3175     rc = true;
3176     } // endif brc || nbw
3177 #else   // !_WIN32
3178   size_t  len = (size_t)req;
3179   ssize_t nbw = write(h, inbuf, len);
3180 
3181   if (nbw != (ssize_t)len) {
3182     const char *fn = (h == Hfile) ? To_File : "Tempfile";
3183 
3184     sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
3185 
3186     if (trace(1))
3187       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3188                       nbw, len, errno, g->Message);
3189 
3190     rc = true;
3191     } // endif nbr
3192 #endif  // !_WIN32
3193 
3194   return rc;
3195   } // end of BigWrite
3196 
3197 /***********************************************************************/
3198 /*  Get the Headlen, Block and Last info from the file header.         */
3199 /***********************************************************************/
GetBlockInfo(PGLOBAL g)3200 int BGVFAM::GetBlockInfo(PGLOBAL g)
3201   {
3202   char      filename[_MAX_PATH];
3203   int       n;
3204   VECHEADER vh;
3205   HANDLE    h;
3206 
3207   if (Header < 1 || Header > 3 || !MaxBlk) {
3208     sprintf(g->Message, "Invalid header value %d", Header);
3209     return -1;
3210   } else
3211     n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
3212 
3213   PlugSetPath(filename, To_File, Tdbp->GetPath());
3214 
3215   if (Header == 2)
3216     strcat(PlugRemoveType(filename, filename), ".blk");
3217 
3218 #if defined(_WIN32)
3219   LARGE_INTEGER len;
3220 
3221   h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
3222                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3223 
3224   if (h != INVALID_HANDLE_VALUE) {
3225     // Get the size of the file (can be greater than 4 GB)
3226     len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
3227     } // endif h
3228 
3229   if (h == INVALID_HANDLE_VALUE || !len.QuadPart) {
3230 #else   // !_WIN32
3231   h = open64(filename, O_RDONLY, 0);
3232 
3233   if (h == INVALID_HANDLE_VALUE || !_filelength(h)) {
3234 #endif  // !_WIN32
3235     // Consider this is a void table
3236     if (trace(1))
3237       htrc("Void table h=%d\n", h);
3238 
3239     Last = Nrec;
3240     Block = 0;
3241 
3242     if (h != INVALID_HANDLE_VALUE)
3243       CloseFileHandle(h);
3244 
3245     return n;
3246   } else if (Header == 3)
3247     /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3248 
3249   if (BigRead(g, h, &vh, sizeof(vh))) {
3250     sprintf(g->Message, "Error reading header file %s", filename);
3251     n = -1;
3252   } else if (MaxBlk * Nrec != vh.MaxRec) {
3253     sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
3254                         vh.MaxRec, MaxBlk, Nrec);
3255     n = -1;
3256   } else {
3257     Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
3258     Last  = (vh.NumRec + Nrec - 1) % Nrec + 1;
3259 
3260     if (trace(1))
3261       htrc("Block=%d Last=%d\n", Block, Last);
3262 
3263   } // endif's
3264 
3265   CloseFileHandle(h);
3266   return n;
3267   } // end of GetBlockInfo
3268 
3269 /***********************************************************************/
3270 /*  Set the MaxRec and NumRec info in the file header.                 */
3271 /***********************************************************************/
3272 bool BGVFAM::SetBlockInfo(PGLOBAL g)
3273   {
3274   char      filename[_MAX_PATH];
3275   bool      b = false, rc = false;
3276   VECHEADER vh;
3277   HANDLE    h = INVALID_HANDLE_VALUE;
3278 
3279   PlugSetPath(filename, To_File, Tdbp->GetPath());
3280 
3281   if (Header != 2) {
3282     if (Hfile != INVALID_HANDLE_VALUE) {
3283       h = Hfile;
3284 
3285       if (Header == 1)
3286         /*bk =*/ BigSeek(g, h, (BIGINT)0);
3287 
3288     } else
3289       b = true;
3290 
3291   } else       // Header == 2
3292     strcat(PlugRemoveType(filename, filename), ".blk");
3293 
3294   if (h == INVALID_HANDLE_VALUE) {
3295 #if defined(_WIN32)
3296     DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING;
3297 
3298     h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0,
3299                    NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
3300 
3301 #else   // !_WIN32
3302     int oflag = (b) ?  O_RDWR : O_RDWR | O_TRUNC;
3303 
3304     h = open64(filename, oflag, 0);
3305 #endif  // !_WIN32
3306 
3307     if (h == INVALID_HANDLE_VALUE) {
3308       sprintf(g->Message, "Error opening header file %s", filename);
3309       return true;
3310       } // endif h
3311 
3312     } // endif h
3313 
3314   if (Header == 3)
3315     /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3316 
3317   vh.MaxRec = MaxBlk * Bsize;
3318   vh.NumRec = (Block - 1) * Nrec + Last;
3319 
3320   if (BigWrite(g, h, &vh, sizeof(vh))) {
3321     sprintf(g->Message, "Error writing header file %s", filename);
3322     rc = true;
3323     } // endif fread
3324 
3325   if (Header == 2 || Hfile == INVALID_HANDLE_VALUE)
3326     CloseFileHandle(h);
3327 
3328   return rc;
3329   } // end of SetBlockInfo
3330 
3331 /***********************************************************************/
3332 /*  VEC Create an empty file for new Vector formatted tables.          */
3333 /***********************************************************************/
3334 bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
3335   {
3336   // Vector formatted file this will create an empty file of the
3337   // required length if it does not exists yet.
3338   char          filename[_MAX_PATH], c = 0;
3339   int           n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
3340 
3341   PlugSetPath(filename, fn, Tdbp->GetPath());
3342 
3343 #if defined(_WIN32)
3344   PCSZ          p;
3345   DWORD         rc;
3346   bool          brc;
3347   LARGE_INTEGER of;
3348   HANDLE        h;
3349 
3350   h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3351                            FILE_ATTRIBUTE_NORMAL, NULL);
3352 
3353   if (h == INVALID_HANDLE_VALUE) {
3354     p = MSG(OPENING);
3355     goto err;
3356     } // endif h
3357 
3358   of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3359 
3360   if (trace(1))
3361     htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n",
3362                of.QuadPart, n, MaxBlk, Blksize);
3363 
3364   of.LowPart = SetFilePointer(h, of.LowPart,
3365                                 &of.HighPart, FILE_BEGIN);
3366 
3367   if (of.LowPart == INVALID_SET_FILE_POINTER &&
3368            GetLastError() != NO_ERROR) {
3369     p = MSG(MAKING);
3370     goto err;
3371     } // endif
3372 
3373   brc = WriteFile(h, &c, 1, &rc, NULL);
3374 
3375   if (!brc || rc != 1) {
3376     p = MSG(WRITING);
3377     goto err;
3378     } // endif
3379 
3380   CloseHandle(h);
3381   return false;
3382 
3383  err:
3384   rc = GetLastError();
3385   sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
3386   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3387                 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3388                 (LPTSTR)filename, sizeof(filename), NULL);
3389   strcat(g->Message, filename);
3390 
3391   if (h != INVALID_HANDLE_VALUE)
3392     CloseHandle(h);
3393 
3394   return true;
3395 #else   // !_WIN32
3396   int    h;
3397   BIGINT pos;
3398 
3399   h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
3400 
3401   if (h == -1)
3402     return true;
3403 
3404   pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3405 
3406   if (trace(1))
3407     htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
3408                pos, n, MaxBlk, Blksize);
3409 
3410   if (lseek64(h, pos, SEEK_SET) < 0)
3411     goto err;
3412 
3413   // This actually fills the empty file
3414   if (write(h, &c, 1) < 0)
3415     goto err;
3416 
3417   close(h);
3418   return false;
3419 
3420  err:
3421   sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
3422   close(h);
3423   return true;
3424 #endif  // !_WIN32
3425   } // end of MakeEmptyFile
3426 
3427 /***********************************************************************/
3428 /*  Vopen function: opens a file using Windows or Unix API's.          */
3429 /***********************************************************************/
3430 bool BGVFAM::OpenTableFile(PGLOBAL g)
3431   {
3432   char    filename[_MAX_PATH];
3433   bool    del = false;
3434   MODE    mode = Tdbp->GetMode();
3435   PDBUSER dbuserp = PlgGetUser(g);
3436 
3437   if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
3438     sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
3439     return true;
3440     } // endif
3441 
3442   /*********************************************************************/
3443   /*  Update block info if necessary.                                  */
3444   /*********************************************************************/
3445   if (Block < 0)
3446     if ((Headlen = GetBlockInfo(g)) < 0)
3447       return true;
3448 
3449   PlugSetPath(filename, To_File, Tdbp->GetPath());
3450 
3451   if (trace(1))
3452     htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
3453                          filename, mode, Last);
3454 
3455 #if defined(_WIN32)
3456   DWORD access, creation, share = 0, rc = 0;
3457 
3458   /*********************************************************************/
3459   /*  Create the file object according to access mode                  */
3460   /*********************************************************************/
3461   switch (mode) {
3462     case MODE_READ:
3463       access = GENERIC_READ;
3464       share = FILE_SHARE_READ;
3465       creation = OPEN_EXISTING;
3466       break;
3467     case MODE_INSERT:
3468       if (MaxBlk) {
3469         if (!Block)
3470           if (MakeEmptyFile(g, To_File))
3471             return true;
3472 
3473         // Required to update empty blocks
3474         access = GENERIC_READ | GENERIC_WRITE;
3475       } else if (Last == Nrec)
3476         access = GENERIC_WRITE;
3477       else
3478         // Required to update the last block
3479         access = GENERIC_READ | GENERIC_WRITE;
3480 
3481       creation = OPEN_ALWAYS;
3482       break;
3483     case MODE_DELETE:
3484       if (!Tdbp->GetNext()) {
3485         // Store the number of deleted lines
3486         DelRows = Cardinality(g);
3487 
3488         // This will stop the process by
3489         // causing GetProgMax to return 0.
3490 //      ResetTableSize(g, 0, Nrec);     must be done later
3491         del = true;
3492 
3493         // This will delete the whole file
3494         access = GENERIC_READ | GENERIC_WRITE;
3495         creation = TRUNCATE_EXISTING;
3496         break;
3497         } // endif
3498 
3499       // Selective delete, pass thru
3500     case MODE_UPDATE:
3501       if ((UseTemp = Tdbp->IsUsingTemp(g)))
3502         access = GENERIC_READ;
3503       else
3504         access = GENERIC_READ | GENERIC_WRITE;
3505 
3506       creation = OPEN_EXISTING;
3507       break;
3508     default:
3509       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3510       return true;
3511     } // endswitch
3512 
3513   /*********************************************************************/
3514   /*  Use specific Windows API functions.                              */
3515   /*********************************************************************/
3516   Hfile = CreateFile(filename, access, share, NULL, creation,
3517                                FILE_ATTRIBUTE_NORMAL, NULL);
3518 
3519   if (Hfile == INVALID_HANDLE_VALUE) {
3520     rc = GetLastError();
3521     sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3522     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3523                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3524                   (LPTSTR)filename, sizeof(filename), NULL);
3525     strcat(g->Message, filename);
3526     } // endif Hfile
3527 
3528   if (trace(1))
3529     htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
3530            rc, access, share, creation, Hfile, filename);
3531 
3532   if (mode == MODE_INSERT) {
3533     /*******************************************************************/
3534     /* In Insert mode we must position the cursor at end of file.      */
3535     /*******************************************************************/
3536     LARGE_INTEGER of;
3537 
3538     of.QuadPart = (BIGINT)0;
3539     of.LowPart = SetFilePointer(Hfile, of.LowPart,
3540                                       &of.HighPart, FILE_END);
3541 
3542     if (of.LowPart == INVALID_SET_FILE_POINTER &&
3543        (rc = GetLastError()) != NO_ERROR) {
3544       sprintf(g->Message, MSG(ERROR_IN_SFP), rc);
3545       CloseHandle(Hfile);
3546       Hfile = INVALID_HANDLE_VALUE;
3547       } // endif
3548 
3549     } // endif Mode
3550 
3551 #else   // UNIX
3552   /*********************************************************************/
3553   /*  The open() function has a transitional interface for 64-bit      */
3554   /*  file  offsets. Note that using open64() is equivalent to using   */
3555   /*  open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp).  */
3556   /*********************************************************************/
3557   int    rc = 0;
3558   int    oflag;
3559   mode_t pmd = 0;
3560 
3561   /*********************************************************************/
3562   /*  Create the file object according to access mode                  */
3563   /*********************************************************************/
3564   switch (mode) {
3565     case MODE_READ:
3566       oflag = O_RDONLY;
3567       break;
3568     case MODE_INSERT:
3569       if (MaxBlk) {
3570         if (!Block)
3571           if (MakeEmptyFile(g, To_File))
3572             return true;
3573 
3574         // Required to update empty blocks
3575         oflag = O_RDWR;
3576       } else if (Last == Nrec)
3577         oflag = O_WRONLY | O_CREAT | O_APPEND;
3578       else
3579         // Required to update the last block
3580         oflag = O_RDWR | O_CREAT | O_APPEND;
3581 
3582       pmd = S_IREAD | S_IWRITE;
3583       break;
3584     case MODE_DELETE:
3585       // This is temporary until a partial delete is implemented
3586       if (!Tdbp->GetNext()) {
3587         // Store the number of deleted lines
3588         DelRows = Cardinality(g);
3589         del = true;
3590 
3591         // This will delete the whole file and provoque ReadDB to
3592         // return immediately.
3593         oflag = O_RDWR | O_TRUNC;
3594         strcpy(g->Message, MSG(NO_VCT_DELETE));
3595         break;
3596         } // endif
3597 
3598       // Selective delete, pass thru
3599       /* fall through */
3600     case MODE_UPDATE:
3601       UseTemp = Tdbp->IsUsingTemp(g);
3602       oflag = (UseTemp) ? O_RDONLY : O_RDWR;
3603       break;
3604     default:
3605       sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3606       return true;
3607     } // endswitch
3608 
3609   Hfile = open64(filename, oflag, pmd); // Enable file size > 2G
3610 
3611   if (Hfile == INVALID_HANDLE_VALUE) {
3612     rc = errno;
3613     sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3614     strcat(g->Message, strerror(errno));
3615     } // endif Hfile
3616 
3617   if (trace(1))
3618     htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n",
3619            rc, oflag, mode, Hfile, filename);
3620 #endif  // UNIX
3621 
3622   if (!rc) {
3623     if (!To_Fb) {
3624       To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3625       To_Fb->Fname = To_File;
3626       To_Fb->Type = TYPE_FB_HANDLE;
3627       To_Fb->Memory = NULL;
3628       To_Fb->Length = 0;
3629       To_Fb->File = NULL;
3630       To_Fb->Next = dbuserp->Openlist;
3631       dbuserp->Openlist = To_Fb;
3632       } // endif To_Fb
3633 
3634     To_Fb->Count = 1;
3635     To_Fb->Mode = mode;
3636     To_Fb->Handle = Hfile;
3637 
3638     if (trace(1))
3639       htrc("File %s is open in mode %d\n", filename, mode);
3640 
3641     if (del)
3642       // This will stop the process by
3643       // causing GetProgMax to return 0.
3644       return ResetTableSize(g, 0, Nrec);
3645 
3646     /*********************************************************************/
3647     /*  Allocate the table and column block buffers.                     */
3648     /*********************************************************************/
3649     return AllocateBuffer(g);
3650   } else
3651     return (mode == MODE_READ && rc == ENOENT)
3652             ? PushWarning(g, Tdbp) : true;
3653 
3654   } // end of OpenTableFile
3655 
3656 /***********************************************************************/
3657 /*  Allocate the block buffers for columns used in the query.          */
3658 /***********************************************************************/
3659 bool BGVFAM::AllocateBuffer(PGLOBAL g)
3660   {
3661   MODE    mode = Tdbp->GetMode();
3662   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
3663   PCOLDEF cdp;
3664   PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3665 
3666   if (mode == MODE_INSERT) {
3667     if (!NewBlock) {
3668       // Not reopening after inserting the last block
3669       bool chk = PlgGetUser(g)->Check & CHK_TYPE;
3670 
3671       NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3672 
3673       for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3674         memset(NewBlock + Nrec * cdp->GetPoff(),
3675               (IsTypeNum(cdp->GetType()) ? 0 : ' '),
3676                           Nrec * cdp->GetClen());
3677 
3678       for (; cp; cp = (PVCTCOL)cp->Next)
3679         cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
3680                                 cp->Buf_Type, Nrec, cp->Format.Length,
3681                                 cp->Format.Prec, chk, true, cp->IsUnsigned());
3682 
3683       InitInsert(g);    // Initialize inserting
3684 
3685       // Currently we don't use a temporary file for inserting
3686       Tfile = Hfile;
3687       } // endif NewBlock
3688 
3689   } else {
3690     if (UseTemp || mode == MODE_DELETE) {
3691       // Allocate all that is needed to move lines
3692       int i = 0;
3693 
3694       if (!Ncol)
3695         for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3696           Ncol++;
3697 
3698       if (MaxBlk)
3699         BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT));
3700       else
3701         Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3702 
3703       Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3704       Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
3705 
3706       for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
3707         if (MaxBlk)
3708           BigDep[i] = (BIGINT)Headlen
3709                     + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk;
3710         else
3711           Deplac[i] = cdp->GetPoff() * Nrec;
3712 
3713         Clens[i] = cdp->GetClen();
3714         Isnum[i] = IsTypeNum(cdp->GetType());
3715         Buflen = MY_MAX(Buflen, cdp->GetClen());
3716         } // endfor cdp
3717 
3718       if (!UseTemp || MaxBlk) {
3719         Buflen *= Nrec;
3720         To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
3721       } else
3722         NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3723 
3724       } // endif mode
3725 
3726     for (; cp; cp = (PVCTCOL)cp->Next)
3727       if (!cp->IsSpecial())            // Not a pseudo column
3728         cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
3729                                 cp->Format.Length, cp->Format.Prec,
3730 					                      true, true, cp->IsUnsigned());
3731 
3732   } //endif mode
3733 
3734   return false;
3735   } // end of AllocateBuffer
3736 
3737 /***********************************************************************/
3738 /*  Data Base write routine for huge VCT access method.                */
3739 /***********************************************************************/
3740 int BGVFAM::WriteBuffer(PGLOBAL g)
3741   {
3742   if (trace(1))
3743     htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n",
3744           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
3745 
3746   if (Tdbp->GetMode() == MODE_UPDATE) {
3747     // Mode Update is done in ReadDB, we just initialize it here
3748     if (Tfile == INVALID_HANDLE_VALUE) {
3749       if (UseTemp) {
3750         if (OpenTempFile(g))
3751           return RC_FX;
3752 
3753         // Most of the time, not all table columns are updated.
3754         // This why we must completely pre-fill the temporary file.
3755         Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
3756                         : Block * Nrec;   // To write last lock
3757 
3758         if (MoveIntermediateLines(g))
3759           return RC_FX;
3760 
3761       } else
3762         Tfile = Hfile;
3763 
3764       } // endif Tfile
3765 
3766   } else {
3767     // Mode Insert
3768     if (MaxBlk && CurBlk == MaxBlk) {
3769       strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
3770       return RC_EF;       // Too many lines for a Vector formatted table
3771       } // endif MaxBlk
3772 
3773     if (Closing || ++CurNum == Nrec) {
3774       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3775 
3776       if (!AddBlock) {
3777         // Write back the updated last block values
3778         for (; cp; cp = (PVCTCOL)cp->Next)
3779           cp->WriteBlock(g);
3780 
3781         if (!Closing && !MaxBlk) {
3782           // Close the VCT file and reopen it in mode Insert
3783 //#if defined(_WIN32)  //OB
3784 //          CloseHandle(Hfile);
3785 //#else    // UNIX
3786 //          close(Hfile);
3787 //#endif   // UNIX
3788           CloseFileHandle(Hfile);
3789           Hfile = INVALID_HANDLE_VALUE;
3790           To_Fb->Count = 0;
3791           Last = Nrec;               // Tested in OpenTableFile
3792 
3793           if (OpenTableFile(g)) {
3794             Closing = true;          // Tell CloseDB of error
3795             return RC_FX;
3796             } // endif Vopen
3797 
3798           AddBlock = true;
3799           } // endif Closing
3800 
3801       } else {
3802         // Here we must add a new block to the VCT file
3803         if (Closing)
3804           // Reset the overwritten columns for last block extra records
3805           for (; cp; cp = (PVCTCOL)cp->Next)
3806             memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
3807                    (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
3808                    (Nrec - Last) * cp->Clen);
3809 
3810         if (BigWrite(g, Hfile, NewBlock, Blksize))
3811           return RC_FX;
3812 
3813       } // endif AddBlock
3814 
3815       if (!Closing) {
3816         CurBlk++;
3817         CurNum = 0;
3818         } // endif Closing
3819 
3820       } // endif
3821 
3822     } // endif Mode
3823 
3824   return RC_OK;
3825   } // end of WriteBuffer
3826 
3827 /***********************************************************************/
3828 /*  Data Base delete line routine for BGVFAM access method.            */
3829 /***********************************************************************/
3830 int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
3831   {
3832   bool eof = false;
3833 
3834   /*********************************************************************/
3835   /*  There is an alternative here depending on UseTemp:               */
3836   /*  1 - use a temporary file in which are copied all not deleted     */
3837   /*      lines, at the end the original file will be deleted and      */
3838   /*      the temporary file renamed to the original file name.        */
3839   /*  2 - directly move the not deleted lines inside the original      */
3840   /*      file, and at the end erase all trailing records.             */
3841   /*********************************************************************/
3842   if (trace(1))
3843     htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
3844                         irc, UseTemp, Fpos, Tpos, Spos);
3845 
3846   if (irc != RC_OK) {
3847     /*******************************************************************/
3848     /*  EOF: position Fpos at the end-of-file position.                */
3849     /*******************************************************************/
3850     Fpos = (Block - 1) * Nrec + Last;
3851 
3852     if (trace(1))
3853       htrc("Fpos placed at file end=%d\n", Fpos);
3854 
3855     eof = UseTemp && !MaxBlk;
3856   } else     // Fpos is the deleted line position
3857     Fpos = CurBlk * Nrec + CurNum;
3858 
3859   if (Tpos == Spos) {
3860     if (UseTemp) {
3861       /*****************************************************************/
3862       /*  Open the temporary file, Spos is at the beginning of file.   */
3863       /*****************************************************************/
3864       if (OpenTempFile(g))
3865         return RC_FX;
3866 
3867     } else {
3868       /*****************************************************************/
3869       /*  Move of eventual preceding lines is not required here.      */
3870       /*  Set the target file as being the source file itself.         */
3871       /*  Set the future Tpos, and give Spos a value to block copying. */
3872       /*****************************************************************/
3873       Tfile = Hfile;
3874       Spos = Tpos = Fpos;
3875     } // endif UseTemp
3876 
3877     } // endif Tpos == Spos
3878 
3879   /*********************************************************************/
3880   /*  Move any intermediate lines.                                     */
3881   /*********************************************************************/
3882   if (MoveIntermediateLines(g, &eof))
3883     return RC_FX;
3884 
3885   if (irc == RC_OK) {
3886 #ifdef _DEBUG
3887     assert(Spos == Fpos);
3888 #endif
3889     Spos++;          // New start position is on next line
3890 
3891     if (trace(1))
3892       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
3893 
3894   } else {
3895     /*******************************************************************/
3896     /*  Last call after EOF has been reached.                          */
3897     /*******************************************************************/
3898     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3899     Last = (Tpos + Nrec - 1) % Nrec + 1;
3900 
3901     if (!UseTemp) {    // The UseTemp case is treated in CloseTableFile
3902       if (!MaxBlk) {
3903         if (Last < Nrec)            // Clean last block
3904           if (CleanUnusedSpace(g))
3905             return RC_FX;
3906 
3907         /***************************************************************/
3908         /*  Remove extra records.                                      */
3909         /***************************************************************/
3910 #if defined(_WIN32)
3911         BIGINT pos = (BIGINT)Block * (BIGINT)Blksize;
3912 
3913         if (BigSeek(g, Hfile, pos))
3914           return RC_FX;
3915 
3916         if (!SetEndOfFile(Hfile)) {
3917           DWORD drc = GetLastError();
3918 
3919           sprintf(g->Message, MSG(SETEOF_ERROR), drc);
3920           return RC_FX;
3921           } // endif error
3922 #else   // !_WIN32
3923         if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
3924           sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3925           return RC_FX;
3926           } // endif
3927 #endif  // !_WIN32
3928       } else // MaxBlk
3929         // Clean the unused space in the file, this is required when
3930         // inserting again with a partial column list.
3931         if (CleanUnusedSpace(g))
3932           return RC_FX;
3933 
3934       if (ResetTableSize(g, Block, Last))
3935        return RC_FX;
3936 
3937       } // endif UseTemp
3938 
3939   } // endif irc
3940 
3941   return RC_OK;                                      // All is correct
3942   } // end of DeleteRecords
3943 
3944 /***********************************************************************/
3945 /*  Open a temporary file used while updating or deleting.             */
3946 /***********************************************************************/
3947 bool BGVFAM::OpenTempFile(PGLOBAL g)
3948   {
3949   char   *tempname;
3950   PDBUSER dup = PlgGetUser(g);
3951 
3952   /*********************************************************************/
3953   /*  Open the temporary file, Spos is at the beginning of file.       */
3954   /*********************************************************************/
3955   tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
3956   PlugSetPath(tempname, To_File, Tdbp->GetPath());
3957   strcat(PlugRemoveType(tempname, tempname), ".t");
3958 
3959   if (!MaxBlk)
3960     remove(tempname);       // Be sure it does not exist yet
3961   else if (MakeEmptyFile(g, tempname))
3962     return true;
3963 
3964 #if defined(_WIN32)
3965   DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW;
3966 
3967   Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
3968                      access, FILE_ATTRIBUTE_NORMAL, NULL);
3969 
3970   if (Tfile == INVALID_HANDLE_VALUE) {
3971     DWORD rc = GetLastError();
3972     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname);
3973     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3974               FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3975               (LPTSTR)tempname, _MAX_PATH, NULL);
3976     strcat(g->Message, tempname);
3977     return true;
3978     } // endif Tfile
3979 #else    // UNIX
3980   int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC;
3981 
3982   Tfile = open64(tempname, oflag, S_IWRITE);
3983 
3984   if (Tfile == INVALID_HANDLE_VALUE) {
3985     int rc = errno;
3986     sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
3987     strcat(g->Message, strerror(errno));
3988     return true;
3989     } //endif Tfile
3990 #endif   // UNIX
3991 
3992   To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3993   To_Fbt->Fname = tempname;
3994   To_Fbt->Type = TYPE_FB_HANDLE;
3995   To_Fbt->Memory = NULL;
3996   To_Fbt->Length = 0;
3997   To_Fbt->File = NULL;
3998   To_Fbt->Next = dup->Openlist;
3999   To_Fbt->Count = 1;
4000   To_Fbt->Mode = MODE_INSERT;
4001   To_Fbt->Handle = Tfile;
4002   dup->Openlist = To_Fbt;
4003   return false;
4004   } // end of OpenTempFile
4005 
4006 /***********************************************************************/
4007 /*  Move intermediate deleted or updated lines.                        */
4008 /***********************************************************************/
4009 bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
4010   {
4011   int    i, n, req, dep;
4012   bool   eof = (b) ? *b : false;
4013   BIGINT pos;
4014 
4015   for (n = Fpos - Spos; n > 0 || eof; n -= req) {
4016     /*******************************************************************/
4017     /*  Non consecutive line to delete. Move intermediate lines.       */
4018     /*******************************************************************/
4019     if (!MaxBlk)
4020       req = (DWORD)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
4021     else
4022       req = (DWORD)MY_MIN(n, Nrec);
4023 
4024     if (req) for (i = 0; i < Ncol; i++) {
4025       if (!MaxBlk) {
4026         if (UseTemp)
4027           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4028 
4029         pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i])
4030             + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize;
4031       } else
4032         pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i];
4033 
4034       if (BigSeek(g, Hfile, pos))
4035         return true;
4036 
4037       if (BigRead(g, Hfile, To_Buf, req * Clens[i]))
4038         return true;
4039 
4040       if (!UseTemp || MaxBlk) {
4041         if (!MaxBlk)
4042           pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i])
4043               + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize;
4044         else
4045           pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4046 
4047         if (BigSeek(g, Tfile, pos))
4048           return true;
4049 
4050         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4051           return true;
4052 
4053         } // endif UseTemp
4054 
4055       } // endfor i
4056 
4057     Tpos += (int)req;
4058     Spos += (int)req;
4059 
4060     if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) {
4061       // Write the full or last block to the temporary file
4062       if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
4063         // Clean the last block in case of future insert, must be
4064         // done here because Tfile was open in write only.
4065         for (i = 0; i < Ncol; i++) {
4066           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4067           memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
4068           } // endfor i
4069 
4070       if (BigWrite(g, Tfile, NewBlock, Blksize))
4071         return true;
4072 
4073       if (Spos == Fpos)
4074         eof = false;
4075 
4076       } // endif Usetemp...
4077 
4078     if (trace(1))
4079       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
4080 
4081     } // endfor n
4082 
4083   return false;
4084   } // end of MoveIntermediateLines
4085 
4086 /***********************************************************************/
4087 /*  Clean deleted space in a huge VCT or Vec table file.               */
4088 /***********************************************************************/
4089 bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
4090   {
4091   int    i;
4092   int   n;
4093   BIGINT pos, dep;
4094 
4095   if (!MaxBlk) {
4096     /*******************************************************************/
4097     /*  Clean last block of the VCT table file.                        */
4098     /*******************************************************************/
4099     assert(!UseTemp); // This case is handled in MoveIntermediateLines
4100 
4101     if (!(n = Nrec - Last))
4102       return false;
4103 
4104     dep = (BIGINT)((Block - 1) * Blksize);
4105 
4106     for (i = 0; i < Ncol; i++) {
4107       memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
4108       pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]);
4109 
4110       if (BigSeek(g, Hfile, pos))
4111         return true;
4112 
4113       if (BigWrite(g, Hfile, To_Buf, n * Clens[i]))
4114         return true;
4115 
4116       } // endfor i
4117 
4118   } else {
4119     int  req;
4120 
4121     if (To_Buf)
4122       memset(To_Buf, 0, Buflen);
4123 
4124     for (n = Fpos - Tpos; n > 0; n -= req) {
4125       /*****************************************************************/
4126       /*  Fill VEC file remaining lines with 0's.                      */
4127       /*  This seems to work even column blocks have been made with    */
4128       /*  Blanks = true. Perhaps should it be set to false for VEC.    */
4129       /*****************************************************************/
4130       req = MY_MIN(n, Nrec);
4131 
4132       for (i = 0; i < Ncol; i++) {
4133         pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4134 
4135         if (BigSeek(g, Tfile, pos))
4136           return true;
4137 
4138         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4139           return true;
4140 
4141         } // endfor i
4142 
4143       Tpos += req;
4144       } // endfor n
4145 
4146   } // endif MaxBlk
4147 
4148   return false;
4149   } // end of CleanUnusedSpace
4150 
4151 /***********************************************************************/
4152 /*  Data Base close routine for huge VEC access method.                */
4153 /***********************************************************************/
4154 void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
4155   {
4156   int  rc = 0, wrc = RC_OK;
4157   MODE mode = Tdbp->GetMode();
4158 
4159   Abort = abort;
4160 
4161   if (mode == MODE_INSERT) {
4162     if (Closing)
4163       wrc = RC_FX;                  // Last write was in error
4164     else
4165       if (CurNum) {
4166         // Some more inserted lines remain to be written
4167         Last = CurNum;
4168         Block = CurBlk + 1;
4169         Closing = true;
4170         wrc = WriteBuffer(g);
4171       } else {
4172         Last = Nrec;
4173         Block = CurBlk;
4174         wrc = RC_OK;
4175       } // endif CurNum
4176 
4177     if (wrc != RC_FX) {
4178       rc = ResetTableSize(g, Block, Last);
4179     } else if (AddBlock) {
4180       // Last block was not written
4181       rc = ResetTableSize(g, CurBlk, Nrec);
4182 			throw 44;
4183 		} // endif
4184 
4185   } else if (mode == MODE_UPDATE) {
4186     // Write back to file any pending modifications
4187     for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols();
4188                  colp; colp = (PVCTCOL)colp->Next)
4189       colp->WriteBlock(g);
4190 
4191     if (UseTemp && Tfile) {
4192       rc = RenameTempFile(g);
4193       Hfile = Tfile = INVALID_HANDLE_VALUE;
4194 
4195       if (Header)
4196         // Header must be set because it was not set in temp file
4197         rc = SetBlockInfo(g);
4198 
4199       } // endif UseTemp
4200 
4201   } else if (mode == MODE_DELETE && UseTemp && Tfile) {
4202     if (MaxBlk)
4203       rc = CleanUnusedSpace(g);
4204 
4205     if ((rc = RenameTempFile(g)) != RC_FX) {
4206       Hfile = Tfile = INVALID_HANDLE_VALUE;    // For SetBlockInfo
4207       rc = ResetTableSize(g, Block, Last);
4208       } // endif rc
4209 
4210   } // endif's mode
4211 
4212   if (Hfile != INVALID_HANDLE_VALUE)
4213     rc = PlugCloseFile(g, To_Fb);
4214 
4215   if (trace(1))
4216     htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n",
4217           To_File, wrc, rc);
4218 
4219   Hfile = INVALID_HANDLE_VALUE;
4220   } // end of CloseDB
4221 
4222 /***********************************************************************/
4223 /*  Rewind routine for huge VCT access method.                         */
4224 /***********************************************************************/
4225 void BGVFAM::Rewind(void)
4226   {
4227   // In mode update we need to read Set Column blocks
4228   if (Tdbp->GetMode() == MODE_UPDATE)
4229     OldBlk = -1;
4230 
4231   // Initialize so block optimization is called for 1st block
4232   CurBlk = -1;
4233   CurNum = Nrec - 1;
4234 
4235 #if 0 // This is probably unuseful as the file is directly accessed
4236 #if defined(_WIN32)  //OB
4237   SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
4238 #else    // UNIX
4239   lseek64(Hfile, 0, SEEK_SET);
4240 #endif   // UNIX
4241 #endif // 0
4242   } // end of Rewind
4243 
4244 /***********************************************************************/
4245 /*  ReadBlock: Read column values from current block.                  */
4246 /***********************************************************************/
4247 bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
4248   {
4249   BIGINT pos;
4250 
4251   /*********************************************************************/
4252   /*  Calculate the offset and size of the block to read.              */
4253   /*********************************************************************/
4254   if (MaxBlk)                                 // File has Vector format
4255     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4256         + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen;
4257   else                                        // Old VCT format
4258     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4259         + (BIGINT)Lrecl * (BIGINT)CurBlk);
4260 
4261   if (trace(1))
4262     htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n",
4263           pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
4264 
4265   if (BigSeek(g, Hfile, pos))
4266     return true;
4267 
4268   if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec))
4269     return true;
4270 
4271   if (trace(1))
4272     num_read++;
4273 
4274   return false;
4275   } // end of ReadBlock
4276 
4277 /***********************************************************************/
4278 /*  WriteBlock: Write back current column values for one block.        */
4279 /*  Note: the test of Status is meant to prevent physical writing of   */
4280 /*  the block during the checking loop in mode Update. It is set to    */
4281 /*  BUF_EMPTY when reopening the table between the two loops.          */
4282 /***********************************************************************/
4283 bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
4284   {
4285   int    len;
4286   BIGINT pos;
4287 
4288   /*********************************************************************/
4289   /*  Calculate the offset and size of the block to write.             */
4290   /*********************************************************************/
4291   if (MaxBlk)                               // File has Vector format
4292     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4293         + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen;
4294   else                                      // Old VCT format
4295     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4296         + (BIGINT)Lrecl * (BIGINT)colp->ColBlk);
4297 
4298   if (trace(1))
4299     htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n",
4300           pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
4301 
4302   if (BigSeek(g, Tfile, pos))
4303     return true;
4304 
4305 //len = colp->Clen * Nrec;    see comment in VCTFAM
4306   len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec);
4307 
4308   if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len))
4309     return true;
4310 
4311   return false;
4312   } // end of WriteBlock
4313 
4314 /* ----------------------- End of FilAMVct --------------------------- */
4315