summaryrefslogtreecommitdiff
path: root/storage/connect/filamvct.cpp
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
committerOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
commit7b400a088d049661b9a4dded385ac78923bb0017 (patch)
treebb3ee7bac4454f66ec6f38cf6683f24bc6c065d4 /storage/connect/filamvct.cpp
parentd67ad26b33ea16a3b59215ef967bdd9b89345e04 (diff)
parente5729127b8a50a0e553fd8b87b2683e4a684dfcc (diff)
downloadmariadb-git-7b400a088d049661b9a4dded385ac78923bb0017.tar.gz
- MRR + Block Indexing
modified: storage/connect/array.h storage/connect/catalog.h storage/connect/colblk.cpp storage/connect/colblk.h storage/connect/connect.cc storage/connect/connect.h storage/connect/domdoc.h storage/connect/filamap.cpp storage/connect/filamap.h storage/connect/filamdbf.h storage/connect/filamfix.cpp storage/connect/filamfix.h storage/connect/filamtxt.cpp storage/connect/filamtxt.h storage/connect/filamvct.cpp storage/connect/filamvct.h storage/connect/filamzip.cpp storage/connect/filamzip.h storage/connect/filter.cpp storage/connect/filter.h storage/connect/global.h storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/myconn.h storage/connect/plgcnx.h storage/connect/plgdbsem.h storage/connect/plgdbutl.cpp storage/connect/plugutil.c storage/connect/preparse.h storage/connect/reldef.cpp storage/connect/reldef.h storage/connect/tabcol.h storage/connect/tabdos.cpp storage/connect/tabdos.h storage/connect/tabfix.cpp storage/connect/tabfmt.cpp storage/connect/tabfmt.h storage/connect/table.cpp storage/connect/tabmysql.cpp storage/connect/tabmysql.h storage/connect/taboccur.h storage/connect/tabodbc.h storage/connect/tabsys.h storage/connect/tabtbl.h storage/connect/tabutil.h storage/connect/tabvct.cpp storage/connect/tabvct.h storage/connect/tabwmi.h storage/connect/tabxml.h storage/connect/user_connect.cc storage/connect/user_connect.h storage/connect/valblk.cpp storage/connect/valblk.h storage/connect/value.cpp storage/connect/value.h storage/connect/xindex.cpp storage/connect/xindex.h storage/connect/xobject.cpp storage/connect/xobject.h storage/connect/xtable.h
Diffstat (limited to 'storage/connect/filamvct.cpp')
-rwxr-xr-xstorage/connect/filamvct.cpp8476
1 files changed, 4234 insertions, 4242 deletions
diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp
index 4887d7e52fd..586c7bd7c54 100755
--- a/storage/connect/filamvct.cpp
+++ b/storage/connect/filamvct.cpp
@@ -1,4242 +1,4234 @@
-/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
-/* PROGRAM NAME: FILAMVCT */
-/* ------------- */
-/* Version 2.5 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the VCT file access method classes. */
-/* Added in version 2: F */
-/* - 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 F
-#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 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 WIN32
-#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;
-extern "C" int trace;
-
-#if defined(UNIX)
-// Add dummy strerror (NGC)
-char *strerror(int num);
-#endif // UNIX
-
-/***********************************************************************/
-/* 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
-
-/***********************************************************************/
-/* 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) {
- sprintf(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)
- strcat(PlugRemoveType(filename, 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)) {
- sprintf(g->Message, "Error reading header file %s", filename);
- n = -1;
- } else if (MaxBlk * Nrec != vh.MaxRec) {
- sprintf(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
- strcat(PlugRemoveType(filename, filename), ".blk");
- s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
- } // endif Header
-
- if (!s) {
- sprintf(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) {
- sprintf(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 s)
- {
- 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 defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == Block - 1) ? Last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == Block - 1) ? Last : Nrec;
-#endif // !BLK_INDX
-
- 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];
- PSZ 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();
- sprintf(filename, Colfn, 1);
- To_File = filename;
- len = GetFileLength(g);
- To_File = savfn;
-
- if (len >= 0) {
- if (!(len % clen))
- card = len / clen; // Fixed length file
- else
- sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen);
-
- if (trace)
- 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 (int)((Block - 1) * Nrec + Last);
- } // 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, char *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) == -1) {
- sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
- close(h);
- return true;
- } // endif h
-
- write(h, &c, 1); // This actually fills the empty file
- close(h);
- return false;
- } // 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:
- strcpy(opmode, "rb");
- break;
- case MODE_DELETE:
- if (!Tdbp->GetNext()) {
- // Store the number of deleted lines
- DelRows = Cardinality(g);
-
- // This will delete the whole file
- strcpy(opmode, "wb");
- break;
- } // endif
-
- // Selective delete, pass thru
- case MODE_UPDATE:
- UseTemp = Tdbp->IsUsingTemp(g);
- strcpy(opmode, (UseTemp) ? "rb" : "r+b");
- break;
- case MODE_INSERT:
- if (MaxBlk) {
- if (!Block)
- if (MakeEmptyFile(g, To_File))
- return true;
-
- strcpy(opmode, "r+b"); // Required to update empty blocks
- } else if (Last == Nrec)
- strcpy(opmode, "ab");
- else
- strcpy(opmode, "r+b"); // Required to update the last block
-
- break;
- default:
- sprintf(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)
- htrc("%s\n", g->Message);
-
- return (mode == MODE_READ && errno == ENOENT)
- ? PushWarning(g, Tdbp) : true;
- } // endif Stream
-
- if (trace)
- 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);
-
- 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 = 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);
-
- } //endif mode
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* Do initial action when inserting. */
-/***********************************************************************/
-bool VCTFAM::InitInsert(PGLOBAL g)
- {
- // We come here in MODE_INSERT only
- if (Last == Nrec) {
- CurBlk = Block;
- CurNum = 0;
- AddBlock = !MaxBlk;
- } else {
- int rc;
- PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
-
- // The starting point must be at the end of file as for append.
- CurBlk = Block - 1;
- CurNum = Last;
-
- // Prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return true;
- } // endif
-
- if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
- g->jump_level--;
- return true;
- } // endif
-
- // Last block must be updated by new values
- for (; cp; cp = (PVCTCOL)cp->Next)
- cp->ReadBlock(g);
-
- g->jump_level--;
- } // endif Last
-
- // We are not currently using a temporary file for Insert
- T_Stream = Stream;
- return false;
- } // 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;
-
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (++CurBlk == Block)
- return RC_EF; // End of file
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* 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
-#endif // BLK_INDX
-
- 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)
- 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)
- 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) {
- strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
- 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)) {
- sprintf(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)
- 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)
- 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 preceeding 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)
- 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))) {
- sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#else
- if (chsize(h, Headlen + Block * Blksize)) {
- sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#endif
-
- close(h);
-
- if (trace)
- 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)
- {
- char *opmode, tempname[_MAX_PATH];
- bool rc = false;
-
- /*********************************************************************/
- /* Open the temporary file, Spos is at the beginning of file. */
- /*********************************************************************/
- PlugSetPath(tempname, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(tempname, 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)
- 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)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec));
- else
- req = (size_t)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)) {
- sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
- return true;
- } // endif
-
- len = fread(To_Buf, Clens[i], req, Stream);
-
- if (trace)
- htrc("after read req=%d len=%d\n", req, len);
-
- if (len != req) {
- sprintf(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)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
- sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
- return true;
- } // endif
-
- } // endif UseTemp
-
- if (trace)
- 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) {
- sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
- return true;
- } // endif
-
- if (Spos == Fpos)
- eof = false;
-
- } // endif UseTemp
-
- if (trace)
- 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)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) {
- sprintf(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)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)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
- sprintf(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)
- {
- int rc = 0, wrc = RC_OK;
- MODE mode = Tdbp->GetMode();
-
- 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);
- longjmp(g->jumper[g->jump_level], 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)
- 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();
- LPCSTR name = Tdbp->GetName();
- PCATLG cat = PlgGetCatalog(g);
-
- defp->SetBlock(Block);
- defp->SetLast(Last);
-
- if (!cat->SetIntCatInfo("Blocks", Block) ||
- !cat->SetIntCatInfo("Last", Last)) {
- sprintf(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)
- 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)) {
- sprintf(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)
- sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
- else
- sprintf(g->Message, MSG(READ_ERROR),
- To_File, strerror(errno));
-
- if (trace)
- htrc(" Read error: %s\n", g->Message);
-
- return true;
- } // endif
-
- if (trace)
- 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)
- 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)) {
- sprintf(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)) {
- sprintf(g->Message, MSG(WRITE_STRERROR),
- (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
-
- if (trace)
- 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];
- int 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)
- 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 {
- strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
- 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))
- sprintf(g->Message, MSG(OPEN_MODE_ERROR),
- "map", (int) rc, filename);
-
- if (trace)
- 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 = mm.lenL;
- 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);
- sprintf(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 = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy((char*)fp->Fname, 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)
- 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);
- 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)
- {
- int rc;
- 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
-
- // Prepare error return
- if (g->jump_level == MAX_JUMP) {
- strcpy(g->Message, MSG(TOO_MANY_JUMPS));
- return true;
- } // endif
-
- if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
- g->jump_level--;
- return true;
- } // endif
-
- // Initialize the column block pointer
- for (; cp; cp = (PVCTCOL)cp->Next)
- cp->ReadBlock(g);
-
- g->jump_level--;
- return false;
- } // end of InitInsert
-
-/***********************************************************************/
-/* Data Base write routine for VMP access method. */
-/***********************************************************************/
-int VCMFAM::WriteBuffer(PGLOBAL g)
- {
- if (trace)
- 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) {
- strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
- 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)
- {
- int i;
- int m, n;
-
- if (trace)
- 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)
- 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 preceeding lines is */
- /* not required here, just setting of future Spos and Tpos. */
- /*******************************************************************/
- Tpos = Fpos; // Spos is set below
- else if (Fpos > Spos) {
- /*******************************************************************/
- /* 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 = Fpos - Spos; n > 0; n -= req) {
- soff = Spos % Nrec;
- toff = Tpos % Nrec;
- req = (size_t)min(n, Nrec - 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...
- n = Fpos - Spos;
-
- 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)
- htrc("move %d bytes\n", n);
-
- } // endif n
-
- if (irc == RC_OK) {
- Spos = Fpos + 1; // New start position
-
- if (trace)
- htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
-
- } else {
- /*******************************************************************/
- /* Last call after EOF has been reached. 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) {
- sprintf(g->Message, MSG(FUNCTION_ERROR),
- "SetFilePointer", GetLastError());
- CloseHandle(fp->Handle);
- return RC_FX;
- } // endif
-
- if (trace)
- htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
-
- if (!SetEndOfFile(fp->Handle)) {
- sprintf(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)) {
- sprintf(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
-
-/***********************************************************************/
-/* Data Base close routine for VMP access method. */
-/***********************************************************************/
-void VCMFAM::CloseTableFile(PGLOBAL g)
- {
- 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)
- PlugCloseFile(g, To_Fb);
-
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* ReadBlock: Read column values from current block. */
-/***********************************************************************/
-bool VCMFAM::ReadBlock(PGLOBAL g, 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)
- 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)
- 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 g, PVCTCOL colp)
- {
-#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)
- 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:
- strcpy(opmode, "rb");
- break;
- case MODE_DELETE:
- if (!Tdbp->GetNext()) {
- // Store the number of deleted lines
- DelRows = Cardinality(g);
-
- // This will delete the whole file
- strcpy(opmode, "wb");
-
- // This will stop the process by causing GetProgMax to return 0.
- ResetTableSize(g, 0, Nrec);
- break;
- } // endif filter
-
- // Selective delete, pass thru
- case MODE_UPDATE:
- UseTemp = Tdbp->IsUsingTemp(g);
- strcpy(opmode, (UseTemp) ? "r": "r+");
- break;
- case MODE_INSERT:
- strcpy(opmode, "ab");
- break;
- default:
- sprintf(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 (!i++)
- b = !Streams[cp->Index - 1];
- else if (b != !Streams[cp->Index - 1])
- return true;
-
- } // 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, char *opmode, int i)
- {
- char filename[_MAX_PATH];
- PDBUSER dup = PlgGetUser(g);
-
- sprintf(filename, Colfn, i+1);
-
- if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
- if (trace)
- htrc("%s\n", g->Message);
-
- return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
- ? PushWarning(g, Tdbp) : true;
- } // endif Streams
-
- if (trace)
- 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);
-
- 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);
- strcpy(Tempat, Colfn);
- PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
- strcat(PlugRemoveType(Tempat, Tempat), ".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 = 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 = 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);
-
- } // endif mode
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* Do initial action when inserting. */
-/***********************************************************************/
-bool VECFAM::InitInsert(PGLOBAL g)
- {
- // 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)
- 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])) {
- sprintf(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...) */
-/***********************************************************************/
-int VECFAM::DeleteRecords(PGLOBAL g, int irc)
- {
- /*********************************************************************/
- /* There is an alternative here: */
- /* 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. */
- /* This depends on the Check setting, false by default. */
- /*********************************************************************/
- if (trace)
- 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)
- 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 preceeding lines is not required here. */
- /* Set the future Tpos, and give Spos a value to block copying. */
- /*****************************************************************/
- Spos = Tpos = Fpos;
-
- /*********************************************************************/
- /* 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)
- 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 file and reopen it 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++) {
- sprintf(filename, 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]))) {
- sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#else
- if (chsize(h, Tpos * Clens[i])) {
- sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#endif
-
- close(h);
-
- if (trace)
- 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. */
- /*****************************************************************/
- sprintf(tempname, Tempat, i+1);
-
- if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
- if (trace)
- 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 *bn)
- {
- int i;
- int 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)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)) {
- sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
- return true;
- } // endif
-
- len = fread(To_Buf, Clens[i], req, Streams[i]);
-
- if (trace)
- htrc("after read req=%d len=%d\n", req, len);
-
- if (len != req) {
- sprintf(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)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
- sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
- return true;
- } // endif
-
- if (trace)
- htrc("after write pos=%d\n", ftell(Streams[i]));
-
- } // endfor i
-
- Tpos += (int)req;
- Spos += (int)req;
-
- if (trace)
- 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;
- sprintf(filename, Colfn, i+1);
- PlugSetPath(filename, filename, Tdbp->GetPath());
- strcat(PlugRemoveType(filetemp, filename), ".ttt");
- remove(filetemp); // May still be there from previous error
-
- if (rename(filename, filetemp)) { // Save file for security
- sprintf(g->Message, MSG(RENAME_ERROR),
- filename, filetemp, strerror(errno));
- rc = RC_FX;
- } else if (rename(tempname, filename)) {
- sprintf(g->Message, MSG(RENAME_ERROR),
- tempname, filename, strerror(errno));
- rc = rename(filetemp, filename); // Restore saved file
- rc = RC_FX;
- } else if (remove(filetemp)) {
- sprintf(g->Message, MSG(REMOVE_ERROR),
- filetemp, strerror(errno));
- rc = RC_INFO; // Acceptable
- } // endif's
-
- } // endfor i
-
- return rc;
- } // end of RenameTempFile
-
-/***********************************************************************/
-/* Data Base close routine for VEC access method. */
-/***********************************************************************/
-void VECFAM::CloseTableFile(PGLOBAL g)
- {
- int rc = 0, wrc = RC_OK;
- MODE mode = Tdbp->GetMode();
-
- 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
- longjmp(g->jumper[g->jump_level], 44);
-
- } else if (mode == MODE_UPDATE) {
- if (UseTemp && !InitUpdate) {
- // Write any intermediate lines to temp file
- Fpos = OldBlk * Nrec;
- wrc = MoveIntermediateLines(g);
-// 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) {
- // Write any intermediate lines to temp file
- Fpos = (Block - 1) * Nrec + Last;
- wrc = MoveIntermediateLines(g);
- } // endif UseTemp
-
- } // endif's mode
-
- if (UseTemp && !InitUpdate) {
- // If they are errors, leave files unchanged
- if (wrc == RC_OK)
- rc = RenameTempFile(g);
- else
- longjmp(g->jumper[g->jump_level], 44);
-
- } 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)
- 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)
- 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)) {
- sprintf(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];
-
- sprintf(fn, Colfn, colp->Index);
-#if defined(WIN32)
- if (feof(Streams[i]))
-#else // !WIN32
- if (errno == NO_ERROR)
-#endif // !WIN32
- sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
- else
- sprintf(g->Message, MSG(READ_ERROR),
- fn, strerror(errno));
-
- if (trace)
- htrc(" Read error: %s\n", g->Message);
-
- return true;
- } // endif
-
- if (trace)
- 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)
- 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)) {
- sprintf(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];
-
- sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
- sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
-
- if (trace)
- 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;
- 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 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;
-
- } // endif mode
-
- /*********************************************************************/
- /* Check for void table or missing columns */
- /*********************************************************************/
- for (b = !Memcol[0], i = 1; i < Ncol; i++)
- if (b != !Memcol[i])
- return true;
-
- /*********************************************************************/
- /* 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];
- int len;
- HANDLE hFile;
- MEMMAP mm;
- PFBLOCK fp;
- PDBUSER dup = PlgGetUser(g);
-
- sprintf(filename, 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)
- 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))
- sprintf(g->Message, MSG(OPEN_MODE_ERROR),
- "map", (int) rc, filename);
- if (trace)
- 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 = mm.lenL;
- 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);
- sprintf(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 = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy((char*)fp->Fname, 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)
- 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);
- 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)
- 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)
- 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 preceeding 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)
- htrc("move %d bytes\n", n);
-
- } // endif n
-
- if (irc == RC_OK) {
- Spos = Fpos + 1; // New start position
-
- if (trace)
- 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;
-
- 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) {
- sprintf(g->Message, MSG(FUNCTION_ERROR),
- "SetFilePointer", GetLastError());
- CloseHandle(fp->Handle);
- return RC_FX;
- } // endif
-
- if (trace)
- htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
-
- if (!SetEndOfFile(fp->Handle)) {
- sprintf(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)) {
- sprintf(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)
- {
- 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);
- sprintf(g->Message, MSG(SFP_ERROR), buf);
- return true;
- } // endif
-#else // !WIN32
- if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
- sprintf(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)
- 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)
- strcpy(buf, MSG(BAD_BYTE_READ));
- else {
- drc = GetLastError();
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)buf, sizeof(buf), NULL);
- } // endelse brc
-
- sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
-
- if (trace)
- 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";
-
- sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
-
- if (trace)
- 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)
- htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
-
- if (!brc || nbw != len) {
- char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile";
-
- if (brc)
- strcpy(buf, MSG(BAD_BYTE_NUM));
- else {
- drc = GetLastError();
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
- (LPTSTR)buf, sizeof(buf), NULL);
- } // endelse brc
-
- sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
-
- if (trace)
- 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";
-
- sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
-
- if (trace)
- 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) {
- sprintf(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)
- strcat(PlugRemoveType(filename, 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)
- 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))) {
- sprintf(g->Message, "Error reading header file %s", filename);
- n = -1;
- } else if (MaxBlk * Nrec != vh.MaxRec) {
- sprintf(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)
- 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
- strcat(PlugRemoveType(filename, 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) {
- sprintf(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))) {
- sprintf(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, char *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)
- char *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)
- 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();
- sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
- (LPTSTR)filename, sizeof(filename), NULL);
- strcat(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)
- htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
- pos, n, MaxBlk, Blksize);
-
- if (lseek64(h, pos, SEEK_SET) < 0) {
- sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
- close(h);
- return true;
- } // endif h
-
- write(h, &c, 1); // This actually fills the empty file
- close(h);
- return false;
-#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) {
- sprintf(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)
- 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:
- sprintf(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();
- sprintf(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);
- strcat(g->Message, filename);
- } // endif Hfile
-
- if (trace)
- 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) {
- sprintf(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;
- strcpy(g->Message, MSG(NO_VCT_DELETE));
- break;
- } // endif
-
- // Selective delete, pass thru
- case MODE_UPDATE:
- UseTemp = Tdbp->IsUsingTemp(g);
- oflag = (UseTemp) ? O_RDONLY : O_RDWR;
- break;
- default:
- sprintf(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;
- sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
- strcat(g->Message, strerror(errno));
- } // endif Hfile
-
- if (trace)
- 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)
- 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);
-
- 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 = 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);
-
- } //endif mode
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* Data Base write routine for huge VCT access method. */
-/***********************************************************************/
-int BGVFAM::WriteBuffer(PGLOBAL g)
- {
- if (trace)
- 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) {
- strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
- 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)
- 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)
- 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 preceeding 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)
- 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();
-
- sprintf(g->Message, MSG(SETEOF_ERROR), drc);
- return RC_FX;
- } // endif error
-#else // !WIN32
- if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
- sprintf(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());
- strcat(PlugRemoveType(tempname, tempname), ".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();
- sprintf(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);
- strcat(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;
- sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
- strcat(g->Message, 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)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec));
- else
- req = (DWORD)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)
- 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;
-
- 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 = 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)
- {
- int rc = 0, wrc = RC_OK;
- MODE mode = Tdbp->GetMode();
-
- 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);
- longjmp(g->jumper[g->jump_level], 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)
- 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)
- 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)
- 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)
- 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 --------------------------- */
+/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
+/* PROGRAM NAME: FILAMVCT */
+/* ------------- */
+/* Version 2.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the VCT file access method classes. */
+/* Added in version 2: F */
+/* - 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 F
+#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 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 WIN32
+#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;
+extern "C" int trace;
+
+#if defined(UNIX)
+// Add dummy strerror (NGC)
+char *strerror(int num);
+#endif // UNIX
+
+/***********************************************************************/
+/* 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
+
+/***********************************************************************/
+/* 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) {
+ sprintf(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)
+ strcat(PlugRemoveType(filename, 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)) {
+ sprintf(g->Message, "Error reading header file %s", filename);
+ n = -1;
+ } else if (MaxBlk * Nrec != vh.MaxRec) {
+ sprintf(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
+ strcat(PlugRemoveType(filename, filename), ".blk");
+ s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
+ } // endif Header
+
+ if (!s) {
+ sprintf(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) {
+ sprintf(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 s)
+ {
+ 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];
+ PSZ 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();
+ sprintf(filename, Colfn, 1);
+ To_File = filename;
+ len = GetFileLength(g);
+ To_File = savfn;
+
+ if (len >= 0) {
+ if (!(len % clen))
+ card = len / clen; // Fixed length file
+ else
+ sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen);
+
+ if (trace)
+ 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 (int)((Block - 1) * Nrec + Last);
+ } // 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, char *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) == -1) {
+ sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
+ close(h);
+ return true;
+ } // endif h
+
+ write(h, &c, 1); // This actually fills the empty file
+ close(h);
+ return false;
+ } // 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:
+ strcpy(opmode, "rb");
+ break;
+ case MODE_DELETE:
+ if (!Tdbp->GetNext()) {
+ // Store the number of deleted lines
+ DelRows = Cardinality(g);
+
+ // This will delete the whole file
+ strcpy(opmode, "wb");
+ break;
+ } // endif
+
+ // Selective delete, pass thru
+ case MODE_UPDATE:
+ UseTemp = Tdbp->IsUsingTemp(g);
+ strcpy(opmode, (UseTemp) ? "rb" : "r+b");
+ break;
+ case MODE_INSERT:
+ if (MaxBlk) {
+ if (!Block)
+ if (MakeEmptyFile(g, To_File))
+ return true;
+
+ strcpy(opmode, "r+b"); // Required to update empty blocks
+ } else if (Last == Nrec)
+ strcpy(opmode, "ab");
+ else
+ strcpy(opmode, "r+b"); // Required to update the last block
+
+ break;
+ default:
+ sprintf(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)
+ htrc("%s\n", g->Message);
+
+ return (mode == MODE_READ && errno == ENOENT)
+ ? PushWarning(g, Tdbp) : true;
+ } // endif Stream
+
+ if (trace)
+ 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);
+
+ 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 = 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);
+
+ } //endif mode
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* Do initial action when inserting. */
+/***********************************************************************/
+bool VCTFAM::InitInsert(PGLOBAL g)
+ {
+ // We come here in MODE_INSERT only
+ if (Last == Nrec) {
+ CurBlk = Block;
+ CurNum = 0;
+ AddBlock = !MaxBlk;
+ } else {
+ int rc;
+ PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
+
+ // The starting point must be at the end of file as for append.
+ CurBlk = Block - 1;
+ CurNum = Last;
+
+ // Prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return true;
+ } // endif
+
+ if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
+ g->jump_level--;
+ return true;
+ } // endif
+
+ // Last block must be updated by new values
+ for (; cp; cp = (PVCTCOL)cp->Next)
+ cp->ReadBlock(g);
+
+ g->jump_level--;
+ } // endif Last
+
+ // We are not currently using a temporary file for Insert
+ T_Stream = Stream;
+ return false;
+ } // 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)
+ 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)
+ 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) {
+ strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
+ 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)) {
+ sprintf(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)
+ 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)
+ 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 preceeding 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)
+ 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))) {
+ sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#else
+ if (chsize(h, Headlen + Block * Blksize)) {
+ sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#endif
+
+ close(h);
+
+ if (trace)
+ 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)
+ {
+ char *opmode, tempname[_MAX_PATH];
+ bool rc = false;
+
+ /*********************************************************************/
+ /* Open the temporary file, Spos is at the beginning of file. */
+ /*********************************************************************/
+ PlugSetPath(tempname, To_File, Tdbp->GetPath());
+ strcat(PlugRemoveType(tempname, 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)
+ 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)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec));
+ else
+ req = (size_t)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)) {
+ sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ len = fread(To_Buf, Clens[i], req, Stream);
+
+ if (trace)
+ htrc("after read req=%d len=%d\n", req, len);
+
+ if (len != req) {
+ sprintf(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)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
+ sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ } // endif UseTemp
+
+ if (trace)
+ 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) {
+ sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ if (Spos == Fpos)
+ eof = false;
+
+ } // endif UseTemp
+
+ if (trace)
+ 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)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) {
+ sprintf(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)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)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
+ sprintf(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)
+ {
+ int rc = 0, wrc = RC_OK;
+ MODE mode = Tdbp->GetMode();
+
+ 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);
+ longjmp(g->jumper[g->jump_level], 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)
+ 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();
+ LPCSTR name = Tdbp->GetName();
+ PCATLG cat = PlgGetCatalog(g);
+
+ defp->SetBlock(Block);
+ defp->SetLast(Last);
+
+ if (!cat->SetIntCatInfo("Blocks", Block) ||
+ !cat->SetIntCatInfo("Last", Last)) {
+ sprintf(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)
+ 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)) {
+ sprintf(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)
+ sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
+ else
+ sprintf(g->Message, MSG(READ_ERROR),
+ To_File, strerror(errno));
+
+ if (trace)
+ htrc(" Read error: %s\n", g->Message);
+
+ return true;
+ } // endif
+
+ if (trace)
+ 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)
+ 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)) {
+ sprintf(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)) {
+ sprintf(g->Message, MSG(WRITE_STRERROR),
+ (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
+
+ if (trace)
+ 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];
+ int 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)
+ 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 {
+ strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
+ 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))
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "map", (int) rc, filename);
+
+ if (trace)
+ 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 = mm.lenL;
+ 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);
+ sprintf(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 = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
+ strcpy((char*)fp->Fname, 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)
+ 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);
+ 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)
+ {
+ int rc;
+ 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
+
+ // Prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return true;
+ } // endif
+
+ if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
+ g->jump_level--;
+ return true;
+ } // endif
+
+ // Initialize the column block pointer
+ for (; cp; cp = (PVCTCOL)cp->Next)
+ cp->ReadBlock(g);
+
+ g->jump_level--;
+ return false;
+ } // end of InitInsert
+
+/***********************************************************************/
+/* Data Base write routine for VMP access method. */
+/***********************************************************************/
+int VCMFAM::WriteBuffer(PGLOBAL g)
+ {
+ if (trace)
+ 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) {
+ strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
+ 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)
+ {
+ int i;
+ int m, n;
+
+ if (trace)
+ 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)
+ 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 preceeding lines is */
+ /* not required here, just setting of future Spos and Tpos. */
+ /*******************************************************************/
+ Tpos = Fpos; // Spos is set below
+ else if (Fpos > Spos) {
+ /*******************************************************************/
+ /* 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 = Fpos - Spos; n > 0; n -= req) {
+ soff = Spos % Nrec;
+ toff = Tpos % Nrec;
+ req = (size_t)min(n, Nrec - 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...
+ n = Fpos - Spos;
+
+ 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)
+ htrc("move %d bytes\n", n);
+
+ } // endif n
+
+ if (irc == RC_OK) {
+ Spos = Fpos + 1; // New start position
+
+ if (trace)
+ htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
+
+ } else {
+ /*******************************************************************/
+ /* Last call after EOF has been reached. 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) {
+ sprintf(g->Message, MSG(FUNCTION_ERROR),
+ "SetFilePointer", GetLastError());
+ CloseHandle(fp->Handle);
+ return RC_FX;
+ } // endif
+
+ if (trace)
+ htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
+
+ if (!SetEndOfFile(fp->Handle)) {
+ sprintf(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)) {
+ sprintf(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
+
+/***********************************************************************/
+/* Data Base close routine for VMP access method. */
+/***********************************************************************/
+void VCMFAM::CloseTableFile(PGLOBAL g)
+ {
+ 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)
+ PlugCloseFile(g, To_Fb);
+
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* ReadBlock: Read column values from current block. */
+/***********************************************************************/
+bool VCMFAM::ReadBlock(PGLOBAL g, 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)
+ 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)
+ 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 g, PVCTCOL colp)
+ {
+#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)
+ 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:
+ strcpy(opmode, "rb");
+ break;
+ case MODE_DELETE:
+ if (!Tdbp->GetNext()) {
+ // Store the number of deleted lines
+ DelRows = Cardinality(g);
+
+ // This will delete the whole file
+ strcpy(opmode, "wb");
+
+ // This will stop the process by causing GetProgMax to return 0.
+ ResetTableSize(g, 0, Nrec);
+ break;
+ } // endif filter
+
+ // Selective delete, pass thru
+ case MODE_UPDATE:
+ UseTemp = Tdbp->IsUsingTemp(g);
+ strcpy(opmode, (UseTemp) ? "r": "r+");
+ break;
+ case MODE_INSERT:
+ strcpy(opmode, "ab");
+ break;
+ default:
+ sprintf(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 (!i++)
+ b = !Streams[cp->Index - 1];
+ else if (b != !Streams[cp->Index - 1])
+ return true;
+
+ } // 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, char *opmode, int i)
+ {
+ char filename[_MAX_PATH];
+ PDBUSER dup = PlgGetUser(g);
+
+ sprintf(filename, Colfn, i+1);
+
+ if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
+ ? PushWarning(g, Tdbp) : true;
+ } // endif Streams
+
+ if (trace)
+ 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);
+
+ 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);
+ strcpy(Tempat, Colfn);
+ PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
+ strcat(PlugRemoveType(Tempat, Tempat), ".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 = 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 = 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);
+
+ } // endif mode
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* Do initial action when inserting. */
+/***********************************************************************/
+bool VECFAM::InitInsert(PGLOBAL g)
+ {
+ // 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)
+ 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])) {
+ sprintf(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...) */
+/***********************************************************************/
+int VECFAM::DeleteRecords(PGLOBAL g, int irc)
+ {
+ /*********************************************************************/
+ /* There is an alternative here: */
+ /* 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. */
+ /* This depends on the Check setting, false by default. */
+ /*********************************************************************/
+ if (trace)
+ 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)
+ 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 preceeding lines is not required here. */
+ /* Set the future Tpos, and give Spos a value to block copying. */
+ /*****************************************************************/
+ Spos = Tpos = Fpos;
+
+ /*********************************************************************/
+ /* 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)
+ 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 file and reopen it 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++) {
+ sprintf(filename, 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]))) {
+ sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#else
+ if (chsize(h, Tpos * Clens[i])) {
+ sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#endif
+
+ close(h);
+
+ if (trace)
+ 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. */
+ /*****************************************************************/
+ sprintf(tempname, Tempat, i+1);
+
+ if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
+ if (trace)
+ 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 *bn)
+ {
+ int i;
+ int 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)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)) {
+ sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ len = fread(To_Buf, Clens[i], req, Streams[i]);
+
+ if (trace)
+ htrc("after read req=%d len=%d\n", req, len);
+
+ if (len != req) {
+ sprintf(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)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
+ sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ if (trace)
+ htrc("after write pos=%d\n", ftell(Streams[i]));
+
+ } // endfor i
+
+ Tpos += (int)req;
+ Spos += (int)req;
+
+ if (trace)
+ 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;
+ sprintf(filename, Colfn, i+1);
+ PlugSetPath(filename, filename, Tdbp->GetPath());
+ strcat(PlugRemoveType(filetemp, filename), ".ttt");
+ remove(filetemp); // May still be there from previous error
+
+ if (rename(filename, filetemp)) { // Save file for security
+ sprintf(g->Message, MSG(RENAME_ERROR),
+ filename, filetemp, strerror(errno));
+ rc = RC_FX;
+ } else if (rename(tempname, filename)) {
+ sprintf(g->Message, MSG(RENAME_ERROR),
+ tempname, filename, strerror(errno));
+ rc = rename(filetemp, filename); // Restore saved file
+ rc = RC_FX;
+ } else if (remove(filetemp)) {
+ sprintf(g->Message, MSG(REMOVE_ERROR),
+ filetemp, strerror(errno));
+ rc = RC_INFO; // Acceptable
+ } // endif's
+
+ } // endfor i
+
+ return rc;
+ } // end of RenameTempFile
+
+/***********************************************************************/
+/* Data Base close routine for VEC access method. */
+/***********************************************************************/
+void VECFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc = 0, wrc = RC_OK;
+ MODE mode = Tdbp->GetMode();
+
+ 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
+ longjmp(g->jumper[g->jump_level], 44);
+
+ } else if (mode == MODE_UPDATE) {
+ if (UseTemp && !InitUpdate) {
+ // Write any intermediate lines to temp file
+ Fpos = OldBlk * Nrec;
+ wrc = MoveIntermediateLines(g);
+// 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) {
+ // Write any intermediate lines to temp file
+ Fpos = (Block - 1) * Nrec + Last;
+ wrc = MoveIntermediateLines(g);
+ } // endif UseTemp
+
+ } // endif's mode
+
+ if (UseTemp && !InitUpdate) {
+ // If they are errors, leave files unchanged
+ if (wrc == RC_OK)
+ rc = RenameTempFile(g);
+ else
+ longjmp(g->jumper[g->jump_level], 44);
+
+ } 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)
+ 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)
+ 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)) {
+ sprintf(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];
+
+ sprintf(fn, Colfn, colp->Index);
+#if defined(WIN32)
+ if (feof(Streams[i]))
+#else // !WIN32
+ if (errno == NO_ERROR)
+#endif // !WIN32
+ sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
+ else
+ sprintf(g->Message, MSG(READ_ERROR),
+ fn, strerror(errno));
+
+ if (trace)
+ htrc(" Read error: %s\n", g->Message);
+
+ return true;
+ } // endif
+
+ if (trace)
+ 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)
+ 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)) {
+ sprintf(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];
+
+ sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
+ sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
+
+ if (trace)
+ 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;
+ 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 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;
+
+ } // endif mode
+
+ /*********************************************************************/
+ /* Check for void table or missing columns */
+ /*********************************************************************/
+ for (b = !Memcol[0], i = 1; i < Ncol; i++)
+ if (b != !Memcol[i])
+ return true;
+
+ /*********************************************************************/
+ /* 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];
+ int len;
+ HANDLE hFile;
+ MEMMAP mm;
+ PFBLOCK fp;
+ PDBUSER dup = PlgGetUser(g);
+
+ sprintf(filename, 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)
+ 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))
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "map", (int) rc, filename);
+ if (trace)
+ 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 = mm.lenL;
+ 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);
+ sprintf(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 = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
+ strcpy((char*)fp->Fname, 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)
+ 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);
+ 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)
+ 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)
+ 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 preceeding 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)
+ htrc("move %d bytes\n", n);
+
+ } // endif n
+
+ if (irc == RC_OK) {
+ Spos = Fpos + 1; // New start position
+
+ if (trace)
+ 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;
+
+ 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) {
+ sprintf(g->Message, MSG(FUNCTION_ERROR),
+ "SetFilePointer", GetLastError());
+ CloseHandle(fp->Handle);
+ return RC_FX;
+ } // endif
+
+ if (trace)
+ htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
+
+ if (!SetEndOfFile(fp->Handle)) {
+ sprintf(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)) {
+ sprintf(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)
+ {
+ 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);
+ sprintf(g->Message, MSG(SFP_ERROR), buf);
+ return true;
+ } // endif
+#else // !WIN32
+ if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
+ sprintf(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)
+ 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)
+ strcpy(buf, MSG(BAD_BYTE_READ));
+ else {
+ drc = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ } // endelse brc
+
+ sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
+
+ if (trace)
+ 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";
+
+ sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
+
+ if (trace)
+ 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)
+ htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
+
+ if (!brc || nbw != len) {
+ char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile";
+
+ if (brc)
+ strcpy(buf, MSG(BAD_BYTE_NUM));
+ else {
+ drc = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
+ (LPTSTR)buf, sizeof(buf), NULL);
+ } // endelse brc
+
+ sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
+
+ if (trace)
+ 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";
+
+ sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
+
+ if (trace)
+ 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) {
+ sprintf(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)
+ strcat(PlugRemoveType(filename, 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)
+ 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))) {
+ sprintf(g->Message, "Error reading header file %s", filename);
+ n = -1;
+ } else if (MaxBlk * Nrec != vh.MaxRec) {
+ sprintf(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)
+ 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
+ strcat(PlugRemoveType(filename, 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) {
+ sprintf(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))) {
+ sprintf(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, char *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)
+ char *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)
+ 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();
+ sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
+ (LPTSTR)filename, sizeof(filename), NULL);
+ strcat(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)
+ htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
+ pos, n, MaxBlk, Blksize);
+
+ if (lseek64(h, pos, SEEK_SET) < 0) {
+ sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
+ close(h);
+ return true;
+ } // endif h
+
+ write(h, &c, 1); // This actually fills the empty file
+ close(h);
+ return false;
+#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) {
+ sprintf(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)
+ 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:
+ sprintf(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();
+ sprintf(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);
+ strcat(g->Message, filename);
+ } // endif Hfile
+
+ if (trace)
+ 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) {
+ sprintf(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;
+ strcpy(g->Message, MSG(NO_VCT_DELETE));
+ break;
+ } // endif
+
+ // Selective delete, pass thru
+ case MODE_UPDATE:
+ UseTemp = Tdbp->IsUsingTemp(g);
+ oflag = (UseTemp) ? O_RDONLY : O_RDWR;
+ break;
+ default:
+ sprintf(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;
+ sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
+ strcat(g->Message, strerror(errno));
+ } // endif Hfile
+
+ if (trace)
+ 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)
+ 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);
+
+ 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 = 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);
+
+ } //endif mode
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* Data Base write routine for huge VCT access method. */
+/***********************************************************************/
+int BGVFAM::WriteBuffer(PGLOBAL g)
+ {
+ if (trace)
+ 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) {
+ strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
+ 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)
+ 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)
+ 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 preceeding 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)
+ 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();
+
+ sprintf(g->Message, MSG(SETEOF_ERROR), drc);
+ return RC_FX;
+ } // endif error
+#else // !WIN32
+ if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
+ sprintf(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());
+ strcat(PlugRemoveType(tempname, tempname), ".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();
+ sprintf(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);
+ strcat(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;
+ sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
+ strcat(g->Message, 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)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec));
+ else
+ req = (DWORD)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)
+ 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;
+
+ 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 = 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)
+ {
+ int rc = 0, wrc = RC_OK;
+ MODE mode = Tdbp->GetMode();
+
+ 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);
+ longjmp(g->jumper[g->jump_level], 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)
+ 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)
+ 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)
+ 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)
+ 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 --------------------------- */