1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // CegoFileHandler.cc
4 // ------------------
5 // Cego database file handler implementation
6 //
7 // Design and Implementation by Bjoern Lemke
8 //
9 // (C)opyright 2000-2019 Bjoern Lemke
10 //
11 // IMPLEMENTATION MODULE
12 //
13 // Class: CegoFileHandler
14 //
15 // Description: The filehandler class provides access to all datafiles. Datafiles consists
16 // of a defined number of pages managed by the this class.
17 // At the header of a datafile, a bitmap is stored for used page information
18 //
19 // Status: CLEAN
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include <lfcbase/Exception.h>
24
25 #include "CegoFileHandler.h"
26 #include "CegoBufferPage.h"
27
28 #define BIT_PER_BYTE 8
29 #define FILEHEADSIZE sizeof(int) + sizeof(FileType) + sizeof(int) + sizeof(PageIdType)
30
31 extern bool __fsyncOn;
32
CegoFileHandler(const Chain & logFile,const Chain & progName)33 CegoFileHandler::CegoFileHandler(const Chain& logFile, const Chain& progName) : CegoModule(logFile, progName)
34 {
35 for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
36 {
37 _isReg[i]=false;
38 _tabSetId[i]=0;
39 _fhList[i]=0;
40 _buMask[i]=0;
41 _commitMask[i]=0;
42 _pageOffset[i]=0;
43 }
44 _appendFid = 0;
45 _isReadOnly = false;
46 _modId = getModId("CegoFileHandler");
47 }
48
~CegoFileHandler()49 CegoFileHandler::~CegoFileHandler()
50 {
51 for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
52 {
53 if ( _fhList[i] != 0 )
54 {
55 _fhList[i]->close();
56 delete _fhList[i];
57 }
58 if ( _buMask[i] != 0 )
59 {
60 delete _buMask[i];
61 }
62 if ( _commitMask[i] != 0 )
63 {
64 delete _commitMask[i];
65 }
66 }
67 }
68
setReadOnly(bool isReadOnly)69 void CegoFileHandler::setReadOnly(bool isReadOnly)
70 {
71 _isReadOnly = isReadOnly;
72 }
73
setBackup(int fileId,bool m)74 void CegoFileHandler::setBackup(int fileId, bool m)
75 {
76 if ( m == true )
77 {
78 _buMask[fileId] = new unsigned[ ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1];
79
80 int i;
81
82 for (i = 0; i < ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
83 {
84 _buMask[fileId][i] = 0;
85 }
86 _fbmMask[fileId] = false;
87 }
88 else
89 {
90 if ( _buMask[fileId] != 0 )
91 {
92 delete _buMask[fileId];
93 _buMask[fileId] = 0;
94 }
95 }
96 }
97
needPageBackup(PageIdType pageId)98 bool CegoFileHandler::needPageBackup(PageIdType pageId)
99 {
100 int fileId = getFileIdForPageId(pageId);
101
102 // check if backup is activated for file
103 if ( _buMask[fileId] )
104 {
105 // is page already marked, no need to backup page
106 if ( isPageMarked(pageId, fileId) )
107 return false;
108 return true;
109 }
110
111 return false;
112 }
113
markPage(PageIdType pageId)114 void CegoFileHandler::markPage(PageIdType pageId)
115 {
116 int fileId = getFileIdForPageId(pageId);
117
118 if ( _buMask[fileId] == 0 )
119 {
120 Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
121 throw Exception(EXLOC, msg);
122 }
123
124 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
125 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
126
127 unsigned bm = _buMask[fileId][bmid];
128
129 unsigned f = ~(~(unsigned)0 << 1);
130 f = f << bmoffset;
131
132 _buMask[fileId][bmid] = bm | f;
133
134 }
135
136
isPageMarked(PageIdType pageId,int fileId)137 bool CegoFileHandler::isPageMarked(PageIdType pageId, int fileId)
138 {
139
140 if ( _buMask[fileId] == 0 )
141 {
142 Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
143 throw Exception(EXLOC, msg);
144 }
145
146 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
147 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
148
149 unsigned bm = _buMask[fileId][bmid];
150
151 unsigned checkbit = bm >> bmoffset & ~(~(unsigned)0 << 1);
152
153 if ( checkbit )
154 {
155 return true;
156 }
157 return false;
158 }
159
160
getPageOffset(int fileId)161 PageIdType CegoFileHandler::getPageOffset(int fileId)
162 {
163 return _pageOffset[fileId];
164 }
165
getFBMSize(int fileId)166 int CegoFileHandler::getFBMSize(int fileId)
167 {
168 return (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
169 }
170
readFBM(int fileId,unsigned * fbm,CegoLockHandler * pLockHandle)171 void CegoFileHandler::readFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
172 {
173 File *pF;
174
175 try
176 {
177 pF = getHandle(fileId);
178 }
179 catch ( Exception e )
180 {
181 throw e;
182 }
183
184 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
185
186 try
187 {
188
189 pF->seek(FILEHEADSIZE);
190
191 // int tabSetId;
192 // pF->readByte((char*)&tabSetId, sizeof(int));
193
194 int numBitmap = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
195 int i;
196 for (i = 0; i < numBitmap; i++)
197 {
198 pF->readByte((char*)&fbm[i], sizeof(unsigned));
199 }
200
201 pLockHandle->unlockDataFile(fileId);
202
203 }
204 catch ( Exception e )
205 {
206 pLockHandle->unlockDataFile(fileId);
207 throw e;
208 }
209 }
210
writeFBM(int fileId,unsigned * fbm,CegoLockHandler * pLockHandle)211 void CegoFileHandler::writeFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
212 {
213 if ( _isReadOnly == true )
214 return;
215
216 File *pF;
217
218 try
219 {
220 pF = getHandle(fileId);
221 }
222 catch ( Exception e )
223 {
224 throw e;
225 }
226
227 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
228
229 try
230 {
231 pF->seek(FILEHEADSIZE);
232
233 int numBitmap = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1;
234 int i;
235
236 for (i = 0; i < numBitmap; i++)
237 {
238 pF->writeByte((char*)&fbm[i], sizeof(unsigned));
239 }
240
241 if ( __fsyncOn )
242 pF->flush();
243
244 pLockHandle->unlockDataFile(fileId);
245
246 }
247 catch ( Exception e )
248 {
249 pLockHandle->unlockDataFile(fileId);
250 throw e;
251 }
252 }
253
254 /* the cleanDataFile method traces the corresponding datafile to check for
255 allocated but unused pages. This may happen, if the allocatePage method
256 was called, but for any reason ( e.g. a system crash ) the page could never be written
257 back to file. In this case, the fixStat attribute is still zero. so the page
258 can be released for other use
259
260 Since introduction of the more complete cleanup procedure based on database object analysis ( CegoTableManager::cleanTableSet )
261 this method is obsolete
262 */
263
264
265 /*
266 void CegoFileHandler::cleanDataFile(int fileId, CegoLockHandler *pLockHandle)
267 {
268 File* pF;
269
270 try
271 {
272 pF = getHandle(fileId);
273 }
274 catch ( Exception e )
275 {
276 throw e;
277 }
278
279 int offset = FILEHEADSIZE
280 + ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned)))+1) * sizeof(unsigned) ;
281
282 int pageIndex;
283
284 for (pageIndex = 0; pageIndex < _numPages[fileId]; pageIndex++)
285 {
286 pF->seek(offset + pageIndex * ( _pageSize + sizeof(unsigned)));
287 unsigned fixStat;
288 pF->readByte((char*)&fixStat, sizeof(unsigned));
289
290 PageIdType pageId = _pageOffset[fileId] + pageIndex;
291
292 if ( fixStat == 0 && isClaimed(pageId, pLockHandle) == true )
293 {
294 log(_modId, Logger::NOTICE, Chain("Releasing page ") + Chain(pageId));
295 unsigned *fbm;
296 int fbmSize;
297
298 releasePage(pageId, pLockHandle, fbm, fbmSize);
299 }
300 }
301 }
302 */
303
304 /* a new datafile is created using the initDataFile method. With the given tabSetId, path, fileId, size and type
305 the file is created and initialized. Note, that after creation the datafile is still NOT registered for further usage.
306 This must be done using the regDataFile method */
307
initDataFile(int tabSetId,const Chain & path,int fileId,int numPages,PageIdType pageOffset,FileType ft)308 void CegoFileHandler::initDataFile(int tabSetId, const Chain& path, int fileId, int numPages, PageIdType pageOffset, FileType ft)
309 {
310 if ( _isReadOnly == true )
311 return;
312
313 File* pF = new File(path);
314
315 if ( pF->exists() )
316 {
317 Chain msg = Chain("Datafile ") + path + Chain(" already exists");
318 delete pF;
319 throw Exception( EXLOC, msg);
320 }
321
322 try
323 {
324 pF->open(File::WRITE);
325
326 pF->writeByte((char*)&tabSetId, sizeof(int));
327 pF->writeByte((char*)&ft, sizeof(FileType));
328 // cout << "Init datafile " << path << " with numPages " << numPages << endl;
329 pF->writeByte((char*)&numPages, sizeof(int));
330
331 pF->writeByte((char*)&pageOffset, sizeof(PageIdType));
332
333 int i;
334
335 for (i = 0; i < (numPages / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
336 {
337 unsigned bm = 0;
338 pF->writeByte((char*)&bm, sizeof(unsigned));
339 }
340
341 char* initBuf = new char[_pageSize];
342 CegoBufferPage ip(initBuf, _pageSize);
343 ip.initPage(CegoBufferPage::TABLE);
344
345 for (i = 0; i < numPages; i++)
346 {
347 unsigned fixStat = 0;
348 pF->writeByte((char*)&fixStat, sizeof(unsigned));
349 pF->writeByte(initBuf, _pageSize);
350 }
351 delete[] initBuf;
352
353 pF->close();
354 delete pF;
355 }
356 catch ( Exception e )
357 {
358 delete pF;
359 throw e;
360 }
361 }
362
resetDataFile(int fileId)363 void CegoFileHandler::resetDataFile(int fileId)
364 {
365
366 if ( _isReadOnly == true )
367 return;
368
369 File* pF;
370
371 try
372 {
373 pF = getHandle(fileId);
374 }
375 catch ( Exception e )
376 {
377 throw e;
378 }
379
380 pF->seek(FILEHEADSIZE);
381
382 int i;
383
384 for (i = 0; i < ( _numPages[fileId] / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
385 {
386 unsigned bm = 0;
387 pF->writeByte((char*)&bm, sizeof(unsigned));
388 }
389
390 if ( __fsyncOn )
391 pF->flush();
392
393 }
394
395
396
397 /* the regDataFile method is used for datafile registration. The given file is checked
398 for consistency and is registered */
399
regDataFile(int tabSetId,const Chain & path,int fileId,CegoLockHandler * pLockHandle)400 void CegoFileHandler::regDataFile(int tabSetId, const Chain& path, int fileId, CegoLockHandler *pLockHandle)
401 {
402 pLockHandle->lockDataFile(fileId, CegoLockHandler::READ);
403
404 if ( _isReg[fileId] )
405 {
406 pLockHandle->unlockDataFile(fileId);
407
408 if ( _path[fileId] == path )
409 {
410 log(_modId, Logger::NOTICE, Chain("Data file ") + path + Chain(" already exists, skipping"));
411 return;
412 }
413 else
414 {
415 Chain msg = Chain("Datafile Id ") + Chain(fileId) + Chain(" already occupied");
416 throw Exception(EXLOC, msg);
417 }
418 }
419
420 int checkTabSetId;
421 int numPage;
422 PageIdType pageOffset;
423
424 FileType ft;
425
426 try
427 {
428 File checkFile(path);
429 checkFile.open(File::READ);
430
431 checkFile.readByte((char*)&checkTabSetId, sizeof(int));
432 checkFile.readByte((char*)&ft, sizeof(FileType));
433 checkFile.readByte((char*)&numPage, sizeof(int));
434 checkFile.readByte((char*)&pageOffset, sizeof(PageIdType));
435
436 checkFile.close();
437 }
438 catch ( Exception e )
439 {
440 Chain ferr;
441 e.pop(ferr);
442 pLockHandle->unlockDataFile(fileId);
443 Chain msg = Chain("Cannot access datafile <") + path + Chain("> :") + ferr;
444 throw Exception(EXLOC, msg);
445 }
446
447 if ( tabSetId != checkTabSetId )
448 {
449 pLockHandle->unlockDataFile(fileId);
450 throw Exception(EXLOC, "Table Set Id does not match");
451 }
452
453 _pageOffset[fileId] = pageOffset;
454 _isReg[fileId] = true;
455 _path[fileId] = path;
456 _tabSetId[fileId] = tabSetId;
457 _fileType[fileId] = ft;
458 _numPages[fileId] = numPage;
459
460 pLockHandle->unlockDataFile(fileId);
461 }
462
463 /* all registered files of the corresponding table set are closed and unregistered
464 with the releaseFiles method */
465
releaseFiles(int tabSetId)466 void CegoFileHandler::releaseFiles(int tabSetId)
467 {
468 for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
469 {
470 if ( _tabSetId[i] == tabSetId )
471 {
472 if ( _fhList[i] != 0 )
473 {
474 _fhList[i]->close();
475 _fhList[i]=0;
476 }
477 _tabSetId[i]=0;
478 _isReg[i]=false;
479 }
480 }
481 }
482
483 /* a single database page is written to file using the writePage method. With the corresponding fileId, pageId
484 and the pointer to the data, the file handler can perform the write operation. The fixStat parameter indicates
485 the number of bufferFix operations on the page and is normally used by the BufferPool class for the page
486 relocation strategy */
487
writePage(PageIdType pageId,unsigned fixStat,char * pageData,CegoLockHandler * pLockHandle)488 void CegoFileHandler::writePage(PageIdType pageId, unsigned fixStat, char* pageData, CegoLockHandler *pLockHandle)
489 {
490 if ( _isReadOnly == true )
491 throw Exception(EXLOC, Chain("Cannot write page in read only mode"));
492
493 int fileId = getFileIdForPageId(pageId);
494
495 File* pF;
496
497 try
498 {
499 pF = getHandle(fileId);
500 }
501 catch ( Exception e )
502 {
503 throw e;
504 }
505
506 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
507
508 try
509 {
510 unsigned long offset = FILEHEADSIZE
511 + ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned) )) + 1) * sizeof(unsigned);
512
513 pF->seek(offset + ( pageId - _pageOffset[fileId] ) * ( _pageSize + sizeof(unsigned)));
514 pF->writeByte((char*)&fixStat, sizeof(unsigned));
515 pF->writeByte(pageData, _pageSize);
516
517 if ( _buMask[fileId] )
518 markPage(pageId);
519
520 if ( __fsyncOn )
521 pF->flush();
522
523 pLockHandle->unlockDataFile(fileId);
524 }
525 catch ( Exception e )
526 {
527 pLockHandle->unlockDataFile(fileId);
528 throw e;
529 }
530 }
531
532 /* pages from datafiles are read by using the readPage method. The method seeks to the appropriate offset
533 in the corresponding datafile and reads the page data into the given data pointer */
534
readPage(PageIdType pageId,int & tabSetId,unsigned & fixStat,char * pageData,CegoLockHandler * pLockHandle)535 void CegoFileHandler::readPage(PageIdType pageId, int& tabSetId, unsigned& fixStat, char* pageData, CegoLockHandler *pLockHandle)
536 {
537 int fileId = getFileIdForPageId(pageId);
538
539 File *pF;
540
541 try
542 {
543 pF = getHandle(fileId);
544 }
545 catch ( Exception e )
546 {
547 throw e;
548 }
549
550 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
551
552 try
553 {
554 pF->seek(0);
555 pF->readByte((char*)&tabSetId, sizeof(int));
556
557 unsigned long offset = FILEHEADSIZE
558 + ((_numPages[fileId] / ( BIT_PER_BYTE * sizeof(unsigned) )) + 1) * sizeof(unsigned) ;
559
560 pF->seek(offset + ( pageId - _pageOffset[fileId] ) * ( _pageSize + sizeof(unsigned)) );
561
562 pF->readByte((char*)&fixStat, sizeof(unsigned));
563
564 unsigned long rb = pF->readByte(pageData, _pageSize);
565 while ( rb < _pageSize )
566 {
567 unsigned long rrb = pF->readByte((char*)((long long)pageData + rb), _pageSize - rb);
568
569 if ( rrb == 0 )
570 throw Exception(EXLOC, Chain("Incomplete file read"));
571
572 rb+=rrb;
573 }
574
575 pLockHandle->unlockDataFile(fileId);
576 }
577 catch ( Exception e )
578 {
579 pLockHandle->unlockDataFile(fileId);
580 throw e;
581 }
582 }
583
584 /* the claimPage method allocates a given page in the given file.
585 Normally this method is used for "well known entry pages", e.g. the system catalog page
586 which is used as an entry point. */
587
claimPage(PageIdType pageId,CegoLockHandler * pLockHandle)588 void CegoFileHandler::claimPage(PageIdType pageId, CegoLockHandler *pLockHandle)
589 {
590 if ( _isReadOnly == true )
591 throw Exception(EXLOC, Chain("Cannot claim page in read only mode"));
592
593 int fileId = getFileIdForPageId(pageId);
594
595 File *pF;
596
597 try
598 {
599 pF = getHandle(fileId);
600 }
601 catch ( Exception e )
602 {
603 throw e;
604 }
605
606 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
607
608 try
609 {
610 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
611 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
612
613 pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
614
615 unsigned bm;
616 pF->readByte((char*)&bm, sizeof(unsigned));
617
618 unsigned f = ~(~(unsigned)0 << 1);
619 f = f << bmoffset;
620
621 bm = bm | f;
622
623 pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
624 pF->writeByte((char*)&bm, sizeof(unsigned));
625
626 if ( _buMask[fileId] )
627 _fbmMask[fileId] = true;
628
629 if ( __fsyncOn )
630 pF->flush();
631
632 pLockHandle->unlockDataFile(fileId);
633 }
634 catch ( Exception e )
635 {
636 pLockHandle->unlockDataFile(fileId);
637 throw e;
638 }
639
640 // write page data to datafile to avoid, that cleanup procedure frees the page
641
642 char* initBuf = new char[_pageSize];
643 CegoBufferPage ip(initBuf, _pageSize);
644 ip.initPage(CegoBufferPage::TABLE);
645
646 writePage(pageId, 1, initBuf, pLockHandle);
647
648 }
649
650 /* any new required page can be requested using the allocatePage method.
651 the required filetype must be specified.
652 the method returns the assigned file and page information */
653
allocatePage(int tabSetId,FileType ft,PageIdType & pageId,CegoLockHandler * pLockHandle,unsigned * & fbm,int & fbmSize,bool doAppend)654 void CegoFileHandler::allocatePage(int tabSetId, FileType ft, PageIdType& pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize, bool doAppend)
655 {
656 if ( _isReadOnly == true )
657 throw Exception(EXLOC, Chain("Cannot allocate page in read only mode"));
658
659 int fid = 0;
660 if ( doAppend )
661 fid = _appendFid;
662
663 while ( fid < FILMNG_MAXDATAFILE )
664 {
665 if ( _isReg[fid] && _tabSetId[fid] == tabSetId && _fileType[fid] == ft )
666 {
667 File* pF;
668 pF = getHandle(fid);
669
670 pLockHandle->lockDataFile(fid, CegoLockHandler::WRITE);
671
672 try
673 {
674 unsigned ws = sizeof(unsigned)*BIT_PER_BYTE;
675
676 PageIdType k;
677
678 if ( doAppend == false || _appendPos[fid] == 0)
679 {
680 pF->seek(FILEHEADSIZE);
681 k = 0;
682 // we reset the append postion to zero, so next allocation with append = true will search from the beginning
683 _appendPos[fid] = 0;
684 }
685 else
686 {
687 // cout << "Seek to " << 2*sizeof(int) + sizeof(FileType) + (_appendPos[fid] / ws ) * sizeof(unsigned) << endl;
688 pF->seek(FILEHEADSIZE + ( _appendPos[fid] / ws) * sizeof(unsigned));
689 k = _appendPos[fid];
690 }
691
692 // cout << "k=" << k << " numPages = " << _numPages[fid] << endl;
693
694 bool isEop = k >= _numPages[fid];
695
696 while (!isEop)
697 {
698 unsigned bm;
699
700 pF->readByte((char*)&bm, sizeof(unsigned));
701
702 // printBitMap(bm);
703
704 if ( (bm & ~0) != ~0)
705 {
706 // we start next append search with this fid
707 _appendFid = fid;
708
709 // free page available
710 int l = 0;
711 while (l < ws && ! isEop)
712 {
713 if ( k+l < _numPages[fid])
714 {
715 unsigned checkbit = bm >> l & ~(~(unsigned)0 << 1);
716 if (checkbit == 0)
717 {
718 pageId = k + l + _pageOffset[fid];
719
720 /*
721 cout << "Allocated page :" << endl;
722 cout << "Fid = " << fid << endl;
723 cout << "PageOffset=" << _pageOffset[fid] << endl;
724 cout << "PageId = " << pageId << endl;
725 */
726
727 // cout << "FH: " << fid << "," << k << "," << l << " using pageId " << pageId << endl;
728
729 unsigned f = ~(~(unsigned)0 << 1);
730 f = f << l;
731 // printBitMap(bm);
732 // cout << "1 bm=" << bm << endl;
733 bm = bm | f;
734 // printBitMap(bm);
735
736 if ( _buMask[fid] )
737 {
738 if ( _fbmMask[fid] == false )
739 {
740 fbmSize = getFBMSize(fid);
741 fbm = new unsigned[fbmSize];
742 readFBM(fid, fbm, pLockHandle);
743 }
744 else
745 {
746 fbmSize = 0;
747 }
748
749 _fbmMask[fid] = true;
750 }
751
752 // cout << "Writing bitmask to file .." << endl;
753 pF->seek(FILEHEADSIZE + k / BIT_PER_BYTE);
754 pF->writeByte((char*)&bm, sizeof(unsigned));
755 if ( __fsyncOn )
756 pF->flush();
757
758 pLockHandle->unlockDataFile(fid);
759
760 if ( _appendPos[fid] < k )
761 {
762 _appendPos[fid] = k;
763 }
764 return;
765 }
766 l++;
767 }
768 else
769 {
770 isEop = true;
771 }
772 }
773 }
774 k+=ws;
775 }
776 pLockHandle->unlockDataFile(fid);
777 }
778 catch ( Exception e )
779 {
780 pLockHandle->unlockDataFile(fid);
781 throw e;
782 }
783 }
784 fid++;
785 }
786
787 if ( doAppend )
788 {
789 _appendFid=0;
790 // if no more pages available with append mode, we make another try without append
791 allocatePage(tabSetId, ft, pageId, pLockHandle, fbm, fbmSize, false);
792 }
793 else
794 {
795 Chain fileType;
796 if ( ft == DATAFILE )
797 fileType = Chain("Data");
798 else if ( ft == SYSTEMFILE )
799 fileType = Chain("System");
800 else if ( ft == TEMP )
801 fileType = Chain("Temp");
802 throw Exception(EXLOC, fileType + Chain(" file pages exceeded "));
803 }
804 }
805
806 /* if a page is no longer used, it can be released using the releasePage method.
807 Normally this is used, in case of delete operations or in case of temporarily used pages */
808
releasePage(PageIdType pageId,CegoLockHandler * pLockHandle,unsigned * & fbm,int & fbmSize)809 void CegoFileHandler::releasePage(PageIdType pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize)
810 {
811 if ( _isReadOnly == true )
812 throw Exception(EXLOC, Chain("Cannot releases page in read only mode"));
813
814 int fileId = getFileIdForPageId(pageId);
815
816 File* pF;
817 pF = getHandle(fileId);
818
819 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
820
821 try
822 {
823 if ( _buMask[fileId] )
824 {
825 if ( _fbmMask[fileId] == false )
826 {
827 fbmSize = getFBMSize(fileId);
828 fbm = new unsigned[fbmSize];
829 readFBM(fileId, fbm, pLockHandle);
830 }
831 }
832 else
833 {
834 fbmSize = 0;
835 }
836
837 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( sizeof(unsigned) * BIT_PER_BYTE );
838 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( sizeof(unsigned) * BIT_PER_BYTE) ;
839
840 // cout << "FH release : " << bmid << "," << bmoffset << " using pageId " << pageId << endl;
841
842 pF->seek(FILEHEADSIZE + ( bmid * sizeof(unsigned)));
843
844 unsigned bm;
845 pF->readByte((char*)&bm, sizeof(unsigned));
846
847 unsigned f = ~(~(unsigned)0 << 1);
848 f = f << bmoffset;
849
850 // printBitMap(bm);
851 // printBitMap(f);
852
853 bm = bm & ~f;
854
855 // cout << "Bitmap after " << endl;
856 // printBitMap(bm);
857
858 // cout << "Write to pos " << sizeof(int) + sizeof(FileType) + sizeof(int) + ( bmid * sizeof(int)) << endl;
859
860 pF->seek(FILEHEADSIZE + ( bmid * sizeof(unsigned)));
861 pF->writeByte((char*)&bm, sizeof(unsigned));
862 if ( __fsyncOn )
863 pF->flush();
864
865 if ( _buMask[fileId] )
866 _fbmMask[fileId] = true;
867
868
869 pLockHandle->unlockDataFile(fileId);
870 }
871 catch ( Exception e )
872 {
873 pLockHandle->unlockDataFile(fileId);
874 throw e;
875 }
876 }
877
commitPageEntry(PageIdType pageId)878 void CegoFileHandler::commitPageEntry(PageIdType pageId)
879 {
880 int fileId = getFileIdForPageId(pageId);
881
882 if ( _commitMask[fileId] == 0 )
883 {
884 _commitMask[fileId] = new unsigned[ ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1];
885
886 int i;
887
888 for (i = 0; i < ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(unsigned))) + 1; i++)
889 {
890 _commitMask[fileId][i] = 0;
891 }
892 }
893
894 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
895 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
896
897 unsigned bm = _commitMask[fileId][bmid];
898
899 unsigned f = ~(~(unsigned)0 << 1);
900 f = f << bmoffset;
901
902 _commitMask[fileId][bmid] = bm | f;
903
904 }
905
cleanPages(CegoLockHandler * pLockHandle)906 int CegoFileHandler::cleanPages(CegoLockHandler *pLockHandle)
907 {
908 if ( _isReadOnly == true )
909 throw Exception(EXLOC, Chain("Cannot clean pages in read only mode"));
910
911 int numCleanup = 0;
912
913 for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
914 {
915 if ( _isReg[i] && _fileType[i] == DATAFILE && _commitMask[i])
916 {
917
918 int fbmSize = getFBMSize(i);
919 unsigned* fbm = new unsigned[fbmSize];
920 readFBM(i, fbm, pLockHandle);
921
922 bool cleanupNeeded = false;
923
924 for ( int j=0; j<fbmSize; j++ )
925 {
926 // cout << "Checking mask " << _commitMask[i][j] << endl;
927
928 if ( fbm[j] != _commitMask[i][j] )
929 {
930 // cleanup required
931
932
933 cleanupNeeded = true;
934
935 numCleanup += mapDiff(fbm[i], _commitMask[i][j] );
936
937 /*
938 cout << "Commited :" << endl;
939 printBitMap(_commitMask[i][j]);
940
941 cout << "Written :" << endl;
942 printBitMap(fbm[j]);
943 */
944 }
945 }
946
947 if ( cleanupNeeded )
948 writeFBM(i, _commitMask[i], pLockHandle);
949
950 delete[] fbm;
951 }
952 }
953
954 return numCleanup;
955 }
956
957
958 /* returns the number of total pages for the given fileId */
959
getNumPages(int fileId)960 int CegoFileHandler::getNumPages(int fileId)
961 {
962 try
963 {
964 // check valid fileId
965 getHandle(fileId);
966 }
967 catch ( Exception e )
968 {
969 throw e;
970 }
971
972 return _numPages[fileId];
973 }
974
975 /* returns the number of used pages for the given fileId */
976
getNumUsedPages(int fileId,CegoLockHandler * pLockHandle)977 int CegoFileHandler::getNumUsedPages(int fileId, CegoLockHandler *pLockHandle)
978 {
979 File *pF;
980
981 try
982 {
983 pF = getHandle(fileId);
984 }
985 catch ( Exception e )
986 {
987 throw e;
988 }
989
990 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
991
992 try
993 {
994 pF->seek(sizeof(int) + sizeof(FileType));
995
996 int numPage;
997 pF->readByte((char*)&numPage, sizeof(int));
998
999 PageIdType pageOffset;
1000 pF->readByte((char*)&pageOffset, sizeof(PageIdType));
1001
1002 int p;
1003
1004 int usedPages = 0;
1005
1006 for ( p = 0; p < (numPage / (BIT_PER_BYTE * sizeof(unsigned))) + 1; p++ )
1007 {
1008 unsigned bm = 0;
1009 pF->readByte((char*)&bm, sizeof(unsigned));
1010
1011 // printBitMap(bm);
1012
1013 int i,j;
1014 for (i = 0; i< sizeof(unsigned); i++)
1015 {
1016 for (j=0; j < BIT_PER_BYTE; j++)
1017 {
1018 int one = 1;
1019
1020 if ( one & bm )
1021 {
1022 usedPages++;
1023 }
1024 bm = bm >> 1;
1025
1026 }
1027 }
1028 }
1029
1030 pLockHandle->unlockDataFile(fileId);
1031
1032 return usedPages;
1033 }
1034 catch ( Exception e )
1035 {
1036 pLockHandle->unlockDataFile(fileId);
1037 throw e;
1038 }
1039 }
1040
1041 ////////////////////////////////////////////////
1042 ////////////// private methods /////////////////
1043 ////////////////////////////////////////////////
1044
1045 /* to proof, if a given pageId is already in use,
1046 the isClaimed method is used */
1047
isClaimed(PageIdType pageId,CegoLockHandler * pLockHandle)1048 bool CegoFileHandler::isClaimed(PageIdType pageId, CegoLockHandler *pLockHandle)
1049 {
1050 int fileId = getFileIdForPageId(pageId);
1051
1052 File *pF;
1053
1054 try
1055 {
1056 pF = getHandle(fileId);
1057 }
1058 catch ( Exception e )
1059 {
1060 throw e;
1061 }
1062
1063 if ( pageId >= _pageOffset[fileId] + _numPages[fileId] )
1064 {
1065 throw Exception(EXLOC, Chain("Invalid page id"));
1066 }
1067
1068 pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);
1069
1070 try
1071 {
1072 unsigned bmid = ( pageId - _pageOffset[fileId] ) / ( BIT_PER_BYTE * sizeof(unsigned));
1073 unsigned bmoffset = ( pageId - _pageOffset[fileId] ) % ( BIT_PER_BYTE * sizeof(unsigned));
1074
1075 pF->seek(FILEHEADSIZE + bmid * sizeof(unsigned));
1076
1077 unsigned bm;
1078 pF->readByte((char*)&bm, sizeof(unsigned));
1079
1080 pLockHandle->unlockDataFile(fileId);
1081
1082 unsigned checkbit = bm >> bmoffset & ~(~(unsigned)0 << 1);
1083
1084 if ( checkbit )
1085 {
1086 return true;
1087 }
1088 return false;
1089 }
1090 catch ( Exception e )
1091 {
1092 pLockHandle->unlockDataFile(fileId);
1093 throw e;
1094 }
1095 }
1096
getFileIdForPageId(PageIdType pageId)1097 int CegoFileHandler::getFileIdForPageId(PageIdType pageId)
1098 {
1099 int i=0;
1100
1101 while ( i < FILMNG_MAXDATAFILE )
1102 {
1103 if ( _isReg[i] == true && pageId >= _pageOffset[i] && pageId < _pageOffset[i] + _numPages[i] )
1104 {
1105 return i;
1106 }
1107 i++;
1108 }
1109 throw Exception(EXLOC, Chain("Invalid page id ") + Chain(pageId));
1110 }
1111
1112 /* getHandle returns the file handle for the given file id. The
1113 file id must be valid and registered. If already open, the method
1114 returns the file handle. Otherwise, the corresponding file is opened
1115 for reading and writing and the handle is returned. */
1116
getHandle(int fileId)1117 File* CegoFileHandler::getHandle(int fileId)
1118 {
1119 if ( fileId > FILMNG_MAXDATAFILE - 1 )
1120 {
1121 Chain msg = "File Id " + Chain(fileId) + " out of valid range";
1122 throw Exception(EXLOC, msg);
1123 }
1124
1125 if ( ! _isReg[fileId] )
1126 {
1127 Chain msg = "File Id " + Chain(fileId) + " not registered";
1128 throw Exception(EXLOC, msg);
1129 }
1130
1131 if ( _fhList[fileId] == 0 )
1132 {
1133 File* pF = new File(_path[fileId]);
1134
1135 try
1136 {
1137 if ( _isReadOnly == true )
1138 pF->open(File::READ);
1139 else
1140 pF->open(File::READWRITE);
1141
1142 _fhList[fileId] = pF;
1143 _appendPos[fileId] = 0;
1144 }
1145 catch (Exception e)
1146 {
1147 Chain ferr;
1148 e.pop(ferr);
1149 Chain msg = "Cannot get file handle " + Chain(fileId) + Chain(": ") + ferr;
1150 throw Exception(EXLOC, msg);
1151 }
1152 }
1153 File *pF = _fhList[fileId];
1154 return pF;
1155 }
1156
1157 ////////////////////////////////////////////////
1158 //////// debugging/testing methods /////////////
1159 ////////////////////////////////////////////////
1160
printBitMap(unsigned bm)1161 void CegoFileHandler::printBitMap(unsigned bm)
1162 {
1163 printf("--- BM -----------\n");
1164
1165 int i,j;
1166 for (i = 0; i< sizeof(unsigned); i++)
1167 {
1168 for (j=0; j < BIT_PER_BYTE; j++)
1169 {
1170 unsigned one = 1;
1171
1172 if ( one & bm )
1173 printf("1");
1174 else
1175 printf("0");
1176 bm = bm >> 1;
1177
1178 }
1179 }
1180 printf("\n");
1181
1182 printf("--------------\n");
1183 }
1184
mapDiff(unsigned bm1,unsigned bm2)1185 int CegoFileHandler::mapDiff(unsigned bm1, unsigned bm2)
1186 {
1187 int numDiff = 0;
1188
1189 int i,j;
1190 for (i = 0; i< sizeof(unsigned); i++)
1191 {
1192 for (j=0; j < BIT_PER_BYTE; j++)
1193 {
1194 unsigned one = 1;
1195
1196 if ( ( one & bm1 ) != ( one & bm2 ) )
1197 numDiff++;
1198
1199 bm1 = bm1 >> 1;
1200 bm2 = bm2 >> 1;
1201
1202
1203 }
1204 }
1205
1206 return numDiff;
1207 }
1208