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
1168 defp->SetBlock(Block);
1169 defp->SetLast(Last);
1170
1171 if (!defp->SetIntCatInfo("Blocks", Block) ||
1172 !defp->SetIntCatInfo("Last", Last)) {
1173 sprintf(g->Message, MSG(UPDATE_ERROR), "Header");
1174 rc = true;
1175 } // endif
1176
1177 } else
1178 rc = SetBlockInfo(g);
1179
1180 } // endif Split
1181
1182 Tdbp->ResetSize();
1183 return rc;
1184 } // end of ResetTableSize
1185
1186 /***********************************************************************/
1187 /* Rewind routine for VCT access method. */
1188 /***********************************************************************/
Rewind(void)1189 void VCTFAM::Rewind(void)
1190 {
1191 // In mode update we need to read Set Column blocks
1192 if (Tdbp->GetMode() == MODE_UPDATE)
1193 OldBlk = -1;
1194
1195 // Initialize so block optimization is called for 1st block
1196 CurBlk = -1;
1197 CurNum = Nrec - 1;
1198 //rewind(Stream); will be placed by fseek
1199 } // end of Rewind
1200
1201 /***********************************************************************/
1202 /* ReadBlock: Read column values from current block. */
1203 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)1204 bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
1205 {
1206 int len;
1207 size_t n;
1208
1209 /*********************************************************************/
1210 /* Calculate the offset and size of the block to read. */
1211 /*********************************************************************/
1212 if (MaxBlk) // True vector format
1213 len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk);
1214 else // Blocked vector format
1215 len = Nrec * (colp->Deplac + Lrecl * CurBlk);
1216
1217 if (trace(1))
1218 htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n",
1219 len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
1220
1221 if (fseek(Stream, len, SEEK_SET)) {
1222 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1223 return true;
1224 } // endif
1225
1226 n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
1227 (size_t)Nrec, Stream);
1228
1229 if (n != (size_t)Nrec) {
1230 if (errno == NO_ERROR)
1231 sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File);
1232 else
1233 sprintf(g->Message, MSG(READ_ERROR),
1234 To_File, strerror(errno));
1235
1236 if (trace(1))
1237 htrc(" Read error: %s\n", g->Message);
1238
1239 return true;
1240 } // endif
1241
1242 if (trace(1))
1243 num_read++;
1244
1245 return false;
1246 } // end of ReadBlock
1247
1248 /***********************************************************************/
1249 /* WriteBlock: Write back current column values for one block. */
1250 /* Note: the test of Status is meant to prevent physical writing of */
1251 /* the block during the checking loop in mode Update. It is set to */
1252 /* BUF_EMPTY when reopening the table between the two loops. */
1253 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)1254 bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
1255 {
1256 int len;
1257 size_t n;
1258
1259 /*********************************************************************/
1260 /* Calculate the offset and size of the block to write. */
1261 /*********************************************************************/
1262 if (MaxBlk) // File has Vector format
1263 len = Headlen
1264 + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk);
1265 else // Old VCT format
1266 len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk);
1267
1268 if (trace(1))
1269 htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
1270 Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
1271
1272 if (fseek(T_Stream, len, SEEK_SET)) {
1273 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
1274 return true;
1275 } // endif
1276
1277 // Here Nrec was changed to CurNum in mode Insert,
1278 // this is the true number of records to write,
1279 // this also avoid writing garbage in the file for true vector tables.
1280 n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec;
1281
1282 if (n != fwrite(colp->Blk->GetValPointer(),
1283 (size_t)colp->Clen, n, T_Stream)) {
1284 sprintf(g->Message, MSG(WRITE_STRERROR),
1285 (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno));
1286
1287 if (trace(1))
1288 htrc("Write error: %s\n", strerror(errno));
1289
1290 return true;
1291 } // endif
1292
1293 #if defined(UNIX)
1294 fflush(T_Stream); //NGC
1295 #endif
1296
1297 #ifdef _DEBUG
1298 num_write++;
1299 #endif
1300
1301 return false;
1302 } // end of WriteBlock
1303
1304 /* -------------------------- Class VCMFAM --------------------------- */
1305
1306 /***********************************************************************/
1307 /* Implementation of the VCMFAM class. */
1308 /***********************************************************************/
VCMFAM(PVCTDEF tdp)1309 VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1310 {
1311 Memory = NULL;
1312 Memcol = NULL;
1313 } // end of VCMFAM standard constructor
1314
VCMFAM(PVCMFAM txfp)1315 VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp)
1316 {
1317 Memory = txfp->Memory;
1318 Memcol = txfp->Memcol;
1319 } // end of VCMFAM copy constructor
1320
1321 /***********************************************************************/
1322 /* Mapped VCT Access Method opening routine. */
1323 /* New method now that this routine is called recursively (last table */
1324 /* first in reverse order): index blocks are immediately linked to */
1325 /* join block of next table if it exists or else are discarted. */
1326 /***********************************************************************/
OpenTableFile(PGLOBAL g)1327 bool VCMFAM::OpenTableFile(PGLOBAL g)
1328 {
1329 char filename[_MAX_PATH];
1330 size_t len;
1331 MODE mode = Tdbp->GetMode();
1332 PFBLOCK fp = NULL;
1333 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1334
1335 /*********************************************************************/
1336 /* Update block info if necessary. */
1337 /*********************************************************************/
1338 if (Block < 0)
1339 if ((Headlen = GetBlockInfo(g)) < 0)
1340 return true;
1341
1342 /*********************************************************************/
1343 /* We used the file name relative to recorded datapath. */
1344 /*********************************************************************/
1345 PlugSetPath(filename, To_File, Tdbp->GetPath());
1346
1347 /*********************************************************************/
1348 /* The whole file will be mapped so we can use it as if it were */
1349 /* entirely read into virtual memory. */
1350 /* Firstly we check whether this file have been already mapped. */
1351 /*********************************************************************/
1352 if (mode == MODE_READ) {
1353 for (fp = dbuserp->Openlist; fp; fp = fp->Next)
1354 if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
1355 && fp->Count && fp->Mode == mode)
1356 break;
1357
1358 if (trace(1))
1359 htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count);
1360
1361 } else
1362 fp = NULL;
1363
1364 if (fp) {
1365 /*******************************************************************/
1366 /* File already mapped. Just increment use count and get pointer. */
1367 /*******************************************************************/
1368 fp->Count++;
1369 Memory = fp->Memory;
1370 len = fp->Length;
1371 } else {
1372 /*******************************************************************/
1373 /* If required, delete the whole file if no filtering is implied. */
1374 /*******************************************************************/
1375 bool del;
1376 HANDLE hFile;
1377 MEMMAP mm;
1378 MODE mapmode = mode;
1379
1380 if (mode == MODE_INSERT) {
1381 if (MaxBlk) {
1382 if (!Block)
1383 if (MakeEmptyFile(g, To_File))
1384 return true;
1385
1386 // Inserting will be like updating the file
1387 mapmode = MODE_UPDATE;
1388 } else {
1389 strcpy(g->Message, "MAP Insert is for VEC Estimate tables only");
1390 return true;
1391 } // endif MaxBlk
1392
1393 } // endif mode
1394
1395 del = mode == MODE_DELETE && !Tdbp->GetNext();
1396
1397 if (del) {
1398 DelRows = Cardinality(g);
1399
1400 // This will stop the process by causing GetProgMax to return 0.
1401 // ResetTableSize(g, 0, Nrec); must be done later
1402 } // endif del
1403
1404 /*******************************************************************/
1405 /* Create the mapping file object. */
1406 /*******************************************************************/
1407 hFile = CreateFileMap(g, filename, &mm, mapmode, del);
1408
1409 if (hFile == INVALID_HANDLE_VALUE) {
1410 DWORD rc = GetLastError();
1411
1412 if (!(*g->Message))
1413 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1414 "map", (int) rc, filename);
1415
1416 if (trace(1))
1417 htrc("%s\n", g->Message);
1418
1419 return (mode == MODE_READ && rc == ENOENT)
1420 ? PushWarning(g, Tdbp) : true;
1421 } // endif hFile
1422
1423 /*******************************************************************/
1424 /* Get the file size. */
1425 /*******************************************************************/
1426 len = (size_t)mm.lenL;
1427
1428 if (mm.lenH)
1429 len += ((size_t)mm.lenH * 0x000000001LL);
1430
1431 Memory = (char *)mm.memory;
1432
1433 if (!len) { // Empty or deleted file
1434 CloseFileHandle(hFile);
1435 bool rc = ResetTableSize(g, 0, Nrec);
1436 return (mapmode == MODE_UPDATE) ? true : rc;
1437 } // endif len
1438
1439 if (!Memory) {
1440 CloseFileHandle(hFile);
1441 sprintf(g->Message, MSG(MAP_VIEW_ERROR),
1442 filename, GetLastError());
1443 return true;
1444 } // endif Memory
1445
1446 if (mode != MODE_DELETE) {
1447 CloseFileHandle(hFile); // Not used anymore
1448 hFile = INVALID_HANDLE_VALUE; // For Fblock
1449 } // endif Mode
1450
1451 /*******************************************************************/
1452 /* Link a Fblock. This make possible to reuse already opened maps */
1453 /* and also to automatically unmap them in case of error g->jump. */
1454 /* Note: block can already exist for previously closed file. */
1455 /*******************************************************************/
1456 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
1457 fp->Type = TYPE_FB_MAP;
1458 fp->Fname = PlugDup(g, filename);
1459 fp->Next = dbuserp->Openlist;
1460 dbuserp->Openlist = fp;
1461 fp->Count = 1;
1462 fp->Length = len;
1463 fp->Memory = Memory;
1464 fp->Mode = mode;
1465 fp->File = NULL;
1466 fp->Handle = hFile; // Used for Delete
1467 } // endif fp
1468
1469 To_Fb = fp; // Useful when closing
1470
1471 if (trace(1))
1472 htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
1473 fp, fp->Count, Memory, len);
1474
1475 return AllocateBuffer(g);
1476 } // end of OpenTableFile
1477
1478 /***********************************************************************/
1479 /* Allocate the block buffers for columns used in the query. */
1480 /* Give a dummy value (1) to prevent allocating the value block. */
1481 /* It will be set pointing into the memory map of the file. */
1482 /* Note: Memcol must be set for all columns because it can be used */
1483 /* for set columns in Update. Clens values are used only in Delete. */
1484 /***********************************************************************/
AllocateBuffer(PGLOBAL g)1485 bool VCMFAM::AllocateBuffer(PGLOBAL g)
1486 {
1487 int m, i = 0;
1488 bool b = Tdbp->GetMode() == MODE_DELETE;
1489 PVCTCOL cp;
1490 PCOLDEF cdp;
1491 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1492
1493 // Calculate the number of columns
1494 if (!Ncol)
1495 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
1496 Ncol++;
1497
1498 // To store the start position of each column
1499 Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*));
1500 m = (MaxBlk) ? MaxBlk : 1;
1501
1502 // We will need all column sizes and type for Delete
1503 if (b) {
1504 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
1505 Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
1506 } // endif b
1507
1508 for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) {
1509 if (b) {
1510 Clens[i] = cdp->GetClen();
1511 Isnum[i] = IsTypeNum(cdp->GetType());
1512 } // endif b
1513
1514 Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec;
1515 } // endfor cdp
1516
1517 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1518 if (!cp->IsSpecial()) { // Not a pseudo column
1519 cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
1520 cp->Format.Length, cp->Format.Prec,
1521 true, true, cp->IsUnsigned());
1522 cp->AddStatus(BUF_MAPPED);
1523 } // endif IsSpecial
1524
1525 if (Tdbp->GetMode() == MODE_INSERT)
1526 return InitInsert(g);
1527
1528 return false;
1529 } // end of AllocateBuffer
1530
1531 /***********************************************************************/
1532 /* Do initial action when inserting. */
1533 /***********************************************************************/
InitInsert(PGLOBAL g)1534 bool VCMFAM::InitInsert(PGLOBAL g)
1535 {
1536 bool rc = false;
1537 volatile PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1538
1539 // We come here in MODE_INSERT only
1540 if (Last == Nrec) {
1541 CurBlk = Block;
1542 CurNum = 0;
1543 AddBlock = !MaxBlk;
1544 } else {
1545 // The starting point must be at the end of file as for append.
1546 CurBlk = Block - 1;
1547 CurNum = Last;
1548 } // endif Last
1549
1550 try {
1551 // Initialize the column block pointer
1552 for (; cp; cp = (PVCTCOL)cp->Next)
1553 cp->ReadBlock(g);
1554
1555 } catch (int n) {
1556 if (trace(1))
1557 htrc("Exception %d: %s\n", n, g->Message);
1558 rc = true;
1559 } catch (const char *msg) {
1560 strcpy(g->Message, msg);
1561 rc = true;
1562 } // end catch
1563
1564 return rc;
1565 } // end of InitInsert
1566
1567 /***********************************************************************/
1568 /* Data Base write routine for VMP access method. */
1569 /***********************************************************************/
WriteBuffer(PGLOBAL g)1570 int VCMFAM::WriteBuffer(PGLOBAL g)
1571 {
1572 if (trace(1))
1573 htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
1574 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
1575
1576 // Mode Update being done in ReadDB we process here Insert mode only.
1577 if (Tdbp->GetMode() == MODE_INSERT) {
1578 if (CurBlk == MaxBlk) {
1579 strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
1580 return RC_EF; // Too many lines for vector formatted table
1581 } // endif MaxBlk
1582
1583 if (Closing || ++CurNum == Nrec) {
1584 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
1585
1586 // Write back the updated last block values
1587 for (; cp; cp = (PVCTCOL)cp->Next)
1588 cp->WriteBlock(g);
1589
1590 if (!Closing) {
1591 CurBlk++;
1592 CurNum = 0;
1593
1594 // Re-initialize the column block pointer
1595 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1596 cp->ReadBlock(g);
1597
1598 } // endif Closing
1599
1600 } // endif Closing || CurNum
1601
1602 } // endif Mode
1603
1604 return RC_OK;
1605 } // end of WriteBuffer
1606
1607 /***********************************************************************/
1608 /* Data Base delete line routine for VMP access method. */
1609 /* Lines between deleted lines are moved in the mapfile view. */
1610 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)1611 int VCMFAM::DeleteRecords(PGLOBAL g, int irc)
1612 {
1613 if (trace(1))
1614 htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
1615 irc, To_Buf, Tpos, Spos);
1616
1617 if (irc != RC_OK) {
1618 /*******************************************************************/
1619 /* EOF: position Fpos at the top of map position. */
1620 /*******************************************************************/
1621 Fpos = (Block - 1) * Nrec + Last;
1622
1623 if (trace(1))
1624 htrc("Fpos placed at file top=%p\n", Fpos);
1625
1626 } else // Fpos is the Deleted line position
1627 Fpos = CurBlk * Nrec + CurNum;
1628
1629 if (Tpos == Spos) {
1630 /*******************************************************************/
1631 /* First line to delete. Move of eventual preceding lines is */
1632 /* not required here, just setting of future Spos and Tpos. */
1633 /*******************************************************************/
1634 Tpos = Spos = Fpos;
1635 } else
1636 (void)MoveIntermediateLines(g);
1637
1638 if (irc == RC_OK) {
1639 Spos = Fpos + 1; // New start position
1640
1641 if (trace(1))
1642 htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
1643
1644 } else {
1645 /*******************************************************************/
1646 /* Last call after EOF has been reached. */
1647 /*******************************************************************/
1648 int i, m, n;
1649
1650 /*******************************************************************/
1651 /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
1652 /*******************************************************************/
1653 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
1654 Last = (Tpos + Nrec - 1) % Nrec + 1;
1655
1656 if (!MaxBlk) {
1657 PFBLOCK fp = To_Fb;
1658
1659 // Clean the unused part of the last block
1660 m = (Block - 1) * Blksize;
1661 n = Nrec - Last;
1662
1663 for (i = 0; i < Ncol; i++)
1664 memset(Memcol[i] + m + Last * Clens[i],
1665 (Isnum[i]) ? 0 : ' ', n * Clens[i]);
1666
1667 // We must Unmap the view and use the saved file handle
1668 // to put an EOF at the end of the last block of the file.
1669 CloseMemMap(fp->Memory, (size_t)fp->Length);
1670 fp->Count = 0; // Avoid doing it twice
1671
1672 // Remove extra blocks
1673 n = Block * Blksize;
1674
1675 #if defined(_WIN32)
1676 DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
1677
1678 if (drc == 0xFFFFFFFF) {
1679 sprintf(g->Message, MSG(FUNCTION_ERROR),
1680 "SetFilePointer", GetLastError());
1681 CloseHandle(fp->Handle);
1682 return RC_FX;
1683 } // endif
1684
1685 if (trace(1))
1686 htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
1687
1688 if (!SetEndOfFile(fp->Handle)) {
1689 sprintf(g->Message, MSG(FUNCTION_ERROR),
1690 "SetEndOfFile", GetLastError());
1691 CloseHandle(fp->Handle);
1692 return RC_FX;
1693 } // endif
1694
1695 CloseHandle(fp->Handle);
1696 #else // UNIX
1697 if (ftruncate(fp->Handle, (off_t)n)) {
1698 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
1699 close(fp->Handle);
1700 return RC_FX;
1701 } // endif
1702
1703 close(fp->Handle);
1704 #endif // UNIX
1705 } else
1706 // True vector table, Table file size does not change.
1707 // Just clean the unused part of the file.
1708 for (n = Fpos - Tpos, i = 0; i < Ncol; i++)
1709 memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]);
1710
1711 // Reset Last and Block values in the catalog
1712 PlugCloseFile(g, To_Fb); // in case of Header
1713 ResetTableSize(g, Block, Last);
1714 } // endif irc
1715
1716 return RC_OK; // All is correct
1717 } // end of DeleteRecords
1718
1719 /***********************************************************************/
1720 /* Move intermediate deleted or updated lines. */
1721 /***********************************************************************/
MoveIntermediateLines(PGLOBAL,bool *)1722 bool VCMFAM::MoveIntermediateLines(PGLOBAL, bool *)
1723 {
1724 int i, m, n;
1725
1726 if ((n = Fpos - Spos) > 0) {
1727 /*******************************************************************/
1728 /* Non consecutive line to delete. Move intermediate lines. */
1729 /*******************************************************************/
1730 if (!MaxBlk) {
1731 // Old VCT format, moving must respect block limits
1732 char *ps, *pt;
1733 int req, soff, toff;
1734
1735 for (; n > 0; n -= req) {
1736 soff = Spos % Nrec;
1737 toff = Tpos % Nrec;
1738 req = (size_t)MY_MIN(n, Nrec - MY_MAX(soff, toff));
1739
1740 for (i = 0; i < Ncol; i++) {
1741 ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i];
1742 pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i];
1743 memmove(pt, ps, req * Clens[i]);
1744 } // endfor i
1745
1746 Tpos += req;
1747 Spos += req;
1748 } // endfor n
1749
1750 } else {
1751 // True vector format, all is simple...
1752 for (i = 0; i < Ncol; i++) {
1753 m = Clens[i];
1754 memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m);
1755 } // endfor i
1756
1757 Tpos += n;
1758 } // endif MaxBlk
1759
1760 if (trace(1))
1761 htrc("move %d bytes\n", n);
1762
1763 } // endif n
1764
1765 return false;
1766 } // end of MoveIntermediate Lines
1767
1768 /***********************************************************************/
1769 /* Data Base close routine for VMP access method. */
1770 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)1771 void VCMFAM::CloseTableFile(PGLOBAL g, bool)
1772 {
1773 int wrc = RC_OK;
1774 MODE mode = Tdbp->GetMode();
1775
1776 if (mode == MODE_INSERT) {
1777 if (!Closing) {
1778 if (CurNum) {
1779 // Some more inserted lines remain to be written
1780 Last = CurNum;
1781 Block = CurBlk + 1;
1782 Closing = true;
1783 wrc = WriteBuffer(g);
1784 } else {
1785 Last = Nrec;
1786 Block = CurBlk;
1787 wrc = RC_OK;
1788 } // endif CurNum
1789
1790 } else
1791 wrc = RC_FX; // Last write was in error
1792
1793 PlugCloseFile(g, To_Fb);
1794
1795 if (wrc != RC_FX)
1796 /*rc =*/ ResetTableSize(g, Block, Last);
1797
1798 } else if (mode != MODE_DELETE || Abort)
1799 PlugCloseFile(g, To_Fb);
1800
1801 } // end of CloseTableFile
1802
1803 /***********************************************************************/
1804 /* ReadBlock: Read column values from current block. */
1805 /***********************************************************************/
ReadBlock(PGLOBAL,PVCTCOL colp)1806 bool VCMFAM::ReadBlock(PGLOBAL, PVCTCOL colp)
1807 {
1808 char *mempos;
1809 int i = colp->Index - 1;
1810 int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl);
1811
1812 /*********************************************************************/
1813 /* Calculate the start position of the column block to read. */
1814 /*********************************************************************/
1815 mempos = Memcol[i] + n * CurBlk;
1816
1817 if (trace(1))
1818 htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n",
1819 mempos, i, Nrec, colp->Clen, CurBlk);
1820
1821 if (colp->GetStatus(BUF_MAPPED))
1822 colp->Blk->SetValPointer(mempos);
1823
1824 if (trace(1))
1825 num_read++;
1826
1827 return false;
1828 } // end of ReadBlock
1829
1830 /***********************************************************************/
1831 /* WriteBlock: Write back current column values for one block. */
1832 /* Note: there is nothing to do because we are working directly into */
1833 /* the mapped file, except when checking for Update but in this case */
1834 /* we do not want to write back the modifications either. */
1835 /***********************************************************************/
WriteBlock(PGLOBAL,PVCTCOL colp)1836 bool VCMFAM::WriteBlock(PGLOBAL, PVCTCOL colp __attribute__((unused)))
1837 {
1838 #if defined(_DEBUG)
1839 char *mempos;
1840 int i = colp->Index - 1;
1841 int n = Nrec * colp->Clen;
1842
1843 /*********************************************************************/
1844 /* Calculate the offset and size of the block to write. */
1845 /*********************************************************************/
1846 mempos = Memcol[i] + n * CurBlk;
1847
1848 if (trace(1))
1849 htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n",
1850 Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk);
1851
1852 #endif // _DEBUG
1853
1854 return false;
1855 } // end of WriteBlock
1856
1857 /* -------------------------- Class VECFAM --------------------------- */
1858
1859 /***********************************************************************/
1860 /* Implementation of the VECFAM class. */
1861 /***********************************************************************/
VECFAM(PVCTDEF tdp)1862 VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp)
1863 {
1864 Streams = NULL;
1865 To_Fbs = NULL;
1866 To_Bufs = NULL;
1867 Split = true;
1868 Block = Last = -1;
1869 InitUpdate = false;
1870 } // end of VECFAM standard constructor
1871
VECFAM(PVECFAM txfp)1872 VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp)
1873 {
1874 Streams = txfp->Streams;
1875 To_Fbs = txfp->To_Fbs;
1876 Clens = txfp->Clens;
1877 To_Bufs = txfp->To_Bufs;
1878 InitUpdate = txfp->InitUpdate;
1879 } // end of VECFAM copy constructor
1880
1881 /***********************************************************************/
1882 /* VEC Access Method opening routine. */
1883 /* New method now that this routine is called recursively (last table */
1884 /* first in reverse order): index blocks are immediately linked to */
1885 /* join block of next table if it exists or else are discarted. */
1886 /***********************************************************************/
OpenTableFile(PGLOBAL g)1887 bool VECFAM::OpenTableFile(PGLOBAL g)
1888 {
1889 char opmode[4];
1890 int i;
1891 bool b= false;
1892 PCOLDEF cdp;
1893 PVCTCOL cp;
1894 MODE mode = Tdbp->GetMode();
1895 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
1896
1897 /*********************************************************************/
1898 /* Call Cardinality to set Block and Last values in case it was not */
1899 /* already called (this happens indeed in test xmode) */
1900 /*********************************************************************/
1901 Cardinality(g);
1902
1903 /*********************************************************************/
1904 /* Open according to input/output mode required. */
1905 /*********************************************************************/
1906 switch (mode) {
1907 case MODE_READ:
1908 strcpy(opmode, "rb");
1909 break;
1910 case MODE_DELETE:
1911 if (!Tdbp->GetNext()) {
1912 // Store the number of deleted lines
1913 DelRows = Cardinality(g);
1914
1915 // This will delete the whole file
1916 strcpy(opmode, "wb");
1917
1918 // This will stop the process by causing GetProgMax to return 0.
1919 ResetTableSize(g, 0, Nrec);
1920 break;
1921 } // endif filter
1922
1923 // Selective delete, pass thru
1924 /* fall through */
1925 case MODE_UPDATE:
1926 UseTemp = Tdbp->IsUsingTemp(g);
1927 strcpy(opmode, (UseTemp) ? "rb": "r+b");
1928 break;
1929 case MODE_INSERT:
1930 strcpy(opmode, "ab");
1931 break;
1932 default:
1933 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
1934 return true;
1935 } // endswitch Mode
1936
1937 /*********************************************************************/
1938 /* Initialize the array of file structures. */
1939 /*********************************************************************/
1940 if (!Colfn) {
1941 // Prepare the column file name pattern and set Ncol
1942 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
1943 Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
1944 } // endif Colfn
1945
1946 Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
1947 To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
1948
1949 for (i = 0; i < Ncol; i++) {
1950 Streams[i] = NULL;
1951 To_Fbs[i] = NULL;
1952 } // endif i
1953
1954 /*********************************************************************/
1955 /* Open the files corresponding to columns used in the query. */
1956 /*********************************************************************/
1957 if (mode == MODE_INSERT || mode == MODE_DELETE) {
1958 // All columns must be written or deleted
1959 for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
1960 if (OpenColumnFile(g, opmode, i))
1961 return true;
1962
1963 // Check for void table or missing columns
1964 for (b = !Streams[0], i = 1; i < Ncol; i++)
1965 if (b != !Streams[i])
1966 return true;
1967
1968 } else {
1969 /*******************************************************************/
1970 /* Open the files corresponding to updated columns of the query. */
1971 /*******************************************************************/
1972 for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
1973 cp = (PVCTCOL)cp->Next)
1974 if (OpenColumnFile(g, opmode, cp->Index - 1))
1975 return true;
1976
1977 // Open in read only mode the used columns not already open
1978 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
1979 if (!cp->IsSpecial() && !Streams[cp->Index - 1])
1980 if (OpenColumnFile(g, "rb", cp->Index - 1))
1981 return true;
1982
1983 // Check for void table or missing columns
1984 for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
1985 cp = (PVCTCOL)cp->Next)
1986 if (!cp->IsSpecial()) {
1987 if (!i++)
1988 b = !Streams[cp->Index - 1];
1989 else if (b != !Streams[cp->Index - 1])
1990 return true;
1991
1992 } // endif Special
1993
1994 } // endif mode
1995
1996 /*********************************************************************/
1997 /* Allocate the table and column block buffer. */
1998 /*********************************************************************/
1999 return (b) ? false : AllocateBuffer(g);
2000 } // end of OpenTableFile
2001
2002 /***********************************************************************/
2003 /* Open the file corresponding to one column. */
2004 /***********************************************************************/
OpenColumnFile(PGLOBAL g,PCSZ opmode,int i)2005 bool VECFAM::OpenColumnFile(PGLOBAL g, PCSZ opmode, int i)
2006 {
2007 char filename[_MAX_PATH];
2008 PDBUSER dup = PlgGetUser(g);
2009
2010 sprintf(filename, Colfn, i+1);
2011
2012 if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) {
2013 if (trace(1))
2014 htrc("%s\n", g->Message);
2015
2016 return (Tdbp->GetMode() == MODE_READ && errno == ENOENT)
2017 ? PushWarning(g, Tdbp) : true;
2018 } // endif Streams
2019
2020 if (trace(1))
2021 htrc("File %s is open in mode %s\n", filename, opmode);
2022
2023 To_Fbs[i] = dup->Openlist; // Keep track of File blocks
2024 return false;
2025 } // end of OpenColumnFile
2026
2027 /***********************************************************************/
2028 /* Allocate the block buffers for columns used in the query. */
2029 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2030 bool VECFAM::AllocateBuffer(PGLOBAL g)
2031 {
2032 int i;
2033 PVCTCOL cp;
2034 PCOLDEF cdp;
2035 PTDBVCT tdbp = (PTDBVCT)Tdbp;
2036 MODE mode = tdbp->GetMode();
2037 PDOSDEF defp = (PDOSDEF)tdbp->GetDef();
2038
2039 if (mode != MODE_READ) {
2040 // Allocate what is needed by all modes except Read
2041 T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*));
2042 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2043
2044 // Give default values
2045 for (i = 0; i < Ncol; i++) {
2046 T_Streams[i] = Streams[i];
2047 Clens[i] = 0;
2048 } // endfor i
2049
2050 } // endif mode
2051
2052 if (mode == MODE_INSERT) {
2053 bool chk = PlgGetUser(g)->Check & CHK_TYPE;
2054
2055 To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*));
2056 cdp = defp->GetCols();
2057
2058 for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2059 Clens[i] = cdp->GetClen();
2060 To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]);
2061
2062 if (cdp->GetType() == TYPE_STRING)
2063 memset(To_Bufs[i], ' ', Nrec * Clens[i]);
2064 else
2065 memset(To_Bufs[i], 0, Nrec * Clens[i]);
2066
2067 } // endfor cdp
2068
2069 for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2070 cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1],
2071 cp->Buf_Type, Nrec, cp->Format.Length,
2072 cp->Format.Prec, chk, true, cp->IsUnsigned());
2073
2074 return InitInsert(g);
2075 } else {
2076 if (UseTemp || mode == MODE_DELETE) {
2077 // Allocate all that is needed to move lines and make Temp
2078 if (UseTemp) {
2079 Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2080 strcpy(Tempat, Colfn);
2081 PlugSetPath(Tempat, Tempat, Tdbp->GetPath());
2082 strcat(PlugRemoveType(Tempat, Tempat), ".t");
2083 T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2084 } // endif UseTemp
2085
2086 if (UseTemp)
2087 for (i = 0; i < Ncol; i++) {
2088 T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL;
2089 T_Fbs[i] = NULL;
2090 } // endfor i
2091
2092 if (mode == MODE_DELETE) { // All columns are moved
2093 cdp = defp->GetCols();
2094
2095 for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) {
2096 Clens[i] = cdp->GetClen();
2097 Buflen = MY_MAX(Buflen, cdp->GetClen());
2098 } // endfor cdp
2099
2100 } else { // Mode Update, only some columns are updated
2101 for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) {
2102 i = cp->Index -1;
2103
2104 if (UseTemp)
2105 T_Streams[i] = NULL; // Mark the streams to open
2106
2107 Clens[i] = cp->Clen;
2108 Buflen = MY_MAX(Buflen, cp->Clen);
2109 } // endfor cp
2110
2111 InitUpdate = true; // To be initialized
2112 } // endif mode
2113
2114 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec);
2115 } // endif mode
2116
2117 // Finally allocate column buffers for all modes
2118 for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next)
2119 if (!cp->IsSpecial()) // Not a pseudo column
2120 cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
2121 cp->Format.Length, cp->Format.Prec,
2122 true, true, cp->IsUnsigned());
2123
2124 } // endif mode
2125
2126 return false;
2127 } // end of AllocateBuffer
2128
2129 /***********************************************************************/
2130 /* Do initial action when inserting. */
2131 /***********************************************************************/
InitInsert(PGLOBAL)2132 bool VECFAM::InitInsert(PGLOBAL)
2133 {
2134 // We come here in MODE_INSERT only
2135 CurBlk = 0;
2136 CurNum = 0;
2137 AddBlock = true;
2138 return false;
2139 } // end of InitInsert
2140
2141 /***********************************************************************/
2142 /* Reset buffer access according to indexing and to mode. */
2143 /* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */
2144 /***********************************************************************/
ResetBuffer(PGLOBAL g)2145 void VECFAM::ResetBuffer(PGLOBAL g)
2146 {
2147 /*********************************************************************/
2148 /* If access is random, performances can be much better when the */
2149 /* reads are done on only one row, except for small tables that can */
2150 /* be entirely read in one block. If the index is just used as a */
2151 /* bitmap filter, as for Update or Delete, reading will be */
2152 /* sequential and we better keep block reading. */
2153 /*********************************************************************/
2154 if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) {
2155 Nrec = 1; // Better for random access
2156 Rbuf = 0;
2157 OldBlk = -2; // Has no meaning anymore
2158 Block = Tdbp->Cardinality(g); // Blocks are one line now
2159 Last = 1; // Probably unuseful
2160 } // endif Mode
2161
2162 } // end of ResetBuffer
2163
2164 /***********************************************************************/
2165 /* Data Base write routine for VCT access method. */
2166 /***********************************************************************/
WriteBuffer(PGLOBAL g)2167 int VECFAM::WriteBuffer(PGLOBAL g)
2168 {
2169 if (trace(1))
2170 htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n",
2171 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
2172
2173 if (Tdbp->GetMode() == MODE_INSERT) {
2174 if (Closing || ++CurNum == Nrec) {
2175 // Here we must add a new blocks to the files
2176 int i;
2177 size_t n = (size_t)CurNum;
2178
2179 for (i = 0; i < Ncol; i++)
2180 if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) {
2181 sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno));
2182 return RC_FX;
2183 } // endif
2184
2185 if (!Closing) {
2186 CurBlk++;
2187 CurNum = 0;
2188 } // endif Closing
2189
2190 } // endif Closing || CurNum
2191
2192 } else // Mode Update
2193 // Writing updates being done in ReadDB we do initialization only.
2194 if (InitUpdate) {
2195 if (OpenTempFile(g))
2196 return RC_FX;
2197
2198 InitUpdate = false; // Done
2199 } // endif InitUpdate
2200
2201 return RC_OK;
2202 } // end of WriteBuffer
2203
2204 /***********************************************************************/
2205 /* Data Base delete line routine for split vertical access methods. */
2206 /* Note: lines are moved directly in the files (ooops...) */
2207 /* Using temp file depends on the Check setting, false by default. */
2208 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2209 int VECFAM::DeleteRecords(PGLOBAL g, int irc)
2210 {
2211 if (trace(1))
2212 htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
2213 irc, UseTemp, Fpos, Tpos, Spos);
2214
2215 if (irc != RC_OK) {
2216 /*******************************************************************/
2217 /* EOF: position Fpos at the end-of-file position. */
2218 /*******************************************************************/
2219 Fpos = Cardinality(g);
2220
2221 if (trace(1))
2222 htrc("Fpos placed at file end=%d\n", Fpos);
2223
2224 } else // Fpos is the Deleted line position
2225 Fpos = CurBlk * Nrec + CurNum;
2226
2227 if (Tpos == Spos) {
2228 // First line to delete
2229 if (UseTemp) {
2230 /*****************************************************************/
2231 /* Open the temporary files, Spos is at the beginning of file. */
2232 /*****************************************************************/
2233 if (OpenTempFile(g))
2234 return RC_FX;
2235
2236 } else
2237 /*****************************************************************/
2238 /* Move of eventual preceding lines is not required here. */
2239 /* Set the future Tpos, and give Spos a value to block copying. */
2240 /*****************************************************************/
2241 Spos = Tpos = Fpos;
2242
2243 } // endif Tpos == Spos
2244
2245 /*********************************************************************/
2246 /* Move any intermediate lines. */
2247 /*********************************************************************/
2248 if (MoveIntermediateLines(g))
2249 return RC_FX;
2250
2251 if (irc == RC_OK) {
2252 #ifdef _DEBUG
2253 assert(Spos == Fpos);
2254 #endif
2255 Spos++; // New start position is on next line
2256
2257 if (trace(1))
2258 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
2259
2260 } else {
2261 /*******************************************************************/
2262 /* Last call after EOF has been reached. */
2263 /*******************************************************************/
2264 if (!UseTemp) {
2265 /*****************************************************************/
2266 /* Because the chsize functionality is only accessible with a */
2267 /* system call we must close the files and reopen them with the */
2268 /* open function (_fopen for MS??) this is still to be checked */
2269 /* for compatibility with other OS's. */
2270 /*****************************************************************/
2271 char filename[_MAX_PATH];
2272 int h; // File handle, return code
2273
2274 for (int i = 0; i < Ncol; i++) {
2275 sprintf(filename, Colfn, i + 1);
2276 /*rc =*/ PlugCloseFile(g, To_Fbs[i]);
2277
2278 if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
2279 return RC_FX;
2280
2281 /***************************************************************/
2282 /* Remove extra records. */
2283 /***************************************************************/
2284 #if defined(UNIX)
2285 if (ftruncate(h, (off_t)(Tpos * Clens[i]))) {
2286 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
2287 close(h);
2288 return RC_FX;
2289 } // endif
2290 #else
2291 if (chsize(h, Tpos * Clens[i])) {
2292 sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
2293 close(h);
2294 return RC_FX;
2295 } // endif
2296 #endif
2297
2298 close(h);
2299
2300 if (trace(1))
2301 htrc("done, h=%d irc=%d\n", h, irc);
2302
2303 } // endfor i
2304
2305 } else // UseTemp
2306 // Ok, now delete old files and rename new temp files
2307 if (RenameTempFile(g) == RC_FX)
2308 return RC_FX;
2309
2310 // Reset these values for TDBVCT::MakeBlockValues
2311 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2312 Last = (Tpos + Nrec - 1) % Nrec + 1;
2313
2314 if (ResetTableSize(g, Block, Last))
2315 return RC_FX;
2316
2317 } // endif irc
2318
2319 return RC_OK; // All is correct
2320 } // end of DeleteRecords
2321
2322 /***********************************************************************/
2323 /* Open temporary files used while updating or deleting. */
2324 /* Note: the files not updated have been given a T_Stream value of 1. */
2325 /***********************************************************************/
OpenTempFile(PGLOBAL g)2326 bool VECFAM::OpenTempFile(PGLOBAL g)
2327 {
2328 char tempname[_MAX_PATH];
2329
2330 for (int i = 0; i < Ncol; i++)
2331 if (!T_Streams[i]) {
2332 /*****************************************************************/
2333 /* Open the temporary file, Spos is at the beginning of file. */
2334 /*****************************************************************/
2335 sprintf(tempname, Tempat, i+1);
2336
2337 if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) {
2338 if (trace(1))
2339 htrc("%s\n", g->Message);
2340
2341 return true;
2342 } else
2343 T_Fbs[i] = PlgGetUser(g)->Openlist;
2344
2345 } else // This is a column that is not updated
2346 T_Streams[i] = NULL; // For RenameTempFile
2347
2348 return false;
2349 } // end of OpenTempFile
2350
2351 /***********************************************************************/
2352 /* Move intermediate updated lines before writing blocks. */
2353 /***********************************************************************/
MoveLines(PGLOBAL g)2354 bool VECFAM::MoveLines(PGLOBAL g)
2355 {
2356 if (UseTemp && !InitUpdate) { // Don't do it in check pass
2357 Fpos = OldBlk * Nrec;
2358
2359 if (MoveIntermediateLines(g)) {
2360 Closing = true; // ???
2361 return true;
2362 } // endif UseTemp
2363
2364 // Spos = Fpos + Nrec;
2365 } // endif UseTemp
2366 return false;
2367
2368 } // end of MoveLines
2369
2370 /***********************************************************************/
2371 /* Move intermediate deleted or updated lines. */
2372 /***********************************************************************/
MoveIntermediateLines(PGLOBAL g,bool *)2373 bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *)
2374 {
2375 int i, n;
2376 bool b = false;
2377 size_t req, len;
2378
2379 for (n = Fpos - Spos; n > 0; n -= Nrec) {
2380 /*******************************************************************/
2381 /* Non consecutive line to delete. Move intermediate lines. */
2382 /*******************************************************************/
2383 req = (size_t)MY_MIN(n, Nrec);
2384
2385 for (i = 0; i < Ncol; i++) {
2386 if (!T_Streams[i])
2387 continue; // Non updated column
2388
2389 if (!UseTemp || !b)
2390 if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) {
2391 sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
2392 return true;
2393 } // endif
2394
2395 len = fread(To_Buf, Clens[i], req, Streams[i]);
2396
2397 if (trace(1))
2398 htrc("after read req=%d len=%d\n", req, len);
2399
2400 if (len != req) {
2401 sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
2402 return true;
2403 } // endif len
2404
2405 if (!UseTemp)
2406 if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) {
2407 sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
2408 return true;
2409 } // endif
2410
2411 if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) {
2412 sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
2413 return true;
2414 } // endif
2415
2416 if (trace(1))
2417 htrc("after write pos=%d\n", ftell(Streams[i]));
2418
2419 } // endfor i
2420
2421 Tpos += (int)req;
2422 Spos += (int)req;
2423
2424 if (trace(1))
2425 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
2426
2427 b = true;
2428 } // endfor n
2429
2430 return false;
2431 } // end of MoveIntermediate Lines
2432
2433 /***********************************************************************/
2434 /* Delete the old files and rename the new temporary files. */
2435 /***********************************************************************/
RenameTempFile(PGLOBAL g)2436 int VECFAM::RenameTempFile(PGLOBAL g)
2437 {
2438 char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
2439 int rc = RC_OK;
2440
2441 // Close all files.
2442 // This loop is necessary because, in case of join,
2443 // the table files can have been open several times.
2444 for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
2445 rc = PlugCloseFile(g, fb);
2446
2447 for (int i = 0; i < Ncol && rc == RC_OK; i++) {
2448 if (!T_Fbs[i])
2449 continue;
2450
2451 tempname = (char*)T_Fbs[i]->Fname;
2452
2453 if (!Abort) {
2454 sprintf(filename, Colfn, i+1);
2455 PlugSetPath(filename, filename, Tdbp->GetPath());
2456 strcat(PlugRemoveType(filetemp, filename), ".ttt");
2457 remove(filetemp); // May still be there from previous error
2458
2459 if (rename(filename, filetemp)) { // Save file for security
2460 snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2461 filename, filetemp, strerror(errno));
2462 rc = RC_FX;
2463 } else if (rename(tempname, filename)) {
2464 snprintf(g->Message, MAX_STR, MSG(RENAME_ERROR),
2465 tempname, filename, strerror(errno));
2466 rc = rename(filetemp, filename); // Restore saved file
2467 rc = RC_FX;
2468 } else if (remove(filetemp)) {
2469 sprintf(g->Message, MSG(REMOVE_ERROR),
2470 filetemp, strerror(errno));
2471 rc = RC_INFO; // Acceptable
2472 } // endif's
2473
2474 } else
2475 remove(tempname);
2476
2477 } // endfor i
2478
2479 return rc;
2480 } // end of RenameTempFile
2481
2482 /***********************************************************************/
2483 /* Data Base close routine for VEC access method. */
2484 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool abort)2485 void VECFAM::CloseTableFile(PGLOBAL g, bool abort)
2486 {
2487 int rc = 0, wrc = RC_OK;
2488 MODE mode = Tdbp->GetMode();
2489
2490 Abort = abort;
2491
2492 if (mode == MODE_INSERT) {
2493 if (Closing)
2494 wrc = RC_FX; // Last write was in error
2495 else
2496 if (CurNum) {
2497 // Some more inserted lines remain to be written
2498 Last += (CurBlk * Nrec + CurNum -1);
2499 Block += (Last / Nrec);
2500 Last = Last % Nrec + 1;
2501 Closing = true;
2502 wrc = WriteBuffer(g);
2503 } else {
2504 Block += CurBlk;
2505 wrc = RC_OK;
2506 } // endif CurNum
2507
2508 if (wrc != RC_FX)
2509 rc = ResetTableSize(g, Block, Last);
2510 else
2511 throw 44;
2512
2513 } else if (mode == MODE_UPDATE) {
2514 if (UseTemp && !InitUpdate && !Abort) {
2515 // Write any intermediate lines to temp file
2516 Fpos = OldBlk * Nrec;
2517 Abort = MoveIntermediateLines(g) != RC_OK;
2518 // Spos = Fpos + Nrec;
2519 } // endif UseTemp
2520
2521 // Write back to file any pending modifications
2522 if (wrc == RC_OK)
2523 for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols;
2524 colp; colp = (PVCTCOL)colp->Next)
2525 colp->WriteBlock(g);
2526
2527 if (wrc == RC_OK && UseTemp && !InitUpdate && !Abort) {
2528 // Write any intermediate lines to temp file
2529 Fpos = (Block - 1) * Nrec + Last;
2530 Abort = MoveIntermediateLines(g) != RC_OK;
2531 } // endif UseTemp
2532
2533 } // endif's mode
2534
2535 if (UseTemp && !InitUpdate) {
2536 // If they are errors, leave files unchanged
2537 rc = RenameTempFile(g);
2538
2539 } else if (Streams)
2540 for (int i = 0; i < Ncol; i++)
2541 if (Streams[i]) {
2542 rc = PlugCloseFile(g, To_Fbs[i]);
2543 Streams[i] = NULL;
2544 To_Fbs[i] = NULL;
2545 } // endif Streams
2546
2547 if (trace(1))
2548 htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc);
2549
2550 } // end of CloseTableFile
2551
2552 /***********************************************************************/
2553 /* ReadBlock: Read column values from current block. */
2554 /***********************************************************************/
ReadBlock(PGLOBAL g,PVCTCOL colp)2555 bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
2556 {
2557 int i, len;
2558 size_t n;
2559
2560 /*********************************************************************/
2561 /* Calculate the offset and size of the block to read. */
2562 /*********************************************************************/
2563 len = Nrec * colp->Clen * CurBlk;
2564 i = colp->Index - 1;
2565
2566 if (trace(1))
2567 htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n",
2568 len, i, Nrec, colp->Deplac, Lrecl, CurBlk);
2569
2570 if (fseek(Streams[i], len, SEEK_SET)) {
2571 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2572 return true;
2573 } // endif
2574
2575 n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen,
2576 (size_t)Nrec, Streams[i]);
2577
2578 if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) {
2579 char fn[_MAX_PATH];
2580
2581 sprintf(fn, Colfn, colp->Index);
2582 #if defined(_WIN32)
2583 if (feof(Streams[i]))
2584 #else // !_WIN32
2585 if (errno == NO_ERROR)
2586 #endif // !_WIN32
2587 sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn);
2588 else
2589 sprintf(g->Message, MSG(READ_ERROR),
2590 fn, strerror(errno));
2591
2592 if (trace(1))
2593 htrc(" Read error: %s\n", g->Message);
2594
2595 return true;
2596 } // endif
2597
2598 if (trace(1))
2599 num_read++;
2600
2601 return false;
2602 } // end of ReadBlock
2603
2604 /***********************************************************************/
2605 /* WriteBlock: Write back current column values for one block. */
2606 /* Note: the test of Status is meant to prevent physical writing of */
2607 /* the block during the checking loop in mode Update. It is set to */
2608 /* BUF_EMPTY when reopening the table between the two loops. */
2609 /***********************************************************************/
WriteBlock(PGLOBAL g,PVCTCOL colp)2610 bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
2611 {
2612 int i, len;
2613 size_t n;
2614
2615 /*********************************************************************/
2616 /* Calculate the offset and size of the block to write. */
2617 /*********************************************************************/
2618 len = Nrec * colp->Clen * colp->ColBlk;
2619 i = colp->Index - 1;
2620
2621 if (trace(1))
2622 htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n",
2623 Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
2624
2625 if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp)
2626 if (fseek(T_Streams[i], len, SEEK_SET)) {
2627 sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
2628 return true;
2629 } // endif
2630
2631 // Here Nrec was changed to CurNum in mode Insert,
2632 // this is the true number of records to write,
2633 // this also avoid writing garbage in the file for true vector tables.
2634 n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum
2635 : (colp->ColBlk == Block - 1) ? Last : Nrec;
2636
2637 if (n != fwrite(colp->Blk->GetValPointer(),
2638 (size_t)colp->Clen, n, T_Streams[i])) {
2639 char fn[_MAX_PATH];
2640
2641 sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index);
2642 sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
2643
2644 if (trace(1))
2645 htrc("Write error: %s\n", strerror(errno));
2646
2647 return true;
2648 } else
2649 Spos = Fpos + n;
2650
2651 #if defined(UNIX)
2652 fflush(Streams[i]); //NGC
2653 #endif
2654 return false;
2655 } // end of WriteBlock
2656
2657 /* -------------------------- Class VMPFAM --------------------------- */
2658
2659 /***********************************************************************/
2660 /* Implementation of the VMPFAM class. */
2661 /***********************************************************************/
VMPFAM(PVCTDEF tdp)2662 VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp)
2663 {
2664 To_Fbs = NULL;
2665 Split = true;
2666 Block = Last = -1;
2667 } // end of VMPFAM standard constructor
2668
VMPFAM(PVMPFAM txfp)2669 VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp)
2670 {
2671 To_Fbs = txfp->To_Fbs;
2672 } // end of VMPFAM copy constructor
2673
2674 /***********************************************************************/
2675 /* VCT Access Method opening routine. */
2676 /* New method now that this routine is called recursively (last table */
2677 /* first in reverse order): index blocks are immediately linked to */
2678 /* join block of next table if it exists or else are discarted. */
2679 /***********************************************************************/
OpenTableFile(PGLOBAL g)2680 bool VMPFAM::OpenTableFile(PGLOBAL g)
2681 {
2682 int i;
2683 bool b = false;
2684 MODE mode = Tdbp->GetMode();
2685 PCOLDEF cdp;
2686 PVCTCOL cp;
2687 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
2688
2689 if (mode == MODE_DELETE && !Tdbp->GetNext()) {
2690 DelRows = Cardinality(g);
2691
2692 // This will stop the process by causing GetProgMax to return 0.
2693 ResetTableSize(g, 0, Nrec);
2694 } else
2695 Cardinality(g); // See comment in VECFAM::OpenTbleFile
2696
2697
2698 /*********************************************************************/
2699 /* Prepare the filename pattern for column files and set Ncol. */
2700 /*********************************************************************/
2701 if (!Colfn) {
2702 // Prepare the column file name pattern
2703 Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
2704 Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn);
2705 } // endif Colfn
2706
2707 /*********************************************************************/
2708 /* Initialize the array of file structures. */
2709 /*********************************************************************/
2710 Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *));
2711 To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK));
2712
2713 for (i = 0; i < Ncol; i++) {
2714 Memcol[i] = NULL;
2715 To_Fbs[i] = NULL;
2716 } // endif i
2717
2718 /*********************************************************************/
2719 /* Open the files corresponding to columns used in the query. */
2720 /*********************************************************************/
2721 if (mode == MODE_DELETE) {
2722 // All columns are used in Delete mode
2723 for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext())
2724 if (MapColumnFile(g, mode, i))
2725 return true;
2726
2727 } else {
2728 /*******************************************************************/
2729 /* Open the files corresponding to updated columns of the query. */
2730 /*******************************************************************/
2731 for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp;
2732 cp = (PVCTCOL)cp->Next)
2733 if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1))
2734 return true;
2735
2736 /*******************************************************************/
2737 /* Open other non already open used columns (except pseudos) */
2738 /*******************************************************************/
2739 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2740 if (!cp->IsSpecial() && !Memcol[cp->Index - 1])
2741 if (MapColumnFile(g, MODE_READ, cp->Index - 1))
2742 return true;
2743
2744 // Check for void table or missing columns
2745 for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp;
2746 cp = (PVCTCOL)cp->Next)
2747 if (!cp->IsSpecial()) {
2748 if (!i++)
2749 b = !Memcol[cp->Index - 1];
2750 else if (b != !Memcol[cp->Index - 1])
2751 return true;
2752
2753 } // endif Special
2754
2755 } // endif mode
2756
2757 /*********************************************************************/
2758 /* Allocate the table and column block buffer. */
2759 /*********************************************************************/
2760 return (b) ? false : AllocateBuffer(g);
2761 } // end of OpenTableFile
2762
2763 /***********************************************************************/
2764 /* Open the file corresponding to one column. */
2765 /***********************************************************************/
MapColumnFile(PGLOBAL g,MODE mode,int i)2766 bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
2767 {
2768 char filename[_MAX_PATH];
2769 size_t len;
2770 HANDLE hFile;
2771 MEMMAP mm;
2772 PFBLOCK fp;
2773 PDBUSER dup = PlgGetUser(g);
2774
2775 sprintf(filename, Colfn, i+1);
2776
2777 /*********************************************************************/
2778 /* The whole file will be mapped so we can use it as */
2779 /* if it were entirely read into virtual memory. */
2780 /* Firstly we check whether this file have been already mapped. */
2781 /*********************************************************************/
2782 if (mode == MODE_READ) {
2783 for (fp = dup->Openlist; fp; fp = fp->Next)
2784 if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
2785 && fp->Count && fp->Mode == mode)
2786 break;
2787
2788 if (trace(1))
2789 htrc("Mapping file, fp=%p\n", fp);
2790
2791 } else
2792 fp = NULL;
2793
2794 if (fp) {
2795 /*******************************************************************/
2796 /* File already mapped. Just increment use count and get pointer. */
2797 /*******************************************************************/
2798 fp->Count++;
2799 Memcol[i] = fp->Memory;
2800 len = fp->Length;
2801 } else {
2802 /*******************************************************************/
2803 /* Create the mapping file object. */
2804 /*******************************************************************/
2805 hFile = CreateFileMap(g, filename, &mm, mode, DelRows);
2806
2807 if (hFile == INVALID_HANDLE_VALUE) {
2808 DWORD rc = GetLastError();
2809
2810 if (!(*g->Message))
2811 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
2812 "map", (int) rc, filename);
2813 if (trace(1))
2814 htrc("%s\n", g->Message);
2815
2816 return (mode == MODE_READ && rc == ENOENT)
2817 ? PushWarning(g, Tdbp) : true;
2818 } // endif hFile
2819
2820 /*****************************************************************/
2821 /* Get the file size (assuming file is smaller than 4 GB) */
2822 /*****************************************************************/
2823 len = (size_t)mm.lenL;
2824
2825 if (mm.lenH)
2826 len += ((size_t)mm.lenH * 0x000000001LL);
2827
2828 Memcol[i] = (char *)mm.memory;
2829
2830 if (!len) { // Empty or deleted file
2831 CloseFileHandle(hFile);
2832 ResetTableSize(g, 0, Nrec);
2833 return false;
2834 } // endif len
2835
2836 if (!Memcol[i]) {
2837 CloseFileHandle(hFile);
2838 sprintf(g->Message, MSG(MAP_VIEW_ERROR),
2839 filename, GetLastError());
2840 return true;
2841 } // endif Memory
2842
2843 if (mode != MODE_DELETE) {
2844 CloseFileHandle(hFile); // Not used anymore
2845 hFile = INVALID_HANDLE_VALUE; // For Fblock
2846 } // endif Mode
2847
2848 /*******************************************************************/
2849 /* Link a Fblock. This make possible to reuse already opened maps */
2850 /* and also to automatically unmap them in case of error g->jump. */
2851 /* Note: block can already exist for previously closed file. */
2852 /*******************************************************************/
2853 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
2854 fp->Type = TYPE_FB_MAP;
2855 fp->Fname = PlugDup(g, filename);
2856 fp->Next = dup->Openlist;
2857 dup->Openlist = fp;
2858 fp->Count = 1;
2859 fp->Length = len;
2860 fp->Memory = Memcol[i];
2861 fp->Mode = mode;
2862 fp->File = NULL;
2863 fp->Handle = hFile; // Used for Delete
2864 } // endif fp
2865
2866 To_Fbs[i] = fp; // Useful when closing
2867
2868 if (trace(1))
2869 htrc("fp=%p count=%d MapView=%p len=%d\n",
2870 fp, fp->Count, Memcol[i], len);
2871
2872 return false;
2873 } // end of MapColumnFile
2874
2875 /***********************************************************************/
2876 /* Allocate the block buffers for columns used in the query. */
2877 /* Give a dummy value (1) to prevent allocating the value block. */
2878 /* It will be set pointing into the memory map of the file. */
2879 /***********************************************************************/
AllocateBuffer(PGLOBAL g)2880 bool VMPFAM::AllocateBuffer(PGLOBAL g)
2881 {
2882 PVCTCOL cp;
2883
2884 if (Tdbp->GetMode() == MODE_DELETE) {
2885 PCOLDEF cdp = Tdbp->GetDef()->GetCols();
2886
2887 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
2888
2889 for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext())
2890 Clens[i] = cdp->GetClen();
2891
2892 } // endif mode
2893
2894 for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next)
2895 if (!cp->IsSpecial()) { // Not a pseudo column
2896 cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec,
2897 cp->Format.Length, cp->Format.Prec,
2898 true, true, cp->IsUnsigned());
2899 cp->AddStatus(BUF_MAPPED);
2900 } // endif IsSpecial
2901
2902 return false;
2903 } // end of AllocateBuffer
2904
2905 /***********************************************************************/
2906 /* Data Base delete line routine for VMP access method. */
2907 /* Lines between deleted lines are moved in the mapfile view. */
2908 /***********************************************************************/
DeleteRecords(PGLOBAL g,int irc)2909 int VMPFAM::DeleteRecords(PGLOBAL g, int irc)
2910 {
2911 int i;
2912 int m, n;
2913
2914 if (trace(1))
2915 htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n",
2916 irc, To_Buf, Tpos, Spos);
2917
2918 if (irc != RC_OK) {
2919 /*******************************************************************/
2920 /* EOF: position Fpos at the top of map position. */
2921 /*******************************************************************/
2922 Fpos = (Block - 1) * Nrec + Last;
2923
2924 if (trace(1))
2925 htrc("Fpos placed at file top=%p\n", Fpos);
2926
2927 } else // Fpos is the Deleted line position
2928 Fpos = CurBlk * Nrec + CurNum;
2929
2930 if (Tpos == Spos) {
2931 /*******************************************************************/
2932 /* First line to delete. Move of eventual preceding lines is */
2933 /* not required here, just setting of future Spos and Tpos. */
2934 /*******************************************************************/
2935 Tpos = Fpos; // Spos is set below
2936 } else if ((n = Fpos - Spos) > 0) {
2937 /*******************************************************************/
2938 /* Non consecutive line to delete. Move intermediate lines. */
2939 /*******************************************************************/
2940 for (i = 0; i < Ncol; i++) {
2941 m = Clens[i];
2942 memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n);
2943 } // endif i
2944
2945 Tpos += n;
2946
2947 if (trace(1))
2948 htrc("move %d bytes\n", n);
2949
2950 } // endif n
2951
2952 if (irc == RC_OK) {
2953 Spos = Fpos + 1; // New start position
2954
2955 if (trace(1))
2956 htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos);
2957
2958 } else {
2959 /*******************************************************************/
2960 /* Last call after EOF has been reached. */
2961 /* We must firstly Unmap the view and use the saved file handle */
2962 /* to put an EOF at the end of the copied part of the file. */
2963 /*******************************************************************/
2964 PFBLOCK fp;
2965
2966 /*******************************************************************/
2967 /* Reset the Block and Last values for TDBVCT::MakeBlockValues. */
2968 /*******************************************************************/
2969 // Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
2970 // Last = (Tpos + Nrec - 1) % Nrec + 1;
2971
2972 for (i = 0; i < Ncol; i++) {
2973 fp = To_Fbs[i];
2974 CloseMemMap(fp->Memory, (size_t)fp->Length);
2975 fp->Count = 0; // Avoid doing it twice
2976
2977 /*****************************************************************/
2978 /* Remove extra records. */
2979 /*****************************************************************/
2980 n = Tpos * Clens[i];
2981
2982 #if defined(_WIN32)
2983 DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN);
2984
2985 if (drc == 0xFFFFFFFF) {
2986 sprintf(g->Message, MSG(FUNCTION_ERROR),
2987 "SetFilePointer", GetLastError());
2988 CloseHandle(fp->Handle);
2989 return RC_FX;
2990 } // endif
2991
2992 if (trace(1))
2993 htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc);
2994
2995 if (!SetEndOfFile(fp->Handle)) {
2996 sprintf(g->Message, MSG(FUNCTION_ERROR),
2997 "SetEndOfFile", GetLastError());
2998 CloseHandle(fp->Handle);
2999 return RC_FX;
3000 } // endif
3001
3002 CloseHandle(fp->Handle);
3003 #else // UNIX
3004 if (ftruncate(fp->Handle, (off_t)n)) {
3005 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3006 close(fp->Handle);
3007 return RC_FX;
3008 } // endif
3009
3010 close(fp->Handle);
3011 #endif // UNIX
3012 } // endfor i
3013
3014 } // endif irc
3015
3016 return RC_OK; // All is correct
3017 } // end of DeleteRecords
3018
3019 /***********************************************************************/
3020 /* Data Base close routine for VMP access method. */
3021 /***********************************************************************/
CloseTableFile(PGLOBAL g,bool)3022 void VMPFAM::CloseTableFile(PGLOBAL g, bool)
3023 {
3024 if (Tdbp->GetMode() == MODE_DELETE) {
3025 // Set Block and Nrec values for TDBVCT::MakeBlockValues
3026 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3027 Last = (Tpos + Nrec - 1) % Nrec + 1;
3028 ResetTableSize(g, Block, Last);
3029 } else if (Tdbp->GetMode() == MODE_INSERT)
3030 assert(false);
3031
3032 for (int i = 0; i < Ncol; i++)
3033 PlugCloseFile(g, To_Fbs[i]);
3034
3035 } // end of CloseTableFile
3036
3037 /* -------------------------- Class BGVFAM --------------------------- */
3038
3039 /***********************************************************************/
3040 /* Implementation of the BGVFAM class. */
3041 /***********************************************************************/
3042 // Constructors
BGVFAM(PVCTDEF tdp)3043 BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp)
3044 {
3045 Hfile = INVALID_HANDLE_VALUE;
3046 Tfile = INVALID_HANDLE_VALUE;
3047 BigDep = NULL;
3048 } // end of BGVFAM constructor
3049
BGVFAM(PBGVFAM txfp)3050 BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp)
3051 {
3052 Hfile = txfp->Hfile;
3053 Tfile = txfp->Tfile;
3054 BigDep= txfp->BigDep;
3055 } // end of BGVFAM copy constructor
3056
3057 /***********************************************************************/
3058 /* Set current position in a big file. */
3059 /***********************************************************************/
BigSeek(PGLOBAL g,HANDLE h,BIGINT pos,bool b)3060 bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b)
3061 {
3062 #if defined(_WIN32)
3063 char buf[256];
3064 DWORD drc, m = (b) ? FILE_END : FILE_BEGIN;
3065 LARGE_INTEGER of;
3066
3067 of.QuadPart = pos;
3068 of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m);
3069
3070 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3071 (drc = GetLastError()) != NO_ERROR) {
3072 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3073 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3074 (LPTSTR)buf, sizeof(buf), NULL);
3075 sprintf(g->Message, MSG(SFP_ERROR), buf);
3076 return true;
3077 } // endif
3078 #else // !_WIN32
3079 if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) {
3080 sprintf(g->Message, MSG(ERROR_IN_LSK), errno);
3081 return true;
3082 } // endif
3083 #endif // !_WIN32
3084
3085 return false;
3086 } // end of BigSeek
3087
3088 /***********************************************************************/
3089 /* Read from a big file. */
3090 /***********************************************************************/
BigRead(PGLOBAL g,HANDLE h,void * inbuf,int req)3091 bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req)
3092 {
3093 bool rc = false;
3094
3095 #if defined(_WIN32)
3096 DWORD nbr, drc, len = (DWORD)req;
3097 bool brc = ReadFile(h, inbuf, len, &nbr, NULL);
3098
3099 if (trace(1))
3100 htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr);
3101
3102 if (!brc || nbr != len) {
3103 char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile";
3104
3105 if (brc)
3106 strcpy(buf, MSG(BAD_BYTE_READ));
3107 else {
3108 drc = GetLastError();
3109 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3110 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3111 (LPTSTR)buf, sizeof(buf), NULL);
3112 } // endelse brc
3113
3114 sprintf(g->Message, MSG(READ_ERROR), To_File, buf);
3115
3116 if (trace(1))
3117 htrc("BIGREAD: %s\n", g->Message);
3118
3119 rc = true;
3120 } // endif brc || nbr
3121 #else // !_WIN32
3122 size_t len = (size_t)req;
3123 ssize_t nbr = read(h, inbuf, len);
3124
3125 if (nbr != (ssize_t)len) {
3126 const char *fn = (h == Hfile) ? To_File : "Tempfile";
3127
3128 sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno));
3129
3130 if (trace(1))
3131 htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n",
3132 nbr, len, errno, g->Message);
3133
3134 rc = true;
3135 } // endif nbr
3136 #endif // !_WIN32
3137
3138 return rc;
3139 } // end of BigRead
3140
3141 /***********************************************************************/
3142 /* Write into a big file. */
3143 /***********************************************************************/
BigWrite(PGLOBAL g,HANDLE h,void * inbuf,int req)3144 bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req)
3145 {
3146 bool rc = false;
3147
3148 #if defined(_WIN32)
3149 DWORD nbw, drc, len = (DWORD)req;
3150 bool brc = WriteFile(h, inbuf, len, &nbw, NULL);
3151
3152 if (trace(1))
3153 htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw);
3154
3155 if (!brc || nbw != len) {
3156 char buf[256];
3157 PCSZ fn = (h == Hfile) ? To_File : "Tempfile";
3158
3159 if (brc)
3160 strcpy(buf, MSG(BAD_BYTE_NUM));
3161 else {
3162 drc = GetLastError();
3163 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3164 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
3165 (LPTSTR)buf, sizeof(buf), NULL);
3166 } // endelse brc
3167
3168 sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf);
3169
3170 if (trace(1))
3171 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3172 nbw, len, drc, g->Message);
3173
3174 rc = true;
3175 } // endif brc || nbw
3176 #else // !_WIN32
3177 size_t len = (size_t)req;
3178 ssize_t nbw = write(h, inbuf, len);
3179
3180 if (nbw != (ssize_t)len) {
3181 const char *fn = (h == Hfile) ? To_File : "Tempfile";
3182
3183 sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno));
3184
3185 if (trace(1))
3186 htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n",
3187 nbw, len, errno, g->Message);
3188
3189 rc = true;
3190 } // endif nbr
3191 #endif // !_WIN32
3192
3193 return rc;
3194 } // end of BigWrite
3195
3196 /***********************************************************************/
3197 /* Get the Headlen, Block and Last info from the file header. */
3198 /***********************************************************************/
GetBlockInfo(PGLOBAL g)3199 int BGVFAM::GetBlockInfo(PGLOBAL g)
3200 {
3201 char filename[_MAX_PATH];
3202 int n;
3203 VECHEADER vh;
3204 HANDLE h;
3205
3206 if (Header < 1 || Header > 3 || !MaxBlk) {
3207 sprintf(g->Message, "Invalid header value %d", Header);
3208 return -1;
3209 } else
3210 n = (Header == 1) ? (int)sizeof(VECHEADER) : 0;
3211
3212 PlugSetPath(filename, To_File, Tdbp->GetPath());
3213
3214 if (Header == 2)
3215 strcat(PlugRemoveType(filename, filename), ".blk");
3216
3217 #if defined(_WIN32)
3218 LARGE_INTEGER len;
3219
3220 h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
3221 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3222
3223 if (h != INVALID_HANDLE_VALUE) {
3224 // Get the size of the file (can be greater than 4 GB)
3225 len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart);
3226 } // endif h
3227
3228 if (h == INVALID_HANDLE_VALUE || !len.QuadPart) {
3229 #else // !_WIN32
3230 h = open64(filename, O_RDONLY, 0);
3231
3232 if (h == INVALID_HANDLE_VALUE || !_filelength(h)) {
3233 #endif // !_WIN32
3234 // Consider this is a void table
3235 if (trace(1))
3236 htrc("Void table h=%d\n", h);
3237
3238 Last = Nrec;
3239 Block = 0;
3240
3241 if (h != INVALID_HANDLE_VALUE)
3242 CloseFileHandle(h);
3243
3244 return n;
3245 } else if (Header == 3)
3246 /*b = */ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3247
3248 if (BigRead(g, h, &vh, sizeof(vh))) {
3249 sprintf(g->Message, "Error reading header file %s", filename);
3250 n = -1;
3251 } else if (MaxBlk * Nrec != vh.MaxRec) {
3252 sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d",
3253 vh.MaxRec, MaxBlk, Nrec);
3254 n = -1;
3255 } else {
3256 Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0;
3257 Last = (vh.NumRec + Nrec - 1) % Nrec + 1;
3258
3259 if (trace(1))
3260 htrc("Block=%d Last=%d\n", Block, Last);
3261
3262 } // endif's
3263
3264 CloseFileHandle(h);
3265 return n;
3266 } // end of GetBlockInfo
3267
3268 /***********************************************************************/
3269 /* Set the MaxRec and NumRec info in the file header. */
3270 /***********************************************************************/
3271 bool BGVFAM::SetBlockInfo(PGLOBAL g)
3272 {
3273 char filename[_MAX_PATH];
3274 bool b = false, rc = false;
3275 VECHEADER vh;
3276 HANDLE h = INVALID_HANDLE_VALUE;
3277
3278 PlugSetPath(filename, To_File, Tdbp->GetPath());
3279
3280 if (Header != 2) {
3281 if (Hfile != INVALID_HANDLE_VALUE) {
3282 h = Hfile;
3283
3284 if (Header == 1)
3285 /*bk =*/ BigSeek(g, h, (BIGINT)0);
3286
3287 } else
3288 b = true;
3289
3290 } else // Header == 2
3291 strcat(PlugRemoveType(filename, filename), ".blk");
3292
3293 if (h == INVALID_HANDLE_VALUE) {
3294 #if defined(_WIN32)
3295 DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING;
3296
3297 h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0,
3298 NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
3299
3300 #else // !_WIN32
3301 int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC;
3302
3303 h = open64(filename, oflag, 0);
3304 #endif // !_WIN32
3305
3306 if (h == INVALID_HANDLE_VALUE) {
3307 sprintf(g->Message, "Error opening header file %s", filename);
3308 return true;
3309 } // endif h
3310
3311 } // endif h
3312
3313 if (Header == 3)
3314 /*bk =*/ BigSeek(g, h, -(BIGINT)sizeof(vh), true);
3315
3316 vh.MaxRec = MaxBlk * Bsize;
3317 vh.NumRec = (Block - 1) * Nrec + Last;
3318
3319 if (BigWrite(g, h, &vh, sizeof(vh))) {
3320 sprintf(g->Message, "Error writing header file %s", filename);
3321 rc = true;
3322 } // endif fread
3323
3324 if (Header == 2 || Hfile == INVALID_HANDLE_VALUE)
3325 CloseFileHandle(h);
3326
3327 return rc;
3328 } // end of SetBlockInfo
3329
3330 /***********************************************************************/
3331 /* VEC Create an empty file for new Vector formatted tables. */
3332 /***********************************************************************/
3333 bool BGVFAM::MakeEmptyFile(PGLOBAL g, PCSZ fn)
3334 {
3335 // Vector formatted file this will create an empty file of the
3336 // required length if it does not exists yet.
3337 char filename[_MAX_PATH], c = 0;
3338 int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0;
3339
3340 PlugSetPath(filename, fn, Tdbp->GetPath());
3341
3342 #if defined(_WIN32)
3343 PCSZ p;
3344 DWORD rc;
3345 bool brc;
3346 LARGE_INTEGER of;
3347 HANDLE h;
3348
3349 h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3350 FILE_ATTRIBUTE_NORMAL, NULL);
3351
3352 if (h == INVALID_HANDLE_VALUE) {
3353 p = MSG(OPENING);
3354 goto err;
3355 } // endif h
3356
3357 of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3358
3359 if (trace(1))
3360 htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n",
3361 of.QuadPart, n, MaxBlk, Blksize);
3362
3363 of.LowPart = SetFilePointer(h, of.LowPart,
3364 &of.HighPart, FILE_BEGIN);
3365
3366 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3367 GetLastError() != NO_ERROR) {
3368 p = MSG(MAKING);
3369 goto err;
3370 } // endif
3371
3372 brc = WriteFile(h, &c, 1, &rc, NULL);
3373
3374 if (!brc || rc != 1) {
3375 p = MSG(WRITING);
3376 goto err;
3377 } // endif
3378
3379 CloseHandle(h);
3380 return false;
3381
3382 err:
3383 rc = GetLastError();
3384 sprintf(g->Message, MSG(EMPTY_FILE), p, filename);
3385 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3386 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3387 (LPTSTR)filename, sizeof(filename), NULL);
3388 strcat(g->Message, filename);
3389
3390 if (h != INVALID_HANDLE_VALUE)
3391 CloseHandle(h);
3392
3393 return true;
3394 #else // !_WIN32
3395 int h;
3396 BIGINT pos;
3397
3398 h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
3399
3400 if (h == -1)
3401 return true;
3402
3403 pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1;
3404
3405 if (trace(1))
3406 htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n",
3407 pos, n, MaxBlk, Blksize);
3408
3409 if (lseek64(h, pos, SEEK_SET) < 0)
3410 goto err;
3411
3412 // This actually fills the empty file
3413 if (write(h, &c, 1) < 0)
3414 goto err;
3415
3416 close(h);
3417 return false;
3418
3419 err:
3420 sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno));
3421 close(h);
3422 return true;
3423 #endif // !_WIN32
3424 } // end of MakeEmptyFile
3425
3426 /***********************************************************************/
3427 /* Vopen function: opens a file using Windows or Unix API's. */
3428 /***********************************************************************/
3429 bool BGVFAM::OpenTableFile(PGLOBAL g)
3430 {
3431 char filename[_MAX_PATH];
3432 bool del = false;
3433 MODE mode = Tdbp->GetMode();
3434 PDBUSER dbuserp = PlgGetUser(g);
3435
3436 if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) {
3437 sprintf(g->Message, MSG(FILE_OPEN_YET), To_File);
3438 return true;
3439 } // endif
3440
3441 /*********************************************************************/
3442 /* Update block info if necessary. */
3443 /*********************************************************************/
3444 if (Block < 0)
3445 if ((Headlen = GetBlockInfo(g)) < 0)
3446 return true;
3447
3448 PlugSetPath(filename, To_File, Tdbp->GetPath());
3449
3450 if (trace(1))
3451 htrc("OpenTableFile: filename=%s mode=%d Last=%d\n",
3452 filename, mode, Last);
3453
3454 #if defined(_WIN32)
3455 DWORD access, creation, share = 0, rc = 0;
3456
3457 /*********************************************************************/
3458 /* Create the file object according to access mode */
3459 /*********************************************************************/
3460 switch (mode) {
3461 case MODE_READ:
3462 access = GENERIC_READ;
3463 share = FILE_SHARE_READ;
3464 creation = OPEN_EXISTING;
3465 break;
3466 case MODE_INSERT:
3467 if (MaxBlk) {
3468 if (!Block)
3469 if (MakeEmptyFile(g, To_File))
3470 return true;
3471
3472 // Required to update empty blocks
3473 access = GENERIC_READ | GENERIC_WRITE;
3474 } else if (Last == Nrec)
3475 access = GENERIC_WRITE;
3476 else
3477 // Required to update the last block
3478 access = GENERIC_READ | GENERIC_WRITE;
3479
3480 creation = OPEN_ALWAYS;
3481 break;
3482 case MODE_DELETE:
3483 if (!Tdbp->GetNext()) {
3484 // Store the number of deleted lines
3485 DelRows = Cardinality(g);
3486
3487 // This will stop the process by
3488 // causing GetProgMax to return 0.
3489 // ResetTableSize(g, 0, Nrec); must be done later
3490 del = true;
3491
3492 // This will delete the whole file
3493 access = GENERIC_READ | GENERIC_WRITE;
3494 creation = TRUNCATE_EXISTING;
3495 break;
3496 } // endif
3497
3498 // Selective delete, pass thru
3499 case MODE_UPDATE:
3500 if ((UseTemp = Tdbp->IsUsingTemp(g)))
3501 access = GENERIC_READ;
3502 else
3503 access = GENERIC_READ | GENERIC_WRITE;
3504
3505 creation = OPEN_EXISTING;
3506 break;
3507 default:
3508 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3509 return true;
3510 } // endswitch
3511
3512 /*********************************************************************/
3513 /* Use specific Windows API functions. */
3514 /*********************************************************************/
3515 Hfile = CreateFile(filename, access, share, NULL, creation,
3516 FILE_ATTRIBUTE_NORMAL, NULL);
3517
3518 if (Hfile == INVALID_HANDLE_VALUE) {
3519 rc = GetLastError();
3520 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3521 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3522 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3523 (LPTSTR)filename, sizeof(filename), NULL);
3524 strcat(g->Message, filename);
3525 } // endif Hfile
3526
3527 if (trace(1))
3528 htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n",
3529 rc, access, share, creation, Hfile, filename);
3530
3531 if (mode == MODE_INSERT) {
3532 /*******************************************************************/
3533 /* In Insert mode we must position the cursor at end of file. */
3534 /*******************************************************************/
3535 LARGE_INTEGER of;
3536
3537 of.QuadPart = (BIGINT)0;
3538 of.LowPart = SetFilePointer(Hfile, of.LowPart,
3539 &of.HighPart, FILE_END);
3540
3541 if (of.LowPart == INVALID_SET_FILE_POINTER &&
3542 (rc = GetLastError()) != NO_ERROR) {
3543 sprintf(g->Message, MSG(ERROR_IN_SFP), rc);
3544 CloseHandle(Hfile);
3545 Hfile = INVALID_HANDLE_VALUE;
3546 } // endif
3547
3548 } // endif Mode
3549
3550 #else // UNIX
3551 /*********************************************************************/
3552 /* The open() function has a transitional interface for 64-bit */
3553 /* file offsets. Note that using open64() is equivalent to using */
3554 /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */
3555 /*********************************************************************/
3556 int rc = 0;
3557 int oflag;
3558 mode_t pmd = 0;
3559
3560 /*********************************************************************/
3561 /* Create the file object according to access mode */
3562 /*********************************************************************/
3563 switch (mode) {
3564 case MODE_READ:
3565 oflag = O_RDONLY;
3566 break;
3567 case MODE_INSERT:
3568 if (MaxBlk) {
3569 if (!Block)
3570 if (MakeEmptyFile(g, To_File))
3571 return true;
3572
3573 // Required to update empty blocks
3574 oflag = O_RDWR;
3575 } else if (Last == Nrec)
3576 oflag = O_WRONLY | O_CREAT | O_APPEND;
3577 else
3578 // Required to update the last block
3579 oflag = O_RDWR | O_CREAT | O_APPEND;
3580
3581 pmd = S_IREAD | S_IWRITE;
3582 break;
3583 case MODE_DELETE:
3584 // This is temporary until a partial delete is implemented
3585 if (!Tdbp->GetNext()) {
3586 // Store the number of deleted lines
3587 DelRows = Cardinality(g);
3588 del = true;
3589
3590 // This will delete the whole file and provoque ReadDB to
3591 // return immediately.
3592 oflag = O_RDWR | O_TRUNC;
3593 strcpy(g->Message, MSG(NO_VCT_DELETE));
3594 break;
3595 } // endif
3596
3597 // Selective delete, pass thru
3598 /* fall through */
3599 case MODE_UPDATE:
3600 UseTemp = Tdbp->IsUsingTemp(g);
3601 oflag = (UseTemp) ? O_RDONLY : O_RDWR;
3602 break;
3603 default:
3604 sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
3605 return true;
3606 } // endswitch
3607
3608 Hfile = open64(filename, oflag, pmd); // Enable file size > 2G
3609
3610 if (Hfile == INVALID_HANDLE_VALUE) {
3611 rc = errno;
3612 sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename);
3613 strcat(g->Message, strerror(errno));
3614 } // endif Hfile
3615
3616 if (trace(1))
3617 htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n",
3618 rc, oflag, mode, Hfile, filename);
3619 #endif // UNIX
3620
3621 if (!rc) {
3622 if (!To_Fb) {
3623 To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3624 To_Fb->Fname = To_File;
3625 To_Fb->Type = TYPE_FB_HANDLE;
3626 To_Fb->Memory = NULL;
3627 To_Fb->Length = 0;
3628 To_Fb->File = NULL;
3629 To_Fb->Next = dbuserp->Openlist;
3630 dbuserp->Openlist = To_Fb;
3631 } // endif To_Fb
3632
3633 To_Fb->Count = 1;
3634 To_Fb->Mode = mode;
3635 To_Fb->Handle = Hfile;
3636
3637 if (trace(1))
3638 htrc("File %s is open in mode %d\n", filename, mode);
3639
3640 if (del)
3641 // This will stop the process by
3642 // causing GetProgMax to return 0.
3643 return ResetTableSize(g, 0, Nrec);
3644
3645 /*********************************************************************/
3646 /* Allocate the table and column block buffers. */
3647 /*********************************************************************/
3648 return AllocateBuffer(g);
3649 } else
3650 return (mode == MODE_READ && rc == ENOENT)
3651 ? PushWarning(g, Tdbp) : true;
3652
3653 } // end of OpenTableFile
3654
3655 /***********************************************************************/
3656 /* Allocate the block buffers for columns used in the query. */
3657 /***********************************************************************/
3658 bool BGVFAM::AllocateBuffer(PGLOBAL g)
3659 {
3660 MODE mode = Tdbp->GetMode();
3661 PDOSDEF defp = (PDOSDEF)Tdbp->GetDef();
3662 PCOLDEF cdp;
3663 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3664
3665 if (mode == MODE_INSERT) {
3666 if (!NewBlock) {
3667 // Not reopening after inserting the last block
3668 bool chk = PlgGetUser(g)->Check & CHK_TYPE;
3669
3670 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3671
3672 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3673 memset(NewBlock + Nrec * cdp->GetPoff(),
3674 (IsTypeNum(cdp->GetType()) ? 0 : ' '),
3675 Nrec * cdp->GetClen());
3676
3677 for (; cp; cp = (PVCTCOL)cp->Next)
3678 cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac,
3679 cp->Buf_Type, Nrec, cp->Format.Length,
3680 cp->Format.Prec, chk, true, cp->IsUnsigned());
3681
3682 InitInsert(g); // Initialize inserting
3683
3684 // Currently we don't use a temporary file for inserting
3685 Tfile = Hfile;
3686 } // endif NewBlock
3687
3688 } else {
3689 if (UseTemp || mode == MODE_DELETE) {
3690 // Allocate all that is needed to move lines
3691 int i = 0;
3692
3693 if (!Ncol)
3694 for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext())
3695 Ncol++;
3696
3697 if (MaxBlk)
3698 BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT));
3699 else
3700 Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3701
3702 Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int));
3703 Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool));
3704
3705 for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) {
3706 if (MaxBlk)
3707 BigDep[i] = (BIGINT)Headlen
3708 + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk;
3709 else
3710 Deplac[i] = cdp->GetPoff() * Nrec;
3711
3712 Clens[i] = cdp->GetClen();
3713 Isnum[i] = IsTypeNum(cdp->GetType());
3714 Buflen = MY_MAX(Buflen, cdp->GetClen());
3715 } // endfor cdp
3716
3717 if (!UseTemp || MaxBlk) {
3718 Buflen *= Nrec;
3719 To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
3720 } else
3721 NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize);
3722
3723 } // endif mode
3724
3725 for (; cp; cp = (PVCTCOL)cp->Next)
3726 if (!cp->IsSpecial()) // Not a pseudo column
3727 cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec,
3728 cp->Format.Length, cp->Format.Prec,
3729 true, true, cp->IsUnsigned());
3730
3731 } //endif mode
3732
3733 return false;
3734 } // end of AllocateBuffer
3735
3736 /***********************************************************************/
3737 /* Data Base write routine for huge VCT access method. */
3738 /***********************************************************************/
3739 int BGVFAM::WriteBuffer(PGLOBAL g)
3740 {
3741 if (trace(1))
3742 htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n",
3743 Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk);
3744
3745 if (Tdbp->GetMode() == MODE_UPDATE) {
3746 // Mode Update is done in ReadDB, we just initialize it here
3747 if (Tfile == INVALID_HANDLE_VALUE) {
3748 if (UseTemp) {
3749 if (OpenTempFile(g))
3750 return RC_FX;
3751
3752 // Most of the time, not all table columns are updated.
3753 // This why we must completely pre-fill the temporary file.
3754 Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last
3755 : Block * Nrec; // To write last lock
3756
3757 if (MoveIntermediateLines(g))
3758 return RC_FX;
3759
3760 } else
3761 Tfile = Hfile;
3762
3763 } // endif Tfile
3764
3765 } else {
3766 // Mode Insert
3767 if (MaxBlk && CurBlk == MaxBlk) {
3768 strcpy(g->Message, MSG(TRUNC_BY_ESTIM));
3769 return RC_EF; // Too many lines for a Vector formatted table
3770 } // endif MaxBlk
3771
3772 if (Closing || ++CurNum == Nrec) {
3773 PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns();
3774
3775 if (!AddBlock) {
3776 // Write back the updated last block values
3777 for (; cp; cp = (PVCTCOL)cp->Next)
3778 cp->WriteBlock(g);
3779
3780 if (!Closing && !MaxBlk) {
3781 // Close the VCT file and reopen it in mode Insert
3782 //#if defined(_WIN32) //OB
3783 // CloseHandle(Hfile);
3784 //#else // UNIX
3785 // close(Hfile);
3786 //#endif // UNIX
3787 CloseFileHandle(Hfile);
3788 Hfile = INVALID_HANDLE_VALUE;
3789 To_Fb->Count = 0;
3790 Last = Nrec; // Tested in OpenTableFile
3791
3792 if (OpenTableFile(g)) {
3793 Closing = true; // Tell CloseDB of error
3794 return RC_FX;
3795 } // endif Vopen
3796
3797 AddBlock = true;
3798 } // endif Closing
3799
3800 } else {
3801 // Here we must add a new block to the VCT file
3802 if (Closing)
3803 // Reset the overwritten columns for last block extra records
3804 for (; cp; cp = (PVCTCOL)cp->Next)
3805 memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen,
3806 (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0',
3807 (Nrec - Last) * cp->Clen);
3808
3809 if (BigWrite(g, Hfile, NewBlock, Blksize))
3810 return RC_FX;
3811
3812 } // endif AddBlock
3813
3814 if (!Closing) {
3815 CurBlk++;
3816 CurNum = 0;
3817 } // endif Closing
3818
3819 } // endif
3820
3821 } // endif Mode
3822
3823 return RC_OK;
3824 } // end of WriteBuffer
3825
3826 /***********************************************************************/
3827 /* Data Base delete line routine for BGVFAM access method. */
3828 /***********************************************************************/
3829 int BGVFAM::DeleteRecords(PGLOBAL g, int irc)
3830 {
3831 bool eof = false;
3832
3833 /*********************************************************************/
3834 /* There is an alternative here depending on UseTemp: */
3835 /* 1 - use a temporary file in which are copied all not deleted */
3836 /* lines, at the end the original file will be deleted and */
3837 /* the temporary file renamed to the original file name. */
3838 /* 2 - directly move the not deleted lines inside the original */
3839 /* file, and at the end erase all trailing records. */
3840 /*********************************************************************/
3841 if (trace(1))
3842 htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n",
3843 irc, UseTemp, Fpos, Tpos, Spos);
3844
3845 if (irc != RC_OK) {
3846 /*******************************************************************/
3847 /* EOF: position Fpos at the end-of-file position. */
3848 /*******************************************************************/
3849 Fpos = (Block - 1) * Nrec + Last;
3850
3851 if (trace(1))
3852 htrc("Fpos placed at file end=%d\n", Fpos);
3853
3854 eof = UseTemp && !MaxBlk;
3855 } else // Fpos is the deleted line position
3856 Fpos = CurBlk * Nrec + CurNum;
3857
3858 if (Tpos == Spos) {
3859 if (UseTemp) {
3860 /*****************************************************************/
3861 /* Open the temporary file, Spos is at the beginning of file. */
3862 /*****************************************************************/
3863 if (OpenTempFile(g))
3864 return RC_FX;
3865
3866 } else {
3867 /*****************************************************************/
3868 /* Move of eventual preceding lines is not required here. */
3869 /* Set the target file as being the source file itself. */
3870 /* Set the future Tpos, and give Spos a value to block copying. */
3871 /*****************************************************************/
3872 Tfile = Hfile;
3873 Spos = Tpos = Fpos;
3874 } // endif UseTemp
3875
3876 } // endif Tpos == Spos
3877
3878 /*********************************************************************/
3879 /* Move any intermediate lines. */
3880 /*********************************************************************/
3881 if (MoveIntermediateLines(g, &eof))
3882 return RC_FX;
3883
3884 if (irc == RC_OK) {
3885 #ifdef _DEBUG
3886 assert(Spos == Fpos);
3887 #endif
3888 Spos++; // New start position is on next line
3889
3890 if (trace(1))
3891 htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
3892
3893 } else {
3894 /*******************************************************************/
3895 /* Last call after EOF has been reached. */
3896 /*******************************************************************/
3897 Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0;
3898 Last = (Tpos + Nrec - 1) % Nrec + 1;
3899
3900 if (!UseTemp) { // The UseTemp case is treated in CloseTableFile
3901 if (!MaxBlk) {
3902 if (Last < Nrec) // Clean last block
3903 if (CleanUnusedSpace(g))
3904 return RC_FX;
3905
3906 /***************************************************************/
3907 /* Remove extra records. */
3908 /***************************************************************/
3909 #if defined(_WIN32)
3910 BIGINT pos = (BIGINT)Block * (BIGINT)Blksize;
3911
3912 if (BigSeek(g, Hfile, pos))
3913 return RC_FX;
3914
3915 if (!SetEndOfFile(Hfile)) {
3916 DWORD drc = GetLastError();
3917
3918 sprintf(g->Message, MSG(SETEOF_ERROR), drc);
3919 return RC_FX;
3920 } // endif error
3921 #else // !_WIN32
3922 if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) {
3923 sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
3924 return RC_FX;
3925 } // endif
3926 #endif // !_WIN32
3927 } else // MaxBlk
3928 // Clean the unused space in the file, this is required when
3929 // inserting again with a partial column list.
3930 if (CleanUnusedSpace(g))
3931 return RC_FX;
3932
3933 if (ResetTableSize(g, Block, Last))
3934 return RC_FX;
3935
3936 } // endif UseTemp
3937
3938 } // endif irc
3939
3940 return RC_OK; // All is correct
3941 } // end of DeleteRecords
3942
3943 /***********************************************************************/
3944 /* Open a temporary file used while updating or deleting. */
3945 /***********************************************************************/
3946 bool BGVFAM::OpenTempFile(PGLOBAL g)
3947 {
3948 char *tempname;
3949 PDBUSER dup = PlgGetUser(g);
3950
3951 /*********************************************************************/
3952 /* Open the temporary file, Spos is at the beginning of file. */
3953 /*********************************************************************/
3954 tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH);
3955 PlugSetPath(tempname, To_File, Tdbp->GetPath());
3956 strcat(PlugRemoveType(tempname, tempname), ".t");
3957
3958 if (!MaxBlk)
3959 remove(tempname); // Be sure it does not exist yet
3960 else if (MakeEmptyFile(g, tempname))
3961 return true;
3962
3963 #if defined(_WIN32)
3964 DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW;
3965
3966 Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL,
3967 access, FILE_ATTRIBUTE_NORMAL, NULL);
3968
3969 if (Tfile == INVALID_HANDLE_VALUE) {
3970 DWORD rc = GetLastError();
3971 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname);
3972 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
3973 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0,
3974 (LPTSTR)tempname, _MAX_PATH, NULL);
3975 strcat(g->Message, tempname);
3976 return true;
3977 } // endif Tfile
3978 #else // UNIX
3979 int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC;
3980
3981 Tfile = open64(tempname, oflag, S_IWRITE);
3982
3983 if (Tfile == INVALID_HANDLE_VALUE) {
3984 int rc = errno;
3985 sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname);
3986 strcat(g->Message, strerror(errno));
3987 return true;
3988 } //endif Tfile
3989 #endif // UNIX
3990
3991 To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
3992 To_Fbt->Fname = tempname;
3993 To_Fbt->Type = TYPE_FB_HANDLE;
3994 To_Fbt->Memory = NULL;
3995 To_Fbt->Length = 0;
3996 To_Fbt->File = NULL;
3997 To_Fbt->Next = dup->Openlist;
3998 To_Fbt->Count = 1;
3999 To_Fbt->Mode = MODE_INSERT;
4000 To_Fbt->Handle = Tfile;
4001 dup->Openlist = To_Fbt;
4002 return false;
4003 } // end of OpenTempFile
4004
4005 /***********************************************************************/
4006 /* Move intermediate deleted or updated lines. */
4007 /***********************************************************************/
4008 bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
4009 {
4010 int i, n, req, dep;
4011 bool eof = (b) ? *b : false;
4012 BIGINT pos;
4013
4014 for (n = Fpos - Spos; n > 0 || eof; n -= req) {
4015 /*******************************************************************/
4016 /* Non consecutive line to delete. Move intermediate lines. */
4017 /*******************************************************************/
4018 if (!MaxBlk)
4019 req = (DWORD)MY_MIN(n, Nrec - MY_MAX(Spos % Nrec, Tpos % Nrec));
4020 else
4021 req = (DWORD)MY_MIN(n, Nrec);
4022
4023 if (req) for (i = 0; i < Ncol; i++) {
4024 if (!MaxBlk) {
4025 if (UseTemp)
4026 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4027
4028 pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i])
4029 + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize;
4030 } else
4031 pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i];
4032
4033 if (BigSeek(g, Hfile, pos))
4034 return true;
4035
4036 if (BigRead(g, Hfile, To_Buf, req * Clens[i]))
4037 return true;
4038
4039 if (!UseTemp || MaxBlk) {
4040 if (!MaxBlk)
4041 pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i])
4042 + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize;
4043 else
4044 pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4045
4046 if (BigSeek(g, Tfile, pos))
4047 return true;
4048
4049 if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4050 return true;
4051
4052 } // endif UseTemp
4053
4054 } // endfor i
4055
4056 Tpos += (int)req;
4057 Spos += (int)req;
4058
4059 if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) {
4060 // Write the full or last block to the temporary file
4061 if ((dep = Nrec - (Tpos % Nrec)) < Nrec)
4062 // Clean the last block in case of future insert, must be
4063 // done here because Tfile was open in write only.
4064 for (i = 0; i < Ncol; i++) {
4065 To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i];
4066 memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]);
4067 } // endfor i
4068
4069 if (BigWrite(g, Tfile, NewBlock, Blksize))
4070 return true;
4071
4072 if (Spos == Fpos)
4073 eof = false;
4074
4075 } // endif Usetemp...
4076
4077 if (trace(1))
4078 htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
4079
4080 } // endfor n
4081
4082 return false;
4083 } // end of MoveIntermediateLines
4084
4085 /***********************************************************************/
4086 /* Clean deleted space in a huge VCT or Vec table file. */
4087 /***********************************************************************/
4088 bool BGVFAM::CleanUnusedSpace(PGLOBAL g)
4089 {
4090 int i;
4091 int n;
4092 BIGINT pos, dep;
4093
4094 if (!MaxBlk) {
4095 /*******************************************************************/
4096 /* Clean last block of the VCT table file. */
4097 /*******************************************************************/
4098 assert(!UseTemp); // This case is handled in MoveIntermediateLines
4099
4100 if (!(n = Nrec - Last))
4101 return false;
4102
4103 dep = (BIGINT)((Block - 1) * Blksize);
4104
4105 for (i = 0; i < Ncol; i++) {
4106 memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]);
4107 pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]);
4108
4109 if (BigSeek(g, Hfile, pos))
4110 return true;
4111
4112 if (BigWrite(g, Hfile, To_Buf, n * Clens[i]))
4113 return true;
4114
4115 } // endfor i
4116
4117 } else {
4118 int req;
4119
4120 if (To_Buf)
4121 memset(To_Buf, 0, Buflen);
4122
4123 for (n = Fpos - Tpos; n > 0; n -= req) {
4124 /*****************************************************************/
4125 /* Fill VEC file remaining lines with 0's. */
4126 /* This seems to work even column blocks have been made with */
4127 /* Blanks = true. Perhaps should it be set to false for VEC. */
4128 /*****************************************************************/
4129 req = MY_MIN(n, Nrec);
4130
4131 for (i = 0; i < Ncol; i++) {
4132 pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i];
4133
4134 if (BigSeek(g, Tfile, pos))
4135 return true;
4136
4137 if (BigWrite(g, Tfile, To_Buf, req * Clens[i]))
4138 return true;
4139
4140 } // endfor i
4141
4142 Tpos += req;
4143 } // endfor n
4144
4145 } // endif MaxBlk
4146
4147 return false;
4148 } // end of CleanUnusedSpace
4149
4150 /***********************************************************************/
4151 /* Data Base close routine for huge VEC access method. */
4152 /***********************************************************************/
4153 void BGVFAM::CloseTableFile(PGLOBAL g, bool abort)
4154 {
4155 int rc = 0, wrc = RC_OK;
4156 MODE mode = Tdbp->GetMode();
4157
4158 Abort = abort;
4159
4160 if (mode == MODE_INSERT) {
4161 if (Closing)
4162 wrc = RC_FX; // Last write was in error
4163 else
4164 if (CurNum) {
4165 // Some more inserted lines remain to be written
4166 Last = CurNum;
4167 Block = CurBlk + 1;
4168 Closing = true;
4169 wrc = WriteBuffer(g);
4170 } else {
4171 Last = Nrec;
4172 Block = CurBlk;
4173 wrc = RC_OK;
4174 } // endif CurNum
4175
4176 if (wrc != RC_FX) {
4177 rc = ResetTableSize(g, Block, Last);
4178 } else if (AddBlock) {
4179 // Last block was not written
4180 rc = ResetTableSize(g, CurBlk, Nrec);
4181 throw 44;
4182 } // endif
4183
4184 } else if (mode == MODE_UPDATE) {
4185 // Write back to file any pending modifications
4186 for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols();
4187 colp; colp = (PVCTCOL)colp->Next)
4188 colp->WriteBlock(g);
4189
4190 if (UseTemp && Tfile) {
4191 rc = RenameTempFile(g);
4192 Hfile = Tfile = INVALID_HANDLE_VALUE;
4193
4194 if (Header)
4195 // Header must be set because it was not set in temp file
4196 rc = SetBlockInfo(g);
4197
4198 } // endif UseTemp
4199
4200 } else if (mode == MODE_DELETE && UseTemp && Tfile) {
4201 if (MaxBlk)
4202 rc = CleanUnusedSpace(g);
4203
4204 if ((rc = RenameTempFile(g)) != RC_FX) {
4205 Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo
4206 rc = ResetTableSize(g, Block, Last);
4207 } // endif rc
4208
4209 } // endif's mode
4210
4211 if (Hfile != INVALID_HANDLE_VALUE)
4212 rc = PlugCloseFile(g, To_Fb);
4213
4214 if (trace(1))
4215 htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n",
4216 To_File, wrc, rc);
4217
4218 Hfile = INVALID_HANDLE_VALUE;
4219 } // end of CloseDB
4220
4221 /***********************************************************************/
4222 /* Rewind routine for huge VCT access method. */
4223 /***********************************************************************/
4224 void BGVFAM::Rewind(void)
4225 {
4226 // In mode update we need to read Set Column blocks
4227 if (Tdbp->GetMode() == MODE_UPDATE)
4228 OldBlk = -1;
4229
4230 // Initialize so block optimization is called for 1st block
4231 CurBlk = -1;
4232 CurNum = Nrec - 1;
4233
4234 #if 0 // This is probably unuseful as the file is directly accessed
4235 #if defined(_WIN32) //OB
4236 SetFilePointer(Hfile, 0, NULL, FILE_BEGIN);
4237 #else // UNIX
4238 lseek64(Hfile, 0, SEEK_SET);
4239 #endif // UNIX
4240 #endif // 0
4241 } // end of Rewind
4242
4243 /***********************************************************************/
4244 /* ReadBlock: Read column values from current block. */
4245 /***********************************************************************/
4246 bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp)
4247 {
4248 BIGINT pos;
4249
4250 /*********************************************************************/
4251 /* Calculate the offset and size of the block to read. */
4252 /*********************************************************************/
4253 if (MaxBlk) // File has Vector format
4254 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4255 + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen;
4256 else // Old VCT format
4257 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4258 + (BIGINT)Lrecl * (BIGINT)CurBlk);
4259
4260 if (trace(1))
4261 htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n",
4262 pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk);
4263
4264 if (BigSeek(g, Hfile, pos))
4265 return true;
4266
4267 if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec))
4268 return true;
4269
4270 if (trace(1))
4271 num_read++;
4272
4273 return false;
4274 } // end of ReadBlock
4275
4276 /***********************************************************************/
4277 /* WriteBlock: Write back current column values for one block. */
4278 /* Note: the test of Status is meant to prevent physical writing of */
4279 /* the block during the checking loop in mode Update. It is set to */
4280 /* BUF_EMPTY when reopening the table between the two loops. */
4281 /***********************************************************************/
4282 bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp)
4283 {
4284 int len;
4285 BIGINT pos;
4286
4287 /*********************************************************************/
4288 /* Calculate the offset and size of the block to write. */
4289 /*********************************************************************/
4290 if (MaxBlk) // File has Vector format
4291 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk
4292 + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen;
4293 else // Old VCT format
4294 pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac
4295 + (BIGINT)Lrecl * (BIGINT)colp->ColBlk);
4296
4297 if (trace(1))
4298 htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n",
4299 pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk);
4300
4301 if (BigSeek(g, Tfile, pos))
4302 return true;
4303
4304 //len = colp->Clen * Nrec; see comment in VCTFAM
4305 len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec);
4306
4307 if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len))
4308 return true;
4309
4310 return false;
4311 } // end of WriteBlock
4312
4313 /* ----------------------- End of FilAMVct --------------------------- */
4314