mariadb/storage/connect/filamgz.cpp
Mikhail Chalov 19af1890b5 Use memory safe snprintf() in Connect Engine
This commit replaces sprintf(buf, ...) with
snprintf(buf, sizeof(buf), ...),
specifically in the "easy" cases where buf is allocated with a size
known at compile time.

The changes make sure we are not write outside array/string bounds which
will lead to undefined behaviour. In case the code is trying to write
outside bounds - safe version of functions simply cut the string
messages so we process this gracefully.

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.

bsonudf.cpp warnings cleanup by Daniel Black

Reviewer: Daniel Black
2022-07-26 16:28:59 +10:00

1426 lines
47 KiB
C++

/************ File AM GZ C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMGZ */
/* ------------- */
/* Version 1.5 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 2005-2016 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* This program are the ZLIB compressed files classes. */
/* */
/***********************************************************************/
/***********************************************************************/
/* 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
//#include <windows.h>
#else // !_WIN32
#if defined(UNIX)
#include <errno.h>
#else // !UNIX
#include <io.h>
#endif
#include <fcntl.h>
#endif // !_WIN32
/***********************************************************************/
/* 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 "plgdbsem.h"
//#include "catalog.h"
//#include "reldef.h"
//#include "xobject.h"
//#include "kindex.h"
#include "filamtxt.h"
#include "tabdos.h"
#if defined(UNIX)
#include "osutil.h"
#endif
/***********************************************************************/
/* This define prepares ZLIB function declarations. */
/***********************************************************************/
//#define ZLIB_DLL
#include "filamgz.h"
/***********************************************************************/
/* DB static variables. */
/***********************************************************************/
extern int num_read, num_there, num_eq[]; // Statistics
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Implementation of the GZFAM class. */
/***********************************************************************/
GZFAM::GZFAM(PGZFAM txfp) : TXTFAM(txfp)
{
Zfile = txfp->Zfile;
Zpos = txfp->Zpos;
} // end of GZFAM copy constructor
/***********************************************************************/
/* Zerror: Error function for gz calls. */
/* gzerror returns the error message for the last error which occurred*/
/* on the given compressed file. errnum is set to zlib error number. */
/* If an error occurred in the file system and not in the compression */
/* library, errnum is set to Z_ERRNO and the application may consult */
/* errno to get the exact error code. */
/***********************************************************************/
int GZFAM::Zerror(PGLOBAL g)
{
int errnum;
strcpy(g->Message, gzerror(Zfile, &errnum));
if (errnum == Z_ERRNO)
#if defined(_WIN32)
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(NULL));
#else // !_WIN32
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(errno));
#endif // !_WIN32
return (errnum == Z_STREAM_END) ? RC_EF : RC_FX;
} // end of Zerror
/***********************************************************************/
/* Reset: reset position values at the beginning of file. */
/***********************************************************************/
void GZFAM::Reset(void)
{
TXTFAM::Reset();
//gzrewind(Zfile); // Useful ?????
Zpos = 0;
} // end of Reset
/***********************************************************************/
/* GZ GetFileLength: returns an estimate of what would be the */
/* uncompressed file size in number of bytes. */
/***********************************************************************/
int GZFAM::GetFileLength(PGLOBAL g)
{
int len = TXTFAM::GetFileLength(g);
if (len > 0)
// Estimate size reduction to a max of 6
len *= 6;
return len;
} // end of GetFileLength
/***********************************************************************/
/* GZ Access Method opening routine. */
/***********************************************************************/
bool GZFAM::OpenTableFile(PGLOBAL g)
{
char opmode[4], filename[_MAX_PATH];
MODE mode = Tdbp->GetMode();
switch (mode) {
case MODE_READ:
strcpy(opmode, "r");
break;
case MODE_UPDATE:
/*****************************************************************/
/* Updating GZ files not implemented yet. */
/*****************************************************************/
strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP));
return true;
case MODE_DELETE:
if (!Tdbp->GetNext()) {
// Store the number of deleted lines
DelRows = Cardinality(g);
// This will erase the entire file
strcpy(opmode, "w");
// Block = 0; // For ZBKFAM
// Last = Nrec; // For ZBKFAM
Tdbp->ResetSize();
} else {
snprintf(g->Message, sizeof(g->Message), MSG(NO_PART_DEL), "GZ");
return true;
} // endif filter
break;
case MODE_INSERT:
strcpy(opmode, "a+");
break;
default:
snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
return true;
} // endswitch Mode
/*********************************************************************/
/* Open according to logical input/output mode required. */
/* Use specific zlib functions. */
/* Treat files as binary. */
/*********************************************************************/
strcat(opmode, "b");
Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode);
if (Zfile == NULL) {
snprintf(g->Message, sizeof(g->Message), MSG(GZOPEN_ERROR),
opmode, (int)errno, filename);
strcat(strcat(g->Message, ": "), strerror(errno));
return (mode == MODE_READ && errno == ENOENT)
? PushWarning(g, Tdbp) : true;
} // endif Zfile
/*********************************************************************/
/* Something to be done here. >>>>>>>> NOT DONE <<<<<<<< */
/*********************************************************************/
//To_Fb = dbuserp->Openlist; // Keep track of File block
/*********************************************************************/
/* Allocate the line buffer. */
/*********************************************************************/
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 GZFAM::AllocateBuffer(PGLOBAL g)
{
MODE mode = Tdbp->GetMode();
Buflen = Lrecl + 2; // Lrecl does not include CRLF
//Buflen *= ((Mode == MODE_DELETE) ? DOS_BUFF_LEN : 1); NIY
if (trace(1))
htrc("SubAllocating a buffer of %d bytes\n", Buflen);
To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
if (mode == MODE_INSERT) {
/*******************************************************************/
/* For Insert buffer must be prepared. */
/*******************************************************************/
memset(To_Buf, ' ', Buflen);
To_Buf[Buflen - 2] = '\n';
To_Buf[Buflen - 1] = '\0';
} // endif Insert
return false;
} // end of AllocateBuffer
/***********************************************************************/
/* GetRowID: return the RowID of last read record. */
/***********************************************************************/
int GZFAM::GetRowID(void)
{
return Rows;
} // end of GetRowID
/***********************************************************************/
/* GetPos: return the position of last read record. */
/***********************************************************************/
int GZFAM::GetPos(void)
{
return (int)Zpos;
} // end of GetPos
/***********************************************************************/
/* GetNextPos: return the position of next record. */
/***********************************************************************/
int GZFAM::GetNextPos(void)
{
return gztell(Zfile);
} // end of GetNextPos
/***********************************************************************/
/* SetPos: Replace the table at the specified position. */
/***********************************************************************/
bool GZFAM::SetPos(PGLOBAL g, int pos __attribute__((unused)))
{
snprintf(g->Message, sizeof(g->Message), MSG(NO_SETPOS_YET), "GZ");
return true;
#if 0
Fpos = pos;
if (fseek(Stream, Fpos, SEEK_SET)) {
snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), Fpos);
return true;
} // endif
Placed = true;
return false;
#endif // 0
} // end of SetPos
/***********************************************************************/
/* Record file position in case of UPDATE or DELETE. */
/***********************************************************************/
bool GZFAM::RecordPos(PGLOBAL)
{
Zpos = gztell(Zfile);
return false;
} // end of RecordPos
/***********************************************************************/
/* Skip one record in file. */
/***********************************************************************/
int GZFAM::SkipRecord(PGLOBAL g, bool header)
{
// Skip this record
if (gzeof(Zfile))
return RC_EF;
else if (gzgets(Zfile, To_Buf, Buflen) == Z_NULL)
return Zerror(g);
if (header)
RecordPos(g);
return RC_OK;
} // end of SkipRecord
/***********************************************************************/
/* ReadBuffer: Read one line from a compressed text file. */
/***********************************************************************/
int GZFAM::ReadBuffer(PGLOBAL g)
{
char *p;
int rc;
if (!Zfile)
return RC_EF;
if (!Placed) {
/*******************************************************************/
/* Record file position in case of UPDATE or DELETE. */
/*******************************************************************/
next:
if (RecordPos(g))
return RC_FX;
CurBlk = Rows++; // Update RowID
/*******************************************************************/
/* 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 (gzeof(Zfile)) {
rc = RC_EF;
} else if (gzgets(Zfile, To_Buf, Buflen) != Z_NULL) {
p = To_Buf + strlen(To_Buf) - 1;
if (*p == '\n')
*p = '\0'; // Eliminate ending new-line character
if (*(--p) == '\r')
*p = '\0'; // Eliminate eventuel carriage return
strcpy(Tdbp->GetLine(), To_Buf);
IsRead = true;
rc = RC_OK;
num_read++;
} else
rc = Zerror(g);
if (trace(2))
htrc(" Read: '%s' rc=%d\n", To_Buf, rc);
return rc;
} // end of ReadBuffer
/***********************************************************************/
/* WriteDB: Data Base write routine for ZDOS access method. */
/* Update is not possible without using a temporary file (NIY). */
/***********************************************************************/
int GZFAM::WriteBuffer(PGLOBAL g)
{
/*********************************************************************/
/* Prepare the write buffer. */
/*********************************************************************/
strcat(strcpy(To_Buf, Tdbp->GetLine()), CrLf);
/*********************************************************************/
/* Now start the writing process. */
/*********************************************************************/
if (gzputs(Zfile, To_Buf) < 0)
return Zerror(g);
return RC_OK;
} // end of WriteBuffer
/***********************************************************************/
/* Data Base delete line routine for ZDOS access method. (NIY) */
/***********************************************************************/
int GZFAM::DeleteRecords(PGLOBAL g, int)
{
strcpy(g->Message, MSG(NO_ZIP_DELETE));
return RC_FX;
} // end of DeleteRecords
/***********************************************************************/
/* Data Base close routine for DOS access method. */
/***********************************************************************/
void GZFAM::CloseTableFile(PGLOBAL, bool)
{
int rc = gzclose(Zfile);
if (trace(1))
htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc);
Zfile = NULL; // So we can know whether table is open
//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
} // end of CloseTableFile
/***********************************************************************/
/* Rewind routine for GZ access method. */
/***********************************************************************/
void GZFAM::Rewind(void)
{
gzrewind(Zfile);
} // end of Rewind
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
ZBKFAM::ZBKFAM(PDOSDEF tdp) : GZFAM(tdp)
{
Blocked = true;
Block = tdp->GetBlock();
Last = tdp->GetLast();
Nrec = tdp->GetElemt();
CurLine = NULL;
NxtLine = NULL;
Closing = false;
BlkPos = tdp->GetTo_Pos();
} // end of ZBKFAM standard constructor
ZBKFAM::ZBKFAM(PZBKFAM txfp) : GZFAM(txfp)
{
CurLine = txfp->CurLine;
NxtLine = txfp->NxtLine;
Closing = txfp->Closing;
} // end of ZBKFAM copy constructor
/***********************************************************************/
/* Use BlockTest to reduce the table estimated size. */
/***********************************************************************/
int ZBKFAM::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
/***********************************************************************/
/* ZBK 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 ZBKFAM::Cardinality(PGLOBAL g)
{
return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
} // end of Cardinality
/***********************************************************************/
/* 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 ZBKFAM::AllocateBuffer(PGLOBAL g)
{
Buflen = Nrec * (Lrecl + 2);
CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
if (Tdbp->GetMode() == MODE_INSERT) {
// Set values so Block and Last can be recalculated
if (Last == Nrec) {
CurBlk = Block;
Rbuf = Nrec; // To be used by WriteDB
} else {
// The last block must be completed
CurBlk = Block - 1;
Rbuf = Nrec - Last; // To be used by WriteDB
} // endif Last
} // endif Insert
return false;
} // end of AllocateBuffer
/***********************************************************************/
/* GetRowID: return the RowID of last read record. */
/***********************************************************************/
int ZBKFAM::GetRowID(void)
{
return CurNum + Nrec * CurBlk + 1;
} // end of GetRowID
/***********************************************************************/
/* GetPos: return the position of last read record. */
/***********************************************************************/
int ZBKFAM::GetPos(void)
{
return CurNum + Nrec * CurBlk; // Computed file index
} // end of GetPos
/***********************************************************************/
/* Record file position in case of UPDATE or DELETE. */
/* Not used yet for fixed tables. */
/***********************************************************************/
bool ZBKFAM::RecordPos(PGLOBAL /*g*/)
{
//strcpy(g->Message, "RecordPos not implemented for gz blocked tables");
//return true;
return RC_OK;
} // end of RecordPos
/***********************************************************************/
/* Skip one record in file. */
/***********************************************************************/
int ZBKFAM::SkipRecord(PGLOBAL /*g*/, bool)
{
//strcpy(g->Message, "SkipRecord not implemented for gz blocked tables");
//return RC_FX;
return RC_OK;
} // end of SkipRecord
/***********************************************************************/
/* ReadBuffer: Read one line from a compressed text file. */
/***********************************************************************/
int ZBKFAM::ReadBuffer(PGLOBAL g)
{
int n, skip, rc = RC_OK;
/*********************************************************************/
/* Sequential reading when Placed is not true. */
/*********************************************************************/
if (++CurNum < Rbuf) {
CurLine = NxtLine;
// Get the position of the next line in the buffer
while (*NxtLine++ != '\n') ;
// Set caller line buffer
n = (int)(NxtLine - CurLine - Ending);
memcpy(Tdbp->GetLine(), CurLine, n);
Tdbp->GetLine()[n] = '\0';
return RC_OK;
} else if (Rbuf < Nrec && CurBlk != -1)
return RC_EF;
/*********************************************************************/
/* New block. */
/*********************************************************************/
CurNum = 0;
skip = 0;
next:
if (++CurBlk >= Block)
return RC_EF;
/*********************************************************************/
/* Before using the 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:
skip++;
goto next;
} // endswitch rc
if (skip)
// Skip blocks rejected by block optimization
for (int i = CurBlk - skip; i < CurBlk; i++) {
BlkLen = BlkPos[i + 1] - BlkPos[i];
if (gzseek(Zfile, (z_off_t)BlkLen, SEEK_CUR) < 0)
return Zerror(g);
} // endfor i
BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
if (!(n = gzread(Zfile, To_Buf, BlkLen))) {
rc = RC_EF;
} else if (n > 0) {
// Get the position of the current line
CurLine = To_Buf;
// Now get the position of the next line
for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
// Set caller line buffer
n = (int)(NxtLine - CurLine - Ending);
memcpy(Tdbp->GetLine(), CurLine, n);
Tdbp->GetLine()[n] = '\0';
Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
IsRead = true;
rc = RC_OK;
num_read++;
} else
rc = Zerror(g);
return rc;
} // end of ReadBuffer
/***********************************************************************/
/* WriteDB: Data Base write routine for ZDOS access method. */
/* Update is not possible without using a temporary file (NIY). */
/***********************************************************************/
int ZBKFAM::WriteBuffer(PGLOBAL g)
{
/*********************************************************************/
/* Prepare the write buffer. */
/*********************************************************************/
if (!Closing)
strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
/*********************************************************************/
/* In Insert mode, blocs are added sequentialy to the file end. */
/* Note: Update mode is not handled for gz files. */
/*********************************************************************/
if (++CurNum == Rbuf) {
/*******************************************************************/
/* New block, start the writing process. */
/*******************************************************************/
BlkLen = CurLine + strlen(CurLine) - To_Buf;
if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
gzflush(Zfile, Z_FULL_FLUSH)) {
Closing = true;
return Zerror(g);
} // endif gzwrite
Rbuf = Nrec;
CurBlk++;
CurNum = 0;
CurLine = To_Buf;
} else
CurLine += strlen(CurLine);
return RC_OK;
} // end of WriteBuffer
/***********************************************************************/
/* Data Base delete line routine for ZBK access method. */
/* Implemented only for total deletion of the table, which is done */
/* by opening the file in mode "wb". */
/***********************************************************************/
int ZBKFAM::DeleteRecords(PGLOBAL g, int irc)
{
if (irc == RC_EF) {
LPCSTR name = Tdbp->GetName();
PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
defp->SetBlock(0);
defp->SetLast(Nrec);
if (!defp->SetIntCatInfo("Blocks", 0) ||
!defp->SetIntCatInfo("Last", 0)) {
snprintf(g->Message, sizeof(g->Message), MSG(UPDATE_ERROR), "Header");
return RC_FX;
} else
return RC_OK;
} else
return irc;
} // end of DeleteRecords
/***********************************************************************/
/* Data Base close routine for ZBK access method. */
/***********************************************************************/
void ZBKFAM::CloseTableFile(PGLOBAL g, bool)
{
int rc = RC_OK;
if (Tdbp->GetMode() == MODE_INSERT) {
LPCSTR name = Tdbp->GetName();
PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
if (CurNum && !Closing) {
// Some more inserted lines remain to be written
Last = (Nrec - Rbuf) + CurNum;
Block = CurBlk + 1;
Rbuf = CurNum--;
Closing = true;
rc = WriteBuffer(g);
} else if (Rbuf == Nrec) {
Last = Nrec;
Block = CurBlk;
} // endif CurNum
if (rc != RC_FX) {
defp->SetBlock(Block);
defp->SetLast(Last);
defp->SetIntCatInfo("Blocks", Block);
defp->SetIntCatInfo("Last", Last);
} // endif
gzclose(Zfile);
} else if (Tdbp->GetMode() == MODE_DELETE) {
rc = DeleteRecords(g, RC_EF);
gzclose(Zfile);
} else
rc = gzclose(Zfile);
if (trace(1))
htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc);
Zfile = NULL; // So we can know whether table is open
//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
} // end of CloseTableFile
/***********************************************************************/
/* Rewind routine for ZBK access method. */
/***********************************************************************/
void ZBKFAM::Rewind(void)
{
gzrewind(Zfile);
CurBlk = -1;
CurNum = Rbuf;
} // end of Rewind
/* ------------------------------------------------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
GZXFAM::GZXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
{
//Block = tdp->GetBlock();
//Last = tdp->GetLast();
Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
Blksize = Nrec * Lrecl;
} // end of GZXFAM standard constructor
/***********************************************************************/
/* ZIX 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 GZXFAM::Cardinality(PGLOBAL g)
{
if (Last)
return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
else // Last and Block not defined, cannot do it yet
return 0;
} // end of Cardinality
/***********************************************************************/
/* 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 GZXFAM::AllocateBuffer(PGLOBAL g)
{
Buflen = Blksize;
To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
if (Tdbp->GetMode() == MODE_INSERT) {
/*******************************************************************/
/* For Insert the buffer must be prepared. */
/*******************************************************************/
memset(To_Buf, ' ', Buflen);
if (Tdbp->GetFtype() < 2)
// if not binary, the file is physically a text file
for (int len = Lrecl; len <= Buflen; len += Lrecl) {
#if defined(_WIN32)
To_Buf[len - 2] = '\r';
#endif // _WIN32
To_Buf[len - 1] = '\n';
} // endfor len
// Set values so Block and Last can be recalculated
if (Last == Nrec) {
CurBlk = Block;
Rbuf = Nrec; // To be used by WriteDB
} else {
// The last block must be completed
CurBlk = Block - 1;
Rbuf = Nrec - Last; // To be used by WriteDB
} // endif Last
} // endif Insert
return false;
} // end of AllocateBuffer
/***********************************************************************/
/* ReadBuffer: Read one line from a compressed text file. */
/***********************************************************************/
int GZXFAM::ReadBuffer(PGLOBAL g)
{
int n, rc = RC_OK;
/*********************************************************************/
/* Sequential reading when Placed is not true. */
/*********************************************************************/
if (++CurNum < Rbuf) {
Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
return RC_OK;
} else if (Rbuf < Nrec && CurBlk != -1)
return RC_EF;
/*********************************************************************/
/* New block. */
/*********************************************************************/
CurNum = 0;
Tdbp->SetLine(To_Buf);
int skip = 0;
next:
if (++CurBlk >= Block)
return RC_EF;
/*********************************************************************/
/* Before using the 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:
skip++;
goto next;
} // endswitch rc
if (skip)
// Skip blocks rejected by block optimization
for (int i = 0; i < skip; i++) {
if (gzseek(Zfile, (z_off_t)Buflen, SEEK_CUR) < 0)
return Zerror(g);
} // endfor i
if (!(n = gzread(Zfile, To_Buf, Buflen))) {
rc = RC_EF;
} else if (n > 0) {
Rbuf = n / Lrecl;
IsRead = true;
rc = RC_OK;
num_read++;
} else
rc = Zerror(g);
return rc;
} // end of ReadBuffer
/***********************************************************************/
/* WriteDB: Data Base write routine for ZDOS access method. */
/* Update is not possible without using a temporary file (NIY). */
/***********************************************************************/
int GZXFAM::WriteBuffer(PGLOBAL g)
{
/*********************************************************************/
/* In Insert mode, blocs are added sequentialy to the file end. */
/* Note: Update mode is not handled for gz files. */
/*********************************************************************/
if (++CurNum == Rbuf) {
/*******************************************************************/
/* New block, start the writing process. */
/*******************************************************************/
BlkLen = Rbuf * Lrecl;
if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
gzflush(Zfile, Z_FULL_FLUSH)) {
Closing = true;
return Zerror(g);
} // endif gzwrite
Rbuf = Nrec;
CurBlk++;
CurNum = 0;
Tdbp->SetLine(To_Buf);
} else
Tdbp->IncLine(Lrecl); // Used by FIXCOL functions
return RC_OK;
} // end of WriteBuffer
/* --------------------------- Class ZLBFAM -------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
ZLBFAM::ZLBFAM(PDOSDEF tdp) : BLKFAM(tdp)
{
Zstream = NULL;
Zbuffer = NULL;
Zlenp = NULL;
Optimized = tdp->IsOptimized();
} // end of ZLBFAM standard constructor
ZLBFAM::ZLBFAM(PZLBFAM txfp) : BLKFAM(txfp)
{
Zstream = txfp->Zstream;
Zbuffer = txfp->Zbuffer;
Zlenp = txfp->Zlenp;
Optimized = txfp->Optimized;
} // end of ZLBFAM (dummy?) copy constructor
/***********************************************************************/
/* ZLB GetFileLength: returns an estimate of what would be the */
/* uncompressed file size in number of bytes. */
/***********************************************************************/
int ZLBFAM::GetFileLength(PGLOBAL g)
{
int len = (Optimized) ? BlkPos[Block] : BLKFAM::GetFileLength(g);
if (len > 0)
// Estimate size reduction to a max of 5
len *= 5;
return len;
} // end of GetFileLength
/***********************************************************************/
/* 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 ZLBFAM::AllocateBuffer(PGLOBAL g)
{
PCSZ msg;
int n, zrc;
#if 0
if (!Optimized && Tdbp->NeedIndexing(g)) {
strcpy(g->Message, MSG(NOP_ZLIB_INDEX));
return TRUE;
} // endif indexing
#endif // 0
#if defined(NOLIB)
if (!zlib && LoadZlib()) {
snprintf(g->Message, sizeof(g->Message), MSG(DLL_LOAD_ERROR), GetLastError(), "zlib.dll");
return TRUE;
} // endif zlib
#endif
BLKFAM::AllocateBuffer(g);
//Buflen = Nrec * (Lrecl + 2);
//Rbuf = Nrec;
// Allocate the compressed buffer
n = Buflen + 16; // ?????????????????????????????????
Zlenp = (int*)PlugSubAlloc(g, NULL, n);
Zbuffer = (Byte*)(Zlenp + 1);
// Allocate and initialize the Z stream
Zstream = (z_streamp)PlugSubAlloc(g, NULL, sizeof(z_stream));
Zstream->zalloc = (alloc_func)0;
Zstream->zfree = (free_func)0;
Zstream->opaque = (voidpf)0;
Zstream->next_in = NULL;
Zstream->avail_in = 0;
if (Tdbp->GetMode() == MODE_READ) {
msg = "inflateInit";
zrc = inflateInit(Zstream);
} else {
msg = "deflateInit";
zrc = deflateInit(Zstream, Z_DEFAULT_COMPRESSION);
} // endif Mode
if (zrc != Z_OK) {
if (Zstream->msg)
snprintf(g->Message, sizeof(g->Message), "%s error: %s", msg, Zstream->msg);
else
snprintf(g->Message, sizeof(g->Message), "%s error: %d", msg, zrc);
return TRUE;
} // endif zrc
if (Tdbp->GetMode() == MODE_INSERT) {
// Write the file header block
if (Last == Nrec) {
CurBlk = Block;
CurNum = 0;
if (!GetFileLength(g)) {
// Write the zlib header as an extra block
strcpy(To_Buf, "PlugDB");
BlkLen = strlen("PlugDB") + 1;
if (WriteCompressedBuffer(g))
return TRUE;
} // endif void file
} else {
// In mode insert, if Last != Nrec, last block must be updated
CurBlk = Block - 1;
CurNum = Last;
strcpy(g->Message, MSG(NO_PAR_BLK_INS));
return TRUE;
} // endif Last
} else { // MODE_READ
// First thing to do is to read the header block
void *rdbuf;
if (Optimized) {
BlkLen = BlkPos[0];
rdbuf = Zlenp;
} else {
// Get the stored length from the file itself
if (fread(Zlenp, sizeof(int), 1, Stream) != 1)
return FALSE; // Empty file
BlkLen = *Zlenp;
rdbuf = Zbuffer;
} // endif Optimized
switch (ReadCompressedBuffer(g, rdbuf)) {
case RC_EF:
return FALSE;
case RC_FX:
#if defined(UNIX)
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(errno));
#else
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
#endif
case RC_NF:
return TRUE;
} // endswitch
// Some old tables can have PlugDB in their header
if (strcmp(To_Buf, "PlugDB")) {
snprintf(g->Message, sizeof(g->Message), MSG(BAD_HEADER), Tdbp->GetFile(g));
return TRUE;
} // endif strcmp
} // endif Mode
return FALSE;
} // end of AllocateBuffer
/***********************************************************************/
/* GetPos: return the position of last read record. */
/***********************************************************************/
int ZLBFAM::GetPos(void)
{
return (Optimized) ? (CurNum + Nrec * CurBlk) : Fpos;
} // end of GetPos
/***********************************************************************/
/* GetNextPos: should not be called for this class. */
/***********************************************************************/
int ZLBFAM::GetNextPos(void)
{
if (Optimized) {
assert(FALSE);
return 0;
} else
return ftell(Stream);
} // end of GetNextPos
/***********************************************************************/
/* SetPos: Replace the table at the specified position. */
/***********************************************************************/
bool ZLBFAM::SetPos(PGLOBAL g, int pos __attribute__((unused)))
{
snprintf(g->Message, sizeof(g->Message), MSG(NO_SETPOS_YET), "GZ");
return true;
#if 0 // All this must be checked
if (pos < 0) {
strcpy(g->Message, MSG(INV_REC_POS));
return true;
} // endif recpos
CurBlk = pos / Nrec;
CurNum = pos % Nrec;
#if defined(_DEBUG)
num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
#endif
// Indicate the table position was externally set
Placed = true;
return false;
#endif // 0
} // end of SetPos
/***********************************************************************/
/* ReadBuffer: Read one line for a text file. */
/***********************************************************************/
int ZLBFAM::ReadBuffer(PGLOBAL g)
{
size_t n;
void *rdbuf;
/*********************************************************************/
/* 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
if (Tdbp->GetFtype() == RECFM_VAR)
while (*NxtLine++ != '\n') ;
else
NxtLine += Lrecl;
// Set caller line buffer
n = NxtLine - CurLine - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
memcpy(Tdbp->GetLine(), CurLine, n);
Tdbp->GetLine()[n] = '\0';
return RC_OK;
} else if (Rbuf < Nrec && CurBlk != -1) {
CurNum--; // To have a correct Last value when optimizing
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. */
/*******************************************************************/
if (Optimized) 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
if (Optimized) {
// Store the position of next block
Fpos = BlkPos[CurBlk];
// fseek is required only in non sequential reading
if (CurBlk != OldBlk + 1)
if (fseek(Stream, Fpos, SEEK_SET)) {
snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), Fpos);
return RC_FX;
} // endif fseek
// Calculate the length of block to read
BlkLen = BlkPos[CurBlk + 1] - Fpos;
rdbuf = Zlenp;
} else { // !Optimized
if (CurBlk != OldBlk + 1) {
strcpy(g->Message, MSG(INV_RAND_ACC));
return RC_FX;
} else
Fpos = ftell(Stream); // Used when optimizing
// Get the stored length from the file itself
if (fread(Zlenp, sizeof(int), 1, Stream) != 1) {
if (feof(Stream))
return RC_EF;
goto err;
} // endif fread
BlkLen = *Zlenp;
rdbuf = Zbuffer;
} // endif Optimized
// Read the next block
switch (ReadCompressedBuffer(g, rdbuf)) {
case RC_FX: goto err;
case RC_NF: return RC_FX;
case RC_EF: return RC_EF;
default: Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
} // endswitch ReadCompressedBuffer
ok:
if (Tdbp->GetFtype() == RECFM_VAR) {
int i;
// 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;
} else {
CurLine = To_Buf + CurNum * Lrecl;
NxtLine = CurLine + Lrecl;
n = Lrecl - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
} // endif Ftype
memcpy(Tdbp->GetLine(), CurLine, n);
Tdbp->GetLine()[n] = '\0';
OldBlk = CurBlk; // Last block actually read
IsRead = TRUE; // Is read indeed
return RC_OK;
err:
#if defined(UNIX)
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(errno));
#else
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
#endif
return RC_FX;
} // end of ReadBuffer
/***********************************************************************/
/* Read and decompress a block from the stream. */
/***********************************************************************/
int ZLBFAM::ReadCompressedBuffer(PGLOBAL g, void *rdbuf)
{
if (fread(rdbuf, 1, (size_t)BlkLen, Stream) == (unsigned)BlkLen) {
int zrc;
num_read++;
if (Optimized && BlkLen != signed(*Zlenp + sizeof(int))) {
snprintf(g->Message, sizeof(g->Message), MSG(BAD_BLK_SIZE), CurBlk + 1);
return RC_NF;
} // endif BlkLen
// HERE WE MUST INFLATE THE BLOCK
Zstream->next_in = Zbuffer;
Zstream->avail_in = (uInt)(*Zlenp);
Zstream->next_out = (Byte*)To_Buf;
Zstream->avail_out = Buflen;
zrc = inflate(Zstream, Z_SYNC_FLUSH);
if (zrc != Z_OK) {
if (Zstream->msg)
snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERR_S), "inflate", Zstream->msg);
else
snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR), "inflate", (int)zrc);
return RC_NF;
} // endif zrc
} else if (feof(Stream)) {
return RC_EF;
} else
return RC_FX;
return RC_OK;
} // end of ReadCompressedBuffer
/***********************************************************************/
/* WriteBuffer: File write routine for DOS access method. */
/* Update is directly written back into the file, */
/* with this (fast) method, record size cannot change. */
/***********************************************************************/
int ZLBFAM::WriteBuffer(PGLOBAL g)
{
assert (Tdbp->GetMode() == MODE_INSERT);
/*********************************************************************/
/* Prepare the write buffer. */
/*********************************************************************/
if (!Closing) {
if (Tdbp->GetFtype() == RECFM_BIN)
memcpy(CurLine, Tdbp->GetLine(), Lrecl);
else
strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
#if defined(_DEBUG)
if (Tdbp->GetFtype() == RECFM_FIX &&
(signed)strlen(CurLine) != Lrecl + (signed)strlen(CrLf)) {
strcpy(g->Message, MSG(BAD_LINE_LEN));
Closing = TRUE;
return RC_FX;
} // endif Lrecl
#endif // _DEBUG
} // endif Closing
/*********************************************************************/
/* In Insert mode, blocs are added sequentialy to the file end. */
/*********************************************************************/
if (++CurNum != Rbuf) {
if (Tdbp->GetFtype() == RECFM_VAR)
CurLine += strlen(CurLine);
else
CurLine += Lrecl;
return RC_OK; // We write only full blocks
} // endif CurNum
// HERE WE MUST DEFLATE THE BLOCK
if (Tdbp->GetFtype() == RECFM_VAR)
NxtLine = CurLine + strlen(CurLine);
else
NxtLine = CurLine + Lrecl;
BlkLen = (int)(NxtLine - To_Buf);
if (WriteCompressedBuffer(g)) {
Closing = TRUE; // To tell CloseDB about a Write error
return RC_FX;
} // endif WriteCompressedBuffer
CurBlk++;
CurNum = 0;
CurLine = To_Buf;
return RC_OK;
} // end of WriteBuffer
/***********************************************************************/
/* Compress the buffer and write the deflated output to stream. */
/***********************************************************************/
bool ZLBFAM::WriteCompressedBuffer(PGLOBAL g)
{
int zrc;
Zstream->next_in = (Byte*)To_Buf;
Zstream->avail_in = (uInt)BlkLen;
Zstream->next_out = Zbuffer;
Zstream->avail_out = Buflen + 16;
Zstream->total_out = 0;
zrc = deflate(Zstream, Z_FULL_FLUSH);
if (zrc != Z_OK) {
if (Zstream->msg)
snprintf(g->Message, sizeof(g->Message), MSG(FUNC_ERR_S), "deflate", Zstream->msg);
else
snprintf(g->Message, sizeof(g->Message), MSG(FUNCTION_ERROR), "deflate", (int)zrc);
return TRUE;
} else
*Zlenp = Zstream->total_out;
// Now start the writing process.
BlkLen = *Zlenp + sizeof(int);
if (fwrite(Zlenp, 1, BlkLen, Stream) != (size_t)BlkLen) {
snprintf(g->Message, sizeof(g->Message), MSG(FWRITE_ERROR), strerror(errno));
return TRUE;
} // endif size
return FALSE;
} // end of WriteCompressedBuffer
/***********************************************************************/
/* Table file close routine for DOS access method. */
/***********************************************************************/
void ZLBFAM::CloseTableFile(PGLOBAL g, bool)
{
int rc = RC_OK;
if (Tdbp->GetMode() == MODE_INSERT) {
LPCSTR name = Tdbp->GetName();
PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
// Closing is True if last Write was in error
if (CurNum && !Closing) {
// Some more inserted lines remain to be written
Last = (Nrec - Rbuf) + CurNum;
Block = CurBlk + 1;
Rbuf = CurNum--;
Closing = TRUE;
rc = WriteBuffer(g);
} else if (Rbuf == Nrec) {
Last = Nrec;
Block = CurBlk;
} // endif CurNum
if (rc != RC_FX) {
defp->SetBlock(Block);
defp->SetLast(Last);
defp->SetIntCatInfo("Blocks", Block);
defp->SetIntCatInfo("Last", Last);
} // endif
fclose(Stream);
} else
rc = fclose(Stream);
if (trace(1))
htrc("ZLB CloseTableFile: closing %s mode=%d rc=%d\n",
To_File, Tdbp->GetMode(), rc);
Stream = NULL; // So we can know whether table is open
To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
if (Tdbp->GetMode() == MODE_READ)
rc = inflateEnd(Zstream);
else
rc = deflateEnd(Zstream);
} // end of CloseTableFile
/***********************************************************************/
/* Rewind routine for ZLIB access method. */
/***********************************************************************/
void ZLBFAM::Rewind(void)
{
// We must be positioned after the header block
if (CurBlk >= 0) { // Nothing to do if no block read yet
if (!Optimized) { // If optimized, fseek will be done in ReadBuffer
size_t st;
rewind(Stream);
if (!(st = fread(Zlenp, sizeof(int), 1, Stream)) && trace(1))
htrc("fread error %d in Rewind", errno);
fseek(Stream, *Zlenp + sizeof(int), SEEK_SET);
OldBlk = -1;
} // endif Optimized
CurBlk = -1;
CurNum = Rbuf;
} // endif CurBlk
//OldBlk = -1;
//Rbuf = 0; commented out in case we reuse last read block
} // end of Rewind
/* ------------------------ End of GzFam ---------------------------- */