1/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/
2/* PROGRAM NAME: FILAMVCT */
3/* ------------- */
4/* Version 2.6 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* This program are the VCT file access method classes. */
13/* Added in version 2: */
14/* - Split Vec format. */
15/* - Partial delete. */
16/* - Use of tempfile for update. */
17/* */
18/***********************************************************************/
19
20/***********************************************************************/
21/* Include relevant MariaDB header file. */
22/***********************************************************************/
23#include "my_global.h"
24#if defined(__WIN__)
25#include <io.h>
26#include <fcntl.h>
27#if defined(__BORLANDC__)
28#define __MFC_COMPAT__ // To define min/max as macro
29#endif // __BORLAND__
30//#include <windows.h>
31#include <sys/stat.h>
32#else // !__WIN__
33#if defined(UNIX)
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include <errno.h>
38#define NO_ERROR 0
39#else // !UNIX
40#include <io.h>
41#endif // !UNIX
42#include <fcntl.h>
43#endif // !__WIN__
44
45/***********************************************************************/
46/* Include application header files: */
47/* global.h is header containing all global declarations. */
48/* plgdbsem.h is header containing the DB application declarations. */
49/* tabdos.h is header containing the TABDOS class declarations. */
50/***********************************************************************/
51#include "global.h"
52#include "osutil.h" // Unuseful for WINDOWS
53#include "plgdbsem.h"
54#include "valblk.h"
55#include "filamfix.h"
56#include "tabdos.h"
57#include "tabvct.h"
58#include "maputil.h"
59#include "filamvct.h"
60
61#ifndef INVALID_SET_FILE_POINTER
62#define INVALID_SET_FILE_POINTER ((DWORD)-1)
63#endif
64
65extern int num_read, num_there; // Statistics
66static int num_write;
67
68#if defined(UNIX)
69// Add dummy strerror (NGC)
70char *strerror(int num);
71#endif // UNIX
72
73/***********************************************************************/
74/* Header containing block info for not split VEC tables. */
75/* Block and last values can be calculated from NumRec and Nrec. */
76/* This is better than directly storing Block and Last because it */
77/* make possible to use the same file with tables having a different */
78/* block size value (Element -> Nrec) */
79/* Note: can be in a separate file if header=1 or a true header (2) */
80/***********************************************************************/
81typedef struct _vecheader {
82//int Block; /* The number of used blocks */
83//int Last; /* The number of used records in last block */
84 int MaxRec; /* Max number of records (True vector format)*/
85 int NumRec; /* Number of valid records in the table */
86 } VECHEADER;
87
88/***********************************************************************/
89/* Char VCT column blocks are right filled with blanks (blank = true) */
90/* Conversion of block values allowed conditionally for insert only. */
91/***********************************************************************/
92PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int,
93 bool check = true, bool blank = true, bool un = false);
94
95/* -------------------------- Class VCTFAM --------------------------- */
96
97/***********************************************************************/
98/* Implementation of the VCTFAM class. */
99/***********************************************************************/
100VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp)
101 {
102 Last = tdp->GetLast();
103 MaxBlk = (tdp->GetEstimate() > 0) ?
104 ((tdp->GetEstimate() - 1) / Nrec + 1) : 0;
105 NewBlock = NULL;
106 AddBlock = false;
107 Split = false;
108
109 if ((Header = (MaxBlk) ? tdp->Header : 0))
110 Block = Last = -1;
111
112 Bsize = Nrec;
113 CurNum = Nrec - 1;
114 Colfn = NULL;
115 Tempat = NULL;
116 Clens = NULL;
117 Deplac = NULL;
118 Isnum = NULL;
119 Ncol = 0;
120 } // end of VCTFAM standard constructor
121
122VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp)
123 {
124 MaxBlk = txfp->MaxBlk;
125 NewBlock = NULL;
126 AddBlock = false;
127 Split = txfp->Split;
128 Header = txfp->Header;
129 Bsize = txfp->Bsize;
130 Colfn = txfp->Colfn;
131 Tempat = txfp->Tempat;
132 Clens = txfp->Clens;
133 Deplac = txfp->Deplac;
134 Isnum = txfp->Isnum;
135 Ncol = txfp->Ncol;
136 } // end of VCTFAM copy constructor
137
138/***********************************************************************/
139/* VCT GetFileLength: returns file size in number of bytes. */
140/* This function is here to be accessible by VECFAM and VMPFAM. */
141/***********************************************************************/
142int VCTFAM::GetFileLength(PGLOBAL g)
143 {
144 if (Split) {
145 // Get the total file length
146 char filename[_MAX_PATH];
147 PCSZ savfile = To_File;
148 int i, len = 0;
149
150 // Initialize the array of file structures
151 if (!Colfn) {
152 // Prepare the column file name pattern and set Ncol
153 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
154 Ncol = ((PVCTDEF)Tdbp->GetDef())->MakeFnPattern(Colfn);
155 } // endif Colfn
156
157 To_File = filename;
158
159 for (i = 0; i < Ncol; i++) {
160 sprintf(filename, Colfn, i+1);
161 len += TXTFAM::GetFileLength(g);
162 } // endfor i
163
164 To_File = savfile;
165 return len;
166 } else
167 return TXTFAM::GetFileLength(g);
168
169 } // end of GetFileLength
170
171/***********************************************************************/
172/* Reset read/write position values. */
173/***********************************************************************/
174void VCTFAM::Reset(void)
175 {
176 FIXFAM::Reset();
177 NewBlock = NULL;
178 AddBlock = false;
179 CurNum = Nrec - 1;
180 } // end of Reset
181
182/***********************************************************************/
183/* Get the Headlen, Block and Last info from the file header. */
184/***********************************************************************/
185int VCTFAM::GetBlockInfo(PGLOBAL g)
186 {
187 char filename[_MAX_PATH];
188 int h, k, n;
189 VECHEADER vh;
190
191 if (Header < 1 || Header > 3 || !MaxBlk) {
192 sprintf(g->Message, "Invalid header value %d", Header);
193 return -1;
194 } else
195 n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
196
197 PlugSetPath(filename, To_File, Tdbp->GetPath());
198
199 if (Header == 2)
200 strcat(PlugRemoveType(filename, filename), ".blk");
201
202 if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1
203 || !_filelength(h)) {
204 // Consider this is a void table
205 Last = Nrec;
206 Block = 0;
207
208 if (h != -1)
209 close(h);
210
211 return n;
212 } else if (Header == 3)
213 k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END);
214
215 if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) {
216 sprintf(g->Message, "Error reading header file %s", filename);
217 n = -1;
218 } else if (MaxBlk * Nrec != vh.MaxRec) {
219 sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
220 vh.MaxRec, MaxBlk, Nrec);
221 n = -1;
222 } else {
223 Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
224 Last = (vh.NumRec + Nrec - 1) % Nrec + 1;
225 } // endif s
226
227 close(h);
228 return n;
229 } // end of GetBlockInfo
230
231/***********************************************************************/
232/* Get the Headlen, Block and Last info from the file header. */
233/***********************************************************************/
234bool VCTFAM::SetBlockInfo(PGLOBAL g)
235 {
236 char filename[_MAX_PATH];
237 bool rc = false;
238 size_t n;
239 VECHEADER vh;
240 FILE *s;
241
242 PlugSetPath(filename, To_File, Tdbp->GetPath());
243
244 if (Header != 2) {
245 if (Stream) {
246 s = Stream;
247
248 if (Header == 1)
249 /*k =*/ fseek(s, 0, SEEK_SET);
250
251 } else
252 s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b");
253
254 } else { // Header == 2
255 strcat(PlugRemoveType(filename, filename), ".blk");
256 s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb");
257 } // endif Header
258
259 if (!s) {
260 sprintf(g->Message, "Error opening header file %s", filename);
261 return true;
262 } else if (Header == 3)
263 /*k =*/ fseek(s, -(int)sizeof(VECHEADER), SEEK_END);
264
265 vh.MaxRec = MaxBlk * Bsize;
266 vh.NumRec = (Block - 1) * Nrec + Last;
267
268 if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) {
269 sprintf(g->Message, "Error writing header file %s", filename);
270 rc = true;
271 } // endif fread
272
273 if (Header == 2 || !Stream)
274 fclose(s);
275
276 return rc;
277 } // end of SetBlockInfo
278
279/***********************************************************************/
280/* Use BlockTest to reduce the table estimated size. */
281/***********************************************************************/
282int VCTFAM::MaxBlkSize(PGLOBAL g, int)
283 {
284 int rc = RC_OK, savcur = CurBlk;
285 int size;
286
287 // Roughly estimate the table size as the sum of blocks
288 // that can contain good rows
289 for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
290 if ((rc = Tdbp->TestBlock(g)) == RC_OK)
291 size += (CurBlk == Block - 1) ? Last : Nrec;
292 else if (rc == RC_EF)
293 break;
294
295 CurBlk = savcur;
296 return size;
297 } // end of MaxBlkSize
298
299/***********************************************************************/
300/* VCT Cardinality: returns table cardinality in number of rows. */
301/* This function can be called with a null argument to test the */
302/* availability of Cardinality implementation (1 yes, 0 no). */
303/***********************************************************************/
304int VCTFAM::Cardinality(PGLOBAL g)
305 {
306 if (!g)
307 return 1;
308
309 if (Block < 0)
310 if (Split) {
311 // Separate column files and no pre setting of Block and Last
312 // This allows to see a table modified externally, but Block
313 // and Last must be set from the file cardinality.
314 // Only happens when called by sub classes.
315 char filename[_MAX_PATH];
316 PCSZ savfn = To_File;
317 int len, clen, card = -1;
318 PCOLDEF cdp = Tdbp->GetDef()->GetCols();
319
320 if (!Colfn) {
321 // Prepare the column file name pattern
322 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
323 Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
324 } // endif Colfn
325
326 // Use the first column file to calculate the cardinality
327 clen = cdp->GetClen();
328 sprintf(filename, Colfn, 1);
329 To_File = filename;
330 len = TXTFAM::GetFileLength(g);
331 To_File = savfn;
332
333 if (len >= 0) {
334 if (!(len % clen))
335 card = len / clen; // Fixed length file
336 else
337 sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen);
338
339 if (trace(1))
340 htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen);
341
342 } else
343 card = 0;
344
345 // Set number of blocks for later use
346 Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
347 Last = (card + Nrec - 1) % Nrec + 1;
348 return card;
349 } else {
350 // Vector table having Block and Last info in a Header (file)
351 if ((Headlen = GetBlockInfo(g)) < 0)
352 return -1; // Error
353
354 } // endif split
355
356 return (Block) ? ((Block - 1) * Nrec + Last) : 0;
357 } // end of Cardinality
358
359/***********************************************************************/
360/* GetRowID: return the RowID of last read record. */
361/***********************************************************************/
362int VCTFAM::GetRowID(void)
363 {
364 return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk
365 : (Block - 1) * Nrec + Last);
366 } // end of GetRowID
367
368/***********************************************************************/
369/* VCT Create an empty file for Vector formatted tables. */
370/***********************************************************************/
371bool VCTFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
372 {
373 // Vector formatted file: this will create an empty file of the
374 // required length if it does not exists yet.
375 char filename[_MAX_PATH], c = 0;
376 int h, n;
377
378 PlugSetPath(filename, fn, Tdbp->GetPath());
379#if defined(__WIN__)
380 h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE);
381#else // !__WIN__
382 h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
383#endif // !__WIN__
384
385 if (h == -1)
386 return true;
387
388 n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
389
390 if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) < 0)
391 goto err;
392
393 // This actually fills the empty file
394 if (write(h, &c, 1) < 0)
395 goto err;
396
397 close(h);
398 return false;
399
400 err:
401 sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
402 close(h);
403 return true;
404 } // end of MakeEmptyFile
405
406/***********************************************************************/
407/* VCT Access Method opening routine. */
408/* New method now that this routine is called recursively (last table */
409/* first in reverse order): index blocks are immediately linked to */
410/* join block of next table if it exists or else are discarted. */
411/***********************************************************************/
412bool VCTFAM::OpenTableFile(PGLOBAL g)
413 {
414 char opmode[4], filename[_MAX_PATH];
415 MODE mode = Tdbp->GetMode();
416 PDBUSER dbuserp = PlgGetUser(g);
417
418 /*********************************************************************/
419 /* Update block info if necessary. */
420 /*********************************************************************/
421 if (Block < 0)
422 if ((Headlen = GetBlockInfo(g)) < 0)
423 return true;
424
425 /*********************************************************************/
426 /* Open according to input/output mode required. */
427 /*********************************************************************/
428 switch (mode) {
429 case MODE_READ:
430 strcpy(opmode, "rb");
431 break;
432 case MODE_DELETE:
433 if (!Tdbp->GetNext()) {
434 // Store the number of deleted lines
435 DelRows = Cardinality(g);
436
437 // This will delete the whole file
438 strcpy(opmode, "wb");
439 break;
440 } // endif
441
442 // Selective delete, pass thru
443 /* fall through */
444 case MODE_UPDATE:
445 UseTemp = Tdbp->IsUsingTemp(g);
446 strcpy(opmode, (UseTemp) ? "rb" : "r+b");
447 break;
448 case MODE_INSERT:
449 if (MaxBlk) {
450 if (!Block)
451 if (MakeEmptyFile(g, To_File))
452 return true;
453
454 strcpy(opmode, "r+b"); // Required to update empty blocks
455 } else if (!Block || Last == Nrec)
456 strcpy(opmode, "ab");
457 else
458 strcpy(opmode, "r+b"); // Required to update the last block
459
460 break;
461 default:
462 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
463 return true;
464 } // endswitch Mode
465
466 /*********************************************************************/
467 /* Use conventionnal input/output functions. */
468 /*********************************************************************/
469 PlugSetPath(filename, To_File, Tdbp->GetPath());
470
471 if (!(Stream = PlugOpenFile(g, filename, opmode))) {
472 if (trace(1))
473 htrc("%s\n", g->Message);
474
475 return (mode == MODE_READ && errno == ENOENT)
476 ? PushWarning(g, Tdbp) : true;
477 } // endif Stream
478
479 if (trace(1))
480 htrc("File %s is open in mode %s\n", filename, opmode);
481
482 To_Fb = dbuserp->Openlist; // Keep track of File block
483
484 if (!strcmp(opmode, "wb"))
485 // This will stop the process by
486 // causing GetProgMax to return 0.
487 return ResetTableSize(g, 0, Nrec);
488
489 num_read = num_there = num_write = 0;
490
491 // Allocate the table and column block buffer
492 return AllocateBuffer(g);
493 } // end of OpenTableFile
494
495/***********************************************************************/
496/* Allocate the block buffers for columns used in the query. */
497/***********************************************************************/
498bool VCTFAM::AllocateBuffer(PGLOBAL g)
499 {
500 MODE mode = Tdbp->GetMode();
501 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
502 PCOLDEF cdp;
503 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
504
505 if (mode == MODE_INSERT) {
506 bool chk = PlgGetUser(g)->Check & CHK_TYPE;
507
508 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
509
510 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
511 memset(NewBlock + Nrec * cdp->GetPoff(),
512 (IsTypeNum(cdp->GetType()) ? 0 : ' '),
513 Nrec * cdp->GetClen());
514
515 for (; cp; cp = (PVCTCOL)cp->Next)
516 cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
517 cp->Buf_Type, Nrec, cp->Format.Length,
518 cp->Format.Prec, chk);
519
520 return InitInsert(g); // Initialize inserting
521 } else {
522 if (UseTemp || mode == MODE_DELETE) {
523 // Allocate all that is needed to move lines
524 int i = 0, n = (MaxBlk) ? MaxBlk : 1;
525
526 if (!Ncol)
527 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
528 Ncol++;
529
530 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
531 Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
532 Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
533
534 for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
535 Clens[i] = cdp->GetClen();
536 Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec;
537 Isnum[i] = IsTypeNum(cdp->GetType());
538 Buflen = MY_MAX(Buflen, cdp->GetClen());
539 } // endfor cdp
540
541 if (!UseTemp || MaxBlk) {
542 Buflen *= Nrec;
543 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
544 } else
545 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
546
547 } // endif mode
548
549 for (; cp; cp = (PVCTCOL)cp->Next)
550 if (!cp->IsSpecial()) // Not a pseudo column
551 cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
552 cp->Format.Length, cp->Format.Prec);
553
554 } //endif mode
555
556 return false;
557 } // end of AllocateBuffer
558
559/***********************************************************************/
560/* Do initial action when inserting. */
561/***********************************************************************/
562bool VCTFAM::InitInsert(PGLOBAL g)
563{
564 bool rc = false;
565
566 // We come here in MODE_INSERT only
567 if (Last == Nrec) {
568 CurBlk = Block;
569 CurNum = 0;
570 AddBlock = !MaxBlk;
571 } else {
572 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
573
574 // The starting point must be at the end of file as for append.
575 CurBlk = Block - 1;
576 CurNum = Last;
577
578 try {
579 // Last block must be updated by new values
580 for (; cp; cp = (PVCTCOL)cp->Next)
581 cp->ReadBlock(g);
582
583 } catch (int n) {
584 if (trace(1))
585 htrc("Exception %d: %s\n", n, g->Message);
586 rc = true;
587 } catch (const char *msg) {
588 strcpy(g->Message, msg);
589 rc = true;
590 } // end catch
591
592 } // endif Last
593
594 if (!rc)
595 // We are not currently using a temporary file for Insert
596 T_Stream = Stream;
597
598 return rc;
599 } // end of InitInsert
600
601/***********************************************************************/
602/* ReadBuffer: Read one line for a VCT file. */
603/***********************************************************************/
604int VCTFAM::ReadBuffer(PGLOBAL g)
605 {
606 int rc = RC_OK;
607 MODE mode = Tdbp->GetMode();
608
609 if (Placed)
610 Placed = false;
611 else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) {
612 /*******************************************************************/
613 /* New block. */
614 /*******************************************************************/
615 CurNum = 0;
616
617 next:
618 if (++CurBlk == Block)
619 return RC_EF; // End of file
620
621 /*******************************************************************/
622 /* Before reading a new block, check whether block optimizing */
623 /* can be done, as well as for join as for local filtering. */
624 /*******************************************************************/
625 switch (Tdbp->TestBlock(g)) {
626 case RC_EF:
627 return RC_EF;
628 case RC_NF:
629 goto next;
630 } // endswitch rc
631
632 num_there++;
633 } // endif CurNum
634
635 if (OldBlk != CurBlk) {
636 if (mode == MODE_UPDATE) {
637 /*****************************************************************/
638 /* Flush the eventually modified column buffers in old blocks */
639 /* and read the blocks to modify attached to Set columns. */
640 /*****************************************************************/
641 if (MoveLines(g)) // For VECFAM
642 return RC_FX;
643
644 for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols();
645 colp; colp = (PVCTCOL)colp->Next) {
646 colp->WriteBlock(g);
647 colp->ReadBlock(g);
648 } // endfor colp
649
650 } // endif mode
651
652 OldBlk = CurBlk; // Last block actually read
653 } // endif oldblk
654
655 if (trace(1))
656 htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK);
657
658 return rc;
659 } // end of ReadBuffer
660
661/***********************************************************************/
662/* Data Base write routine for VCT access method. */
663/***********************************************************************/
664int VCTFAM::WriteBuffer(PGLOBAL g)
665 {
666 if (trace(1))
667 htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
668 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
669
670 if (Tdbp->GetMode() == MODE_UPDATE) {
671 // Mode Update is done in ReadDB, we just initialize it here
672 if (!T_Stream) {
673 if (UseTemp) {
674 if (OpenTempFile(g))
675 return RC_FX;
676
677 // Most of the time, not all table columns are updated.
678 // This why we must completely pre-fill the temporary file.
679 Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
680 : Block * Nrec; // To write last lock
681
682 if (MoveIntermediateLines(g))
683 return RC_FX;
684
685 } else
686 T_Stream = Stream;
687
688 } // endif T_Stream
689
690 } else {
691 // Mode Insert
692 if (MaxBlk && CurBlk == MaxBlk) {
693 strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
694 return RC_EF; // Too many lines for vector formatted table
695 } // endif MaxBlk
696
697 if (Closing || ++CurNum == Nrec) {
698 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
699
700 if (!AddBlock) {
701 // Write back the updated last block values
702 for (; cp; cp = (PVCTCOL)cp->Next)
703 cp->WriteBlock(g);
704
705 if (!Closing && !MaxBlk) {
706 // For VCT tables, future blocks must be added
707 char filename[_MAX_PATH];
708
709 // Close the file and reopen it in mode Insert
710 fclose(Stream);
711 PlugSetPath(filename, To_File, Tdbp->GetPath());
712
713 if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) {
714 Closing = true; // Tell CloseDB of error
715 return RC_FX;
716 } // endif Stream
717
718 AddBlock = true;
719 } // endif Closing
720
721 } else {
722 // Here we must add a new block to the file
723 if (Closing)
724 // Reset the overwritten columns for last block extra records
725 for (; cp; cp = (PVCTCOL)cp->Next)
726 memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
727 (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
728 (Nrec - Last) * cp->Clen);
729
730 if ((size_t)Nrec !=
731 fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) {
732 sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
733 return RC_FX;
734 } // endif
735
736 } // endif AddBlock
737
738 if (!Closing) {
739 CurBlk++;
740 CurNum = 0;
741 } // endif Closing
742
743 } // endif Closing || CurNum
744
745 } // endif Mode
746
747 return RC_OK;
748 } // end of WriteBuffer
749
750/***********************************************************************/
751/* Data Base delete line routine for VCT access method. */
752/* Note: lines are moved directly in the files (ooops...) */
753/* Using temp file depends on the Check setting, false by default. */
754/***********************************************************************/
755int VCTFAM::DeleteRecords(PGLOBAL g, int irc)
756 {
757 bool eof = false;
758
759 if (trace(1))
760 htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
761 irc, UseTemp, Fpos, Tpos, Spos);
762
763 if (irc != RC_OK) {
764 /*******************************************************************/
765 /* EOF: position Fpos at the end-of-file position. */
766 /*******************************************************************/
767 Fpos = (Block - 1) * Nrec + Last;
768
769 if (trace(1))
770 htrc("Fpos placed at file end=%d\n", Fpos);
771
772 eof = UseTemp && !MaxBlk;
773 } else // Fpos is the Deleted line position
774 Fpos = CurBlk * Nrec + CurNum;
775
776 if (Tpos == Spos) {
777 if (UseTemp) {
778 /*****************************************************************/
779 /* Open the temporary file, Spos is at the beginning of file. */
780 /*****************************************************************/
781 if (OpenTempFile(g))
782 return RC_FX;
783
784 } else {
785 /*****************************************************************/
786 /* First line to delete. Move of eventual preceding lines is */
787 /* not required here, just the setting of future Spos and Tpos. */
788 /*****************************************************************/
789 T_Stream = Stream;
790 Spos = Tpos = Fpos;
791 } // endif UseTemp
792
793 } // endif Tpos == Spos
794
795 /*********************************************************************/
796 /* Move any intermediate lines. */
797 /*********************************************************************/
798 if (MoveIntermediateLines(g, &eof))
799 return RC_FX;
800
801 if (irc == RC_OK) {
802 /*******************************************************************/
803 /* Reposition the file pointer and set Spos. */
804 /*******************************************************************/
805#ifdef _DEBUG
806 assert(Spos == Fpos);
807#endif
808 Spos++; // New start position is on next line
809
810 if (trace(1))
811 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
812
813 } else {
814 /*******************************************************************/
815 /* Last call after EOF has been reached. */
816 /* Update the Block and Last values. */
817 /*******************************************************************/
818 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
819 Last = (Tpos + Nrec - 1) % Nrec + 1;
820
821 if (!UseTemp) { // The UseTemp case is treated in CloseTableFile
822 if (!MaxBlk) {
823 /***************************************************************/
824 /* Because the chsize functionality is only accessible with a */
825 /* system call we must close the file and reopen it with the */
826 /* open function (_fopen for MS ??) this is still to be */
827 /* checked for compatibility with Text files and other OS's. */
828 /***************************************************************/
829 char filename[_MAX_PATH];
830 int h;
831
832 /*rc =*/ CleanUnusedSpace(g); // Clean last block
833 /*rc =*/ PlugCloseFile(g, To_Fb);
834 Stream = NULL; // For SetBlockInfo
835 PlugSetPath(filename, To_File, Tdbp->GetPath());
836
837 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
838 return RC_FX;
839
840 /***************************************************************/
841 /* Remove extra blocks. */
842 /***************************************************************/
843#if defined(UNIX)
844 if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) {
845 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
846 close(h);
847 return RC_FX;
848 } // endif
849#else
850 if (chsize(h, Headlen + Block * Blksize)) {
851 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
852 close(h);
853 return RC_FX;
854 } // endif
855#endif
856
857 close(h);
858
859 if (trace(1))
860 htrc("done, h=%d irc=%d\n", h, irc);
861
862 } else
863 // Clean the unused space in the file, this is required when
864 // inserting again with a partial column list.
865 if (CleanUnusedSpace(g))
866 return RC_FX;
867
868 if (ResetTableSize(g, Block, Last))
869 return RC_FX;
870
871 } // endif UseTemp
872
873 } // endif irc
874
875 return RC_OK; // All is correct
876 } // end of DeleteRecords
877
878/***********************************************************************/
879/* Open a temporary file used while updating or deleting. */
880/***********************************************************************/
881bool VCTFAM::OpenTempFile(PGLOBAL g)
882 {
883 PCSZ opmode;
884 char tempname[_MAX_PATH];
885 bool rc = false;
886
887 /*********************************************************************/
888 /* Open the temporary file, Spos is at the beginning of file. */
889 /*********************************************************************/
890 PlugSetPath(tempname, To_File, Tdbp->GetPath());
891 strcat(PlugRemoveType(tempname, tempname), ".t");
892
893 if (MaxBlk) {
894 if (MakeEmptyFile(g, tempname))
895 return true;
896
897 opmode = "r+b";
898 } else
899 opmode = "wb";
900
901 if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) {
902 if (trace(1))
903 htrc("%s\n", g->Message);
904
905 rc = true;
906 } else
907 To_Fbt = PlgGetUser(g)->Openlist;
908
909 return rc;
910 } // end of OpenTempFile
911
912/***********************************************************************/
913/* Move intermediate deleted or updated lines. */
914/***********************************************************************/
915bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
916 {
917 int i, dep, off;
918 int n;
919 bool eof = (b) ? *b : false;
920 size_t req, len;
921
922 for (n = Fpos - Spos; n > 0 || eof; n -= req) {
923 /*******************************************************************/
924 /* Non consecutive line to delete. Move intermediate lines. */
925 /*******************************************************************/
926 if (!MaxBlk)
927 req = (size_t)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
928 else
929 req = (size_t)MY_MIN(n, Nrec);
930
931 if (req) for (i = 0; i < Ncol; i++) {
932 if (MaxBlk) {
933 dep = Deplac[i];
934 off = Spos * Clens[i];
935 } else {
936 if (UseTemp)
937 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
938
939 dep = Deplac[i] + (Spos / Nrec) * Blksize;
940 off = (Spos % Nrec) * Clens[i];
941 } // endif MaxBlk
942
943 if (fseek(Stream, dep + off, SEEK_SET)) {
944 sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
945 return true;
946 } // endif
947
948 len = fread(To_Buf, Clens[i], req, Stream);
949
950 if (trace(1))
951 htrc("after read req=%d len=%d\n", req, len);
952
953 if (len != req) {
954 sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
955 return true;
956 } // endif len
957
958 if (!UseTemp || MaxBlk) {
959 if (MaxBlk) {
960 dep = Deplac[i];
961 off = Tpos * Clens[i];
962 } else {
963 dep = Deplac[i] + (Tpos / Nrec) * Blksize;
964 off = (Tpos % Nrec) * Clens[i];
965 } // endif MaxBlk
966
967 if (fseek(T_Stream, dep + off, SEEK_SET)) {
968 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
969 return true;
970 } // endif
971
972 if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
973 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
974 return true;
975 } // endif
976
977 } // endif UseTemp
978
979 if (trace(1))
980 htrc("after write pos=%d\n", ftell(Stream));
981
982 } // endfor i
983
984 Tpos += (int)req;
985 Spos += (int)req;
986
987 if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) {
988 // Write the full or last block to the temporary file
989 if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
990 // Clean the last block in case of future insert,
991 // must be done here because T_Stream was open in write only.
992 for (i = 0; i < Ncol; i++) {
993 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
994 memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
995 } // endfor i
996
997 // Write a new block in the temporary file
998 len = (size_t)Blksize;
999
1000 if (fwrite(NewBlock, 1, len, T_Stream) != len) {
1001 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1002 return true;
1003 } // endif
1004
1005 if (Spos == Fpos)
1006 eof = false;
1007
1008 } // endif UseTemp
1009
1010 if (trace(1))
1011 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
1012
1013 } // endfor n
1014
1015 return false;
1016 } // end of MoveIntermediateLines
1017
1018/***********************************************************************/
1019/* Clean deleted space in a VCT or Vec table file. */
1020/***********************************************************************/
1021bool VCTFAM::CleanUnusedSpace(PGLOBAL g)
1022 {
1023 int i, dep;
1024 int n;
1025 size_t req, len;
1026
1027 if (!MaxBlk) {
1028 /*******************************************************************/
1029 /* Clean last block of the VCT table file. */
1030 /*******************************************************************/
1031 assert(!UseTemp);
1032
1033 if (!(n = Nrec - Last))
1034 return false;
1035
1036 dep = (Block - 1) * Blksize;
1037 req = (size_t)n;
1038
1039 for (i = 0; i < Ncol; i++) {
1040 memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1041
1042 if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) {
1043 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
1044 return true;
1045 } // endif
1046
1047 if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) {
1048 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1049 return true;
1050 } // endif
1051
1052 } // endfor i
1053
1054 } else for (n = Fpos - Tpos; n > 0; n -= req) {
1055 /*******************************************************************/
1056 /* Fill VEC file remaining lines with 0's. */
1057 /* Note: this seems to work even column blocks have been made */
1058 /* with Blanks = true. Perhaps should it be set to false for VEC. */
1059 /*******************************************************************/
1060 req = (size_t)MY_MIN(n, Nrec);
1061 memset(To_Buf, 0, Buflen);
1062
1063 for (i = 0; i < Ncol; i++) {
1064 if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) {
1065 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
1066 return true;
1067 } // endif
1068
1069 if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) {
1070 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
1071 return true;
1072 } // endif
1073
1074 } // endfor i
1075
1076 Tpos += (int)req;
1077 } // endfor n
1078
1079 return false;
1080 } // end of CleanUnusedSpace
1081
1082/***********************************************************************/
1083/* Data Base close routine for VCT access method. */
1084/***********************************************************************/
1085void VCTFAM::CloseTableFile(PGLOBAL g, bool abort)
1086 {
1087 int rc = 0, wrc = RC_OK;
1088 MODE mode = Tdbp->GetMode();
1089
1090 Abort = abort;
1091
1092 if (mode == MODE_INSERT) {
1093 if (Closing)
1094 wrc = RC_FX; // Last write was in error
1095 else
1096 if (CurNum) {
1097 // Some more inserted lines remain to be written
1098 Last = CurNum;
1099 Block = CurBlk + 1;
1100 Closing = true;
1101 wrc = WriteBuffer(g);
1102 } else {
1103 Last = Nrec;
1104 Block = CurBlk;
1105 wrc = RC_OK;
1106 } // endif CurNum
1107
1108 if (wrc != RC_FX) {
1109 rc = ResetTableSize(g, Block, Last);
1110 } else if (AddBlock) {
1111 // Last block was not written
1112 rc = ResetTableSize(g, CurBlk, Nrec);
1113 throw 44;
1114 } // endif
1115
1116 } else if (mode == MODE_UPDATE) {
1117 // Write back to file any pending modifications
1118 for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
1119 colp; colp = (PVCTCOL)colp->Next)
1120 colp->WriteBlock(g);
1121
1122 if (UseTemp && T_Stream) {
1123 rc = RenameTempFile(g);
1124
1125 if (Header) {
1126 // Header must be set because it was not set in temp file
1127 Stream = T_Stream = NULL; // For SetBlockInfo
1128 rc = SetBlockInfo(g);
1129 } // endif Header
1130
1131 } // endif UseTemp
1132
1133 } else if (mode == MODE_DELETE && UseTemp && T_Stream) {
1134 if (MaxBlk)
1135 rc = CleanUnusedSpace(g);
1136
1137 if ((rc = RenameTempFile(g)) != RC_FX) {
1138 Stream = T_Stream = NULL; // For SetBlockInfo
1139 rc = ResetTableSize(g, Block, Last);
1140 } // endif rc
1141
1142 } // endif's mode
1143
1144 if (!(UseTemp && T_Stream))
1145 rc = PlugCloseFile(g, To_Fb);
1146
1147 if (trace(1))
1148 htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n",
1149 To_File, wrc, rc);
1150
1151 Stream = NULL;
1152 } // end of CloseTableFile
1153
1154/***********************************************************************/
1155/* Data Base close routine for VCT access method. */
1156/***********************************************************************/
1157bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last)
1158 {
1159 bool rc = false;
1160
1161 // Set Block and Last values for TDBVCT::MakeBlockValues
1162 Block = block;
1163 Last = last;
1164
1165 if (!Split) {
1166 if (!Header) {
1167 // Update catalog values for Block and Last
1168 PVCTDEF defp = (PVCTDEF)Tdbp->GetDef();
1169 LPCSTR name = Tdbp->GetName();
1170
1171 defp->SetBlock(Block);
1172 defp->SetLast(Last);
1173
1174 if (!defp->SetIntCatInfo("Blocks", Block) ||
1175 !defp->SetIntCatInfo("Last", Last)) {
1176 sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
1177 rc = true;
1178 } // endif
1179
1180 } else
1181 rc = SetBlockInfo(g);
1182
1183 } // endif Split
1184
1185 Tdbp->ResetSize();
1186 return rc;
1187 } // end of ResetTableSize
1188
1189/***********************************************************************/
1190/* Rewind routine for VCT access method. */
1191/***********************************************************************/
1192void VCTFAM::Rewind(void)
1193 {
1194 // In mode update we need to read Set Column blocks
1195 if (Tdbp->GetMode() == MODE_UPDATE)
1196 OldBlk = -1;
1197
1198 // Initialize so block optimization is called for 1st block
1199 CurBlk = -1;
1200 CurNum = Nrec - 1;
1201//rewind(Stream); will be placed by fseek
1202 } // end of Rewind
1203
1204/***********************************************************************/
1205/* ReadBlock: Read column values from current block. */
1206/***********************************************************************/
1207bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
1208 {
1209 int len;
1210 size_t n;
1211
1212 /*********************************************************************/
1213 /* Calculate the offset and size of the block to read. */
1214 /*********************************************************************/
1215 if (MaxBlk) // True vector format
1216 len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk);
1217 else // Blocked vector format
1218 len = Nrec * (colp->Deplac + Lrecl * CurBlk);
1219
1220 if (trace(1))
1221 htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n",
1222 len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
1223
1224 if (fseek(Stream, len, SEEK_SET)) {
1225 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1226 return true;
1227 } // endif
1228
1229 n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
1230 (size_t)Nrec, Stream);
1231
1232 if (n != (size_t)Nrec) {
1233 if (errno == NO_ERROR)
1234 sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
1235 else
1236 sprintf(g->Message, MSG(READ_ERROR),
1237 To_File, strerror(errno));
1238
1239 if (trace(1))
1240 htrc(" Read error: %s\n", g->Message);
1241
1242 return true;
1243 } // endif
1244
1245 if (trace(1))
1246 num_read++;
1247
1248 return false;
1249 } // end of ReadBlock
1250
1251/***********************************************************************/
1252/* WriteBlock: Write back current column values for one block. */
1253/* Note: the test of Status is meant to prevent physical writing of */
1254/* the block during the checking loop in mode Update. It is set to */
1255/* BUF_EMPTY when reopening the table between the two loops. */
1256/***********************************************************************/
1257bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
1258 {
1259 int len;
1260 size_t n;
1261
1262 /*********************************************************************/
1263 /* Calculate the offset and size of the block to write. */
1264 /*********************************************************************/
1265 if (MaxBlk) // File has Vector format
1266 len = Headlen
1267 + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
1268 else // Old VCT format
1269 len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
1270
1271 if (trace(1))
1272 htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
1273 Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
1274
1275 if (fseek(T_Stream, len, SEEK_SET)) {
1276 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1277 return true;
1278 } // endif
1279
1280 // Here Nrec was changed to CurNum in mode Insert,
1281 // this is the true number of records to write,
1282 // this also avoid writing garbage in the file for true vector tables.
1283 n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec;
1284
1285 if (n != fwrite(colp->Blk->GetValPointer(),
1286 (size_t)colp->Clen, n, T_Stream)) {
1287 sprintf(g->Message, MSG(WRITE_STRERROR),
1288 (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
1289
1290 if (trace(1))
1291 htrc("Write error: %s\n", strerror(errno));
1292
1293 return true;
1294 } // endif
1295
1296#if defined(UNIX)
1297 fflush(T_Stream); //NGC
1298#endif
1299
1300#ifdef _DEBUG
1301 num_write++;
1302#endif
1303
1304 return false;
1305 } // end of WriteBlock
1306
1307/* -------------------------- Class VCMFAM --------------------------- */
1308
1309/***********************************************************************/
1310/* Implementation of the VCMFAM class. */
1311/***********************************************************************/
1312VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1313 {
1314 Memory = NULL;
1315 Memcol = NULL;
1316 } // end of VCMFAM standard constructor
1317
1318VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp)
1319 {
1320 Memory = txfp->Memory;
1321 Memcol = txfp->Memcol;
1322 } // end of VCMFAM copy constructor
1323
1324/***********************************************************************/
1325/* Mapped VCT Access Method opening routine. */
1326/* New method now that this routine is called recursively (last table */
1327/* first in reverse order): index blocks are immediately linked to */
1328/* join block of next table if it exists or else are discarted. */
1329/***********************************************************************/
1330bool VCMFAM::OpenTableFile(PGLOBAL g)
1331 {
1332 char filename[_MAX_PATH];
1333 int len;
1334 MODE mode = Tdbp->GetMode();
1335 PFBLOCK fp = NULL;
1336 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1337
1338 /*********************************************************************/
1339 /* Update block info if necessary. */
1340 /*********************************************************************/
1341 if (Block < 0)
1342 if ((Headlen = GetBlockInfo(g)) < 0)
1343 return true;
1344
1345 /*********************************************************************/
1346 /* We used the file name relative to recorded datapath. */
1347 /*********************************************************************/
1348 PlugSetPath(filename, To_File, Tdbp->GetPath());
1349
1350 /*********************************************************************/
1351 /* The whole file will be mapped so we can use it as if it were */
1352 /* entirely read into virtual memory. */
1353 /* Firstly we check whether this file have been already mapped. */
1354 /*********************************************************************/
1355 if (mode == MODE_READ) {
1356 for (fp = dbuserp->Openlist; fp; fp = fp->Next)
1357 if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
1358 && fp->Count && fp->Mode == mode)
1359 break;
1360
1361 if (trace(1))
1362 htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count);
1363
1364 } else
1365 fp = NULL;
1366
1367 if (fp) {
1368 /*******************************************************************/
1369 /* File already mapped. Just increment use count and get pointer. */
1370 /*******************************************************************/
1371 fp->Count++;
1372 Memory = fp->Memory;
1373 len = fp->Length;
1374 } else {
1375 /*******************************************************************/
1376 /* If required, delete the whole file if no filtering is implied. */
1377 /*******************************************************************/
1378 bool del;
1379 HANDLE hFile;
1380 MEMMAP mm;
1381 MODE mapmode = mode;
1382
1383 if (mode == MODE_INSERT) {
1384 if (MaxBlk) {
1385 if (!Block)
1386 if (MakeEmptyFile(g, To_File))
1387 return true;
1388
1389 // Inserting will be like updating the file
1390 mapmode = MODE_UPDATE;
1391 } else {
1392 strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
1393 return true;
1394 } // endif MaxBlk
1395
1396 } // endif mode
1397
1398 del = mode == MODE_DELETE && !Tdbp->GetNext();
1399
1400 if (del) {
1401 DelRows = Cardinality(g);
1402
1403 // This will stop the process by causing GetProgMax to return 0.
1404// ResetTableSize(g, 0, Nrec); must be done later
1405 } // endif del
1406
1407 /*******************************************************************/
1408 /* Create the mapping file object. */
1409 /*******************************************************************/
1410 hFile = CreateFileMap(g, filename, &mm, mapmode, del);
1411
1412 if (hFile == INVALID_HANDLE_VALUE) {
1413 DWORD rc = GetLastError();
1414
1415 if (!(*g->Message))
1416 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1417 "map", (int) rc, filename);
1418
1419 if (trace(1))
1420 htrc("%s\n", g->Message);
1421
1422 return (mode == MODE_READ && rc == ENOENT)
1423 ? PushWarning(g, Tdbp) : true;
1424 } // endif hFile
1425
1426 /*******************************************************************/
1427 /* Get the file size (assuming file is smaller than 4 GB) */
1428 /*******************************************************************/
1429 len = mm.lenL;
1430 Memory = (char *)mm.memory;
1431
1432 if (!len) { // Empty or deleted file
1433 CloseFileHandle(hFile);
1434 bool rc = ResetTableSize(g, 0, Nrec);
1435 return (mapmode == MODE_UPDATE) ? true : rc;
1436 } // endif len
1437
1438 if (!Memory) {
1439 CloseFileHandle(hFile);
1440 sprintf(g->Message, MSG(MAP_VIEW_ERROR),
1441 filename, GetLastError());
1442 return true;
1443 } // endif Memory
1444
1445 if (mode != MODE_DELETE) {
1446 CloseFileHandle(hFile); // Not used anymore
1447 hFile = INVALID_HANDLE_VALUE; // For Fblock
1448 } // endif Mode
1449
1450 /*******************************************************************/
1451 /* Link a Fblock. This make possible to reuse already opened maps */
1452 /* and also to automatically unmap them in case of error g->jump. */
1453 /* Note: block can already exist for previously closed file. */
1454 /*******************************************************************/
1455 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1456 fp->Type = TYPE_FB_MAP;
1457 fp->Fname = PlugDup(g, filename);
1458 fp->Next = dbuserp->Openlist;
1459 dbuserp->Openlist = fp;
1460 fp->Count = 1;
1461 fp->Length = len;
1462 fp->Memory = Memory;
1463 fp->Mode = mode;
1464 fp->File = NULL;
1465 fp->Handle = hFile; // Used for Delete
1466 } // endif fp
1467
1468 To_Fb = fp; // Useful when closing
1469
1470 if (trace(1))
1471 htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
1472 fp, fp->Count, Memory, len);
1473
1474 return AllocateBuffer(g);
1475 } // end of OpenTableFile
1476
1477/***********************************************************************/
1478/* Allocate the block buffers for columns used in the query. */
1479/* Give a dummy value (1) to prevent allocating the value block. */
1480/* It will be set pointing into the memory map of the file. */
1481/* Note: Memcol must be set for all columns because it can be used */
1482/* for set columns in Update. Clens values are used only in Delete. */
1483/***********************************************************************/
1484bool VCMFAM::AllocateBuffer(PGLOBAL g)
1485 {
1486 int m, i = 0;
1487 bool b = Tdbp->GetMode() == MODE_DELETE;
1488 PVCTCOL cp;
1489 PCOLDEF cdp;
1490 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1491
1492 // Calculate the number of columns
1493 if (!Ncol)
1494 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
1495 Ncol++;
1496
1497 // To store the start position of each column
1498 Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*));
1499 m = (MaxBlk) ? MaxBlk : 1;
1500
1501 // We will need all column sizes and type for Delete
1502 if (b) {
1503 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
1504 Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
1505 } // endif b
1506
1507 for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) {
1508 if (b) {
1509 Clens[i] = cdp->GetClen();
1510 Isnum[i] = IsTypeNum(cdp->GetType());
1511 } // endif b
1512
1513 Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec;
1514 } // endfor cdp
1515
1516 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1517 if (!cp->IsSpecial()) { // Not a pseudo column
1518 cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
1519 cp->Format.Length, cp->Format.Prec);
1520 cp->AddStatus(BUF_MAPPED);
1521 } // endif IsSpecial
1522
1523 if (Tdbp->GetMode() == MODE_INSERT)
1524 return InitInsert(g);
1525
1526 return false;
1527 } // end of AllocateBuffer
1528
1529/***********************************************************************/
1530/* Do initial action when inserting. */
1531/***********************************************************************/
1532bool VCMFAM::InitInsert(PGLOBAL g)
1533{
1534 bool rc = false;
1535 volatile PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1536
1537 // We come here in MODE_INSERT only
1538 if (Last == Nrec) {
1539 CurBlk = Block;
1540 CurNum = 0;
1541 AddBlock = !MaxBlk;
1542 } else {
1543 // The starting point must be at the end of file as for append.
1544 CurBlk = Block - 1;
1545 CurNum = Last;
1546 } // endif Last
1547
1548 try {
1549 // Initialize the column block pointer
1550 for (; cp; cp = (PVCTCOL)cp->Next)
1551 cp->ReadBlock(g);
1552
1553 } catch (int n) {
1554 if (trace(1))
1555 htrc("Exception %d: %s\n", n, g->Message);
1556 rc = true;
1557 } catch (const char *msg) {
1558 strcpy(g->Message, msg);
1559 rc = true;
1560 } // end catch
1561
1562 return rc;
1563} // end of InitInsert
1564
1565/***********************************************************************/
1566/* Data Base write routine for VMP access method. */
1567/***********************************************************************/
1568int VCMFAM::WriteBuffer(PGLOBAL g)
1569 {
1570 if (trace(1))
1571 htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
1572 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
1573
1574 // Mode Update being done in ReadDB we process here Insert mode only.
1575 if (Tdbp->GetMode() == MODE_INSERT) {
1576 if (CurBlk == MaxBlk) {
1577 strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
1578 return RC_EF; // Too many lines for vector formatted table
1579 } // endif MaxBlk
1580
1581 if (Closing || ++CurNum == Nrec) {
1582 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1583
1584 // Write back the updated last block values
1585 for (; cp; cp = (PVCTCOL)cp->Next)
1586 cp->WriteBlock(g);
1587
1588 if (!Closing) {
1589 CurBlk++;
1590 CurNum = 0;
1591
1592 // Re-initialize the column block pointer
1593 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1594 cp->ReadBlock(g);
1595
1596 } // endif Closing
1597
1598 } // endif Closing || CurNum
1599
1600 } // endif Mode
1601
1602 return RC_OK;
1603 } // end of WriteBuffer
1604
1605/***********************************************************************/
1606/* Data Base delete line routine for VMP access method. */
1607/* Lines between deleted lines are moved in the mapfile view. */
1608/***********************************************************************/
1609int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
1610 {
1611 if (trace(1))
1612 htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
1613 irc, To_Buf, Tpos, Spos);
1614
1615 if (irc != RC_OK) {
1616 /*******************************************************************/
1617 /* EOF: position Fpos at the top of map position. */
1618 /*******************************************************************/
1619 Fpos = (Block - 1) * Nrec + Last;
1620
1621 if (trace(1))
1622 htrc("Fpos placed at file top=%p\n", Fpos);
1623
1624 } else // Fpos is the Deleted line position
1625 Fpos = CurBlk * Nrec + CurNum;
1626
1627 if (Tpos == Spos) {
1628 /*******************************************************************/
1629 /* First line to delete. Move of eventual preceding lines is */
1630 /* not required here, just setting of future Spos and Tpos. */
1631 /*******************************************************************/
1632 Tpos = Spos = Fpos;
1633 } else
1634 (void)MoveIntermediateLines(g);
1635
1636 if (irc == RC_OK) {
1637 Spos = Fpos + 1; // New start position
1638
1639 if (trace(1))
1640 htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
1641
1642 } else {
1643 /*******************************************************************/
1644 /* Last call after EOF has been reached. */
1645 /*******************************************************************/
1646 int i, m, n;
1647
1648 /*******************************************************************/
1649 /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
1650 /*******************************************************************/
1651 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
1652 Last = (Tpos + Nrec - 1) % Nrec + 1;
1653
1654 if (!MaxBlk) {
1655 PFBLOCK fp = To_Fb;
1656
1657 // Clean the unused part of the last block
1658 m = (Block - 1) * Blksize;
1659 n = Nrec - Last;
1660
1661 for (i = 0; i < Ncol; i++)
1662 memset(Memcol[i] + m + Last * Clens[i],
1663 (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1664
1665 // We must Unmap the view and use the saved file handle
1666 // to put an EOF at the end of the last block of the file.
1667 CloseMemMap(fp->Memory, (size_t)fp->Length);
1668 fp->Count = 0; // Avoid doing it twice
1669
1670 // Remove extra blocks
1671 n = Block * Blksize;
1672
1673#if defined(__WIN__)
1674 DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
1675
1676 if (drc == 0xFFFFFFFF) {
1677 sprintf(g->Message, MSG(FUNCTION_ERROR),
1678 "SetFilePointer", GetLastError());
1679 CloseHandle(fp->Handle);
1680 return RC_FX;
1681 } // endif
1682
1683 if (trace(1))
1684 htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
1685
1686 if (!SetEndOfFile(fp->Handle)) {
1687 sprintf(g->Message, MSG(FUNCTION_ERROR),
1688 "SetEndOfFile", GetLastError());
1689 CloseHandle(fp->Handle);
1690 return RC_FX;
1691 } // endif
1692
1693 CloseHandle(fp->Handle);
1694#else // UNIX
1695 if (ftruncate(fp->Handle, (off_t)n)) {
1696 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1697 close(fp->Handle);
1698 return RC_FX;
1699 } // endif
1700
1701 close(fp->Handle);
1702#endif // UNIX
1703 } else
1704 // True vector table, Table file size does not change.
1705 // Just clean the unused part of the file.
1706 for (n = Fpos - Tpos, i = 0; i < Ncol; i++)
1707 memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]);
1708
1709 // Reset Last and Block values in the catalog
1710 PlugCloseFile(g, To_Fb); // in case of Header
1711 ResetTableSize(g, Block, Last);
1712 } // endif irc
1713
1714 return RC_OK; // All is correct
1715 } // end of DeleteRecords
1716
1717/***********************************************************************/
1718/* Move intermediate deleted or updated lines. */
1719/***********************************************************************/
1720bool VCMFAM::MoveIntermediateLines(PGLOBAL, bool *)
1721 {
1722 int i, m, n;
1723
1724 if ((n = Fpos - Spos) > 0) {
1725 /*******************************************************************/
1726 /* Non consecutive line to delete. Move intermediate lines. */
1727 /*******************************************************************/
1728 if (!MaxBlk) {
1729 // Old VCT format, moving must respect block limits
1730 char *ps, *pt;
1731 int req, soff, toff;
1732
1733 for (; n > 0; n -= req) {
1734 soff = Spos % Nrec;
1735 toff = Tpos % Nrec;
1736 req = (size_t)MY_MIN(n, Nrec - MY_MAX(soff, toff));
1737
1738 for (i = 0; i < Ncol; i++) {
1739 ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i];
1740 pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i];
1741 memmove(pt, ps, req * Clens[i]);
1742 } // endfor i
1743
1744 Tpos += req;
1745 Spos += req;
1746 } // endfor n
1747
1748 } else {
1749 // True vector format, all is simple...
1750 for (i = 0; i < Ncol; i++) {
1751 m = Clens[i];
1752 memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m);
1753 } // endfor i
1754
1755 Tpos += n;
1756 } // endif MaxBlk
1757
1758 if (trace(1))
1759 htrc("move %d bytes\n", n);
1760
1761 } // endif n
1762
1763 return false;
1764 } // end of MoveIntermediate Lines
1765
1766/***********************************************************************/
1767/* Data Base close routine for VMP access method. */
1768/***********************************************************************/
1769void VCMFAM::CloseTableFile(PGLOBAL g, bool)
1770 {
1771 int wrc = RC_OK;
1772 MODE mode = Tdbp->GetMode();
1773
1774 if (mode == MODE_INSERT) {
1775 if (!Closing) {
1776 if (CurNum) {
1777 // Some more inserted lines remain to be written
1778 Last = CurNum;
1779 Block = CurBlk + 1;
1780 Closing = true;
1781 wrc = WriteBuffer(g);
1782 } else {
1783 Last = Nrec;
1784 Block = CurBlk;
1785 wrc = RC_OK;
1786 } // endif CurNum
1787
1788 } else
1789 wrc = RC_FX; // Last write was in error
1790
1791 PlugCloseFile(g, To_Fb);
1792
1793 if (wrc != RC_FX)
1794 /*rc =*/ ResetTableSize(g, Block, Last);
1795
1796 } else if (mode != MODE_DELETE || Abort)
1797 PlugCloseFile(g, To_Fb);
1798
1799 } // end of CloseTableFile
1800
1801/***********************************************************************/
1802/* ReadBlock: Read column values from current block. */
1803/***********************************************************************/
1804bool VCMFAM::ReadBlock(PGLOBAL, PVCTCOL colp)
1805 {
1806 char *mempos;
1807 int i = colp->Index - 1;
1808 int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl);
1809
1810 /*********************************************************************/
1811 /* Calculate the start position of the column block to read. */
1812 /*********************************************************************/
1813 mempos = Memcol[i] + n * CurBlk;
1814
1815 if (trace(1))
1816 htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n",
1817 mempos, i, Nrec, colp->Clen, CurBlk);
1818
1819 if (colp->GetStatus(BUF_MAPPED))
1820 colp->Blk->SetValPointer(mempos);
1821
1822 if (trace(1))
1823 num_read++;
1824
1825 return false;
1826 } // end of ReadBlock
1827
1828/***********************************************************************/
1829/* WriteBlock: Write back current column values for one block. */
1830/* Note: there is nothing to do because we are working directly into */
1831/* the mapped file, except when checking for Update but in this case */
1832/* we do not want to write back the modifications either. */
1833/***********************************************************************/
1834bool VCMFAM::WriteBlock(PGLOBAL, PVCTCOL colp __attribute__((unused)))
1835 {
1836#if defined(_DEBUG)
1837 char *mempos;
1838 int i = colp->Index - 1;
1839 int n = Nrec * colp->Clen;
1840
1841 /*********************************************************************/
1842 /* Calculate the offset and size of the block to write. */
1843 /*********************************************************************/
1844 mempos = Memcol[i] + n * CurBlk;
1845
1846 if (trace(1))
1847 htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n",
1848 Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk);
1849
1850#endif // _DEBUG
1851
1852 return false;
1853 } // end of WriteBlock
1854
1855/* -------------------------- Class VECFAM --------------------------- */
1856
1857/***********************************************************************/
1858/* Implementation of the VECFAM class. */
1859/***********************************************************************/
1860VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1861 {
1862 Streams = NULL;
1863 To_Fbs = NULL;
1864 To_Bufs = NULL;
1865 Split = true;
1866 Block = Last = -1;
1867 InitUpdate = false;
1868 } // end of VECFAM standard constructor
1869
1870VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp)
1871 {
1872 Streams = txfp->Streams;
1873 To_Fbs = txfp->To_Fbs;
1874 Clens = txfp->Clens;
1875 To_Bufs = txfp->To_Bufs;
1876 InitUpdate = txfp->InitUpdate;
1877 } // end of VECFAM copy constructor
1878
1879/***********************************************************************/
1880/* VEC Access Method opening routine. */
1881/* New method now that this routine is called recursively (last table */
1882/* first in reverse order): index blocks are immediately linked to */
1883/* join block of next table if it exists or else are discarted. */
1884/***********************************************************************/
1885bool VECFAM::OpenTableFile(PGLOBAL g)
1886 {
1887 char opmode[4];
1888 int i;
1889 bool b= false;
1890 PCOLDEF cdp;
1891 PVCTCOL cp;
1892 MODE mode = Tdbp->GetMode();
1893 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1894
1895 /*********************************************************************/
1896 /* Call Cardinality to set Block and Last values in case it was not */
1897 /* already called (this happens indeed in test xmode) */
1898 /*********************************************************************/
1899 Cardinality(g);
1900
1901 /*********************************************************************/
1902 /* Open according to input/output mode required. */
1903 /*********************************************************************/
1904 switch (mode) {
1905 case MODE_READ:
1906 strcpy(opmode, "rb");
1907 break;
1908 case MODE_DELETE:
1909 if (!Tdbp->GetNext()) {
1910 // Store the number of deleted lines
1911 DelRows = Cardinality(g);
1912
1913 // This will delete the whole file
1914 strcpy(opmode, "wb");
1915
1916 // This will stop the process by causing GetProgMax to return 0.
1917 ResetTableSize(g, 0, Nrec);
1918 break;
1919 } // endif filter
1920
1921 // Selective delete, pass thru
1922 /* fall through */
1923 case MODE_UPDATE:
1924 UseTemp = Tdbp->IsUsingTemp(g);
1925 strcpy(opmode, (UseTemp) ? "rb": "r+b");
1926 break;
1927 case MODE_INSERT:
1928 strcpy(opmode, "ab");
1929 break;
1930 default:
1931 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
1932 return true;
1933 } // endswitch Mode
1934
1935 /*********************************************************************/
1936 /* Initialize the array of file structures. */
1937 /*********************************************************************/
1938 if (!Colfn) {
1939 // Prepare the column file name pattern and set Ncol
1940 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1941 Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
1942 } // endif Colfn
1943
1944 Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
1945 To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
1946
1947 for (i = 0; i < Ncol; i++) {
1948 Streams[i] = NULL;
1949 To_Fbs[i] = NULL;
1950 } // endif i
1951
1952 /*********************************************************************/
1953 /* Open the files corresponding to columns used in the query. */
1954 /*********************************************************************/
1955 if (mode == MODE_INSERT || mode == MODE_DELETE) {
1956 // All columns must be written or deleted
1957 for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
1958 if (OpenColumnFile(g, opmode, i))
1959 return true;
1960
1961 // Check for void table or missing columns
1962 for (b = !Streams[0], i = 1; i < Ncol; i++)
1963 if (b != !Streams[i])
1964 return true;
1965
1966 } else {
1967 /*******************************************************************/
1968 /* Open the files corresponding to updated columns of the query. */
1969 /*******************************************************************/
1970 for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
1971 cp = (PVCTCOL)cp->Next)
1972 if (OpenColumnFile(g, opmode, cp->Index - 1))
1973 return true;
1974
1975 // Open in read only mode the used columns not already open
1976 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1977 if (!cp->IsSpecial() && !Streams[cp->Index - 1])
1978 if (OpenColumnFile(g, "rb", cp->Index - 1))
1979 return true;
1980
1981 // Check for void table or missing columns
1982 for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
1983 cp = (PVCTCOL)cp->Next)
1984 if (!cp->IsSpecial()) {
1985 if (!i++)
1986 b = !Streams[cp->Index - 1];
1987 else if (b != !Streams[cp->Index - 1])
1988 return true;
1989
1990 } // endif Special
1991
1992 } // endif mode
1993
1994 /*********************************************************************/
1995 /* Allocate the table and column block buffer. */
1996 /*********************************************************************/
1997 return (b) ? false : AllocateBuffer(g);
1998 } // end of OpenTableFile
1999
2000/***********************************************************************/
2001/* Open the file corresponding to one column. */
2002/***********************************************************************/
2003bool VECFAM::OpenColumnFile(PGLOBAL g, PCSZ opmode, int i)
2004 {
2005 char filename[_MAX_PATH];
2006 PDBUSER dup = PlgGetUser(g);
2007
2008 sprintf(filename, Colfn, i+1);
2009
2010 if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
2011 if (trace(1))
2012 htrc("%s\n", g->Message);
2013
2014 return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
2015 ? PushWarning(g, Tdbp) : true;
2016 } // endif Streams
2017
2018 if (trace(1))
2019 htrc("File %s is open in mode %s\n", filename, opmode);
2020
2021 To_Fbs[i] = dup->Openlist; // Keep track of File blocks
2022 return false;
2023 } // end of OpenColumnFile
2024
2025/***********************************************************************/
2026/* Allocate the block buffers for columns used in the query. */
2027/***********************************************************************/
2028bool VECFAM::AllocateBuffer(PGLOBAL g)
2029 {
2030 int i;
2031 PVCTCOL cp;
2032 PCOLDEF cdp;
2033 PTDBVCT tdbp = (PTDBVCT)Tdbp;
2034 MODE mode = tdbp->GetMode();
2035 PDOSDEF defp = (PDOSDEF)tdbp->GetDef();
2036
2037 if (mode != MODE_READ) {
2038 // Allocate what is needed by all modes except Read
2039 T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
2040 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2041
2042 // Give default values
2043 for (i = 0; i < Ncol; i++) {
2044 T_Streams[i] = Streams[i];
2045 Clens[i] = 0;
2046 } // endfor i
2047
2048 } // endif mode
2049
2050 if (mode == MODE_INSERT) {
2051 bool chk = PlgGetUser(g)->Check & CHK_TYPE;
2052
2053 To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*));
2054 cdp = defp->GetCols();
2055
2056 for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2057 Clens[i] = cdp->GetClen();
2058 To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]);
2059
2060 if (cdp->GetType() == TYPE_STRING)
2061 memset(To_Bufs[i], ' ', Nrec * Clens[i]);
2062 else
2063 memset(To_Bufs[i], 0, Nrec * Clens[i]);
2064
2065 } // endfor cdp
2066
2067 for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2068 cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1],
2069 cp->Buf_Type, Nrec, cp->Format.Length,
2070 cp->Format.Prec, chk);
2071
2072 return InitInsert(g);
2073 } else {
2074 if (UseTemp || mode == MODE_DELETE) {
2075 // Allocate all that is needed to move lines and make Temp
2076 if (UseTemp) {
2077 Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2078 strcpy(Tempat, Colfn);
2079 PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
2080 strcat(PlugRemoveType(Tempat, Tempat), ".t");
2081 T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2082 } // endif UseTemp
2083
2084 if (UseTemp)
2085 for (i = 0; i < Ncol; i++) {
2086 T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL;
2087 T_Fbs[i] = NULL;
2088 } // endfor i
2089
2090 if (mode == MODE_DELETE) { // All columns are moved
2091 cdp = defp->GetCols();
2092
2093 for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2094 Clens[i] = cdp->GetClen();
2095 Buflen = MY_MAX(Buflen, cdp->GetClen());
2096 } // endfor cdp
2097
2098 } else { // Mode Update, only some columns are updated
2099 for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) {
2100 i = cp->Index -1;
2101
2102 if (UseTemp)
2103 T_Streams[i] = NULL; // Mark the streams to open
2104
2105 Clens[i] = cp->Clen;
2106 Buflen = MY_MAX(Buflen, cp->Clen);
2107 } // endfor cp
2108
2109 InitUpdate = true; // To be initialized
2110 } // endif mode
2111
2112 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec);
2113 } // endif mode
2114
2115 // Finally allocate column buffers for all modes
2116 for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2117 if (!cp->IsSpecial()) // Not a pseudo column
2118 cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
2119 cp->Format.Length, cp->Format.Prec);
2120
2121 } // endif mode
2122
2123 return false;
2124 } // end of AllocateBuffer
2125
2126/***********************************************************************/
2127/* Do initial action when inserting. */
2128/***********************************************************************/
2129bool VECFAM::InitInsert(PGLOBAL)
2130 {
2131 // We come here in MODE_INSERT only
2132 CurBlk = 0;
2133 CurNum = 0;
2134 AddBlock = true;
2135 return false;
2136 } // end of InitInsert
2137
2138/***********************************************************************/
2139/* Reset buffer access according to indexing and to mode. */
2140/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
2141/***********************************************************************/
2142void VECFAM::ResetBuffer(PGLOBAL g)
2143 {
2144 /*********************************************************************/
2145 /* If access is random, performances can be much better when the */
2146 /* reads are done on only one row, except for small tables that can */
2147 /* be entirely read in one block. If the index is just used as a */
2148 /* bitmap filter, as for Update or Delete, reading will be */
2149 /* sequential and we better keep block reading. */
2150 /*********************************************************************/
2151 if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) {
2152 Nrec = 1; // Better for random access
2153 Rbuf = 0;
2154 OldBlk = -2; // Has no meaning anymore
2155 Block = Tdbp->Cardinality(g); // Blocks are one line now
2156 Last = 1; // Probably unuseful
2157 } // endif Mode
2158
2159 } // end of ResetBuffer
2160
2161/***********************************************************************/
2162/* Data Base write routine for VCT access method. */
2163/***********************************************************************/
2164int VECFAM::WriteBuffer(PGLOBAL g)
2165 {
2166 if (trace(1))
2167 htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
2168 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
2169
2170 if (Tdbp->GetMode() == MODE_INSERT) {
2171 if (Closing || ++CurNum == Nrec) {
2172 // Here we must add a new blocks to the files
2173 int i;
2174 size_t n = (size_t)CurNum;
2175
2176 for (i = 0; i < Ncol; i++)
2177 if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) {
2178 sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
2179 return RC_FX;
2180 } // endif
2181
2182 if (!Closing) {
2183 CurBlk++;
2184 CurNum = 0;
2185 } // endif Closing
2186
2187 } // endif Closing || CurNum
2188
2189 } else // Mode Update
2190 // Writing updates being done in ReadDB we do initialization only.
2191 if (InitUpdate) {
2192 if (OpenTempFile(g))
2193 return RC_FX;
2194
2195 InitUpdate = false; // Done
2196 } // endif InitUpdate
2197
2198 return RC_OK;
2199 } // end of WriteBuffer
2200
2201/***********************************************************************/
2202/* Data Base delete line routine for split vertical access methods. */
2203/* Note: lines are moved directly in the files (ooops...) */
2204/* Using temp file depends on the Check setting, false by default. */
2205/***********************************************************************/
2206int VECFAM::DeleteRecords(PGLOBAL g, int irc)
2207 {
2208 if (trace(1))
2209 htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
2210 irc, UseTemp, Fpos, Tpos, Spos);
2211
2212 if (irc != RC_OK) {
2213 /*******************************************************************/
2214 /* EOF: position Fpos at the end-of-file position. */
2215 /*******************************************************************/
2216 Fpos = Cardinality(g);
2217
2218 if (trace(1))
2219 htrc("Fpos placed at file end=%d\n", Fpos);
2220
2221 } else // Fpos is the Deleted line position
2222 Fpos = CurBlk * Nrec + CurNum;
2223
2224 if (Tpos == Spos) {
2225 // First line to delete
2226 if (UseTemp) {
2227 /*****************************************************************/
2228 /* Open the temporary files, Spos is at the beginning of file. */
2229 /*****************************************************************/
2230 if (OpenTempFile(g))
2231 return RC_FX;
2232
2233 } else
2234 /*****************************************************************/
2235 /* Move of eventual preceding lines is not required here. */
2236 /* Set the future Tpos, and give Spos a value to block copying. */
2237 /*****************************************************************/
2238 Spos = Tpos = Fpos;
2239
2240 } // endif Tpos == Spos
2241
2242 /*********************************************************************/
2243 /* Move any intermediate lines. */
2244 /*********************************************************************/
2245 if (MoveIntermediateLines(g))
2246 return RC_FX;
2247
2248 if (irc == RC_OK) {
2249#ifdef _DEBUG
2250 assert(Spos == Fpos);
2251#endif
2252 Spos++; // New start position is on next line
2253
2254 if (trace(1))
2255 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
2256
2257 } else {
2258 /*******************************************************************/
2259 /* Last call after EOF has been reached. */
2260 /*******************************************************************/
2261 if (!UseTemp) {
2262 /*****************************************************************/
2263 /* Because the chsize functionality is only accessible with a */
2264 /* system call we must close the files and reopen them with the */
2265 /* open function (_fopen for MS??) this is still to be checked */
2266 /* for compatibility with other OS's. */
2267 /*****************************************************************/
2268 char filename[_MAX_PATH];
2269 int h; // File handle, return code
2270
2271 for (int i = 0; i < Ncol; i++) {
2272 sprintf(filename, Colfn, i + 1);
2273 /*rc =*/ PlugCloseFile(g, To_Fbs[i]);
2274
2275 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
2276 return RC_FX;
2277
2278 /***************************************************************/
2279 /* Remove extra records. */
2280 /***************************************************************/
2281#if defined(UNIX)
2282 if (ftruncate(h, (off_t)(Tpos * Clens[i]))) {
2283 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2284 close(h);
2285 return RC_FX;
2286 } // endif
2287#else
2288 if (chsize(h, Tpos * Clens[i])) {
2289 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
2290 close(h);
2291 return RC_FX;
2292 } // endif
2293#endif
2294
2295 close(h);
2296
2297 if (trace(1))
2298 htrc("done, h=%d irc=%d\n", h, irc);
2299
2300 } // endfor i
2301
2302 } else // UseTemp
2303 // Ok, now delete old files and rename new temp files
2304 if (RenameTempFile(g) == RC_FX)
2305 return RC_FX;
2306
2307 // Reset these values for TDBVCT::MakeBlockValues
2308 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2309 Last = (Tpos + Nrec - 1) % Nrec + 1;
2310
2311 if (ResetTableSize(g, Block, Last))
2312 return RC_FX;
2313
2314 } // endif irc
2315
2316 return RC_OK; // All is correct
2317 } // end of DeleteRecords
2318
2319/***********************************************************************/
2320/* Open temporary files used while updating or deleting. */
2321/* Note: the files not updated have been given a T_Stream value of 1. */
2322/***********************************************************************/
2323bool VECFAM::OpenTempFile(PGLOBAL g)
2324 {
2325 char tempname[_MAX_PATH];
2326
2327 for (int i = 0; i < Ncol; i++)
2328 if (!T_Streams[i]) {
2329 /*****************************************************************/
2330 /* Open the temporary file, Spos is at the beginning of file. */
2331 /*****************************************************************/
2332 sprintf(tempname, Tempat, i+1);
2333
2334 if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
2335 if (trace(1))
2336 htrc("%s\n", g->Message);
2337
2338 return true;
2339 } else
2340 T_Fbs[i] = PlgGetUser(g)->Openlist;
2341
2342 } else // This is a column that is not updated
2343 T_Streams[i] = NULL; // For RenameTempFile
2344
2345 return false;
2346 } // end of OpenTempFile
2347
2348/***********************************************************************/
2349/* Move intermediate updated lines before writing blocks. */
2350/***********************************************************************/
2351bool VECFAM::MoveLines(PGLOBAL g)
2352 {
2353 if (UseTemp && !InitUpdate) { // Don't do it in check pass
2354 Fpos = OldBlk * Nrec;
2355
2356 if (MoveIntermediateLines(g)) {
2357 Closing = true; // ???
2358 return true;
2359 } // endif UseTemp
2360
2361// Spos = Fpos + Nrec;
2362 } // endif UseTemp
2363 return false;
2364
2365 } // end of MoveLines
2366
2367/***********************************************************************/
2368/* Move intermediate deleted or updated lines. */
2369/***********************************************************************/
2370bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *)
2371 {
2372 int i, n;
2373 bool b = false;
2374 size_t req, len;
2375
2376 for (n = Fpos - Spos; n > 0; n -= Nrec) {
2377 /*******************************************************************/
2378 /* Non consecutive line to delete. Move intermediate lines. */
2379 /*******************************************************************/
2380 req = (size_t)MY_MIN(n, Nrec);
2381
2382 for (i = 0; i < Ncol; i++) {
2383 if (!T_Streams[i])
2384 continue; // Non updated column
2385
2386 if (!UseTemp || !b)
2387 if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) {
2388 sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
2389 return true;
2390 } // endif
2391
2392 len = fread(To_Buf, Clens[i], req, Streams[i]);
2393
2394 if (trace(1))
2395 htrc("after read req=%d len=%d\n", req, len);
2396
2397 if (len != req) {
2398 sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
2399 return true;
2400 } // endif len
2401
2402 if (!UseTemp)
2403 if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) {
2404 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
2405 return true;
2406 } // endif
2407
2408 if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
2409 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
2410 return true;
2411 } // endif
2412
2413 if (trace(1))
2414 htrc("after write pos=%d\n", ftell(Streams[i]));
2415
2416 } // endfor i
2417
2418 Tpos += (int)req;
2419 Spos += (int)req;
2420
2421 if (trace(1))
2422 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
2423
2424 b = true;
2425 } // endfor n
2426
2427 return false;
2428 } // end of MoveIntermediate Lines
2429
2430/***********************************************************************/
2431/* Delete the old files and rename the new temporary files. */
2432/***********************************************************************/
2433int VECFAM::RenameTempFile(PGLOBAL g)
2434 {
2435 char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
2436 int rc = RC_OK;
2437
2438 // Close all files.
2439 // This loop is necessary because, in case of join,
2440 // the table files can have been open several times.
2441 for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
2442 rc = PlugCloseFile(g, fb);
2443
2444 for (int i = 0; i < Ncol && rc == RC_OK; i++) {
2445 if (!T_Fbs[i])
2446 continue;
2447
2448 tempname = (char*)T_Fbs[i]->Fname;
2449
2450 if (!Abort) {
2451 sprintf(filename, Colfn, i+1);
2452 PlugSetPath(filename, filename, Tdbp->GetPath());
2453 strcat(PlugRemoveType(filetemp, filename), ".ttt");
2454 remove(filetemp); // May still be there from previous error
2455
2456 if (rename(filename, filetemp)) { // Save file for security
2457 sprintf(g->Message, MSG(RENAME_ERROR),
2458 filename, filetemp, strerror(errno));
2459 rc = RC_FX;
2460 } else if (rename(tempname, filename)) {
2461 sprintf(g->Message, MSG(RENAME_ERROR),
2462 tempname, filename, strerror(errno));
2463 rc = rename(filetemp, filename); // Restore saved file
2464 rc = RC_FX;
2465 } else if (remove(filetemp)) {
2466 sprintf(g->Message, MSG(REMOVE_ERROR),
2467 filetemp, strerror(errno));
2468 rc = RC_INFO; // Acceptable
2469 } // endif's
2470
2471 } else
2472 remove(tempname);
2473
2474 } // endfor i
2475
2476 return rc;
2477 } // end of RenameTempFile
2478
2479/***********************************************************************/
2480/* Data Base close routine for VEC access method. */
2481/***********************************************************************/
2482void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
2483 {
2484 int rc = 0, wrc = RC_OK;
2485 MODE mode = Tdbp->GetMode();
2486
2487 Abort = abort;
2488
2489 if (mode == MODE_INSERT) {
2490 if (Closing)
2491 wrc = RC_FX; // Last write was in error
2492 else
2493 if (CurNum) {
2494 // Some more inserted lines remain to be written
2495 Last += (CurBlk * Nrec + CurNum -1);
2496 Block += (Last / Nrec);
2497 Last = Last % Nrec + 1;
2498 Closing = true;
2499 wrc = WriteBuffer(g);
2500 } else {
2501 Block += CurBlk;
2502 wrc = RC_OK;
2503 } // endif CurNum
2504
2505 if (wrc != RC_FX)
2506 rc = ResetTableSize(g, Block, Last);
2507 else
2508 throw 44;
2509
2510 } else if (mode == MODE_UPDATE) {
2511 if (UseTemp && !InitUpdate && !Abort) {
2512 // Write any intermediate lines to temp file
2513 Fpos = OldBlk * Nrec;
2514 Abort = MoveIntermediateLines(g) != RC_OK;
2515// Spos = Fpos + Nrec;
2516 } // endif UseTemp
2517
2518 // Write back to file any pending modifications
2519 if (wrc == RC_OK)
2520 for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
2521 colp; colp = (PVCTCOL)colp->Next)
2522 colp->WriteBlock(g);
2523
2524 if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
2525 // Write any intermediate lines to temp file
2526 Fpos = (Block - 1) * Nrec + Last;
2527 Abort = MoveIntermediateLines(g) != RC_OK;
2528 } // endif UseTemp
2529
2530 } // endif's mode
2531
2532 if (UseTemp && !InitUpdate) {
2533 // If they are errors, leave files unchanged
2534 rc = RenameTempFile(g);
2535
2536 } else if (Streams)
2537 for (int i = 0; i < Ncol; i++)
2538 if (Streams[i]) {
2539 rc = PlugCloseFile(g, To_Fbs[i]);
2540 Streams[i] = NULL;
2541 To_Fbs[i] = NULL;
2542 } // endif Streams
2543
2544 if (trace(1))
2545 htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc);
2546
2547 } // end of CloseTableFile
2548
2549/***********************************************************************/
2550/* ReadBlock: Read column values from current block. */
2551/***********************************************************************/
2552bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
2553 {
2554 int i, len;
2555 size_t n;
2556
2557 /*********************************************************************/
2558 /* Calculate the offset and size of the block to read. */
2559 /*********************************************************************/
2560 len = Nrec * colp->Clen * CurBlk;
2561 i = colp->Index - 1;
2562
2563 if (trace(1))
2564 htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n",
2565 len, i, Nrec, colp->Deplac, Lrecl, CurBlk);
2566
2567 if (fseek(Streams[i], len, SEEK_SET)) {
2568 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2569 return true;
2570 } // endif
2571
2572 n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
2573 (size_t)Nrec, Streams[i]);
2574
2575 if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) {
2576 char fn[_MAX_PATH];
2577
2578 sprintf(fn, Colfn, colp->Index);
2579#if defined(__WIN__)
2580 if (feof(Streams[i]))
2581#else // !__WIN__
2582 if (errno == NO_ERROR)
2583#endif // !__WIN__
2584 sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
2585 else
2586 sprintf(g->Message, MSG(READ_ERROR),
2587 fn, strerror(errno));
2588
2589 if (trace(1))
2590 htrc(" Read error: %s\n", g->Message);
2591
2592 return true;
2593 } // endif
2594
2595 if (trace(1))
2596 num_read++;
2597
2598 return false;
2599 } // end of ReadBlock
2600
2601/***********************************************************************/
2602/* WriteBlock: Write back current column values for one block. */
2603/* Note: the test of Status is meant to prevent physical writing of */
2604/* the block during the checking loop in mode Update. It is set to */
2605/* BUF_EMPTY when reopening the table between the two loops. */
2606/***********************************************************************/
2607bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
2608 {
2609 int i, len;
2610 size_t n;
2611
2612 /*********************************************************************/
2613 /* Calculate the offset and size of the block to write. */
2614 /*********************************************************************/
2615 len = Nrec * colp->Clen * colp->ColBlk;
2616 i = colp->Index - 1;
2617
2618 if (trace(1))
2619 htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
2620 Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
2621
2622 if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp)
2623 if (fseek(T_Streams[i], len, SEEK_SET)) {
2624 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2625 return true;
2626 } // endif
2627
2628 // Here Nrec was changed to CurNum in mode Insert,
2629 // this is the true number of records to write,
2630 // this also avoid writing garbage in the file for true vector tables.
2631 n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum
2632 : (colp->ColBlk == Block - 1) ? Last : Nrec;
2633
2634 if (n != fwrite(colp->Blk->GetValPointer(),
2635 (size_t)colp->Clen, n, T_Streams[i])) {
2636 char fn[_MAX_PATH];
2637
2638 sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
2639 sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
2640
2641 if (trace(1))
2642 htrc("Write error: %s\n", strerror(errno));
2643
2644 return true;
2645 } else
2646 Spos = Fpos + n;
2647
2648#if defined(UNIX)
2649 fflush(Streams[i]); //NGC
2650#endif
2651 return false;
2652 } // end of WriteBlock
2653
2654/* -------------------------- Class VMPFAM --------------------------- */
2655
2656/***********************************************************************/
2657/* Implementation of the VMPFAM class. */
2658/***********************************************************************/
2659VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp)
2660 {
2661 To_Fbs = NULL;
2662 Split = true;
2663 Block = Last = -1;
2664 } // end of VMPFAM standard constructor
2665
2666VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
2667 {
2668 To_Fbs = txfp->To_Fbs;
2669 } // end of VMPFAM copy constructor
2670
2671/***********************************************************************/
2672/* VCT Access Method opening routine. */
2673/* New method now that this routine is called recursively (last table */
2674/* first in reverse order): index blocks are immediately linked to */
2675/* join block of next table if it exists or else are discarted. */
2676/***********************************************************************/
2677bool VMPFAM::OpenTableFile(PGLOBAL g)
2678 {
2679 int i;
2680 bool b = false;
2681 MODE mode = Tdbp->GetMode();
2682 PCOLDEF cdp;
2683 PVCTCOL cp;
2684 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
2685
2686 if (mode == MODE_DELETE && !Tdbp->GetNext()) {
2687 DelRows = Cardinality(g);
2688
2689 // This will stop the process by causing GetProgMax to return 0.
2690 ResetTableSize(g, 0, Nrec);
2691 } else
2692 Cardinality(g); // See comment in VECFAM::OpenTbleFile
2693
2694
2695 /*********************************************************************/
2696 /* Prepare the filename pattern for column files and set Ncol. */
2697 /*********************************************************************/
2698 if (!Colfn) {
2699 // Prepare the column file name pattern
2700 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2701 Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
2702 } // endif Colfn
2703
2704 /*********************************************************************/
2705 /* Initialize the array of file structures. */
2706 /*********************************************************************/
2707 Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *));
2708 To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2709
2710 for (i = 0; i < Ncol; i++) {
2711 Memcol[i] = NULL;
2712 To_Fbs[i] = NULL;
2713 } // endif i
2714
2715 /*********************************************************************/
2716 /* Open the files corresponding to columns used in the query. */
2717 /*********************************************************************/
2718 if (mode == MODE_DELETE) {
2719 // All columns are used in Delete mode
2720 for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
2721 if (MapColumnFile(g, mode, i))
2722 return true;
2723
2724 } else {
2725 /*******************************************************************/
2726 /* Open the files corresponding to updated columns of the query. */
2727 /*******************************************************************/
2728 for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
2729 cp = (PVCTCOL)cp->Next)
2730 if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1))
2731 return true;
2732
2733 /*******************************************************************/
2734 /* Open other non already open used columns (except pseudos) */
2735 /*******************************************************************/
2736 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2737 if (!cp->IsSpecial() && !Memcol[cp->Index - 1])
2738 if (MapColumnFile(g, MODE_READ, cp->Index - 1))
2739 return true;
2740
2741 // Check for void table or missing columns
2742 for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
2743 cp = (PVCTCOL)cp->Next)
2744 if (!cp->IsSpecial()) {
2745 if (!i++)
2746 b = !Memcol[cp->Index - 1];
2747 else if (b != !Memcol[cp->Index - 1])
2748 return true;
2749
2750 } // endif Special
2751
2752 } // endif mode
2753
2754 /*********************************************************************/
2755 /* Allocate the table and column block buffer. */
2756 /*********************************************************************/
2757 return (b) ? false : AllocateBuffer(g);
2758 } // end of OpenTableFile
2759
2760/***********************************************************************/
2761/* Open the file corresponding to one column. */
2762/***********************************************************************/
2763bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
2764 {
2765 char filename[_MAX_PATH];
2766 int len;
2767 HANDLE hFile;
2768 MEMMAP mm;
2769 PFBLOCK fp;
2770 PDBUSER dup = PlgGetUser(g);
2771
2772 sprintf(filename, Colfn, i+1);
2773
2774 /*********************************************************************/
2775 /* The whole file will be mapped so we can use it as */
2776 /* if it were entirely read into virtual memory. */
2777 /* Firstly we check whether this file have been already mapped. */
2778 /*********************************************************************/
2779 if (mode == MODE_READ) {
2780 for (fp = dup->Openlist; fp; fp = fp->Next)
2781 if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
2782 && fp->Count && fp->Mode == mode)
2783 break;
2784
2785 if (trace(1))
2786 htrc("Mapping file, fp=%p\n", fp);
2787
2788 } else
2789 fp = NULL;
2790
2791 if (fp) {
2792 /*******************************************************************/
2793 /* File already mapped. Just increment use count and get pointer. */
2794 /*******************************************************************/
2795 fp->Count++;
2796 Memcol[i] = fp->Memory;
2797 len = fp->Length;
2798 } else {
2799 /*******************************************************************/
2800 /* Create the mapping file object. */
2801 /*******************************************************************/
2802 hFile = CreateFileMap(g, filename, &mm, mode, DelRows);
2803
2804 if (hFile == INVALID_HANDLE_VALUE) {
2805 DWORD rc = GetLastError();
2806
2807 if (!(*g->Message))
2808 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
2809 "map", (int) rc, filename);
2810 if (trace(1))
2811 htrc("%s\n", g->Message);
2812
2813 return (mode == MODE_READ && rc == ENOENT)
2814 ? PushWarning(g, Tdbp) : true;
2815 } // endif hFile
2816
2817 /*****************************************************************/
2818 /* Get the file size (assuming file is smaller than 4 GB) */
2819 /*****************************************************************/
2820 len = mm.lenL;
2821 Memcol[i] = (char *)mm.memory;
2822
2823 if (!len) { // Empty or deleted file
2824 CloseFileHandle(hFile);
2825 ResetTableSize(g, 0, Nrec);
2826 return false;
2827 } // endif len
2828
2829 if (!Memcol[i]) {
2830 CloseFileHandle(hFile);
2831 sprintf(g->Message, MSG(MAP_VIEW_ERROR),
2832 filename, GetLastError());
2833 return true;
2834 } // endif Memory
2835
2836 if (mode != MODE_DELETE) {
2837 CloseFileHandle(hFile); // Not used anymore
2838 hFile = INVALID_HANDLE_VALUE; // For Fblock
2839 } // endif Mode
2840
2841 /*******************************************************************/
2842 /* Link a Fblock. This make possible to reuse already opened maps */
2843 /* and also to automatically unmap them in case of error g->jump. */
2844 /* Note: block can already exist for previously closed file. */
2845 /*******************************************************************/
2846 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
2847 fp->Type = TYPE_FB_MAP;
2848 fp->Fname = PlugDup(g, filename);
2849 fp->Next = dup->Openlist;
2850 dup->Openlist = fp;
2851 fp->Count = 1;
2852 fp->Length = len;
2853 fp->Memory = Memcol[i];
2854 fp->Mode = mode;
2855 fp->File = NULL;
2856 fp->Handle = hFile; // Used for Delete
2857 } // endif fp
2858
2859 To_Fbs[i] = fp; // Useful when closing
2860
2861 if (trace(1))
2862 htrc("fp=%p count=%d MapView=%p len=%d\n",
2863 fp, fp->Count, Memcol[i], len);
2864
2865 return false;
2866 } // end of MapColumnFile
2867
2868/***********************************************************************/
2869/* Allocate the block buffers for columns used in the query. */
2870/* Give a dummy value (1) to prevent allocating the value block. */
2871/* It will be set pointing into the memory map of the file. */
2872/***********************************************************************/
2873bool VMPFAM::AllocateBuffer(PGLOBAL g)
2874 {
2875 PVCTCOL cp;
2876
2877 if (Tdbp->GetMode() == MODE_DELETE) {
2878 PCOLDEF cdp = Tdbp->GetDef()->GetCols();
2879
2880 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2881
2882 for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext())
2883 Clens[i] = cdp->GetClen();
2884
2885 } // endif mode
2886
2887 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2888 if (!cp->IsSpecial()) { // Not a pseudo column
2889 cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
2890 cp->Format.Length, cp->Format.Prec);
2891 cp->AddStatus(BUF_MAPPED);
2892 } // endif IsSpecial
2893
2894 return false;
2895 } // end of AllocateBuffer
2896
2897/***********************************************************************/
2898/* Data Base delete line routine for VMP access method. */
2899/* Lines between deleted lines are moved in the mapfile view. */
2900/***********************************************************************/
2901int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
2902 {
2903 int i;
2904 int m, n;
2905
2906 if (trace(1))
2907 htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
2908 irc, To_Buf, Tpos, Spos);
2909
2910 if (irc != RC_OK) {
2911 /*******************************************************************/
2912 /* EOF: position Fpos at the top of map position. */
2913 /*******************************************************************/
2914 Fpos = (Block - 1) * Nrec + Last;
2915
2916 if (trace(1))
2917 htrc("Fpos placed at file top=%p\n", Fpos);
2918
2919 } else // Fpos is the Deleted line position
2920 Fpos = CurBlk * Nrec + CurNum;
2921
2922 if (Tpos == Spos) {
2923 /*******************************************************************/
2924 /* First line to delete. Move of eventual preceding lines is */
2925 /* not required here, just setting of future Spos and Tpos. */
2926 /*******************************************************************/
2927 Tpos = Fpos; // Spos is set below
2928 } else if ((n = Fpos - Spos) > 0) {
2929 /*******************************************************************/
2930 /* Non consecutive line to delete. Move intermediate lines. */
2931 /*******************************************************************/
2932 for (i = 0; i < Ncol; i++) {
2933 m = Clens[i];
2934 memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n);
2935 } // endif i
2936
2937 Tpos += n;
2938
2939 if (trace(1))
2940 htrc("move %d bytes\n", n);
2941
2942 } // endif n
2943
2944 if (irc == RC_OK) {
2945 Spos = Fpos + 1; // New start position
2946
2947 if (trace(1))
2948 htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
2949
2950 } else {
2951 /*******************************************************************/
2952 /* Last call after EOF has been reached. */
2953 /* We must firstly Unmap the view and use the saved file handle */
2954 /* to put an EOF at the end of the copied part of the file. */
2955 /*******************************************************************/
2956 PFBLOCK fp;
2957
2958 /*******************************************************************/
2959 /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
2960 /*******************************************************************/
2961// Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2962// Last = (Tpos + Nrec - 1) % Nrec + 1;
2963
2964 for (i = 0; i < Ncol; i++) {
2965 fp = To_Fbs[i];
2966 CloseMemMap(fp->Memory, (size_t)fp->Length);
2967 fp->Count = 0; // Avoid doing it twice
2968
2969 /*****************************************************************/
2970 /* Remove extra records. */
2971 /*****************************************************************/
2972 n = Tpos * Clens[i];
2973
2974#if defined(__WIN__)
2975 DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
2976
2977 if (drc == 0xFFFFFFFF) {
2978 sprintf(g->Message, MSG(FUNCTION_ERROR),
2979 "SetFilePointer", GetLastError());
2980 CloseHandle(fp->Handle);
2981 return RC_FX;
2982 } // endif
2983
2984 if (trace(1))
2985 htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
2986
2987 if (!SetEndOfFile(fp->Handle)) {
2988 sprintf(g->Message, MSG(FUNCTION_ERROR),
2989 "SetEndOfFile", GetLastError());
2990 CloseHandle(fp->Handle);
2991 return RC_FX;
2992 } // endif
2993
2994 CloseHandle(fp->Handle);
2995#else // UNIX
2996 if (ftruncate(fp->Handle, (off_t)n)) {
2997 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2998 close(fp->Handle);
2999 return RC_FX;
3000 } // endif
3001
3002 close(fp->Handle);
3003#endif // UNIX
3004 } // endfor i
3005
3006 } // endif irc
3007
3008 return RC_OK; // All is correct
3009 } // end of DeleteRecords
3010
3011/***********************************************************************/
3012/* Data Base close routine for VMP access method. */
3013/***********************************************************************/
3014void VMPFAM::CloseTableFile(PGLOBAL g, bool)
3015 {
3016 if (Tdbp->GetMode() == MODE_DELETE) {
3017 // Set Block and Nrec values for TDBVCT::MakeBlockValues
3018 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3019 Last = (Tpos + Nrec - 1) % Nrec + 1;
3020 ResetTableSize(g, Block, Last);
3021 } else if (Tdbp->GetMode() == MODE_INSERT)
3022 assert(false);
3023
3024 for (int i = 0; i < Ncol; i++)
3025 PlugCloseFile(g, To_Fbs[i]);
3026
3027 } // end of CloseTableFile
3028
3029/* -------------------------- Class BGVFAM --------------------------- */
3030
3031/***********************************************************************/
3032/* Implementation of the BGVFAM class. */
3033/***********************************************************************/
3034// Constructors
3035BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp)
3036 {
3037 Hfile = INVALID_HANDLE_VALUE;
3038 Tfile = INVALID_HANDLE_VALUE;
3039 BigDep = NULL;
3040 } // end of BGVFAM constructor
3041
3042BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp)
3043 {
3044 Hfile = txfp->Hfile;
3045 Tfile = txfp->Tfile;
3046 BigDep= txfp->BigDep;
3047 } // end of BGVFAM copy constructor
3048
3049/***********************************************************************/
3050/* Set current position in a big file. */
3051/***********************************************************************/
3052bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b)
3053 {
3054#if defined(__WIN__)
3055 char buf[256];
3056 DWORD drc, m = (b) ? FILE_END : FILE_BEGIN;
3057 LARGE_INTEGER of;
3058
3059 of.QuadPart = pos;
3060 of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m);
3061
3062 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3063 (drc = GetLastError()) != NO_ERROR) {
3064 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3065 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3066 (LPTSTR)buf, sizeof(buf), NULL);
3067 sprintf(g->Message, MSG(SFP_ERROR), buf);
3068 return true;
3069 } // endif
3070#else // !__WIN__
3071 if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
3072 sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
3073 return true;
3074 } // endif
3075#endif // !__WIN__
3076
3077 return false;
3078 } // end of BigSeek
3079
3080/***********************************************************************/
3081/* Read from a big file. */
3082/***********************************************************************/
3083bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
3084 {
3085 bool rc = false;
3086
3087#if defined(__WIN__)
3088 DWORD nbr, drc, len = (DWORD)req;
3089 bool brc = ReadFile(h, inbuf, len, &nbr, NULL);
3090
3091 if (trace(1))
3092 htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
3093
3094 if (!brc || nbr != len) {
3095 char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile";
3096
3097 if (brc)
3098 strcpy(buf, MSG(BAD_BYTE_READ));
3099 else {
3100 drc = GetLastError();
3101 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3102 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3103 (LPTSTR)buf, sizeof(buf), NULL);
3104 } // endelse brc
3105
3106 sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
3107
3108 if (trace(1))
3109 htrc("BIGREAD: %s\n", g->Message);
3110
3111 rc = true;
3112 } // endif brc || nbr
3113#else // !__WIN__
3114 size_t len = (size_t)req;
3115 ssize_t nbr = read(h, inbuf, len);
3116
3117 if (nbr != (ssize_t)len) {
3118 const char *fn = (h == Hfile) ? To_File : "Tempfile";
3119
3120 sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
3121
3122 if (trace(1))
3123 htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n",
3124 nbr, len, errno, g->Message);
3125
3126 rc = true;
3127 } // endif nbr
3128#endif // !__WIN__
3129
3130 return rc;
3131 } // end of BigRead
3132
3133/***********************************************************************/
3134/* Write into a big file. */
3135/***********************************************************************/
3136bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
3137 {
3138 bool rc = false;
3139
3140#if defined(__WIN__)
3141 DWORD nbw, drc, len = (DWORD)req;
3142 bool brc = WriteFile(h, inbuf, len, &nbw, NULL);
3143
3144 if (trace(1))
3145 htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
3146
3147 if (!brc || nbw != len) {
3148 char buf[256];
3149 PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
3150
3151 if (brc)
3152 strcpy(buf, MSG(BAD_BYTE_NUM));
3153 else {
3154 drc = GetLastError();
3155 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3156 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3157 (LPTSTR)buf, sizeof(buf), NULL);
3158 } // endelse brc
3159
3160 sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
3161
3162 if (trace(1))
3163 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3164 nbw, len, drc, g->Message);
3165
3166 rc = true;
3167 } // endif brc || nbw
3168#else // !__WIN__
3169 size_t len = (size_t)req;
3170 ssize_t nbw = write(h, inbuf, len);
3171
3172 if (nbw != (ssize_t)len) {
3173 const char *fn = (h == Hfile) ? To_File : "Tempfile";
3174
3175 sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
3176
3177 if (trace(1))
3178 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3179 nbw, len, errno, g->Message);
3180
3181 rc = true;
3182 } // endif nbr
3183#endif // !__WIN__
3184
3185 return rc;
3186 } // end of BigWrite
3187
3188/***********************************************************************/
3189/* Get the Headlen, Block and Last info from the file header. */
3190/***********************************************************************/
3191int BGVFAM::GetBlockInfo(PGLOBAL g)
3192 {
3193 char filename[_MAX_PATH];
3194 int n;
3195 VECHEADER vh;
3196 HANDLE h;
3197
3198 if (Header < 1 || Header > 3 || !MaxBlk) {
3199 sprintf(g->Message, "Invalid header value %d", Header);
3200 return -1;
3201 } else
3202 n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
3203
3204 PlugSetPath(filename, To_File, Tdbp->GetPath());
3205
3206 if (Header == 2)
3207 strcat(PlugRemoveType(filename, filename), ".blk");
3208
3209#if defined(__WIN__)
3210 LARGE_INTEGER len;
3211
3212 h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
3213 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3214
3215 if (h != INVALID_HANDLE_VALUE) {
3216 // Get the size of the file (can be greater than 4 GB)
3217 len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
3218 } // endif h
3219
3220 if (h == INVALID_HANDLE_VALUE || !len.QuadPart) {
3221#else // !__WIN__
3222 h = open64(filename, O_RDONLY, 0);
3223
3224 if (h == INVALID_HANDLE_VALUE || !_filelength(h)) {
3225#endif // !__WIN__
3226 // Consider this is a void table
3227 if (trace(1))
3228 htrc("Void table h=%d\n", h);
3229
3230 Last = Nrec;
3231 Block = 0;
3232
3233 if (h != INVALID_HANDLE_VALUE)
3234 CloseFileHandle(h);
3235
3236 return n;
3237 } else if (Header == 3)
3238 /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3239
3240 if (BigRead(g, h, &vh, sizeof(vh))) {
3241 sprintf(g->Message, "Error reading header file %s", filename);
3242 n = -1;
3243 } else if (MaxBlk * Nrec != vh.MaxRec) {
3244 sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
3245 vh.MaxRec, MaxBlk, Nrec);
3246 n = -1;
3247 } else {
3248 Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
3249 Last = (vh.NumRec + Nrec - 1) % Nrec + 1;
3250
3251 if (trace(1))
3252 htrc("Block=%d Last=%d\n", Block, Last);
3253
3254 } // endif's
3255
3256 CloseFileHandle(h);
3257 return n;
3258 } // end of GetBlockInfo
3259
3260/***********************************************************************/
3261/* Set the MaxRec and NumRec info in the file header. */
3262/***********************************************************************/
3263bool BGVFAM::SetBlockInfo(PGLOBAL g)
3264 {
3265 char filename[_MAX_PATH];
3266 bool b = false, rc = false;
3267 VECHEADER vh;
3268 HANDLE h = INVALID_HANDLE_VALUE;
3269
3270 PlugSetPath(filename, To_File, Tdbp->GetPath());
3271
3272 if (Header != 2) {
3273 if (Hfile != INVALID_HANDLE_VALUE) {
3274 h = Hfile;
3275
3276 if (Header == 1)
3277 /*bk =*/ BigSeek(g, h, (BIGINT)0);
3278
3279 } else
3280 b = true;
3281
3282 } else // Header == 2
3283 strcat(PlugRemoveType(filename, filename), ".blk");
3284
3285 if (h == INVALID_HANDLE_VALUE) {
3286#if defined(__WIN__)
3287 DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING;
3288
3289 h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0,
3290 NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
3291
3292#else // !__WIN__
3293 int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC;
3294
3295 h = open64(filename, oflag, 0);
3296#endif // !__WIN__
3297
3298 if (h == INVALID_HANDLE_VALUE) {
3299 sprintf(g->Message, "Error opening header file %s", filename);
3300 return true;
3301 } // endif h
3302
3303 } // endif h
3304
3305 if (Header == 3)
3306 /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3307
3308 vh.MaxRec = MaxBlk * Bsize;
3309 vh.NumRec = (Block - 1) * Nrec + Last;
3310
3311 if (BigWrite(g, h, &vh, sizeof(vh))) {
3312 sprintf(g->Message, "Error writing header file %s", filename);
3313 rc = true;
3314 } // endif fread
3315
3316 if (Header == 2 || Hfile == INVALID_HANDLE_VALUE)
3317 CloseFileHandle(h);
3318
3319 return rc;
3320 } // end of SetBlockInfo
3321
3322/***********************************************************************/
3323/* VEC Create an empty file for new Vector formatted tables. */
3324/***********************************************************************/
3325bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
3326 {
3327 // Vector formatted file this will create an empty file of the
3328 // required length if it does not exists yet.
3329 char filename[_MAX_PATH], c = 0;
3330 int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
3331
3332 PlugSetPath(filename, fn, Tdbp->GetPath());
3333
3334#if defined(__WIN__)
3335 PCSZ p;
3336 DWORD rc;
3337 bool brc;
3338 LARGE_INTEGER of;
3339 HANDLE h;
3340
3341 h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3342 FILE_ATTRIBUTE_NORMAL, NULL);
3343
3344 if (h == INVALID_HANDLE_VALUE) {
3345 p = MSG(OPENING);
3346 goto err;
3347 } // endif h
3348
3349 of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3350
3351 if (trace(1))
3352 htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n",
3353 of.QuadPart, n, MaxBlk, Blksize);
3354
3355 of.LowPart = SetFilePointer(h, of.LowPart,
3356 &of.HighPart, FILE_BEGIN);
3357
3358 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3359 GetLastError() != NO_ERROR) {
3360 p = MSG(MAKING);
3361 goto err;
3362 } // endif
3363
3364 brc = WriteFile(h, &c, 1, &rc, NULL);
3365
3366 if (!brc || rc != 1) {
3367 p = MSG(WRITING);
3368 goto err;
3369 } // endif
3370
3371 CloseHandle(h);
3372 return false;
3373
3374 err:
3375 rc = GetLastError();
3376 sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
3377 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3378 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3379 (LPTSTR)filename, sizeof(filename), NULL);
3380 strcat(g->Message, filename);
3381
3382 if (h != INVALID_HANDLE_VALUE)
3383 CloseHandle(h);
3384
3385 return true;
3386#else // !__WIN__
3387 int h;
3388 BIGINT pos;
3389
3390 h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
3391
3392 if (h == -1)
3393 return true;
3394
3395 pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3396
3397 if (trace(1))
3398 htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
3399 pos, n, MaxBlk, Blksize);
3400
3401 if (lseek64(h, pos, SEEK_SET) < 0)
3402 goto err;
3403
3404 // This actually fills the empty file
3405 if (write(h, &c, 1) < 0)
3406 goto err;
3407
3408 close(h);
3409 return false;
3410
3411 err:
3412 sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
3413 close(h);
3414 return true;
3415#endif // !__WIN__
3416 } // end of MakeEmptyFile
3417
3418/***********************************************************************/
3419/* Vopen function: opens a file using Windows or Unix API's. */
3420/***********************************************************************/
3421bool BGVFAM::OpenTableFile(PGLOBAL g)
3422 {
3423 char filename[_MAX_PATH];
3424 bool del = false;
3425 MODE mode = Tdbp->GetMode();
3426 PDBUSER dbuserp = PlgGetUser(g);
3427
3428 if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
3429 sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
3430 return true;
3431 } // endif
3432
3433 /*********************************************************************/
3434 /* Update block info if necessary. */
3435 /*********************************************************************/
3436 if (Block < 0)
3437 if ((Headlen = GetBlockInfo(g)) < 0)
3438 return true;
3439
3440 PlugSetPath(filename, To_File, Tdbp->GetPath());
3441
3442 if (trace(1))
3443 htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
3444 filename, mode, Last);
3445
3446#if defined(__WIN__)
3447 DWORD access, creation, share = 0, rc = 0;
3448
3449 /*********************************************************************/
3450 /* Create the file object according to access mode */
3451 /*********************************************************************/
3452 switch (mode) {
3453 case MODE_READ:
3454 access = GENERIC_READ;
3455 share = FILE_SHARE_READ;
3456 creation = OPEN_EXISTING;
3457 break;
3458 case MODE_INSERT:
3459 if (MaxBlk) {
3460 if (!Block)
3461 if (MakeEmptyFile(g, To_File))
3462 return true;
3463
3464 // Required to update empty blocks
3465 access = GENERIC_READ | GENERIC_WRITE;
3466 } else if (Last == Nrec)
3467 access = GENERIC_WRITE;
3468 else
3469 // Required to update the last block
3470 access = GENERIC_READ | GENERIC_WRITE;
3471
3472 creation = OPEN_ALWAYS;
3473 break;
3474 case MODE_DELETE:
3475 if (!Tdbp->GetNext()) {
3476 // Store the number of deleted lines
3477 DelRows = Cardinality(g);
3478
3479 // This will stop the process by
3480 // causing GetProgMax to return 0.
3481// ResetTableSize(g, 0, Nrec); must be done later
3482 del = true;
3483
3484 // This will delete the whole file
3485 access = GENERIC_READ | GENERIC_WRITE;
3486 creation = TRUNCATE_EXISTING;
3487 break;
3488 } // endif
3489
3490 // Selective delete, pass thru
3491 case MODE_UPDATE:
3492 if ((UseTemp = Tdbp->IsUsingTemp(g)))
3493 access = GENERIC_READ;
3494 else
3495 access = GENERIC_READ | GENERIC_WRITE;
3496
3497 creation = OPEN_EXISTING;
3498 break;
3499 default:
3500 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3501 return true;
3502 } // endswitch
3503
3504 /*********************************************************************/
3505 /* Use specific Windows API functions. */
3506 /*********************************************************************/
3507 Hfile = CreateFile(filename, access, share, NULL, creation,
3508 FILE_ATTRIBUTE_NORMAL, NULL);
3509
3510 if (Hfile == INVALID_HANDLE_VALUE) {
3511 rc = GetLastError();
3512 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3513 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3514 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3515 (LPTSTR)filename, sizeof(filename), NULL);
3516 strcat(g->Message, filename);
3517 } // endif Hfile
3518
3519 if (trace(1))
3520 htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
3521 rc, access, share, creation, Hfile, filename);
3522
3523 if (mode == MODE_INSERT) {
3524 /*******************************************************************/
3525 /* In Insert mode we must position the cursor at end of file. */
3526 /*******************************************************************/
3527 LARGE_INTEGER of;
3528
3529 of.QuadPart = (BIGINT)0;
3530 of.LowPart = SetFilePointer(Hfile, of.LowPart,
3531 &of.HighPart, FILE_END);
3532
3533 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3534 (rc = GetLastError()) != NO_ERROR) {
3535 sprintf(g->Message, MSG(ERROR_IN_SFP), rc);
3536 CloseHandle(Hfile);
3537 Hfile = INVALID_HANDLE_VALUE;
3538 } // endif
3539
3540 } // endif Mode
3541
3542#else // UNIX
3543 /*********************************************************************/
3544 /* The open() function has a transitional interface for 64-bit */
3545 /* file offsets. Note that using open64() is equivalent to using */
3546 /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */
3547 /*********************************************************************/
3548 int rc = 0;
3549 int oflag;
3550 mode_t pmd = 0;
3551
3552 /*********************************************************************/
3553 /* Create the file object according to access mode */
3554 /*********************************************************************/
3555 switch (mode) {
3556 case MODE_READ:
3557 oflag = O_RDONLY;
3558 break;
3559 case MODE_INSERT:
3560 if (MaxBlk) {
3561 if (!Block)
3562 if (MakeEmptyFile(g, To_File))
3563 return true;
3564
3565 // Required to update empty blocks
3566 oflag = O_RDWR;
3567 } else if (Last == Nrec)
3568 oflag = O_WRONLY | O_CREAT | O_APPEND;
3569 else
3570 // Required to update the last block
3571 oflag = O_RDWR | O_CREAT | O_APPEND;
3572
3573 pmd = S_IREAD | S_IWRITE;
3574 break;
3575 case MODE_DELETE:
3576 // This is temporary until a partial delete is implemented
3577 if (!Tdbp->GetNext()) {
3578 // Store the number of deleted lines
3579 DelRows = Cardinality(g);
3580 del = true;
3581
3582 // This will delete the whole file and provoque ReadDB to
3583 // return immediately.
3584 oflag = O_RDWR | O_TRUNC;
3585 strcpy(g->Message, MSG(NO_VCT_DELETE));
3586 break;
3587 } // endif
3588
3589 // Selective delete, pass thru
3590 /* fall through */
3591 case MODE_UPDATE:
3592 UseTemp = Tdbp->IsUsingTemp(g);
3593 oflag = (UseTemp) ? O_RDONLY : O_RDWR;
3594 break;
3595 default:
3596 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3597 return true;
3598 } // endswitch
3599
3600 Hfile = open64(filename, oflag, pmd); // Enable file size > 2G
3601
3602 if (Hfile == INVALID_HANDLE_VALUE) {
3603 rc = errno;
3604 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3605 strcat(g->Message, strerror(errno));
3606 } // endif Hfile
3607
3608 if (trace(1))
3609 htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n",
3610 rc, oflag, mode, Hfile, filename);
3611#endif // UNIX
3612
3613 if (!rc) {
3614 if (!To_Fb) {
3615 To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3616 To_Fb->Fname = To_File;
3617 To_Fb->Type = TYPE_FB_HANDLE;
3618 To_Fb->Memory = NULL;
3619 To_Fb->Length = 0;
3620 To_Fb->File = NULL;
3621 To_Fb->Next = dbuserp->Openlist;
3622 dbuserp->Openlist = To_Fb;
3623 } // endif To_Fb
3624
3625 To_Fb->Count = 1;
3626 To_Fb->Mode = mode;
3627 To_Fb->Handle = Hfile;
3628
3629 if (trace(1))
3630 htrc("File %s is open in mode %d\n", filename, mode);
3631
3632 if (del)
3633 // This will stop the process by
3634 // causing GetProgMax to return 0.
3635 return ResetTableSize(g, 0, Nrec);
3636
3637 /*********************************************************************/
3638 /* Allocate the table and column block buffers. */
3639 /*********************************************************************/
3640 return AllocateBuffer(g);
3641 } else
3642 return (mode == MODE_READ && rc == ENOENT)
3643 ? PushWarning(g, Tdbp) : true;
3644
3645 } // end of OpenTableFile
3646
3647/***********************************************************************/
3648/* Allocate the block buffers for columns used in the query. */
3649/***********************************************************************/
3650bool BGVFAM::AllocateBuffer(PGLOBAL g)
3651 {
3652 MODE mode = Tdbp->GetMode();
3653 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
3654 PCOLDEF cdp;
3655 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3656
3657 if (mode == MODE_INSERT) {
3658 if (!NewBlock) {
3659 // Not reopening after inserting the last block
3660 bool chk = PlgGetUser(g)->Check & CHK_TYPE;
3661
3662 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3663
3664 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3665 memset(NewBlock + Nrec * cdp->GetPoff(),
3666 (IsTypeNum(cdp->GetType()) ? 0 : ' '),
3667 Nrec * cdp->GetClen());
3668
3669 for (; cp; cp = (PVCTCOL)cp->Next)
3670 cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
3671 cp->Buf_Type, Nrec, cp->Format.Length,
3672 cp->Format.Prec, chk);
3673
3674 InitInsert(g); // Initialize inserting
3675
3676 // Currently we don't use a temporary file for inserting
3677 Tfile = Hfile;
3678 } // endif NewBlock
3679
3680 } else {
3681 if (UseTemp || mode == MODE_DELETE) {
3682 // Allocate all that is needed to move lines
3683 int i = 0;
3684
3685 if (!Ncol)
3686 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3687 Ncol++;
3688
3689 if (MaxBlk)
3690 BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT));
3691 else
3692 Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3693
3694 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3695 Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
3696
3697 for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
3698 if (MaxBlk)
3699 BigDep[i] = (BIGINT)Headlen
3700 + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk;
3701 else
3702 Deplac[i] = cdp->GetPoff() * Nrec;
3703
3704 Clens[i] = cdp->GetClen();
3705 Isnum[i] = IsTypeNum(cdp->GetType());
3706 Buflen = MY_MAX(Buflen, cdp->GetClen());
3707 } // endfor cdp
3708
3709 if (!UseTemp || MaxBlk) {
3710 Buflen *= Nrec;
3711 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
3712 } else
3713 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3714
3715 } // endif mode
3716
3717 for (; cp; cp = (PVCTCOL)cp->Next)
3718 if (!cp->IsSpecial()) // Not a pseudo column
3719 cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
3720 cp->Format.Length, cp->Format.Prec);
3721
3722 } //endif mode
3723
3724 return false;
3725 } // end of AllocateBuffer
3726
3727/***********************************************************************/
3728/* Data Base write routine for huge VCT access method. */
3729/***********************************************************************/
3730int BGVFAM::WriteBuffer(PGLOBAL g)
3731 {
3732 if (trace(1))
3733 htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n",
3734 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
3735
3736 if (Tdbp->GetMode() == MODE_UPDATE) {
3737 // Mode Update is done in ReadDB, we just initialize it here
3738 if (Tfile == INVALID_HANDLE_VALUE) {
3739 if (UseTemp) {
3740 if (OpenTempFile(g))
3741 return RC_FX;
3742
3743 // Most of the time, not all table columns are updated.
3744 // This why we must completely pre-fill the temporary file.
3745 Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
3746 : Block * Nrec; // To write last lock
3747
3748 if (MoveIntermediateLines(g))
3749 return RC_FX;
3750
3751 } else
3752 Tfile = Hfile;
3753
3754 } // endif Tfile
3755
3756 } else {
3757 // Mode Insert
3758 if (MaxBlk && CurBlk == MaxBlk) {
3759 strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
3760 return RC_EF; // Too many lines for a Vector formatted table
3761 } // endif MaxBlk
3762
3763 if (Closing || ++CurNum == Nrec) {
3764 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3765
3766 if (!AddBlock) {
3767 // Write back the updated last block values
3768 for (; cp; cp = (PVCTCOL)cp->Next)
3769 cp->WriteBlock(g);
3770
3771 if (!Closing && !MaxBlk) {
3772 // Close the VCT file and reopen it in mode Insert
3773//#if defined(__WIN__) //OB
3774// CloseHandle(Hfile);
3775//#else // UNIX
3776// close(Hfile);
3777//#endif // UNIX
3778 CloseFileHandle(Hfile);
3779 Hfile = INVALID_HANDLE_VALUE;
3780 To_Fb->Count = 0;
3781 Last = Nrec; // Tested in OpenTableFile
3782
3783 if (OpenTableFile(g)) {
3784 Closing = true; // Tell CloseDB of error
3785 return RC_FX;
3786 } // endif Vopen
3787
3788 AddBlock = true;
3789 } // endif Closing
3790
3791 } else {
3792 // Here we must add a new block to the VCT file
3793 if (Closing)
3794 // Reset the overwritten columns for last block extra records
3795 for (; cp; cp = (PVCTCOL)cp->Next)
3796 memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
3797 (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
3798 (Nrec - Last) * cp->Clen);
3799
3800 if (BigWrite(g, Hfile, NewBlock, Blksize))
3801 return RC_FX;
3802
3803 } // endif AddBlock
3804
3805 if (!Closing) {
3806 CurBlk++;
3807 CurNum = 0;
3808 } // endif Closing
3809
3810 } // endif
3811
3812 } // endif Mode
3813
3814 return RC_OK;
3815 } // end of WriteBuffer
3816
3817/***********************************************************************/
3818/* Data Base delete line routine for BGVFAM access method. */
3819/***********************************************************************/
3820int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
3821 {
3822 bool eof = false;
3823
3824 /*********************************************************************/
3825 /* There is an alternative here depending on UseTemp: */
3826 /* 1 - use a temporary file in which are copied all not deleted */
3827 /* lines, at the end the original file will be deleted and */
3828 /* the temporary file renamed to the original file name. */
3829 /* 2 - directly move the not deleted lines inside the original */
3830 /* file, and at the end erase all trailing records. */
3831 /*********************************************************************/
3832 if (trace(1))
3833 htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
3834 irc, UseTemp, Fpos, Tpos, Spos);
3835
3836 if (irc != RC_OK) {
3837 /*******************************************************************/
3838 /* EOF: position Fpos at the end-of-file position. */
3839 /*******************************************************************/
3840 Fpos = (Block - 1) * Nrec + Last;
3841
3842 if (trace(1))
3843 htrc("Fpos placed at file end=%d\n", Fpos);
3844
3845 eof = UseTemp && !MaxBlk;
3846 } else // Fpos is the deleted line position
3847 Fpos = CurBlk * Nrec + CurNum;
3848
3849 if (Tpos == Spos) {
3850 if (UseTemp) {
3851 /*****************************************************************/
3852 /* Open the temporary file, Spos is at the beginning of file. */
3853 /*****************************************************************/
3854 if (OpenTempFile(g))
3855 return RC_FX;
3856
3857 } else {
3858 /*****************************************************************/
3859 /* Move of eventual preceding lines is not required here. */
3860 /* Set the target file as being the source file itself. */
3861 /* Set the future Tpos, and give Spos a value to block copying. */
3862 /*****************************************************************/
3863 Tfile = Hfile;
3864 Spos = Tpos = Fpos;
3865 } // endif UseTemp
3866
3867 } // endif Tpos == Spos
3868
3869 /*********************************************************************/
3870 /* Move any intermediate lines. */
3871 /*********************************************************************/
3872 if (MoveIntermediateLines(g, &eof))
3873 return RC_FX;
3874
3875 if (irc == RC_OK) {
3876#ifdef _DEBUG
3877 assert(Spos == Fpos);
3878#endif
3879 Spos++; // New start position is on next line
3880
3881 if (trace(1))
3882 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
3883
3884 } else {
3885 /*******************************************************************/
3886 /* Last call after EOF has been reached. */
3887 /*******************************************************************/
3888 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3889 Last = (Tpos + Nrec - 1) % Nrec + 1;
3890
3891 if (!UseTemp) { // The UseTemp case is treated in CloseTableFile
3892 if (!MaxBlk) {
3893 if (Last < Nrec) // Clean last block
3894 if (CleanUnusedSpace(g))
3895 return RC_FX;
3896
3897 /***************************************************************/
3898 /* Remove extra records. */
3899 /***************************************************************/
3900#if defined(__WIN__)
3901 BIGINT pos = (BIGINT)Block * (BIGINT)Blksize;
3902
3903 if (BigSeek(g, Hfile, pos))
3904 return RC_FX;
3905
3906 if (!SetEndOfFile(Hfile)) {
3907 DWORD drc = GetLastError();
3908
3909 sprintf(g->Message, MSG(SETEOF_ERROR), drc);
3910 return RC_FX;
3911 } // endif error
3912#else // !__WIN__
3913 if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
3914 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3915 return RC_FX;
3916 } // endif
3917#endif // !__WIN__
3918 } else // MaxBlk
3919 // Clean the unused space in the file, this is required when
3920 // inserting again with a partial column list.
3921 if (CleanUnusedSpace(g))
3922 return RC_FX;
3923
3924 if (ResetTableSize(g, Block, Last))
3925 return RC_FX;
3926
3927 } // endif UseTemp
3928
3929 } // endif irc
3930
3931 return RC_OK; // All is correct
3932 } // end of DeleteRecords
3933
3934/***********************************************************************/
3935/* Open a temporary file used while updating or deleting. */
3936/***********************************************************************/
3937bool BGVFAM::OpenTempFile(PGLOBAL g)
3938 {
3939 char *tempname;
3940 PDBUSER dup = PlgGetUser(g);
3941
3942 /*********************************************************************/
3943 /* Open the temporary file, Spos is at the beginning of file. */
3944 /*********************************************************************/
3945 tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
3946 PlugSetPath(tempname, To_File, Tdbp->GetPath());
3947 strcat(PlugRemoveType(tempname, tempname), ".t");
3948
3949 if (!MaxBlk)
3950 remove(tempname); // Be sure it does not exist yet
3951 else if (MakeEmptyFile(g, tempname))
3952 return true;
3953
3954#if defined(__WIN__)
3955 DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW;
3956
3957 Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
3958 access, FILE_ATTRIBUTE_NORMAL, NULL);
3959
3960 if (Tfile == INVALID_HANDLE_VALUE) {
3961 DWORD rc = GetLastError();
3962 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname);
3963 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3964 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3965 (LPTSTR)tempname, _MAX_PATH, NULL);
3966 strcat(g->Message, tempname);
3967 return true;
3968 } // endif Tfile
3969#else // UNIX
3970 int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC;
3971
3972 Tfile = open64(tempname, oflag, S_IWRITE);
3973
3974 if (Tfile == INVALID_HANDLE_VALUE) {
3975 int rc = errno;
3976 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
3977 strcat(g->Message, strerror(errno));
3978 return true;
3979 } //endif Tfile
3980#endif // UNIX
3981
3982 To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3983 To_Fbt->Fname = tempname;
3984 To_Fbt->Type = TYPE_FB_HANDLE;
3985 To_Fbt->Memory = NULL;
3986 To_Fbt->Length = 0;
3987 To_Fbt->File = NULL;
3988 To_Fbt->Next = dup->Openlist;
3989 To_Fbt->Count = 1;
3990 To_Fbt->Mode = MODE_INSERT;
3991 To_Fbt->Handle = Tfile;
3992 dup->Openlist = To_Fbt;
3993 return false;
3994 } // end of OpenTempFile
3995
3996/***********************************************************************/
3997/* Move intermediate deleted or updated lines. */
3998/***********************************************************************/
3999bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
4000 {
4001 int i, n, req, dep;
4002 bool eof = (b) ? *b : false;
4003 BIGINT pos;
4004
4005 for (n = Fpos - Spos; n > 0 || eof; n -= req) {
4006 /*******************************************************************/
4007 /* Non consecutive line to delete. Move intermediate lines. */
4008 /*******************************************************************/
4009 if (!MaxBlk)
4010 req = (DWORD)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
4011 else
4012 req = (DWORD)MY_MIN(n, Nrec);
4013
4014 if (req) for (i = 0; i < Ncol; i++) {
4015 if (!MaxBlk) {
4016 if (UseTemp)
4017 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4018
4019 pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i])
4020 + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize;
4021 } else
4022 pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i];
4023
4024 if (BigSeek(g, Hfile, pos))
4025 return true;
4026
4027 if (BigRead(g, Hfile, To_Buf, req * Clens[i]))
4028 return true;
4029
4030 if (!UseTemp || MaxBlk) {
4031 if (!MaxBlk)
4032 pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i])
4033 + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize;
4034 else
4035 pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4036
4037 if (BigSeek(g, Tfile, pos))
4038 return true;
4039
4040 if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4041 return true;
4042
4043 } // endif UseTemp
4044
4045 } // endfor i
4046
4047 Tpos += (int)req;
4048 Spos += (int)req;
4049
4050 if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) {
4051 // Write the full or last block to the temporary file
4052 if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
4053 // Clean the last block in case of future insert, must be
4054 // done here because Tfile was open in write only.
4055 for (i = 0; i < Ncol; i++) {
4056 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4057 memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
4058 } // endfor i
4059
4060 if (BigWrite(g, Tfile, NewBlock, Blksize))
4061 return true;
4062
4063 if (Spos == Fpos)
4064 eof = false;
4065
4066 } // endif Usetemp...
4067
4068 if (trace(1))
4069 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
4070
4071 } // endfor n
4072
4073 return false;
4074 } // end of MoveIntermediateLines
4075
4076/***********************************************************************/
4077/* Clean deleted space in a huge VCT or Vec table file. */
4078/***********************************************************************/
4079bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
4080 {
4081 int i;
4082 int n;
4083 BIGINT pos, dep;
4084
4085 if (!MaxBlk) {
4086 /*******************************************************************/
4087 /* Clean last block of the VCT table file. */
4088 /*******************************************************************/
4089 assert(!UseTemp); // This case is handled in MoveIntermediateLines
4090
4091 if (!(n = Nrec - Last))
4092 return false;
4093
4094 dep = (BIGINT)((Block - 1) * Blksize);
4095
4096 for (i = 0; i < Ncol; i++) {
4097 memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
4098 pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]);
4099
4100 if (BigSeek(g, Hfile, pos))
4101 return true;
4102
4103 if (BigWrite(g, Hfile, To_Buf, n * Clens[i]))
4104 return true;
4105
4106 } // endfor i
4107
4108 } else {
4109 int req;
4110
4111 memset(To_Buf, 0, Buflen);
4112
4113 for (n = Fpos - Tpos; n > 0; n -= req) {
4114 /*****************************************************************/
4115 /* Fill VEC file remaining lines with 0's. */
4116 /* This seems to work even column blocks have been made with */
4117 /* Blanks = true. Perhaps should it be set to false for VEC. */
4118 /*****************************************************************/
4119 req = MY_MIN(n, Nrec);
4120
4121 for (i = 0; i < Ncol; i++) {
4122 pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4123
4124 if (BigSeek(g, Tfile, pos))
4125 return true;
4126
4127 if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4128 return true;
4129
4130 } // endfor i
4131
4132 Tpos += req;
4133 } // endfor n
4134
4135 } // endif MaxBlk
4136
4137 return false;
4138 } // end of CleanUnusedSpace
4139
4140/***********************************************************************/
4141/* Data Base close routine for huge VEC access method. */
4142/***********************************************************************/
4143void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
4144 {
4145 int rc = 0, wrc = RC_OK;
4146 MODE mode = Tdbp->GetMode();
4147
4148 Abort = abort;
4149
4150 if (mode == MODE_INSERT) {
4151 if (Closing)
4152 wrc = RC_FX; // Last write was in error
4153 else
4154 if (CurNum) {
4155 // Some more inserted lines remain to be written
4156 Last = CurNum;
4157 Block = CurBlk + 1;
4158 Closing = true;
4159 wrc = WriteBuffer(g);
4160 } else {
4161 Last = Nrec;
4162 Block = CurBlk;
4163 wrc = RC_OK;
4164 } // endif CurNum
4165
4166 if (wrc != RC_FX) {
4167 rc = ResetTableSize(g, Block, Last);
4168 } else if (AddBlock) {
4169 // Last block was not written
4170 rc = ResetTableSize(g, CurBlk, Nrec);
4171 throw 44;
4172 } // endif
4173
4174 } else if (mode == MODE_UPDATE) {
4175 // Write back to file any pending modifications
4176 for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols();
4177 colp; colp = (PVCTCOL)colp->Next)
4178 colp->WriteBlock(g);
4179
4180 if (UseTemp && Tfile) {
4181 rc = RenameTempFile(g);
4182 Hfile = Tfile = INVALID_HANDLE_VALUE;
4183
4184 if (Header)
4185 // Header must be set because it was not set in temp file
4186 rc = SetBlockInfo(g);
4187
4188 } // endif UseTemp
4189
4190 } else if (mode == MODE_DELETE && UseTemp && Tfile) {
4191 if (MaxBlk)
4192 rc = CleanUnusedSpace(g);
4193
4194 if ((rc = RenameTempFile(g)) != RC_FX) {
4195 Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo
4196 rc = ResetTableSize(g, Block, Last);
4197 } // endif rc
4198
4199 } // endif's mode
4200
4201 if (Hfile != INVALID_HANDLE_VALUE)
4202 rc = PlugCloseFile(g, To_Fb);
4203
4204 if (trace(1))
4205 htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n",
4206 To_File, wrc, rc);
4207
4208 Hfile = INVALID_HANDLE_VALUE;
4209 } // end of CloseDB
4210
4211/***********************************************************************/
4212/* Rewind routine for huge VCT access method. */
4213/***********************************************************************/
4214void BGVFAM::Rewind(void)
4215 {
4216 // In mode update we need to read Set Column blocks
4217 if (Tdbp->GetMode() == MODE_UPDATE)
4218 OldBlk = -1;
4219
4220 // Initialize so block optimization is called for 1st block
4221 CurBlk = -1;
4222 CurNum = Nrec - 1;
4223
4224#if 0 // This is probably unuseful as the file is directly accessed
4225#if defined(__WIN__) //OB
4226 SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
4227#else // UNIX
4228 lseek64(Hfile, 0, SEEK_SET);
4229#endif // UNIX
4230#endif // 0
4231 } // end of Rewind
4232
4233/***********************************************************************/
4234/* ReadBlock: Read column values from current block. */
4235/***********************************************************************/
4236bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
4237 {
4238 BIGINT pos;
4239
4240 /*********************************************************************/
4241 /* Calculate the offset and size of the block to read. */
4242 /*********************************************************************/
4243 if (MaxBlk) // File has Vector format
4244 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4245 + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen;
4246 else // Old VCT format
4247 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4248 + (BIGINT)Lrecl * (BIGINT)CurBlk);
4249
4250 if (trace(1))
4251 htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n",
4252 pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
4253
4254 if (BigSeek(g, Hfile, pos))
4255 return true;
4256
4257 if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec))
4258 return true;
4259
4260 if (trace(1))
4261 num_read++;
4262
4263 return false;
4264 } // end of ReadBlock
4265
4266/***********************************************************************/
4267/* WriteBlock: Write back current column values for one block. */
4268/* Note: the test of Status is meant to prevent physical writing of */
4269/* the block during the checking loop in mode Update. It is set to */
4270/* BUF_EMPTY when reopening the table between the two loops. */
4271/***********************************************************************/
4272bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
4273 {
4274 int len;
4275 BIGINT pos;
4276
4277 /*********************************************************************/
4278 /* Calculate the offset and size of the block to write. */
4279 /*********************************************************************/
4280 if (MaxBlk) // File has Vector format
4281 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4282 + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen;
4283 else // Old VCT format
4284 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4285 + (BIGINT)Lrecl * (BIGINT)colp->ColBlk);
4286
4287 if (trace(1))
4288 htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n",
4289 pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
4290
4291 if (BigSeek(g, Tfile, pos))
4292 return true;
4293
4294//len = colp->Clen * Nrec; see comment in VCTFAM
4295 len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec);
4296
4297 if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len))
4298 return true;
4299
4300 return false;
4301 } // end of WriteBlock
4302
4303/* ----------------------- End of FilAMVct --------------------------- */
4304