1/*********** File AM Fix C++ Program Source Code File (.CPP) ***********/
2/* PROGRAM NAME: FILAMFIX */
3/* ------------- */
4/* Version 1.6 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the FIX/BIN file access method classes. */
13/* */
14/***********************************************************************/
15
16/***********************************************************************/
17/* Include relevant sections of the System 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 <errno.h>
31#include <unistd.h>
32#else // !UNIX
33#include <io.h>
34#endif // !UNIX
35#include <sys/stat.h>
36#include <fcntl.h>
37#endif // !__WIN__
38
39/***********************************************************************/
40/* Include application header files: */
41/* global.h is header containing all global declarations. */
42/* plgdbsem.h is header containing the DB application declarations. */
43/* filamfix.h is header containing the file AM classes declarations. */
44/***********************************************************************/
45#include "global.h"
46#include "plgdbsem.h"
47#include "filamfix.h"
48#include "tabdos.h"
49#include "tabfix.h"
50#include "osutil.h"
51
52#ifndef INVALID_SET_FILE_POINTER
53#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
54#endif
55
56extern int num_read, num_there, num_eq[2]; // Statistics
57
58/* --------------------------- Class FIXFAM -------------------------- */
59
60/***********************************************************************/
61/* Constructors. */
62/***********************************************************************/
63FIXFAM::FIXFAM(PDOSDEF tdp) : BLKFAM(tdp)
64 {
65 Blksize = tdp->GetBlksize();
66 Padded = tdp->GetPadded();
67
68 if (Padded && Blksize)
69 Nrec = Blksize / Lrecl;
70 else {
71 Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
72 Blksize = Nrec * Lrecl;
73 Padded = false;
74 } // endelse
75
76 } // end of FIXFAM standard constructor
77
78FIXFAM::FIXFAM(PFIXFAM txfp) : BLKFAM(txfp)
79 {
80 } // end of FIXFAM copy constructor
81
82/***********************************************************************/
83/* SetPos: Replace the table at the specified position. */
84/***********************************************************************/
85bool FIXFAM::SetPos(PGLOBAL g, int pos)
86 {
87 if (pos < 0) {
88 strcpy(g->Message, MSG(INV_REC_POS));
89 return true;
90 } // endif recpos
91
92 CurBlk = pos / Nrec;
93 CurNum = pos % Nrec;
94#if defined(_DEBUG)
95 num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
96#endif
97
98 // Indicate the table position was externally set
99 Placed = true;
100 return false;
101 } // end of SetPos
102
103/***********************************************************************/
104/* Initialize CurBlk and CurNum for indexed DELETE. */
105/***********************************************************************/
106int FIXFAM::InitDelete(PGLOBAL, int fpos, int)
107 {
108 CurBlk = fpos / Nrec;
109 CurNum = fpos % Nrec;
110 return RC_OK;
111 } // end of InitDelete
112
113/***********************************************************************/
114/* Allocate the block buffer for the table. */
115/***********************************************************************/
116bool FIXFAM::AllocateBuffer(PGLOBAL g)
117 {
118 Buflen = Blksize;
119 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
120
121 if (UseTemp || Tdbp->GetMode() == MODE_DELETE) {
122 if (Padded) {
123 strcpy(g->Message, MSG(NO_MODE_PADDED));
124 return true;
125 } // endif Padded
126
127 // Allocate a separate buffer so block reading can be kept
128 Dbflen = Nrec;
129 DelBuf = PlugSubAlloc(g, NULL, Blksize);
130 } else if (Tdbp->GetMode() == MODE_INSERT) {
131 /*******************************************************************/
132 /* For Insert the buffer must be prepared. */
133 /*******************************************************************/
134 if (Tdbp->GetFtype() == RECFM_BIN) {
135 // The buffer must be prepared depending on column types
136 int n = 0;
137 bool b = false;
138 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
139// PCOLDEF cdp;
140 PBINCOL colp;
141
142 // Prepare the first line of the buffer
143 memset(To_Buf, 0, Buflen);
144
145#if 0
146 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) {
147 if (!IsTypeNum(cdp->GetType())) {
148 memset(To_Buf + cdp->GetOffset(), ' ', cdp->GetClen());
149 b = true;
150 } // endif not num
151
152 n = MY_MAX(n, cdp->GetOffset() + cdp->GetClen());
153 } // endfor cdp
154#endif // 0
155
156 for (colp = (PBINCOL)Tdbp->GetColumns(); colp;
157 colp = (PBINCOL)colp->GetNext())
158 if (!colp->IsSpecial()) {
159 if (!IsTypeNum(colp->GetResultType())) {
160 memset(To_Buf + colp->GetDeplac(), ' ', colp->GetLength());
161 b = true;
162 } // endif not num
163
164 n = MY_MAX(n, colp->GetDeplac() + colp->GetFileSize());
165 } // endif !special
166
167 // We do this for binary table because the lrecl can have been
168 // specified with additional space to include line ending.
169 if (n < Lrecl && Ending) {
170 To_Buf[Lrecl - 1] = '\n';
171
172 if (n < Lrecl - 1 && Ending == 2)
173 To_Buf[Lrecl - 2] = '\r';
174
175 } // endif n
176
177 if (b)
178 // Now repeat this for the whole buffer
179 for (int len = Lrecl; len <= Buflen - Lrecl; len += Lrecl)
180 memcpy(To_Buf + len, To_Buf, Lrecl);
181
182 } else {
183 memset(To_Buf, ' ', Buflen);
184
185 if (!Padded)
186 // The file is physically a text file.
187 for (int len = Lrecl; len <= Buflen; len += Lrecl) {
188 if (Ending == 2)
189 To_Buf[len - 2] = '\r';
190
191 To_Buf[len - 1] = '\n';
192 } // endfor len
193
194 } // endif Ftype
195
196 Rbuf = Nrec; // To be used by WriteDB
197 } // endif Insert
198
199 return false;
200 } // end of AllocateBuffer
201
202/***********************************************************************/
203/* Reset buffer access according to indexing and to mode. */
204/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
205/***********************************************************************/
206void FIXFAM::ResetBuffer(PGLOBAL g)
207 {
208 /*********************************************************************/
209 /* If access is random, performances can be much better when the */
210 /* reads are done on only one row, except for small tables that can */
211 /* be entirely read in one block. */
212 /*********************************************************************/
213 if (Tdbp->GetKindex() && ReadBlks != 1 && !Padded) {
214 Nrec = 1; // Better for random access
215 Rbuf = 0;
216 Blksize = Lrecl;
217 OldBlk = -2; // Has no meaning anymore
218 Block = Tdbp->Cardinality(g); // Blocks are one line now
219 } // endif Mode
220
221 } // end of ResetBuffer
222
223/***********************************************************************/
224/* WriteModifiedBlock: Used when updating. */
225/***********************************************************************/
226int FIXFAM::WriteModifiedBlock(PGLOBAL g)
227 {
228 /*********************************************************************/
229 /* The old block was modified in Update mode. */
230 /* In Update mode we simply rewrite the old block on itself. */
231 /*********************************************************************/
232 int rc = RC_OK;
233 bool moved = false;
234
235 // Using temp copy any intermediate lines.
236 if (UseTemp && MoveIntermediateLines(g, &moved))
237 rc = RC_FX;
238
239 // Fpos is last position, Headlen is DBF file header length
240 else if (!moved && fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
241 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
242 rc = RC_FX;
243 } else if (fwrite(To_Buf, Lrecl, Rbuf, T_Stream) != (size_t)Rbuf) {
244 sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
245 rc = RC_FX;
246 } else
247 Spos = Fpos + Nrec; // + Rbuf ???
248
249 if (Closing || rc != RC_OK) { // Error or called from CloseDB
250 Closing = true; // To tell CloseDB about error
251 return rc;
252 } // endif Closing
253
254 // NOTE: Next line was added to avoid a very strange fread bug.
255 // When the fseek is not executed (even the file has the good
256 // pointer position) the next read can happen anywhere in the file.
257 OldBlk = -2; // This will force fseek to be executed
258 Modif = 0;
259 return rc;
260 } // end of WriteModifiedBlock
261
262/***********************************************************************/
263/* ReadBuffer: Read one line for a FIX file. */
264/***********************************************************************/
265int FIXFAM::ReadBuffer(PGLOBAL g)
266 {
267 int n, rc = RC_OK;
268
269 /*********************************************************************/
270 /* Sequential reading when Placed is not true. */
271 /*********************************************************************/
272 if (Placed) {
273 Tdbp->SetLine(To_Buf + CurNum * Lrecl);
274 Placed = false;
275 } else if (++CurNum < Rbuf) {
276 Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
277 return RC_OK;
278 } else if (Rbuf < Nrec && CurBlk != -1) {
279 return RC_EF;
280 } else {
281 /*******************************************************************/
282 /* New block. */
283 /*******************************************************************/
284 CurNum = 0;
285 Tdbp->SetLine(To_Buf);
286
287 next:
288 if (++CurBlk >= Block)
289 return RC_EF;
290
291 /*******************************************************************/
292 /* Before reading a new block, check whether block indexing */
293 /* can be done, as well as for join as for local filtering. */
294 /*******************************************************************/
295 switch (Tdbp->TestBlock(g)) {
296 case RC_EF:
297 return RC_EF;
298 case RC_NF:
299 goto next;
300 } // endswitch rc
301 } // endif's
302
303 if (OldBlk == CurBlk) {
304 IsRead = true; // Was read indeed
305 return RC_OK; // Block is already there
306 } // endif OldBlk
307
308 // Write modified block in mode UPDATE
309 if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
310 return rc;
311
312 // This could be done only for new block. However note that FPOS
313 // is used as block position when updating and as line position
314 // when deleting so this has to be carefully checked.
315 Fpos = CurBlk * Nrec; // Fpos is new line position
316
317 // fseek is required only in non sequential reading
318 if (CurBlk != OldBlk + 1)
319 // Note: Headlen is for DBF tables
320 if (fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) {
321 sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
322 return RC_FX;
323 } // endif fseek
324
325 if (trace(2))
326 htrc("File position is now %d\n", ftell(Stream));
327
328 if (Padded)
329 n = fread(To_Buf, (size_t)Blksize, 1, Stream);
330 else
331 n = fread(To_Buf, (size_t)Lrecl, (size_t)Nrec, Stream);
332
333 if (n) {
334 rc = RC_OK;
335 Rbuf = (Padded) ? n * Nrec : n;
336 ReadBlks++;
337 num_read++;
338 } else if (feof(Stream)) {
339 rc = RC_EF;
340 } else {
341#if defined(__WIN__)
342 sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
343#else
344 sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
345#endif
346
347 if (trace(1))
348 htrc("%s\n", g->Message);
349
350 return RC_FX;
351 } // endelse
352
353 OldBlk = CurBlk; // Last block actually read
354 IsRead = true; // Is read indeed
355 return rc;
356 } // end of ReadBuffer
357
358/***********************************************************************/
359/* WriteBuffer: File write routine for FIX access method. */
360/* Updates are written into the (Temp) file in ReadBuffer. */
361/***********************************************************************/
362int FIXFAM::WriteBuffer(PGLOBAL g)
363 {
364 if (trace(2))
365 htrc("FIX WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n",
366 Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum);
367
368 if (Tdbp->GetMode() == MODE_INSERT) {
369 /*******************************************************************/
370 /* In Insert mode, blocs are added sequentialy to the file end. */
371 /*******************************************************************/
372 if (++CurNum != Rbuf) {
373 Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
374 return RC_OK; // We write only full blocks
375 } // endif CurNum
376
377 if (trace(2))
378 htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf);
379
380 // Now start the writing process.
381 if (fwrite(To_Buf, Lrecl, Rbuf, Stream) != (size_t)Rbuf) {
382 sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
383 Closing = true; // To tell CloseDB about a Write error
384 return RC_FX;
385 } // endif size
386
387 CurBlk++;
388 CurNum = 0;
389 Tdbp->SetLine(To_Buf);
390
391 if (trace(2))
392 htrc("write done\n");
393
394 } else { // Mode == MODE_UPDATE
395 // T_Stream is the temporary stream or the table file stream itself
396 if (!T_Stream) {
397 if (UseTemp) {
398 if (OpenTempFile(g))
399 return RC_FX;
400 else if (CopyHeader(g)) // For DBF tables
401 return RC_FX;
402
403 } else
404 T_Stream = Stream;
405
406 } // endif T_Stream
407
408 if (Nrec > 1)
409 Modif++; // Modified line in blocked mode
410 else if (WriteModifiedBlock(g)) // Indexed update
411 return RC_FX;
412
413 } // endif Mode
414
415 return RC_OK;
416 } // end of WriteBuffer
417
418/***********************************************************************/
419/* Data Base delete line routine for FIXFAM access method. */
420/***********************************************************************/
421int FIXFAM::DeleteRecords(PGLOBAL g, int irc)
422 {
423 bool moved;
424
425 /*********************************************************************/
426 /* There is an alternative here: */
427 /* 1 - use a temporary file in which are copied all not deleted */
428 /* lines, at the end the original file will be deleted and */
429 /* the temporary file renamed to the original file name. */
430 /* 2 - directly move the not deleted lines inside the original */
431 /* file, and at the end erase all trailing records. */
432 /* This will be experimented. */
433 /*********************************************************************/
434 if (trace(2))
435 htrc("DOS DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
436 irc, UseTemp, Fpos, Tpos, Spos);
437
438 if (irc != RC_OK) {
439 /*******************************************************************/
440 /* EOF: position Fpos at the end-of-file position. */
441 /*******************************************************************/
442 Fpos = Tdbp->Cardinality(g);
443
444 if (trace(2))
445 htrc("Fpos placed at file end=%d\n", Fpos);
446
447 } else // Fpos is the deleted line position
448 Fpos = CurBlk * Nrec + CurNum;
449
450 if (Tpos == Spos) {
451 /*******************************************************************/
452 /* First line to delete. */
453 /*******************************************************************/
454 if (UseTemp) {
455 /*****************************************************************/
456 /* Open temporary file, lines before this will be moved. */
457 /*****************************************************************/
458 if (OpenTempFile(g))
459 return RC_FX;
460
461 } else {
462 /*****************************************************************/
463 /* Move of eventual preceding lines is not required here. */
464 /* Set the target file as being the source file itself. */
465 /* Set the future Tpos, and give Spos a value to block moving. */
466 /*****************************************************************/
467 T_Stream = Stream;
468 Spos = Tpos = Fpos;
469 } // endif UseTemp
470
471 } // endif Tpos == Spos
472
473 /*********************************************************************/
474 /* Move any intermediate lines. */
475 /*********************************************************************/
476 if (MoveIntermediateLines(g, &moved))
477 return RC_FX;
478
479 if (irc == RC_OK) {
480 /*******************************************************************/
481 /* Reposition the file pointer and set Spos. */
482 /*******************************************************************/
483 Spos = Fpos + 1; // New start position is on next line
484
485 if (moved) {
486 if (fseek(Stream, Spos * Lrecl, SEEK_SET)) {
487 sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
488 return RC_FX;
489 } // endif fseek
490
491 OldBlk = -2; // To force fseek to be executed on next block
492 } // endif moved
493
494 if (trace(2))
495 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
496
497 } else {
498 /*******************************************************************/
499 /* Last call after EOF has been reached. */
500 /*******************************************************************/
501 if (UseTemp) {
502 /*****************************************************************/
503 /* Ok, now delete old file and rename new temp file. */
504 /*****************************************************************/
505 if (RenameTempFile(g))
506 return RC_FX;
507
508 } else {
509 /*****************************************************************/
510 /* Because the chsize functionality is only accessible with a */
511 /* system call we must close the file and reopen it with the */
512 /* open function (_fopen for MS ??) this is still to be checked */
513 /* for compatibility with Text files and other OS's. */
514 /*****************************************************************/
515 char filename[_MAX_PATH];
516 int h;
517
518 /*rc= */PlugCloseFile(g, To_Fb);
519 PlugSetPath(filename, To_File, Tdbp->GetPath());
520
521 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
522 return RC_FX;
523
524 /*****************************************************************/
525 /* Remove extra records. */
526 /*****************************************************************/
527#if defined(UNIX)
528 if (ftruncate(h, (off_t)(Tpos * Lrecl))) {
529 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
530 close(h);
531 return RC_FX;
532 } // endif
533#else
534 if (chsize(h, Tpos * Lrecl)) {
535 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
536 close(h);
537 return RC_FX;
538 } // endif
539#endif
540
541 close(h);
542
543 if (trace(2))
544 htrc("done, h=%d irc=%d\n", h, irc);
545
546 } // endif UseTemp
547
548 } // endif irc
549
550 return RC_OK; // All is correct
551 } // end of DeleteRecords
552
553/***********************************************************************/
554/* Move intermediate deleted or updated lines. */
555/* This works only for file open in binary mode. */
556/***********************************************************************/
557bool FIXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
558 {
559 int n;
560 size_t req, len;
561
562 for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
563 /*******************************************************************/
564 /* Non consecutive line to delete. Move intermediate lines. */
565 /*******************************************************************/
566 if (!UseTemp || !*b)
567 if (fseek(Stream, Headlen + Spos * Lrecl, SEEK_SET)) {
568 sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
569 return true;
570 } // endif
571
572 req = (size_t)MY_MIN(n, Dbflen);
573 len = fread(DelBuf, Lrecl, req, Stream);
574
575 if (trace(2))
576 htrc("after read req=%d len=%d\n", req, len);
577
578 if (len != req) {
579 sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
580 return true;
581 } // endif len
582
583 if (!UseTemp) // Delete mode, cannot be a DBF file
584 if (fseek(T_Stream, Tpos * Lrecl, SEEK_SET)) {
585 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
586 return true;
587 } // endif
588
589 if ((len = fwrite(DelBuf, Lrecl, req, T_Stream)) != req) {
590 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
591 return true;
592 } // endif
593
594 if (trace(2))
595 htrc("after write pos=%d\n", ftell(Stream));
596
597 Tpos += (int)req;
598 Spos += (int)req;
599
600 if (trace(2))
601 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
602
603 *b = true;
604 } // endfor n
605
606 return false;
607 } // end of MoveIntermediate Lines
608
609/***********************************************************************/
610/* Table file close routine for FIX access method. */
611/***********************************************************************/
612void FIXFAM::CloseTableFile(PGLOBAL g, bool abort)
613 {
614 int rc = RC_OK, wrc = RC_OK;
615 MODE mode = Tdbp->GetMode();
616
617 Abort = abort;
618
619 // Closing is True if last Write was in error
620 if (mode == MODE_INSERT && CurNum && !Closing) {
621 // Some more inserted lines remain to be written
622 Rbuf = CurNum--;
623 wrc = WriteBuffer(g);
624 } else if (mode == MODE_UPDATE) {
625 if (Modif && !Closing) {
626 // Last updated block remains to be written
627 Closing = true; // ???
628 wrc = WriteModifiedBlock(g);
629 } // endif Modif
630
631 if (UseTemp && T_Stream && wrc == RC_OK) {
632 if (!Abort) {
633 // Copy any remaining lines
634 bool b;
635
636 Fpos = Tdbp->Cardinality(g);
637 Abort = MoveIntermediateLines(g, &b) != RC_OK;
638 } // endif Abort
639
640 // Delete the old file and rename the new temp file.
641 RenameTempFile(g);
642 goto fin;
643 } // endif UseTemp
644
645 } // endif's mode
646
647 // Finally close the file
648 rc = PlugCloseFile(g, To_Fb);
649
650 fin:
651 if (trace(1))
652 htrc("FIX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
653 To_File, mode, wrc, rc);
654
655 Stream = NULL; // So we can know whether table is open
656 } // end of CloseTableFile
657
658/* ------------------------- Class BGXFAM ---------------------------- */
659
660/***********************************************************************/
661/* Implementation of the BGXFAM class. */
662/* This is the FAM class for FIX tables of more than 2 gigabytes. */
663/***********************************************************************/
664BGXFAM::BGXFAM(PDOSDEF tdp) : FIXFAM(tdp)
665 {
666 Hfile = INVALID_HANDLE_VALUE;
667 Tfile = INVALID_HANDLE_VALUE;
668 } // end of BGXFAM constructor
669
670BGXFAM::BGXFAM(PBGXFAM txfp) : FIXFAM(txfp)
671 {
672 Hfile = txfp->Hfile;
673 Tfile = txfp->Tfile;
674 } // end of BGXFAM copy constructor
675
676/***********************************************************************/
677/* Set current position in a big file. */
678/***********************************************************************/
679bool BGXFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, int org)
680 {
681#if defined(__WIN__)
682 char buf[256];
683 DWORD drc;
684 LARGE_INTEGER of;
685
686 of.QuadPart = pos;
687 of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, org);
688
689 if (of.LowPart == INVALID_SET_FILE_POINTER &&
690 (drc = GetLastError()) != NO_ERROR) {
691 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
692 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
693 (LPTSTR)buf, sizeof(buf), NULL);
694 sprintf(g->Message, MSG(SFP_ERROR), buf);
695 return true;
696 } // endif
697#else // !__WIN__
698 if (lseek64(h, pos, org) < 0) {
699// sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
700 sprintf(g->Message, "lseek64: %s", strerror(errno));
701 printf("%s\n", g->Message);
702 return true;
703 } // endif
704#endif // !__WIN__
705
706 return false;
707 } // end of BigSeek
708
709/***********************************************************************/
710/* Read from a big file. */
711/***********************************************************************/
712int BGXFAM::BigRead(PGLOBAL g __attribute__((unused)),
713 HANDLE h, void *inbuf, int req)
714 {
715 int rc;
716
717#if defined(__WIN__)
718 DWORD nbr, drc, len = (DWORD)req;
719 bool brc = ReadFile(h, inbuf, len, &nbr, NULL);
720
721 if (trace(2))
722 htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
723
724 if (!brc) {
725 char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile";
726
727 drc = GetLastError();
728 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
729 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
730 (LPTSTR)buf, sizeof(buf), NULL);
731 sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
732
733 if (trace(2))
734 htrc("BIGREAD: %s\n", g->Message);
735
736 rc = -1;
737 } else
738 rc = (int)nbr;
739#else // !__WIN__
740 size_t len = (size_t)req;
741 ssize_t nbr = read(h, inbuf, len);
742
743 rc = (int)nbr;
744#endif // !__WIN__
745
746 return rc;
747 } // end of BigRead
748
749/***********************************************************************/
750/* Write into a big file. */
751/***********************************************************************/
752bool BGXFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
753 {
754 bool rc = false;
755
756#if defined(__WIN__)
757 DWORD nbw, drc, len = (DWORD)req;
758 bool brc = WriteFile(h, inbuf, len, &nbw, NULL);
759
760 if (trace(2))
761 htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
762
763 if (!brc || nbw != len) {
764 char buf[256];
765 PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
766
767 if (brc)
768 strcpy(buf, MSG(BAD_BYTE_NUM));
769 else {
770 drc = GetLastError();
771 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
772 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
773 (LPTSTR)buf, sizeof(buf), NULL);
774 } // endelse brc
775
776 sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
777
778 if (trace(2))
779 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
780 nbw, len, drc, g->Message);
781
782 rc = true;
783 } // endif brc || nbw
784#else // !__WIN__
785 size_t len = (size_t)req;
786 ssize_t nbw = write(h, inbuf, len);
787
788 if (nbw != (ssize_t)len) {
789 const char *fn = (h == Hfile) ? To_File : "Tempfile";
790
791 sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
792
793 if (trace(2))
794 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
795 nbw, len, errno, g->Message);
796
797 rc = true;
798 } // endif nbr
799#endif // !__WIN__
800
801 return rc;
802 } // end of BigWrite
803
804#if 0
805/***********************************************************************/
806/* Reset: reset position values at the beginning of file. */
807/***********************************************************************/
808void BGXFAM::Reset(void)
809 {
810 FIXFAM::Reset();
811 Xpos = 0;
812 } // end of Reset
813#endif // 0
814
815/***********************************************************************/
816/* OpenTableFile: opens a huge file using Windows/Unix API's. */
817/***********************************************************************/
818bool BGXFAM::OpenTableFile(PGLOBAL g)
819 {
820 char filename[_MAX_PATH];
821 MODE mode = Tdbp->GetMode();
822 PDBUSER dbuserp = PlgGetUser(g);
823
824 if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
825 sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
826 return true;
827 } // endif
828
829 PlugSetPath(filename, To_File, Tdbp->GetPath());
830
831 if (trace(1))
832 htrc("OpenTableFile: filename=%s mode=%d\n", filename, mode);
833
834#if defined(__WIN__)
835 DWORD rc, access, creation, share = 0;
836
837 /*********************************************************************/
838 /* Create the file object according to access mode */
839 /*********************************************************************/
840 switch (mode) {
841 case MODE_READ:
842 access = GENERIC_READ;
843 share = FILE_SHARE_READ;
844 creation = OPEN_EXISTING;
845 break;
846 case MODE_DELETE:
847 if (!Tdbp->GetNext()) {
848 // Store the number of deleted rows
849 DelRows = Cardinality(g);
850
851 // This will delete the whole file and provoque ReadDB to
852 // return immediately.
853 access = GENERIC_READ | GENERIC_WRITE;
854 creation = TRUNCATE_EXISTING;
855 Tdbp->ResetSize();
856 Headlen = 0;
857 break;
858 } // endif
859
860 // Selective delete, pass thru
861 case MODE_UPDATE:
862 if ((UseTemp = Tdbp->IsUsingTemp(g)))
863 access = GENERIC_READ;
864 else
865 access = GENERIC_READ | GENERIC_WRITE;
866
867 creation = OPEN_EXISTING;
868 break;
869 case MODE_INSERT:
870 access = GENERIC_WRITE;
871 creation = OPEN_ALWAYS;
872 break;
873 default:
874 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
875 return true;
876 } // endswitch
877
878 Hfile = CreateFile(filename, access, share, NULL, creation,
879 FILE_ATTRIBUTE_NORMAL, NULL);
880
881 if (Hfile == INVALID_HANDLE_VALUE) {
882 rc = GetLastError();
883 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
884 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
885 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
886 (LPTSTR)filename, sizeof(filename), NULL);
887 strcat(g->Message, filename);
888 } else
889 rc = 0;
890
891 if (trace(2))
892 htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
893 rc, access, share, creation, Hfile, filename);
894
895 if (mode == MODE_INSERT)
896 /*******************************************************************/
897 /* In Insert mode we must position the cursor at end of file. */
898 /*******************************************************************/
899 if (BigSeek(g, Hfile, (BIGINT)0, FILE_END))
900 return true;
901
902#else // UNIX
903 int rc = 0;
904 int oflag = O_LARGEFILE; // Enable file size > 2G
905 mode_t tmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
906
907 /*********************************************************************/
908 /* Create the file object according to access mode */
909 /*********************************************************************/
910 switch (mode) {
911 case MODE_READ:
912 oflag |= O_RDONLY;
913 break;
914 case MODE_DELETE:
915 if (!Tdbp->GetNext()) {
916 // This will delete the whole file and provoque ReadDB to
917 // return immediately.
918 oflag |= (O_RDWR | O_TRUNC);
919 Tdbp->ResetSize();
920 break;
921 } // endif
922
923 // Selective delete
924 /* fall through */
925 case MODE_UPDATE:
926 UseTemp = Tdbp->IsUsingTemp(g);
927 oflag |= (UseTemp) ? O_RDONLY : O_RDWR;
928 break;
929 case MODE_INSERT:
930 oflag |= (O_WRONLY | O_CREAT | O_APPEND);
931 // tmode = S_IREAD | S_IWRITE;
932 break;
933 default:
934 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
935 return true;
936 } // endswitch
937
938 Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, tmode);
939
940 if (Hfile == INVALID_HANDLE_VALUE) {
941 rc = errno;
942 } else
943 rc = 0;
944
945 if (trace(2))
946 htrc(" rc=%d oflag=%p tmode=%p handle=%p fn=%s\n",
947 rc, oflag, tmode, Hfile, filename);
948
949#endif // UNIX
950
951 if (!rc) {
952 if (!To_Fb) {
953 To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
954 To_Fb->Fname = To_File;
955 To_Fb->Type = TYPE_FB_HANDLE;
956 To_Fb->Memory = NULL;
957 To_Fb->Length = 0;
958 To_Fb->Mode = mode;
959 To_Fb->File = NULL;
960 To_Fb->Next = dbuserp->Openlist;
961 dbuserp->Openlist = To_Fb;
962 } // endif To_Fb
963
964 To_Fb->Count = 1;
965 To_Fb->Mode = mode;
966 To_Fb->Handle = Hfile;
967
968 /*******************************************************************/
969 /* Allocate the block buffer. */
970 /*******************************************************************/
971 return AllocateBuffer(g);
972 } else
973 return (mode == MODE_READ && rc == ENOENT)
974 ? PushWarning(g, Tdbp) : true;
975
976 } // end of OpenTableFile
977
978/***********************************************************************/
979/* BIGFIX Cardinality: returns table cardinality in number of rows. */
980/* This function can be called with a null argument to test the */
981/* availability of Cardinality implementation (1 yes, 0 no). */
982/***********************************************************************/
983int BGXFAM::Cardinality(PGLOBAL g)
984 {
985 if (g) {
986 char filename[_MAX_PATH];
987 int card = -1;
988 BIGINT fsize;
989
990 PlugSetPath(filename, To_File, Tdbp->GetPath());
991
992#if defined(__WIN__) // OB
993 LARGE_INTEGER len;
994 DWORD rc = 0;
995
996 len.QuadPart = -1;
997
998 if (Hfile == INVALID_HANDLE_VALUE) {
999 HANDLE h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
1000 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1001
1002 if (h == INVALID_HANDLE_VALUE)
1003 if ((rc = GetLastError()) != ERROR_FILE_NOT_FOUND) {
1004 sprintf(g->Message, MSG(OPEN_ERROR), rc, 10, filename);
1005 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1006 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
1007 (LPTSTR)filename, sizeof(filename), NULL);
1008 strcat(g->Message, filename);
1009 return -1;
1010 } else
1011 return 0; // File does not exist
1012
1013 // Get the size of the file (can be greater than 4 GB)
1014 len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
1015 CloseHandle(h);
1016 } else
1017 len.LowPart = GetFileSize(Hfile, (LPDWORD)&len.HighPart);
1018
1019 if (len.LowPart == 0xFFFFFFFF && (rc = GetLastError()) != NO_ERROR) {
1020 sprintf(g->Message, MSG(FILELEN_ERROR), "GetFileSize", filename);
1021 return -2;
1022 } else
1023 fsize = len.QuadPart;
1024
1025#else // UNIX
1026 if (Hfile == INVALID_HANDLE_VALUE) {
1027 int h = open64(filename, O_RDONLY, 0);
1028
1029 if (trace(1))
1030 htrc(" h=%d\n", h);
1031
1032 if (h == INVALID_HANDLE_VALUE) {
1033 if (trace(1))
1034 htrc(" errno=%d ENOENT=%d\n", errno, ENOENT);
1035
1036 if (errno != ENOENT) {
1037 sprintf(g->Message, MSG(OPEN_ERROR_IS),
1038 filename, strerror(errno));
1039 return -1;
1040 } else
1041 return 0; // File does not exist
1042
1043 } // endif h
1044
1045 // Get the size of the file (can be greater than 4 GB)
1046 fsize = lseek64(h, 0, SEEK_END);
1047 close(h);
1048 } else {
1049 BIGINT curpos = lseek64(Hfile, 0, SEEK_CUR);
1050
1051 fsize = lseek64(Hfile, 0, SEEK_END);
1052 lseek64(Hfile, curpos, SEEK_SET);
1053 } // endif Hfile
1054
1055 if (fsize < 0) {
1056 sprintf(g->Message, MSG(FILELEN_ERROR), "lseek64", filename);
1057 return -2;
1058 } // endif fsize
1059
1060#endif // UNIX
1061
1062 // Check the real size of the file
1063 if (Padded && Blksize) {
1064 if (fsize % (BIGINT)Blksize) {
1065 sprintf(g->Message, MSG(NOT_FIXED_LEN),
1066 filename, (int)fsize, Lrecl);
1067 return -3;
1068 } else
1069 card = (int)(fsize / (BIGINT)Blksize) * Nrec;
1070
1071 } else if (fsize % (BIGINT)Lrecl) {
1072 sprintf(g->Message, MSG(NOT_FIXED_LEN), filename, (int)fsize, Lrecl);
1073 return -3;
1074 } else
1075 card = (int)(fsize / (BIGINT)Lrecl); // Fixed length file
1076
1077 if (trace(1))
1078 htrc(" Computed max_K=%d fsize=%lf lrecl=%d\n",
1079 card, (double)fsize, Lrecl);
1080
1081 // Set number of blocks for later use
1082 Block = (card + Nrec - 1) / Nrec;
1083 return card;
1084 } else
1085 return -1;
1086
1087 } // end of Cardinality
1088
1089/***********************************************************************/
1090/* WriteModifiedBlock: Used when updating. */
1091/***********************************************************************/
1092int BGXFAM::WriteModifiedBlock(PGLOBAL g)
1093 {
1094 /*********************************************************************/
1095 /* The old block was modified in Update mode. */
1096 /* In Update mode we simply rewrite the old block on itself. */
1097 /*********************************************************************/
1098 int rc = RC_OK;
1099 bool moved = false;
1100
1101 if (UseTemp) // Copy any intermediate lines.
1102 if (MoveIntermediateLines(g, &moved))
1103 rc = RC_FX;
1104
1105 if (rc == RC_OK) {
1106 // Set file position to OldBlk position (Fpos)
1107 if (!moved && BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
1108 rc = RC_FX;
1109 else if (BigWrite(g, Tfile, To_Buf, Lrecl * Rbuf))
1110 rc = RC_FX;
1111
1112 Spos = Fpos + Nrec; // + Rbuf ???
1113 } // endif rc
1114
1115 if (Closing || rc != RC_OK) // Error or called from CloseDB
1116 return rc;
1117
1118 // NOTE: Next line was added to avoid a very strange fread bug.
1119 // When the fseek is not executed (even the file has the good
1120 // pointer position) the next read can happen anywhere in the file.
1121 OldBlk = CurBlk; // This will force fseek to be executed
1122 Modif = 0;
1123 return rc;
1124 } // end of WriteModifiedBlock
1125
1126/***********************************************************************/
1127/* ReadBuffer: Read Nrec lines for a big fixed/binary file. */
1128/***********************************************************************/
1129int BGXFAM::ReadBuffer(PGLOBAL g)
1130 {
1131 int nbr, rc = RC_OK;
1132
1133 /*********************************************************************/
1134 /* Sequential reading when Placed is not true. */
1135 /*********************************************************************/
1136 if (Placed) {
1137 Tdbp->SetLine(To_Buf + CurNum * Lrecl);
1138 Placed = false;
1139 } else if (++CurNum < Rbuf) {
1140 Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
1141 return RC_OK;
1142 } else if (Rbuf < Nrec && CurBlk != -1) {
1143 return RC_EF;
1144 } else {
1145 /*******************************************************************/
1146 /* New block. */
1147 /*******************************************************************/
1148 CurNum = 0;
1149 Tdbp->SetLine(To_Buf);
1150
1151 next:
1152 if (++CurBlk >= Block)
1153 return RC_EF;
1154
1155 /*******************************************************************/
1156 /* Before reading a new block, check whether block optimization */
1157 /* can be done, as well as for join as for local filtering. */
1158 /*******************************************************************/
1159 switch (Tdbp->TestBlock(g)) {
1160 case RC_EF:
1161 return RC_EF;
1162 case RC_NF:
1163 goto next;
1164 } // endswitch rc
1165
1166 } // endif's
1167
1168 if (OldBlk == CurBlk) {
1169 IsRead = true; // Was read indeed
1170 return RC_OK; // Block is already there
1171 } // endif OldBlk
1172
1173 // Write modified block in mode UPDATE
1174 if (Modif && (rc = WriteModifiedBlock(g)) != RC_OK)
1175 return rc;
1176
1177 Fpos = CurBlk * Nrec;
1178
1179 // Setting file pointer is required only in non sequential reading
1180 if (CurBlk != OldBlk + 1)
1181 if (BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl))
1182 return RC_FX;
1183
1184 if (trace(2))
1185 htrc("File position is now %d\n", Fpos);
1186
1187 nbr = BigRead(g, Hfile, To_Buf, (Padded) ? Blksize : Lrecl * Nrec);
1188
1189 if (nbr > 0) {
1190 Rbuf = (Padded) ? Nrec : nbr / Lrecl;
1191 rc = RC_OK;
1192 ReadBlks++;
1193 num_read++;
1194 } else
1195 rc = (nbr == 0) ? RC_EF : RC_FX;
1196
1197 OldBlk = CurBlk; // Last block actually read
1198 IsRead = true; // Is read indeed
1199 return rc;
1200 } // end of ReadBuffer
1201
1202/***********************************************************************/
1203/* WriteBuffer: File write routine for BGXFAM access method. */
1204/* Updates are written into the (Temp) file in ReadBuffer. */
1205/***********************************************************************/
1206int BGXFAM::WriteBuffer(PGLOBAL g)
1207 {
1208 if (trace(2))
1209 htrc("BIG WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n",
1210 Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum);
1211
1212 if (Tdbp->GetMode() == MODE_INSERT) {
1213 /*******************************************************************/
1214 /* In Insert mode, blocks are added sequentialy to the file end. */
1215 /*******************************************************************/
1216 if (++CurNum != Rbuf) {
1217 Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
1218 return RC_OK; // We write only full blocks
1219 } // endif CurNum
1220
1221 if (trace(2))
1222 htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf);
1223
1224 // Now start the writing process.
1225 if (BigWrite(g, Hfile, To_Buf, Lrecl * Rbuf))
1226 return RC_FX;
1227
1228 CurBlk++;
1229 CurNum = 0;
1230 Tdbp->SetLine(To_Buf);
1231
1232 if (trace(2))
1233 htrc("write done\n");
1234
1235 } else { // Mode == MODE_UPDATE
1236 // Tfile is the temporary file or the table file handle itself
1237 if (Tfile == INVALID_HANDLE_VALUE) {
1238 if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
1239 if (OpenTempFile(g))
1240 return RC_FX;
1241
1242 } else
1243 Tfile = Hfile;
1244
1245 } // endif Tfile
1246
1247 if (Nrec > 1)
1248 Modif++; // Modified line in blocked mode
1249 else if (WriteModifiedBlock(g)) // Indexed update
1250 return RC_FX;
1251
1252 } // endif Mode
1253
1254 return RC_OK;
1255 } // end of WriteBuffer
1256
1257/***********************************************************************/
1258/* Data Base delete line routine for BGXFAM access method. */
1259/***********************************************************************/
1260int BGXFAM::DeleteRecords(PGLOBAL g, int irc)
1261 {
1262 bool moved;
1263
1264 /*********************************************************************/
1265 /* There is an alternative here: */
1266 /* 1 - use a temporary file in which are copied all not deleted */
1267 /* lines, at the end the original file will be deleted and */
1268 /* the temporary file renamed to the original file name. */
1269 /* 2 - directly move the not deleted lines inside the original */
1270 /* file, and at the end erase all trailing records. */
1271 /* This will be experimented. */
1272 /*********************************************************************/
1273 if (trace(2))
1274 htrc("BGX DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
1275 irc, UseTemp, Fpos, Tpos, Spos);
1276
1277 if (irc != RC_OK) {
1278 /*******************************************************************/
1279 /* EOF: position Fpos at the end-of-file position. */
1280 /*******************************************************************/
1281 Fpos = Tdbp->Cardinality(g);
1282
1283 if (trace(2))
1284 htrc("Fpos placed at file end=%d\n", Fpos);
1285
1286 } else // Fpos is the deleted line position
1287 Fpos = CurBlk * Nrec + CurNum;
1288
1289 if (Tpos == Spos) {
1290 /*******************************************************************/
1291 /* First line to delete. Move of eventual preceding lines is */
1292 /* not required here if a temporary file is not used, just the */
1293 /* setting of future Spos and Tpos. */
1294 /*******************************************************************/
1295 if (UseTemp) {
1296 /*****************************************************************/
1297 /* Open the temporary file, Spos is at the beginning of file. */
1298 /*****************************************************************/
1299 if (OpenTempFile(g))
1300 return RC_FX;
1301
1302 } else {
1303 /*****************************************************************/
1304 /* Move of eventual preceding lines is not required here. */
1305 /* Set the target file as being the source file itself. */
1306 /* Set the future Tpos, and give Spos a value to block copying. */
1307 /*****************************************************************/
1308 Tfile = Hfile;
1309 Spos = Tpos = Fpos;
1310 } // endif UseTemp
1311
1312 } // endif Tpos == Spos
1313
1314 /*********************************************************************/
1315 /* Move any intermediate lines. */
1316 /*********************************************************************/
1317 if (MoveIntermediateLines(g, &moved))
1318 return RC_FX;
1319
1320 if (irc == RC_OK) {
1321 if (trace(1))
1322 assert(Spos == Fpos);
1323
1324 Spos++; // New start position is on next line
1325
1326 if (moved) {
1327 if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl))
1328 return RC_FX;
1329
1330 OldBlk = -2; // To force fseek to be executed on next block
1331 } // endif moved
1332
1333 if (trace(2))
1334 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
1335
1336 } else if (irc != RC_OK) {
1337 /*******************************************************************/
1338 /* Last call after EOF has been reached. */
1339 /*******************************************************************/
1340 if (UseTemp) {
1341 /*****************************************************************/
1342 /* Ok, now delete old file and rename new temp file. */
1343 /*****************************************************************/
1344 if (RenameTempFile(g))
1345 return RC_FX;
1346
1347 } else {
1348 /*****************************************************************/
1349 /* Remove extra records. */
1350 /*****************************************************************/
1351#if defined(__WIN__)
1352 if (BigSeek(g, Hfile, (BIGINT)Tpos * (BIGINT)Lrecl))
1353 return RC_FX;
1354
1355 if (!SetEndOfFile(Hfile)) {
1356 DWORD drc = GetLastError();
1357
1358 sprintf(g->Message, MSG(SETEOF_ERROR), drc);
1359 return RC_FX;
1360 } // endif error
1361#else // !__WIN__
1362 if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
1363 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1364 return RC_FX;
1365 } // endif
1366#endif // !__WIN__
1367
1368 } // endif UseTemp
1369
1370 } // endif irc
1371
1372 return RC_OK; // All is correct
1373 } // end of DeleteRecords
1374
1375/***********************************************************************/
1376/* Open a temporary file used while updating or deleting. */
1377/***********************************************************************/
1378bool BGXFAM::OpenTempFile(PGLOBAL g)
1379 {
1380 char *tempname;
1381 PDBUSER dup = PlgGetUser(g);
1382
1383 /*********************************************************************/
1384 /* Open the temporary file, Spos is at the beginning of file. */
1385 /*********************************************************************/
1386 tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1387 PlugSetPath(tempname, To_File, Tdbp->GetPath());
1388 strcat(PlugRemoveType(tempname, tempname), ".t");
1389 remove(tempname); // Be sure it does not exist yet
1390
1391#if defined(__WIN__)
1392 Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
1393 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1394
1395 if (Tfile == INVALID_HANDLE_VALUE) {
1396 DWORD rc = GetLastError();
1397 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
1398 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
1399 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
1400 (LPTSTR)tempname, _MAX_PATH, NULL);
1401 strcat(g->Message, tempname);
1402 return true;
1403 } // endif Tfile
1404#else // UNIX
1405 Tfile = open64(tempname, O_WRONLY | O_TRUNC, S_IWRITE);
1406
1407 if (Tfile == INVALID_HANDLE_VALUE) {
1408 int rc = errno;
1409 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
1410 strcat(g->Message, strerror(errno));
1411 return true;
1412 } //endif Tfile
1413#endif // UNIX
1414
1415 To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1416 To_Fbt->Fname = tempname;
1417 To_Fbt->Type = TYPE_FB_HANDLE;
1418 To_Fbt->Memory = NULL;
1419 To_Fbt->Length = 0;
1420 To_Fbt->File = NULL;
1421 To_Fbt->Next = dup->Openlist;
1422 To_Fbt->Count = 1;
1423 To_Fbt->Mode = MODE_INSERT;
1424 To_Fbt->Handle = Tfile;
1425 dup->Openlist = To_Fbt;
1426 return false;
1427 } // end of OpenTempFile
1428
1429/***********************************************************************/
1430/* Move intermediate deleted or updated lines. */
1431/***********************************************************************/
1432bool BGXFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
1433 {
1434 int n, req, nbr;
1435
1436 for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
1437 /*******************************************************************/
1438 /* Non consecutive line to delete. Move intermediate lines. */
1439 /*******************************************************************/
1440 if (!UseTemp || !*b)
1441 if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl))
1442 return true;
1443
1444 req = MY_MIN(n, Dbflen) * Lrecl;
1445
1446 if ((nbr = BigRead(g, Hfile, DelBuf, req)) != req) {
1447 sprintf(g->Message, MSG(DEL_READ_ERROR), req, nbr);
1448 return true;
1449 } // endif nbr
1450
1451 if (!UseTemp)
1452 if (BigSeek(g, Tfile, (BIGINT)Tpos * (BIGINT)Lrecl))
1453 return true;
1454
1455 if (BigWrite(g, Tfile, DelBuf, req))
1456 return true;
1457
1458 req /= Lrecl;
1459 Tpos += (int)req;
1460 Spos += (int)req;
1461
1462 if (trace(2))
1463 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
1464
1465 *b = true;
1466 } // endfor n
1467
1468 return false;
1469 } // end of MoveIntermediateLines
1470
1471/***********************************************************************/
1472/* Data Base close routine for BIGFIX access method. */
1473/***********************************************************************/
1474void BGXFAM::CloseTableFile(PGLOBAL g, bool abort)
1475 {
1476 int rc = RC_OK, wrc = RC_OK;
1477 MODE mode = Tdbp->GetMode();
1478
1479 Abort = abort;
1480
1481 // Closing is True if last Write was in error
1482 if (mode == MODE_INSERT && CurNum && !Closing) {
1483 // Some more inserted lines remain to be written
1484 Rbuf = CurNum--;
1485 wrc = WriteBuffer(g);
1486 } else if (mode == MODE_UPDATE) {
1487 if (Modif && !Closing) {
1488 // Last updated block remains to be written
1489 Closing = true;
1490 wrc = WriteModifiedBlock(g);
1491 } // endif Modif
1492
1493 if (UseTemp && Tfile && wrc == RC_OK) {
1494 if (!Abort) {
1495 // Copy any remaining lines
1496 bool b;
1497
1498 Fpos = Tdbp->Cardinality(g);
1499 Abort = MoveIntermediateLines(g, &b) != RC_OK;
1500 } // endif Abort
1501
1502 // Delete the old file and rename the new temp file.
1503 RenameTempFile(g);
1504 goto fin;
1505 } // endif UseTemp
1506
1507 } // endif's mode
1508
1509 // Finally close the file
1510 rc = PlugCloseFile(g, To_Fb);
1511
1512 fin:
1513 if (trace(1))
1514 htrc("BGX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
1515 To_File, mode, wrc, rc);
1516
1517 Hfile = INVALID_HANDLE_VALUE; // So we can know whether table is open
1518 } // end of CloseTableFile
1519
1520/***********************************************************************/
1521/* Rewind routine for huge FIX access method. */
1522/* Note: commenting out OldBlk = -1 has two advantages: */
1523/* 1 - It forces fseek on first block, thus suppressing the need to */
1524/* rewind the file, anyway unuseful when second pass if indexed. */
1525/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
1526/* (even very unlikely for huge files!) */
1527/***********************************************************************/
1528void BGXFAM::Rewind(void)
1529 {
1530#if 0 // This is probably unuseful because file is accessed directly
1531#if defined(__WIN__) //OB
1532 SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
1533#else // UNIX
1534 lseek64(Hfile, 0, SEEK_SET);
1535#endif // UNIX
1536#endif // 0
1537 CurBlk = -1;
1538 CurNum = Rbuf;
1539//OldBlk = -1;
1540//Rbuf = 0; commented out in case we reuse last read block
1541 Fpos = 0;
1542 } // end of Rewind
1543