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