mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
00a313ecf3
Note, the fix for "MDEV-23328 Server hang due to Galera lock conflict resolution" was null-merged. 10.4 version of the fix is coming up separately
2119 lines
69 KiB
C++
2119 lines
69 KiB
C++
/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
|
|
/* PROGRAM NAME: FILAMTXT */
|
|
/* ------------- */
|
|
/* Version 1.8 */
|
|
/* */
|
|
/* COPYRIGHT: */
|
|
/* ---------- */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2005-2020 */
|
|
/* */
|
|
/* WHAT THIS PROGRAM DOES: */
|
|
/* ----------------------- */
|
|
/* This program are the Text file access method classes. */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/***********************************************************************/
|
|
/* Include relevant sections of the System header files. */
|
|
/***********************************************************************/
|
|
#include "my_global.h"
|
|
#if defined(__WIN__)
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#if defined(__BORLANDC__)
|
|
#define __MFC_COMPAT__ // To define min/max as macro
|
|
#endif // __BORLANDC__
|
|
//#include <windows.h>
|
|
#else // !__WIN__
|
|
#if defined(UNIX) || defined(UNIV_LINUX)
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
//#if !defined(sun) // Sun has the ftruncate fnc.
|
|
//#define USETEMP // Force copy mode for DELETE
|
|
//#endif // !sun
|
|
#else // !UNIX
|
|
#include <io.h>
|
|
#endif // !UNIX
|
|
#include <fcntl.h>
|
|
#endif // !__WIN__
|
|
|
|
/***********************************************************************/
|
|
/* Include application header files: */
|
|
/* global.h is header containing all global declarations. */
|
|
/* plgdbsem.h is header containing the DB application declarations. */
|
|
/* filamtxt.h is header containing the file AM classes declarations. */
|
|
/***********************************************************************/
|
|
#include "global.h"
|
|
#include "plgdbsem.h"
|
|
#include "filamtxt.h"
|
|
#include "tabdos.h"
|
|
#include "tabjson.h"
|
|
|
|
#if defined(UNIX) || defined(UNIV_LINUX)
|
|
#include "osutil.h"
|
|
#define _fileno fileno
|
|
#define _O_RDONLY O_RDONLY
|
|
#endif
|
|
|
|
extern int num_read, num_there, num_eq[2]; // Statistics
|
|
|
|
/***********************************************************************/
|
|
/* Routine called externally by TXTFAM SortedRows functions. */
|
|
/***********************************************************************/
|
|
PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
|
|
|
|
/* --------------------------- Class TXTFAM -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Constructors. */
|
|
/***********************************************************************/
|
|
TXTFAM::TXTFAM(PDOSDEF tdp)
|
|
{
|
|
Tdbp = NULL;
|
|
To_Fb = NULL;
|
|
|
|
if (tdp) {
|
|
To_File = tdp->Fn;
|
|
Lrecl = tdp->Lrecl;
|
|
Eof = tdp->Eof;
|
|
Ending = tdp->Ending;
|
|
} else {
|
|
To_File = NULL;
|
|
Lrecl = 0;
|
|
Eof = false;
|
|
#if defined(__WIN__)
|
|
Ending = 2;
|
|
#else
|
|
Ending = 1;
|
|
#endif
|
|
} // endif tdp
|
|
|
|
Placed = false;
|
|
IsRead = true;
|
|
Blocked = false;
|
|
To_Buf = NULL;
|
|
DelBuf = NULL;
|
|
BlkPos = NULL;
|
|
To_Pos = NULL;
|
|
To_Sos = NULL;
|
|
To_Upd = NULL;
|
|
Posar = NULL;
|
|
Sosar = NULL;
|
|
Updar = NULL;
|
|
BlkLen = 0;
|
|
Buflen = 0;
|
|
Dbflen = 0;
|
|
Rows = 0;
|
|
DelRows = 0;
|
|
Headlen = 0;
|
|
Block = 0;
|
|
Last = 0;
|
|
Nrec = 1;
|
|
OldBlk = -1;
|
|
CurBlk = -1;
|
|
ReadBlks = 0;
|
|
CurNum = 0;
|
|
Rbuf = 0;
|
|
Modif = 0;
|
|
Blksize = 0;
|
|
Fpos = Spos = Tpos = 0;
|
|
Padded = false;
|
|
Abort = false;
|
|
CrLf = (char*)(Ending == 1 ? "\n" : "\r\n");
|
|
} // end of TXTFAM standard constructor
|
|
|
|
TXTFAM::TXTFAM(PTXF txfp)
|
|
{
|
|
Tdbp = txfp->Tdbp;
|
|
To_Fb = txfp->To_Fb;
|
|
To_File = txfp->To_File;
|
|
Lrecl = txfp->Lrecl;
|
|
Placed = txfp->Placed;
|
|
IsRead = txfp->IsRead;
|
|
Blocked = txfp->Blocked;
|
|
To_Buf = txfp->To_Buf;
|
|
DelBuf = txfp->DelBuf;
|
|
BlkPos = txfp->BlkPos;
|
|
To_Pos = txfp->To_Pos;
|
|
To_Sos = txfp->To_Sos;
|
|
To_Upd = txfp->To_Upd;
|
|
Posar = txfp->Posar;
|
|
Sosar = txfp->Sosar;
|
|
Updar = txfp->Updar;
|
|
BlkLen = txfp->BlkLen;
|
|
Buflen = txfp->Buflen;
|
|
Dbflen = txfp->Dbflen;
|
|
Rows = txfp->Rows;
|
|
DelRows = txfp->DelRows;
|
|
Headlen = txfp->Headlen;
|
|
Block = txfp->Block;
|
|
Last = txfp->Last;
|
|
Nrec = txfp->Nrec;
|
|
OldBlk = txfp->OldBlk;
|
|
CurBlk = txfp->CurBlk;
|
|
ReadBlks = txfp->ReadBlks;
|
|
CurNum = txfp->CurNum;
|
|
Rbuf = txfp->Rbuf;
|
|
Modif = txfp->Modif;
|
|
Blksize = txfp->Blksize;
|
|
Fpos = txfp->Fpos;
|
|
Spos = txfp->Spos;
|
|
Tpos = txfp->Tpos;
|
|
Padded = txfp->Padded;
|
|
Eof = txfp->Eof;
|
|
Ending = txfp->Ending;
|
|
Abort = txfp->Abort;
|
|
CrLf = txfp->CrLf;
|
|
} // end of TXTFAM copy constructor
|
|
|
|
/***********************************************************************/
|
|
/* Reset: reset position values at the beginning of file. */
|
|
/***********************************************************************/
|
|
void TXTFAM::Reset(void)
|
|
{
|
|
Rows = 0;
|
|
DelRows = 0;
|
|
OldBlk = -1;
|
|
CurBlk = -1;
|
|
ReadBlks = 0;
|
|
CurNum = 0;
|
|
Rbuf = 0;
|
|
Modif = 0;
|
|
Placed = false;
|
|
} // end of Reset
|
|
|
|
/***********************************************************************/
|
|
/* TXT GetFileLength: returns file size in number of bytes. */
|
|
/***********************************************************************/
|
|
int TXTFAM::GetFileLength(PGLOBAL g)
|
|
{
|
|
char filename[_MAX_PATH];
|
|
int h;
|
|
int len;
|
|
|
|
PlugSetPath(filename, To_File, Tdbp->GetPath());
|
|
h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
|
|
|
|
if (trace(1))
|
|
htrc("GetFileLength: fn=%s h=%d\n", filename, h);
|
|
|
|
if (h == -1) {
|
|
if (errno != ENOENT) {
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
len = -1;
|
|
} else {
|
|
len = 0; // File does not exist yet
|
|
g->Message[0]= '\0';
|
|
} // endif errno
|
|
|
|
} else {
|
|
if ((len = _filelength(h)) < 0)
|
|
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
|
|
|
|
if (Eof && len)
|
|
len--; // Do not count the EOF character
|
|
|
|
close(h);
|
|
} // endif h
|
|
|
|
return len;
|
|
} // end of GetFileLength
|
|
|
|
/***********************************************************************/
|
|
/* 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). */
|
|
/* Note: This function is meant only for fixed length files but is */
|
|
/* placed here to be available to FIXFAM and MPXFAM classes. */
|
|
/***********************************************************************/
|
|
int TXTFAM::Cardinality(PGLOBAL g)
|
|
{
|
|
if (g) {
|
|
int card = -1;
|
|
int len = GetFileLength(g);
|
|
|
|
if (len >= 0) {
|
|
if (Padded && Blksize) {
|
|
if (!(len % Blksize))
|
|
card = (len / Blksize) * Nrec;
|
|
else
|
|
sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
|
|
|
|
} else {
|
|
if (!(len % Lrecl))
|
|
card = len / (int)Lrecl; // Fixed length file
|
|
else
|
|
sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
|
|
|
|
} // endif Padded
|
|
|
|
if (trace(1))
|
|
htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
|
|
card, len, Lrecl);
|
|
|
|
} else
|
|
card = 0;
|
|
|
|
// Set number of blocks for later use
|
|
Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
|
|
return card;
|
|
} else
|
|
return 1;
|
|
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* Use BlockTest to reduce the table estimated size. */
|
|
/* Note: This function is meant only for fixed length files but is */
|
|
/* placed here to be available to FIXFAM and MPXFAM classes. */
|
|
/***********************************************************************/
|
|
int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
|
|
{
|
|
int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
|
|
int size, last = s - blm1 * Nrec;
|
|
|
|
// 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 == blm1) ? last : Nrec;
|
|
else if (rc == RC_EF)
|
|
break;
|
|
|
|
CurBlk = savcur;
|
|
return size;
|
|
} // end of MaxBlkSize
|
|
|
|
/***********************************************************************/
|
|
/* AddListValue: Used when doing indexed update or delete. */
|
|
/***********************************************************************/
|
|
bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
|
|
{
|
|
PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
|
|
|
|
switch (type) {
|
|
// case TYPE_INT:
|
|
// pp->Value = PlugSubAlloc(g, NULL, sizeof(int));
|
|
// *((int*)pp->Value) = *((int*)val);
|
|
// break;
|
|
case TYPE_VOID:
|
|
pp->Intval = *(int*)val;
|
|
break;
|
|
// case TYPE_STRING:
|
|
// pp->Value = PlugDup(g, (char*)val);
|
|
// break;
|
|
case TYPE_PCHAR:
|
|
pp->Value = val;
|
|
break;
|
|
default:
|
|
return true;
|
|
} // endswitch type
|
|
|
|
pp->Type = type;
|
|
pp->Domain = 0;
|
|
pp->Next = *top;
|
|
*top = pp;
|
|
return false;
|
|
} // end of AddListValue
|
|
|
|
/***********************************************************************/
|
|
/* Store needed values for indexed UPDATE or DELETE. */
|
|
/***********************************************************************/
|
|
int TXTFAM::StoreValues(PGLOBAL g, bool upd)
|
|
{
|
|
int pos = GetPos();
|
|
bool rc = AddListValue(g, TYPE_VOID, &pos, &To_Pos);
|
|
|
|
if (!rc) {
|
|
pos = GetNextPos();
|
|
rc = AddListValue(g, TYPE_VOID, &pos, &To_Sos);
|
|
} // endif rc
|
|
|
|
if (upd && !rc) {
|
|
char *buf;
|
|
|
|
if (Tdbp->PrepareWriting(g))
|
|
return RC_FX;
|
|
|
|
buf = PlugDup(g, Tdbp->GetLine());
|
|
rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
|
|
} // endif upd
|
|
|
|
return rc ? RC_FX : RC_OK;
|
|
} // end of StoreValues
|
|
|
|
/***********************************************************************/
|
|
/* UpdateSortedRows. When updating using indexing, the issue is that */
|
|
/* record are not necessarily updated in sequential order. */
|
|
/* Moving intermediate lines cannot be done while making them because */
|
|
/* this can cause extra wrong records to be included in the new file. */
|
|
/* What we do here is to reorder the updated records and do all the */
|
|
/* updates ordered by record position. */
|
|
/***********************************************************************/
|
|
int TXTFAM::UpdateSortedRows(PGLOBAL g)
|
|
{
|
|
int *ix, i;
|
|
|
|
/*********************************************************************/
|
|
/* Get the stored update values and sort them. */
|
|
/*********************************************************************/
|
|
if (!(Posar = MakeValueArray(g, To_Pos))) {
|
|
// strcpy(g->Message, "Position array is null");
|
|
// return RC_INFO;
|
|
return RC_OK; // Nothing to do
|
|
} else if (!(Sosar = MakeValueArray(g, To_Sos))) {
|
|
strcpy(g->Message, "Start position array is null");
|
|
goto err;
|
|
} else if (!(Updar = MakeValueArray(g, To_Upd))) {
|
|
strcpy(g->Message, "Updated line array is null");
|
|
goto err;
|
|
} else if (!(ix = (int*)Posar->GetSortIndex(g))) {
|
|
strcpy(g->Message, "Error getting array sort index");
|
|
goto err;
|
|
} // endif's
|
|
|
|
Rewind();
|
|
|
|
for (i = 0; i < Posar->GetNval(); i++) {
|
|
SetPos(g, Sosar->GetIntValue(ix[i]));
|
|
Fpos = Posar->GetIntValue(ix[i]);
|
|
strcpy(Tdbp->To_Line, Updar->GetStringValue(ix[i]));
|
|
|
|
// Now write the updated line.
|
|
if (WriteBuffer(g))
|
|
goto err;
|
|
|
|
} // endfor i
|
|
|
|
return RC_OK;
|
|
|
|
err:
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
return RC_FX;
|
|
} // end of UpdateSortedRows
|
|
|
|
/***********************************************************************/
|
|
/* DeleteSortedRows. When deleting using indexing, the issue is that */
|
|
/* record are not necessarily deleted in sequential order. Moving */
|
|
/* intermediate lines cannot be done while deleing them because */
|
|
/* this can cause extra wrong records to be included in the new file. */
|
|
/* What we do here is to reorder the deleted record and delete from */
|
|
/* the file from the ordered deleted records. */
|
|
/***********************************************************************/
|
|
int TXTFAM::DeleteSortedRows(PGLOBAL g)
|
|
{
|
|
int *ix, i, irc;
|
|
|
|
/*********************************************************************/
|
|
/* Get the stored delete values and sort them. */
|
|
/*********************************************************************/
|
|
if (!(Posar = MakeValueArray(g, To_Pos))) {
|
|
// strcpy(g->Message, "Position array is null");
|
|
// return RC_INFO;
|
|
return RC_OK; // Nothing to do
|
|
} else if (!(Sosar = MakeValueArray(g, To_Sos))) {
|
|
strcpy(g->Message, "Start position array is null");
|
|
goto err;
|
|
} else if (!(ix = (int*)Posar->GetSortIndex(g))) {
|
|
strcpy(g->Message, "Error getting array sort index");
|
|
goto err;
|
|
} // endif's
|
|
|
|
Tpos = Spos = 0;
|
|
|
|
for (i = 0; i < Posar->GetNval(); i++) {
|
|
if ((irc = InitDelete(g, Posar->GetIntValue(ix[i]),
|
|
Sosar->GetIntValue(ix[i]))) == RC_FX)
|
|
goto err;
|
|
|
|
// Now delete the sorted rows
|
|
if (DeleteRecords(g, irc))
|
|
goto err;
|
|
|
|
} // endfor i
|
|
|
|
return RC_OK;
|
|
|
|
err:
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
return RC_FX;
|
|
} // end of DeleteSortedRows
|
|
|
|
/***********************************************************************/
|
|
/* The purpose of this function is to deal with access methods that */
|
|
/* are not coherent regarding the use of SetPos and GetPos. */
|
|
/***********************************************************************/
|
|
int TXTFAM::InitDelete(PGLOBAL g, int, int)
|
|
{
|
|
strcpy(g->Message, "InitDelete should not be used by this table type");
|
|
return RC_FX;
|
|
} // end of InitDelete
|
|
|
|
/* --------------------------- Class DOSFAM -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Constructors. */
|
|
/***********************************************************************/
|
|
DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
|
|
{
|
|
To_Fbt = NULL;
|
|
Stream = NULL;
|
|
T_Stream = NULL;
|
|
UseTemp = false;
|
|
Bin = false;
|
|
} // end of DOSFAM standard constructor
|
|
|
|
DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
|
|
{
|
|
To_Fbt = tdfp->To_Fbt;
|
|
Stream = tdfp->Stream;
|
|
T_Stream = tdfp->T_Stream;
|
|
UseTemp = tdfp->UseTemp;
|
|
Bin = tdfp->Bin;
|
|
} // end of DOSFAM copy constructor
|
|
|
|
DOSFAM::DOSFAM(PBLKFAM tdfp, PDOSDEF tdp) : TXTFAM(tdp)
|
|
{
|
|
Tdbp = tdfp->Tdbp;
|
|
To_Fb = tdfp->To_Fb;
|
|
To_Fbt = tdfp->To_Fbt;
|
|
Stream = tdfp->Stream;
|
|
T_Stream = tdfp->T_Stream;
|
|
UseTemp = tdfp->UseTemp;
|
|
Bin = tdfp->Bin;
|
|
} // end of DOSFAM constructor from BLKFAM
|
|
|
|
/***********************************************************************/
|
|
/* Reset: reset position values at the beginning of file. */
|
|
/***********************************************************************/
|
|
void DOSFAM::Reset(void)
|
|
{
|
|
TXTFAM::Reset();
|
|
Bin = false;
|
|
Fpos = Tpos = Spos = 0;
|
|
} // end of Reset
|
|
|
|
/***********************************************************************/
|
|
/* DOS GetFileLength: returns file size in number of bytes. */
|
|
/***********************************************************************/
|
|
int DOSFAM::GetFileLength(PGLOBAL g)
|
|
{
|
|
int len;
|
|
|
|
if (!Stream)
|
|
len = TXTFAM::GetFileLength(g);
|
|
else
|
|
if ((len = _filelength(_fileno(Stream))) < 0)
|
|
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
|
|
|
|
if (trace(1))
|
|
htrc("File length=%d\n", len);
|
|
|
|
return len;
|
|
} // end of GetFileLength
|
|
|
|
/***********************************************************************/
|
|
/* 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 DOSFAM::Cardinality(PGLOBAL g)
|
|
{
|
|
return (g) ? -1 : 0;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* Use BlockTest to reduce the table estimated size. */
|
|
/* Note: This function is not really implemented yet. */
|
|
/***********************************************************************/
|
|
int DOSFAM::MaxBlkSize(PGLOBAL, int s)
|
|
{
|
|
return s;
|
|
} // end of MaxBlkSize
|
|
|
|
/***********************************************************************/
|
|
/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::OpenTableFile(PGLOBAL g)
|
|
{
|
|
char opmode[4], filename[_MAX_PATH];
|
|
//int ftype = Tdbp->GetFtype();
|
|
MODE mode = Tdbp->Mode;
|
|
PDBUSER dbuserp = PlgGetUser(g);
|
|
|
|
// This is required when using Unix files under Windows and vice versa
|
|
//Bin = (Blocked || Ending != CRLF);
|
|
Bin = true; // To avoid ftell problems
|
|
|
|
switch (mode) {
|
|
case MODE_READ:
|
|
strcpy(opmode, "r");
|
|
break;
|
|
case MODE_DELETE:
|
|
if (!Tdbp->Next) {
|
|
// Store the number of deleted lines
|
|
DelRows = Cardinality(g);
|
|
|
|
if (Blocked) {
|
|
// Cardinality must return 0
|
|
Block = 0;
|
|
Last = Nrec;
|
|
} // endif blocked
|
|
|
|
// This will erase the entire file
|
|
strcpy(opmode, "w");
|
|
Tdbp->ResetSize();
|
|
break;
|
|
} // endif
|
|
|
|
// Selective delete, pass thru
|
|
Bin = true;
|
|
/* fall through */
|
|
case MODE_UPDATE:
|
|
if ((UseTemp = Tdbp->IsUsingTemp(g))) {
|
|
strcpy(opmode, "r");
|
|
Bin = true;
|
|
} else
|
|
strcpy(opmode, "r+");
|
|
|
|
break;
|
|
case MODE_INSERT:
|
|
strcpy(opmode, "a+");
|
|
break;
|
|
default:
|
|
sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
|
|
return true;
|
|
} // endswitch Mode
|
|
|
|
// For blocked I/O or for moving lines, open the table in binary
|
|
strcat(opmode, (Bin) ? "b" : "t");
|
|
|
|
// Now open the file stream
|
|
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 open Stream=%p mode=%s\n", filename, Stream, opmode);
|
|
|
|
To_Fb = dbuserp->Openlist; // Keep track of File block
|
|
|
|
/*********************************************************************/
|
|
/* Allocate the line buffer. For mode Delete a bigger buffer has to */
|
|
/* be allocated because is it also used to move lines into the file.*/
|
|
/*********************************************************************/
|
|
return AllocateBuffer(g);
|
|
} // end of OpenTableFile
|
|
|
|
/***********************************************************************/
|
|
/* Allocate the line buffer. For mode Delete a bigger buffer has to */
|
|
/* be allocated because is it also used to move lines into the file. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::AllocateBuffer(PGLOBAL g)
|
|
{
|
|
MODE mode = Tdbp->Mode;
|
|
|
|
// Lrecl does not include line ending
|
|
Buflen = Lrecl + Ending + ((Bin) ? 1 : 0) + 1; // Sergei
|
|
|
|
if (trace(1))
|
|
htrc("SubAllocating a buffer of %d bytes\n", Buflen);
|
|
|
|
To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
|
|
|
|
if (UseTemp || mode == MODE_DELETE) {
|
|
// Have a big buffer to move lines
|
|
Dbflen = Buflen * DOS_BUFF_LEN;
|
|
DelBuf = PlugSubAlloc(g, NULL, Dbflen);
|
|
} else if (mode == MODE_INSERT) {
|
|
/*******************************************************************/
|
|
/* Prepare the buffer so eventual gaps are filled with blanks. */
|
|
/*******************************************************************/
|
|
memset(To_Buf, ' ', Buflen);
|
|
To_Buf[Buflen - 2] = '\n';
|
|
To_Buf[Buflen - 1] = '\0';
|
|
} // endif's mode
|
|
|
|
return false;
|
|
} // end of AllocateBuffer
|
|
|
|
/***********************************************************************/
|
|
/* GetRowID: return the RowID of last read record. */
|
|
/***********************************************************************/
|
|
int DOSFAM::GetRowID(void)
|
|
{
|
|
return Rows;
|
|
} // end of GetRowID
|
|
|
|
/***********************************************************************/
|
|
/* GetPos: return the position of last read record. */
|
|
/***********************************************************************/
|
|
int DOSFAM::GetPos(void)
|
|
{
|
|
return Fpos;
|
|
} // end of GetPos
|
|
|
|
/***********************************************************************/
|
|
/* GetNextPos: return the position of next record. */
|
|
/***********************************************************************/
|
|
int DOSFAM::GetNextPos(void)
|
|
{
|
|
return ftell(Stream);
|
|
} // end of GetNextPos
|
|
|
|
/***********************************************************************/
|
|
/* SetPos: Replace the table at the specified position. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::SetPos(PGLOBAL g, int pos)
|
|
{
|
|
Fpos = pos;
|
|
|
|
if (fseek(Stream, Fpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
|
|
return true;
|
|
} // endif
|
|
|
|
Placed = true;
|
|
return false;
|
|
} // end of SetPos
|
|
|
|
/***********************************************************************/
|
|
/* Record file position in case of UPDATE or DELETE. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::RecordPos(PGLOBAL g)
|
|
{
|
|
if ((Fpos = ftell(Stream)) < 0) {
|
|
sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
|
|
// strcat(g->Message, " (possible wrong ENDING option value)");
|
|
return true;
|
|
} // endif Fpos
|
|
|
|
return false;
|
|
} // end of RecordPos
|
|
|
|
/***********************************************************************/
|
|
/* Initialize Fpos and the current position for indexed DELETE. */
|
|
/***********************************************************************/
|
|
int DOSFAM::InitDelete(PGLOBAL g, int fpos, int spos)
|
|
{
|
|
Fpos = fpos;
|
|
|
|
if (fseek(Stream, spos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
return RC_OK;
|
|
} // end of InitDelete
|
|
|
|
/***********************************************************************/
|
|
/* Skip one record in file. */
|
|
/***********************************************************************/
|
|
int DOSFAM::SkipRecord(PGLOBAL g, bool header)
|
|
{
|
|
PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
|
|
|
|
// Skip this record
|
|
if (!fgets(To_Buf, Buflen, Stream)) {
|
|
if (feof(Stream))
|
|
return RC_EF;
|
|
|
|
#if defined(__WIN__)
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
|
|
#else
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
|
|
#endif
|
|
return RC_FX;
|
|
} // endif fgets
|
|
|
|
// Update progress information
|
|
dup->ProgCur = GetPos();
|
|
|
|
if (header) {
|
|
// For Delete
|
|
Fpos = ftell(Stream);
|
|
|
|
if (!UseTemp)
|
|
Tpos = Spos = Fpos; // No need to move header
|
|
|
|
} // endif header
|
|
|
|
#if defined(THREAD)
|
|
return RC_NF; // To have progress info
|
|
#else
|
|
return RC_OK; // To loop locally
|
|
#endif
|
|
} // end of SkipRecord
|
|
|
|
/***********************************************************************/
|
|
/* ReadBuffer: Read one line for a text file. */
|
|
/***********************************************************************/
|
|
int DOSFAM::ReadBuffer(PGLOBAL g)
|
|
{
|
|
char *p;
|
|
int rc;
|
|
|
|
if (!Stream)
|
|
return RC_EF;
|
|
|
|
if (trace(2))
|
|
htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
|
|
Tdbp, Tdbp->To_Line, Placed);
|
|
|
|
if (!Placed) {
|
|
/*******************************************************************/
|
|
/* Record file position in case of UPDATE or DELETE. */
|
|
/*******************************************************************/
|
|
next:
|
|
if (RecordPos(g))
|
|
return RC_FX;
|
|
|
|
CurBlk = (int)Rows++;
|
|
|
|
if (trace(2))
|
|
htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
|
|
|
|
/********************************************************************/
|
|
/* Check whether optimization on ROWID */
|
|
/* 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:
|
|
// Skip this record
|
|
if ((rc = SkipRecord(g, FALSE)) != RC_OK)
|
|
return rc;
|
|
|
|
goto next;
|
|
} // endswitch rc
|
|
|
|
} else
|
|
Placed = false;
|
|
|
|
if (trace(2))
|
|
htrc(" About to read: stream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
|
|
Stream, To_Buf, Buflen, Fpos);
|
|
|
|
if (fgets(To_Buf, Buflen, Stream)) {
|
|
p = To_Buf + strlen(To_Buf) - 1;
|
|
|
|
if (trace(2))
|
|
htrc(" Read: To_Buf=%p p=%c\n", To_Buf, p);
|
|
|
|
#if defined(__WIN__)
|
|
if (Bin) {
|
|
// Data file is read in binary so CRLF remains
|
|
#else
|
|
if (true) {
|
|
// Data files can be imported from Windows (having CRLF)
|
|
#endif
|
|
if (*p == '\n' || *p == '\r') {
|
|
// is this enough for Unix ???
|
|
*p = '\0'; // Eliminate ending CR or LF character
|
|
|
|
if (p > To_Buf) {
|
|
// is this enough for Unix ???
|
|
p--;
|
|
|
|
if (*p == '\n' || *p == '\r')
|
|
*p = '\0'; // Eliminate ending CR or LF character
|
|
|
|
} // endif To_Buf
|
|
|
|
} // endif p
|
|
|
|
} else if (*p == '\n')
|
|
*p = '\0'; // Eliminate ending new-line character
|
|
|
|
if (trace(2))
|
|
htrc(" To_Buf='%s'\n", To_Buf);
|
|
|
|
strcpy(Tdbp->To_Line, To_Buf);
|
|
num_read++;
|
|
rc = RC_OK;
|
|
} else if (feof(Stream)) {
|
|
rc = RC_EF;
|
|
} else {
|
|
#if defined(__WIN__)
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
|
|
#else
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
|
|
#endif
|
|
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
rc = RC_FX;
|
|
} // endif's fgets
|
|
|
|
if (trace(2))
|
|
htrc("ReadBuffer: rc=%d\n", rc);
|
|
|
|
IsRead = true;
|
|
return rc;
|
|
} // end of ReadBuffer
|
|
|
|
/***********************************************************************/
|
|
/* WriteBuffer: File write routine for DOS access method. */
|
|
/***********************************************************************/
|
|
int DOSFAM::WriteBuffer(PGLOBAL g)
|
|
{
|
|
int curpos = 0;
|
|
bool moved = true;
|
|
|
|
// T_Stream is the temporary stream or the table file stream itself
|
|
if (!T_Stream) {
|
|
if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
|
|
if (OpenTempFile(g))
|
|
return RC_FX;
|
|
|
|
} else
|
|
T_Stream = Stream;
|
|
|
|
} // endif T_Stream
|
|
|
|
if (Tdbp->Mode == MODE_UPDATE) {
|
|
/*******************************************************************/
|
|
/* Here we simply rewrite a record on itself. There are two cases */
|
|
/* were another method should be used, a/ when Update apply to */
|
|
/* the whole file, b/ when updating the last field of a variable */
|
|
/* length file. The method could be to rewrite a new file, then */
|
|
/* to erase the old one and rename the new updated file. */
|
|
/*******************************************************************/
|
|
curpos = ftell(Stream);
|
|
|
|
if (trace(1))
|
|
htrc("Last : %d cur: %d\n", Fpos, curpos);
|
|
|
|
if (UseTemp) {
|
|
/*****************************************************************/
|
|
/* We are using a temporary file. */
|
|
/* Before writing the updated record, we must eventually copy */
|
|
/* all the intermediate records that have not been updated. */
|
|
/*****************************************************************/
|
|
if (MoveIntermediateLines(g, &moved))
|
|
return RC_FX;
|
|
|
|
Spos = curpos; // New start position
|
|
} else
|
|
// Update is directly written back into the file,
|
|
// with this (fast) method, record size cannot change.
|
|
if (fseek(Stream, Fpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
} // endif mode
|
|
|
|
/*********************************************************************/
|
|
/* Prepare the write the updated line. */
|
|
/*********************************************************************/
|
|
strcat(strcpy(To_Buf, Tdbp->To_Line), (Bin) ? CrLf : "\n");
|
|
|
|
/*********************************************************************/
|
|
/* Now start the writing process. */
|
|
/*********************************************************************/
|
|
if ((fputs(To_Buf, T_Stream)) == EOF) {
|
|
sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
|
|
return RC_FX;
|
|
} // endif EOF
|
|
|
|
if (Tdbp->Mode == MODE_UPDATE && moved)
|
|
if (fseek(Stream, curpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
if (trace(1))
|
|
htrc("write done\n");
|
|
|
|
return RC_OK;
|
|
} // end of WriteBuffer
|
|
|
|
/***********************************************************************/
|
|
/* Data Base delete line routine for DOS and BLK access methods. */
|
|
/***********************************************************************/
|
|
int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
|
|
{
|
|
bool moved;
|
|
int curpos = ftell(Stream);
|
|
|
|
/*********************************************************************/
|
|
/* There is an alternative here: */
|
|
/* 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. */
|
|
/* This will be experimented. */
|
|
/*********************************************************************/
|
|
if (trace(1))
|
|
htrc(
|
|
"DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
|
|
irc, UseTemp, curpos, Fpos, Tpos, Spos);
|
|
|
|
if (irc != RC_OK) {
|
|
/*******************************************************************/
|
|
/* EOF: position Fpos at the end-of-file position. */
|
|
/*******************************************************************/
|
|
fseek(Stream, 0, SEEK_END);
|
|
Fpos = ftell(Stream);
|
|
|
|
if (trace(1))
|
|
htrc("Fpos placed at file end=%d\n", Fpos);
|
|
|
|
} // endif irc
|
|
|
|
if (Tpos == Spos) {
|
|
/*******************************************************************/
|
|
/* First line to delete, Open temporary file. */
|
|
/*******************************************************************/
|
|
if (UseTemp) {
|
|
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. */
|
|
/*****************************************************************/
|
|
T_Stream = Stream;
|
|
Spos = Tpos = Fpos;
|
|
} // endif UseTemp
|
|
|
|
} // endif Tpos == Spos
|
|
|
|
/*********************************************************************/
|
|
/* Move any intermediate lines. */
|
|
/*********************************************************************/
|
|
if (MoveIntermediateLines(g, &moved))
|
|
return RC_FX;
|
|
|
|
if (irc == RC_OK) {
|
|
/*******************************************************************/
|
|
/* Reposition the file pointer and set Spos. */
|
|
/*******************************************************************/
|
|
if (!UseTemp || moved)
|
|
if (fseek(Stream, curpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
Spos = GetNextPos(); // New start position
|
|
|
|
if (trace(1))
|
|
htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
|
|
|
|
} else {
|
|
/*******************************************************************/
|
|
/* Last call after EOF has been reached. */
|
|
/* The UseTemp case is treated in CloseTableFile. */
|
|
/*******************************************************************/
|
|
if (!UseTemp & !Abort) {
|
|
/*****************************************************************/
|
|
/* 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; // File handle, return code
|
|
|
|
PlugSetPath(filename, To_File, Tdbp->GetPath());
|
|
/*rc=*/ PlugCloseFile(g, To_Fb);
|
|
|
|
if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
|
|
return RC_FX;
|
|
|
|
/*****************************************************************/
|
|
/* Remove extra records. */
|
|
/*****************************************************************/
|
|
#if defined(__WIN__)
|
|
if (chsize(h, Tpos)) {
|
|
sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
|
|
close(h);
|
|
return RC_FX;
|
|
} // endif
|
|
#else
|
|
if (ftruncate(h, (off_t)Tpos)) {
|
|
sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
|
|
close(h);
|
|
return RC_FX;
|
|
} // endif
|
|
#endif
|
|
|
|
close(h);
|
|
|
|
if (trace(1))
|
|
htrc("done, h=%d irc=%d\n", h, irc);
|
|
|
|
} // endif !UseTemp
|
|
|
|
} // endif irc
|
|
|
|
return RC_OK; // All is correct
|
|
} // end of DeleteRecords
|
|
|
|
/***********************************************************************/
|
|
/* Open a temporary file used while updating or deleting. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::OpenTempFile(PGLOBAL g)
|
|
{
|
|
char tempname[_MAX_PATH];
|
|
bool rc = false;
|
|
|
|
/*********************************************************************/
|
|
/* Open the temporary file, Spos is at the beginning of file. */
|
|
/*********************************************************************/
|
|
PlugSetPath(tempname, To_File, Tdbp->GetPath());
|
|
strcat(PlugRemoveType(tempname, tempname), ".t");
|
|
|
|
if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
|
|
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. */
|
|
/* This works only for file open in binary mode. */
|
|
/***********************************************************************/
|
|
bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
|
|
{
|
|
int n;
|
|
size_t req, len;
|
|
|
|
for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
|
|
if (!UseTemp || !*b)
|
|
if (fseek(Stream, Spos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
|
|
return true;
|
|
} // endif
|
|
|
|
req = (size_t)MY_MIN(n, Dbflen);
|
|
len = fread(DelBuf, 1, req, Stream);
|
|
|
|
if (trace(1))
|
|
htrc("after read req=%d len=%d\n", req, len);
|
|
|
|
if (len != req) {
|
|
sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
|
|
return true;
|
|
} // endif len
|
|
|
|
if (!UseTemp)
|
|
if (fseek(T_Stream, Tpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
|
|
return true;
|
|
} // endif
|
|
|
|
if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
|
|
sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
|
|
return true;
|
|
} // endif
|
|
|
|
if (trace(1))
|
|
htrc("after write pos=%d\n", ftell(Stream));
|
|
|
|
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 file and rename the new temp file. */
|
|
/* If aborting just delete the new temp file. */
|
|
/* If indexed, make the temp file from the arrays. */
|
|
/***********************************************************************/
|
|
int DOSFAM::RenameTempFile(PGLOBAL g)
|
|
{
|
|
char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
|
|
int rc = RC_OK;
|
|
|
|
if (To_Fbt)
|
|
tempname = (char*)To_Fbt->Fname;
|
|
else
|
|
return RC_INFO; // Nothing to do ???
|
|
|
|
// This loop is necessary because, in case of join,
|
|
// To_File can have been open several times.
|
|
for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
|
|
if (fb == To_Fb || (fb == To_Fbt))
|
|
rc = PlugCloseFile(g, fb);
|
|
|
|
if (!Abort) {
|
|
PlugSetPath(filename, To_File, Tdbp->GetPath());
|
|
strcat(PlugRemoveType(filetemp, filename), ".ttt");
|
|
remove(filetemp); // May still be there from previous error
|
|
|
|
if (rename(filename, filetemp)) { // Save file for security
|
|
snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
|
|
filename, filetemp, strerror(errno));
|
|
throw 51;
|
|
} else if (rename(tempname, filename)) {
|
|
snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
|
|
tempname, filename, strerror(errno));
|
|
rc = rename(filetemp, filename); // Restore saved file
|
|
throw 52;
|
|
} else if (remove(filetemp)) {
|
|
sprintf(g->Message, MSG(REMOVE_ERROR),
|
|
filetemp, strerror(errno));
|
|
rc = RC_INFO; // Acceptable
|
|
} // endif's
|
|
|
|
} else
|
|
remove(tempname);
|
|
|
|
return rc;
|
|
} // end of RenameTempFile
|
|
|
|
/***********************************************************************/
|
|
/* Table file close routine for DOS access method. */
|
|
/***********************************************************************/
|
|
void DOSFAM::CloseTableFile(PGLOBAL g, bool abort)
|
|
{
|
|
int rc;
|
|
|
|
Abort = abort;
|
|
|
|
if (UseTemp && T_Stream) {
|
|
if (Tdbp->Mode == MODE_UPDATE && !Abort) {
|
|
// Copy eventually remaining lines
|
|
bool b;
|
|
|
|
fseek(Stream, 0, SEEK_END);
|
|
Fpos = ftell(Stream);
|
|
Abort = MoveIntermediateLines(g, &b) != RC_OK;
|
|
} // endif Abort
|
|
|
|
// Delete the old file and rename the new temp file.
|
|
rc = RenameTempFile(g); // Also close all files
|
|
} else {
|
|
rc = PlugCloseFile(g, To_Fb);
|
|
|
|
if (trace(1))
|
|
htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
|
|
|
|
} // endif UseTemp
|
|
|
|
Stream = NULL; // So we can know whether table is open
|
|
T_Stream = NULL;
|
|
} // end of CloseTableFile
|
|
|
|
/***********************************************************************/
|
|
/* Rewind routine for DOS access method. */
|
|
/***********************************************************************/
|
|
void DOSFAM::Rewind(void)
|
|
{
|
|
if (Stream) // Can be NULL when making index on void table
|
|
rewind(Stream);
|
|
|
|
Rows = 0;
|
|
OldBlk = CurBlk = -1;
|
|
} // end of Rewind
|
|
|
|
/* --------------------------- Class BLKFAM -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Constructors. */
|
|
/***********************************************************************/
|
|
BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
|
|
{
|
|
Blocked = true;
|
|
Block = tdp->GetBlock();
|
|
Last = tdp->GetLast();
|
|
Nrec = tdp->GetElemt();
|
|
Closing = false;
|
|
BlkPos = tdp->GetTo_Pos();
|
|
CurLine = NULL;
|
|
NxtLine = NULL;
|
|
OutBuf = NULL;
|
|
} // end of BLKFAM standard constructor
|
|
|
|
BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
|
|
{
|
|
Closing = txfp->Closing;
|
|
CurLine = txfp->CurLine;
|
|
NxtLine = txfp->NxtLine;
|
|
OutBuf = txfp->OutBuf;
|
|
} // end of BLKFAM copy constructor
|
|
|
|
/***********************************************************************/
|
|
/* Reset: reset position values at the beginning of file. */
|
|
/***********************************************************************/
|
|
void BLKFAM::Reset(void)
|
|
{
|
|
DOSFAM::Reset();
|
|
Closing = false;
|
|
} // end of Reset
|
|
|
|
/***********************************************************************/
|
|
/* 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 BLKFAM::Cardinality(PGLOBAL g)
|
|
{
|
|
return (g) ? ((Block > 0) ? (int)((Block - 1) * Nrec + Last) : 0) : 1;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* Use BlockTest to reduce the table estimated size. */
|
|
/***********************************************************************/
|
|
int BLKFAM::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
|
|
|
|
/***********************************************************************/
|
|
/* Allocate the line buffer. For mode Delete or when a temp file is */
|
|
/* used another big buffer has to be allocated because is it used */
|
|
/* to move or update the lines into the (temp) file. */
|
|
/***********************************************************************/
|
|
bool BLKFAM::AllocateBuffer(PGLOBAL g)
|
|
{
|
|
int len;
|
|
MODE mode = Tdbp->GetMode();
|
|
|
|
// For variable length files, Lrecl does not include CRLF
|
|
len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
|
|
Buflen = len * Nrec;
|
|
CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
|
|
|
|
if (UseTemp || mode == MODE_DELETE) {
|
|
if (mode == MODE_UPDATE)
|
|
OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
|
|
|
|
Dbflen = Buflen;
|
|
DelBuf = PlugSubAlloc(g, NULL, Dbflen);
|
|
} else if (mode == MODE_INSERT)
|
|
Rbuf = Nrec; // To be used by WriteDB
|
|
|
|
return false;
|
|
} // end of AllocateBuffer
|
|
|
|
/***********************************************************************/
|
|
/* GetRowID: return the RowID of last read record. */
|
|
/***********************************************************************/
|
|
int BLKFAM::GetRowID(void)
|
|
{
|
|
return CurNum + Nrec * CurBlk + 1;
|
|
} // end of GetRowID
|
|
|
|
/***********************************************************************/
|
|
/* GetPos: return the position of last read record. */
|
|
/***********************************************************************/
|
|
int BLKFAM::GetPos(void)
|
|
{
|
|
return (CurNum + Nrec * CurBlk); // Computed file index
|
|
} // end of GetPos
|
|
|
|
/***********************************************************************/
|
|
/* GetNextPos: called by DeleteRecords. */
|
|
/***********************************************************************/
|
|
int BLKFAM::GetNextPos(void)
|
|
{
|
|
return (int)(Fpos + NxtLine - CurLine);
|
|
} // end of GetNextPos
|
|
|
|
/***********************************************************************/
|
|
/* SetPos: Replace the table at the specified position. */
|
|
/***********************************************************************/
|
|
bool BLKFAM::SetPos(PGLOBAL g, int)
|
|
{
|
|
strcpy(g->Message, "Blocked variable tables cannot be used indexed");
|
|
return true;
|
|
} // end of SetPos
|
|
|
|
/***********************************************************************/
|
|
/* Record file position in case of UPDATE or DELETE. */
|
|
/* Not used yet for blocked tables. */
|
|
/***********************************************************************/
|
|
bool BLKFAM::RecordPos(PGLOBAL)
|
|
{
|
|
Fpos = (CurNum + Nrec * CurBlk); // Computed file index
|
|
return false;
|
|
} // end of RecordPos
|
|
|
|
/***********************************************************************/
|
|
/* Skip one record in file. */
|
|
/***********************************************************************/
|
|
int BLKFAM::SkipRecord(PGLOBAL, bool header)
|
|
{
|
|
if (header) {
|
|
// For Delete
|
|
Fpos = BlkPos[0]; // First block starts after the header
|
|
|
|
if (!UseTemp)
|
|
Tpos = Spos = Fpos; // No need to move header
|
|
|
|
} // endif header
|
|
|
|
OldBlk = -2; // To force fseek on first block
|
|
return RC_OK;
|
|
} // end of SkipRecord
|
|
|
|
/***********************************************************************/
|
|
/* ReadBuffer: Read one line for a text file. */
|
|
/***********************************************************************/
|
|
int BLKFAM::ReadBuffer(PGLOBAL g)
|
|
{
|
|
int i, rc = RC_OK;
|
|
size_t n;
|
|
|
|
/*********************************************************************/
|
|
/* Sequential reading when Placed is not true. */
|
|
/*********************************************************************/
|
|
if (Placed) {
|
|
Placed = false;
|
|
} else if (++CurNum < Rbuf) {
|
|
CurLine = NxtLine;
|
|
|
|
// Get the position of the next line in the buffer
|
|
while (*NxtLine++ != '\n') ;
|
|
|
|
// Set caller line buffer
|
|
n = NxtLine - CurLine - Ending;
|
|
memcpy(Tdbp->GetLine(), CurLine, n);
|
|
Tdbp->GetLine()[n] = '\0';
|
|
goto fin;
|
|
} else if (Rbuf < Nrec && CurBlk != -1) {
|
|
return RC_EF;
|
|
} else {
|
|
/*******************************************************************/
|
|
/* New block. */
|
|
/*******************************************************************/
|
|
CurNum = 0;
|
|
|
|
next:
|
|
if (++CurBlk >= Block)
|
|
return RC_EF;
|
|
|
|
/*******************************************************************/
|
|
/* Before reading a new block, check whether block optimization */
|
|
/* 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
|
|
|
|
} // endif's
|
|
|
|
if (OldBlk == CurBlk)
|
|
goto ok; // Block is already there
|
|
|
|
// fseek is required only in non sequential reading
|
|
if (CurBlk != OldBlk + 1)
|
|
if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
|
|
return RC_FX;
|
|
} // endif fseek
|
|
|
|
// Calculate the length of block to read
|
|
BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
|
|
|
|
if (trace(1))
|
|
htrc("File position is now %d\n", ftell(Stream));
|
|
|
|
// Read the entire next block
|
|
n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
|
|
|
|
if ((size_t) n == (size_t) BlkLen) {
|
|
// ReadBlks++;
|
|
num_read++;
|
|
Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
|
|
|
|
ok:
|
|
rc = RC_OK;
|
|
|
|
// Get the position of the current line
|
|
for (i = 0, CurLine = To_Buf; i < CurNum; i++)
|
|
while (*CurLine++ != '\n') ; // What about Unix ???
|
|
|
|
// Now get the position of the next line
|
|
for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
|
|
|
|
// Set caller line buffer
|
|
n = NxtLine - CurLine - Ending;
|
|
memcpy(Tdbp->GetLine(), CurLine, n);
|
|
Tdbp->GetLine()[n] = '\0';
|
|
} else if (feof(Stream)) {
|
|
rc = RC_EF;
|
|
} else {
|
|
#if defined(__WIN__)
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
|
|
#else
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
|
|
#endif
|
|
|
|
if (trace(1))
|
|
htrc("%s\n", g->Message);
|
|
|
|
return RC_FX;
|
|
} // endelse
|
|
|
|
OldBlk = CurBlk; // Last block actually read
|
|
IsRead = true; // Is read indeed
|
|
|
|
fin:
|
|
// Store the current record file position for Delete and Update
|
|
Fpos = (int)(BlkPos[CurBlk] + CurLine - To_Buf);
|
|
return rc;
|
|
} // end of ReadBuffer
|
|
|
|
/***********************************************************************/
|
|
/* WriteBuffer: File write routine for the blocked DOS access method. */
|
|
/* Update is directly written back into the file, */
|
|
/* with this (fast) method, record size cannot change. */
|
|
/***********************************************************************/
|
|
int BLKFAM::WriteBuffer(PGLOBAL g)
|
|
{
|
|
if (Tdbp->GetMode() == MODE_INSERT) {
|
|
/*******************************************************************/
|
|
/* In Insert mode, blocks are added sequentially to the file end. */
|
|
/*******************************************************************/
|
|
if (!Closing) { // Add line to the write buffer
|
|
strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
|
|
|
|
if (++CurNum != Rbuf) {
|
|
CurLine += strlen(CurLine);
|
|
return RC_OK; // We write only full blocks
|
|
} // endif CurNum
|
|
|
|
} // endif Closing
|
|
|
|
// Now start the writing process.
|
|
NxtLine = CurLine + strlen(CurLine);
|
|
BlkLen = (int)(NxtLine - To_Buf);
|
|
|
|
if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
|
|
sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
|
|
Closing = true; // To tell CloseDB about a Write error
|
|
return RC_FX;
|
|
} // endif size
|
|
|
|
CurBlk++;
|
|
CurNum = 0;
|
|
CurLine = To_Buf;
|
|
} else {
|
|
/*******************************************************************/
|
|
/* Mode == MODE_UPDATE. */
|
|
/*******************************************************************/
|
|
const char *crlf;
|
|
size_t len;
|
|
int curpos = ftell(Stream);
|
|
bool moved = true;
|
|
|
|
// T_Stream is the temporary stream or the table file stream itself
|
|
if (!T_Stream)
|
|
{
|
|
if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
|
|
if (OpenTempFile(g))
|
|
return RC_FX;
|
|
|
|
} else
|
|
T_Stream = Stream;
|
|
}
|
|
if (UseTemp) {
|
|
/*****************************************************************/
|
|
/* We are using a temporary file. Before writing the updated */
|
|
/* record, we must eventually copy all the intermediate records */
|
|
/* that have not been updated. */
|
|
/*****************************************************************/
|
|
if (MoveIntermediateLines(g, &moved))
|
|
return RC_FX;
|
|
|
|
Spos = GetNextPos(); // New start position
|
|
|
|
// Prepare the output buffer
|
|
#if defined(__WIN__)
|
|
crlf = "\r\n";
|
|
#else
|
|
crlf = "\n";
|
|
#endif // __WIN__
|
|
strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
|
|
len = strlen(OutBuf);
|
|
} else {
|
|
if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
|
|
return RC_FX;
|
|
} // endif fseek
|
|
|
|
// Replace the line inside read buffer (length has not changed)
|
|
memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
|
|
OutBuf = CurLine;
|
|
len = (size_t)(NxtLine - CurLine);
|
|
} // endif UseTemp
|
|
|
|
if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
|
|
sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
|
|
return RC_FX;
|
|
} // endif fwrite
|
|
|
|
if (moved)
|
|
if (fseek(Stream, curpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
} // endif Mode
|
|
|
|
return RC_OK;
|
|
} // end of WriteBuffer
|
|
|
|
/***********************************************************************/
|
|
/* Table file close routine for DOS access method. */
|
|
/***********************************************************************/
|
|
void BLKFAM::CloseTableFile(PGLOBAL g, bool abort)
|
|
{
|
|
int rc, wrc = RC_OK;
|
|
|
|
Abort = abort;
|
|
|
|
if (UseTemp && T_Stream) {
|
|
if (Tdbp->GetMode() == MODE_UPDATE && !Abort) {
|
|
// Copy eventually remaining lines
|
|
bool b;
|
|
|
|
fseek(Stream, 0, SEEK_END);
|
|
Fpos = ftell(Stream);
|
|
Abort = MoveIntermediateLines(g, &b) != RC_OK;
|
|
} // endif Abort
|
|
|
|
// Delete the old file and rename the new temp file.
|
|
rc = RenameTempFile(g); // Also close all files
|
|
} else {
|
|
// Closing is True if last Write was in error
|
|
if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
|
|
// Some more inserted lines remain to be written
|
|
Rbuf = CurNum--;
|
|
Closing = true;
|
|
wrc = WriteBuffer(g);
|
|
} else if (Modif && !Closing) {
|
|
// Last updated block remains to be written
|
|
Closing = true;
|
|
wrc = ReadBuffer(g);
|
|
} // endif's
|
|
|
|
rc = PlugCloseFile(g, To_Fb);
|
|
|
|
if (trace(1))
|
|
htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
|
|
To_File, Tdbp->GetMode(), wrc, rc);
|
|
|
|
} // endif UseTemp
|
|
|
|
Stream = NULL; // So we can know whether table is open
|
|
} // end of CloseTableFile
|
|
|
|
/***********************************************************************/
|
|
/* Rewind routine for DOS access method. */
|
|
/* Note: commenting out OldBlk = -1 has two advantages: */
|
|
/* 1 - It forces fseek on first block, thus suppressing the need to */
|
|
/* rewind the file, anyway unuseful when second pass if indexed. */
|
|
/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
|
|
/***********************************************************************/
|
|
void BLKFAM::Rewind(void)
|
|
{
|
|
//rewind(Stream); will be placed by fseek
|
|
CurBlk = -1;
|
|
CurNum = Rbuf;
|
|
//OldBlk = -1; commented out in case we reuse last read block
|
|
//Rbuf = 0; commented out in case we reuse last read block
|
|
} // end of Rewind
|
|
|
|
/* --------------------------- Class BINFAM -------------------------- */
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* BIN GetFileLength: returns file size in number of bytes. */
|
|
/***********************************************************************/
|
|
int BINFAM::GetFileLength(PGLOBAL g)
|
|
{
|
|
int len;
|
|
|
|
if (!Stream)
|
|
len = TXTFAM::GetFileLength(g);
|
|
else
|
|
if ((len = _filelength(_fileno(Stream))) < 0)
|
|
sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
|
|
|
|
xtrc(1, "File length=%d\n", len);
|
|
return len;
|
|
} // end of GetFileLength
|
|
|
|
/***********************************************************************/
|
|
/* 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 BINFAM::Cardinality(PGLOBAL g)
|
|
{
|
|
return (g) ? -1 : 0;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
|
|
/***********************************************************************/
|
|
bool BINFAM::OpenTableFile(PGLOBAL g) {
|
|
char opmode[4], filename[_MAX_PATH];
|
|
MODE mode = Tdbp->GetMode();
|
|
PDBUSER dbuserp = PlgGetUser(g);
|
|
|
|
switch (mode) {
|
|
case MODE_READ:
|
|
strcpy(opmode, "rb");
|
|
break;
|
|
case MODE_WRITE:
|
|
strcpy(opmode, "wb");
|
|
break;
|
|
default:
|
|
sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
|
|
return true;
|
|
} // endswitch Mode
|
|
|
|
// Now open the file stream
|
|
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 open Stream=%p mode=%s\n", filename, Stream, opmode);
|
|
|
|
To_Fb = dbuserp->Openlist; // Keep track of File block
|
|
|
|
/*********************************************************************/
|
|
/* Allocate the line buffer. */
|
|
/*********************************************************************/
|
|
return AllocateBuffer(g);
|
|
} // end of OpenTableFile
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* Allocate the line buffer. For mode Delete a bigger buffer has to */
|
|
/* be allocated because is it also used to move lines into the file. */
|
|
/***********************************************************************/
|
|
bool BINFAM::AllocateBuffer(PGLOBAL g)
|
|
{
|
|
MODE mode = Tdbp->GetMode();
|
|
|
|
// Lrecl is Ok
|
|
Buflen = Lrecl;
|
|
|
|
// Buffer will be allocated separately
|
|
if (mode == MODE_ANY) {
|
|
xtrc(1, "SubAllocating a buffer of %d bytes\n", Buflen);
|
|
To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
|
|
} else if (UseTemp || mode == MODE_DELETE) {
|
|
// Have a big buffer to move lines
|
|
Dbflen = Buflen * DOS_BUFF_LEN;
|
|
DelBuf = PlugSubAlloc(g, NULL, Dbflen);
|
|
} // endif mode
|
|
|
|
return false;
|
|
#if 0
|
|
MODE mode = Tdbp->GetMode();
|
|
|
|
// Lrecl is Ok
|
|
Dbflen = Buflen = Lrecl;
|
|
|
|
if (trace(1))
|
|
htrc("SubAllocating a buffer of %d bytes\n", Buflen);
|
|
|
|
DelBuf = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
|
|
return false;
|
|
#endif // 0
|
|
} // end of AllocateBuffer
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* GetRowID: return the RowID of last read record. */
|
|
/***********************************************************************/
|
|
int BINFAM::GetRowID(void) {
|
|
return Rows;
|
|
} // end of GetRowID
|
|
|
|
/***********************************************************************/
|
|
/* GetPos: return the position of last read record. */
|
|
/***********************************************************************/
|
|
int BINFAM::GetPos(void) {
|
|
return Fpos;
|
|
} // end of GetPos
|
|
|
|
/***********************************************************************/
|
|
/* GetNextPos: return the position of next record. */
|
|
/***********************************************************************/
|
|
int BINFAM::GetNextPos(void) {
|
|
return ftell(Stream);
|
|
} // end of GetNextPos
|
|
|
|
/***********************************************************************/
|
|
/* SetPos: Replace the table at the specified position. */
|
|
/***********************************************************************/
|
|
bool BINFAM::SetPos(PGLOBAL g, int pos) {
|
|
Fpos = pos;
|
|
|
|
if (fseek(Stream, Fpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
|
|
return true;
|
|
} // endif
|
|
|
|
Placed = true;
|
|
return false;
|
|
} // end of SetPos
|
|
|
|
/***********************************************************************/
|
|
/* Record file position in case of UPDATE or DELETE. */
|
|
/***********************************************************************/
|
|
bool BINFAM::RecordPos(PGLOBAL g) {
|
|
if ((Fpos = ftell(Stream)) < 0) {
|
|
sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
|
|
// strcat(g->Message, " (possible wrong ENDING option value)");
|
|
return true;
|
|
} // endif Fpos
|
|
|
|
return false;
|
|
} // end of RecordPos
|
|
#endif // 0
|
|
|
|
/***********************************************************************/
|
|
/* ReadBuffer: Read one line for a text file. */
|
|
/***********************************************************************/
|
|
int BINFAM::ReadBuffer(PGLOBAL g)
|
|
{
|
|
int rc;
|
|
|
|
if (!Stream)
|
|
return RC_EF;
|
|
|
|
xtrc(2, "ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
|
|
Tdbp, Tdbp->GetLine(), Placed);
|
|
|
|
if (!Placed) {
|
|
/*******************************************************************/
|
|
/* Record file position in case of UPDATE or DELETE. */
|
|
/*******************************************************************/
|
|
if (RecordPos(g))
|
|
return RC_FX;
|
|
|
|
CurBlk = (int)Rows++;
|
|
xtrc(2, "ReadBuffer: CurBlk=%d\n", CurBlk);
|
|
} else
|
|
Placed = false;
|
|
|
|
xtrc(2, " About to read: bstream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
|
|
Stream, To_Buf, Buflen, Fpos);
|
|
|
|
// Read the prefix giving the row length
|
|
if (!fread(&Recsize, sizeof(size_t), 1, Stream)) {
|
|
if (!feof(Stream)) {
|
|
strcpy(g->Message, "Error reading line prefix\n");
|
|
return RC_FX;
|
|
} else
|
|
return RC_EF;
|
|
|
|
} else if (Recsize > (unsigned)Buflen) {
|
|
sprintf(g->Message, "Record too big (Recsize=%zd Buflen=%d)\n", Recsize, Buflen);
|
|
return RC_FX;
|
|
} // endif Recsize
|
|
|
|
if (fread(To_Buf, Recsize, 1, Stream)) {
|
|
xtrc(2, " Read: To_Buf=%p Recsize=%zd\n", To_Buf, Recsize);
|
|
num_read++;
|
|
rc = RC_OK;
|
|
} else if (feof(Stream)) {
|
|
rc = RC_EF;
|
|
} else {
|
|
#if defined(__WIN__)
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
|
|
#else
|
|
sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
|
|
#endif
|
|
xtrc(2, "%s\n", g->Message);
|
|
rc = RC_FX;
|
|
} // endif's fread
|
|
|
|
xtrc(2, "ReadBuffer: rc=%d\n", rc);
|
|
IsRead = true;
|
|
return rc;
|
|
} // end of ReadBuffer
|
|
|
|
/***********************************************************************/
|
|
/* WriteBuffer: File write routine for BIN access method. */
|
|
/***********************************************************************/
|
|
int BINFAM::WriteBuffer(PGLOBAL g)
|
|
{
|
|
int curpos = 0;
|
|
bool moved = true;
|
|
|
|
// T_Stream is the temporary stream or the table file stream itself
|
|
if (!T_Stream) {
|
|
if (UseTemp && Tdbp->GetMode() == MODE_UPDATE) {
|
|
if (OpenTempFile(g))
|
|
return RC_FX;
|
|
|
|
} else
|
|
T_Stream = Stream;
|
|
|
|
} // endif T_Stream
|
|
|
|
if (Tdbp->GetMode() == MODE_UPDATE) {
|
|
/*******************************************************************/
|
|
/* Here we simply rewrite a record on itself. There are two cases */
|
|
/* were another method should be used, a/ when Update apply to */
|
|
/* the whole file, b/ when updating the last field of a variable */
|
|
/* length file. The method could be to rewrite a new file, then */
|
|
/* to erase the old one and rename the new updated file. */
|
|
/*******************************************************************/
|
|
curpos = ftell(Stream);
|
|
|
|
if (trace(1))
|
|
htrc("Last : %d cur: %d\n", Fpos, curpos);
|
|
|
|
if (UseTemp) {
|
|
/*****************************************************************/
|
|
/* We are using a temporary file. */
|
|
/* Before writing the updated record, we must eventually copy */
|
|
/* all the intermediate records that have not been updated. */
|
|
/*****************************************************************/
|
|
if (MoveIntermediateLines(g, &moved))
|
|
return RC_FX;
|
|
|
|
Spos = curpos; // New start position
|
|
} else
|
|
// Update is directly written back into the file,
|
|
// with this (fast) method, record size cannot change.
|
|
if (fseek(Stream, Fpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
} // endif mode
|
|
|
|
/*********************************************************************/
|
|
/* Prepare writing the line. */
|
|
/*********************************************************************/
|
|
//memcpy(To_Buf, Tdbp->GetLine(), Recsize);
|
|
|
|
/*********************************************************************/
|
|
/* Now start the writing process. */
|
|
/*********************************************************************/
|
|
if (fwrite(&Recsize, sizeof(size_t), 1, T_Stream) != 1) {
|
|
sprintf(g->Message, "Error %d writing prefix to %s",
|
|
errno, To_File);
|
|
return RC_FX;
|
|
} else if (fwrite(To_Buf, Recsize, 1, T_Stream) != 1) {
|
|
sprintf(g->Message, "Error %d writing %zd bytes to %s",
|
|
errno, Recsize, To_File);
|
|
return RC_FX;
|
|
} // endif fwrite
|
|
|
|
if (Tdbp->GetMode() == MODE_UPDATE && moved)
|
|
if (fseek(Stream, curpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
xtrc(1, "Binary write done\n");
|
|
return RC_OK;
|
|
} // end of WriteBuffer
|
|
|
|
#if 0
|
|
/***********************************************************************/
|
|
/* Data Base delete line routine for DOS and BLK access methods. */
|
|
/***********************************************************************/
|
|
int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
|
|
{
|
|
bool moved;
|
|
int curpos = ftell(Stream);
|
|
|
|
/*********************************************************************/
|
|
/* There is an alternative here: */
|
|
/* 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. */
|
|
/* This will be experimented. */
|
|
/*********************************************************************/
|
|
if (trace(1))
|
|
htrc(
|
|
"DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
|
|
irc, UseTemp, curpos, Fpos, Tpos, Spos);
|
|
|
|
if (irc != RC_OK) {
|
|
/*******************************************************************/
|
|
/* EOF: position Fpos at the end-of-file position. */
|
|
/*******************************************************************/
|
|
fseek(Stream, 0, SEEK_END);
|
|
Fpos = ftell(Stream);
|
|
|
|
if (trace(1))
|
|
htrc("Fpos placed at file end=%d\n", Fpos);
|
|
|
|
} // endif irc
|
|
|
|
if (Tpos == Spos) {
|
|
/*******************************************************************/
|
|
/* First line to delete, Open temporary file. */
|
|
/*******************************************************************/
|
|
if (UseTemp) {
|
|
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. */
|
|
/*****************************************************************/
|
|
T_Stream = Stream;
|
|
Spos = Tpos = Fpos;
|
|
} // endif UseTemp
|
|
|
|
} // endif Tpos == Spos
|
|
|
|
/*********************************************************************/
|
|
/* Move any intermediate lines. */
|
|
/*********************************************************************/
|
|
if (MoveIntermediateLines(g, &moved))
|
|
return RC_FX;
|
|
|
|
if (irc == RC_OK) {
|
|
/*******************************************************************/
|
|
/* Reposition the file pointer and set Spos. */
|
|
/*******************************************************************/
|
|
if (!UseTemp || moved)
|
|
if (fseek(Stream, curpos, SEEK_SET)) {
|
|
sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
|
|
return RC_FX;
|
|
} // endif
|
|
|
|
Spos = GetNextPos(); // New start position
|
|
|
|
if (trace(1))
|
|
htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
|
|
|
|
} else {
|
|
/*******************************************************************/
|
|
/* Last call after EOF has been reached. */
|
|
/* The UseTemp case is treated in CloseTableFile. */
|
|
/*******************************************************************/
|
|
if (!UseTemp & !Abort) {
|
|
/*****************************************************************/
|
|
/* 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; // File handle, return code
|
|
|
|
PlugSetPath(filename, To_File, Tdbp->GetPath());
|
|
/*rc=*/ PlugCloseFile(g, To_Fb);
|
|
|
|
if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
|
|
return RC_FX;
|
|
|
|
/*****************************************************************/
|
|
/* Remove extra records. */
|
|
/*****************************************************************/
|
|
#if defined(__WIN__)
|
|
if (chsize(h, Tpos)) {
|
|
sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
|
|
close(h);
|
|
return RC_FX;
|
|
} // endif
|
|
#else
|
|
if (ftruncate(h, (off_t)Tpos)) {
|
|
sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
|
|
close(h);
|
|
return RC_FX;
|
|
} // endif
|
|
#endif
|
|
|
|
close(h);
|
|
|
|
if (trace(1))
|
|
htrc("done, h=%d irc=%d\n", h, irc);
|
|
|
|
} // endif !UseTemp
|
|
|
|
} // endif irc
|
|
|
|
return RC_OK; // All is correct
|
|
} // end of DeleteRecords
|
|
|
|
/***********************************************************************/
|
|
/* Table file close routine for DOS access method. */
|
|
/***********************************************************************/
|
|
void BINFAM::CloseTableFile(PGLOBAL g, bool abort)
|
|
{
|
|
int rc;
|
|
|
|
Abort = abort;
|
|
rc = PlugCloseFile(g, To_Fb);
|
|
xtrc(1, "BIN Close: closing %s rc=%d\n", To_File, rc);
|
|
Stream = NULL; // So we can know whether table is open
|
|
} // end of CloseTableFile
|
|
|
|
/***********************************************************************/
|
|
/* Rewind routine for BIN access method. */
|
|
/***********************************************************************/
|
|
void BINFAM::Rewind(void)
|
|
{
|
|
if (Stream) // Can be NULL when making index on void table
|
|
rewind(Stream);
|
|
|
|
Rows = 0;
|
|
OldBlk = CurBlk = -1;
|
|
} // end of Rewind
|
|
#endif // 0
|