mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			4328 lines
		
	
	
	
		
			143 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4328 lines
		
	
	
	
		
			143 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
 | |
| /* PROGRAM NAME: FILAMVCT                                              */
 | |
| /* -------------                                                       */
 | |
| /*  Version 2.6                                                        */
 | |
| /*                                                                     */
 | |
| /* COPYRIGHT:                                                          */
 | |
| /* ----------                                                          */
 | |
| /*  (C) Copyright to the author Olivier BERTRAND          2005-2020    */
 | |
| /*                                                                     */
 | |
| /* WHAT THIS PROGRAM DOES:                                             */
 | |
| /* -----------------------                                             */
 | |
| /*  This program are the VCT file access method classes.               */
 | |
| /*  Added in version 2:                                                */
 | |
| /*  - Split Vec format.                                                */
 | |
| /*  - Partial delete.                                                  */
 | |
| /*  - Use of tempfile for update.                                      */
 | |
| /*                                                                     */
 | |
| /***********************************************************************/
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include relevant MariaDB header file.                              */
 | |
| /***********************************************************************/
 | |
| #include "my_global.h"
 | |
| #if defined(_WIN32)
 | |
| #include <io.h>
 | |
| #include <fcntl.h>
 | |
| #if defined(__BORLANDC__)
 | |
| #define __MFC_COMPAT__                   // To define min/max as macro
 | |
| #endif   // __BORLAND__
 | |
| //#include <windows.h>
 | |
| #include <sys/stat.h>
 | |
| #else   // !_WIN32
 | |
| #if defined(UNIX)
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <unistd.h>
 | |
| #include <errno.h>
 | |
| #define NO_ERROR 0
 | |
| #else   // !UNIX
 | |
| #include <io.h>
 | |
| #endif  // !UNIX
 | |
| #include <fcntl.h>
 | |
| #endif  // !_WIN32
 | |
| 
 | |
| #include <m_string.h>
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Include application header files:                                  */
 | |
| /*  global.h    is header containing all global declarations.          */
 | |
| /*  plgdbsem.h  is header containing the DB application declarations.  */
 | |
| /*  tabdos.h    is header containing the TABDOS class declarations.    */
 | |
| /***********************************************************************/
 | |
| #include "global.h"
 | |
| #include "osutil.h"            // Unuseful for WINDOWS
 | |
| #include "plgdbsem.h"
 | |
| #include "valblk.h"
 | |
| #include "filamfix.h"
 | |
| #include "tabdos.h"
 | |
| #include "tabvct.h"
 | |
| #include "maputil.h"
 | |
| #include "filamvct.h"
 | |
| 
 | |
| #ifndef INVALID_SET_FILE_POINTER
 | |
| #define INVALID_SET_FILE_POINTER  ((DWORD)-1)
 | |
| #endif
 | |
| 
 | |
| extern int num_read, num_there;                          // Statistics
 | |
| static int num_write;
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Header containing block info for not split VEC tables.             */
 | |
| /*  Block and last values can be calculated from NumRec and Nrec.      */
 | |
| /*  This is better than directly storing Block and Last because it     */
 | |
| /*  make possible to use the same file with tables having a different  */
 | |
| /*  block size value (Element -> Nrec)                                 */
 | |
| /*  Note: can be in a separate file if header=1 or a true header (2)   */
 | |
| /***********************************************************************/
 | |
| typedef struct _vecheader {
 | |
| //int Block;              /* The number of used blocks                 */
 | |
| //int Last;               /* The number of used records in last block  */
 | |
|   int MaxRec;             /* Max number of records (True vector format)*/
 | |
|   int NumRec;             /* Number of valid records in the table      */
 | |
|   } VECHEADER;
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Char VCT column blocks are right filled with blanks (blank = true) */
 | |
| /*  Conversion of block values allowed conditionally for insert only.  */
 | |
| /***********************************************************************/
 | |
| PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
 | |
|                     bool check = true, bool blank = true, bool un = false);
 | |
| 
 | |
| /* -------------------------- Class VCTFAM --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the VCTFAM class.                                */
 | |
| /***********************************************************************/
 | |
| VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp)
 | |
|   {
 | |
|   Last = tdp->GetLast();
 | |
|   MaxBlk = (tdp->GetEstimate() > 0) ?
 | |
|           ((tdp->GetEstimate() - 1) / Nrec + 1) : 0;
 | |
|   NewBlock = NULL;
 | |
|   AddBlock = false;
 | |
|   Split = false;
 | |
| 
 | |
|   if ((Header = (MaxBlk) ? tdp->Header : 0))
 | |
|     Block = Last = -1;
 | |
| 
 | |
|   Bsize = Nrec;
 | |
|   CurNum = Nrec - 1;
 | |
|   Colfn = NULL;
 | |
|   Tempat = NULL;
 | |
|   Clens = NULL;
 | |
|   Deplac = NULL;
 | |
|   Isnum = NULL;
 | |
|   Ncol = 0;
 | |
|   } // end of VCTFAM standard constructor
 | |
| 
 | |
| VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp)
 | |
|   {
 | |
|   MaxBlk = txfp->MaxBlk;
 | |
|   NewBlock = NULL;
 | |
|   AddBlock = false;
 | |
|   Split = txfp->Split;
 | |
|   Header = txfp->Header;
 | |
|   Bsize = txfp->Bsize;
 | |
|   Colfn = txfp->Colfn;
 | |
|   Tempat = txfp->Tempat;
 | |
|   Clens = txfp->Clens;
 | |
|   Deplac = txfp->Deplac;
 | |
|   Isnum = txfp->Isnum;
 | |
|   Ncol = txfp->Ncol;
 | |
|   } // end of VCTFAM copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VCT GetFileLength: returns file size in number of bytes.           */
 | |
| /*  This function is here to be accessible by VECFAM and VMPFAM.       */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::GetFileLength(PGLOBAL g)
 | |
|   {
 | |
|   if (Split) {
 | |
|     // Get the total file length
 | |
|     char filename[_MAX_PATH];
 | |
|     PCSZ savfile = To_File;
 | |
|     int  i, len = 0;
 | |
| 
 | |
|     //  Initialize the array of file structures
 | |
|     if (!Colfn) {
 | |
|       // Prepare the column file name pattern and set Ncol
 | |
|       Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|       Ncol = ((PVCTDEF)Tdbp->GetDef())->MakeFnPattern(Colfn);
 | |
|       } // endif Colfn
 | |
| 
 | |
|     To_File = filename;
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       snprintf(filename, _MAX_PATH, Colfn, i+1);
 | |
|       len += TXTFAM::GetFileLength(g);
 | |
|       } // endfor i
 | |
| 
 | |
|     To_File = savfile;
 | |
|     return len;
 | |
|   } else
 | |
|     return TXTFAM::GetFileLength(g);
 | |
| 
 | |
|   } // end of GetFileLength
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Reset read/write position values.                                  */
 | |
| /***********************************************************************/
 | |
| void VCTFAM::Reset(void)
 | |
|   {
 | |
|   FIXFAM::Reset();
 | |
|   NewBlock = NULL;
 | |
|   AddBlock = false;
 | |
|   CurNum = Nrec - 1;
 | |
|   } // end of Reset
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Get the Headlen, Block and Last info from the file header.         */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::GetBlockInfo(PGLOBAL g)
 | |
|   {
 | |
|   char      filename[_MAX_PATH];
 | |
|   int       h, k, n;
 | |
|   VECHEADER vh;
 | |
| 
 | |
|   if (Header < 1 || Header > 3 || !MaxBlk) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Invalid header value %d", Header);
 | |
|     return -1;
 | |
|   } else
 | |
|     n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
 | |
| 
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (Header == 2)
 | |
|   {
 | |
|     PlugRemoveType(filename, filename);
 | |
|     safe_strcat(filename, sizeof(filename), ".blk");
 | |
|   }
 | |
| 
 | |
|   if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
 | |
|       || !_filelength(h)) {
 | |
|     // Consider this is a void table
 | |
|     Last = Nrec;
 | |
|     Block = 0;
 | |
| 
 | |
|     if (h != -1)
 | |
|       close(h);
 | |
| 
 | |
|     return n;
 | |
|   } else if (Header == 3)
 | |
|     k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END);
 | |
| 
 | |
|   if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Error reading header file %s", filename);
 | |
|     n = -1;
 | |
|   } else if (MaxBlk * Nrec != vh.MaxRec) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
 | |
|                         vh.MaxRec, MaxBlk, Nrec);
 | |
|     n = -1;
 | |
|   } else {
 | |
|     Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
 | |
|     Last  = (vh.NumRec + Nrec - 1) % Nrec + 1;
 | |
|   } // endif s
 | |
| 
 | |
|   close(h);
 | |
|   return n;
 | |
|   } // end of GetBlockInfo
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Get the Headlen, Block and Last info from the file header.         */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::SetBlockInfo(PGLOBAL g)
 | |
|   {
 | |
|   char      filename[_MAX_PATH];
 | |
|   bool      rc = false;
 | |
|   size_t    n;
 | |
|   VECHEADER vh;
 | |
|   FILE     *s;
 | |
| 
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (Header != 2) {
 | |
|     if (Stream) {
 | |
|       s = Stream;
 | |
| 
 | |
|       if (Header == 1)
 | |
|         /*k =*/ fseek(s, 0, SEEK_SET);
 | |
| 
 | |
|     } else
 | |
|       s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b");
 | |
| 
 | |
|   } else {      // Header == 2
 | |
|     PlugRemoveType(filename, filename);
 | |
|     safe_strcat(filename, sizeof(filename), ".blk");
 | |
|     s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
 | |
|   } // endif Header
 | |
| 
 | |
|   if (!s) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Error opening header file %s", filename);
 | |
|     return true;
 | |
|   } else if (Header == 3)
 | |
|     /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END);
 | |
| 
 | |
|   vh.MaxRec = MaxBlk * Bsize;
 | |
|   vh.NumRec = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|   if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Error writing header file %s", filename);
 | |
|     rc = true;
 | |
|     } // endif fread
 | |
| 
 | |
|   if (Header == 2 || !Stream)
 | |
|     fclose(s);
 | |
| 
 | |
|   return rc;
 | |
|   } // end of SetBlockInfo
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Use BlockTest to reduce the table estimated size.                  */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::MaxBlkSize(PGLOBAL g, int)
 | |
|   {
 | |
|   int rc = RC_OK, savcur = CurBlk;
 | |
|   int size;
 | |
| 
 | |
|   // Roughly estimate the table size as the sum of blocks
 | |
|   // that can contain good rows
 | |
|   for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
 | |
|     if ((rc = Tdbp->TestBlock(g)) == RC_OK)
 | |
|       size += (CurBlk == Block - 1) ? Last : Nrec;
 | |
|     else if (rc == RC_EF)
 | |
|       break;
 | |
| 
 | |
|   CurBlk = savcur;
 | |
|   return size;
 | |
|   } // end of MaxBlkSize
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VCT Cardinality: returns table cardinality in number of rows.      */
 | |
| /*  This function can be called with a null argument to test the       */
 | |
| /*  availability of Cardinality implementation (1 yes, 0 no).          */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::Cardinality(PGLOBAL g)
 | |
|   {
 | |
|   if (!g)
 | |
|     return 1;
 | |
| 
 | |
|   if (Block < 0)
 | |
|   {
 | |
|     if (Split) {
 | |
|       // Separate column files and no pre setting of Block and Last
 | |
|       // This allows to see a table modified externally, but Block
 | |
|       // and Last must be set from the file cardinality.
 | |
|       // Only happens when called by sub classes.
 | |
|       char    filename[_MAX_PATH];
 | |
|       PCSZ    savfn = To_File;
 | |
|       int     len, clen, card = -1;
 | |
|       PCOLDEF cdp = Tdbp->GetDef()->GetCols();
 | |
| 
 | |
|       if (!Colfn) {
 | |
|         // Prepare the column file name pattern
 | |
|         Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|         Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
 | |
|         } // endif Colfn
 | |
| 
 | |
|       // Use the first column file to calculate the cardinality
 | |
|       clen = cdp->GetClen();
 | |
|       snprintf(filename, _MAX_PATH, Colfn, 1);
 | |
|       To_File = filename;
 | |
|       len = TXTFAM::GetFileLength(g);
 | |
|       To_File = savfn;
 | |
| 
 | |
|       if (len >= 0) {
 | |
|         if (!(len % clen))
 | |
|           card = len / clen;           // Fixed length file
 | |
|         else
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(NOT_FIXED_LEN), To_File, len, clen);
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen);
 | |
| 
 | |
|       } else
 | |
|         card = 0;
 | |
| 
 | |
|       // Set number of blocks for later use
 | |
|       Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
 | |
|       Last = (card + Nrec - 1) % Nrec + 1;
 | |
|       return card;
 | |
|     } else {
 | |
|       // Vector table having Block and Last info in a Header (file)
 | |
|       if ((Headlen = GetBlockInfo(g)) < 0)
 | |
|         return -1;             // Error
 | |
| 
 | |
|     } // endif split
 | |
|   }
 | |
|   return (Block) ? ((Block - 1) * Nrec + Last) : 0;
 | |
|   } // end of Cardinality
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  GetRowID: return the RowID of last read record.                    */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::GetRowID(void)
 | |
|   {
 | |
|   return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk
 | |
|                                : (Block - 1) * Nrec + Last);
 | |
|   } // end of GetRowID
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VCT Create an empty file for Vector formatted tables.              */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
 | |
|   {
 | |
|   // Vector formatted file: this will create an empty file of the
 | |
|   // required length if it does not exists yet.
 | |
|   char filename[_MAX_PATH], c = 0;
 | |
|   int  h, n;
 | |
| 
 | |
|   PlugSetPath(filename, fn, Tdbp->GetPath());
 | |
| #if defined(_WIN32)
 | |
|   h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE);
 | |
| #else   // !_WIN32
 | |
|   h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename,  O_CREAT |  O_WRONLY, S_IREAD | S_IWRITE);
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|   if (h == -1)
 | |
|     return true;
 | |
| 
 | |
|   n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
 | |
| 
 | |
|   if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) < 0)
 | |
|     goto err;
 | |
| 
 | |
|   // This actually fills the empty file
 | |
|   if (write(h, &c, 1) < 0)     
 | |
|     goto err;
 | |
| 
 | |
|   close(h);
 | |
|   return false;
 | |
|   
 | |
|  err:
 | |
|   snprintf(g->Message, sizeof(g->Message), MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
 | |
|   close(h);
 | |
|   return true;
 | |
|   } // end of MakeEmptyFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VCT Access Method opening routine.                                 */
 | |
| /*  New method now that this routine is called recursively (last table */
 | |
| /*  first in reverse order): index blocks are immediately linked to    */
 | |
| /*  join block of next table if it exists or else are discarted.       */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::OpenTableFile(PGLOBAL g)
 | |
|   {
 | |
|   char    opmode[4], filename[_MAX_PATH];
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PDBUSER dbuserp = PlgGetUser(g);
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Update block info if necessary.                                  */
 | |
|   /*********************************************************************/
 | |
|   if (Block < 0)
 | |
|     if ((Headlen = GetBlockInfo(g)) < 0)
 | |
|       return true;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open according to input/output mode required.                    */
 | |
|   /*********************************************************************/
 | |
|   switch (mode) {
 | |
|     case MODE_READ:
 | |
|       snprintf(opmode, sizeof(opmode), "rb");
 | |
|       break;
 | |
|     case MODE_DELETE:
 | |
|       if (!Tdbp->GetNext()) {
 | |
|         // Store the number of deleted lines
 | |
|         DelRows = Cardinality(g);
 | |
| 
 | |
|         // This will delete the whole file
 | |
|         snprintf(opmode, sizeof(opmode), "wb");
 | |
|         break;
 | |
|         } // endif
 | |
| 
 | |
|       // Selective delete, pass thru
 | |
|       /* fall through */
 | |
|     case MODE_UPDATE:
 | |
|       UseTemp = Tdbp->IsUsingTemp(g);
 | |
|       snprintf(opmode, sizeof(opmode), (UseTemp) ? "rb" : "r+b");
 | |
|       break;
 | |
|     case MODE_INSERT:
 | |
|       if (MaxBlk) {
 | |
|         if (!Block)
 | |
|           if (MakeEmptyFile(g, To_File))
 | |
|             return true;
 | |
| 
 | |
|         snprintf(opmode, sizeof(opmode), "r+b");   // Required to update empty blocks
 | |
|       } else if (!Block || Last == Nrec)
 | |
|         snprintf(opmode, sizeof(opmode), "ab");
 | |
|       else
 | |
|         snprintf(opmode, sizeof(opmode), "r+b");   // Required to update the last block
 | |
| 
 | |
|       break;
 | |
|     default:
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | |
|       return true;
 | |
|     } // endswitch Mode
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Use conventionnal input/output functions.                        */
 | |
|   /*********************************************************************/
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (!(Stream = PlugOpenFile(g, filename, opmode))) {
 | |
|     if (trace(1))
 | |
|       htrc("%s\n", g->Message);
 | |
| 
 | |
|     return (mode == MODE_READ && errno == ENOENT)
 | |
|             ? PushWarning(g, Tdbp) : true;
 | |
|     } // endif Stream
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("File %s is open in mode %s\n", filename, opmode);
 | |
| 
 | |
|   To_Fb = dbuserp->Openlist;     // Keep track of File block
 | |
| 
 | |
|   if (!strcmp(opmode, "wb"))
 | |
|     // This will stop the process by
 | |
|     // causing GetProgMax to return 0.
 | |
|     return ResetTableSize(g, 0, Nrec);
 | |
| 
 | |
|   num_read = num_there = num_write = 0;
 | |
| 
 | |
|   //  Allocate the table and column block buffer
 | |
|   return AllocateBuffer(g);
 | |
|   } // end of OpenTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate the block buffers for columns used in the query.          */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::AllocateBuffer(PGLOBAL g)
 | |
|   {
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
 | |
|   PCOLDEF cdp;
 | |
|   PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     bool chk = PlgGetUser(g)->Check & CHK_TYPE;
 | |
| 
 | |
|     NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
 | |
| 
 | |
|     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|       memset(NewBlock + Nrec * cdp->GetPoff(),
 | |
|             (IsTypeNum(cdp->GetType()) ? 0 : ' '),
 | |
|                         Nrec * cdp->GetClen());
 | |
| 
 | |
|     for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|       cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
 | |
|                               cp->Buf_Type, Nrec, cp->Format.Length,
 | |
|                               cp->Format.Prec, chk, true, 
 | |
| 				                      cp->IsUnsigned());
 | |
| 
 | |
|     return InitInsert(g);    // Initialize inserting
 | |
|   } else {
 | |
|     if (UseTemp || mode == MODE_DELETE) {
 | |
|       // Allocate all that is needed to move lines
 | |
|       int i = 0, n = (MaxBlk) ? MaxBlk : 1;
 | |
| 
 | |
|       if (!Ncol)
 | |
|         for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|           Ncol++;
 | |
| 
 | |
|       Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
|       Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
|       Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
 | |
| 
 | |
|       for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
 | |
|         Clens[i] = cdp->GetClen();
 | |
|         Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec;
 | |
|         Isnum[i] = IsTypeNum(cdp->GetType());
 | |
|         Buflen = MY_MAX(Buflen, cdp->GetClen());
 | |
|         } // endfor cdp
 | |
| 
 | |
|       if (!UseTemp || MaxBlk) {
 | |
|         Buflen *= Nrec;
 | |
|         To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
 | |
|       } else
 | |
|         NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
 | |
| 
 | |
|       } // endif mode
 | |
| 
 | |
|     for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial())            // Not a pseudo column
 | |
|         cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
 | |
|                                 cp->Format.Length, cp->Format.Prec,
 | |
| 				                        true, true, cp->IsUnsigned());
 | |
| 
 | |
|   } //endif mode
 | |
| 
 | |
|   return false;
 | |
|   } // end of AllocateBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Do initial action when inserting.                                  */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::InitInsert(PGLOBAL g)
 | |
| {
 | |
| 	bool rc = false;
 | |
| 
 | |
|   // We come here in MODE_INSERT only
 | |
|   if (Last == Nrec) {
 | |
|     CurBlk = Block;
 | |
|     CurNum = 0;
 | |
|     AddBlock = !MaxBlk;
 | |
|   } else {
 | |
|     PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|     // The starting point must be at the end of file as for append.
 | |
|     CurBlk = Block - 1;
 | |
|     CurNum = Last;
 | |
| 
 | |
| 		try {
 | |
| 			// Last block must be updated by new values
 | |
| 			for (; cp; cp = (PVCTCOL)cp->Next)
 | |
| 				cp->ReadBlock(g);
 | |
| 
 | |
| 		} catch (int n) {
 | |
| 			if (trace(1))
 | |
| 				htrc("Exception %d: %s\n", n, g->Message);
 | |
| 			rc = true;
 | |
| 		} catch (const char *msg) {
 | |
| 			safe_strcpy(g->Message, sizeof(msg), msg);
 | |
| 			rc = true;
 | |
| 		} // end catch
 | |
| 
 | |
|   } // endif Last
 | |
| 
 | |
| 	if (!rc)
 | |
|     // We are not currently using a temporary file for Insert
 | |
|     T_Stream = Stream;
 | |
| 
 | |
|   return rc;
 | |
|   } // end of InitInsert
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ReadBuffer: Read one line for a VCT file.                          */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::ReadBuffer(PGLOBAL g)
 | |
|   {
 | |
|   int  rc = RC_OK;
 | |
|   MODE mode = Tdbp->GetMode();
 | |
| 
 | |
|   if (Placed)
 | |
|     Placed = false;
 | |
|   else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) {
 | |
|     /*******************************************************************/
 | |
|     /*  New block.                                                     */
 | |
|     /*******************************************************************/
 | |
|     CurNum = 0;
 | |
| 
 | |
|    next:
 | |
|     if (++CurBlk == Block)
 | |
|       return RC_EF;                        // End of file
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Before reading a new block, check whether block optimizing     */
 | |
|     /*  can be done, as well as for join as for local filtering.       */
 | |
|     /*******************************************************************/
 | |
|     switch (Tdbp->TestBlock(g)) {
 | |
|       case RC_EF:
 | |
|         return RC_EF;
 | |
|       case RC_NF:
 | |
|         goto next;
 | |
|       } // endswitch rc
 | |
| 
 | |
|     num_there++;
 | |
|     } // endif CurNum
 | |
| 
 | |
|   if (OldBlk != CurBlk) {
 | |
|     if (mode == MODE_UPDATE) {
 | |
|       /*****************************************************************/
 | |
|       /*  Flush the eventually modified column buffers in old blocks   */
 | |
|       /*  and read the blocks to modify attached to Set columns.       */
 | |
|       /*****************************************************************/
 | |
|       if (MoveLines(g))               // For VECFAM
 | |
|         return RC_FX;
 | |
| 
 | |
|       for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols();
 | |
|                    colp; colp = (PVCTCOL)colp->Next) {
 | |
|         colp->WriteBlock(g);
 | |
|         colp->ReadBlock(g);
 | |
|         } // endfor colp
 | |
| 
 | |
|       } // endif mode
 | |
| 
 | |
|     OldBlk = CurBlk;             // Last block actually read
 | |
|     } // endif oldblk
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK);
 | |
| 
 | |
|   return rc;
 | |
|   } // end of ReadBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base write routine for VCT access method.                     */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::WriteBuffer(PGLOBAL g)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
 | |
|           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_UPDATE) {
 | |
|     // Mode Update is done in ReadDB, we just initialize it here
 | |
|     if (!T_Stream) {
 | |
|       if (UseTemp) {
 | |
|         if (OpenTempFile(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|         // Most of the time, not all table columns are updated.
 | |
|         // This why we must completely pre-fill the temporary file.
 | |
|         Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
 | |
|                         : Block * Nrec;    // To write last lock
 | |
| 
 | |
|         if (MoveIntermediateLines(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|       } else
 | |
|         T_Stream = Stream;
 | |
| 
 | |
|       } // endif T_Stream
 | |
| 
 | |
|   } else {
 | |
|     // Mode Insert
 | |
|     if (MaxBlk && CurBlk == MaxBlk) {
 | |
|       strncpy(g->Message, MSG(TRUNC_BY_ESTIM), sizeof(g->Message));
 | |
|       return RC_EF;       // Too many lines for vector formatted table
 | |
|       } // endif MaxBlk
 | |
| 
 | |
|     if (Closing || ++CurNum == Nrec) {
 | |
|       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|       if (!AddBlock) {
 | |
|         // Write back the updated last block values
 | |
|         for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|           cp->WriteBlock(g);
 | |
| 
 | |
|         if (!Closing && !MaxBlk) {
 | |
|           // For VCT tables, future blocks must be added
 | |
|           char filename[_MAX_PATH];
 | |
| 
 | |
|           // Close the file and reopen it in mode Insert
 | |
|           fclose(Stream);
 | |
|           PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|           if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) {
 | |
|             Closing = true;          // Tell CloseDB of error
 | |
|             return RC_FX;
 | |
|             } // endif Stream
 | |
| 
 | |
|           AddBlock = true;
 | |
|           } // endif Closing
 | |
| 
 | |
|       } else {
 | |
|         // Here we must add a new block to the file
 | |
|         if (Closing)
 | |
|           // Reset the overwritten columns for last block extra records
 | |
|           for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|             memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
 | |
|                    (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
 | |
|                    (Nrec - Last) * cp->Clen);
 | |
| 
 | |
|         if ((size_t)Nrec !=
 | |
|              fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR), To_File, strerror(errno));
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| 
 | |
|       } // endif AddBlock
 | |
| 
 | |
|       if (!Closing) {
 | |
|         CurBlk++;
 | |
|         CurNum = 0;
 | |
|         } // endif Closing
 | |
| 
 | |
|       } // endif Closing || CurNum
 | |
| 
 | |
|   } // endif Mode
 | |
| 
 | |
|   return RC_OK;
 | |
|   } // end of WriteBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for VCT access method.               */
 | |
| /*  Note: lines are moved directly in the files (ooops...)             */
 | |
| /*  Using temp file depends on the Check setting, false by default.    */
 | |
| /***********************************************************************/
 | |
| int VCTFAM::DeleteRecords(PGLOBAL g, int irc)
 | |
|   {
 | |
|   bool eof = false;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
 | |
|           irc, UseTemp, Fpos, Tpos, Spos);
 | |
| 
 | |
|   if (irc != RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  EOF: position Fpos at the end-of-file position.                */
 | |
|     /*******************************************************************/
 | |
|     Fpos = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Fpos placed at file end=%d\n", Fpos);
 | |
| 
 | |
|     eof = UseTemp && !MaxBlk;
 | |
|   } else     // Fpos is the Deleted line position
 | |
|     Fpos = CurBlk * Nrec + CurNum;
 | |
| 
 | |
|   if (Tpos == Spos) {
 | |
|     if (UseTemp) {
 | |
|       /*****************************************************************/
 | |
|       /*  Open the temporary file, Spos is at the beginning of file.   */
 | |
|       /*****************************************************************/
 | |
|       if (OpenTempFile(g))
 | |
|         return RC_FX;
 | |
| 
 | |
|     } else {
 | |
|       /*****************************************************************/
 | |
|       /*  First line to delete. Move of eventual preceding lines is   */
 | |
|       /*  not required here, just the setting of future Spos and Tpos. */
 | |
|       /*****************************************************************/
 | |
|       T_Stream = Stream;
 | |
|       Spos = Tpos = Fpos;
 | |
|     } // endif UseTemp
 | |
| 
 | |
|     } // endif Tpos == Spos
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Move any intermediate lines.                                     */
 | |
|   /*********************************************************************/
 | |
|   if (MoveIntermediateLines(g, &eof))
 | |
|     return RC_FX;
 | |
| 
 | |
|   if (irc == RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  Reposition the file pointer and set Spos.                      */
 | |
|     /*******************************************************************/
 | |
| #ifdef _DEBUG
 | |
|     assert(Spos == Fpos);
 | |
| #endif
 | |
|     Spos++;          // New start position is on next line
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Last call after EOF has been reached.                          */
 | |
|     /*  Update the Block and Last values.                              */
 | |
|     /*******************************************************************/
 | |
|     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
|     Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     if (!UseTemp) {   // The UseTemp case is treated in CloseTableFile
 | |
|       if (!MaxBlk) {
 | |
|         /***************************************************************/
 | |
|         /*  Because the chsize functionality is only accessible with a */
 | |
|         /*  system call we must close the file and reopen it with the  */
 | |
|         /*  open function (_fopen for MS ??) this is still to be       */
 | |
|         /*  checked for compatibility with Text files and other OS's.  */
 | |
|         /***************************************************************/
 | |
|         char filename[_MAX_PATH];
 | |
|         int h;
 | |
| 
 | |
|         /*rc =*/ CleanUnusedSpace(g);           // Clean last block
 | |
|         /*rc =*/ PlugCloseFile(g, To_Fb);
 | |
|         Stream = NULL;                      // For SetBlockInfo
 | |
|         PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|         if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
 | |
|           return RC_FX;
 | |
| 
 | |
|         /***************************************************************/
 | |
|         /*  Remove extra blocks.                                       */
 | |
|         /***************************************************************/
 | |
| #if defined(UNIX)
 | |
|         if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | |
|           close(h);
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| #else
 | |
|         if (chsize(h, Headlen + Block * Blksize)) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(CHSIZE_ERROR), strerror(errno));
 | |
|           close(h);
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| #endif
 | |
| 
 | |
|         close(h);
 | |
| 
 | |
|         if (trace(1))
 | |
|           htrc("done, h=%d irc=%d\n", h, irc);
 | |
| 
 | |
|       } else
 | |
|         // Clean the unused space in the file, this is required when
 | |
|         // inserting again with a partial column list.
 | |
|         if (CleanUnusedSpace(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|       if (ResetTableSize(g, Block, Last))
 | |
|         return RC_FX;
 | |
| 
 | |
|       } // endif UseTemp
 | |
| 
 | |
|   } // endif irc
 | |
| 
 | |
|   return RC_OK;                                      // All is correct
 | |
|   } // end of DeleteRecords
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open a temporary file used while updating or deleting.             */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::OpenTempFile(PGLOBAL g)
 | |
|   {
 | |
|   PCSZ  opmode;
 | |
| 	char  tempname[_MAX_PATH];
 | |
| 	bool  rc = false;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open the temporary file, Spos is at the beginning of file.       */
 | |
|   /*********************************************************************/
 | |
|   PlugSetPath(tempname, To_File, Tdbp->GetPath());
 | |
|   PlugRemoveType(tempname, tempname);
 | |
|   safe_strcat(tempname, sizeof(tempname), ".t");
 | |
|   if (MaxBlk) {
 | |
|     if (MakeEmptyFile(g, tempname))
 | |
|       return true;
 | |
| 
 | |
|     opmode = "r+b";
 | |
|   } else
 | |
|     opmode = "wb";
 | |
| 
 | |
|   if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) {
 | |
|     if (trace(1))
 | |
|       htrc("%s\n", g->Message);
 | |
| 
 | |
|     rc = true;
 | |
|   } else
 | |
|     To_Fbt = PlgGetUser(g)->Openlist;
 | |
| 
 | |
|   return rc;
 | |
|   } // end of OpenTempFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Move intermediate deleted or updated lines.                        */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
 | |
|   {
 | |
|   int    i, dep, off;
 | |
|   int    n;
 | |
|   bool   eof = (b) ? *b : false;
 | |
|   size_t req, len;
 | |
| 
 | |
|   for (n = Fpos - Spos; n > 0 || eof; n -= req) {
 | |
|     /*******************************************************************/
 | |
|     /*  Non consecutive line to delete. Move intermediate lines.       */
 | |
|     /*******************************************************************/
 | |
|     if (!MaxBlk)
 | |
|       req = (size_t)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
 | |
|     else
 | |
|       req = (size_t)MY_MIN(n, Nrec);
 | |
| 
 | |
|     if (req) for (i = 0; i < Ncol; i++) {
 | |
|       if (MaxBlk) {
 | |
|         dep = Deplac[i];
 | |
|         off = Spos * Clens[i];
 | |
|       } else {
 | |
|         if (UseTemp)
 | |
|           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
 | |
| 
 | |
|         dep = Deplac[i] + (Spos / Nrec) * Blksize;
 | |
|         off = (Spos % Nrec) * Clens[i];
 | |
|       } // endif MaxBlk
 | |
| 
 | |
|       if (fseek(Stream, dep + off, SEEK_SET)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(READ_SEEK_ERROR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       len = fread(To_Buf, Clens[i], req, Stream);
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("after read req=%d len=%d\n", req, len);
 | |
| 
 | |
|       if (len != req) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_READ_ERROR), (int) req, (int) len);
 | |
|         return true;
 | |
|         } // endif len
 | |
| 
 | |
|       if (!UseTemp || MaxBlk) {
 | |
|         if (MaxBlk) {
 | |
|           dep = Deplac[i];
 | |
|           off = Tpos * Clens[i];
 | |
|         } else {
 | |
|           dep = Deplac[i] + (Tpos / Nrec) * Blksize;
 | |
|           off = (Tpos % Nrec) * Clens[i];
 | |
|         } // endif MaxBlk
 | |
| 
 | |
|         if (fseek(T_Stream, dep + off, SEEK_SET)) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(WRITE_SEEK_ERR), strerror(errno));
 | |
|           return true;
 | |
|           } // endif
 | |
| 
 | |
|         if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | |
|           return true;
 | |
|           } // endif
 | |
| 
 | |
|         } // endif UseTemp
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("after write pos=%d\n", ftell(Stream));
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|     Tpos += (int)req;
 | |
|     Spos += (int)req;
 | |
| 
 | |
|     if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) {
 | |
|       // Write the full or last block to the temporary file
 | |
|       if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
 | |
|         // Clean the last block in case of future insert,
 | |
|         // must be done here because T_Stream was open in write only.
 | |
|         for (i = 0; i < Ncol; i++) {
 | |
|           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
 | |
|           memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
 | |
|           } // endfor i
 | |
| 
 | |
|       // Write a new block in the temporary file
 | |
|       len = (size_t)Blksize;
 | |
| 
 | |
|       if (fwrite(NewBlock, 1, len, T_Stream) != len) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       if (Spos == Fpos)
 | |
|         eof = false;
 | |
| 
 | |
|       } // endif UseTemp
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|     } // endfor n
 | |
| 
 | |
|   return false;
 | |
|   } // end of MoveIntermediateLines
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Clean deleted space in a VCT or Vec table file.                    */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::CleanUnusedSpace(PGLOBAL g)
 | |
|   {
 | |
|   int     i, dep;
 | |
|   int    n;
 | |
|   size_t  req, len;
 | |
| 
 | |
|   if (!MaxBlk) {
 | |
|     /*******************************************************************/
 | |
|     /*  Clean last block of the VCT table file.                        */
 | |
|     /*******************************************************************/
 | |
|     assert(!UseTemp);
 | |
| 
 | |
|     if (!(n = Nrec - Last))
 | |
|       return false;
 | |
| 
 | |
|     dep = (Block - 1) * Blksize;
 | |
|     req = (size_t)n;
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
 | |
| 
 | |
|       if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(WRITE_SEEK_ERR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|   } else for (n = Fpos - Tpos; n > 0; n -= req) {
 | |
|     /*******************************************************************/
 | |
|     /*  Fill VEC file remaining lines with 0's.                        */
 | |
|     /*  Note: this seems to work even column blocks have been made     */
 | |
|     /*  with Blanks = true. Perhaps should it be set to false for VEC. */
 | |
|     /*******************************************************************/
 | |
|     req = (size_t)MY_MIN(n, Nrec);
 | |
|     memset(To_Buf, 0, Buflen);
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(WRITE_SEEK_ERR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|     Tpos += (int)req;
 | |
|     } // endfor n
 | |
| 
 | |
|   return false;
 | |
|   } // end of CleanUnusedSpace
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for VCT access method.                     */
 | |
| /***********************************************************************/
 | |
| void VCTFAM::CloseTableFile(PGLOBAL g, bool abort)
 | |
|   {
 | |
|   int  rc = 0, wrc = RC_OK;
 | |
|   MODE mode = Tdbp->GetMode();
 | |
| 
 | |
|   Abort = abort;
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     if (Closing)
 | |
|       wrc = RC_FX;                  // Last write was in error
 | |
|     else
 | |
|       if (CurNum) {
 | |
|         // Some more inserted lines remain to be written
 | |
|         Last = CurNum;
 | |
|         Block = CurBlk + 1;
 | |
|         Closing = true;
 | |
|         wrc = WriteBuffer(g);
 | |
|       } else {
 | |
|         Last = Nrec;
 | |
|         Block = CurBlk;
 | |
|         wrc = RC_OK;
 | |
|       } // endif CurNum
 | |
| 
 | |
|     if (wrc != RC_FX) {
 | |
|       rc = ResetTableSize(g, Block, Last);
 | |
|     } else if (AddBlock) {
 | |
|       // Last block was not written
 | |
|       rc = ResetTableSize(g, CurBlk, Nrec);
 | |
| 			throw 44;
 | |
|     } // endif
 | |
| 
 | |
|   } else if (mode == MODE_UPDATE) {
 | |
|     // Write back to file any pending modifications
 | |
|     for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
 | |
|                  colp; colp = (PVCTCOL)colp->Next)
 | |
|       colp->WriteBlock(g);
 | |
| 
 | |
|     if (UseTemp && T_Stream) {
 | |
|       rc = RenameTempFile(g);
 | |
| 
 | |
|       if (Header) {
 | |
|         // Header must be set because it was not set in temp file
 | |
|         Stream = T_Stream = NULL;      // For SetBlockInfo
 | |
|         rc = SetBlockInfo(g);
 | |
|         } // endif Header
 | |
| 
 | |
|       } // endif UseTemp
 | |
| 
 | |
|   } else if (mode == MODE_DELETE && UseTemp && T_Stream) {
 | |
|     if (MaxBlk)
 | |
|       rc = CleanUnusedSpace(g);
 | |
| 
 | |
|     if ((rc = RenameTempFile(g)) != RC_FX) {
 | |
|       Stream = T_Stream = NULL;      // For SetBlockInfo
 | |
|       rc = ResetTableSize(g, Block, Last);
 | |
|       } // endif rc
 | |
| 
 | |
|   } // endif's mode
 | |
| 
 | |
|   if (!(UseTemp && T_Stream))
 | |
|     rc = PlugCloseFile(g, To_Fb);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n",
 | |
|           To_File, wrc, rc);
 | |
| 
 | |
|   Stream = NULL;
 | |
|   } // end of CloseTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for VCT access method.                     */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last)
 | |
|   {
 | |
|   bool rc = false;
 | |
| 
 | |
|   // Set Block and Last values for TDBVCT::MakeBlockValues
 | |
|   Block = block;
 | |
|   Last = last;
 | |
| 
 | |
|   if (!Split) {
 | |
|     if (!Header) {
 | |
|       // Update catalog values for Block and Last
 | |
|       PVCTDEF defp = (PVCTDEF)Tdbp->GetDef();
 | |
| 
 | |
|       defp->SetBlock(Block);
 | |
|       defp->SetLast(Last);
 | |
| 
 | |
|       if (!defp->SetIntCatInfo("Blocks", Block) ||
 | |
|           !defp->SetIntCatInfo("Last", Last)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(UPDATE_ERROR), "Header");
 | |
|         rc = true;
 | |
|         } // endif
 | |
| 
 | |
|     } else
 | |
|       rc = SetBlockInfo(g);
 | |
| 
 | |
|     } // endif Split
 | |
| 
 | |
|   Tdbp->ResetSize();
 | |
|   return rc;
 | |
|   } // end of ResetTableSize
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Rewind routine for VCT access method.                              */
 | |
| /***********************************************************************/
 | |
| void VCTFAM::Rewind(void)
 | |
|   {
 | |
|   // In mode update we need to read Set Column blocks
 | |
|   if (Tdbp->GetMode() == MODE_UPDATE)
 | |
|     OldBlk = -1;
 | |
| 
 | |
|   // Initialize so block optimization is called for 1st block
 | |
|   CurBlk = -1;
 | |
|   CurNum = Nrec - 1;
 | |
| //rewind(Stream);             will be placed by fseek
 | |
|   } // end of Rewind
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ReadBlock: Read column values from current block.                  */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   int     len;
 | |
|   size_t  n;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to read.              */
 | |
|   /*********************************************************************/
 | |
|   if (MaxBlk)                                 // True vector format
 | |
|     len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk);
 | |
|   else                                        // Blocked vector format
 | |
|     len = Nrec * (colp->Deplac + Lrecl * CurBlk);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n",
 | |
|           len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
 | |
| 
 | |
|   if (fseek(Stream, len, SEEK_SET)) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
 | |
|                                         (size_t)Nrec, Stream);
 | |
| 
 | |
|   if (n != (size_t)Nrec) {
 | |
|     if (errno == NO_ERROR)
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_READ_NUMBER), (int) n, To_File);
 | |
|     else
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR),
 | |
|               To_File, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc(" Read error: %s\n", g->Message);
 | |
| 
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   if (trace(1))
 | |
|     num_read++;
 | |
| 
 | |
|   return false;
 | |
|   } // end of ReadBlock
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  WriteBlock: Write back current column values for one block.        */
 | |
| /*  Note: the test of Status is meant to prevent physical writing of   */
 | |
| /*  the block during the checking loop in mode Update. It is set to    */
 | |
| /*  BUF_EMPTY when reopening the table between the two loops.          */
 | |
| /***********************************************************************/
 | |
| bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   int   len;
 | |
|   size_t n;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to write.             */
 | |
|   /*********************************************************************/
 | |
|   if (MaxBlk)                               // File has Vector format
 | |
|     len = Headlen
 | |
|         + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
 | |
|   else                                      // Old VCT format
 | |
|     len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
 | |
|           Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
 | |
| 
 | |
|   if (fseek(T_Stream, len, SEEK_SET)) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   // Here Nrec was changed to CurNum in mode Insert,
 | |
|   // this is the true number of records to write,
 | |
|   // this also avoid writing garbage in the file for true vector tables.
 | |
|   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec;
 | |
| 
 | |
|   if (n != fwrite(colp->Blk->GetValPointer(),
 | |
|                             (size_t)colp->Clen, n, T_Stream)) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR),
 | |
|             (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Write error: %s\n", strerror(errno));
 | |
| 
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
| #if defined(UNIX)
 | |
|   fflush(T_Stream); //NGC
 | |
| #endif
 | |
| 
 | |
| #ifdef _DEBUG
 | |
|   num_write++;
 | |
| #endif
 | |
| 
 | |
|   return false;
 | |
|   } // end of WriteBlock
 | |
| 
 | |
| /* -------------------------- Class VCMFAM --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the VCMFAM class.                                */
 | |
| /***********************************************************************/
 | |
| VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
 | |
|   {
 | |
|   Memory = NULL;
 | |
|   Memcol = NULL;
 | |
|   } // end of VCMFAM standard constructor
 | |
| 
 | |
| VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp)
 | |
|   {
 | |
|   Memory = txfp->Memory;
 | |
|   Memcol = txfp->Memcol;
 | |
|   } // end of VCMFAM copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Mapped VCT Access Method opening routine.                          */
 | |
| /*  New method now that this routine is called recursively (last table */
 | |
| /*  first in reverse order): index blocks are immediately linked to    */
 | |
| /*  join block of next table if it exists or else are discarted.       */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::OpenTableFile(PGLOBAL g)
 | |
|   {
 | |
|   char    filename[_MAX_PATH];
 | |
|   size_t  len;
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PFBLOCK fp = NULL;
 | |
|   PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Update block info if necessary.                                  */
 | |
|   /*********************************************************************/
 | |
|   if (Block < 0)
 | |
|     if ((Headlen = GetBlockInfo(g)) < 0)
 | |
|       return true;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  We used the file name relative to recorded datapath.             */
 | |
|   /*********************************************************************/
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  The whole file will be mapped so we can use it as if it were     */
 | |
|   /*  entirely read into virtual memory.                               */
 | |
|   /*  Firstly we check whether this file have been already mapped.     */
 | |
|   /*********************************************************************/
 | |
|   if (mode == MODE_READ) {
 | |
|     for (fp = dbuserp->Openlist; fp; fp = fp->Next)
 | |
|       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
 | |
|                      && fp->Count && fp->Mode == mode)
 | |
|         break;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count);
 | |
| 
 | |
|   } else
 | |
|     fp = NULL;
 | |
| 
 | |
|   if (fp) {
 | |
|     /*******************************************************************/
 | |
|     /*  File already mapped. Just increment use count and get pointer. */
 | |
|     /*******************************************************************/
 | |
|     fp->Count++;
 | |
|     Memory = fp->Memory;
 | |
|     len = fp->Length;
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  If required, delete the whole file if no filtering is implied. */
 | |
|     /*******************************************************************/
 | |
|     bool   del;
 | |
|     HANDLE hFile;
 | |
|     MEMMAP mm;
 | |
|     MODE   mapmode = mode;
 | |
| 
 | |
|     if (mode == MODE_INSERT) {
 | |
|       if (MaxBlk) {
 | |
|         if (!Block)
 | |
|           if (MakeEmptyFile(g, To_File))
 | |
|             return true;
 | |
| 
 | |
|         // Inserting will be like updating the file
 | |
|         mapmode = MODE_UPDATE;
 | |
|       } else {
 | |
|         strncpy(g->Message, "MAP Insert is for VEC Estimate tables only", sizeof(g->Message));
 | |
|         return true;
 | |
|       } // endif MaxBlk
 | |
| 
 | |
|       } // endif mode
 | |
| 
 | |
|     del = mode == MODE_DELETE && !Tdbp->GetNext();
 | |
| 
 | |
|     if (del) {
 | |
|       DelRows = Cardinality(g);
 | |
| 
 | |
|       // This will stop the process by causing GetProgMax to return 0.
 | |
| //    ResetTableSize(g, 0, Nrec);          must be done later
 | |
|       } // endif del
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Create the mapping file object.                                */
 | |
|     /*******************************************************************/
 | |
|     hFile = CreateFileMap(g, filename, &mm, mapmode, del);
 | |
| 
 | |
|     if (hFile == INVALID_HANDLE_VALUE) {
 | |
|       DWORD rc = GetLastError();
 | |
| 
 | |
|       if (!(*g->Message))
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
 | |
|                 "map", (int) rc, filename);
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("%s\n", g->Message);
 | |
| 
 | |
|       return (mode == MODE_READ && rc == ENOENT)
 | |
|               ? PushWarning(g, Tdbp) : true;
 | |
|       } // endif hFile
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Get the file size.                                             */
 | |
|     /*******************************************************************/
 | |
| 		len = (size_t)mm.lenL;
 | |
| 
 | |
| 		if (mm.lenH)
 | |
| 			len += ((size_t)mm.lenH * 0x000000001LL);
 | |
| 
 | |
| 		Memory = (char *)mm.memory;
 | |
| 
 | |
|     if (!len) {             // Empty or deleted file
 | |
|       CloseFileHandle(hFile);
 | |
|       bool rc = ResetTableSize(g, 0, Nrec);
 | |
|       return (mapmode == MODE_UPDATE) ? true : rc;
 | |
|       } // endif len
 | |
| 
 | |
|     if (!Memory) {
 | |
|       CloseFileHandle(hFile);
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR),
 | |
|                           filename, GetLastError());
 | |
|       return true;
 | |
|       } // endif Memory
 | |
| 
 | |
|     if (mode != MODE_DELETE) {
 | |
|       CloseFileHandle(hFile);                    // Not used anymore
 | |
|       hFile = INVALID_HANDLE_VALUE;              // For Fblock
 | |
|       } // endif Mode
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Link a Fblock. This make possible to reuse already opened maps */
 | |
|     /*  and also to automatically unmap them in case of error g->jump. */
 | |
|     /*  Note: block can already exist for previously closed file.      */
 | |
|     /*******************************************************************/
 | |
|     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
 | |
|     fp->Type = TYPE_FB_MAP;
 | |
|     fp->Fname = PlugDup(g, filename);
 | |
|     fp->Next = dbuserp->Openlist;
 | |
|     dbuserp->Openlist = fp;
 | |
|     fp->Count = 1;
 | |
|     fp->Length = len;
 | |
|     fp->Memory = Memory;
 | |
|     fp->Mode = mode;
 | |
|     fp->File = NULL;
 | |
|     fp->Handle = hFile;                // Used for Delete
 | |
|   } // endif fp
 | |
| 
 | |
|   To_Fb = fp;                               // Useful when closing
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
 | |
|           fp, fp->Count, Memory, len);
 | |
| 
 | |
|   return AllocateBuffer(g);
 | |
|   } // end of OpenTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate the block buffers for columns used in the query.          */
 | |
| /*  Give a dummy value (1) to prevent allocating the value block.     */
 | |
| /*  It will be set pointing into the memory map of the file.           */
 | |
| /*  Note: Memcol must be set for all columns because it can be used    */
 | |
| /*  for set columns in Update. Clens values are used only in Delete.   */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::AllocateBuffer(PGLOBAL g)
 | |
|   {
 | |
|   int     m, i = 0;
 | |
|   bool    b = Tdbp->GetMode() == MODE_DELETE;
 | |
|   PVCTCOL cp;
 | |
|   PCOLDEF cdp;
 | |
|   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
 | |
| 
 | |
|   // Calculate the number of columns
 | |
|   if (!Ncol)
 | |
|     for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|       Ncol++;
 | |
| 
 | |
|   // To store the start position of each column
 | |
|   Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*));
 | |
|   m = (MaxBlk) ? MaxBlk : 1;
 | |
| 
 | |
|   // We will need all column sizes and type for Delete
 | |
|   if (b) {
 | |
|     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
|     Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
 | |
|     } // endif b
 | |
| 
 | |
|   for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) {
 | |
|     if (b) {
 | |
|       Clens[i] = cdp->GetClen();
 | |
|       Isnum[i] = IsTypeNum(cdp->GetType());
 | |
|       } // endif b
 | |
| 
 | |
|     Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec;
 | |
|     } // endfor cdp
 | |
| 
 | |
|   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
 | |
|     if (!cp->IsSpecial()) {            // Not a pseudo column
 | |
|       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
 | |
|                               cp->Format.Length, cp->Format.Prec,
 | |
| 				                      true, true, cp->IsUnsigned());
 | |
|       cp->AddStatus(BUF_MAPPED);
 | |
|       } // endif IsSpecial
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_INSERT)
 | |
|     return InitInsert(g);
 | |
| 
 | |
|   return false;
 | |
|   } // end of AllocateBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Do initial action when inserting.                                  */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::InitInsert(PGLOBAL g)
 | |
| {
 | |
|   bool     rc = false;
 | |
|   volatile PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|   // We come here in MODE_INSERT only
 | |
|   if (Last == Nrec) {
 | |
|     CurBlk = Block;
 | |
|     CurNum = 0;
 | |
|     AddBlock = !MaxBlk;
 | |
|   } else {
 | |
|     // The starting point must be at the end of file as for append.
 | |
|     CurBlk = Block - 1;
 | |
|     CurNum = Last;
 | |
|   } // endif Last
 | |
| 
 | |
| 	try {
 | |
| 		// Initialize the column block pointer
 | |
| 		for (; cp; cp = (PVCTCOL)cp->Next)
 | |
| 			cp->ReadBlock(g);
 | |
| 
 | |
| 	} catch (int n) {
 | |
| 	  if (trace(1))
 | |
| 		  htrc("Exception %d: %s\n", n, g->Message);
 | |
| 		rc = true;
 | |
|   } catch (const char *msg) {
 | |
| 	  safe_strcpy(g->Message, sizeof(g->Message), msg);
 | |
| 		rc = true;
 | |
|   } // end catch
 | |
| 
 | |
|   return rc;
 | |
| } // end of InitInsert
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base write routine for VMP access method.                     */
 | |
| /***********************************************************************/
 | |
| int VCMFAM::WriteBuffer(PGLOBAL g)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
 | |
|           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
 | |
| 
 | |
|   // Mode Update being done in ReadDB we process here Insert mode only.
 | |
|   if (Tdbp->GetMode() == MODE_INSERT) {
 | |
|     if (CurBlk == MaxBlk) {
 | |
|       strncpy(g->Message, MSG(TRUNC_BY_ESTIM), sizeof(g->Message));
 | |
|       return RC_EF;       // Too many lines for vector formatted table
 | |
|       } // endif MaxBlk
 | |
| 
 | |
|     if (Closing || ++CurNum == Nrec) {
 | |
|       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|       // Write back the updated last block values
 | |
|       for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|         cp->WriteBlock(g);
 | |
| 
 | |
|       if (!Closing) {
 | |
|         CurBlk++;
 | |
|         CurNum = 0;
 | |
| 
 | |
|         // Re-initialize the column block pointer
 | |
|         for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
 | |
|           cp->ReadBlock(g);
 | |
| 
 | |
|         } // endif Closing
 | |
| 
 | |
|       } // endif Closing || CurNum
 | |
| 
 | |
|     } // endif Mode
 | |
| 
 | |
|   return RC_OK;
 | |
|   } // end of WriteBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for VMP access method.               */
 | |
| /*  Lines between deleted lines are moved in the mapfile view.         */
 | |
| /***********************************************************************/
 | |
| int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
 | |
|                         irc, To_Buf, Tpos, Spos);
 | |
| 
 | |
|   if (irc != RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  EOF: position Fpos at the top of map position.                 */
 | |
|     /*******************************************************************/
 | |
|     Fpos = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Fpos placed at file top=%p\n", Fpos);
 | |
| 
 | |
|   } else     // Fpos is the Deleted line position
 | |
|     Fpos = CurBlk * Nrec + CurNum;
 | |
| 
 | |
|   if (Tpos == Spos) {
 | |
|     /*******************************************************************/
 | |
|     /*  First line to delete. Move of eventual preceding lines is     */
 | |
|     /*  not required here, just setting of future Spos and Tpos.       */
 | |
|     /*******************************************************************/
 | |
|     Tpos = Spos = Fpos;
 | |
|   } else
 | |
|     (void)MoveIntermediateLines(g);
 | |
| 
 | |
|   if (irc == RC_OK) {
 | |
|     Spos = Fpos + 1;                               // New start position
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Last call after EOF has been reached.                          */
 | |
|     /*******************************************************************/
 | |
|     int i, m, n;
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
 | |
|     /*******************************************************************/
 | |
|     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
|     Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     if (!MaxBlk) {
 | |
|       PFBLOCK fp = To_Fb;
 | |
| 
 | |
|       // Clean the unused part of the last block
 | |
|       m = (Block - 1) * Blksize;
 | |
|       n = Nrec - Last;
 | |
| 
 | |
|       for (i = 0; i < Ncol; i++)
 | |
|         memset(Memcol[i] + m + Last * Clens[i],
 | |
|             (Isnum[i]) ? 0 : ' ', n * Clens[i]);
 | |
| 
 | |
|       // We must Unmap the view and use the saved file handle
 | |
|       // to put an EOF at the end of the last block of the file.
 | |
|       CloseMemMap(fp->Memory, (size_t)fp->Length);
 | |
|       fp->Count = 0;                            // Avoid doing it twice
 | |
| 
 | |
|       // Remove extra blocks
 | |
|       n = Block * Blksize;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
 | |
| 
 | |
|       if (drc == 0xFFFFFFFF) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR),
 | |
|                             "SetFilePointer", GetLastError());
 | |
|         CloseHandle(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
 | |
| 
 | |
|       if (!SetEndOfFile(fp->Handle)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR),
 | |
|                             "SetEndOfFile", GetLastError());
 | |
|         CloseHandle(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       CloseHandle(fp->Handle);
 | |
| #else    // UNIX
 | |
|       if (ftruncate(fp->Handle, (off_t)n)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | |
|         close(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       close(fp->Handle);
 | |
| #endif   // UNIX
 | |
|     } else
 | |
|       // True vector table, Table file size does not change.
 | |
|       // Just clean the unused part of the file.
 | |
|       for (n = Fpos - Tpos, i = 0; i < Ncol; i++)
 | |
|         memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]);
 | |
| 
 | |
|     // Reset Last and Block values in the catalog
 | |
|     PlugCloseFile(g, To_Fb);      // in case of Header
 | |
|     ResetTableSize(g, Block, Last);
 | |
|   } // endif irc
 | |
| 
 | |
|   return RC_OK;                                      // All is correct
 | |
|   } // end of DeleteRecords
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Move intermediate deleted or updated lines.                        */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::MoveIntermediateLines(PGLOBAL, bool *)
 | |
|   {
 | |
|   int i, m, n;
 | |
| 
 | |
|   if ((n = Fpos - Spos) > 0) {
 | |
|     /*******************************************************************/
 | |
|     /*  Non consecutive line to delete. Move intermediate lines.       */
 | |
|     /*******************************************************************/
 | |
|     if (!MaxBlk) {
 | |
|       // Old VCT format, moving must respect block limits
 | |
|       char *ps, *pt;
 | |
|       int   req, soff, toff;
 | |
| 
 | |
|       for (; n > 0; n -= req) {
 | |
|         soff = Spos % Nrec;
 | |
|         toff = Tpos % Nrec;
 | |
|         req = (size_t)MY_MIN(n, Nrec - MY_MAX(soff, toff));
 | |
| 
 | |
|         for (i = 0; i < Ncol; i++) {
 | |
|           ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i];
 | |
|           pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i];
 | |
|           memmove(pt, ps, req * Clens[i]);
 | |
|           } // endfor i
 | |
| 
 | |
|         Tpos += req;
 | |
|         Spos += req;
 | |
|         } // endfor n
 | |
| 
 | |
|     } else {
 | |
|       // True vector format, all is simple...
 | |
|       for (i = 0; i < Ncol; i++) {
 | |
|         m = Clens[i];
 | |
|         memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m);
 | |
|         } // endfor i
 | |
| 
 | |
|       Tpos += n;
 | |
|     } // endif MaxBlk
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("move %d bytes\n", n);
 | |
| 
 | |
|     } // endif n
 | |
| 
 | |
|   return false;
 | |
|   } // end of MoveIntermediate Lines
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for VMP access method.                     */
 | |
| /***********************************************************************/
 | |
| void VCMFAM::CloseTableFile(PGLOBAL g, bool)
 | |
|   {
 | |
|   int  wrc = RC_OK;
 | |
|   MODE mode = Tdbp->GetMode();
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     if (!Closing) {
 | |
|       if (CurNum) {
 | |
|         // Some more inserted lines remain to be written
 | |
|         Last = CurNum;
 | |
|         Block = CurBlk + 1;
 | |
|         Closing = true;
 | |
|         wrc = WriteBuffer(g);
 | |
|       } else {
 | |
|         Last = Nrec;
 | |
|         Block = CurBlk;
 | |
|         wrc = RC_OK;
 | |
|       } // endif CurNum
 | |
| 
 | |
|     } else
 | |
|       wrc = RC_FX;                  // Last write was in error
 | |
| 
 | |
|     PlugCloseFile(g, To_Fb);
 | |
| 
 | |
|     if (wrc != RC_FX)
 | |
|       /*rc =*/ ResetTableSize(g, Block, Last);
 | |
| 
 | |
|   } else if (mode != MODE_DELETE || Abort)
 | |
|     PlugCloseFile(g, To_Fb);
 | |
| 
 | |
|   } // end of CloseTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ReadBlock: Read column values from current block.                  */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::ReadBlock(PGLOBAL, PVCTCOL colp)
 | |
|   {
 | |
|   char *mempos;
 | |
|   int   i = colp->Index - 1;
 | |
|   int  n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl);
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the start position of the column block to read.        */
 | |
|   /*********************************************************************/
 | |
|   mempos = Memcol[i] + n * CurBlk;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n",
 | |
|           mempos, i, Nrec, colp->Clen, CurBlk);
 | |
| 
 | |
|   if (colp->GetStatus(BUF_MAPPED))
 | |
|     colp->Blk->SetValPointer(mempos);
 | |
| 
 | |
|   if (trace(1))
 | |
|     num_read++;
 | |
| 
 | |
|   return false;
 | |
|   } // end of ReadBlock
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  WriteBlock: Write back current column values for one block.        */
 | |
| /*  Note: there is nothing to do because we are working directly into  */
 | |
| /*  the mapped file, except when checking for Update but in this case  */
 | |
| /*  we do not want to write back the modifications either.             */
 | |
| /***********************************************************************/
 | |
| bool VCMFAM::WriteBlock(PGLOBAL, PVCTCOL colp __attribute__((unused)))
 | |
|   {
 | |
| #if defined(_DEBUG)
 | |
|   char *mempos;
 | |
|   int   i = colp->Index - 1;
 | |
|   int   n = Nrec * colp->Clen;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to write.             */
 | |
|   /*********************************************************************/
 | |
|   mempos = Memcol[i] + n * CurBlk;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n",
 | |
|           Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk);
 | |
| 
 | |
| #endif // _DEBUG
 | |
| 
 | |
|   return false;
 | |
|   } // end of WriteBlock
 | |
| 
 | |
| /* -------------------------- Class VECFAM --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the VECFAM class.                                */
 | |
| /***********************************************************************/
 | |
| VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
 | |
|   {
 | |
|   Streams = NULL;
 | |
|   To_Fbs = NULL;
 | |
|   To_Bufs = NULL;
 | |
|   Split = true;
 | |
|   Block = Last = -1;
 | |
|   InitUpdate = false;
 | |
|   } // end of VECFAM standard constructor
 | |
| 
 | |
| VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp)
 | |
|   {
 | |
|   Streams = txfp->Streams;
 | |
|   To_Fbs = txfp->To_Fbs;
 | |
|   Clens = txfp->Clens;
 | |
|   To_Bufs = txfp->To_Bufs;
 | |
|   InitUpdate = txfp->InitUpdate;
 | |
|   } // end of VECFAM copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VEC Access Method opening routine.                                 */
 | |
| /*  New method now that this routine is called recursively (last table */
 | |
| /*  first in reverse order): index blocks are immediately linked to    */
 | |
| /*  join block of next table if it exists or else are discarted.       */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::OpenTableFile(PGLOBAL g)
 | |
|   {
 | |
|   char    opmode[4];
 | |
|   int     i;
 | |
|   bool    b= false;
 | |
|   PCOLDEF cdp;
 | |
|   PVCTCOL cp;
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Call Cardinality to set Block and Last values in case it was not */
 | |
|   /*  already called (this happens indeed in test xmode)               */
 | |
|   /*********************************************************************/
 | |
|   Cardinality(g);
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open according to input/output mode required.                    */
 | |
|   /*********************************************************************/
 | |
|   switch (mode) {
 | |
|     case MODE_READ:
 | |
|       snprintf(opmode, sizeof(opmode), "rb");
 | |
|       break;
 | |
|     case MODE_DELETE:
 | |
|       if (!Tdbp->GetNext()) {
 | |
|         // Store the number of deleted lines
 | |
|         DelRows = Cardinality(g);
 | |
| 
 | |
|         // This will delete the whole file
 | |
|         snprintf(opmode, sizeof(opmode), "wb");
 | |
| 
 | |
|         // This will stop the process by causing GetProgMax to return 0.
 | |
|         ResetTableSize(g, 0, Nrec);
 | |
|         break;
 | |
|         } // endif filter
 | |
| 
 | |
|       // Selective delete, pass thru
 | |
|       /* fall through */
 | |
|     case MODE_UPDATE:
 | |
|       UseTemp = Tdbp->IsUsingTemp(g);
 | |
|       snprintf(opmode, sizeof(opmode), (UseTemp) ? "rb": "r+b");
 | |
|       break;
 | |
|     case MODE_INSERT:
 | |
|       snprintf(opmode, sizeof(opmode), "ab");
 | |
|       break;
 | |
|     default:
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | |
|       return true;
 | |
|     } // endswitch Mode
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Initialize the array of file structures.                         */
 | |
|   /*********************************************************************/
 | |
|   if (!Colfn) {
 | |
|     // Prepare the column file name pattern and set Ncol
 | |
|     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
 | |
|     } // endif Colfn
 | |
| 
 | |
|   Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
 | |
|   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
 | |
| 
 | |
|   for (i = 0; i < Ncol; i++) {
 | |
|     Streams[i] = NULL;
 | |
|     To_Fbs[i] = NULL;
 | |
|     } // endif i
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open the files corresponding to columns used in the query.       */
 | |
|   /*********************************************************************/
 | |
|   if (mode == MODE_INSERT || mode == MODE_DELETE) {
 | |
|     // All columns must be written or deleted
 | |
|     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
 | |
|       if (OpenColumnFile(g, opmode, i))
 | |
|         return true;
 | |
| 
 | |
|     // Check for void table or missing columns
 | |
|     for (b = !Streams[0], i = 1; i < Ncol; i++)
 | |
|       if (b != !Streams[i])
 | |
|         return true;
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Open the files corresponding to updated columns of the query.  */
 | |
|     /*******************************************************************/
 | |
|     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
 | |
|          cp = (PVCTCOL)cp->Next)
 | |
|       if (OpenColumnFile(g, opmode, cp->Index - 1))
 | |
|         return true;
 | |
| 
 | |
|     // Open in read only mode the used columns not already open
 | |
|     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial() && !Streams[cp->Index - 1])
 | |
|         if (OpenColumnFile(g, "rb", cp->Index - 1))
 | |
|           return true;
 | |
| 
 | |
|     // Check for void table or missing columns
 | |
|     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
 | |
|                 cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial()) {
 | |
|         if (!i++)
 | |
|           b = !Streams[cp->Index - 1];
 | |
|         else if (b != !Streams[cp->Index - 1])
 | |
|           return true;
 | |
|     
 | |
|         } // endif Special
 | |
| 
 | |
|   } // endif mode
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Allocate the table and column block buffer.                      */
 | |
|   /*********************************************************************/
 | |
|   return (b) ? false : AllocateBuffer(g);
 | |
|   } // end of OpenTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open the file corresponding to one column.                         */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::OpenColumnFile(PGLOBAL g, PCSZ opmode, int i)
 | |
|   {
 | |
|   char    filename[_MAX_PATH];
 | |
|   PDBUSER dup = PlgGetUser(g);
 | |
| 
 | |
|   snprintf(filename, _MAX_PATH, Colfn, i+1);
 | |
| 
 | |
|   if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
 | |
|     if (trace(1))
 | |
|       htrc("%s\n", g->Message);
 | |
| 
 | |
|     return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
 | |
|             ? PushWarning(g, Tdbp) : true;
 | |
|     } // endif Streams
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("File %s is open in mode %s\n", filename, opmode);
 | |
| 
 | |
|   To_Fbs[i] = dup->Openlist;       // Keep track of File blocks
 | |
|   return false;
 | |
|   } // end of OpenColumnFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate the block buffers for columns used in the query.          */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::AllocateBuffer(PGLOBAL g)
 | |
|   {
 | |
|   int     i;
 | |
|   PVCTCOL cp;
 | |
|   PCOLDEF cdp;
 | |
|   PTDBVCT tdbp = (PTDBVCT)Tdbp;
 | |
|   MODE    mode = tdbp->GetMode();
 | |
|   PDOSDEF defp = (PDOSDEF)tdbp->GetDef();
 | |
| 
 | |
|   if (mode != MODE_READ) {
 | |
|     // Allocate what is needed by all modes except Read
 | |
|     T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
 | |
|     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
| 
 | |
|     // Give default values
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       T_Streams[i] = Streams[i];
 | |
|       Clens[i] = 0;
 | |
|       } // endfor i
 | |
| 
 | |
|     } // endif mode
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     bool    chk = PlgGetUser(g)->Check & CHK_TYPE;
 | |
| 
 | |
|     To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*));
 | |
|     cdp = defp->GetCols();
 | |
| 
 | |
|     for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
 | |
|       Clens[i] = cdp->GetClen();
 | |
|       To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]);
 | |
| 
 | |
|       if (cdp->GetType() == TYPE_STRING)
 | |
|         memset(To_Bufs[i], ' ', Nrec * Clens[i]);
 | |
|       else
 | |
|         memset(To_Bufs[i],   0, Nrec * Clens[i]);
 | |
| 
 | |
|       } // endfor cdp
 | |
| 
 | |
|     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
 | |
|       cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1],
 | |
|                               cp->Buf_Type, Nrec, cp->Format.Length,
 | |
|                               cp->Format.Prec, chk, true, cp->IsUnsigned());
 | |
| 
 | |
|     return InitInsert(g);
 | |
|   } else {
 | |
|     if (UseTemp || mode == MODE_DELETE) {
 | |
|       // Allocate all that is needed to move lines and make Temp
 | |
|       if (UseTemp) {
 | |
|         Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|         safe_strcpy(Tempat, _MAX_PATH, Colfn);
 | |
|         PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
 | |
|         PlugRemoveType(Tempat, Tempat);
 | |
|         safe_strcat(Tempat, _MAX_PATH, ".t");
 | |
|         T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
 | |
|         } // endif UseTemp
 | |
| 
 | |
|       if (UseTemp)
 | |
|         for (i = 0; i < Ncol; i++) {
 | |
|           T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL;
 | |
|           T_Fbs[i] = NULL;
 | |
|           } // endfor i
 | |
| 
 | |
|       if (mode == MODE_DELETE) {  // All columns are moved
 | |
|         cdp = defp->GetCols();
 | |
| 
 | |
|         for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
 | |
|           Clens[i] = cdp->GetClen();
 | |
|           Buflen = MY_MAX(Buflen, cdp->GetClen());
 | |
|           } // endfor cdp
 | |
| 
 | |
|       } else {  // Mode Update, only some columns are updated
 | |
|         for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) {
 | |
|           i = cp->Index -1;
 | |
| 
 | |
|           if (UseTemp)
 | |
|             T_Streams[i] = NULL;   // Mark the streams to open
 | |
| 
 | |
|           Clens[i] = cp->Clen;
 | |
|           Buflen = MY_MAX(Buflen, cp->Clen);
 | |
|           } // endfor cp
 | |
| 
 | |
|         InitUpdate = true;         // To be initialized
 | |
|       } // endif mode
 | |
| 
 | |
|       To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec);
 | |
|       } // endif mode
 | |
| 
 | |
|     // Finally allocate column buffers for all modes
 | |
|     for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial())            // Not a pseudo column
 | |
|           cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
 | |
|                                   cp->Format.Length, cp->Format.Prec,
 | |
| 						                      true, true, cp->IsUnsigned());
 | |
| 
 | |
|   } // endif mode
 | |
| 
 | |
|   return false;
 | |
|   } // end of AllocateBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Do initial action when inserting.                                  */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::InitInsert(PGLOBAL)
 | |
|   {
 | |
|   // We come here in MODE_INSERT only
 | |
|   CurBlk = 0;
 | |
|   CurNum = 0;
 | |
|   AddBlock = true;
 | |
|   return false;
 | |
|   } // end of InitInsert
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Reset buffer access according to indexing and to mode.             */
 | |
| /*  >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
 | |
| /***********************************************************************/
 | |
| void VECFAM::ResetBuffer(PGLOBAL g)
 | |
|   {
 | |
|   /*********************************************************************/
 | |
|   /*  If access is random, performances can be much better when the    */
 | |
|   /*  reads are done on only one row, except for small tables that can */
 | |
|   /*  be entirely read in one block. If the index is just used as a    */
 | |
|   /*  bitmap filter, as for Update or Delete, reading will be          */
 | |
|   /*  sequential and we better keep block reading.                     */
 | |
|   /*********************************************************************/
 | |
|   if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) {
 | |
|     Nrec = 1;                       // Better for random access
 | |
|     Rbuf = 0;
 | |
|     OldBlk = -2;                    // Has no meaning anymore
 | |
|     Block = Tdbp->Cardinality(g);   // Blocks are one line now
 | |
|     Last = 1;                       // Probably unuseful
 | |
|     } // endif Mode
 | |
| 
 | |
|   } // end of ResetBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base write routine for VCT access method.                     */
 | |
| /***********************************************************************/
 | |
| int VECFAM::WriteBuffer(PGLOBAL g)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
 | |
|           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_INSERT) {
 | |
|     if (Closing || ++CurNum == Nrec) {
 | |
|       // Here we must add a new blocks to the files
 | |
|       int    i;
 | |
|       size_t n = (size_t)CurNum;
 | |
| 
 | |
|       for (i = 0; i < Ncol; i++)
 | |
|         if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR), To_File, strerror(errno));
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| 
 | |
|       if (!Closing) {
 | |
|         CurBlk++;
 | |
|         CurNum = 0;
 | |
|         } // endif Closing
 | |
| 
 | |
|       } // endif Closing || CurNum
 | |
| 
 | |
|   } else              // Mode Update
 | |
|     // Writing updates being done in ReadDB we do initialization only.
 | |
|     if (InitUpdate) {
 | |
|       if (OpenTempFile(g))
 | |
|         return RC_FX;
 | |
| 
 | |
|       InitUpdate = false;                 // Done
 | |
|       } // endif InitUpdate
 | |
| 
 | |
|   return RC_OK;
 | |
|   } // end of WriteBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for split vertical access methods.   */
 | |
| /*  Note: lines are moved directly in the files (ooops...)             */
 | |
| /*  Using temp file depends on the Check setting, false by default.    */
 | |
| /***********************************************************************/
 | |
| int VECFAM::DeleteRecords(PGLOBAL g, int irc)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
 | |
|           irc, UseTemp, Fpos, Tpos, Spos);
 | |
| 
 | |
|   if (irc != RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  EOF: position Fpos at the end-of-file position.                */
 | |
|     /*******************************************************************/
 | |
|     Fpos = Cardinality(g);
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Fpos placed at file end=%d\n", Fpos);
 | |
| 
 | |
|   } else     // Fpos is the Deleted line position
 | |
|     Fpos = CurBlk * Nrec + CurNum;
 | |
| 
 | |
|   if (Tpos == Spos) {
 | |
|     // First line to delete
 | |
|     if (UseTemp) {
 | |
|       /*****************************************************************/
 | |
|       /*  Open the temporary files, Spos is at the beginning of file.  */
 | |
|       /*****************************************************************/
 | |
|       if (OpenTempFile(g))
 | |
|         return RC_FX;
 | |
| 
 | |
|     } else
 | |
|       /*****************************************************************/
 | |
|       /*  Move of eventual preceding lines is not required here.      */
 | |
|       /*  Set the future Tpos, and give Spos a value to block copying. */
 | |
|       /*****************************************************************/
 | |
|       Spos = Tpos = Fpos;
 | |
| 
 | |
|     } // endif Tpos == Spos
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Move any intermediate lines.                                     */
 | |
|   /*********************************************************************/
 | |
|   if (MoveIntermediateLines(g))
 | |
|     return RC_FX;
 | |
| 
 | |
|   if (irc == RC_OK) {
 | |
| #ifdef _DEBUG
 | |
|     assert(Spos == Fpos);
 | |
| #endif
 | |
|     Spos++;          // New start position is on next line
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Last call after EOF has been reached.                          */
 | |
|     /*******************************************************************/
 | |
|     if (!UseTemp) {
 | |
|       /*****************************************************************/
 | |
|       /* Because the chsize functionality is only accessible with a    */
 | |
|       /* system call we must close the files and reopen them with the  */
 | |
|       /* open function (_fopen for MS??) this is still to be checked   */
 | |
|       /* for compatibility with other OS's.                            */
 | |
|       /*****************************************************************/
 | |
|       char filename[_MAX_PATH];
 | |
|       int  h;                            // File handle, return code
 | |
| 
 | |
|       for (int i = 0; i < Ncol; i++) {
 | |
|         snprintf(filename, _MAX_PATH, Colfn, i + 1);
 | |
|         /*rc =*/ PlugCloseFile(g, To_Fbs[i]);
 | |
| 
 | |
|         if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
 | |
|           return RC_FX;
 | |
| 
 | |
|         /***************************************************************/
 | |
|         /*  Remove extra records.                                      */
 | |
|         /***************************************************************/
 | |
| #if defined(UNIX)
 | |
|         if (ftruncate(h, (off_t)(Tpos * Clens[i]))) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | |
|           close(h);
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| #else
 | |
|         if (chsize(h, Tpos * Clens[i])) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(CHSIZE_ERROR), strerror(errno));
 | |
|           close(h);
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| #endif
 | |
| 
 | |
|         close(h);
 | |
| 
 | |
|         if (trace(1))
 | |
|           htrc("done, h=%d irc=%d\n", h, irc);
 | |
| 
 | |
|         } // endfor i
 | |
| 
 | |
|     } else        // UseTemp
 | |
|       // Ok, now delete old files and rename new temp files
 | |
|       if (RenameTempFile(g) == RC_FX)
 | |
|         return RC_FX;
 | |
| 
 | |
|     // Reset these values for TDBVCT::MakeBlockValues
 | |
|     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
|     Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     if (ResetTableSize(g, Block, Last))
 | |
|       return RC_FX;
 | |
| 
 | |
|   } // endif irc
 | |
| 
 | |
|   return RC_OK;                                      // All is correct
 | |
|   } // end of DeleteRecords
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open temporary files used while updating or deleting.              */
 | |
| /*  Note: the files not updated have been given a T_Stream value of 1. */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::OpenTempFile(PGLOBAL g)
 | |
|   {
 | |
|   char tempname[_MAX_PATH];
 | |
| 
 | |
|   for (int i = 0; i < Ncol; i++)
 | |
|     if (!T_Streams[i]) {
 | |
|       /*****************************************************************/
 | |
|       /*  Open the temporary file, Spos is at the beginning of file.   */
 | |
|       /*****************************************************************/
 | |
|       snprintf(tempname, _MAX_PATH, Tempat, i+1);
 | |
| 
 | |
|       if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
 | |
|         if (trace(1))
 | |
|           htrc("%s\n", g->Message);
 | |
| 
 | |
|         return true;
 | |
|       } else
 | |
|         T_Fbs[i] = PlgGetUser(g)->Openlist;
 | |
| 
 | |
|     } else       // This is a column that is not updated
 | |
|       T_Streams[i] = NULL;        // For RenameTempFile
 | |
| 
 | |
|   return false;
 | |
|   } // end of OpenTempFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Move intermediate updated lines before writing blocks.             */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::MoveLines(PGLOBAL g)
 | |
|   {
 | |
|   if (UseTemp && !InitUpdate) {    // Don't do it in check pass
 | |
|     Fpos = OldBlk * Nrec;
 | |
| 
 | |
|     if (MoveIntermediateLines(g)) {
 | |
|       Closing = true;           // ???
 | |
|       return true;
 | |
|       } // endif UseTemp
 | |
| 
 | |
| //  Spos = Fpos + Nrec;
 | |
|     } // endif UseTemp
 | |
|   return false;
 | |
| 
 | |
|   } // end of MoveLines
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Move intermediate deleted or updated lines.                        */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *)
 | |
|   {
 | |
|   int    i, n;
 | |
|   bool   b = false;
 | |
|   size_t req, len;
 | |
| 
 | |
|   for (n = Fpos - Spos; n > 0; n -= Nrec) {
 | |
|     /*******************************************************************/
 | |
|     /*  Non consecutive line to delete. Move intermediate lines.       */
 | |
|     /*******************************************************************/
 | |
|     req = (size_t)MY_MIN(n, Nrec);
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       if (!T_Streams[i])
 | |
|         continue;             // Non updated column
 | |
| 
 | |
|       if (!UseTemp || !b)
 | |
|         if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(READ_SEEK_ERROR), strerror(errno));
 | |
|           return true;
 | |
|           } // endif
 | |
| 
 | |
|       len = fread(To_Buf, Clens[i], req, Streams[i]);
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("after read req=%d len=%d\n", req, len);
 | |
| 
 | |
|       if (len != req) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_READ_ERROR), (int) req, (int) len);
 | |
|         return true;
 | |
|         } // endif len
 | |
| 
 | |
|       if (!UseTemp)
 | |
|         if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(WRITE_SEEK_ERR), strerror(errno));
 | |
|           return true;
 | |
|           } // endif
 | |
| 
 | |
|       if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | |
|         return true;
 | |
|         } // endif
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("after write pos=%d\n", ftell(Streams[i]));
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|     Tpos += (int)req;
 | |
|     Spos += (int)req;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|     b = true;
 | |
|     } // endfor n
 | |
| 
 | |
|   return false;
 | |
|   } // end of MoveIntermediate Lines
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Delete the old files and rename the new temporary files.           */
 | |
| /***********************************************************************/
 | |
| int VECFAM::RenameTempFile(PGLOBAL g)
 | |
|   {
 | |
|   char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
 | |
|   int   rc = RC_OK;
 | |
| 
 | |
|   // Close all files.
 | |
|   // This loop is necessary because, in case of join,
 | |
|   // the table files can have been open several times.
 | |
|   for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
 | |
|     rc = PlugCloseFile(g, fb);
 | |
| 
 | |
|   for (int i = 0; i < Ncol && rc == RC_OK; i++) {
 | |
|     if (!T_Fbs[i])
 | |
|       continue;
 | |
| 
 | |
|     tempname = (char*)T_Fbs[i]->Fname;
 | |
| 
 | |
|     if (!Abort) {
 | |
|       snprintf(filename, _MAX_PATH, Colfn, i+1);
 | |
|       PlugSetPath(filename, filename, Tdbp->GetPath());
 | |
|       PlugRemoveType(filetemp, filename);
 | |
|       safe_strcat(filetemp, sizeof(filetemp), ".ttt");
 | |
|       remove(filetemp);   // May still be there from previous error
 | |
|   
 | |
|       if (rename(filename, filetemp)) {    // Save file for security
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(RENAME_ERROR),
 | |
|                 filename, filetemp, strerror(errno));
 | |
|         rc = RC_FX;
 | |
|       } else if (rename(tempname, filename)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(RENAME_ERROR),
 | |
|                 tempname, filename, strerror(errno));
 | |
|         rc = rename(filetemp, filename);   // Restore saved file
 | |
|         rc = RC_FX;
 | |
|       } else if (remove(filetemp)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(REMOVE_ERROR),
 | |
|                 filetemp, strerror(errno));
 | |
|         rc = RC_INFO;                      // Acceptable
 | |
|       } // endif's
 | |
| 
 | |
|     } else
 | |
|       remove(tempname);
 | |
| 
 | |
|     } // endfor i
 | |
| 
 | |
|   return rc;
 | |
|   } // end of RenameTempFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for VEC access method.                     */
 | |
| /***********************************************************************/
 | |
| void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
 | |
|   {
 | |
|   int  rc = 0, wrc = RC_OK;
 | |
|   MODE mode = Tdbp->GetMode();
 | |
| 
 | |
|   Abort = abort;
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     if (Closing)
 | |
|       wrc = RC_FX;                  // Last write was in error
 | |
|     else
 | |
|       if (CurNum) {
 | |
|         // Some more inserted lines remain to be written
 | |
|         Last += (CurBlk * Nrec + CurNum -1);
 | |
|         Block += (Last / Nrec);
 | |
|         Last = Last % Nrec + 1;
 | |
|         Closing = true;
 | |
|         wrc = WriteBuffer(g);
 | |
|       } else {
 | |
|         Block += CurBlk;
 | |
|         wrc = RC_OK;
 | |
|       } // endif CurNum
 | |
| 
 | |
|     if (wrc != RC_FX)
 | |
|       rc = ResetTableSize(g, Block, Last);
 | |
|     else
 | |
| 			throw 44;
 | |
| 
 | |
|   } else if (mode == MODE_UPDATE) {
 | |
|     if (UseTemp && !InitUpdate && !Abort) {
 | |
|       // Write any intermediate lines to temp file
 | |
|       Fpos = OldBlk * Nrec;
 | |
|       Abort = MoveIntermediateLines(g) != RC_OK;
 | |
| //    Spos = Fpos + Nrec;
 | |
|       } // endif UseTemp
 | |
| 
 | |
|     // Write back to file any pending modifications
 | |
|     if (wrc == RC_OK)
 | |
|       for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
 | |
|                    colp; colp = (PVCTCOL)colp->Next)
 | |
|         colp->WriteBlock(g);
 | |
| 
 | |
|     if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
 | |
|       // Write any intermediate lines to temp file
 | |
|       Fpos = (Block - 1) * Nrec + Last;
 | |
|       Abort = MoveIntermediateLines(g) != RC_OK;
 | |
|       } // endif UseTemp
 | |
| 
 | |
|   } // endif's mode
 | |
| 
 | |
|   if (UseTemp && !InitUpdate) {
 | |
|     // If they are errors, leave files unchanged
 | |
|     rc = RenameTempFile(g);
 | |
| 
 | |
|   } else if (Streams)
 | |
|     for (int i = 0; i < Ncol; i++)
 | |
|       if (Streams[i]) {
 | |
|         rc = PlugCloseFile(g, To_Fbs[i]);
 | |
|         Streams[i] = NULL;
 | |
|         To_Fbs[i] = NULL;
 | |
|         } // endif Streams
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc);
 | |
| 
 | |
|   } // end of CloseTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ReadBlock: Read column values from current block.                  */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   int     i, len;
 | |
|   size_t  n;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to read.              */
 | |
|   /*********************************************************************/
 | |
|   len = Nrec * colp->Clen * CurBlk;
 | |
|   i = colp->Index - 1;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n",
 | |
|           len, i, Nrec, colp->Deplac, Lrecl, CurBlk);
 | |
| 
 | |
|   if (fseek(Streams[i], len, SEEK_SET)) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
 | |
|                                         (size_t)Nrec, Streams[i]);
 | |
| 
 | |
|   if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) {
 | |
|     char fn[_MAX_PATH];
 | |
| 
 | |
|     snprintf(fn, _MAX_PATH, Colfn, colp->Index);
 | |
| #if defined(_WIN32)
 | |
|     if (feof(Streams[i]))
 | |
| #else   // !_WIN32
 | |
|     if (errno == NO_ERROR)
 | |
| #endif  // !_WIN32
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_READ_NUMBER), (int) n, fn);
 | |
|     else
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR),
 | |
|               fn, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc(" Read error: %s\n", g->Message);
 | |
| 
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   if (trace(1))
 | |
|     num_read++;
 | |
| 
 | |
|   return false;
 | |
|   } // end of ReadBlock
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  WriteBlock: Write back current column values for one block.        */
 | |
| /*  Note: the test of Status is meant to prevent physical writing of   */
 | |
| /*  the block during the checking loop in mode Update. It is set to    */
 | |
| /*  BUF_EMPTY when reopening the table between the two loops.          */
 | |
| /***********************************************************************/
 | |
| bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   int   i, len;
 | |
|   size_t n;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to write.             */
 | |
|   /*********************************************************************/
 | |
|   len = Nrec * colp->Clen * colp->ColBlk;
 | |
|   i = colp->Index - 1;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
 | |
|           Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp)
 | |
|     if (fseek(T_Streams[i], len, SEEK_SET)) {
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | |
|       return true;
 | |
|       } // endif
 | |
| 
 | |
|   // Here Nrec was changed to CurNum in mode Insert,
 | |
|   // this is the true number of records to write,
 | |
|   // this also avoid writing garbage in the file for true vector tables.
 | |
|   n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum
 | |
|     : (colp->ColBlk == Block - 1) ? Last : Nrec;
 | |
| 
 | |
|   if (n != fwrite(colp->Blk->GetValPointer(),
 | |
|                             (size_t)colp->Clen, n, T_Streams[i])) {
 | |
|     char fn[_MAX_PATH];
 | |
| 
 | |
|     snprintf(fn, _MAX_PATH, (UseTemp) ? Tempat : Colfn, colp->Index);
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR), fn, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Write error: %s\n", strerror(errno));
 | |
| 
 | |
|     return true;
 | |
|   } else
 | |
|     Spos = Fpos + n;
 | |
| 
 | |
| #if defined(UNIX)
 | |
|   fflush(Streams[i]); //NGC
 | |
| #endif
 | |
|   return false;
 | |
|   } // end of WriteBlock
 | |
| 
 | |
| /* -------------------------- Class VMPFAM --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the VMPFAM class.                                */
 | |
| /***********************************************************************/
 | |
| VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp)
 | |
|   {
 | |
|   To_Fbs = NULL;
 | |
|   Split = true;
 | |
|   Block = Last = -1;
 | |
|   } // end of VMPFAM standard constructor
 | |
| 
 | |
| VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
 | |
|   {
 | |
|   To_Fbs = txfp->To_Fbs;
 | |
|   } // end of VMPFAM copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VCT Access Method opening routine.                                 */
 | |
| /*  New method now that this routine is called recursively (last table */
 | |
| /*  first in reverse order): index blocks are immediately linked to    */
 | |
| /*  join block of next table if it exists or else are discarted.       */
 | |
| /***********************************************************************/
 | |
| bool VMPFAM::OpenTableFile(PGLOBAL g)
 | |
|   {
 | |
|   int     i;
 | |
|   bool    b = false;
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PCOLDEF cdp;
 | |
|   PVCTCOL cp;
 | |
|   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
 | |
| 
 | |
|   if (mode == MODE_DELETE && !Tdbp->GetNext()) {
 | |
|     DelRows = Cardinality(g);
 | |
| 
 | |
|     // This will stop the process by causing GetProgMax to return 0.
 | |
|     ResetTableSize(g, 0, Nrec);
 | |
|   } else
 | |
|     Cardinality(g);        // See comment in VECFAM::OpenTbleFile
 | |
| 
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Prepare the filename pattern for column files and set Ncol.      */
 | |
|   /*********************************************************************/
 | |
|   if (!Colfn) {
 | |
|     // Prepare the column file name pattern
 | |
|     Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|     Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
 | |
|     } // endif Colfn
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Initialize the array of file structures.                         */
 | |
|   /*********************************************************************/
 | |
|   Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *));
 | |
|   To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
 | |
| 
 | |
|   for (i = 0; i < Ncol; i++) {
 | |
|     Memcol[i] = NULL;
 | |
|     To_Fbs[i] = NULL;
 | |
|     } // endif i
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open the files corresponding to columns used in the query.       */
 | |
|   /*********************************************************************/
 | |
|   if (mode == MODE_DELETE) {
 | |
|     // All columns are used in Delete mode
 | |
|     for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
 | |
|       if (MapColumnFile(g, mode, i))
 | |
|         return true;
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Open the files corresponding to updated columns of the query.  */
 | |
|     /*******************************************************************/
 | |
|     for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
 | |
|          cp = (PVCTCOL)cp->Next)
 | |
|       if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1))
 | |
|         return true;
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /* Open other non already open used columns (except pseudos)      */
 | |
|     /*******************************************************************/
 | |
|     for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial() && !Memcol[cp->Index - 1])
 | |
|         if (MapColumnFile(g, MODE_READ, cp->Index - 1))
 | |
|           return true;
 | |
| 
 | |
|     // Check for void table or missing columns
 | |
|     for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
 | |
|                 cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial()) {
 | |
|         if (!i++)
 | |
|           b = !Memcol[cp->Index - 1];
 | |
|         else if (b != !Memcol[cp->Index - 1])
 | |
|           return true;
 | |
| 
 | |
|         } // endif Special
 | |
| 
 | |
|   } // endif mode
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Allocate the table and column block buffer.                      */
 | |
|   /*********************************************************************/
 | |
|   return (b) ? false : AllocateBuffer(g);
 | |
|   } // end of OpenTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open the file corresponding to one column.                         */
 | |
| /***********************************************************************/
 | |
| bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
 | |
|   {
 | |
|   char    filename[_MAX_PATH];
 | |
|   size_t  len;
 | |
|   HANDLE  hFile;
 | |
|   MEMMAP  mm;
 | |
|   PFBLOCK fp;
 | |
|   PDBUSER dup = PlgGetUser(g);
 | |
| 
 | |
|   snprintf(filename, _MAX_PATH, Colfn, i+1);
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  The whole file will be mapped so we can use it as                */
 | |
|   /*  if it were entirely read into virtual memory.                    */
 | |
|   /*  Firstly we check whether this file have been already mapped.     */
 | |
|   /*********************************************************************/
 | |
|   if (mode == MODE_READ) {
 | |
|     for (fp = dup->Openlist; fp; fp = fp->Next)
 | |
|       if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
 | |
|                      && fp->Count && fp->Mode == mode)
 | |
|         break;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Mapping file, fp=%p\n", fp);
 | |
| 
 | |
|   } else
 | |
|     fp = NULL;
 | |
| 
 | |
|   if (fp) {
 | |
|     /*******************************************************************/
 | |
|     /*  File already mapped. Just increment use count and get pointer. */
 | |
|     /*******************************************************************/
 | |
|     fp->Count++;
 | |
|     Memcol[i] = fp->Memory;
 | |
|     len = fp->Length;
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Create the mapping file object.                                */
 | |
|     /*******************************************************************/
 | |
|     hFile = CreateFileMap(g, filename, &mm, mode, DelRows);
 | |
| 
 | |
|     if (hFile == INVALID_HANDLE_VALUE) {
 | |
|       DWORD rc = GetLastError();
 | |
| 
 | |
|       if (!(*g->Message))
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
 | |
|                 "map", (int) rc, filename);
 | |
|       if (trace(1))
 | |
|         htrc("%s\n", g->Message);
 | |
| 
 | |
|       return (mode == MODE_READ && rc == ENOENT)
 | |
|               ? PushWarning(g, Tdbp) : true;
 | |
|       } // endif hFile
 | |
| 
 | |
|     /*****************************************************************/
 | |
|     /*  Get the file size (assuming file is smaller than 4 GB)       */
 | |
|     /*****************************************************************/
 | |
| 		len = (size_t)mm.lenL;
 | |
| 
 | |
| 		if (mm.lenH)
 | |
| 			len += ((size_t)mm.lenH * 0x000000001LL);
 | |
| 
 | |
| 		Memcol[i] = (char *)mm.memory;
 | |
| 
 | |
|     if (!len) {             // Empty or deleted file
 | |
|       CloseFileHandle(hFile);
 | |
|       ResetTableSize(g, 0, Nrec);
 | |
|       return false;
 | |
|       } // endif len
 | |
| 
 | |
|     if (!Memcol[i]) {
 | |
|       CloseFileHandle(hFile);
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(MAP_VIEW_ERROR),
 | |
|                           filename, GetLastError());
 | |
|       return true;
 | |
|       } // endif Memory
 | |
| 
 | |
|     if (mode != MODE_DELETE) {
 | |
|       CloseFileHandle(hFile);                    // Not used anymore
 | |
|       hFile = INVALID_HANDLE_VALUE;              // For Fblock
 | |
|       } // endif Mode
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Link a Fblock. This make possible to reuse already opened maps */
 | |
|     /*  and also to automatically unmap them in case of error g->jump. */
 | |
|     /*  Note: block can already exist for previously closed file.      */
 | |
|     /*******************************************************************/
 | |
|     fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
 | |
|     fp->Type = TYPE_FB_MAP;
 | |
|     fp->Fname = PlugDup(g, filename);
 | |
|     fp->Next = dup->Openlist;
 | |
|     dup->Openlist = fp;
 | |
|     fp->Count = 1;
 | |
|     fp->Length = len;
 | |
|     fp->Memory = Memcol[i];
 | |
|     fp->Mode = mode;
 | |
|     fp->File = NULL;
 | |
|     fp->Handle = hFile;                        // Used for Delete
 | |
|   } // endif fp
 | |
| 
 | |
|   To_Fbs[i] = fp;                              // Useful when closing
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("fp=%p count=%d MapView=%p len=%d\n",
 | |
|           fp, fp->Count, Memcol[i], len);
 | |
| 
 | |
|   return false;
 | |
|   } // end of MapColumnFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate the block buffers for columns used in the query.          */
 | |
| /*  Give a dummy value (1) to prevent allocating the value block.     */
 | |
| /*  It will be set pointing into the memory map of the file.           */
 | |
| /***********************************************************************/
 | |
| bool VMPFAM::AllocateBuffer(PGLOBAL g)
 | |
|   {
 | |
|   PVCTCOL cp;
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_DELETE) {
 | |
|     PCOLDEF cdp = Tdbp->GetDef()->GetCols();
 | |
| 
 | |
|     Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
| 
 | |
|     for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext())
 | |
|       Clens[i] = cdp->GetClen();
 | |
| 
 | |
|     } // endif mode
 | |
| 
 | |
|   for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
 | |
|     if (!cp->IsSpecial()) {            // Not a pseudo column
 | |
|       cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
 | |
|                               cp->Format.Length, cp->Format.Prec,
 | |
| 				                      true, true, cp->IsUnsigned());
 | |
|       cp->AddStatus(BUF_MAPPED);
 | |
|       } // endif IsSpecial
 | |
| 
 | |
|   return false;
 | |
|   } // end of AllocateBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for VMP access method.               */
 | |
| /*  Lines between deleted lines are moved in the mapfile view.         */
 | |
| /***********************************************************************/
 | |
| int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
 | |
|   {
 | |
|   int  i;
 | |
|   int m, n;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
 | |
|                         irc, To_Buf, Tpos, Spos);
 | |
| 
 | |
|   if (irc != RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  EOF: position Fpos at the top of map position.                 */
 | |
|     /*******************************************************************/
 | |
|     Fpos = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Fpos placed at file top=%p\n", Fpos);
 | |
| 
 | |
|   } else     // Fpos is the Deleted line position
 | |
|     Fpos = CurBlk * Nrec + CurNum;
 | |
| 
 | |
|   if (Tpos == Spos) {
 | |
|     /*******************************************************************/
 | |
|     /*  First line to delete. Move of eventual preceding lines is     */
 | |
|     /*  not required here, just setting of future Spos and Tpos.       */
 | |
|     /*******************************************************************/
 | |
|     Tpos = Fpos;                               // Spos is set below
 | |
|   } else if ((n = Fpos - Spos) > 0) {
 | |
|     /*******************************************************************/
 | |
|     /*  Non consecutive line to delete. Move intermediate lines.       */
 | |
|     /*******************************************************************/
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       m = Clens[i];
 | |
|       memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n);
 | |
|       } // endif i
 | |
| 
 | |
|     Tpos += n;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("move %d bytes\n", n);
 | |
| 
 | |
|     } // endif n
 | |
| 
 | |
|   if (irc == RC_OK) {
 | |
|     Spos = Fpos + 1;                               // New start position
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Last call after EOF has been reached.                          */
 | |
|     /*  We must firstly Unmap the view and use the saved file handle   */
 | |
|     /*  to put an EOF at the end of the copied part of the file.       */
 | |
|     /*******************************************************************/
 | |
|     PFBLOCK fp;
 | |
| 
 | |
|     /*******************************************************************/
 | |
|     /*  Reset the Block and Last values for TDBVCT::MakeBlockValues.   */
 | |
|     /*******************************************************************/
 | |
| //  Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
| //  Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       fp = To_Fbs[i];
 | |
|       CloseMemMap(fp->Memory, (size_t)fp->Length);
 | |
|       fp->Count = 0;                             // Avoid doing it twice
 | |
| 
 | |
|       /*****************************************************************/
 | |
|       /*  Remove extra records.                                        */
 | |
|       /*****************************************************************/
 | |
|       n = Tpos * Clens[i];
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|       DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
 | |
| 
 | |
|       if (drc == 0xFFFFFFFF) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR),
 | |
|                             "SetFilePointer", GetLastError());
 | |
|         CloseHandle(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       if (trace(1))
 | |
|         htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
 | |
| 
 | |
|       if (!SetEndOfFile(fp->Handle)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR),
 | |
|                             "SetEndOfFile", GetLastError());
 | |
|         CloseHandle(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       CloseHandle(fp->Handle);
 | |
| #else    // UNIX
 | |
|       if (ftruncate(fp->Handle, (off_t)n)) {
 | |
|         snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | |
|         close(fp->Handle);
 | |
|         return RC_FX;
 | |
|         } // endif
 | |
| 
 | |
|       close(fp->Handle);
 | |
| #endif   // UNIX
 | |
|       } // endfor i
 | |
| 
 | |
|   } // endif irc
 | |
| 
 | |
|   return RC_OK;                                      // All is correct
 | |
|   } // end of DeleteRecords
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for VMP access method.                     */
 | |
| /***********************************************************************/
 | |
| void VMPFAM::CloseTableFile(PGLOBAL g, bool)
 | |
|   {
 | |
|   if (Tdbp->GetMode() == MODE_DELETE) {
 | |
|     // Set Block and Nrec values for TDBVCT::MakeBlockValues
 | |
|     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
|     Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
|     ResetTableSize(g, Block, Last);
 | |
|   } else if (Tdbp->GetMode() == MODE_INSERT)
 | |
|     assert(false);
 | |
| 
 | |
|   for (int i = 0; i < Ncol; i++)
 | |
|     PlugCloseFile(g, To_Fbs[i]);
 | |
| 
 | |
|   } // end of CloseTableFile
 | |
| 
 | |
| /* -------------------------- Class BGVFAM --------------------------- */
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Implementation of the BGVFAM class.                                */
 | |
| /***********************************************************************/
 | |
| // Constructors
 | |
| BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp)
 | |
|   {
 | |
|   Hfile = INVALID_HANDLE_VALUE;
 | |
|   Tfile = INVALID_HANDLE_VALUE;
 | |
|   BigDep = NULL;
 | |
|   } // end of BGVFAM constructor
 | |
| 
 | |
| BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp)
 | |
|   {
 | |
|   Hfile = txfp->Hfile;
 | |
|   Tfile = txfp->Tfile;
 | |
|   BigDep= txfp->BigDep;
 | |
|   } // end of BGVFAM copy constructor
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Set current position in a big file.                                */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b)
 | |
|   {
 | |
| #if defined(_WIN32)
 | |
|   char          buf[256];
 | |
|   DWORD         drc, m = (b) ? FILE_END : FILE_BEGIN;
 | |
|   LARGE_INTEGER of;
 | |
| 
 | |
|   of.QuadPart = pos;
 | |
|   of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m);
 | |
| 
 | |
|   if (of.LowPart == INVALID_SET_FILE_POINTER &&
 | |
|            (drc = GetLastError()) != NO_ERROR) {
 | |
|     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
 | |
|                   (LPTSTR)buf, sizeof(buf), NULL);
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(SFP_ERROR), buf);
 | |
|     return true;
 | |
|     } // endif
 | |
| #else   // !_WIN32
 | |
|   if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(ERROR_IN_LSK), errno);
 | |
|     return true;
 | |
|     } // endif
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|   return false;
 | |
|   } // end of BigSeek
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Read from a big file.                                              */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
 | |
|   {
 | |
|   bool rc = false;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   DWORD nbr, drc, len = (DWORD)req;
 | |
|   bool  brc = ReadFile(h, inbuf, len, &nbr, NULL);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
 | |
| 
 | |
|   if (!brc || nbr != len) {
 | |
|     char buf[256];  // , *fn = (h == Hfile) ? To_File : "Tempfile";
 | |
| 
 | |
|     if (brc)
 | |
|       strncpy(buf, MSG(BAD_BYTE_READ), 256);
 | |
|     else {
 | |
|       drc = GetLastError();
 | |
|       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
 | |
|                     (LPTSTR)buf, sizeof(buf), NULL);
 | |
|       } // endelse brc
 | |
| 
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, buf);
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("BIGREAD: %s\n", g->Message);
 | |
| 
 | |
|     rc = true;
 | |
|     } // endif brc || nbr
 | |
| #else   // !_WIN32
 | |
|   size_t  len = (size_t)req;
 | |
|   ssize_t nbr = read(h, inbuf, len);
 | |
| 
 | |
|   if (nbr != (ssize_t)len) {
 | |
|     const char *fn = (h == Hfile) ? To_File : "Tempfile";
 | |
| 
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), fn, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n",
 | |
|                      nbr, len, errno, g->Message);
 | |
| 
 | |
|     rc = true;
 | |
|     } // endif nbr
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|   return rc;
 | |
|   } // end of BigRead
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Write into a big file.                                             */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
 | |
|   {
 | |
|   bool rc = false;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   DWORD nbw, drc, len = (DWORD)req;
 | |
|   bool  brc = WriteFile(h, inbuf, len, &nbw, NULL);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
 | |
| 
 | |
|   if (!brc || nbw != len) {
 | |
| 		char buf[256];
 | |
| 		PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
 | |
| 
 | |
|     if (brc)
 | |
|       strncpy(buf, MSG(BAD_BYTE_NUM), 256);
 | |
|     else {
 | |
|       drc = GetLastError();
 | |
|       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                     FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
 | |
|                     (LPTSTR)buf, sizeof(buf), NULL);
 | |
|       } // endelse brc
 | |
| 
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR), fn, buf);
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
 | |
|                       nbw, len, drc, g->Message);
 | |
| 
 | |
|     rc = true;
 | |
|     } // endif brc || nbw
 | |
| #else   // !_WIN32
 | |
|   size_t  len = (size_t)req;
 | |
|   ssize_t nbw = write(h, inbuf, len);
 | |
| 
 | |
|   if (nbw != (ssize_t)len) {
 | |
|     const char *fn = (h == Hfile) ? To_File : "Tempfile";
 | |
| 
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(WRITE_STRERROR), fn, strerror(errno));
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
 | |
|                       nbw, len, errno, g->Message);
 | |
| 
 | |
|     rc = true;
 | |
|     } // endif nbr
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|   return rc;
 | |
|   } // end of BigWrite
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Get the Headlen, Block and Last info from the file header.         */
 | |
| /***********************************************************************/
 | |
| int BGVFAM::GetBlockInfo(PGLOBAL g)
 | |
|   {
 | |
|   char      filename[_MAX_PATH];
 | |
|   int       n;
 | |
|   VECHEADER vh;
 | |
|   HANDLE    h;
 | |
| 
 | |
|   if (Header < 1 || Header > 3 || !MaxBlk) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Invalid header value %d", Header);
 | |
|     return -1;
 | |
|   } else
 | |
|     n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
 | |
| 
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (Header == 2)
 | |
|   {
 | |
|     PlugRemoveType(filename, filename);
 | |
|     safe_strcat(filename, sizeof(filename), ".blk");
 | |
|   }
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   LARGE_INTEGER len;
 | |
| 
 | |
|   h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
 | |
|                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (h != INVALID_HANDLE_VALUE) {
 | |
|     // Get the size of the file (can be greater than 4 GB)
 | |
|     len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
 | |
|     } // endif h
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE || !len.QuadPart) {
 | |
| #else   // !_WIN32
 | |
|   h = open64(filename, O_RDONLY, 0);
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE || !_filelength(h)) {
 | |
| #endif  // !_WIN32
 | |
|     // Consider this is a void table
 | |
|     if (trace(1))
 | |
|       htrc("Void table h=%d\n", h);
 | |
| 
 | |
|     Last = Nrec;
 | |
|     Block = 0;
 | |
| 
 | |
|     if (h != INVALID_HANDLE_VALUE)
 | |
|       CloseFileHandle(h);
 | |
| 
 | |
|     return n;
 | |
|   } else if (Header == 3)
 | |
|     /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
 | |
| 
 | |
|   if (BigRead(g, h, &vh, sizeof(vh))) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Error reading header file %s", filename);
 | |
|     n = -1;
 | |
|   } else if (MaxBlk * Nrec != vh.MaxRec) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
 | |
|                         vh.MaxRec, MaxBlk, Nrec);
 | |
|     n = -1;
 | |
|   } else {
 | |
|     Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
 | |
|     Last  = (vh.NumRec + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Block=%d Last=%d\n", Block, Last);
 | |
| 
 | |
|   } // endif's
 | |
| 
 | |
|   CloseFileHandle(h);
 | |
|   return n;
 | |
|   } // end of GetBlockInfo
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Set the MaxRec and NumRec info in the file header.                 */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::SetBlockInfo(PGLOBAL g)
 | |
|   {
 | |
|   char      filename[_MAX_PATH];
 | |
|   bool      b = false, rc = false;
 | |
|   VECHEADER vh;
 | |
|   HANDLE    h = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (Header != 2) {
 | |
|     if (Hfile != INVALID_HANDLE_VALUE) {
 | |
|       h = Hfile;
 | |
| 
 | |
|       if (Header == 1)
 | |
|         /*bk =*/ BigSeek(g, h, (BIGINT)0);
 | |
| 
 | |
|     } else
 | |
|       b = true;
 | |
| 
 | |
|   } else       // Header == 2
 | |
|   {
 | |
|     PlugRemoveType(filename, filename);
 | |
|     safe_strcat(filename, sizeof(filename), ".blk");
 | |
|   }
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE) {
 | |
| #if defined(_WIN32)
 | |
|     DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING;
 | |
| 
 | |
|     h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0,
 | |
|                    NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
| #else   // !_WIN32
 | |
|     int oflag = (b) ?  O_RDWR : O_RDWR | O_TRUNC;
 | |
| 
 | |
|     h = open64(filename, oflag, 0);
 | |
| #endif  // !_WIN32
 | |
| 
 | |
|     if (h == INVALID_HANDLE_VALUE) {
 | |
|       snprintf(g->Message, sizeof(g->Message), "Error opening header file %s", filename);
 | |
|       return true;
 | |
|       } // endif h
 | |
| 
 | |
|     } // endif h
 | |
| 
 | |
|   if (Header == 3)
 | |
|     /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
 | |
| 
 | |
|   vh.MaxRec = MaxBlk * Bsize;
 | |
|   vh.NumRec = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|   if (BigWrite(g, h, &vh, sizeof(vh))) {
 | |
|     snprintf(g->Message, sizeof(g->Message), "Error writing header file %s", filename);
 | |
|     rc = true;
 | |
|     } // endif fread
 | |
| 
 | |
|   if (Header == 2 || Hfile == INVALID_HANDLE_VALUE)
 | |
|     CloseFileHandle(h);
 | |
| 
 | |
|   return rc;
 | |
|   } // end of SetBlockInfo
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  VEC Create an empty file for new Vector formatted tables.          */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
 | |
|   {
 | |
|   // Vector formatted file this will create an empty file of the
 | |
|   // required length if it does not exists yet.
 | |
|   char          filename[_MAX_PATH], c = 0;
 | |
|   int           n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
 | |
| 
 | |
|   PlugSetPath(filename, fn, Tdbp->GetPath());
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   PCSZ          p;
 | |
|   DWORD         rc;
 | |
|   bool          brc;
 | |
|   LARGE_INTEGER of;
 | |
|   HANDLE        h;
 | |
| 
 | |
|   h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
 | |
|                            FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (h == INVALID_HANDLE_VALUE) {
 | |
|     p = MSG(OPENING);
 | |
|     goto err;
 | |
|     } // endif h
 | |
| 
 | |
|   of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n",
 | |
|                of.QuadPart, n, MaxBlk, Blksize);
 | |
| 
 | |
|   of.LowPart = SetFilePointer(h, of.LowPart,
 | |
|                                 &of.HighPart, FILE_BEGIN);
 | |
| 
 | |
|   if (of.LowPart == INVALID_SET_FILE_POINTER &&
 | |
|            GetLastError() != NO_ERROR) {
 | |
|     p = MSG(MAKING);
 | |
|     goto err;
 | |
|     } // endif
 | |
| 
 | |
|   brc = WriteFile(h, &c, 1, &rc, NULL);
 | |
| 
 | |
|   if (!brc || rc != 1) {
 | |
|     p = MSG(WRITING);
 | |
|     goto err;
 | |
|     } // endif
 | |
| 
 | |
|   CloseHandle(h);
 | |
|   return false;
 | |
| 
 | |
|  err:
 | |
|   rc = GetLastError();
 | |
|   snprintf(g->Message, sizeof(g->Message), MSG(EMPTY_FILE), p, filename);
 | |
|   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
 | |
|                 (LPTSTR)filename, sizeof(filename), NULL);
 | |
|   safe_strcat(g->Message, sizeof(g->Message), filename);
 | |
| 
 | |
|   if (h != INVALID_HANDLE_VALUE)
 | |
|     CloseHandle(h);
 | |
| 
 | |
|   return true;
 | |
| #else   // !_WIN32
 | |
|   int    h;
 | |
|   BIGINT pos;
 | |
| 
 | |
|   h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
 | |
| 
 | |
|   if (h == -1)
 | |
|     return true;
 | |
| 
 | |
|   pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
 | |
|                pos, n, MaxBlk, Blksize);
 | |
| 
 | |
|   if (lseek64(h, pos, SEEK_SET) < 0)
 | |
|     goto err;
 | |
| 
 | |
|   // This actually fills the empty file
 | |
|   if (write(h, &c, 1) < 0)
 | |
|     goto err;
 | |
|        
 | |
|   close(h);
 | |
|   return false;
 | |
|   
 | |
|  err:
 | |
|   snprintf(g->Message, sizeof(g->Message), MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
 | |
|   close(h);
 | |
|   return true;
 | |
| #endif  // !_WIN32
 | |
|   } // end of MakeEmptyFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Vopen function: opens a file using Windows or Unix API's.          */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::OpenTableFile(PGLOBAL g)
 | |
|   {
 | |
|   char    filename[_MAX_PATH];
 | |
|   bool    del = false;
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PDBUSER dbuserp = PlgGetUser(g);
 | |
| 
 | |
|   if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(FILE_OPEN_YET), To_File);
 | |
|     return true;
 | |
|     } // endif
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Update block info if necessary.                                  */
 | |
|   /*********************************************************************/
 | |
|   if (Block < 0)
 | |
|     if ((Headlen = GetBlockInfo(g)) < 0)
 | |
|       return true;
 | |
| 
 | |
|   PlugSetPath(filename, To_File, Tdbp->GetPath());
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
 | |
|                          filename, mode, Last);
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   DWORD access, creation, share = 0, rc = 0;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Create the file object according to access mode                  */
 | |
|   /*********************************************************************/
 | |
|   switch (mode) {
 | |
|     case MODE_READ:
 | |
|       access = GENERIC_READ;
 | |
|       share = FILE_SHARE_READ;
 | |
|       creation = OPEN_EXISTING;
 | |
|       break;
 | |
|     case MODE_INSERT:
 | |
|       if (MaxBlk) {
 | |
|         if (!Block)
 | |
|           if (MakeEmptyFile(g, To_File))
 | |
|             return true;
 | |
| 
 | |
|         // Required to update empty blocks
 | |
|         access = GENERIC_READ | GENERIC_WRITE;
 | |
|       } else if (Last == Nrec)
 | |
|         access = GENERIC_WRITE;
 | |
|       else
 | |
|         // Required to update the last block
 | |
|         access = GENERIC_READ | GENERIC_WRITE;
 | |
| 
 | |
|       creation = OPEN_ALWAYS;
 | |
|       break;
 | |
|     case MODE_DELETE:
 | |
|       if (!Tdbp->GetNext()) {
 | |
|         // Store the number of deleted lines
 | |
|         DelRows = Cardinality(g);
 | |
| 
 | |
|         // This will stop the process by
 | |
|         // causing GetProgMax to return 0.
 | |
| //      ResetTableSize(g, 0, Nrec);     must be done later
 | |
|         del = true;
 | |
| 
 | |
|         // This will delete the whole file
 | |
|         access = GENERIC_READ | GENERIC_WRITE;
 | |
|         creation = TRUNCATE_EXISTING;
 | |
|         break;
 | |
|         } // endif
 | |
| 
 | |
|       // Selective delete, pass thru
 | |
|     case MODE_UPDATE:
 | |
|       if ((UseTemp = Tdbp->IsUsingTemp(g)))
 | |
|         access = GENERIC_READ;
 | |
|       else
 | |
|         access = GENERIC_READ | GENERIC_WRITE;
 | |
| 
 | |
|       creation = OPEN_EXISTING;
 | |
|       break;
 | |
|     default:
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | |
|       return true;
 | |
|     } // endswitch
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Use specific Windows API functions.                              */
 | |
|   /*********************************************************************/
 | |
|   Hfile = CreateFile(filename, access, share, NULL, creation,
 | |
|                                FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (Hfile == INVALID_HANDLE_VALUE) {
 | |
|     rc = GetLastError();
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, mode, filename);
 | |
|     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
 | |
|                   (LPTSTR)filename, sizeof(filename), NULL);
 | |
|     safe_strcat(g->Message, sizeof(g->Message), filename);
 | |
|     } // endif Hfile
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
 | |
|            rc, access, share, creation, Hfile, filename);
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     /*******************************************************************/
 | |
|     /* In Insert mode we must position the cursor at end of file.      */
 | |
|     /*******************************************************************/
 | |
|     LARGE_INTEGER of;
 | |
| 
 | |
|     of.QuadPart = (BIGINT)0;
 | |
|     of.LowPart = SetFilePointer(Hfile, of.LowPart,
 | |
|                                       &of.HighPart, FILE_END);
 | |
| 
 | |
|     if (of.LowPart == INVALID_SET_FILE_POINTER &&
 | |
|        (rc = GetLastError()) != NO_ERROR) {
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(ERROR_IN_SFP), rc);
 | |
|       CloseHandle(Hfile);
 | |
|       Hfile = INVALID_HANDLE_VALUE;
 | |
|       } // endif
 | |
| 
 | |
|     } // endif Mode
 | |
| 
 | |
| #else   // UNIX
 | |
|   /*********************************************************************/
 | |
|   /*  The open() function has a transitional interface for 64-bit      */
 | |
|   /*  file  offsets. Note that using open64() is equivalent to using   */
 | |
|   /*  open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp).  */
 | |
|   /*********************************************************************/
 | |
|   int    rc = 0;
 | |
|   int    oflag;
 | |
|   mode_t pmd = 0;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Create the file object according to access mode                  */
 | |
|   /*********************************************************************/
 | |
|   switch (mode) {
 | |
|     case MODE_READ:
 | |
|       oflag = O_RDONLY;
 | |
|       break;
 | |
|     case MODE_INSERT:
 | |
|       if (MaxBlk) {
 | |
|         if (!Block)
 | |
|           if (MakeEmptyFile(g, To_File))
 | |
|             return true;
 | |
| 
 | |
|         // Required to update empty blocks
 | |
|         oflag = O_RDWR;
 | |
|       } else if (Last == Nrec)
 | |
|         oflag = O_WRONLY | O_CREAT | O_APPEND;
 | |
|       else
 | |
|         // Required to update the last block
 | |
|         oflag = O_RDWR | O_CREAT | O_APPEND;
 | |
| 
 | |
|       pmd = S_IREAD | S_IWRITE;
 | |
|       break;
 | |
|     case MODE_DELETE:
 | |
|       // This is temporary until a partial delete is implemented
 | |
|       if (!Tdbp->GetNext()) {
 | |
|         // Store the number of deleted lines
 | |
|         DelRows = Cardinality(g);
 | |
|         del = true;
 | |
| 
 | |
|         // This will delete the whole file and provoque ReadDB to
 | |
|         // return immediately.
 | |
|         oflag = O_RDWR | O_TRUNC;
 | |
|         strncpy(g->Message, MSG(NO_VCT_DELETE), sizeof(g->Message));
 | |
|         break;
 | |
|         } // endif
 | |
| 
 | |
|       // Selective delete, pass thru
 | |
|       /* fall through */
 | |
|     case MODE_UPDATE:
 | |
|       UseTemp = Tdbp->IsUsingTemp(g);
 | |
|       oflag = (UseTemp) ? O_RDONLY : O_RDWR;
 | |
|       break;
 | |
|     default:
 | |
|       snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | |
|       return true;
 | |
|     } // endswitch
 | |
| 
 | |
|   Hfile = open64(filename, oflag, pmd); // Enable file size > 2G
 | |
| 
 | |
|   if (Hfile == INVALID_HANDLE_VALUE) {
 | |
|     rc = errno;
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR)"%s", rc, mode,
 | |
|              filename, strerror(errno));
 | |
|     } // endif Hfile
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n",
 | |
|            rc, oflag, mode, Hfile, filename);
 | |
| #endif  // UNIX
 | |
| 
 | |
|   if (!rc) {
 | |
|     if (!To_Fb) {
 | |
|       To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
 | |
|       To_Fb->Fname = To_File;
 | |
|       To_Fb->Type = TYPE_FB_HANDLE;
 | |
|       To_Fb->Memory = NULL;
 | |
|       To_Fb->Length = 0;
 | |
|       To_Fb->File = NULL;
 | |
|       To_Fb->Next = dbuserp->Openlist;
 | |
|       dbuserp->Openlist = To_Fb;
 | |
|       } // endif To_Fb
 | |
| 
 | |
|     To_Fb->Count = 1;
 | |
|     To_Fb->Mode = mode;
 | |
|     To_Fb->Handle = Hfile;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("File %s is open in mode %d\n", filename, mode);
 | |
| 
 | |
|     if (del)
 | |
|       // This will stop the process by
 | |
|       // causing GetProgMax to return 0.
 | |
|       return ResetTableSize(g, 0, Nrec);
 | |
| 
 | |
|     /*********************************************************************/
 | |
|     /*  Allocate the table and column block buffers.                     */
 | |
|     /*********************************************************************/
 | |
|     return AllocateBuffer(g);
 | |
|   } else
 | |
|     return (mode == MODE_READ && rc == ENOENT)
 | |
|             ? PushWarning(g, Tdbp) : true;
 | |
| 
 | |
|   } // end of OpenTableFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Allocate the block buffers for columns used in the query.          */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::AllocateBuffer(PGLOBAL g)
 | |
|   {
 | |
|   MODE    mode = Tdbp->GetMode();
 | |
|   PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
 | |
|   PCOLDEF cdp;
 | |
|   PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     if (!NewBlock) {
 | |
|       // Not reopening after inserting the last block
 | |
|       bool chk = PlgGetUser(g)->Check & CHK_TYPE;
 | |
| 
 | |
|       NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
 | |
| 
 | |
|       for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|         memset(NewBlock + Nrec * cdp->GetPoff(),
 | |
|               (IsTypeNum(cdp->GetType()) ? 0 : ' '),
 | |
|                           Nrec * cdp->GetClen());
 | |
| 
 | |
|       for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|         cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
 | |
|                                 cp->Buf_Type, Nrec, cp->Format.Length,
 | |
|                                 cp->Format.Prec, chk, true, cp->IsUnsigned());
 | |
| 
 | |
|       InitInsert(g);    // Initialize inserting
 | |
| 
 | |
|       // Currently we don't use a temporary file for inserting
 | |
|       Tfile = Hfile;
 | |
|       } // endif NewBlock
 | |
| 
 | |
|   } else {
 | |
|     if (UseTemp || mode == MODE_DELETE) {
 | |
|       // Allocate all that is needed to move lines
 | |
|       int i = 0;
 | |
| 
 | |
|       if (!Ncol)
 | |
|         for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
 | |
|           Ncol++;
 | |
| 
 | |
|       if (MaxBlk)
 | |
|         BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT));
 | |
|       else
 | |
|         Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
| 
 | |
|       Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
 | |
|       Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
 | |
| 
 | |
|       for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
 | |
|         if (MaxBlk)
 | |
|           BigDep[i] = (BIGINT)Headlen
 | |
|                     + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk;
 | |
|         else
 | |
|           Deplac[i] = cdp->GetPoff() * Nrec;
 | |
| 
 | |
|         Clens[i] = cdp->GetClen();
 | |
|         Isnum[i] = IsTypeNum(cdp->GetType());
 | |
|         Buflen = MY_MAX(Buflen, cdp->GetClen());
 | |
|         } // endfor cdp
 | |
| 
 | |
|       if (!UseTemp || MaxBlk) {
 | |
|         Buflen *= Nrec;
 | |
|         To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
 | |
|       } else
 | |
|         NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
 | |
| 
 | |
|       } // endif mode
 | |
| 
 | |
|     for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|       if (!cp->IsSpecial())            // Not a pseudo column
 | |
|         cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
 | |
|                                 cp->Format.Length, cp->Format.Prec,
 | |
| 					                      true, true, cp->IsUnsigned());
 | |
| 
 | |
|   } //endif mode
 | |
| 
 | |
|   return false;
 | |
|   } // end of AllocateBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base write routine for huge VCT access method.                */
 | |
| /***********************************************************************/
 | |
| int BGVFAM::WriteBuffer(PGLOBAL g)
 | |
|   {
 | |
|   if (trace(1))
 | |
|     htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n",
 | |
|           Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
 | |
| 
 | |
|   if (Tdbp->GetMode() == MODE_UPDATE) {
 | |
|     // Mode Update is done in ReadDB, we just initialize it here
 | |
|     if (Tfile == INVALID_HANDLE_VALUE) {
 | |
|       if (UseTemp) {
 | |
|         if (OpenTempFile(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|         // Most of the time, not all table columns are updated.
 | |
|         // This why we must completely pre-fill the temporary file.
 | |
|         Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
 | |
|                         : Block * Nrec;   // To write last lock
 | |
| 
 | |
|         if (MoveIntermediateLines(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|       } else
 | |
|         Tfile = Hfile;
 | |
| 
 | |
|       } // endif Tfile
 | |
| 
 | |
|   } else {
 | |
|     // Mode Insert
 | |
|     if (MaxBlk && CurBlk == MaxBlk) {
 | |
|       strncpy(g->Message, MSG(TRUNC_BY_ESTIM), sizeof(g->Message));
 | |
|       return RC_EF;       // Too many lines for a Vector formatted table
 | |
|       } // endif MaxBlk
 | |
| 
 | |
|     if (Closing || ++CurNum == Nrec) {
 | |
|       PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
 | |
| 
 | |
|       if (!AddBlock) {
 | |
|         // Write back the updated last block values
 | |
|         for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|           cp->WriteBlock(g);
 | |
| 
 | |
|         if (!Closing && !MaxBlk) {
 | |
|           // Close the VCT file and reopen it in mode Insert
 | |
| //#if defined(_WIN32)  //OB
 | |
| //          CloseHandle(Hfile);
 | |
| //#else    // UNIX
 | |
| //          close(Hfile);
 | |
| //#endif   // UNIX
 | |
|           CloseFileHandle(Hfile);
 | |
|           Hfile = INVALID_HANDLE_VALUE;
 | |
|           To_Fb->Count = 0;
 | |
|           Last = Nrec;               // Tested in OpenTableFile
 | |
| 
 | |
|           if (OpenTableFile(g)) {
 | |
|             Closing = true;          // Tell CloseDB of error
 | |
|             return RC_FX;
 | |
|             } // endif Vopen
 | |
| 
 | |
|           AddBlock = true;
 | |
|           } // endif Closing
 | |
| 
 | |
|       } else {
 | |
|         // Here we must add a new block to the VCT file
 | |
|         if (Closing)
 | |
|           // Reset the overwritten columns for last block extra records
 | |
|           for (; cp; cp = (PVCTCOL)cp->Next)
 | |
|             memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
 | |
|                    (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
 | |
|                    (Nrec - Last) * cp->Clen);
 | |
| 
 | |
|         if (BigWrite(g, Hfile, NewBlock, Blksize))
 | |
|           return RC_FX;
 | |
| 
 | |
|       } // endif AddBlock
 | |
| 
 | |
|       if (!Closing) {
 | |
|         CurBlk++;
 | |
|         CurNum = 0;
 | |
|         } // endif Closing
 | |
| 
 | |
|       } // endif
 | |
| 
 | |
|     } // endif Mode
 | |
| 
 | |
|   return RC_OK;
 | |
|   } // end of WriteBuffer
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base delete line routine for BGVFAM access method.            */
 | |
| /***********************************************************************/
 | |
| int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
 | |
|   {
 | |
|   bool eof = false;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  There is an alternative here depending on UseTemp:               */
 | |
|   /*  1 - use a temporary file in which are copied all not deleted     */
 | |
|   /*      lines, at the end the original file will be deleted and      */
 | |
|   /*      the temporary file renamed to the original file name.        */
 | |
|   /*  2 - directly move the not deleted lines inside the original      */
 | |
|   /*      file, and at the end erase all trailing records.             */
 | |
|   /*********************************************************************/
 | |
|   if (trace(1))
 | |
|     htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
 | |
|                         irc, UseTemp, Fpos, Tpos, Spos);
 | |
| 
 | |
|   if (irc != RC_OK) {
 | |
|     /*******************************************************************/
 | |
|     /*  EOF: position Fpos at the end-of-file position.                */
 | |
|     /*******************************************************************/
 | |
|     Fpos = (Block - 1) * Nrec + Last;
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("Fpos placed at file end=%d\n", Fpos);
 | |
| 
 | |
|     eof = UseTemp && !MaxBlk;
 | |
|   } else     // Fpos is the deleted line position
 | |
|     Fpos = CurBlk * Nrec + CurNum;
 | |
| 
 | |
|   if (Tpos == Spos) {
 | |
|     if (UseTemp) {
 | |
|       /*****************************************************************/
 | |
|       /*  Open the temporary file, Spos is at the beginning of file.   */
 | |
|       /*****************************************************************/
 | |
|       if (OpenTempFile(g))
 | |
|         return RC_FX;
 | |
| 
 | |
|     } else {
 | |
|       /*****************************************************************/
 | |
|       /*  Move of eventual preceding lines is not required here.      */
 | |
|       /*  Set the target file as being the source file itself.         */
 | |
|       /*  Set the future Tpos, and give Spos a value to block copying. */
 | |
|       /*****************************************************************/
 | |
|       Tfile = Hfile;
 | |
|       Spos = Tpos = Fpos;
 | |
|     } // endif UseTemp
 | |
| 
 | |
|     } // endif Tpos == Spos
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Move any intermediate lines.                                     */
 | |
|   /*********************************************************************/
 | |
|   if (MoveIntermediateLines(g, &eof))
 | |
|     return RC_FX;
 | |
| 
 | |
|   if (irc == RC_OK) {
 | |
| #ifdef _DEBUG
 | |
|     assert(Spos == Fpos);
 | |
| #endif
 | |
|     Spos++;          // New start position is on next line
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|   } else {
 | |
|     /*******************************************************************/
 | |
|     /*  Last call after EOF has been reached.                          */
 | |
|     /*******************************************************************/
 | |
|     Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
 | |
|     Last = (Tpos + Nrec - 1) % Nrec + 1;
 | |
| 
 | |
|     if (!UseTemp) {    // The UseTemp case is treated in CloseTableFile
 | |
|       if (!MaxBlk) {
 | |
|         if (Last < Nrec)            // Clean last block
 | |
|           if (CleanUnusedSpace(g))
 | |
|             return RC_FX;
 | |
| 
 | |
|         /***************************************************************/
 | |
|         /*  Remove extra records.                                      */
 | |
|         /***************************************************************/
 | |
| #if defined(_WIN32)
 | |
|         BIGINT pos = (BIGINT)Block * (BIGINT)Blksize;
 | |
| 
 | |
|         if (BigSeek(g, Hfile, pos))
 | |
|           return RC_FX;
 | |
| 
 | |
|         if (!SetEndOfFile(Hfile)) {
 | |
|           DWORD drc = GetLastError();
 | |
| 
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(SETEOF_ERROR), drc);
 | |
|           return RC_FX;
 | |
|           } // endif error
 | |
| #else   // !_WIN32
 | |
|         if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
 | |
|           snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | |
|           return RC_FX;
 | |
|           } // endif
 | |
| #endif  // !_WIN32
 | |
|       } else // MaxBlk
 | |
|         // Clean the unused space in the file, this is required when
 | |
|         // inserting again with a partial column list.
 | |
|         if (CleanUnusedSpace(g))
 | |
|           return RC_FX;
 | |
| 
 | |
|       if (ResetTableSize(g, Block, Last))
 | |
|        return RC_FX;
 | |
| 
 | |
|       } // endif UseTemp
 | |
| 
 | |
|   } // endif irc
 | |
| 
 | |
|   return RC_OK;                                      // All is correct
 | |
|   } // end of DeleteRecords
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Open a temporary file used while updating or deleting.             */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::OpenTempFile(PGLOBAL g)
 | |
|   {
 | |
|   char   *tempname;
 | |
|   PDBUSER dup = PlgGetUser(g);
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Open the temporary file, Spos is at the beginning of file.       */
 | |
|   /*********************************************************************/
 | |
|   tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
 | |
|   PlugSetPath(tempname, To_File, Tdbp->GetPath());
 | |
|   PlugRemoveType(tempname, tempname);
 | |
|   safe_strcat(tempname, _MAX_PATH, ".t");
 | |
| 
 | |
|   if (!MaxBlk)
 | |
|     remove(tempname);       // Be sure it does not exist yet
 | |
|   else if (MakeEmptyFile(g, tempname))
 | |
|     return true;
 | |
| 
 | |
| #if defined(_WIN32)
 | |
|   DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW;
 | |
| 
 | |
|   Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
 | |
|                      access, FILE_ATTRIBUTE_NORMAL, NULL);
 | |
| 
 | |
|   if (Tfile == INVALID_HANDLE_VALUE) {
 | |
|     DWORD rc = GetLastError();
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR), rc, MODE_DELETE, tempname);
 | |
|     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
 | |
|               FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
 | |
|               (LPTSTR)tempname, _MAX_PATH, NULL);
 | |
|     safe_strcat(g->Message, sizeof(g->Message), tempname);
 | |
|     return true;
 | |
|     } // endif Tfile
 | |
| #else    // UNIX
 | |
|   int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC;
 | |
| 
 | |
|   Tfile = open64(tempname, oflag, S_IWRITE);
 | |
| 
 | |
|   if (Tfile == INVALID_HANDLE_VALUE) {
 | |
|     int rc = errno;
 | |
|     snprintf(g->Message, sizeof(g->Message), MSG(OPEN_ERROR) "%s", rc, MODE_INSERT,
 | |
|              tempname, strerror(errno));
 | |
|     return true;
 | |
|     } //endif Tfile
 | |
| #endif   // UNIX
 | |
| 
 | |
|   To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
 | |
|   To_Fbt->Fname = tempname;
 | |
|   To_Fbt->Type = TYPE_FB_HANDLE;
 | |
|   To_Fbt->Memory = NULL;
 | |
|   To_Fbt->Length = 0;
 | |
|   To_Fbt->File = NULL;
 | |
|   To_Fbt->Next = dup->Openlist;
 | |
|   To_Fbt->Count = 1;
 | |
|   To_Fbt->Mode = MODE_INSERT;
 | |
|   To_Fbt->Handle = Tfile;
 | |
|   dup->Openlist = To_Fbt;
 | |
|   return false;
 | |
|   } // end of OpenTempFile
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Move intermediate deleted or updated lines.                        */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
 | |
|   {
 | |
|   int    i, n, req, dep;
 | |
|   bool   eof = (b) ? *b : false;
 | |
|   BIGINT pos;
 | |
| 
 | |
|   for (n = Fpos - Spos; n > 0 || eof; n -= req) {
 | |
|     /*******************************************************************/
 | |
|     /*  Non consecutive line to delete. Move intermediate lines.       */
 | |
|     /*******************************************************************/
 | |
|     if (!MaxBlk)
 | |
|       req = (DWORD)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
 | |
|     else
 | |
|       req = (DWORD)MY_MIN(n, Nrec);
 | |
| 
 | |
|     if (req) for (i = 0; i < Ncol; i++) {
 | |
|       if (!MaxBlk) {
 | |
|         if (UseTemp)
 | |
|           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
 | |
| 
 | |
|         pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i])
 | |
|             + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize;
 | |
|       } else
 | |
|         pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i];
 | |
| 
 | |
|       if (BigSeek(g, Hfile, pos))
 | |
|         return true;
 | |
| 
 | |
|       if (BigRead(g, Hfile, To_Buf, req * Clens[i]))
 | |
|         return true;
 | |
| 
 | |
|       if (!UseTemp || MaxBlk) {
 | |
|         if (!MaxBlk)
 | |
|           pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i])
 | |
|               + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize;
 | |
|         else
 | |
|           pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
 | |
| 
 | |
|         if (BigSeek(g, Tfile, pos))
 | |
|           return true;
 | |
| 
 | |
|         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
 | |
|           return true;
 | |
| 
 | |
|         } // endif UseTemp
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|     Tpos += (int)req;
 | |
|     Spos += (int)req;
 | |
| 
 | |
|     if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) {
 | |
|       // Write the full or last block to the temporary file
 | |
|       if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
 | |
|         // Clean the last block in case of future insert, must be
 | |
|         // done here because Tfile was open in write only.
 | |
|         for (i = 0; i < Ncol; i++) {
 | |
|           To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
 | |
|           memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
 | |
|           } // endfor i
 | |
| 
 | |
|       if (BigWrite(g, Tfile, NewBlock, Blksize))
 | |
|         return true;
 | |
| 
 | |
|       if (Spos == Fpos)
 | |
|         eof = false;
 | |
| 
 | |
|       } // endif Usetemp...
 | |
| 
 | |
|     if (trace(1))
 | |
|       htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
 | |
| 
 | |
|     } // endfor n
 | |
| 
 | |
|   return false;
 | |
|   } // end of MoveIntermediateLines
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Clean deleted space in a huge VCT or Vec table file.               */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
 | |
|   {
 | |
|   int    i;
 | |
|   int   n;
 | |
|   BIGINT pos, dep;
 | |
| 
 | |
|   if (!MaxBlk) {
 | |
|     /*******************************************************************/
 | |
|     /*  Clean last block of the VCT table file.                        */
 | |
|     /*******************************************************************/
 | |
|     assert(!UseTemp); // This case is handled in MoveIntermediateLines
 | |
| 
 | |
|     if (!(n = Nrec - Last))
 | |
|       return false;
 | |
| 
 | |
|     dep = (BIGINT)((Block - 1) * Blksize);
 | |
| 
 | |
|     for (i = 0; i < Ncol; i++) {
 | |
|       memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
 | |
|       pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]);
 | |
| 
 | |
|       if (BigSeek(g, Hfile, pos))
 | |
|         return true;
 | |
| 
 | |
|       if (BigWrite(g, Hfile, To_Buf, n * Clens[i]))
 | |
|         return true;
 | |
| 
 | |
|       } // endfor i
 | |
| 
 | |
|   } else {
 | |
|     int  req;
 | |
| 
 | |
|     if (To_Buf)
 | |
|       memset(To_Buf, 0, Buflen);
 | |
| 
 | |
|     for (n = Fpos - Tpos; n > 0; n -= req) {
 | |
|       /*****************************************************************/
 | |
|       /*  Fill VEC file remaining lines with 0's.                      */
 | |
|       /*  This seems to work even column blocks have been made with    */
 | |
|       /*  Blanks = true. Perhaps should it be set to false for VEC.    */
 | |
|       /*****************************************************************/
 | |
|       req = MY_MIN(n, Nrec);
 | |
| 
 | |
|       for (i = 0; i < Ncol; i++) {
 | |
|         pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
 | |
| 
 | |
|         if (BigSeek(g, Tfile, pos))
 | |
|           return true;
 | |
| 
 | |
|         if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
 | |
|           return true;
 | |
| 
 | |
|         } // endfor i
 | |
| 
 | |
|       Tpos += req;
 | |
|       } // endfor n
 | |
| 
 | |
|   } // endif MaxBlk
 | |
| 
 | |
|   return false;
 | |
|   } // end of CleanUnusedSpace
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Data Base close routine for huge VEC access method.                */
 | |
| /***********************************************************************/
 | |
| void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
 | |
|   {
 | |
|   int  rc = 0, wrc = RC_OK;
 | |
|   MODE mode = Tdbp->GetMode();
 | |
| 
 | |
|   Abort = abort;
 | |
| 
 | |
|   if (mode == MODE_INSERT) {
 | |
|     if (Closing)
 | |
|       wrc = RC_FX;                  // Last write was in error
 | |
|     else
 | |
|       if (CurNum) {
 | |
|         // Some more inserted lines remain to be written
 | |
|         Last = CurNum;
 | |
|         Block = CurBlk + 1;
 | |
|         Closing = true;
 | |
|         wrc = WriteBuffer(g);
 | |
|       } else {
 | |
|         Last = Nrec;
 | |
|         Block = CurBlk;
 | |
|         wrc = RC_OK;
 | |
|       } // endif CurNum
 | |
| 
 | |
|     if (wrc != RC_FX) {
 | |
|       rc = ResetTableSize(g, Block, Last);
 | |
|     } else if (AddBlock) {
 | |
|       // Last block was not written
 | |
|       rc = ResetTableSize(g, CurBlk, Nrec);
 | |
| 			throw 44;
 | |
| 		} // endif
 | |
| 
 | |
|   } else if (mode == MODE_UPDATE) {
 | |
|     // Write back to file any pending modifications
 | |
|     for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols();
 | |
|                  colp; colp = (PVCTCOL)colp->Next)
 | |
|       colp->WriteBlock(g);
 | |
| 
 | |
|     if (UseTemp && Tfile) {
 | |
|       rc = RenameTempFile(g);
 | |
|       Hfile = Tfile = INVALID_HANDLE_VALUE;
 | |
| 
 | |
|       if (Header)
 | |
|         // Header must be set because it was not set in temp file
 | |
|         rc = SetBlockInfo(g);
 | |
| 
 | |
|       } // endif UseTemp
 | |
| 
 | |
|   } else if (mode == MODE_DELETE && UseTemp && Tfile) {
 | |
|     if (MaxBlk)
 | |
|       rc = CleanUnusedSpace(g);
 | |
| 
 | |
|     if ((rc = RenameTempFile(g)) != RC_FX) {
 | |
|       Hfile = Tfile = INVALID_HANDLE_VALUE;    // For SetBlockInfo
 | |
|       rc = ResetTableSize(g, Block, Last);
 | |
|       } // endif rc
 | |
| 
 | |
|   } // endif's mode
 | |
| 
 | |
|   if (Hfile != INVALID_HANDLE_VALUE)
 | |
|     rc = PlugCloseFile(g, To_Fb);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n",
 | |
|           To_File, wrc, rc);
 | |
| 
 | |
|   Hfile = INVALID_HANDLE_VALUE;
 | |
|   } // end of CloseDB
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  Rewind routine for huge VCT access method.                         */
 | |
| /***********************************************************************/
 | |
| void BGVFAM::Rewind(void)
 | |
|   {
 | |
|   // In mode update we need to read Set Column blocks
 | |
|   if (Tdbp->GetMode() == MODE_UPDATE)
 | |
|     OldBlk = -1;
 | |
| 
 | |
|   // Initialize so block optimization is called for 1st block
 | |
|   CurBlk = -1;
 | |
|   CurNum = Nrec - 1;
 | |
| 
 | |
| #if 0 // This is probably unuseful as the file is directly accessed
 | |
| #if defined(_WIN32)  //OB
 | |
|   SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
 | |
| #else    // UNIX
 | |
|   lseek64(Hfile, 0, SEEK_SET);
 | |
| #endif   // UNIX
 | |
| #endif // 0
 | |
|   } // end of Rewind
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  ReadBlock: Read column values from current block.                  */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   BIGINT pos;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to read.              */
 | |
|   /*********************************************************************/
 | |
|   if (MaxBlk)                                 // File has Vector format
 | |
|     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
 | |
|         + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen;
 | |
|   else                                        // Old VCT format
 | |
|     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
 | |
|         + (BIGINT)Lrecl * (BIGINT)CurBlk);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n",
 | |
|           pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
 | |
| 
 | |
|   if (BigSeek(g, Hfile, pos))
 | |
|     return true;
 | |
| 
 | |
|   if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec))
 | |
|     return true;
 | |
| 
 | |
|   if (trace(1))
 | |
|     num_read++;
 | |
| 
 | |
|   return false;
 | |
|   } // end of ReadBlock
 | |
| 
 | |
| /***********************************************************************/
 | |
| /*  WriteBlock: Write back current column values for one block.        */
 | |
| /*  Note: the test of Status is meant to prevent physical writing of   */
 | |
| /*  the block during the checking loop in mode Update. It is set to    */
 | |
| /*  BUF_EMPTY when reopening the table between the two loops.          */
 | |
| /***********************************************************************/
 | |
| bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
 | |
|   {
 | |
|   int    len;
 | |
|   BIGINT pos;
 | |
| 
 | |
|   /*********************************************************************/
 | |
|   /*  Calculate the offset and size of the block to write.             */
 | |
|   /*********************************************************************/
 | |
|   if (MaxBlk)                               // File has Vector format
 | |
|     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
 | |
|         + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen;
 | |
|   else                                      // Old VCT format
 | |
|     pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
 | |
|         + (BIGINT)Lrecl * (BIGINT)colp->ColBlk);
 | |
| 
 | |
|   if (trace(1))
 | |
|     htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n",
 | |
|           pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
 | |
| 
 | |
|   if (BigSeek(g, Tfile, pos))
 | |
|     return true;
 | |
| 
 | |
| //len = colp->Clen * Nrec;    see comment in VCTFAM
 | |
|   len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec);
 | |
| 
 | |
|   if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len))
 | |
|     return true;
 | |
| 
 | |
|   return false;
 | |
|   } // end of WriteBlock
 | |
| 
 | |
| /* ----------------------- End of FilAMVct --------------------------- */
 | 
