mariadb/storage/connect/filamvct.cpp
Mikhail Chalov 2ff01e763e Fix insecure use of strcpy, strcat and sprintf in Connect
Old style C functions `strcpy()`, `strcat()` and `sprintf()` are vulnerable to
security issues due to lacking memory boundary checks. Replace these in the
Connect storage engine with safe new and/or custom functions such as
`snprintf()` `safe_strcpy()` and `safe_strcat()`.

With this change FlawFinder and other static security analyzers report 287
fewer findings.

All new code of the whole pull request, including one or several files that are
either new files or modified ones, are contributed under the BSD-new license. I
am contributing on behalf of my employer Amazon Web Services, Inc.
2023-05-12 15:37:00 +01:00

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