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 DbRetVal
Database::writeDirtyPages(char *dataFile
)
28 int fd
= os::open(dataFile
, fileOpenCreat
, 0);
29 os::lseek(fd
, 0, SEEK_SET
);
30 void *buf
= (void *) metaData_
;
31 int sizeToWrite
= os::alignLong(sizeof(DatabaseMetaData
));
32 size_t retSize
= os::write(fd
, (char*)buf
, sizeToWrite
);
35 printError(ErrWarning
, "Warning:Unable to write metadata");
36 return ErrSysInternal
;
38 PageInfo
*pageInfo
= (PageInfo
*) getFirstPage();
39 long pageSize
=PAGE_SIZE
;
40 int pagesWritten
=0, writeOffset
=0;
41 long long totalBytesWritten
=0;
42 while(isValidAddress((char*) pageInfo
))
44 if ( NULL
== pageInfo
) break;
45 if (pageInfo
> getCurrentPage()) {
47 os::lseek(fd
, getMaxSize() -1, SEEK_SET
);
48 if ( -1 == os::write(fd
, a
, 1)) {
49 printError(ErrSysInternal
, "Unable to extend chkpt file");
51 return ErrSysInternal
;
55 if (BITSET(pageInfo
->flags
, IS_DIRTY
)) {
56 if (NULL
== pageInfo
->nextPageAfterMerge_
)
59 pageSize
= (long)pageInfo
->nextPageAfterMerge_
- (long)pageInfo
;
60 writeOffset
= (long) pageInfo
- (long) metaData_
;
61 ::lseek(fd
, writeOffset
, SEEK_SET
);
62 CLEARBIT(pageInfo
->flags
, IS_DIRTY
);
63 retSize
= os::write(fd
, (char*)pageInfo
, pageSize
);
64 if ( -1 == retSize
) {
65 printError(ErrSysInternal
, "Unable to write dirty page %x", pageInfo
);
67 return ErrSysInternal
;
69 totalBytesWritten
= totalBytesWritten
+ retSize
;
72 if ( pageInfo
->nextPageAfterMerge_
== NULL
) {
73 pageInfo
= (PageInfo
*)((char*)pageInfo
+ PAGE_SIZE
);
75 pageInfo
= (PageInfo
*)pageInfo
->nextPageAfterMerge_
;
78 //printf("Total Dirty pages written %d %lld\n", pagesWritten, totalBytesWritten);
79 logFine(Conf::logger
, "Total Dirty pages written %d\n", pagesWritten
);
84 DbRetVal
Database::checkPoint()
86 char dataFile
[MAX_FILE_LEN
];
87 char cmd
[MAX_FILE_LEN
];
88 char dbRedoFileName
[MAX_FILE_LEN
];
89 sprintf(dbRedoFileName
, "%s/csql.db.cur", Conf::config
.getDbFile());
90 if (!Conf::config
.useMmap()) {
91 // sprintf(dataFile, "%s/db.chkpt.data1", Conf::config.getDbFile());
92 sprintf(dataFile
, "%s/db.chkpt.data", Conf::config
.getDbFile());
94 if (fp
= fopen(dataFile
, "r")) {
96 int ret
= ::unlink(dataFile
);
98 printError(ErrOS
, "Unable to delete old chkpt file. Failure");
102 int fd
= ::open(dataFile
, O_WRONLY
|O_CREAT
, 0644);
103 void *buf
= (void *) metaData_
;
104 os::lseek(fd
, 0, SEEK_SET
);
105 os::write(fd
, (char*) buf
, Conf::config
.getMaxDbSize());
107 sprintf(cmd
, "cp -f %s/db.chkpt.data %s/db.chkpt.data1", Conf::config
.getDbFile(), Conf::config
.getDbFile());
108 int ret
= system(cmd
);
110 printError(ErrOS
, "Unable to take checkpoint back up file");
114 file_desc fd
= getChkptfd();
115 if (!os::fdatasync(fd
)) {
116 logFine(Conf::logger
, "fsync succedded");
118 filterAndRemoveStmtLogs();
119 int ret
= os::truncate(dbRedoFileName
);
122 printError(ErrSysInternal
, "Unable to truncate redo log file");
123 printError(ErrSysInternal
, "Delete %s manually and restart the server", dbRedoFileName
);
126 //switch the checkpoint so that during recovery, fsynced checkpoint is
127 //used during recovery if the below step(writeDirtyPages)
128 //is not completed succesfully.
129 if (Database::getCheckpointID() == 0)
130 Database::setCheckpointID(1);
132 Database::setCheckpointID(0);
134 int val
=Database::getCheckpointID();
136 sprintf(dataFile
, "%s/db.chkpt.data%d", Conf::config
.getDbFile(), val
);
137 DbRetVal rv
= writeDirtyPages(dataFile
);
140 printError(ErrSysInternal
, "Unable to write dirty pages");
145 //Note: do not change order, chkpt id should be switched only after
146 //all dirty pages are written to disk. otherwise(if server crashes
147 //when it writes these dirty pages) recovery should use
148 //mapped file as fsync is already done on that file.
149 if (Database::getCheckpointID() == 0)
150 Database::setCheckpointID(1);
152 Database::setCheckpointID(0);
157 filterAndRemoveStmtLogs();
158 int ret
= os::truncate(dbRedoFileName
);
160 printError(ErrSysInternal
, "Unable to truncate redo log file. Delete and restart the server\n");
166 DbRetVal
Database::filterAndRemoveStmtLogs()
169 char fName
[MAX_FILE_LEN
];
170 sprintf(fName
, "%s/csql.db.stmt", Conf::config
.getDbFile());
171 file_desc fdRead
= os::openFile(fName
, fileOpenReadOnly
,0);
172 if ((file_desc
)-1 == fdRead
) { return OK
; }
173 if (::stat(fName
, &st
) == -1) {
174 printError(ErrSysInternal
, "Unable to retrieve stmt log file size");
175 os::closeFile(fdRead
);
176 return ErrSysInternal
;
178 if (st
.st_size
==0) {
179 os::closeFile(fdRead
);
182 void *startAddr
= os::mmap(NULL
, st
.st_size
, mapProtRead
, mapPrivate
, fdRead
, 0);
183 if ((void*) MAP_FAILED
== startAddr
) {
184 printError(ErrSysInternal
, "Unable to mmap stmt log file\n");
185 return ErrSysInternal
;
187 sprintf(fName
, "%s/csql.db.stmt1", Conf::config
.getDbFile());
188 int fd
= os::openFileForAppend(fName
, O_CREAT
|O_TRUNC
);
189 char *iter
= (char*)startAddr
;
190 char *logStart
= NULL
, *logEnd
= NULL
;
197 stmtMap
.setKeySize(sizeof(int));
198 //PASS-I load all prepare stmts and free them
200 if (iter
- (char*)startAddr
>= st
.st_size
) break;
201 logType
= *(int*)iter
;
203 if (logType
== -1) { //prepare
204 iter
= iter
+ sizeof(int);
206 iter
= iter
+ 2 * sizeof(int);
207 stmtID
= *(int*)iter
;
208 stmtMap
.insert(iter
);
209 iter
= logStart
+ len
;
212 else if(logType
== -3) { //free
213 iter
= iter
+ sizeof(int);
214 txnID
= *(int*) iter
; iter
+= sizeof(int);
215 loglen
= *(int*) iter
; iter
+= sizeof(int);
216 stmtID
= *(int*)iter
;
217 stmtMap
.remove(iter
);
218 iter
= iter
+ sizeof(int);
220 printError(ErrSysInternal
, "Stmt Redo log file corrupted: logType:%d", logType
);
225 //PASS-II take the prepared statements which are not freed into another backup file
227 if (iter
- (char*)startAddr
>= st
.st_size
) break;
228 logType
= *(int*)iter
;
230 if (logType
== -1) { //prepare
231 iter
= iter
+ sizeof(int);
233 iter
= iter
+ 2 * sizeof(int);
234 stmtID
= *(int*)iter
;
235 iter
= logStart
+ len
;
237 if (stmtMap
.find(&stmtID
))
238 ret
= os::write(fd
, logStart
, len
);
240 printError(ErrSysInternal
, "Unable to write statement logs");
243 else if(logType
== -3) { //free
244 iter
= logStart
+ 4 *sizeof(int);
245 //neglet free stmt logs in this pass
247 printError(ErrSysInternal
, "Stmt Redo log file corrupted: logType:%d", logType
);
254 os::munmap((char*)startAddr
, st
.st_size
);
255 os::closeFile(fdRead
);
257 char cmd
[MAX_FILE_LEN
*2];
258 sprintf(cmd
, "mv %s/csql.db.stmt1 %s/csql.db.stmt",
259 Conf::config
.getDbFile(), Conf::config
.getDbFile());
264 int Database::getCheckpointID()
267 char curCkptFile
[MAX_FILE_LEN
];
268 sprintf(curCkptFile
, "%s/db.chkpt.cur", Conf::config
.getDbFile());
269 FILE *fp
= fopen(curCkptFile
, "r");
270 if (NULL
== fp
) { setCheckpointID(0); return 0; }
271 fscanf(fp
, "%d", &id
);
276 void Database::setCheckpointID(int id
)
278 char curCkptFile
[MAX_FILE_LEN
];
279 sprintf(curCkptFile
, "%s/db.chkpt.cur", Conf::config
.getDbFile());
280 FILE *fp
= fopen(curCkptFile
, "w");
283 printError(ErrSysInternal
, "Unable to set checkpointID");
286 fprintf(fp
, "%d", id
);
287 logFine(Conf::logger
, "Current checkpoint set to %d", id
);
292 //used only by the user database not the system database
293 DbRetVal
Database::recoverUserDB()
295 char dataFile
[MAX_FILE_LEN
];
296 char cmd
[MAX_FILE_LEN
];
297 sprintf(dataFile
, "%s/db.chkpt.data", Conf::config
.getDbFile());
298 int fd
= os::open(dataFile
, fileOpenReadOnly
, 0);
299 if (-1 == fd
) { return OK
; }
300 void *buf
= (void *) metaData_
;
301 int readbytes
= read(fd
, buf
, Conf::config
.getMaxDbSize());
302 if (readbytes
== -1) { os::close(fd
); return ErrOS
; }
307 //used only by the system database
308 DbRetVal
Database::recoverSystemDB()
310 char mapFile
[MAX_FILE_LEN
];
311 sprintf(mapFile
, "%s/db.chkpt.map", Conf::config
.getDbFile());
312 int fd
= open(mapFile
, O_RDONLY
);
313 if (-1 == fd
) { return OK
; }
314 CatalogTableTABLE
cTable(this);
315 CatalogTableINDEX
cIndex(this);
317 while (read(fd
, &buf
, sizeof(buf
))) {
318 if (buf
.type
== Tbl
) {
319 cTable
.setChunkPtr(buf
.name
, buf
.firstPage
, buf
.curPage
);
321 else if (buf
.type
== hIdx
|| buf
.type
== tIdx
) {
322 cIndex
.setChunkPtr(buf
.name
, buf
.type
, buf
.bucketChunk
, buf
.firstPage
, buf
.curPage
);