1/************ File AM GZ C++ Program Source Code File (.CPP) ***********/
2/* PROGRAM NAME: FILAMGZ */
3/* ------------- */
4/* Version 1.5 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2005-2016 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the ZLIB compressed files classes. */
13/* */
14/***********************************************************************/
15
16/***********************************************************************/
17/* Include relevant MariaDB header file. */
18/***********************************************************************/
19#include "my_global.h"
20#if defined(__WIN__)
21#include <io.h>
22#include <fcntl.h>
23#if defined(__BORLANDC__)
24#define __MFC_COMPAT__ // To define min/max as macro
25#endif
26//#include <windows.h>
27#else // !__WIN__
28#if defined(UNIX)
29#include <errno.h>
30#else // !UNIX
31#include <io.h>
32#endif
33#include <fcntl.h>
34#endif // !__WIN__
35
36/***********************************************************************/
37/* Include application header files: */
38/* global.h is header containing all global declarations. */
39/* plgdbsem.h is header containing the DB application declarations. */
40/* tabdos.h is header containing the TABDOS class declarations. */
41/***********************************************************************/
42#include "global.h"
43#include "plgdbsem.h"
44//#include "catalog.h"
45//#include "reldef.h"
46//#include "xobject.h"
47//#include "kindex.h"
48#include "filamtxt.h"
49#include "tabdos.h"
50#if defined(UNIX)
51#include "osutil.h"
52#endif
53
54/***********************************************************************/
55/* This define prepares ZLIB function declarations. */
56/***********************************************************************/
57//#define ZLIB_DLL
58
59#include "filamgz.h"
60
61/***********************************************************************/
62/* DB static variables. */
63/***********************************************************************/
64extern int num_read, num_there, num_eq[]; // Statistics
65
66/* ------------------------------------------------------------------- */
67
68/***********************************************************************/
69/* Implementation of the GZFAM class. */
70/***********************************************************************/
71GZFAM::GZFAM(PGZFAM txfp) : TXTFAM(txfp)
72 {
73 Zfile = txfp->Zfile;
74 Zpos = txfp->Zpos;
75 } // end of GZFAM copy constructor
76
77/***********************************************************************/
78/* Zerror: Error function for gz calls. */
79/* gzerror returns the error message for the last error which occurred*/
80/* on the given compressed file. errnum is set to zlib error number. */
81/* If an error occurred in the file system and not in the compression */
82/* library, errnum is set to Z_ERRNO and the application may consult */
83/* errno to get the exact error code. */
84/***********************************************************************/
85int GZFAM::Zerror(PGLOBAL g)
86 {
87 int errnum;
88
89 strcpy(g->Message, gzerror(Zfile, &errnum));
90
91 if (errnum == Z_ERRNO)
92#if defined(__WIN__)
93 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(NULL));
94#else // !__WIN__
95 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
96#endif // !__WIN__
97
98 return (errnum == Z_STREAM_END) ? RC_EF : RC_FX;
99 } // end of Zerror
100
101/***********************************************************************/
102/* Reset: reset position values at the beginning of file. */
103/***********************************************************************/
104void GZFAM::Reset(void)
105 {
106 TXTFAM::Reset();
107//gzrewind(Zfile); // Useful ?????
108 Zpos = 0;
109 } // end of Reset
110
111/***********************************************************************/
112/* GZ GetFileLength: returns an estimate of what would be the */
113/* uncompressed file size in number of bytes. */
114/***********************************************************************/
115int GZFAM::GetFileLength(PGLOBAL g)
116 {
117 int len = TXTFAM::GetFileLength(g);
118
119 if (len > 0)
120 // Estimate size reduction to a max of 6
121 len *= 6;
122
123 return len;
124 } // end of GetFileLength
125
126/***********************************************************************/
127/* GZ Access Method opening routine. */
128/***********************************************************************/
129bool GZFAM::OpenTableFile(PGLOBAL g)
130 {
131 char opmode[4], filename[_MAX_PATH];
132 MODE mode = Tdbp->GetMode();
133
134 switch (mode) {
135 case MODE_READ:
136 strcpy(opmode, "r");
137 break;
138 case MODE_UPDATE:
139 /*****************************************************************/
140 /* Updating GZ files not implemented yet. */
141 /*****************************************************************/
142 strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP));
143 return true;
144 case MODE_DELETE:
145 if (!Tdbp->GetNext()) {
146 // Store the number of deleted lines
147 DelRows = Cardinality(g);
148
149 // This will erase the entire file
150 strcpy(opmode, "w");
151// Block = 0; // For ZBKFAM
152// Last = Nrec; // For ZBKFAM
153 Tdbp->ResetSize();
154 } else {
155 sprintf(g->Message, MSG(NO_PART_DEL), "GZ");
156 return true;
157 } // endif filter
158
159 break;
160 case MODE_INSERT:
161 strcpy(opmode, "a+");
162 break;
163 default:
164 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
165 return true;
166 } // endswitch Mode
167
168 /*********************************************************************/
169 /* Open according to logical input/output mode required. */
170 /* Use specific zlib functions. */
171 /* Treat files as binary. */
172 /*********************************************************************/
173 strcat(opmode, "b");
174 Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode);
175
176 if (Zfile == NULL) {
177 sprintf(g->Message, MSG(GZOPEN_ERROR),
178 opmode, (int)errno, filename);
179 strcat(strcat(g->Message, ": "), strerror(errno));
180 return (mode == MODE_READ && errno == ENOENT)
181 ? PushWarning(g, Tdbp) : true;
182 } // endif Zfile
183
184 /*********************************************************************/
185 /* Something to be done here. >>>>>>>> NOT DONE <<<<<<<< */
186 /*********************************************************************/
187//To_Fb = dbuserp->Openlist; // Keep track of File block
188
189 /*********************************************************************/
190 /* Allocate the line buffer. */
191 /*********************************************************************/
192 return AllocateBuffer(g);
193 } // end of OpenTableFile
194
195/***********************************************************************/
196/* Allocate the line buffer. For mode Delete a bigger buffer has to */
197/* be allocated because is it also used to move lines into the file. */
198/***********************************************************************/
199bool GZFAM::AllocateBuffer(PGLOBAL g)
200 {
201 MODE mode = Tdbp->GetMode();
202
203 Buflen = Lrecl + 2; // Lrecl does not include CRLF
204//Buflen *= ((Mode == MODE_DELETE) ? DOS_BUFF_LEN : 1); NIY
205
206 if (trace(1))
207 htrc("SubAllocating a buffer of %d bytes\n", Buflen);
208
209 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
210
211 if (mode == MODE_INSERT) {
212 /*******************************************************************/
213 /* For Insert buffer must be prepared. */
214 /*******************************************************************/
215 memset(To_Buf, ' ', Buflen);
216 To_Buf[Buflen - 2] = '\n';
217 To_Buf[Buflen - 1] = '\0';
218 } // endif Insert
219
220 return false;
221 } // end of AllocateBuffer
222
223/***********************************************************************/
224/* GetRowID: return the RowID of last read record. */
225/***********************************************************************/
226int GZFAM::GetRowID(void)
227 {
228 return Rows;
229 } // end of GetRowID
230
231/***********************************************************************/
232/* GetPos: return the position of last read record. */
233/***********************************************************************/
234int GZFAM::GetPos(void)
235 {
236 return (int)Zpos;
237 } // end of GetPos
238
239/***********************************************************************/
240/* GetNextPos: return the position of next record. */
241/***********************************************************************/
242int GZFAM::GetNextPos(void)
243 {
244 return gztell(Zfile);
245 } // end of GetNextPos
246
247/***********************************************************************/
248/* SetPos: Replace the table at the specified position. */
249/***********************************************************************/
250bool GZFAM::SetPos(PGLOBAL g, int pos __attribute__((unused)))
251 {
252 sprintf(g->Message, MSG(NO_SETPOS_YET), "GZ");
253 return true;
254#if 0
255 Fpos = pos;
256
257 if (fseek(Stream, Fpos, SEEK_SET)) {
258 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
259 return true;
260 } // endif
261
262 Placed = true;
263 return false;
264#endif // 0
265 } // end of SetPos
266
267/***********************************************************************/
268/* Record file position in case of UPDATE or DELETE. */
269/***********************************************************************/
270bool GZFAM::RecordPos(PGLOBAL)
271 {
272 Zpos = gztell(Zfile);
273 return false;
274 } // end of RecordPos
275
276/***********************************************************************/
277/* Skip one record in file. */
278/***********************************************************************/
279int GZFAM::SkipRecord(PGLOBAL g, bool header)
280 {
281 // Skip this record
282 if (gzeof(Zfile))
283 return RC_EF;
284 else if (gzgets(Zfile, To_Buf, Buflen) == Z_NULL)
285 return Zerror(g);
286
287 if (header)
288 RecordPos(g);
289
290 return RC_OK;
291 } // end of SkipRecord
292
293/***********************************************************************/
294/* ReadBuffer: Read one line from a compressed text file. */
295/***********************************************************************/
296int GZFAM::ReadBuffer(PGLOBAL g)
297 {
298 char *p;
299 int rc;
300
301 if (!Zfile)
302 return RC_EF;
303
304 if (!Placed) {
305 /*******************************************************************/
306 /* Record file position in case of UPDATE or DELETE. */
307 /*******************************************************************/
308 next:
309 if (RecordPos(g))
310 return RC_FX;
311
312 CurBlk = Rows++; // Update RowID
313
314 /*******************************************************************/
315 /* Check whether optimization on ROWID */
316 /* can be done, as well as for join as for local filtering. */
317 /*******************************************************************/
318 switch (Tdbp->TestBlock(g)) {
319 case RC_EF:
320 return RC_EF;
321 case RC_NF:
322 // Skip this record
323 if ((rc = SkipRecord(g, FALSE)) != RC_OK)
324 return rc;
325
326 goto next;
327 } // endswitch rc
328
329 } else
330 Placed = false;
331
332 if (gzeof(Zfile)) {
333 rc = RC_EF;
334 } else if (gzgets(Zfile, To_Buf, Buflen) != Z_NULL) {
335 p = To_Buf + strlen(To_Buf) - 1;
336
337 if (*p == '\n')
338 *p = '\0'; // Eliminate ending new-line character
339
340 if (*(--p) == '\r')
341 *p = '\0'; // Eliminate eventuel carriage return
342
343 strcpy(Tdbp->GetLine(), To_Buf);
344 IsRead = true;
345 rc = RC_OK;
346 num_read++;
347 } else
348 rc = Zerror(g);
349
350 if (trace(2))
351 htrc(" Read: '%s' rc=%d\n", To_Buf, rc);
352
353 return rc;
354 } // end of ReadBuffer
355
356/***********************************************************************/
357/* WriteDB: Data Base write routine for ZDOS access method. */
358/* Update is not possible without using a temporary file (NIY). */
359/***********************************************************************/
360int GZFAM::WriteBuffer(PGLOBAL g)
361 {
362 /*********************************************************************/
363 /* Prepare the write buffer. */
364 /*********************************************************************/
365 strcat(strcpy(To_Buf, Tdbp->GetLine()), CrLf);
366
367 /*********************************************************************/
368 /* Now start the writing process. */
369 /*********************************************************************/
370 if (gzputs(Zfile, To_Buf) < 0)
371 return Zerror(g);
372
373 return RC_OK;
374 } // end of WriteBuffer
375
376/***********************************************************************/
377/* Data Base delete line routine for ZDOS access method. (NIY) */
378/***********************************************************************/
379int GZFAM::DeleteRecords(PGLOBAL g, int)
380 {
381 strcpy(g->Message, MSG(NO_ZIP_DELETE));
382 return RC_FX;
383 } // end of DeleteRecords
384
385/***********************************************************************/
386/* Data Base close routine for DOS access method. */
387/***********************************************************************/
388void GZFAM::CloseTableFile(PGLOBAL, bool)
389 {
390 int rc = gzclose(Zfile);
391
392 if (trace(1))
393 htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc);
394
395 Zfile = NULL; // So we can know whether table is open
396//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
397 } // end of CloseTableFile
398
399/***********************************************************************/
400/* Rewind routine for GZ access method. */
401/***********************************************************************/
402void GZFAM::Rewind(void)
403 {
404 gzrewind(Zfile);
405 } // end of Rewind
406
407/* ------------------------------------------------------------------- */
408
409/***********************************************************************/
410/* Constructors. */
411/***********************************************************************/
412ZBKFAM::ZBKFAM(PDOSDEF tdp) : GZFAM(tdp)
413 {
414 Blocked = true;
415 Block = tdp->GetBlock();
416 Last = tdp->GetLast();
417 Nrec = tdp->GetElemt();
418 CurLine = NULL;
419 NxtLine = NULL;
420 Closing = false;
421 BlkPos = tdp->GetTo_Pos();
422 } // end of ZBKFAM standard constructor
423
424ZBKFAM::ZBKFAM(PZBKFAM txfp) : GZFAM(txfp)
425 {
426 CurLine = txfp->CurLine;
427 NxtLine = txfp->NxtLine;
428 Closing = txfp->Closing;
429 } // end of ZBKFAM copy constructor
430
431/***********************************************************************/
432/* Use BlockTest to reduce the table estimated size. */
433/***********************************************************************/
434int ZBKFAM::MaxBlkSize(PGLOBAL g, int)
435 {
436 int rc = RC_OK, savcur = CurBlk;
437 int size;
438
439 // Roughly estimate the table size as the sum of blocks
440 // that can contain good rows
441 for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
442 if ((rc = Tdbp->TestBlock(g)) == RC_OK)
443 size += (CurBlk == Block - 1) ? Last : Nrec;
444 else if (rc == RC_EF)
445 break;
446
447 CurBlk = savcur;
448 return size;
449 } // end of MaxBlkSize
450
451/***********************************************************************/
452/* ZBK Cardinality: returns table cardinality in number of rows. */
453/* This function can be called with a null argument to test the */
454/* availability of Cardinality implementation (1 yes, 0 no). */
455/***********************************************************************/
456int ZBKFAM::Cardinality(PGLOBAL g)
457 {
458 return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
459 } // end of Cardinality
460
461/***********************************************************************/
462/* Allocate the line buffer. For mode Delete a bigger buffer has to */
463/* be allocated because is it also used to move lines into the file. */
464/***********************************************************************/
465bool ZBKFAM::AllocateBuffer(PGLOBAL g)
466 {
467 Buflen = Nrec * (Lrecl + 2);
468 CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
469
470 if (Tdbp->GetMode() == MODE_INSERT) {
471 // Set values so Block and Last can be recalculated
472 if (Last == Nrec) {
473 CurBlk = Block;
474 Rbuf = Nrec; // To be used by WriteDB
475 } else {
476 // The last block must be completed
477 CurBlk = Block - 1;
478 Rbuf = Nrec - Last; // To be used by WriteDB
479 } // endif Last
480
481 } // endif Insert
482
483 return false;
484 } // end of AllocateBuffer
485
486/***********************************************************************/
487/* GetRowID: return the RowID of last read record. */
488/***********************************************************************/
489int ZBKFAM::GetRowID(void)
490 {
491 return CurNum + Nrec * CurBlk + 1;
492 } // end of GetRowID
493
494/***********************************************************************/
495/* GetPos: return the position of last read record. */
496/***********************************************************************/
497int ZBKFAM::GetPos(void)
498 {
499 return CurNum + Nrec * CurBlk; // Computed file index
500 } // end of GetPos
501
502/***********************************************************************/
503/* Record file position in case of UPDATE or DELETE. */
504/* Not used yet for fixed tables. */
505/***********************************************************************/
506bool ZBKFAM::RecordPos(PGLOBAL /*g*/)
507 {
508//strcpy(g->Message, "RecordPos not implemented for gz blocked tables");
509//return true;
510 return RC_OK;
511 } // end of RecordPos
512
513/***********************************************************************/
514/* Skip one record in file. */
515/***********************************************************************/
516int ZBKFAM::SkipRecord(PGLOBAL /*g*/, bool)
517 {
518//strcpy(g->Message, "SkipRecord not implemented for gz blocked tables");
519//return RC_FX;
520 return RC_OK;
521 } // end of SkipRecord
522
523/***********************************************************************/
524/* ReadBuffer: Read one line from a compressed text file. */
525/***********************************************************************/
526int ZBKFAM::ReadBuffer(PGLOBAL g)
527 {
528 int n, skip, rc = RC_OK;
529
530 /*********************************************************************/
531 /* Sequential reading when Placed is not true. */
532 /*********************************************************************/
533 if (++CurNum < Rbuf) {
534 CurLine = NxtLine;
535
536 // Get the position of the next line in the buffer
537 while (*NxtLine++ != '\n') ;
538
539 // Set caller line buffer
540 n = (int)(NxtLine - CurLine - Ending);
541 memcpy(Tdbp->GetLine(), CurLine, n);
542 Tdbp->GetLine()[n] = '\0';
543 return RC_OK;
544 } else if (Rbuf < Nrec && CurBlk != -1)
545 return RC_EF;
546
547 /*********************************************************************/
548 /* New block. */
549 /*********************************************************************/
550 CurNum = 0;
551 skip = 0;
552
553 next:
554 if (++CurBlk >= Block)
555 return RC_EF;
556
557 /*********************************************************************/
558 /* Before using the new block, check whether block optimization */
559 /* can be done, as well as for join as for local filtering. */
560 /*********************************************************************/
561 switch (Tdbp->TestBlock(g)) {
562 case RC_EF:
563 return RC_EF;
564 case RC_NF:
565 skip++;
566 goto next;
567 } // endswitch rc
568
569 if (skip)
570 // Skip blocks rejected by block optimization
571 for (int i = CurBlk - skip; i < CurBlk; i++) {
572 BlkLen = BlkPos[i + 1] - BlkPos[i];
573
574 if (gzseek(Zfile, (z_off_t)BlkLen, SEEK_CUR) < 0)
575 return Zerror(g);
576
577 } // endfor i
578
579 BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
580
581 if (!(n = gzread(Zfile, To_Buf, BlkLen))) {
582 rc = RC_EF;
583 } else if (n > 0) {
584 // Get the position of the current line
585 CurLine = To_Buf;
586
587 // Now get the position of the next line
588 for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
589
590 // Set caller line buffer
591 n = (int)(NxtLine - CurLine - Ending);
592 memcpy(Tdbp->GetLine(), CurLine, n);
593 Tdbp->GetLine()[n] = '\0';
594 Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
595 IsRead = true;
596 rc = RC_OK;
597 num_read++;
598 } else
599 rc = Zerror(g);
600
601 return rc;
602 } // end of ReadBuffer
603
604/***********************************************************************/
605/* WriteDB: Data Base write routine for ZDOS access method. */
606/* Update is not possible without using a temporary file (NIY). */
607/***********************************************************************/
608int ZBKFAM::WriteBuffer(PGLOBAL g)
609 {
610 /*********************************************************************/
611 /* Prepare the write buffer. */
612 /*********************************************************************/
613 if (!Closing)
614 strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
615
616 /*********************************************************************/
617 /* In Insert mode, blocs are added sequentialy to the file end. */
618 /* Note: Update mode is not handled for gz files. */
619 /*********************************************************************/
620 if (++CurNum == Rbuf) {
621 /*******************************************************************/
622 /* New block, start the writing process. */
623 /*******************************************************************/
624 BlkLen = CurLine + strlen(CurLine) - To_Buf;
625
626 if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
627 gzflush(Zfile, Z_FULL_FLUSH)) {
628 Closing = true;
629 return Zerror(g);
630 } // endif gzwrite
631
632 Rbuf = Nrec;
633 CurBlk++;
634 CurNum = 0;
635 CurLine = To_Buf;
636 } else
637 CurLine += strlen(CurLine);
638
639 return RC_OK;
640 } // end of WriteBuffer
641
642/***********************************************************************/
643/* Data Base delete line routine for ZBK access method. */
644/* Implemented only for total deletion of the table, which is done */
645/* by opening the file in mode "wb". */
646/***********************************************************************/
647int ZBKFAM::DeleteRecords(PGLOBAL g, int irc)
648 {
649 if (irc == RC_EF) {
650 LPCSTR name = Tdbp->GetName();
651 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
652
653 defp->SetBlock(0);
654 defp->SetLast(Nrec);
655
656 if (!defp->SetIntCatInfo("Blocks", 0) ||
657 !defp->SetIntCatInfo("Last", 0)) {
658 sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
659 return RC_FX;
660 } else
661 return RC_OK;
662
663 } else
664 return irc;
665
666 } // end of DeleteRecords
667
668/***********************************************************************/
669/* Data Base close routine for ZBK access method. */
670/***********************************************************************/
671void ZBKFAM::CloseTableFile(PGLOBAL g, bool)
672 {
673 int rc = RC_OK;
674
675 if (Tdbp->GetMode() == MODE_INSERT) {
676 LPCSTR name = Tdbp->GetName();
677 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
678
679 if (CurNum && !Closing) {
680 // Some more inserted lines remain to be written
681 Last = (Nrec - Rbuf) + CurNum;
682 Block = CurBlk + 1;
683 Rbuf = CurNum--;
684 Closing = true;
685 rc = WriteBuffer(g);
686 } else if (Rbuf == Nrec) {
687 Last = Nrec;
688 Block = CurBlk;
689 } // endif CurNum
690
691 if (rc != RC_FX) {
692 defp->SetBlock(Block);
693 defp->SetLast(Last);
694 defp->SetIntCatInfo("Blocks", Block);
695 defp->SetIntCatInfo("Last", Last);
696 } // endif
697
698 gzclose(Zfile);
699 } else if (Tdbp->GetMode() == MODE_DELETE) {
700 rc = DeleteRecords(g, RC_EF);
701 gzclose(Zfile);
702 } else
703 rc = gzclose(Zfile);
704
705 if (trace(1))
706 htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc);
707
708 Zfile = NULL; // So we can know whether table is open
709//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
710 } // end of CloseTableFile
711
712/***********************************************************************/
713/* Rewind routine for ZBK access method. */
714/***********************************************************************/
715void ZBKFAM::Rewind(void)
716 {
717 gzrewind(Zfile);
718 CurBlk = -1;
719 CurNum = Rbuf;
720 } // end of Rewind
721
722/* ------------------------------------------------------------------- */
723
724/***********************************************************************/
725/* Constructors. */
726/***********************************************************************/
727GZXFAM::GZXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
728 {
729//Block = tdp->GetBlock();
730//Last = tdp->GetLast();
731 Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
732 Blksize = Nrec * Lrecl;
733 } // end of GZXFAM standard constructor
734
735/***********************************************************************/
736/* ZIX Cardinality: returns table cardinality in number of rows. */
737/* This function can be called with a null argument to test the */
738/* availability of Cardinality implementation (1 yes, 0 no). */
739/***********************************************************************/
740int GZXFAM::Cardinality(PGLOBAL g)
741 {
742 if (Last)
743 return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
744 else // Last and Block not defined, cannot do it yet
745 return 0;
746
747 } // end of Cardinality
748
749/***********************************************************************/
750/* Allocate the line buffer. For mode Delete a bigger buffer has to */
751/* be allocated because is it also used to move lines into the file. */
752/***********************************************************************/
753bool GZXFAM::AllocateBuffer(PGLOBAL g)
754 {
755 Buflen = Blksize;
756 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
757
758 if (Tdbp->GetMode() == MODE_INSERT) {
759 /*******************************************************************/
760 /* For Insert the buffer must be prepared. */
761 /*******************************************************************/
762 memset(To_Buf, ' ', Buflen);
763
764 if (Tdbp->GetFtype() < 2)
765 // if not binary, the file is physically a text file
766 for (int len = Lrecl; len <= Buflen; len += Lrecl) {
767#if defined(__WIN__)
768 To_Buf[len - 2] = '\r';
769#endif // __WIN__
770 To_Buf[len - 1] = '\n';
771 } // endfor len
772
773 // Set values so Block and Last can be recalculated
774 if (Last == Nrec) {
775 CurBlk = Block;
776 Rbuf = Nrec; // To be used by WriteDB
777 } else {
778 // The last block must be completed
779 CurBlk = Block - 1;
780 Rbuf = Nrec - Last; // To be used by WriteDB
781 } // endif Last
782
783 } // endif Insert
784
785 return false;
786 } // end of AllocateBuffer
787
788/***********************************************************************/
789/* ReadBuffer: Read one line from a compressed text file. */
790/***********************************************************************/
791int GZXFAM::ReadBuffer(PGLOBAL g)
792 {
793 int n, rc = RC_OK;
794
795 /*********************************************************************/
796 /* Sequential reading when Placed is not true. */
797 /*********************************************************************/
798 if (++CurNum < Rbuf) {
799 Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
800 return RC_OK;
801 } else if (Rbuf < Nrec && CurBlk != -1)
802 return RC_EF;
803
804 /*********************************************************************/
805 /* New block. */
806 /*********************************************************************/
807 CurNum = 0;
808 Tdbp->SetLine(To_Buf);
809
810 int skip = 0;
811
812 next:
813 if (++CurBlk >= Block)
814 return RC_EF;
815
816 /*********************************************************************/
817 /* Before using the new block, check whether block optimization */
818 /* can be done, as well as for join as for local filtering. */
819 /*********************************************************************/
820 switch (Tdbp->TestBlock(g)) {
821 case RC_EF:
822 return RC_EF;
823 case RC_NF:
824 skip++;
825 goto next;
826 } // endswitch rc
827
828 if (skip)
829 // Skip blocks rejected by block optimization
830 for (int i = 0; i < skip; i++) {
831 if (gzseek(Zfile, (z_off_t)Buflen, SEEK_CUR) < 0)
832 return Zerror(g);
833
834 } // endfor i
835
836 if (!(n = gzread(Zfile, To_Buf, Buflen))) {
837 rc = RC_EF;
838 } else if (n > 0) {
839 Rbuf = n / Lrecl;
840 IsRead = true;
841 rc = RC_OK;
842 num_read++;
843 } else
844 rc = Zerror(g);
845
846 return rc;
847 } // end of ReadBuffer
848
849/***********************************************************************/
850/* WriteDB: Data Base write routine for ZDOS access method. */
851/* Update is not possible without using a temporary file (NIY). */
852/***********************************************************************/
853int GZXFAM::WriteBuffer(PGLOBAL g)
854 {
855 /*********************************************************************/
856 /* In Insert mode, blocs are added sequentialy to the file end. */
857 /* Note: Update mode is not handled for gz files. */
858 /*********************************************************************/
859 if (++CurNum == Rbuf) {
860 /*******************************************************************/
861 /* New block, start the writing process. */
862 /*******************************************************************/
863 BlkLen = Rbuf * Lrecl;
864
865 if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen ||
866 gzflush(Zfile, Z_FULL_FLUSH)) {
867 Closing = true;
868 return Zerror(g);
869 } // endif gzwrite
870
871 Rbuf = Nrec;
872 CurBlk++;
873 CurNum = 0;
874 Tdbp->SetLine(To_Buf);
875 } else
876 Tdbp->IncLine(Lrecl); // Used by FIXCOL functions
877
878 return RC_OK;
879 } // end of WriteBuffer
880
881/* --------------------------- Class ZLBFAM -------------------------- */
882
883/***********************************************************************/
884/* Constructors. */
885/***********************************************************************/
886ZLBFAM::ZLBFAM(PDOSDEF tdp) : BLKFAM(tdp)
887 {
888 Zstream = NULL;
889 Zbuffer = NULL;
890 Zlenp = NULL;
891 Optimized = tdp->IsOptimized();
892 } // end of ZLBFAM standard constructor
893
894ZLBFAM::ZLBFAM(PZLBFAM txfp) : BLKFAM(txfp)
895 {
896 Zstream = txfp->Zstream;
897 Zbuffer = txfp->Zbuffer;
898 Zlenp = txfp->Zlenp;
899 Optimized = txfp->Optimized;
900 } // end of ZLBFAM (dummy?) copy constructor
901
902/***********************************************************************/
903/* ZLB GetFileLength: returns an estimate of what would be the */
904/* uncompressed file size in number of bytes. */
905/***********************************************************************/
906int ZLBFAM::GetFileLength(PGLOBAL g)
907 {
908 int len = (Optimized) ? BlkPos[Block] : BLKFAM::GetFileLength(g);
909
910 if (len > 0)
911 // Estimate size reduction to a max of 5
912 len *= 5;
913
914 return len;
915 } // end of GetFileLength
916
917/***********************************************************************/
918/* Allocate the line buffer. For mode Delete a bigger buffer has to */
919/* be allocated because is it also used to move lines into the file. */
920/***********************************************************************/
921bool ZLBFAM::AllocateBuffer(PGLOBAL g)
922 {
923 PCSZ msg;
924 int n, zrc;
925
926#if 0
927 if (!Optimized && Tdbp->NeedIndexing(g)) {
928 strcpy(g->Message, MSG(NOP_ZLIB_INDEX));
929 return TRUE;
930 } // endif indexing
931#endif // 0
932
933#if defined(NOLIB)
934 if (!zlib && LoadZlib()) {
935 sprintf(g->Message, MSG(DLL_LOAD_ERROR), GetLastError(), "zlib.dll");
936 return TRUE;
937 } // endif zlib
938#endif
939
940 BLKFAM::AllocateBuffer(g);
941//Buflen = Nrec * (Lrecl + 2);
942//Rbuf = Nrec;
943
944 // Allocate the compressed buffer
945 n = Buflen + 16; // ?????????????????????????????????
946 Zlenp = (int*)PlugSubAlloc(g, NULL, n);
947 Zbuffer = (Byte*)(Zlenp + 1);
948
949 // Allocate and initialize the Z stream
950 Zstream = (z_streamp)PlugSubAlloc(g, NULL, sizeof(z_stream));
951 Zstream->zalloc = (alloc_func)0;
952 Zstream->zfree = (free_func)0;
953 Zstream->opaque = (voidpf)0;
954 Zstream->next_in = NULL;
955 Zstream->avail_in = 0;
956
957 if (Tdbp->GetMode() == MODE_READ) {
958 msg = "inflateInit";
959 zrc = inflateInit(Zstream);
960 } else {
961 msg = "deflateInit";
962 zrc = deflateInit(Zstream, Z_DEFAULT_COMPRESSION);
963 } // endif Mode
964
965 if (zrc != Z_OK) {
966 if (Zstream->msg)
967 sprintf(g->Message, "%s error: %s", msg, Zstream->msg);
968 else
969 sprintf(g->Message, "%s error: %d", msg, zrc);
970
971 return TRUE;
972 } // endif zrc
973
974 if (Tdbp->GetMode() == MODE_INSERT) {
975 // Write the file header block
976 if (Last == Nrec) {
977 CurBlk = Block;
978 CurNum = 0;
979
980 if (!GetFileLength(g)) {
981 // Write the zlib header as an extra block
982 strcpy(To_Buf, "PlugDB");
983 BlkLen = strlen("PlugDB") + 1;
984
985 if (WriteCompressedBuffer(g))
986 return TRUE;
987
988 } // endif void file
989
990 } else {
991 // In mode insert, if Last != Nrec, last block must be updated
992 CurBlk = Block - 1;
993 CurNum = Last;
994
995 strcpy(g->Message, MSG(NO_PAR_BLK_INS));
996 return TRUE;
997 } // endif Last
998
999 } else { // MODE_READ
1000 // First thing to do is to read the header block
1001 void *rdbuf;
1002
1003 if (Optimized) {
1004 BlkLen = BlkPos[0];
1005 rdbuf = Zlenp;
1006 } else {
1007 // Get the stored length from the file itself
1008 if (fread(Zlenp, sizeof(int), 1, Stream) != 1)
1009 return FALSE; // Empty file
1010
1011 BlkLen = *Zlenp;
1012 rdbuf = Zbuffer;
1013 } // endif Optimized
1014
1015 switch (ReadCompressedBuffer(g, rdbuf)) {
1016 case RC_EF:
1017 return FALSE;
1018 case RC_FX:
1019#if defined(UNIX)
1020 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
1021#else
1022 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
1023#endif
1024 case RC_NF:
1025 return TRUE;
1026 } // endswitch
1027
1028 // Some old tables can have PlugDB in their header
1029 if (strcmp(To_Buf, "PlugDB")) {
1030 sprintf(g->Message, MSG(BAD_HEADER), Tdbp->GetFile(g));
1031 return TRUE;
1032 } // endif strcmp
1033
1034 } // endif Mode
1035
1036 return FALSE;
1037 } // end of AllocateBuffer
1038
1039/***********************************************************************/
1040/* GetPos: return the position of last read record. */
1041/***********************************************************************/
1042int ZLBFAM::GetPos(void)
1043 {
1044 return (Optimized) ? (CurNum + Nrec * CurBlk) : Fpos;
1045 } // end of GetPos
1046
1047/***********************************************************************/
1048/* GetNextPos: should not be called for this class. */
1049/***********************************************************************/
1050int ZLBFAM::GetNextPos(void)
1051 {
1052 if (Optimized) {
1053 assert(FALSE);
1054 return 0;
1055 } else
1056 return ftell(Stream);
1057
1058 } // end of GetNextPos
1059
1060/***********************************************************************/
1061/* SetPos: Replace the table at the specified position. */
1062/***********************************************************************/
1063bool ZLBFAM::SetPos(PGLOBAL g, int pos __attribute__((unused)))
1064 {
1065 sprintf(g->Message, MSG(NO_SETPOS_YET), "GZ");
1066 return true;
1067#if 0 // All this must be checked
1068 if (pos < 0) {
1069 strcpy(g->Message, MSG(INV_REC_POS));
1070 return true;
1071 } // endif recpos
1072
1073 CurBlk = pos / Nrec;
1074 CurNum = pos % Nrec;
1075#if defined(_DEBUG)
1076 num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
1077#endif
1078
1079 // Indicate the table position was externally set
1080 Placed = true;
1081 return false;
1082#endif // 0
1083 } // end of SetPos
1084
1085/***********************************************************************/
1086/* ReadBuffer: Read one line for a text file. */
1087/***********************************************************************/
1088int ZLBFAM::ReadBuffer(PGLOBAL g)
1089 {
1090 size_t n;
1091 void *rdbuf;
1092
1093 /*********************************************************************/
1094 /* Sequential reading when Placed is not true. */
1095 /*********************************************************************/
1096 if (Placed) {
1097 Placed = FALSE;
1098 } else if (++CurNum < Rbuf) {
1099 CurLine = NxtLine;
1100
1101 // Get the position of the next line in the buffer
1102 if (Tdbp->GetFtype() == RECFM_VAR)
1103 while (*NxtLine++ != '\n') ;
1104 else
1105 NxtLine += Lrecl;
1106
1107 // Set caller line buffer
1108 n = NxtLine - CurLine - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
1109 memcpy(Tdbp->GetLine(), CurLine, n);
1110 Tdbp->GetLine()[n] = '\0';
1111 return RC_OK;
1112 } else if (Rbuf < Nrec && CurBlk != -1) {
1113 CurNum--; // To have a correct Last value when optimizing
1114 return RC_EF;
1115 } else {
1116 /*******************************************************************/
1117 /* New block. */
1118 /*******************************************************************/
1119 CurNum = 0;
1120
1121 next:
1122 if (++CurBlk >= Block)
1123 return RC_EF;
1124
1125 /*******************************************************************/
1126 /* Before reading a new block, check whether block optimization */
1127 /* can be done, as well as for join as for local filtering. */
1128 /*******************************************************************/
1129 if (Optimized) switch (Tdbp->TestBlock(g)) {
1130 case RC_EF:
1131 return RC_EF;
1132 case RC_NF:
1133 goto next;
1134 } // endswitch rc
1135
1136 } // endif's
1137
1138 if (OldBlk == CurBlk)
1139 goto ok; // Block is already there
1140
1141 if (Optimized) {
1142 // Store the position of next block
1143 Fpos = BlkPos[CurBlk];
1144
1145 // fseek is required only in non sequential reading
1146 if (CurBlk != OldBlk + 1)
1147 if (fseek(Stream, Fpos, SEEK_SET)) {
1148 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
1149 return RC_FX;
1150 } // endif fseek
1151
1152 // Calculate the length of block to read
1153 BlkLen = BlkPos[CurBlk + 1] - Fpos;
1154 rdbuf = Zlenp;
1155 } else { // !Optimized
1156 if (CurBlk != OldBlk + 1) {
1157 strcpy(g->Message, MSG(INV_RAND_ACC));
1158 return RC_FX;
1159 } else
1160 Fpos = ftell(Stream); // Used when optimizing
1161
1162 // Get the stored length from the file itself
1163 if (fread(Zlenp, sizeof(int), 1, Stream) != 1) {
1164 if (feof(Stream))
1165 return RC_EF;
1166
1167 goto err;
1168 } // endif fread
1169
1170 BlkLen = *Zlenp;
1171 rdbuf = Zbuffer;
1172 } // endif Optimized
1173
1174 // Read the next block
1175 switch (ReadCompressedBuffer(g, rdbuf)) {
1176 case RC_FX: goto err;
1177 case RC_NF: return RC_FX;
1178 case RC_EF: return RC_EF;
1179 default: Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
1180 } // endswitch ReadCompressedBuffer
1181
1182 ok:
1183 if (Tdbp->GetFtype() == RECFM_VAR) {
1184 int i;
1185
1186 // Get the position of the current line
1187 for (i = 0, CurLine = To_Buf; i < CurNum; i++)
1188 while (*CurLine++ != '\n') ; // What about Unix ???
1189
1190 // Now get the position of the next line
1191 for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
1192
1193 // Set caller line buffer
1194 n = NxtLine - CurLine - Ending;
1195 } else {
1196 CurLine = To_Buf + CurNum * Lrecl;
1197 NxtLine = CurLine + Lrecl;
1198 n = Lrecl - ((Tdbp->GetFtype() == RECFM_BIN) ? 0 : Ending);
1199 } // endif Ftype
1200
1201 memcpy(Tdbp->GetLine(), CurLine, n);
1202 Tdbp->GetLine()[n] = '\0';
1203
1204 OldBlk = CurBlk; // Last block actually read
1205 IsRead = TRUE; // Is read indeed
1206 return RC_OK;
1207
1208 err:
1209#if defined(UNIX)
1210 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
1211#else
1212 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
1213#endif
1214 return RC_FX;
1215 } // end of ReadBuffer
1216
1217/***********************************************************************/
1218/* Read and decompress a block from the stream. */
1219/***********************************************************************/
1220int ZLBFAM::ReadCompressedBuffer(PGLOBAL g, void *rdbuf)
1221 {
1222 if (fread(rdbuf, 1, (size_t)BlkLen, Stream) == (unsigned)BlkLen) {
1223 int zrc;
1224
1225 num_read++;
1226
1227 if (Optimized && BlkLen != signed(*Zlenp + sizeof(int))) {
1228 sprintf(g->Message, MSG(BAD_BLK_SIZE), CurBlk + 1);
1229 return RC_NF;
1230 } // endif BlkLen
1231
1232 // HERE WE MUST INFLATE THE BLOCK
1233 Zstream->next_in = Zbuffer;
1234 Zstream->avail_in = (uInt)(*Zlenp);
1235 Zstream->next_out = (Byte*)To_Buf;
1236 Zstream->avail_out = Buflen;
1237 zrc = inflate(Zstream, Z_SYNC_FLUSH);
1238
1239 if (zrc != Z_OK) {
1240 if (Zstream->msg)
1241 sprintf(g->Message, MSG(FUNC_ERR_S), "inflate", Zstream->msg);
1242 else
1243 sprintf(g->Message, MSG(FUNCTION_ERROR), "inflate", (int)zrc);
1244
1245 return RC_NF;
1246 } // endif zrc
1247
1248 } else if (feof(Stream)) {
1249 return RC_EF;
1250 } else
1251 return RC_FX;
1252
1253 return RC_OK;
1254 } // end of ReadCompressedBuffer
1255
1256/***********************************************************************/
1257/* WriteBuffer: File write routine for DOS access method. */
1258/* Update is directly written back into the file, */
1259/* with this (fast) method, record size cannot change. */
1260/***********************************************************************/
1261int ZLBFAM::WriteBuffer(PGLOBAL g)
1262 {
1263 assert (Tdbp->GetMode() == MODE_INSERT);
1264
1265 /*********************************************************************/
1266 /* Prepare the write buffer. */
1267 /*********************************************************************/
1268 if (!Closing) {
1269 if (Tdbp->GetFtype() == RECFM_BIN)
1270 memcpy(CurLine, Tdbp->GetLine(), Lrecl);
1271 else
1272 strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
1273
1274#if defined(_DEBUG)
1275 if (Tdbp->GetFtype() == RECFM_FIX &&
1276 (signed)strlen(CurLine) != Lrecl + (signed)strlen(CrLf)) {
1277 strcpy(g->Message, MSG(BAD_LINE_LEN));
1278 Closing = TRUE;
1279 return RC_FX;
1280 } // endif Lrecl
1281#endif // _DEBUG
1282 } // endif Closing
1283
1284 /*********************************************************************/
1285 /* In Insert mode, blocs are added sequentialy to the file end. */
1286 /*********************************************************************/
1287 if (++CurNum != Rbuf) {
1288 if (Tdbp->GetFtype() == RECFM_VAR)
1289 CurLine += strlen(CurLine);
1290 else
1291 CurLine += Lrecl;
1292
1293 return RC_OK; // We write only full blocks
1294 } // endif CurNum
1295
1296 // HERE WE MUST DEFLATE THE BLOCK
1297 if (Tdbp->GetFtype() == RECFM_VAR)
1298 NxtLine = CurLine + strlen(CurLine);
1299 else
1300 NxtLine = CurLine + Lrecl;
1301
1302 BlkLen = (int)(NxtLine - To_Buf);
1303
1304 if (WriteCompressedBuffer(g)) {
1305 Closing = TRUE; // To tell CloseDB about a Write error
1306 return RC_FX;
1307 } // endif WriteCompressedBuffer
1308
1309 CurBlk++;
1310 CurNum = 0;
1311 CurLine = To_Buf;
1312 return RC_OK;
1313 } // end of WriteBuffer
1314
1315/***********************************************************************/
1316/* Compress the buffer and write the deflated output to stream. */
1317/***********************************************************************/
1318bool ZLBFAM::WriteCompressedBuffer(PGLOBAL g)
1319 {
1320 int zrc;
1321
1322 Zstream->next_in = (Byte*)To_Buf;
1323 Zstream->avail_in = (uInt)BlkLen;
1324 Zstream->next_out = Zbuffer;
1325 Zstream->avail_out = Buflen + 16;
1326 Zstream->total_out = 0;
1327 zrc = deflate(Zstream, Z_FULL_FLUSH);
1328
1329 if (zrc != Z_OK) {
1330 if (Zstream->msg)
1331 sprintf(g->Message, MSG(FUNC_ERR_S), "deflate", Zstream->msg);
1332 else
1333 sprintf(g->Message, MSG(FUNCTION_ERROR), "deflate", (int)zrc);
1334
1335 return TRUE;
1336 } else
1337 *Zlenp = Zstream->total_out;
1338
1339 // Now start the writing process.
1340 BlkLen = *Zlenp + sizeof(int);
1341
1342 if (fwrite(Zlenp, 1, BlkLen, Stream) != (size_t)BlkLen) {
1343 sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
1344 return TRUE;
1345 } // endif size
1346
1347 return FALSE;
1348 } // end of WriteCompressedBuffer
1349
1350/***********************************************************************/
1351/* Table file close routine for DOS access method. */
1352/***********************************************************************/
1353void ZLBFAM::CloseTableFile(PGLOBAL g, bool)
1354 {
1355 int rc = RC_OK;
1356
1357 if (Tdbp->GetMode() == MODE_INSERT) {
1358 LPCSTR name = Tdbp->GetName();
1359 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1360
1361 // Closing is True if last Write was in error
1362 if (CurNum && !Closing) {
1363 // Some more inserted lines remain to be written
1364 Last = (Nrec - Rbuf) + CurNum;
1365 Block = CurBlk + 1;
1366 Rbuf = CurNum--;
1367 Closing = TRUE;
1368 rc = WriteBuffer(g);
1369 } else if (Rbuf == Nrec) {
1370 Last = Nrec;
1371 Block = CurBlk;
1372 } // endif CurNum
1373
1374 if (rc != RC_FX) {
1375 defp->SetBlock(Block);
1376 defp->SetLast(Last);
1377 defp->SetIntCatInfo("Blocks", Block);
1378 defp->SetIntCatInfo("Last", Last);
1379 } // endif
1380
1381 fclose(Stream);
1382 } else
1383 rc = fclose(Stream);
1384
1385 if (trace(1))
1386 htrc("ZLB CloseTableFile: closing %s mode=%d rc=%d\n",
1387 To_File, Tdbp->GetMode(), rc);
1388
1389 Stream = NULL; // So we can know whether table is open
1390 To_Fb->Count = 0; // Avoid double closing by PlugCloseAll
1391
1392 if (Tdbp->GetMode() == MODE_READ)
1393 rc = inflateEnd(Zstream);
1394 else
1395 rc = deflateEnd(Zstream);
1396
1397 } // end of CloseTableFile
1398
1399/***********************************************************************/
1400/* Rewind routine for ZLIB access method. */
1401/***********************************************************************/
1402void ZLBFAM::Rewind(void)
1403 {
1404 // We must be positioned after the header block
1405 if (CurBlk >= 0) { // Nothing to do if no block read yet
1406 if (!Optimized) { // If optimized, fseek will be done in ReadBuffer
1407 size_t st;
1408
1409 rewind(Stream);
1410
1411 if (!(st = fread(Zlenp, sizeof(int), 1, Stream)) && trace(1))
1412 htrc("fread error %d in Rewind", errno);
1413
1414 fseek(Stream, *Zlenp + sizeof(int), SEEK_SET);
1415 OldBlk = -1;
1416 } // endif Optimized
1417
1418 CurBlk = -1;
1419 CurNum = Rbuf;
1420 } // endif CurBlk
1421
1422//OldBlk = -1;
1423//Rbuf = 0; commented out in case we reuse last read block
1424 } // end of Rewind
1425
1426/* ------------------------ End of GzFam ---------------------------- */
1427