mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +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 --------------------------- */
 |