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