mariadb/storage/connect/filamvct.cpp
2023-01-28 18:22:55 +01:00

4328 lines
143 KiB
C++

/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMVCT */
/* ------------- */
/* Version 2.6 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2020 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* This program are the VCT file access method classes. */
/* Added in version 2: */
/* - Split Vec format. */
/* - Partial delete. */
/* - Use of tempfile for update. */
/* */
/***********************************************************************/
/***********************************************************************/
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
#if defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#if defined(__BORLANDC__)
#define __MFC_COMPAT__ // To define min/max as macro
#endif // __BORLAND__
//#include <windows.h>
#include <sys/stat.h>
#else // !_WIN32
#if defined(UNIX)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#define NO_ERROR 0
#else // !UNIX
#include <io.h>
#endif // !UNIX
#include <fcntl.h>
#endif // !_WIN32
#include <m_string.h>
/***********************************************************************/
/* Include application header files: */
/* global.h is header containing all global declarations. */
/* plgdbsem.h is header containing the DB application declarations. */
/* tabdos.h is header containing the TABDOS class declarations. */
/***********************************************************************/
#include "global.h"
#include "osutil.h" // Unuseful for WINDOWS
#include "plgdbsem.h"
#include "valblk.h"
#include "filamfix.h"
#include "tabdos.h"
#include "tabvct.h"
#include "maputil.h"
#include "filamvct.h"
#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
extern int num_read, num_there; // Statistics
static int num_write;
/***********************************************************************/
/* Header containing block info for not split VEC tables. */
/* Block and last values can be calculated from NumRec and Nrec. */
/* This is better than directly storing Block and Last because it */
/* make possible to use the same file with tables having a different */
/* block size value (Element -> Nrec) */
/* Note: can be in a separate file if header=1 or a true header (2) */
/***********************************************************************/
typedef struct _vecheader {
//int Block; /* The number of used blocks */
//int Last; /* The number of used records in last block */
int MaxRec; /* Max number of records (True vector format)*/
int NumRec; /* Number of valid records in the table */
} VECHEADER;
/***********************************************************************/
/* Char VCT column blocks are right filled with blanks (blank = true) */
/* Conversion of block values allowed conditionally for insert only. */
/***********************************************************************/
PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
bool check = true, bool blank = true, bool un = false);
/* -------------------------- Class VCTFAM --------------------------- */
/***********************************************************************/
/* Implementation of the VCTFAM class. */
/***********************************************************************/
VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp)
{
Last = tdp->GetLast();
MaxBlk = (tdp->GetEstimate() > 0) ?
((tdp->GetEstimate() - 1) / Nrec + 1) : 0;
NewBlock = NULL;
AddBlock = false;
Split = false;
if ((Header = (MaxBlk) ? tdp->Header : 0))
Block = Last = -1;
Bsize = Nrec;
CurNum = Nrec - 1;
Colfn = NULL;
Tempat = NULL;
Clens = NULL;
Deplac = NULL;
Isnum = NULL;
Ncol = 0;
} // end of VCTFAM standard constructor
VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp)
{
MaxBlk = txfp->MaxBlk;
NewBlock = NULL;
AddBlock = false;
Split = txfp->Split;
Header = txfp->Header;
Bsize = txfp->Bsize;
Colfn = txfp->Colfn;
Tempat = txfp->Tempat;
Clens = txfp->Clens;
Deplac = txfp->Deplac;
Isnum = txfp->Isnum;
Ncol = txfp->Ncol;
} // end of VCTFAM copy constructor
/***********************************************************************/
/* VCT GetFileLength: returns file size in number of bytes. */
/* This function is here to be accessible by VECFAM and VMPFAM. */
/***********************************************************************/
int VCTFAM::GetFileLength(PGLOBAL g)
{
if (Split) {
// Get the total file length
char filename[_MAX_PATH];
PCSZ savfile = To_File;
int i, len = 0;
// Initialize the array of file structures
if (!Colfn) {
// Prepare the column file name pattern and set Ncol
Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
Ncol = ((PVCTDEF)Tdbp->GetDef())->MakeFnPattern(Colfn);
} // endif Colfn
To_File = filename;
for (i = 0; i < Ncol; i++) {
snprintf(filename, _MAX_PATH, Colfn, i+1);
len += TXTFAM::GetFileLength(g);
} // endfor i
To_File = savfile;
return len;
} else
return TXTFAM::GetFileLength(g);
} // end of GetFileLength
/***********************************************************************/
/* Reset read/write position values. */
/***********************************************************************/
void VCTFAM::Reset(void)
{
FIXFAM::Reset();
NewBlock = NULL;
AddBlock = false;
CurNum = Nrec - 1;
} // end of Reset
/***********************************************************************/
/* Get the Headlen, Block and Last info from the file header. */
/***********************************************************************/
int VCTFAM::GetBlockInfo(PGLOBAL g)
{
char filename[_MAX_PATH];
int h, k, n;
VECHEADER vh;
if (Header < 1 || Header > 3 || !MaxBlk) {
snprintf(g->Message, sizeof(g->Message), "Invalid header value %d", Header);
return -1;
} else
n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
PlugSetPath(filename, To_File, Tdbp->GetPath());
if (Header == 2)
{
PlugRemoveType(filename, filename);
safe_strcat(filename, sizeof(filename), ".blk");
}
if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
|| !_filelength(h)) {
// Consider this is a void table
Last = Nrec;
Block = 0;
if (h != -1)
close(h);
return n;
} else if (Header == 3)
k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END);
if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) {
snprintf(g->Message, sizeof(g->Message), "Error reading header file %s", filename);
n = -1;
} else if (MaxBlk * Nrec != vh.MaxRec) {
snprintf(g->Message, sizeof(g->Message), "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
vh.MaxRec, MaxBlk, Nrec);
n = -1;
} else {
Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
Last = (vh.NumRec + Nrec - 1) % Nrec + 1;
} // endif s
close(h);
return n;
} // end of GetBlockInfo
/***********************************************************************/
/* Get the Headlen, Block and Last info from the file header. */
/***********************************************************************/
bool VCTFAM::SetBlockInfo(PGLOBAL g)
{
char filename[_MAX_PATH];
bool rc = false;
size_t n;
VECHEADER vh;
FILE *s;
PlugSetPath(filename, To_File, Tdbp->GetPath());
if (Header != 2) {
if (Stream) {
s = Stream;
if (Header == 1)
/*k =*/ fseek(s, 0, SEEK_SET);
} else
s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b");
} else { // Header == 2
PlugRemoveType(filename, filename);
safe_strcat(filename, sizeof(filename), ".blk");
s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
} // endif Header
if (!s) {
snprintf(g->Message, sizeof(g->Message), "Error opening header file %s", filename);
return true;
} else if (Header == 3)
/*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END);
vh.MaxRec = MaxBlk * Bsize;
vh.NumRec = (Block - 1) * Nrec + Last;
if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) {
snprintf(g->Message, sizeof(g->Message), "Error writing header file %s", filename);
rc = true;
} // endif fread
if (Header == 2 || !Stream)
fclose(s);
return rc;
} // end of SetBlockInfo
/***********************************************************************/
/* Use BlockTest to reduce the table estimated size. */
/***********************************************************************/
int VCTFAM::MaxBlkSize(PGLOBAL g, int)
{
int rc = RC_OK, savcur = CurBlk;
int size;
// Roughly estimate the table size as the sum of blocks
// that can contain good rows
for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
if ((rc = Tdbp->TestBlock(g)) == RC_OK)
size += (CurBlk == Block - 1) ? Last : Nrec;
else if (rc == RC_EF)
break;
CurBlk = savcur;
return size;
} // end of MaxBlkSize
/***********************************************************************/
/* VCT Cardinality: returns table cardinality in number of rows. */
/* This function can be called with a null argument to test the */
/* availability of Cardinality implementation (1 yes, 0 no). */
/***********************************************************************/
int VCTFAM::Cardinality(PGLOBAL g)
{
if (!g)
return 1;
if (Block < 0)
{
if (Split) {
// Separate column files and no pre setting of Block and Last
// This allows to see a table modified externally, but Block
// and Last must be set from the file cardinality.
// Only happens when called by sub classes.
char filename[_MAX_PATH];
PCSZ savfn = To_File;
int len, clen, card = -1;
PCOLDEF cdp = Tdbp->GetDef()->GetCols();
if (!Colfn) {
// Prepare the column file name pattern
Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
} // endif Colfn
// Use the first column file to calculate the cardinality
clen = cdp->GetClen();
snprintf(filename, _MAX_PATH, Colfn, 1);
To_File = filename;
len = TXTFAM::GetFileLength(g);
To_File = savfn;
if (len >= 0) {
if (!(len % clen))
card = len / clen; // Fixed length file
else
snprintf(g->Message, sizeof(g->Message), MSG(NOT_FIXED_LEN), To_File, len, clen);
if (trace(1))
htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen);
} else
card = 0;
// Set number of blocks for later use
Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
Last = (card + Nrec - 1) % Nrec + 1;
return card;
} else {
// Vector table having Block and Last info in a Header (file)
if ((Headlen = GetBlockInfo(g)) < 0)
return -1; // Error
} // endif split
}
return (Block) ? ((Block - 1) * Nrec + Last) : 0;
} // end of Cardinality
/***********************************************************************/
/* GetRowID: return the RowID of last read record. */
/***********************************************************************/
int VCTFAM::GetRowID(void)
{
return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk
: (Block - 1) * Nrec + Last);
} // end of GetRowID
/***********************************************************************/
/* VCT Create an empty file for Vector formatted tables. */
/***********************************************************************/
bool VCTFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
{
// Vector formatted file: this will create an empty file of the
// required length if it does not exists yet.
char filename[_MAX_PATH], c = 0;
int h, n;
PlugSetPath(filename, fn, Tdbp->GetPath());
#if defined(_WIN32)
h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE);
#else // !_WIN32
h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
#endif // !_WIN32
if (h == -1)
return true;
n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) < 0)
goto err;
// This actually fills the empty file
if (write(h, &c, 1) < 0)
goto err;
close(h);
return false;
err:
snprintf(g->Message, sizeof(g->Message), MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
close(h);
return true;
} // end of MakeEmptyFile
/***********************************************************************/
/* VCT Access Method opening routine. */
/* New method now that this routine is called recursively (last table */
/* first in reverse order): index blocks are immediately linked to */
/* join block of next table if it exists or else are discarted. */
/***********************************************************************/
bool VCTFAM::OpenTableFile(PGLOBAL g)
{
char opmode[4], filename[_MAX_PATH];
MODE mode = Tdbp->GetMode();
PDBUSER dbuserp = PlgGetUser(g);
/*********************************************************************/
/* Update block info if necessary. */
/*********************************************************************/
if (Block < 0)
if ((Headlen = GetBlockInfo(g)) < 0)
return true;
/*********************************************************************/
/* Open according to input/output mode required. */
/*********************************************************************/
switch (mode) {
case MODE_READ:
strcpy(opmode, "rb");
break;
case MODE_DELETE:
if (!Tdbp->GetNext()) {
// Store the number of deleted lines
DelRows = Cardinality(g);
// This will delete the whole file
strcpy(opmode, "wb");
break;
} // endif
// Selective delete, pass thru
/* fall through */
case MODE_UPDATE:
UseTemp = Tdbp->IsUsingTemp(g);
strcpy(opmode, (UseTemp) ? "rb" : "r+b");
break;
case MODE_INSERT:
if (MaxBlk) {
if (!Block)
if (MakeEmptyFile(g, To_File))
return true;
strcpy(opmode, "r+b"); // Required to update empty blocks
} else if (!Block || Last == Nrec)
strcpy(opmode, "ab");
else
strcpy(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:
strcpy(opmode, "rb");
break;
case MODE_DELETE:
if (!Tdbp->GetNext()) {
// Store the number of deleted lines
DelRows = Cardinality(g);
// This will delete the whole file
strcpy(opmode, "wb");
// This will stop the process by causing GetProgMax to return 0.
ResetTableSize(g, 0, Nrec);
break;
} // endif filter
// Selective delete, pass thru
/* fall through */
case MODE_UPDATE:
UseTemp = Tdbp->IsUsingTemp(g);
strcpy(opmode, (UseTemp) ? "rb": "r+b");
break;
case MODE_INSERT:
strcpy(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 --------------------------- */