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