1 /**************************************************************************
2 * Copyright (C) 2007 by Prabakaran Thirumalai *
3 * praba_tuty@yahoo.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 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
23 void Recovery::addToHashTable(int stmtID
, AbsSqlStatement
* sHdl
,
26 int bucketNo
= stmtID
% STMT_BUCKET_SIZE
;
27 printDebug(DM_Recovery
, "Inside addToHashTable %x", stmtBuckets
);
28 StmtBucket
*buck
= (StmtBucket
*) stmtBuckets
;
29 StmtBucket
*stmtBucket
= &buck
[bucketNo
];
30 StmtNode
*node
= new StmtNode();
31 node
->stmtId
= stmtID
;
33 strcpy(node
->stmtstr
, stmtstr
);
34 stmtBucket
->bucketList
.append(node
);
38 void Recovery::removeFromHashTable(int stmtID
)
40 int bucketNo
= stmtID
% STMT_BUCKET_SIZE
;
41 StmtBucket
*buck
= (StmtBucket
*) stmtBuckets
;
42 StmtBucket
*stmtBucket
= &buck
[bucketNo
];
43 StmtNode
*node
= NULL
, *delNode
= NULL
;
44 ListIterator it
= stmtBucket
->bucketList
.getIterator();
45 while(it
.hasElement()) {
46 node
= (StmtNode
*) it
.nextElement();
47 if(stmtID
== node
->stmtId
) { delNode
= node
; break; }
50 if (delNode
!= NULL
) {
51 stmtBucket
->bucketList
.remove(delNode
);
57 AbsSqlStatement
* Recovery::getStmtFromHashTable(int stmtId
)
59 int bucketNo
= stmtId
% STMT_BUCKET_SIZE
;
60 StmtBucket
*buck
= (StmtBucket
*) stmtBuckets
;
61 StmtBucket
*stmtBucket
= &buck
[bucketNo
];
62 if (stmtBucket
== NULL
) return NULL
;
63 StmtNode
*node
= NULL
;
64 ListIterator it
= stmtBucket
->bucketList
.getIterator();
65 while(it
.hasElement()) {
66 node
= (StmtNode
*) it
.nextElement();
67 if(stmtId
== node
->stmtId
) {
68 SqlStatement
*sqlStmt
= (SqlStatement
*)node
->stmt
;
69 if (!sqlStmt
->isPrepared()) sqlStmt
->prepare(node
->stmtstr
);
76 bool Recovery::isStmtInHashTable(int stmtId
)
78 int bucketNo
= stmtId
% STMT_BUCKET_SIZE
;
79 StmtBucket
*buck
= (StmtBucket
*) stmtBuckets
;
80 StmtBucket
*stmtBucket
= &buck
[bucketNo
];
81 if (stmtBucket
== NULL
) return false;
82 StmtNode
*node
= NULL
;
83 ListIterator it
= stmtBucket
->bucketList
.getIterator();
84 while(it
.hasElement()) {
85 node
= (StmtNode
*) it
.nextElement();
86 if(stmtId
== node
->stmtId
) {
87 SqlStatement
*sqlStmt
= (SqlStatement
*)node
->stmt
;
88 if (sqlStmt
->isPrepared()) return true;
95 void Recovery::freeAllStmtHandles()
97 if (NULL
== stmtBuckets
) return;
98 StmtBucket
*buck
= (StmtBucket
*) stmtBuckets
;
99 StmtNode
*node
= NULL
;
100 for (int i
=0; i
<STMT_BUCKET_SIZE
; i
++)
102 StmtBucket
*stmtBucket
= &buck
[i
];
103 if (stmtBucket
== NULL
) continue;
104 ListIterator it
= stmtBucket
->bucketList
.getIterator();
105 while(it
.hasElement()) {
106 node
= (StmtNode
*) it
.nextElement();
115 DbRetVal
Recovery::filterAndWriteStmtLogs()
118 char fName
[MAX_FILE_LEN
];
119 sprintf(fName
, "%s/csql.db.stmt", Conf::config
.getDbFile());
120 file_desc fdRead
= os::openFile(fName
, fileOpenReadOnly
,0);
121 if ((file_desc
)-1 == fdRead
) { return OK
; }
122 if (::stat(fName
, &st
) == -1) {
123 printError(ErrSysInternal
, "Unable to retrieve stmt log file size");
124 os::closeFile(fdRead
);
125 return ErrSysInternal
;
127 if (st
.st_size
==0) {
128 os::closeFile(fdRead
);
131 void *startAddr
= os::mmap(NULL
, st
.st_size
, mapProtRead
, mapPrivate
, fdRead
, 0);
132 if ((void*)MAP_FAILED
== startAddr
) {
133 printError(ErrSysInternal
, "Unable to mmap stmt log file\n");
134 return ErrSysInternal
;
136 sprintf(fName
, "%s/csql.db.stmt1", Conf::config
.getDbFile());
137 int fd
= os::openFileForAppend(fName
, O_CREAT
|O_TRUNC
);
138 char *iter
= (char*)startAddr
;
139 char *logStart
= NULL
, *logEnd
= NULL
;
145 if (iter
- (char*)startAddr
>= st
.st_size
) break;
146 logType
= *(int*)iter
;
148 if (logType
== -1) { //prepare
149 iter
= iter
+ sizeof(int);
151 iter
= iter
+ 2 * sizeof(int);
152 stmtID
= *(int*)iter
;
153 iter
= logStart
+ len
;
155 if (isStmtInHashTable(stmtID
))
156 ret
= os::write(fd
, logStart
, len
);
158 printError(ErrSysInternal
, "Unable to write statement logs");
161 else if(logType
== -3) { //free
162 iter
= logStart
+ 4 *sizeof(int);
164 printError(ErrSysInternal
, "Stmt Redo log file corrupted: logType:%d", logType
);
170 os::munmap((char*)startAddr
, st
.st_size
);
171 os::closeFile(fdRead
);
172 char cmd
[MAX_FILE_LEN
*2];
173 sprintf(cmd
, "mv %s/csql.db.stmt1 %s/csql.db.stmt",
174 Conf::config
.getDbFile(), Conf::config
.getDbFile());
179 DbRetVal
Recovery::readAndPopulateStmts(AbsSqlConnection
*conn
, bool list
, bool interactive
)
182 char fName
[MAX_FILE_LEN
];
183 sprintf(fName
, "%s/csql.db.stmt", Conf::config
.getDbFile());
184 printDebug(DM_Recovery
,"Statement Redo log filename is :%s\n", fName
);
185 file_desc fd
= os::openFile(fName
, fileOpenReadOnly
, 0);
186 if ((file_desc
)-1 == fd
) {
187 printDebug(DM_Recovery
, "No redo log file found");
190 if (::stat(fName
, &st
) == -1) {
191 printError(ErrSysInternal
, "Unable to retrieve stmt log file size");
193 return ErrSysInternal
;
195 if (NULL
!= stmtBuckets
)
197 printError(ErrSysInternal
, "stmtBuckets already populated");
199 return ErrSysInternal
;
201 stmtBuckets
= (void*) ::malloc (STMT_BUCKET_SIZE
* sizeof(StmtBucket
));
202 printDebug(DM_Recovery
, "stmtBuckets allocated %x", stmtBuckets
);
203 memset(stmtBuckets
, 0, STMT_BUCKET_SIZE
* sizeof(StmtBucket
));
204 if (st
.st_size
==0) {
205 printError(ErrNote
, "No Statement logs found during recovery");
209 void *startAddr
= os::mmap(NULL
, st
.st_size
, mapProtRead
, mapPrivate
, fd
, 0);
210 if ((void*)MAP_FAILED
== startAddr
) {
211 printError(ErrSysInternal
, "Unable to mmap stmt log file\n");
215 return ErrSysInternal
;
217 DbRetVal rv
= iterateStmtLogs(conn
, startAddr
, st
.st_size
, list
, interactive
);
218 os::munmap((char*)startAddr
, st
.st_size
);
223 DbRetVal
Recovery::iterateStmtLogs(AbsSqlConnection
*conn
, void *startAddr
, int size
, bool list
, bool interactive
)
225 char *iter
= (char*)startAddr
;
230 int len
, ret
, retVal
=0;
232 char stmtString
[SQL_STMT_LEN
];
235 if (iter
- (char*)startAddr
>= size
) break;
236 logType
= *(int*)iter
;
237 if (logType
== -1) { //prepare
238 iter
= iter
+ sizeof(int);
239 txnID
= *(int*) iter
; iter
+= sizeof(int);
240 loglen
= *(int*) iter
; iter
+= sizeof(int);
241 stmtID
= *(int*)iter
;
242 iter
= iter
+ sizeof(int);
244 iter
= iter
+ sizeof(int);
245 strncpy(stmtString
, iter
, len
);
248 printf("PREPARE: SID:%d %s\n", stmtID
, stmtString
);
251 if (interactive
) printf("STMTLOG PREPARE SID:%d %s\n", stmtID
, stmtString
);
252 AbsSqlStatement
*csqlStmt
= SqlFactory::createStatement(CSqlDirect
);
253 csqlStmt
->setConnection(conn
);
254 SqlStatement
*sqlStmt
= (SqlStatement
*)csqlStmt
;
255 sqlStmt
->setStmtString(stmtString
);
256 addToHashTable(stmtID
, csqlStmt
, stmtString
);
258 else if(logType
== -3) { //free
259 iter
= iter
+ sizeof(int);
260 txnID
= *(int*) iter
; iter
+= sizeof(int);
261 loglen
= *(int*) iter
; iter
+= sizeof(int);
262 stmtID
= *(int*)iter
;
263 iter
= iter
+ sizeof(int);
265 printf("FREE: SID:%d TID:%d \n", stmtID
, txnID
);
269 printError(ErrSysInternal
, "Stmt Redo log file corrupted: logType:%d", logType
);
277 #if (defined MMDB && defined EMBED)
279 DbRetVal
Recovery::recoverCsqlDB()
282 char dbRedoFileName
[MAX_FILE_LEN
];
283 char dbChkptSchema
[MAX_FILE_LEN
];
284 char dbChkptMap
[MAX_FILE_LEN
];
285 char dbChkptData
[MAX_FILE_LEN
];
286 char dbBackupFile
[MAX_FILE_LEN
];
287 char cmd
[IDENTIFIER_LENGTH
];
288 //check for check point file if present recover
289 sprintf(dbChkptSchema
, "%s/db.chkpt.schema1", Conf::config
.getDbFile());
290 if (FILE *file
= fopen(dbChkptSchema
, "r")) {
292 sprintf(cmd
, "cp -f %s %s/db.chkpt.schema", dbChkptSchema
,
293 Conf::config
.getDbFile());
294 int ret
= system(cmd
);
295 if (ret
!= 0) return ErrOS
;
297 sprintf(dbChkptMap
, "%s/db.chkpt.map1", Conf::config
.getDbFile());
298 if (FILE *file
= fopen(dbChkptMap
, "r")) {
300 sprintf(cmd
, "cp -f %s %s/db.chkpt.map", dbChkptMap
,
301 Conf::config
.getDbFile());
302 int ret
= system(cmd
);
303 if (ret
!= 0) return ErrOS
;
305 int chkptID
= Database::getCheckpointID();
306 sprintf(dbChkptData
, "%s/db.chkpt.data%d", Conf::config
.getDbFile(),
308 sprintf(dbBackupFile
, "%s/db.chkpt.data1", Conf::config
.getDbFile());
310 if (!Conf::config
.useMmap() && (fl
= fopen(dbBackupFile
, "r"))) {
312 sprintf(cmd
, "cp %s/db.chkpt.data1 %s", Conf::config
.getDbFile(),
314 int ret
= system(cmd
);
315 if (ret
!= 0) return ErrOS
;
317 if (FILE *file
= fopen(dbChkptData
, "r")) {
319 rv
= recoverSystemAndUserDB();
320 if (rv
!= OK
) return rv
;
323 //check for redo log file if present apply redo logs
324 sprintf(dbRedoFileName
, "%s/csql.db.cur", Conf::config
.getDbFile());
325 if (FILE *file
= fopen(dbRedoFileName
, "r"))
328 rv
= (DbRetVal
) applyRedoLogs(dbRedoFileName
);
329 if (rv
!= OK
) return rv
;
330 DatabaseManager
*dbMgr
= getConnObject().getDatabaseManager();
331 rv
= dbMgr
->checkPoint();
334 printError(ErrSysInternal
, "checkpoint failed after redo log apply");
341 DbRetVal
Recovery::recoverSystemAndUserDB()
345 sprintf(schFile
, "%s/db.chkpt.schema", Conf::config
.getDbFile());
346 if (FILE *file
= fopen(schFile
, "r")) {
347 rv
= applySchemaFile(file
);
348 if (rv
!= OK
) { fclose(file
); return rv
; }
350 DatabaseManager
*dbMgr
= getConnObject().getDatabaseManager();
351 rv
= dbMgr
->recover();
355 DbRetVal
Recovery::applySchemaFile(FILE *fp
)
360 SqlStatement
*stmt
= new SqlStatement();
361 while ((eof
= getQueryFromSchemaFile(fp
,buf
)) != EOF
) {
362 stmt
->setConnection(this);
363 rv
= stmt
->prepare(buf
);
364 if (rv
!= OK
) { delete stmt
; return rv
; }
367 if (rv
!= OK
) { stmt
->free(); delete stmt
; return rv
; }
373 char Recovery::getQueryFromSchemaFile(FILE *fp
, char *buf
)
375 char c
, *bufBegin
=buf
;
377 while( (c
=(char ) fgetc(fp
)) != EOF
&& c
!= ';')
379 *buf
++ = c
; charCnt
++;
380 if( charCnt
== SQL_STMT_LEN
) {
381 printf("SQL Statement length is greater than %d. "
382 "Ignoring the statement.\n", SQL_STMT_LEN
);
393 int Recovery::applyRedoLogs(char *redoFile
, AbsSqlConnection
*conn
, bool list
, bool interactive
)
397 file_desc fd
= os::openFile(redoFile
, fileOpenReadOnly
, 0);
398 if ((file_desc
)-1 == fd
) { return OK
; }
399 if (fstat(fd
, &st
) == -1) {
400 printError(ErrSysInternal
, "Unable to retrieve undo log file size");
404 if (st
.st_size
==0) {
405 printError(ErrNote
, "No Redo logs found during recovery");
406 readAndPopulateStmts(conn
, list
, interactive
);
410 void *startAddr
= os::mmap(NULL
, st
.st_size
, mapProtRead
, mapPrivate
, fd
, 0);
411 if (MAP_FAILED
== startAddr
) {
412 printf("Unable to read undo log file:mmap failed.\n");
416 rv
= readAndPopulateStmts(conn
, list
, interactive
);
419 printf("Unable to read stmt log file\n");
423 iter
= (char*)startAddr
;
428 int len
, ret
, retVal
=0;
430 char stmtString
[SQL_STMT_LEN
];
431 printDebug(DM_Recovery
,"Redo log filename is :%s\n", redoFile
);
433 if (iter
- (char*)startAddr
>= st
.st_size
) break;
434 logType
= *(int*)iter
;
435 if (logType
== -1) { //prepare
436 rv
= handlePrepare(conn
, list
, interactive
);
438 printError(ErrSysInternal
, "unable to handle prepare stmt");
443 else if(logType
== -2) { //commit
444 rv
= handleCommit(conn
, startAddr
, st
.st_size
, list
, interactive
);
445 if (ErrEndReached
== rv
)
447 printDebug(DM_Recovery
, "During Commit processing, end redo log reached");
450 }else if (OK
!= rv
) {
451 printError(ErrSysInternal
, "unable to handle prepare stmt");
456 else if(logType
== -3) { //free
457 handleFree(list
, interactive
);
459 else if(logType
== -4) { //prepare and execute
460 handlePrepareAndExecute(conn
, list
, interactive
);
462 printError(ErrSysInternal
, "Redo log file corrupted: logType:%d", logType
);
467 printDebug(DM_Recovery
, "Before munmap");
468 os::munmap((char*)startAddr
, st
.st_size
);
471 //when redo log list option is specified do not write statement logs
472 //it will block debugging and change the state at which problem
474 printDebug(DM_Recovery
, "Before filter and writestmt logs");
475 filterAndWriteStmtLogs();
476 printDebug(DM_Recovery
, "Before freeallstmthandles");
477 freeAllStmtHandles();
481 DbRetVal
Recovery::handlePrepare( AbsSqlConnection
*conn
, bool list
, bool interactive
)
483 char stmtString
[SQL_STMT_LEN
];
484 iter
= iter
+ sizeof(int);
485 int txnID
= *(int*) iter
;
487 int loglen
= *(int*) iter
;
489 int stmtID
= *(int*)iter
;
490 iter
= iter
+ sizeof(int);
491 int len
= *(int*)iter
;
492 iter
= iter
+ sizeof(int);
493 strncpy(stmtString
, iter
, len
);
495 printDebug(DM_Recovery
,"PREPARE: SID:%d %s\n", stmtID
, stmtString
);
497 printf("PREPARE: SID:%d %s\n", stmtID
, stmtString
);
500 AbsSqlStatement
*stmt
= SqlFactory::createStatement(CSqlDirect
);
501 stmt
->setConnection(conn
);
502 if (interactive
) printf("PREPARE %d : %s\n", stmtID
, stmtString
);
503 DbRetVal rv
= stmt
->prepare(stmtString
);
505 printError(ErrSysInternal
, "unable to prepare stmt:%s", stmtString
);
506 return ErrSysInternal
;
508 SqlStatement
*sqlStmt
= (SqlStatement
*)stmt
;
509 sqlStmt
->setLoading(true);
510 addToHashTable(stmtID
, stmt
, stmtString
);
511 printDebug(DM_Recovery
,"Added to hashtable SID:%d ", stmtID
);
514 DbRetVal
Recovery::handleCommit(AbsSqlConnection
*conn
, void *startAddr
, long size
, bool list
, bool interactive
)
517 iter
= iter
+ sizeof(int);
518 int txnID
= *(int*) iter
; iter
+= sizeof(int);
519 int loglen
= *(int*) iter
; iter
+= sizeof(int);
525 printDebug(DM_Recovery
, "Iter length %d\n", iter
- curPtr
);
526 if (iter
- (char*)startAddr
>= size
) {
528 printDebug(DM_Recovery
, "Redo log file end\n");
529 return ErrEndReached
;
531 int stmtID
= *(int*)iter
;
532 printDebug(DM_Recovery
, "stmtid %d\n", stmtID
);
533 if (interactive
) printf("EXEC %d :\n", stmtID
);
534 iter
= iter
+ sizeof(int);
535 int eType
= *(int*)iter
;
536 printDebug(DM_Recovery
,"eType is %d\n", eType
);
537 AbsSqlStatement
*stmt
= NULL
;
539 stmt
= getStmtFromHashTable(stmtID
);
541 printError(ErrSysInternal
, "Unable to find in stmt hashtable");
542 return ErrSysInternal
;
545 if (0 == eType
) { //execute type
546 iter
= iter
+ sizeof(int);
547 printDebug(DM_Recovery
, "EXEC SID:%d TID:%d\n", stmtID
, txnID
);
549 printf("EXEC SID:%d TID:%d\n", stmtID
, txnID
);
550 if (*(int*)iter
<0) break;
554 rv
= stmt
->execute(ret
);
556 printError(ErrSysInternal
, "unable to execute");
557 return ErrSysInternal
;
560 printError(ErrSysInternal
, "statement not found for %d\n",stmtID
);
562 printDebug(DM_Recovery
, "iter:%x value:%d", iter
, *(int*)iter
);
563 if (*(int*)iter
<0) break;
564 } else if ( 1 == eType
) { //set type
565 iter
=iter
+sizeof(int);
566 int pos
= *(int*) iter
;
567 iter
=iter
+sizeof(int);
568 int isNull
= *(int *) iter
;
569 iter
=iter
+sizeof(int);
571 DataType type
= (DataType
)(*(int*)iter
);
572 iter
=iter
+sizeof(int);
573 int len
= *(int*) iter
;
574 iter
=iter
+sizeof(int);
577 printDebug(DM_Recovery
, "SET SID:%d POS:%d ISNULL:FALSE TYPE:%d LEN:%d Value:", stmtID
, pos
, type
, len
);
579 printf("SET SID:%d POS:%d ISNULL:FALSE TYPE:%d LEN:%d Value:", stmtID
, pos
, type
, len
);
580 AllDataType::printVal(value
, type
, len
);
582 if (*(int*)iter
<0) break;
585 SqlStatement::setParamValues(stmt
, pos
, type
, len
, value
);
587 printDebug(DM_Recovery
, "SET SID:%d POS:%d ISNULL:TRUE\n", stmtID
, pos
);
589 printf("SET SID:%d POS:%d ISNULL:TRUE\n", stmtID
, pos
);
594 if (*(int*)iter
<0) break;
600 DbRetVal
Recovery::handleFree(bool list
, bool interactive
)
602 iter
= iter
+ sizeof(int);
603 int txnID
= *(int*) iter
; iter
+= sizeof(int);
604 int loglen
= *(int*) iter
; iter
+= sizeof(int);
605 int stmtID
= *(int*)iter
;
606 iter
= iter
+ sizeof(int);
607 printDebug(DM_Recovery
, "FREE SID:%d \n", stmtID
);
609 printf("FREE SID:%d \n", stmtID
);
612 if (interactive
) printf("FREE %d:\n", stmtID
);
613 AbsSqlStatement
*stmt
= getStmtFromHashTable(stmtID
);
617 removeFromHashTable(stmtID
);
619 printError(ErrSysInternal
, "statement not found for %d\n",stmtID
);
623 DbRetVal
Recovery::handlePrepareAndExecute(AbsSqlConnection
*conn
, bool list
, bool interactive
)
627 char stmtString
[SQL_STMT_LEN
];
628 iter
= iter
+ sizeof(int);
629 int txnID
= *(int*) iter
; iter
+= sizeof(int);
630 int loglen
= *(int*) iter
; iter
+= sizeof(int);
631 int stmtID
= *(int*)iter
;
632 iter
= iter
+ sizeof(int);
633 int len
= *(int*)iter
;
634 iter
= iter
+ sizeof(int);
635 strncpy(stmtString
, iter
, len
);
637 printDebug(DM_Recovery
, "EXECDIRECT SID:%d TID:%d STMT:%s\n", stmtID
, txnID
, stmtString
);
639 printf("EXECDIRECT SID:%d TID:%d STMT:%s\n", stmtID
, txnID
, stmtString
);
642 AbsSqlStatement
*stmt
= SqlFactory::createStatement(CSqlDirect
);
644 printError(ErrSysInternal
, "unable to prepare:%s", stmtString
);
645 return ErrSysInternal
;
647 stmt
->setConnection(conn
);
648 if (interactive
) printf("EXECDIRECT %d : %s\n", stmtID
, stmtString
);
649 rv
= stmt
->prepare(stmtString
);
651 printError(ErrSysInternal
, "unable to prepare:%s", stmtString
);
654 return ErrSysInternal
;
656 rv
= stmt
->execute(ret
);
658 if (strlen(stmtString
) > 6 &&
659 ( (strncasecmp(stmtString
,"CREATE", 6) == 0) ||
660 (strncasecmp(stmtString
,"DROP", 4) == 0) ||
661 (strncasecmp(stmtString
,"RENAME", 6) == 0) ||
662 (strncasecmp(stmtString
,"ALTER", 5) == 0)) ) {
665 printError(ErrSysInternal
, "unable to execute %s", stmtString
);
667 return ErrSysInternal
;