mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
2ff01e763e
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.
4329 lines
143 KiB
C++
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 --------------------------- */
|