You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4219 lines
138 KiB

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