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