1/************* TabFix C++ Program Source Code File (.CPP) **************/
2/* PROGRAM NAME: TABFIX */
3/* ------------- */
4/* Version 4.9.2 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the TDBFIX class DB routines. */
13/* */
14/***********************************************************************/
15
16/***********************************************************************/
17/* Include relevant section of system dependant header files. */
18/***********************************************************************/
19#include "my_global.h"
20#if defined(__WIN__)
21#include <io.h>
22#include <fcntl.h>
23#include <errno.h>
24#if defined(__BORLANDC__)
25#define __MFC_COMPAT__ // To define min/max as macro
26#endif // __BORLANDC__
27//#include <windows.h>
28#else // !__WIN__
29#if defined(UNIX)
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <unistd.h>
33#include <errno.h>
34#else // !UNIX
35#include <io.h>
36#endif // !UNIX
37#include <fcntl.h>
38#endif // !__WIN__
39
40/***********************************************************************/
41/* Include application header files: */
42/***********************************************************************/
43#include "global.h" // global declares
44#include "plgdbsem.h" // DB application declares
45#include "filamfix.h"
46#include "filamdbf.h"
47#include "tabfix.h" // TDBFIX, FIXCOL classes declares
48#include "array.h"
49#include "blkfil.h"
50
51/***********************************************************************/
52/* DB static variables. */
53/***********************************************************************/
54extern int num_read, num_there, num_eq[2]; // Statistics
55static const longlong M2G = 0x80000000;
56static const longlong M4G = (longlong)2 * M2G;
57char BINCOL::Endian = 'H';
58
59/***********************************************************************/
60/* External function. */
61/***********************************************************************/
62USETEMP UseTemp(void);
63
64/* ------------------------------------------------------------------- */
65
66/***********************************************************************/
67/* Implementation of the TDBFIX class. */
68/***********************************************************************/
69TDBFIX::TDBFIX(PDOSDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
70 {
71 Teds = tdp->Teds; // For BIN tables
72 } // end of TDBFIX standard constructor
73
74TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp)
75 {
76 Teds = tdbp->Teds;
77 } // end of TDBFIX copy constructor
78
79// Method
80PTDB TDBFIX::Clone(PTABS t)
81 {
82 PTDB tp;
83 PGLOBAL g = t->G;
84
85 tp = new(g) TDBFIX(g, this);
86
87 if (Ftype < 2) {
88 // File is text
89 PDOSCOL cp1, cp2;
90
91 for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
92 cp2 = new(g) DOSCOL(cp1, tp); // Make a copy
93 NewPointer(t, cp1, cp2);
94 } // endfor cp1
95
96 } else {
97 // File is binary
98 PBINCOL cp1, cp2;
99
100 for (cp1 = (PBINCOL)Columns; cp1; cp1 = (PBINCOL)cp1->GetNext()) {
101 cp2 = new(g) BINCOL(cp1, tp); // Make a copy
102 NewPointer(t, cp1, cp2);
103 } // endfor cp1
104
105 } // endif Ftype
106
107 return tp;
108 } // end of Clone
109
110/***********************************************************************/
111/* Reset read/write position values. */
112/***********************************************************************/
113void TDBFIX::ResetDB(void)
114 {
115 TDBDOS::ResetDB();
116 } // end of ResetDB
117
118/***********************************************************************/
119/* Allocate FIX (DOS) or BIN column description block. */
120/***********************************************************************/
121PCOL TDBFIX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
122 {
123 if (Ftype == RECFM_BIN)
124 return new(g) BINCOL(g, cdp, this, cprec, n);
125 else
126 return new(g) DOSCOL(g, cdp, this, cprec, n);
127
128 } // end of MakeCol
129
130/***********************************************************************/
131/* Remake the indexes after the table was modified. */
132/***********************************************************************/
133int TDBFIX::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
134 {
135 int prc, rc = RC_OK;
136
137 To_Filter = NULL; // Disable filtering
138//To_BlkIdx = NULL; // and block filtering
139 To_BlkFil = NULL; // and index filtering
140 Cardinality(g); // If called by create
141 RestoreNrec(); // May have been modified
142 MaxSize = -1; // Size must be recalculated
143 Cardinal = -1; // as well as Cardinality
144
145 // After the table was modified the indexes
146 // are invalid and we should mark them as such...
147 rc = ((PDOSDEF)To_Def)->InvalidateIndex(g);
148
149 if (dop) {
150 Columns = NULL; // Not used anymore
151 Txfp->Reset();
152// OldBlk = CurBlk = -1;
153// ReadBlks = CurNum = Rbuf = Modif = 0;
154 Use = USE_READY; // So the table can be reopened
155 Mode = MODE_ANY; // Just to be clean
156 rc = MakeBlockValues(g); // Redo optimization
157 } // endif dop
158
159 if (dox && (rc == RC_OK || rc == RC_INFO)) {
160 // Remake eventual indexes
161 Columns = NULL; // Not used anymore
162 Txfp->Reset(); // New start
163 Use = USE_READY; // So the table can be reopened
164 Mode = MODE_READ; // New mode
165 prc = rc;
166
167 if (PlgGetUser(g)->Check & CHK_OPT)
168 // We must remake indexes.
169 rc = MakeIndex(g, NULL, FALSE);
170
171 rc = (rc == RC_INFO) ? prc : rc;
172 } // endif dox
173
174 return rc;
175 } // end of ResetTableOpt
176
177/***********************************************************************/
178/* Reset the Nrec and BlkSize values that can have been modified. */
179/***********************************************************************/
180void TDBFIX::RestoreNrec(void)
181 {
182 if (!Txfp->Padded) {
183 Txfp->Nrec = (To_Def && To_Def->GetElemt()) ? To_Def->GetElemt()
184 : DOS_BUFF_LEN;
185 Txfp->Blksize = Txfp->Nrec * Txfp->Lrecl;
186
187 if (Cardinal >= 0)
188 Txfp->Block = (Cardinal > 0)
189 ? (Cardinal + Txfp->Nrec - 1) / Txfp->Nrec : 0;
190
191 } // endif Padded
192
193 } // end of RestoreNrec
194
195/***********************************************************************/
196/* FIX Cardinality: returns table cardinality in number of rows. */
197/* This function can be called with a null argument to test the */
198/* availability of Cardinality implementation (1 yes, 0 no). */
199/***********************************************************************/
200int TDBFIX::Cardinality(PGLOBAL g)
201 {
202 if (!g)
203 return Txfp->Cardinality(g);
204
205 if (Cardinal < 0)
206 Cardinal = Txfp->Cardinality(g);
207
208 return Cardinal;
209 } // end of Cardinality
210
211/***********************************************************************/
212/* FIX GetMaxSize: returns file size in number of lines. */
213/***********************************************************************/
214int TDBFIX::GetMaxSize(PGLOBAL g)
215 {
216 if (MaxSize < 0) {
217 MaxSize = Cardinality(g);
218
219 if (MaxSize > 0 && (To_BlkFil = InitBlockFilter(g, To_Filter))
220 && !To_BlkFil->Correlated()) {
221 // Use BlockTest to reduce the estimated size
222 MaxSize = Txfp->MaxBlkSize(g, MaxSize);
223 ResetBlockFilter(g);
224 } // endif To_BlkFil
225
226 } // endif MaxSize
227
228 return MaxSize;
229 } // end of GetMaxSize
230
231/***********************************************************************/
232/* FIX ResetSize: Must reset Headlen for DBF tables only. */
233/***********************************************************************/
234void TDBFIX::ResetSize(void)
235 {
236 if (Txfp->GetAmType() == TYPE_AM_DBF)
237 Txfp->Headlen = 0;
238
239 MaxSize = Cardinal = -1;
240 } // end of ResetSize
241
242/***********************************************************************/
243/* FIX GetProgMax: get the max value for progress information. */
244/***********************************************************************/
245int TDBFIX::GetProgMax(PGLOBAL g)
246 {
247 return Cardinality(g);
248 } // end of GetProgMax
249
250/***********************************************************************/
251/* RowNumber: return the ordinal number of the current row. */
252/***********************************************************************/
253int TDBFIX::RowNumber(PGLOBAL g, bool b)
254 {
255 if (Txfp->GetAmType() == TYPE_AM_DBF) {
256 if (!b && To_Kindex) {
257 /*****************************************************************/
258 /* Don't know how to retrieve Rows from DBF file address */
259 /* because of eventual deleted lines still in the file. */
260 /*****************************************************************/
261 sprintf(g->Message, MSG(NO_ROWID_FOR_AM),
262 GetAmName(g, Txfp->GetAmType()));
263 return 0;
264 } // endif To_Kindex
265
266 if (!b)
267 return Txfp->GetRows();
268
269 } // endif DBF
270
271 return Txfp->GetRowID();
272 } // end of RowNumber
273
274/***********************************************************************/
275/* FIX tables don't use temporary files except if specified as do it. */
276/***********************************************************************/
277bool TDBFIX::IsUsingTemp(PGLOBAL)
278 {
279 // Not ready yet to handle using a temporary file with mapping
280 // or while deleting from DBF files.
281 return ((UseTemp() == TMP_YES && Txfp->GetAmType() != TYPE_AM_MAP &&
282 !(Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_DBF)) ||
283 UseTemp() == TMP_FORCE || UseTemp() == TMP_TEST);
284 } // end of IsUsingTemp
285
286/***********************************************************************/
287/* FIX Access Method opening routine (also used by the BIN a.m.) */
288/* New method now that this routine is called recursively (last table */
289/* first in reverse order): index blocks are immediately linked to */
290/* join block of next table if it exists or else are discarted. */
291/***********************************************************************/
292bool TDBFIX::OpenDB(PGLOBAL g)
293 {
294 if (trace(1))
295 htrc("FIX OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d Ftype=%d\n",
296 this, Tdb_No, Use, To_Key_Col, Mode, Ftype);
297
298 if (Use == USE_OPEN) {
299 /*******************************************************************/
300 /* Table already open, just replace it at its beginning. */
301 /*******************************************************************/
302 if (To_Kindex)
303 /*****************************************************************/
304 /* Table is to be accessed through a sorted index table. */
305 /*****************************************************************/
306 To_Kindex->Reset();
307 else
308 Txfp->Rewind(); // see comment in Work.log
309
310 ResetBlockFilter(g);
311 return false;
312 } // endif use
313
314 if (Mode == MODE_DELETE && Txfp->GetAmType() == TYPE_AM_MAP &&
315 (!Next || UseTemp() == TMP_FORCE)) {
316 // Delete all lines or using temp. Not handled in MAP mode
317 Txfp = new(g) FIXFAM((PDOSDEF)To_Def);
318 Txfp->SetTdbp(this);
319 } // endif Mode
320
321 /*********************************************************************/
322 /* Call Cardinality to calculate Block in the case of Func queries. */
323 /* and also in the case of multiple tables. */
324 /*********************************************************************/
325 if (Cardinality(g) < 0)
326 return true;
327
328 /*********************************************************************/
329 /* Open according to required logical input/output mode. */
330 /* Use conventionnal input/output functions. */
331 /* Treat fixed length text files as binary. */
332 /*********************************************************************/
333 if (Txfp->OpenTableFile(g))
334 return true;
335
336 Use = USE_OPEN; // Do it now in case we are recursively called
337
338 /*********************************************************************/
339 /* Initialize To_Line at the beginning of the block buffer. */
340 /*********************************************************************/
341 To_Line = Txfp->GetBuf(); // For WriteDB
342
343 /*********************************************************************/
344 /* Allocate the block filter tree if evaluation is possible. */
345 /*********************************************************************/
346 To_BlkFil = InitBlockFilter(g, To_Filter);
347
348 if (trace(1))
349 htrc("OpenFix: R%hd mode=%d BlkFil=%p\n", Tdb_No, Mode, To_BlkFil);
350
351 /*********************************************************************/
352 /* Reset buffer access according to indexing and to mode. */
353 /*********************************************************************/
354 Txfp->ResetBuffer(g);
355
356 /*********************************************************************/
357 /* Reset statistics values. */
358 /*********************************************************************/
359 num_read = num_there = num_eq[0] = num_eq[1] = 0;
360 return false;
361 } // end of OpenDB
362
363/***********************************************************************/
364/* WriteDB: Data Base write routine for FIX access method. */
365/***********************************************************************/
366int TDBFIX::WriteDB(PGLOBAL g)
367 {
368 return Txfp->WriteBuffer(g);
369 } // end of WriteDB
370
371// ------------------------ BINCOL functions ----------------------------
372
373/***********************************************************************/
374/* BINCOL public constructor. */
375/***********************************************************************/
376BINCOL::BINCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PCSZ am)
377 : DOSCOL(g, cdp, tp, cp, i, am)
378 {
379 char c, *fmt = cdp->GetFmt();
380
381 Fmt = GetDomain() ? 'C' : 'X';
382 Buff = NULL;
383 Eds = ((PTDBFIX)tp)->Teds;
384 N = 0;
385 M = GetTypeSize(Buf_Type, sizeof(longlong));
386 Lim = 0;
387
388 if (fmt) {
389 for (N = 0, i = 0; fmt[i]; i++) {
390 c = toupper(fmt[i]);
391
392 if (isdigit(c))
393 N = (N * 10 + (c - '0'));
394 else if (c == 'L' || c == 'B' || c == 'H')
395 Eds = c;
396 else
397 Fmt = c;
398
399 } // endfor i
400
401 // M is the size of the source value
402 switch (Fmt) {
403 case 'C': Eds = 0; break;
404 case 'X': break;
405 case 'S': M = sizeof(short); break;
406 case 'T': M = sizeof(char); break;
407 case 'I': M = sizeof(int); break;
408 case 'G': M = sizeof(longlong); break;
409 case 'R': // Real
410 case 'F': M = sizeof(float); break;
411 case 'D': M = sizeof(double); break;
412 default:
413 sprintf(g->Message, MSG(BAD_BIN_FMT), Fmt, Name);
414 throw 11;
415 } // endswitch Fmt
416
417 } else if (IsTypeChar(Buf_Type))
418 Eds = 0;
419
420 if (Eds) {
421 // This is a byte order specification
422 if (!N)
423 N = M;
424
425 if (Eds != 'L' && Eds != 'B')
426 Eds = Endian;
427
428 if (N != M || Eds != Endian || IsTypeChar(Buf_Type)) {
429 Buff = (char*)PlugSubAlloc(g, NULL, M);
430 memset(Buff, 0, M);
431 Lim = MY_MIN(N, M);
432 } else
433 Eds = 0; // New format is a no op
434
435 } // endif Eds
436
437 } // end of BINCOL constructor
438
439/***********************************************************************/
440/* BINCOL constructor used for copying columns. */
441/* tdbp is the pointer to the new table descriptor. */
442/***********************************************************************/
443BINCOL::BINCOL(BINCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
444 {
445 Eds = col1->Eds;
446 Fmt = col1->Fmt;
447 N = col1->N;
448 M = col1->M;
449 Lim = col1->Lim;
450 } // end of BINCOL copy constructor
451
452/***********************************************************************/
453/* Set Endian according to the host setting. */
454/***********************************************************************/
455void BINCOL::SetEndian(void)
456 {
457 union {
458 short S;
459 char C[sizeof(short)];
460 };
461
462 S = 1;
463 Endian = (C[0] == 1) ? 'L' : 'B';
464 } // end of SetEndian
465
466/***********************************************************************/
467/* ReadColumn: what this routine does is to access the last line */
468/* read from the corresponding table and extract from it the field */
469/* corresponding to this column. */
470/***********************************************************************/
471void BINCOL::ReadColumn(PGLOBAL g)
472 {
473 char *p = NULL;
474 int rc;
475 PTDBFIX tdbp = (PTDBFIX)To_Tdb;
476
477 if (trace(2))
478 htrc("BIN ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
479 Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);
480
481 /*********************************************************************/
482 /* If physical reading of the line was deferred, do it now. */
483 /*********************************************************************/
484 if (!tdbp->IsRead())
485 if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
486 if (rc == RC_EF)
487 sprintf(g->Message, MSG(INV_DEF_READ), rc);
488
489 throw 11;
490 } // endif
491
492 p = tdbp->To_Line + Deplac;
493
494 /*********************************************************************/
495 /* Set Value from the line field. */
496 /*********************************************************************/
497 if (Eds) {
498 for (int i = 0; i < Lim; i++)
499 if (Eds == 'B' && Endian == 'L')
500 Buff[i] = p[N - i - 1];
501 else if (Eds == 'L' && Endian == 'B')
502 Buff[M - i - 1] = p[i];
503 else if (Endian == 'B')
504 Buff[M - i - 1] = p[N - i - 1];
505 else
506 Buff[i] = p[i];
507
508 p = Buff;
509 } // endif Eds
510
511 switch (Fmt) {
512 case 'X': // Standard not converted values
513 if (Eds && IsTypeChar(Buf_Type))
514 Value->SetValueNonAligned<longlong>(p);
515 else
516 Value->SetBinValue(p);
517
518 break;
519 case 'S': // Short integer
520 Value->SetValueNonAligned<short>(p);
521 break;
522 case 'T': // Tiny integer
523 Value->SetValue(*p);
524 break;
525 case 'I': // Integer
526 Value->SetValueNonAligned<int>(p);
527 break;
528 case 'G': // Large (great) integer
529 Value->SetValueNonAligned<longlong>(p);
530 break;
531 case 'F': // Float
532 case 'R': // Real
533 Value->SetValueNonAligned<float>(p);
534 break;
535 case 'D': // Double
536 Value->SetValueNonAligned<double>(p);
537 break;
538 case 'C': // Text
539 if (Value->SetValue_char(p, Long)) {
540 sprintf(g->Message, "Out of range value for column %s at row %d",
541 Name, tdbp->RowNumber(g));
542 PushWarning(g, tdbp);
543 } // endif SetValue_char
544
545 break;
546 default:
547 sprintf(g->Message, MSG(BAD_BIN_FMT), Fmt, Name);
548 throw 11;
549 } // endswitch Fmt
550
551 // Set null when applicable
552 if (Nullable)
553 Value->SetNull(Value->IsZero());
554
555 } // end of ReadColumn
556
557/***********************************************************************/
558/* WriteColumn: what this routine does is to access the last line */
559/* read from the corresponding table, and rewrite the field */
560/* corresponding to this column from the column buffer. */
561/***********************************************************************/
562void BINCOL::WriteColumn(PGLOBAL g)
563 {
564 char *p, *s;
565 longlong n;
566 PTDBFIX tdbp = (PTDBFIX)To_Tdb;
567
568 if (trace(1)) {
569 htrc("BIN WriteColumn: col %s R%d coluse=%.4X status=%.4X",
570 Name, tdbp->GetTdb_No(), ColUse, Status);
571 htrc(" Lrecl=%d\n", tdbp->Lrecl);
572 htrc("Long=%d deplac=%d coltype=%d ftype=%c\n",
573 Long, Deplac, Buf_Type, *Format.Type);
574 } // endif trace
575
576 /*********************************************************************/
577 /* Check whether the new value has to be converted to Buf_Type. */
578 /*********************************************************************/
579 if (Value != To_Val)
580 Value->SetValue_pval(To_Val, false); // Convert the updated value
581
582 p = (Eds) ? Buff : tdbp->To_Line + Deplac;
583
584 /*********************************************************************/
585 /* Check whether updating is Ok, meaning col value is not too long. */
586 /* Updating will be done only during the second pass (Status=true) */
587 /* Conversion occurs if the external format Fmt is specified. */
588 /*********************************************************************/
589 switch (Fmt) {
590 case 'X':
591 // Standard not converted values
592 if (Eds && IsTypeChar(Buf_Type)) {
593 if (Status)
594 Value->GetValueNonAligned<longlong>(p, Value->GetBigintValue());
595 } else if (Value->GetBinValue(p, Long, Status)) {
596 sprintf(g->Message, MSG(BIN_F_TOO_LONG),
597 Name, Value->GetSize(), Long);
598 throw 31;
599 } // endif p
600
601 break;
602 case 'S': // Short integer
603 n = Value->GetBigintValue();
604
605 if (n > 32767LL || n < -32768LL) {
606 sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name);
607 throw 31;
608 } else if (Status)
609 Value->GetValueNonAligned<short>(p, (short)n);
610
611 break;
612 case 'T': // Tiny integer
613 n = Value->GetBigintValue();
614
615 if (n > 255LL || n < -256LL) {
616 sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name);
617 throw 31;
618 } else if (Status)
619 *p = (char)n;
620
621 break;
622 case 'I': // Integer
623 n = Value->GetBigintValue();
624
625 if (n > INT_MAX || n < INT_MIN) {
626 sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name);
627 throw 31;
628 } else if (Status)
629 Value->GetValueNonAligned<int>(p, (int)n);
630
631 break;
632 case 'G': // Large (great) integer
633 if (Status)
634 *(longlong *)p = Value->GetBigintValue();
635
636 break;
637 case 'F': // Float
638 case 'R': // Real
639 if (Status)
640 Value->GetValueNonAligned<float>(p, (float)Value->GetFloatValue());
641
642 break;
643 case 'D': // Double
644 if (Status)
645 Value->GetValueNonAligned<double>(p, Value->GetFloatValue());
646
647 break;
648 case 'C': // Characters
649 if ((n = (signed)strlen(Value->GetCharString(Buf))) > Long) {
650 sprintf(g->Message, MSG(BIN_F_TOO_LONG), Name, (int) n, Long);
651 throw 31;
652 } // endif n
653
654 if (Status) {
655 s = Value->GetCharString(Buf);
656 memset(p, ' ', Long);
657 memcpy(p, s, strlen(s));
658 } // endif Status
659
660 break;
661 default:
662 sprintf(g->Message, MSG(BAD_BIN_FMT), Fmt, Name);
663 throw 31;
664 } // endswitch Fmt
665
666 if (Eds && Status) {
667 p = tdbp->To_Line + Deplac;
668
669 for (int i = 0; i < Lim; i++)
670 if (Eds == 'B' && Endian == 'L')
671 p[N - i - 1] = Buff[i];
672 else if (Eds == 'L' && Endian == 'B')
673 p[i] = Buff[M - i - 1];
674 else if (Endian == 'B')
675 p[N - i - 1] = Buff[M - i - 1];
676 else
677 p[i] = Buff[i];
678
679 } // endif Eds
680
681 } // end of WriteColumn
682
683/* ------------------------ End of TabFix ---------------------------- */
684