diff options
Diffstat (limited to 'storage/connect/filamvct.cpp')
-rwxr-xr-x | storage/connect/filamvct.cpp | 8476 |
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 --------------------------- */
|