1 /***************************************************************************
2 * Copyright (C) 2007 by www.databasecache.com *
3 * Contact: praba_tuty@databasecache.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 ***************************************************************************/
18 #include<CatalogTables.h>
19 #include<Transaction.h>
24 #include<HeapAllocator.h>
26 const char* Database::getName()
28 return metaData_
->dbName_
;
31 int Database::getDatabaseID()
33 return metaData_
->dbID_
;
36 long Database::getMaxSize()
38 return metaData_
->maxSize_
;
41 long Database::getCurrentSize()
43 return metaData_
->curSize_
;
47 Page
* Database::getCurrentPage()
49 return metaData_
->curPage_
;
53 Page
* Database::getFirstPage()
55 return metaData_
->firstPage_
;
59 int Database::getNoOfChunks()
61 return metaData_
->noOfChunks_
;
64 Chunk
* Database::getHashIndexChunk()
66 return metaData_
->hashIndexChunk_
;
69 void Database::setDatabaseID(int id
)
71 metaData_
->dbID_
= id
;
74 void Database::setName(const char *name
)
76 strcpy(metaData_
->dbName_
, name
);
79 void Database::setCurrentSize(long size
)
81 metaData_
->curSize_
= size
;
84 void Database::setCurrentPage(Page
*page
)
86 //metaData_->curPage_ = page;
87 Mutex::CASL((long*)&metaData_
->curPage_
, (long)metaData_
->curPage_
, (long)page
);
90 void Database::setFirstPage(Page
*page
)
92 metaData_
->firstPage_
= page
;
95 void Database::setMaxSize(long size
)
97 metaData_
->maxSize_
= size
;
100 void Database::setNoOfChunks(int chunks
)
102 metaData_
->noOfChunks_
= chunks
;
105 void Database::setHashIndexChunk(Chunk
*ch
)
107 metaData_
->hashIndexChunk_
= ch
;
110 void Database::printDebugMutexInfo()
112 metaData_
->dbAllocMutex_
.print();
113 metaData_
->ckptMutex_
.print();
114 metaData_
->dbTransTableMutex_
.print();
115 metaData_
->dbProcTableMutex_
.print();
116 metaData_
->dbPrepareStmtMutex_
.print();
117 metaData_
->chunkUniqueID_
.print();
120 int Database::initAllocDatabaseMutex()
122 return metaData_
->dbAllocMutex_
.init("allocdb");
125 DbRetVal
Database::getAllocDatabaseMutex(bool procAccount
)
127 struct timeval timeout
, timeval
;
128 timeout
.tv_sec
= Conf::config
.getMutexSecs();
129 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
131 int totalTries
= Conf::config
.getMutexRetries() *2;
133 while (tries
< totalTries
)
135 ret
= metaData_
->dbAllocMutex_
.getLock(procSlot
, procAccount
);
137 timeval
.tv_sec
= timeout
.tv_sec
;
138 timeval
.tv_usec
= timeout
.tv_usec
;
139 os::select(0, 0, 0, 0, &timeval
);
142 if (tries
>= totalTries
) return ErrLockTimeOut
;
146 DbRetVal
Database::releaseAllocDatabaseMutex(bool procAccount
)
148 metaData_
->dbAllocMutex_
.releaseLock(procSlot
, procAccount
);
152 int Database::initPrepareStmtMutex()
154 return metaData_
->dbPrepareStmtMutex_
.init("prepstmt");
157 DbRetVal
Database::getPrepareStmtMutex(bool procAccount
)
159 struct timeval timeout
, timeval
;
160 timeout
.tv_sec
= Conf::config
.getMutexSecs();
161 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
163 int totalTries
= Conf::config
.getMutexRetries() *2;
165 while (tries
< totalTries
)
167 ret
= metaData_
->dbPrepareStmtMutex_
.getLock(procSlot
, procAccount
);
169 timeval
.tv_sec
= timeout
.tv_sec
;
170 timeval
.tv_usec
= timeout
.tv_usec
;
171 os::select(0, 0, 0, 0, &timeval
);
174 if (tries
>= totalTries
) return ErrLockTimeOut
;
179 DbRetVal
Database::releasePrepareStmtMutex(bool procAccount
)
181 metaData_
->dbPrepareStmtMutex_
.releaseLock(procSlot
, procAccount
);
185 int Database::initTransTableMutex()
187 return metaData_
->dbTransTableMutex_
.init("transtable");
190 DbRetVal
Database::getTransTableMutex()
192 struct timeval timeout
, timeval
;
193 timeout
.tv_sec
= Conf::config
.getMutexSecs();
194 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
196 int totalTries
= Conf::config
.getMutexRetries() *2;
198 while (tries
< totalTries
)
200 ret
= metaData_
->dbTransTableMutex_
.getLock(procSlot
);
202 timeval
.tv_sec
= timeout
.tv_sec
;
203 timeval
.tv_usec
= timeout
.tv_usec
;
204 os::select(0, 0, 0, 0, &timeval
);
207 if (tries
>= totalTries
) return ErrLockTimeOut
;
212 DbRetVal
Database::releaseTransTableMutex()
214 metaData_
->dbTransTableMutex_
.releaseLock(procSlot
);
220 int Database::initProcessTableMutex()
222 return metaData_
->dbProcTableMutex_
.init("proctable");
225 DbRetVal
Database::getProcessTableMutex(bool procAccount
)
227 struct timeval timeout
, timeval
;
228 timeout
.tv_sec
= Conf::config
.getMutexSecs();
229 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
231 int totalTries
= Conf::config
.getMutexRetries() *2;
233 while (tries
< totalTries
)
235 ret
= metaData_
->dbProcTableMutex_
.getLock(procSlot
, procAccount
);
237 timeval
.tv_sec
= timeout
.tv_sec
;
238 timeval
.tv_usec
= timeout
.tv_usec
;
239 os::select(0, 0, 0, 0, &timeval
);
242 if (tries
>= totalTries
) return ErrLockTimeOut
;
247 DbRetVal
Database::releaseProcessTableMutex(bool procAccount
)
249 metaData_
->dbProcTableMutex_
.releaseLock(procSlot
, procAccount
);
253 int Database::initCheckpointMutex()
255 return metaData_
->ckptMutex_
.init("checkpoint");
258 DbRetVal
Database::getSCheckpointMutex(bool procAccount
)
260 struct timeval timeout
, timeval
;
261 timeout
.tv_sec
= Conf::config
.getMutexSecs();
262 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
264 int totalTries
= Conf::config
.getMutexRetries() *2;
266 while (tries
< totalTries
)
268 ret
= metaData_
->ckptMutex_
.getShareLock(procSlot
, procAccount
);
270 timeval
.tv_sec
= timeout
.tv_sec
;
271 timeval
.tv_usec
= timeout
.tv_usec
;
272 os::select(0, 0, 0, 0, &timeval
);
275 if (tries
>= totalTries
) return ErrLockTimeOut
;
280 DbRetVal
Database::getXCheckpointMutex(bool procAccount
)
282 struct timeval timeout
, timeval
;
283 timeout
.tv_sec
= Conf::config
.getMutexSecs();
284 timeout
.tv_usec
= Conf::config
.getMutexUSecs();
286 int totalTries
= Conf::config
.getMutexRetries() *2;
288 while (tries
< totalTries
)
290 ret
= metaData_
->ckptMutex_
.getExclusiveLock(procSlot
, procAccount
);
292 timeval
.tv_sec
= timeout
.tv_sec
;
293 timeval
.tv_usec
= timeout
.tv_usec
;
294 os::select(0, 0, 0, 0, &timeval
);
297 if (tries
>= totalTries
) return ErrLockTimeOut
;
302 DbRetVal
Database::releaseCheckpointMutex(bool procAccount
)
304 metaData_
->ckptMutex_
.releaseShareLock(procSlot
, procAccount
);
308 // Gets the free page
309 // Each page is segmented by PAGE_SIZE, so it checks the pageInfo
310 // of each page to determine if the page is free
311 // Algorithm is to scan through the pageInfo objects stored at
312 // address (db start address + i * PAGE_SIZE) where i = 1..n till end
314 // But in case of large tuples, pages are merged, so there wont be
315 // PageInfo object on pages which are merged.
316 // These pages are skipped by checking the nextPageAfterMerge_ of PageInfo
318 //NOTE::IMPORTANT::assumes alloc database lock is taken before calling this
319 Page
* Database::getFreePage()
321 Page
* page
= getFirstPage();
322 //Page* page = getCurrentPage();
323 //printDebug(DM_Alloc, "Database::getFreePage firstPage:%x",page);
324 printDebug(DM_Alloc
, "Database::getFreePage currentpage:%x",page
);
325 PageInfo
* pageInfo
= ((PageInfo
*)page
);
326 char* endAddr
= ((char*)getMetaDataPtr()) + getMaxSize();
327 int pageSize
= PAGE_SIZE
;
328 bool isEndAddchk
=false;
329 while( 1 == pageInfo
->isUsed_
)
331 //If any pages are merged to store data larger than PAGE_SIZE
332 //move to the next page after the merge and check whether it is used
333 if ( pageInfo
->nextPageAfterMerge_
== NULL
) {
334 pageInfo
= (PageInfo
*)((char*)pageInfo
+ pageSize
);
335 printDebug(DM_Alloc
,"Normal Page:Moving to page:%x",pageInfo
);
338 pageInfo
= (PageInfo
*)pageInfo
->nextPageAfterMerge_
;
339 printDebug(DM_Alloc
,"Merged Page:Moving to page:%x",pageInfo
);
342 if((((char*) pageInfo
) + pageSize
) >= endAddr
)
346 pageInfo
=(PageInfo
*)getFirstPage();
351 if ((char*)pageInfo
>= endAddr
)
353 //printError(ErrSysInternal,"Invalid address %x",pageInfo);
358 if (!isValidAddress(((char*) pageInfo
) + pageSize
))
360 printError(ErrSysInternal
, "Invalid address %x",((char*) pageInfo
) + pageSize
);
363 setCurrentPage((Page
*) pageInfo
);
364 printDebug(DM_Alloc
,"Database::getFreePage returning page:%x",pageInfo
);
365 return (Page
*) pageInfo
;
368 //Used by tuples more than PAGE_SIZE
369 //NOTE::IMPORTANT::assumes alloc database lock is taken before calling this
370 Page
* Database::getFreePage(size_t size
)
372 Page
* page
= getFirstPage();
373 PageInfo
* pageInfo
= ((PageInfo
*)page
);
374 int multiple
= size
/ PAGE_SIZE
;
375 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
376 printDebug(DM_Alloc
, "Database::getFreePage firstPage:%x size:%ld",page
, size
);
377 char* endAddr
= ((char*)getMetaDataPtr()) + getMaxSize();
378 int pageSize
= PAGE_SIZE
;
379 bool isEndAddchk
= false;
381 while( 1 == pageInfo
->isUsed_
)
383 //If any pages are merged to store data larger than PAGE_SIZE
384 //move to the next page after the merge and check whether it is used
385 if ( pageInfo
->nextPageAfterMerge_
== NULL
) {
386 pageInfo
= (PageInfo
*)((char*)pageInfo
+ pageSize
);
387 printDebug(DM_Alloc
,"Normal Page:Moving to page:%x",pageInfo
);
390 pageInfo
= (PageInfo
*)pageInfo
->nextPageAfterMerge_
;
391 printDebug(DM_Alloc
,"Merged Page:Moving to page:%x",pageInfo
);
393 if((((char*) pageInfo
) + offset
) >= endAddr
)
397 pageInfo
=(PageInfo
*)getFirstPage();
404 PageInfo
*pInfo
= pageInfo
;
405 if ((((char*)pInfo
) + offset
) >= endAddr
)
407 printError(ErrSysInternal
,"Invalid address %x",((char*)pInfo
) + offset
);
410 for (i
= 0; i
< multiple
+ 1; i
++)
412 if (1 == pInfo
->isUsed_
) break;
413 pInfo
= (PageInfo
*)((char*)pInfo
+ pageSize
);
415 if ( i
== (multiple
+ 1)) break;
416 pageInfo
= (PageInfo
*)((char*)pInfo
+ pageSize
);
419 printDebug(DM_Alloc
,"Database::getFreePage returning page:%x",pageInfo
);
420 setCurrentPage((Page
*) pageInfo
);
421 return (Page
*) pageInfo
;
424 void Database::printStatistics()
426 Page
* page
= getFirstPage();
427 PageInfo
* pageInfo
= ((PageInfo
*)page
);
428 int usedPageCount
=0, usedMergedPageCount
=0, totalPages
=0;
429 int totalDirtyPages
=0;
430 printf("<DatabaseStatistics>\n");
431 printf(" <Database Name> %s </Database Name>\n", getName());
432 printf(" <Max Size> %ld </Max Size>\n", getMaxSize());
433 printf(" <First Page> %x </First Page>\n", getFirstPage());
434 while(isValidAddress((char*) pageInfo
))
436 if (pageInfo
== NULL
) break;
437 //if (pageInfo > getCurrentPage()) break;
438 if (1 == pageInfo
->isUsed_
) {
439 if ( pageInfo
->nextPageAfterMerge_
== NULL
) {
440 if (BITSET(pageInfo
->flags
, IS_DIRTY
)) totalDirtyPages
++;
441 pageInfo
= (PageInfo
*)((char*)pageInfo
+ PAGE_SIZE
);
442 usedPageCount
++; totalPages
++;
443 printDebug(DM_Alloc
, "Normal Page:Moving to page:%x\n",pageInfo
);
447 if (BITSET(pageInfo
->flags
, IS_DIRTY
)) totalDirtyPages
++;
448 pageInfo
= (PageInfo
*)pageInfo
->nextPageAfterMerge_
;
449 usedMergedPageCount
++; totalPages
++;
450 printDebug(DM_Alloc
,"Merged Page:Moving to page:%x\n",pageInfo
);
453 } else if (BITSET(pageInfo
->flags
, IS_DIRTY
)) totalDirtyPages
++;
454 pageInfo
= (PageInfo
*)((char*)pageInfo
+ PAGE_SIZE
);
455 printDebug(DM_Alloc
,"Normal Page not used:Moving to page:%x\n",pageInfo
);
458 printf(" <Total Pages> %d </Total Pages>\n", totalPages
);
459 if (Conf::config
.useDurability())
460 printf(" <Dirty Pages> %d </Dirty Pages>\n", totalDirtyPages
);
461 printf(" <Used Normal Pages> %d </Used Normal Pages>\n", usedPageCount
);
462 printf(" <Used Merged Pages> %d </Used Merged Pages>\n", usedMergedPageCount
);
463 printf(" <Chunks Used> %d </Chunks Used>\n", getNoOfChunks());
464 printf("</DatabaseStatistics>\n");
470 //called only in case of system database to create and initialize the chunk
472 DbRetVal
Database::createSystemDatabaseChunk(AllocType type
, size_t size
, int id
)
478 printError(ErrSysFatal
, "Database ID corrupted");
481 chunk
= getSystemDatabaseChunk(id
);
483 chunk
->setChunkNameForSystemDB(id
);
485 if (FixedSizeAllocator
== type
) chunk
->setSize(size
);
486 //getDatabaseMutex();
487 if (chunk
->allocSize_
> PAGE_SIZE
)
488 chunk
->curPage_
= getFreePage(chunk
->allocSize_
);
490 chunk
->curPage_
= getFreePage();
491 if ( chunk
->curPage_
== NULL
)
493 //releaseDatabaseMutex();
494 printError(ErrNoMemory
, "No free pages in database: Database full");
498 chunk
->firstPage_
= chunk
->curPage_
;
499 PageInfo
* firstPageInfo
= ((PageInfo
*)chunk
->firstPage_
);
500 firstPageInfo
->setFirstPageAsUsed();
501 chunk
->setChunkID(id
);
502 chunk
->setAllocType(type
);
503 chunk
->initMutex(id
);
504 printDebug(DM_Database
, "Creating System Database Chunk:%d Size:%d",id
, chunk
->allocSize_
);
505 if (chunk
->allocSize_
> PAGE_SIZE
)
507 int multiple
= os::floor(chunk
->allocSize_
/ PAGE_SIZE
);
508 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
509 firstPageInfo
->nextPageAfterMerge_
= ((char*)firstPageInfo
)+ offset
;
514 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)firstPageInfo
) + sizeof(PageInfo
));
515 varInfo
->isUsed_
= 0;
516 varInfo
->size_
= PAGE_SIZE
- sizeof(PageInfo
) - sizeof(VarSizeInfo
);
520 //releaseDatabaseMutex();
524 //This is never called currently. If situation arises will be coded later.
525 DbRetVal
Database::deleteSystemDatabaseChunk(int id
)
528 Chunk
*chunk
= getSystemDatabaseChunk(id
);
529 chunk
->setChunkID(-1);
531 chunk
->setAllocType(UnknownAllocator
);
534 //walk though the pageList ptr and get all the page pointers
535 //then free all the pages used to store this by setting the
536 //start of page to notused
537 chunk
->firstPage_
= NULL
;
538 chunk
->curPage_
= NULL
;
544 void Database::createAllCatalogTables()
546 //These are special chunks which hold catalog tables and other information
548 // chunk id 0 ->userChunkTable
549 // chunk id 1 ->lockBucketHash
550 // chunk id 2 ->lockTable
552 // chunk id 10->DATABASE
554 // chunk id 12->TABLE
555 // chunk id 13->FIELD
556 // chunk id 14->ACCESS
558 createSystemTables();
559 createMetaDataTables();
562 void Database::createSystemTables()
564 createSystemDatabaseChunk(FixedSizeAllocator
,
565 sizeof(Chunk
), UserChunkTableId
);
566 createSystemDatabaseChunk(FixedSizeAllocator
,
567 sizeof(Bucket
) * LOCK_BUCKET_SIZE
,
568 LockTableHashBucketId
);
569 createSystemDatabaseChunk(FixedSizeAllocator
,
570 sizeof(Mutex
)* LOCK_BUCKET_SIZE
,
572 createSystemDatabaseChunk(FixedSizeAllocator
,
573 sizeof(LockHashNode
), LockTableId
);
574 createSystemDatabaseChunk(FixedSizeAllocator
,
575 sizeof(TransHasNode
), TransHasTableId
);
577 createSystemDatabaseChunk(VariableSizeAllocator
,
581 void Database::createMetaDataTables()
583 createSystemDatabaseChunk(FixedSizeAllocator
,
584 sizeof(CDATABASEFILE
), DatabaseTableId
);
585 createSystemDatabaseChunk(FixedSizeAllocator
,
586 sizeof(CUSER
), UserTableId
);
587 createSystemDatabaseChunk(FixedSizeAllocator
,
588 sizeof(CTABLE
), TableTableId
);
589 createSystemDatabaseChunk(FixedSizeAllocator
,
590 sizeof(CFIELD
), FieldTableId
);
591 createSystemDatabaseChunk(FixedSizeAllocator
,
592 sizeof(CACCESS
), AccessTableId
);
593 createSystemDatabaseChunk(FixedSizeAllocator
,
594 sizeof(CINDEX
), IndexTableId
);
595 createSystemDatabaseChunk(FixedSizeAllocator
,
596 sizeof(CINDEXFIELD
), IndexFieldTableId
);
597 createSystemDatabaseChunk(FixedSizeAllocator
,
598 sizeof(CFK
), ForeignKeyTableId
);
599 createSystemDatabaseChunk(FixedSizeAllocator
,
600 sizeof(CFKFIELD
), ForeignKeyFieldTableId
);
603 //used in case of system database
604 Chunk
* Database::getSystemDatabaseChunk(int id
)
606 size_t offset
= os::alignLong(sizeof (DatabaseMetaData
)) +
608 return (Chunk
*)(((char*) metaData_
) + offset
);
612 //used in case of system database
613 Transaction
* Database::getSystemDatabaseTrans(int slot
)
615 size_t offset
= os::alignLong(sizeof (DatabaseMetaData
)) +
616 os::alignLong(MAX_CHUNKS
* sizeof (Chunk
)) +
617 slot
* sizeof (Transaction
);
618 return (Transaction
*)(((char*) metaData_
) + offset
);
621 bool Database::isValidAddress(void* addr
)
623 if ((char*) addr
>= ((char*)getMetaDataPtr()) + getMaxSize())
629 //should be called only on system database
630 void* Database::allocLockHashBuckets()
632 Chunk
*chunk
= getSystemDatabaseChunk(LockTableHashBucketId
);
634 void *ptr
= chunk
->allocate(this, &rv
);
637 printError(ErrNoMemory
, "Chunk Allocation failed for lock hash bucket catalog table");
642 Bucket
* Database::getLockHashBuckets()
644 Chunk
*tChunk
= getSystemDatabaseChunk(LockTableHashBucketId
);
645 ChunkIterator iter
= tChunk
->getIterator();
646 return (Bucket
*)iter
.nextElement();
649 void Database::setUniqueChunkID(int id
)
651 (metaData_
->chunkUniqueID_
).setID(id
);
654 int Database::getUniqueIDForChunk()
656 return ((metaData_
->chunkUniqueID_
).getID());
659 DbRetVal
Database::recoverMutex(Mutex
*mut
)
661 //TODO: operations need to be undone before recovering the mutex.