/*********** 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 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"
/***********************************************************************/ /* 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) */ /***********************************************************************/ typedefstruct _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 --------------------------- */
/***********************************************************************/ /* 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
/***********************************************************************/ /* 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;
/***********************************************************************/ /* 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;
/***********************************************************************/ /* 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; elseif (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) return1;
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
/***********************************************************************/ /* GetRowID: return the RowID of last read record. */ /***********************************************************************/ int VCTFAM::GetRowID(void)
{ return1 + ((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;
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); returnfalse;
err:
snprintf(g->Message, sizeof(g->Message), MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
close(h); returntrue;
} // 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) returntrue;
/*********************************************************************/ /* 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)) returntrue;
snprintf(opmode, sizeof(opmode), "r+b"); // Required to update empty blocks
} elseif (!Block || Last == Nrec)
snprintf(opmode, sizeof(opmode), "ab"); else
snprintf(opmode, sizeof(opmode), "r+b"); // Required to update the last block
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++;
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
returnfalse;
} // 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 (constchar *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();
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;
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
} 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);
/***********************************************************************/ /* 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 (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;
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)) returntrue;
opmode = "r+b";
} else
opmode = "wb";
if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) { if (trace(1))
htrc("%s\n", g->Message);
/***********************************************************************/ /* 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)); returntrue;
} // 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); returntrue;
} // 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)); returntrue;
} // endif
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 (trace(1))
htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
} // endfor n
returnfalse;
} // 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)) returnfalse;
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)); returntrue;
} // endif
} elsefor (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)); returntrue;
} // endif
/***********************************************************************/ /* 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);
} elseif (AddBlock) { // Last block was not written
rc = ResetTableSize(g, CurBlk, Nrec); throw44;
} // endif
} elseif (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
/***********************************************************************/ /* 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();
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);
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);
returntrue;
} // endif
if (trace(1))
num_read++;
returnfalse;
} // 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);
// 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));
returntrue;
} // endif
#ifdefined(UNIX)
fflush(T_Stream); //NGC #endif
#ifdef _DEBUG
num_write++; #endif
returnfalse;
} // 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) returntrue;
/*********************************************************************/ /* 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)) returntrue;
// 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)); returntrue;
} // 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
/*******************************************************************/ /* 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 (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
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
// 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 (constchar *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
// 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;
#ifdefined(_WIN32)
DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
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));
} 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
returnfalse;
} // 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);
/***********************************************************************/ /* 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++;
returnfalse;
} // 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)))
{ #ifdefined(_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
returnfalse;
} // 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
/***********************************************************************/ /* 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); returntrue;
} // 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
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)) returntrue;
// Check for void table or missing columns for (b = !Streams[0], i = 1; i < Ncol; i++) if (b != !Streams[i]) returntrue;
} 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)) returntrue;
// 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)) returntrue;
// 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]; elseif (b != !Streams[cp->Index - 1]) returntrue;
} // 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);
// 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
returnfalse;
} // 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; returnfalse;
} // 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
} 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;
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);
/***********************************************************************/ /* 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)); returntrue;
} // 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); returntrue;
} // 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)); returntrue;
} // 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
returnfalse;
} // 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
/***********************************************************************/ /* 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
// 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);
/***********************************************************************/ /* 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);
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); #ifdefined(_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);
returntrue;
} // endif
if (trace(1))
num_read++;
returnfalse;
} // 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)); returntrue;
} // 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];
if (trace(1))
htrc("Write error: %s\n", strerror(errno));
returntrue;
} else
Spos = Fpos + n;
#ifdefined(UNIX)
fflush(Streams[i]); //NGC #endif returnfalse;
} // 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
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)) returntrue;
} 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)) returntrue;
/*******************************************************************/ /* 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)) returntrue;
// 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]; elseif (b != !Memcol[cp->Index - 1]) returntrue;
} // 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);
/*****************************************************************/ /* 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); returnfalse;
} // endif len
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);
returnfalse;
} // 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();
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
returnfalse;
} // 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 (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
} elseif ((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];
#ifdefined(_WIN32)
DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
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);
} elseif (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)
{ #ifdefined(_WIN32) char buf[256];
DWORD drc, m = (b) ? FILE_END : FILE_BEGIN;
LARGE_INTEGER of;
/***********************************************************************/ /* 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;
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);
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);
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;
/*********************************************************************/ /* Update block info if necessary. */ /*********************************************************************/ if (Block < 0) if ((Headlen = GetBlockInfo(g)) < 0) returntrue;
PlugSetPath(filename, To_File, Tdbp->GetPath());
if (trace(1))
htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
filename, mode, Last);
/*********************************************************************/ /* 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)) returntrue;
// Required to update empty blocks
access = GENERIC_READ | GENERIC_WRITE;
} elseif (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;
/*********************************************************************/ /* Use specific Windows API functions. */ /*********************************************************************/
Hfile = CreateFile(filename, access, share, NULL, creation,
FILE_ATTRIBUTE_NORMAL, NULL);
if (mode == MODE_INSERT) { /*******************************************************************/ /* In Insert mode we must position the cursor at end of file. */ /*******************************************************************/
LARGE_INTEGER of;
#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)) returntrue;
// Required to update empty blocks
oflag = O_RDWR;
} elseif (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
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
returnfalse;
} // 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 (!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;
/***********************************************************************/ /* 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. */ /***************************************************************/ #ifdefined(_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 elseif (MakeEmptyFile(g, tempname)) returntrue;
if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) returntrue;
} // 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)) returntrue;
if (Spos == Fpos)
eof = false;
} // endif Usetemp...
if (trace(1))
htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
} // endfor n
returnfalse;
} // 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)) returnfalse;
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)) returntrue;
if (BigWrite(g, Hfile, To_Buf, n * Clens[i])) returntrue;
} // 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)) returntrue;
if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) returntrue;
} // endfor i
Tpos += req;
} // endfor n
} // endif MaxBlk
returnfalse;
} // 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);
} elseif (AddBlock) { // Last block was not written
rc = ResetTableSize(g, CurBlk, Nrec); throw44;
} // endif
} elseif (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);
/***********************************************************************/ /* 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;
#if0// This is probably unuseful as the file is directly accessed #ifdefined(_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 (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec)) returntrue;
if (trace(1))
num_read++;
returnfalse;
} // 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);
//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)) returntrue;
returnfalse;
} // end of WriteBlock
/* ----------------------- End of FilAMVct --------------------------- */
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.232Bemerkung:
(vorverarbeitet am 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.