📄 db.c
字号:
#include <memory.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>#include <fcntl.h>#include <iostream>#include <math.h>#include <stdio.h>#include "page.h"#include "db.h"#include "buf.h"#define DBP(p) (*(DBPage*)&p)// openfile hash table implementationOpenFileHashTbl::OpenFileHashTbl(){ HTSIZE = 113; // hack // allocate an array of pointers to fleHashBuckets ht = new fileHashBucket* [HTSIZE]; for(int i=0; i < HTSIZE; i++) ht[i] = NULL;}OpenFileHashTbl::~OpenFileHashTbl(){ for(int i = 0; i < HTSIZE; i++) { fileHashBucket* tmpBuf = ht[i]; while (ht[i]) { tmpBuf = ht[i]; ht[i] = ht[i]->next; // blow away the file object in case someone forgot to close it if (tmpBuf->file != NULL) delete tmpBuf->file; delete tmpBuf; } } delete [] ht;}int OpenFileHashTbl::hash(const string fileName){ int i, value, len; len = (int) fileName.length(); value = 0; for (i=0;i<len;i++) value = 31*value + (int) fileName[i]; value = abs(value % HTSIZE); return value;}// inserts fileName into hash table of open files// returns OK if insertion was successful, HASHTBLERROR if an error occurred//---------------------------------------------------------------Status OpenFileHashTbl::insert(const string fileName, File* file ) { int index = hash(fileName); fileHashBucket* tmpBuc = ht[index]; while (tmpBuc) { if (tmpBuc->fname == fileName) return HASHTBLERROR; tmpBuc = tmpBuc->next; } tmpBuc = new fileHashBucket; if (!tmpBuc) return HASHTBLERROR; tmpBuc->fname = fileName; tmpBuc->file = file; tmpBuc->next = ht[index]; ht[index] = tmpBuc; return OK;}//------------------------------------------------------------------- // returns OK if file is already open. Else returns HASHNOTFOUND// if the file is open it also returns a pointer to the associated file object// via the file//-------------------------------------------------------------------Status OpenFileHashTbl::find(const string fileName, File*& file){ int index = hash(fileName); fileHashBucket* tmpBuc = ht[index]; while (tmpBuc) { if (tmpBuc->fname == fileName) { file = tmpBuc->file; return OK; } tmpBuc = tmpBuc->next; } return HASHNOTFOUND;}//-------------------------------------------------------------------// remove fileName from list of open files// returns OK if file was removed.// Else return HASHTBLERROR//-------------------------------------------------------------------Status OpenFileHashTbl::erase(const string fileName){ int index = hash(fileName); fileHashBucket* tmpBuc = ht[index]; fileHashBucket* prevBuc = ht[index]; while (tmpBuc) { if (tmpBuc->fname == fileName) { if (tmpBuc == ht[index]) ht[index] = tmpBuc->next; else prevBuc->next = tmpBuc->next; tmpBuc->file = NULL; delete tmpBuc; return OK; } else { prevBuc = tmpBuc; tmpBuc = tmpBuc->next; } } return HASHTBLERROR;}// Construct a File object which can operate on Unix files.File::File(const string & fname){ fileName = fname; openCnt = 0; unixFile = -1;}// Deallocate a file objectFile::~File(){ if (openCnt == 0) return; // This means that file must be closed down if open // and buffer pages flushed. // To ensure that all this happens, must push down the openCnt to 1. openCnt = 1; Status status = close(); if (status != OK) { Error error; error.print(status); }}Status const File::create(const string & fileName){ int file; if ((file = ::open(fileName.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0) { if (errno == EEXIST) return FILEEXISTS; else return UNIXERR; } // An empty file contains just a DB header page. Page header; memset(&header, 0, sizeof header); DBP(header).nextFree = -1; DBP(header).firstPage = -1; DBP(header).numPages = 1; if (write(file, (char*)&header, sizeof header) != sizeof header) return UNIXERR; if (::close(file) < 0) return UNIXERR; return OK;}const Status File::destroy(const string & fileName){ if (remove(fileName.c_str()) < 0) { cout << "db.destroy. unlink returned error" << "\n"; return UNIXERR; } return OK;}const Status File::open(){ // Open file -- it will be closed in closeFile(). if (openCnt == 0) { if ((unixFile = ::open(fileName.c_str(), O_RDWR)) < 0) return UNIXERR; // Store file info in open files table. openCnt = 1; } else openCnt++; return OK;}const Status File::close(){ if (openCnt <= 0) return FILENOTOPEN; openCnt--; // File actually closed only when open count goes to zero. if (openCnt == 0) { if (bufMgr) bufMgr->flushFile(this); if (::close(unixFile) < 0) return UNIXERR; } return OK;}// Allocate a page either from a free list (list of pages which// were previously disposed of), or extend file if no free pages// are available.Status File::allocatePage(int& pageNo){ Page header; Status status; if ((status = intread(0, &header)) != OK) return status; // If free list has pages on it, take one from there // and adjust free list accordingly. if (DBP(header).nextFree != -1) { // free list exists? // Return first page on free list to the caller, // adjust free list accordingly. pageNo = DBP(header).nextFree; Page firstFree; if ((status = intread(pageNo, &firstFree)) != OK) return status; DBP(header).nextFree = DBP(firstFree).nextFree; } else { // no free list, have to extend file // Extend file -- the current number of pages will be // the page number of the page to be returned. pageNo = DBP(header).numPages; Page newPage; memset(&newPage, 0, sizeof newPage); if ((status = intwrite(pageNo, &newPage)) != OK) return status; DBP(header).numPages++; if (DBP(header).firstPage == -1) // first user page in file? DBP(header).firstPage = pageNo; } if ((status = intwrite(0, &header)) != OK) return status; #ifdef DEBUGFREE listFree();#endif return OK;}// Deallocate a page from file. The page will be put on a free// list and returned back to the caller upon a subsequent// allocPage() call.const Status File::disposePage(const int pageNo){ if (pageNo < 1) return BADPAGENO; Page header; Status status; if ((status = intread(0, &header)) != OK) return status; // The first user-allocated page in the file cannot be // disposed of. The File layer has no knowledge of what // is the next page in the file and hence would not be // able to adjust the firstPage field in file header. if (DBP(header).firstPage == pageNo || pageNo >= DBP(header).numPages) return BADPAGENO; // Deallocate page by attaching it to the free list. Page away; if ((status = intread(pageNo, &away)) != OK) return status; memset(&away, 0, sizeof away); DBP(away).nextFree = DBP(header).nextFree; DBP(header).nextFree = pageNo; if ((status = intwrite(pageNo, &away)) != OK) return status; if ((status = intwrite(0, &header)) != OK) return status;#ifdef DEBUGFREE listFree();#endif return OK;}// Read a page from file and store page contents at the page address// provided by the caller.const Status File::intread(int pageNo, Page* pagePtr) const{ if (lseek(unixFile, pageNo * sizeof(Page), SEEK_SET) == -1) return UNIXERR; int nbytes = read(unixFile, (char*)pagePtr, sizeof(Page));#ifdef DEBUGIO cerr << "%% File " << (int)this << ": read bytes "; cerr << pageNo * sizeof(Page) << ":+" << nbytes << endl; cerr << "%% "; for(int i = 0; i < 10; i++) cerr << *((int*)pagePtr + i) << " "; cerr << endl;#endif if (nbytes != sizeof(Page)) return UNIXERR; return OK;}// Write a page to file. Page data is at the page address// provided by the caller.const Status File::intwrite(const int pageNo, const Page* pagePtr){ if (lseek(unixFile, pageNo * sizeof(Page), SEEK_SET) == -1) return UNIXERR; int nbytes = write(unixFile, (char*)pagePtr, sizeof(Page));#ifdef DEBUGIO cerr << "%% File " << (int)this << ": wrote bytes "; cerr << pageNo * sizeof(Page) << ":+" << nbytes << endl; cerr << "%% "; for(int i = 0; i < 10; i++) cerr << *((int*)pagePtr + i) << " "; cerr << endl;#endif if (nbytes != sizeof(Page)) return UNIXERR; return OK;}// Read a page from file, check parameters for validity.const Status File::readPage(const int pageNo, Page* pagePtr) const{ if (!pagePtr) return BADPAGEPTR; if (pageNo < 1) return BADPAGENO; return intread(pageNo, pagePtr);}// Write a page to file, check parameters for validity.const Status File::writePage(const int pageNo, const Page *pagePtr){ if (!pagePtr) return BADPAGEPTR; if (pageNo < 1) return BADPAGENO; return intwrite(pageNo, pagePtr);}// Return the number of the first page in file. It is stored// on the file's header page (field firstPage).const Status File::getFirstPage(int& pageNo) const{ Page header; Status status; if ((status = intread(0, &header)) != OK) return status; pageNo = DBP(header).firstPage; return OK;}#ifdef DEBUGFREE// Print out the page numbers on the free list. For debugging only.void File::listFree(){ cerr << "%% File " << (int)this << " free pages:"; int pageNo = 0; for(int i = 0; i < 10; i++) { Page page; if (intread(pageNo, &page) != OK) break; pageNo = DBP(page).nextFree; cerr << " " << pageNo; if (pageNo == -1) break; } cerr << endl;}#endif// Construct a DB object which keeps track of creating, opening, and// closing files.DB::DB(){ // Check that DB header page data fits on a regular data page. if (sizeof(DBPage) >= sizeof(Page)) { cerr << "sizeof(DBPage) cannot exceed sizeof(Page): " << sizeof(DBPage) << " " << sizeof(Page) << endl; exit(1); }}// Destroy DB object. DB::~DB(){ // this could leave some open files open. // need to fix this by iterating through the hash table deleting each open file} // Create a database file.const Status DB::createFile(const string &fileName) { File* file; if (fileName.empty()) return BADFILE; // First check if the file has already been opened if (openFiles.find(fileName, file) == OK) return FILEEXISTS; // Do the actual work return File::create(fileName);}// Delete a database file.const Status DB::destroyFile(const string & fileName) { File* file; if (fileName.empty()) return BADFILE; // Make sure file is not open currently. if (openFiles.find(fileName, file) == OK) return FILEOPEN; // Do the actual work return File::destroy(fileName);}// Open a database file. If file already open, increment open count,// otherwise find a vacant slot in the open files table and store// file info there.const Status DB::openFile(const string & fileName, File*& filePtr){ Status status; File* file; if (fileName.empty()) return BADFILE; // Check if file already open. if (openFiles.find(fileName, file) == OK) { // file is already open, call open again on the file object // to increment it's open count. status = file->open(); filePtr = file; } else { // file is not already open // Otherwise create a new file object and open it filePtr = new File(fileName); status = filePtr->open(); if (status != OK) { delete filePtr; return status; } // Insert into the mapping table status = openFiles.insert(fileName, filePtr); } return status;}// Close a database file. Get file info from open files table,// call Unix close() only if open count now goes to zero.const Status DB::closeFile(File* file){ if (!file) return BADFILEPTR; // Close the file file->close(); // If there are no remaining references to the file, then we should delete // the file object and remove it from the openFilesMap if (file->openCnt == 0) { if (openFiles.erase(file->fileName) != OK) return BADFILEPTR; delete file; } return OK;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -