1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // CegoRecoveryManager.cc
4 // ----------------------
5 // Cego recovery manager class implementation
6 //
7 // Design and Implementation by Bjoern Lemke
8 //
9 // (C)opyright 2000-2019 Bjoern Lemke
10 //
11 // IMPLEMENTATION MODULE
12 //
13 // Class: CegoRecoveryManager
14 //
15 // Description: Transaction recovery handling
16 //
17 // Status: CLEAN
18 //
19 ///////////////////////////////////////////////////////////////////////////////
20
21 // LFC INCLUDES
22 #include <lfcbase/SetT.h>
23 #include <lfcbase/Datetime.h>
24 #include <lfcbase/Sleeper.h>
25 #include <lfcbase/CommandExecuter.h>
26
27 // CEGO INCLUDES
28 #include "CegoXMLdef.h"
29 #include "CegoRecoveryManager.h"
30
31 #include <string.h>
32 #include <stdlib.h>
33
CegoRecoveryManager(CegoDistManager * pGTM,CegoRecoveryManager::RecoveryMode mode)34 CegoRecoveryManager::CegoRecoveryManager(CegoDistManager *pGTM, CegoRecoveryManager::RecoveryMode mode)
35 {
36 _pGTM = pGTM;
37 _pLogger = _pGTM->getDBMng();
38 _pDBMng = _pGTM->getDBMng();
39 _recoveryMode = mode;
40 _modId = _pDBMng->getModId("CegoRecoveryManager");
41 }
42
~CegoRecoveryManager()43 CegoRecoveryManager::~CegoRecoveryManager()
44 {
45 }
46
recoverTableSet(const Chain & tableSet,unsigned long long pit,CegoAdminHandler * pAH)47 unsigned long long CegoRecoveryManager::recoverTableSet(const Chain& tableSet, unsigned long long pit, CegoAdminHandler* pAH)
48 {
49 _pLogger->log(_modId, Logger::NOTICE, Chain("Recovering tableset ") + tableSet + Chain(" ..."));
50
51 char *pShell = getenv(CGEXESHELLVARNAME);
52 if ( pShell == NULL )
53 {
54 _shellCmd = Chain(CGSTDEXESHELL);
55 }
56 else
57 {
58 _shellCmd = Chain(pShell);
59 }
60
61 int tabSetId = _pDBMng->getTabSetId(tableSet);
62
63 _pDBMng->setTableSetRunState(tableSet, XML_RECOVERY_VALUE);
64
65 _pDBMng->setRecoveryMode(tabSetId, CegoDatabaseManager::ON );
66
67 try
68 {
69 while ( _pDBMng->getTableSetSyncState(tableSet) != Chain(XML_SYNCHED_VALUE) && _pDBMng->getRecoveryMode(tabSetId) == CegoDatabaseManager::ON)
70 {
71 #ifdef CGDEBUG
72 _pDBMng->log(_modId, Logger::DEBUG, Chain("Waiting for tableset sync ... "));
73 #endif
74
75 Sleeper s;
76 s.secSleep(LOGMNG_RECOVERY_DELAY);
77 }
78
79 if ( _pDBMng->getTableSetSyncState(tableSet) != Chain(XML_SYNCHED_VALUE) )
80 {
81 Chain msg = Chain("No sync on tableset ") + tableSet + Chain(", recovery failed");
82 _pDBMng->setRecoveryMode(tabSetId, CegoDatabaseManager::OFF );
83 _pDBMng->log(_modId, Logger::LOGERR, msg);
84 throw Exception(EXLOC, msg);
85 }
86
87 // release online logfiles to force archiving
88 if ( _recoveryMode == LOCAL )
89 {
90 if ( pAH )
91 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Releasing log files ...\n"));
92 _pDBMng->releaseLogFiles(tableSet, true);
93 }
94
95 // during recovery, we do not write to online log files
96 if ( pAH )
97 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Stopping log ...\n"));
98 _pDBMng->stopLog(tabSetId);
99
100 if ( pAH )
101 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Registering datafiles ...\n"));
102 _pGTM->regDataFiles(tableSet);
103
104 if ( pAH )
105 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Registering objects ...\n"));
106 _pGTM->registerObjects(tableSet);
107
108 unsigned long long cplsn;
109
110 Chain tsTicketName = _pDBMng->getTSTicket(tableSet);
111
112 File tsTicket(tsTicketName);
113 if ( tsTicket.exists() )
114 {
115 _pDBMng->log(_modId, Logger::NOTICE, Chain("Backup tableset ticket detected, datafile file recovery required ..."));
116 tsTicket.open(File::READ);
117
118 XMLSuite xml;
119
120 Document *pDoc = new Document;
121 pDoc->setAttribute(XML_VERSION_ATTR, XML_VERSION_VALUE);
122
123 xml.setDocument(pDoc);
124 xml.setFile(&tsTicket);
125
126 xml.parse();
127
128 Element *pRoot = pDoc->getRootElement();
129 pDoc->setRootElement(0);
130 delete pDoc;
131
132 _pDBMng->setTableSetInfo(tableSet, pRoot);
133
134 cplsn = _pDBMng->getCommittedLSN(tableSet);
135
136 _pDBMng->log(_modId, Logger::NOTICE, Chain("Committed lsn = ") + Chain(cplsn));
137
138 if ( pAH )
139 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Committed lsn is ") + Chain(cplsn) + Chain("\n"));
140
141 _pDBMng->log(_modId, Logger::NOTICE, Chain("Starting datafile recovery for tableset ") + tableSet + Chain(" ..."));
142 unsigned long long lsn = dataFileRecovery(tableSet, tabSetId, cplsn, pAH);
143
144 _pDBMng->log(_modId, Logger::NOTICE, Chain("Datafile recovery for tableset ") + tableSet + Chain(" finished with lsn ") + Chain(lsn));
145 tsTicket.close();
146
147 #ifdef CGDEBUG
148 _pDBMng->log(_modId, Logger::DEBUG, Chain("Removing backup ticket ") + tsTicket.getFileName() + Chain(" ..."));
149 #endif
150
151 tsTicket.remove();
152 }
153 else
154 {
155 cplsn = _pDBMng->getCommittedLSN(tableSet);
156
157 _pDBMng->log(_modId, Logger::NOTICE, Chain("Committed lsn = ") + Chain(cplsn));
158
159 if ( pAH )
160 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Committed lsn is ") + Chain(cplsn) + Chain("\n"));
161 }
162
163 if ( pit == 0 )
164 {
165 _pDBMng->log(_modId, Logger::NOTICE, Chain("Starting up-to-crash transaction recovery for tableset ") + tableSet + Chain(" ..."));
166
167 if ( pAH )
168 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Starting up-to-crash recovery ...\n"));
169 }
170 else
171 {
172 Datetime d(pit);
173 _pDBMng->log(_modId, Logger::NOTICE,
174 Chain("Starting point-in-time transaction recovery to ") + d.asChain() + Chain(" for tableset ") + tableSet + Chain(" ..."));
175 if ( pAH )
176 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Starting point-in-time recovery ...\n"));
177 }
178
179 // during transaction recovery phase, we must ignore invalid btrees/index objects
180 // since these objects could be invalidated but Log data integrity and consistency is ensured
181 _pGTM->setIgnoreInvalid(true);
182 unsigned long long lsn = transactionRecovery(tableSet, tabSetId, cplsn, pit, pAH);
183 _pGTM->setIgnoreInvalid(false);
184
185 // correct invalidated index an btrees
186 _pGTM->correctTableSet(tabSetId, false);
187
188 if ( pAH )
189 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Init logfiles ...\n"));
190 _pDBMng->initLogFiles(tableSet, true);
191
192 _pDBMng->setActiveLogFile(tableSet);
193
194 if ( pAH )
195 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Starting log ...\n"));
196 _pDBMng->startLog(tabSetId);
197
198 CegoLogRecord logRec;
199 CegoLogRecord lr;
200 lr.setAction(CegoLogRecord::LOGREC_SYNC);
201
202 _pDBMng->logIt(tabSetId, lr, _pGTM->getLockHandle());
203
204 _pDBMng->setTableSetRunState(tableSet, XML_ONLINE_VALUE);
205
206 _pDBMng->setRecoveryMode(tabSetId, CegoDatabaseManager::OFF );
207
208 unsigned long long newcplsn = _pDBMng->getCurrentLSN(tabSetId);
209
210 _pDBMng->setCommittedLSN(tabSetId, newcplsn);
211
212 _pGTM->registerObjects(tableSet);
213
214 if ( pAH )
215 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Writing final checkpoint ...\n"));
216 _pDBMng->writeCheckPoint(tableSet, true, false, _pGTM->getLockHandle());
217
218 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovery for tableset ") + tableSet + Chain(" finished"));
219
220 return lsn;
221 }
222 catch ( Exception e )
223 {
224 Chain msg = e.getBaseMsg();
225
226 _pDBMng->setTableSetRunState(tableSet, XML_OFFLINE_VALUE);
227 _pDBMng->setRecoveryMode(tabSetId, CegoDatabaseManager::OFF );
228
229 throw Exception(EXLOC, Chain("Recovery failed : ") + msg);
230 }
231 }
232
dataFileRecovery(const Chain & tableSet,int tabSetId,unsigned long long cplsn,CegoAdminHandler * pAH)233 unsigned long long CegoRecoveryManager::dataFileRecovery(const Chain& tableSet, int tabSetId, unsigned long long cplsn, CegoAdminHandler* pAH)
234 {
235 bool endOfBackup = false;
236
237 unsigned long long lsn = cplsn;
238
239 while ( endOfBackup == false && _pDBMng->getRecoveryMode(tabSetId) == CegoDatabaseManager::ON )
240 {
241 Chain archLogFileName = _pDBMng->getArchiveLogName(tableSet, lsn);
242
243 ListT<Chain> archIdList;
244 ListT<Chain> archPathList;
245
246 _pDBMng->getArchLogInfo(tabSetId, archIdList, archPathList);
247
248 Chain *pArchLogPath = archPathList.First();
249
250 Chain sourceFileName;
251
252 bool fileFound = false;
253
254 while ( pArchLogPath && fileFound == false )
255 {
256 sourceFileName = *pArchLogPath + Chain("/") + archLogFileName;
257
258 File checkFile(sourceFileName);
259
260 _pDBMng->log(_modId, Logger::NOTICE, Chain("Checking file ") + sourceFileName);
261
262 if ( checkFile.exists() )
263 {
264 fileFound = true;
265 }
266 else
267 {
268 pArchLogPath = archPathList.Next();
269 }
270 }
271
272 if ( fileFound )
273 {
274 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering offline logfile ") + sourceFileName + Chain(" ..."));
275 _pDBMng->setLogFile(tabSetId, sourceFileName, true);
276 _pDBMng->setCurrentLSN(tabSetId, lsn);
277
278 if ( pAH )
279 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Datafile recovery with ") + sourceFileName + Chain("\n"));
280 // recover offline logs
281 lsn = recoverCurrentDataFileLog(tabSetId, endOfBackup);
282
283 _pDBMng->setCurrentLSN(tabSetId, lsn);
284
285 }
286 else if ( _recoveryMode == LOCAL )
287 {
288 if ( restoreLogFile(tableSet, lsn) == false)
289 endOfBackup = true;
290 }
291 else
292 {
293 Sleeper s;
294 s.secSleep(LOGMNG_RECOVERY_DELAY);
295 }
296 }
297
298 return lsn;
299 }
300
transactionRecovery(const Chain & tableSet,int tabSetId,unsigned long long cplsn,unsigned long long pit,CegoAdminHandler * pAH)301 unsigned long long CegoRecoveryManager::transactionRecovery(const Chain& tableSet, int tabSetId, unsigned long long cplsn, unsigned long long pit, CegoAdminHandler* pAH)
302 {
303 bool endOfRecovery = false;
304 Chain sourceFileName;
305 unsigned long long ts = 0;
306
307 unsigned long long lsn = cplsn;
308
309 RecoveryState rstate = CegoRecoveryManager::RECOK;
310
311 while ( _pDBMng->getRecoveryMode(tabSetId) == CegoDatabaseManager::ON
312 && rstate != CegoRecoveryManager::RECPITREACHED
313 && endOfRecovery == false)
314 {
315 bool fileFound = false;
316
317 while ( fileFound == false && endOfRecovery == false && _pDBMng->getRecoveryMode(tabSetId) == CegoDatabaseManager::ON )
318 {
319 Chain archLogFileName = _pDBMng->getArchiveLogName(tableSet, lsn);
320
321 ListT<Chain> archIdList;
322 ListT<Chain> archPathList;
323
324 _pDBMng->getArchLogInfo(tabSetId, archIdList, archPathList);
325
326 Chain *pArchLogPath = archPathList.First();
327
328 while ( pArchLogPath && fileFound == false)
329 {
330 sourceFileName = *pArchLogPath + Chain("/") + archLogFileName;
331
332 File checkFile(sourceFileName);
333
334 _pDBMng->log(_modId, Logger::NOTICE, Chain("Checking logfile ") + sourceFileName + Chain(" ..."));
335
336 if ( checkFile.exists() )
337 {
338 fileFound = true;
339 }
340 else
341 {
342 pArchLogPath = archPathList.Next();
343 }
344 }
345
346 if ( fileFound == false && _recoveryMode == LOCAL)
347 {
348 if ( restoreLogFile(tableSet, lsn) == false)
349 endOfRecovery = true;
350 }
351 if ( fileFound == false )
352 {
353 Sleeper s;
354 s.secSleep(LOGMNG_RECOVERY_DELAY);
355 }
356 }
357
358 // if we found the appropriate file, we try to recover it
359 if ( fileFound )
360 {
361 bool isRecovered = false;
362 while ( ! isRecovered && _pDBMng->getRecoveryMode(tabSetId) == CegoDatabaseManager::ON )
363 {
364 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering offline logfile ") + sourceFileName + Chain(" ..."));
365 _pDBMng->setLogFile(tabSetId, sourceFileName, true);
366 _pDBMng->setCurrentLSN(tabSetId, lsn);
367
368 if ( pAH )
369 pAH->syncWithInfo(Chain("local"), Chain("local"), Chain("Transaction recovery with ") + sourceFileName + Chain("\n"));
370
371 rstate = recoverCurrentTransactionLog(tabSetId, pit, lsn, ts);
372
373 _pDBMng->setCurrentLSN(tabSetId, lsn);
374
375 if ( rstate == CegoRecoveryManager::RECOK )
376 {
377 _pDBMng->log(_modId, Logger::NOTICE, Chain("Transaction recovery ok"));
378 isRecovered=true;
379 lsn++;
380 }
381 else if ( rstate == CegoRecoveryManager::RECINCOMPLETE )
382 {
383 _pDBMng->log(_modId, Logger::NOTICE, Chain("Incomplete offline logfile ") + sourceFileName + Chain(" detected, waiting ..."));
384 Sleeper s;
385 s.secSleep(LOGMNG_RECOVERY_DELAY);
386 }
387 else if ( rstate == CegoRecoveryManager::RECPITREACHED )
388 {
389 _pDBMng->log(_modId, Logger::NOTICE, Chain("PIT reached"));
390 isRecovered=true;
391 }
392 else
393 {
394 throw Exception(EXLOC, Chain("Unknwon recovery state"));
395 }
396 }
397 }
398 }
399
400 _pDBMng->log(_modId, Logger::NOTICE, Chain("Finished recovery loop ..."));
401
402 // after recovery mode has been finished, we still have to check for the last logfile to recover
403 if ( rstate == CegoRecoveryManager::RECINCOMPLETE )
404 {
405 while ( rstate != CegoRecoveryManager::RECOK )
406 {
407 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering final offline logfile ") + sourceFileName + Chain(" ..."));
408
409 rstate = recoverCurrentTransactionLog(tabSetId, pit, lsn, ts);
410
411 if ( rstate == CegoRecoveryManager::RECINCOMPLETE )
412 {
413 _pDBMng->log(_modId, Logger::NOTICE, Chain("Waiting for final offline logfile ..."));
414 Sleeper s;
415 s.secSleep(LOGMNG_RECOVERY_DELAY);
416 }
417 }
418 }
419
420 _pDBMng->writeCheckPoint(tableSet, false, false, _pGTM->getLockHandle());
421
422 _pDBMng->log(_modId, Logger::NOTICE, Chain("Transaction recovery finished with lsn ") + Chain(lsn));
423 return lsn;
424 }
425
recoverCurrentDataFileLog(int tabSetId,bool & endOfBackup)426 int CegoRecoveryManager::recoverCurrentDataFileLog(int tabSetId, bool& endOfBackup)
427 {
428 unsigned long long lsn=0;
429
430 _pDBMng->seekToStart(tabSetId);
431
432 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering logfile ... "));
433
434 CegoLockHandler* pLockHandle = _pGTM->getLockHandle();
435
436 // now recover to last lsn in log
437
438 endOfBackup = false;
439 CegoLogRecord logRec;
440 while ( _pDBMng->logRead(tabSetId, logRec) && ! endOfBackup )
441 {
442 if ( logRec.getLSN() > _pDBMng->getCurrentLSN(tabSetId) )
443 {
444 lsn=logRec.getLSN();
445
446 switch ( logRec.getAction() )
447 {
448 case CegoLogRecord::LOGREC_BUPAGE:
449 {
450 #ifdef CGDEBUG
451 _pDBMng->log(_modId, Logger::DEBUG, Chain("Writing page (") + Chain(logRec.getFileId()) + Chain(",") + Chain(logRec.getPageId()) + Chain(")"));
452 #endif
453
454 _pDBMng->writePage(logRec.getPageId(), 0, logRec.getData(), pLockHandle );
455 break;
456 }
457 case CegoLogRecord::LOGREC_BUFBM:
458 {
459 #ifdef CGDEBUG
460 _pDBMng->log(_modId, Logger::DEBUG, Chain("Writing fbm for fileId ") + Chain(logRec.getFileId()));
461 #endif
462
463 _pDBMng->writeFBM(logRec.getFileId(), (unsigned*)logRec.getData(), pLockHandle );
464 break;
465 }
466 case CegoLogRecord::LOGREC_BUFIN:
467 {
468 _pDBMng->log(_modId, Logger::NOTICE, Chain("Detected end of backup"));
469 endOfBackup=true;
470 break;
471 }
472 case CegoLogRecord::LOGREC_CREATE:
473 case CegoLogRecord::LOGREC_DROP:
474 case CegoLogRecord::LOGREC_ALTER:
475 case CegoLogRecord::LOGREC_RENAME:
476 case CegoLogRecord::LOGREC_INSERT:
477 case CegoLogRecord::LOGREC_DELETE:
478 case CegoLogRecord::LOGREC_UPDATE:
479 case CegoLogRecord::LOGREC_BEGIN:
480 case CegoLogRecord::LOGREC_COMMIT:
481 case CegoLogRecord::LOGREC_ABORT:
482 case CegoLogRecord::LOGREC_SYNC:
483 case CegoLogRecord::LOGREC_ADDCOUNTER:
484 case CegoLogRecord::LOGREC_DELCOUNTER:
485 case CegoLogRecord::LOGREC_TRUNCATE:
486 break;
487 }
488 }
489 else // logRec.getLSN() <= _pDBMng->getLSN(tabSetId)
490 {
491 _pDBMng->log(_modId, Logger::NOTICE, Chain("Ignoring lsn ") + Chain(logRec.getLSN()) + Chain(" ( expected greater than ") + Chain(_pDBMng->getCurrentLSN(tabSetId)) + Chain(")"));
492 }
493
494 if ( logRec.getData() )
495 free ( logRec.getData() ) ;
496 }
497 return lsn;
498 }
499
recoverCurrentTransactionLog(int tabSetId,unsigned long long pit,unsigned long long & lsn,unsigned long long & ts)500 CegoRecoveryManager::RecoveryState CegoRecoveryManager::recoverCurrentTransactionLog(int tabSetId, unsigned long long pit, unsigned long long& lsn, unsigned long long& ts)
501 {
502
503 _invalidatedList.Empty();
504
505 _pDBMng->log(_modId, Logger::NOTICE, Chain("Invalidating transaction affected index objects ..."));
506 _pGTM->getTransactionManager()->getCrashAffectedTables(tabSetId, _invalidatedList);
507
508 Chain *pTableName = _invalidatedList.First();
509 while ( pTableName )
510 {
511 _pDBMng->log(_modId, Logger::NOTICE, Chain("Invalidating index objects for table ") + *pTableName + Chain(" ..."));
512 _pGTM->invalidateIndexForTable(tabSetId, *pTableName);
513 pTableName = _invalidatedList.Next();
514 }
515 _pDBMng->log(_modId, Logger::NOTICE, Chain("Index invalidation done"));
516
517 // initially, we set the logfile to incomplete
518 RecoveryState rstate = CegoRecoveryManager::RECINCOMPLETE;
519
520 _pDBMng->seekToStart(tabSetId);
521
522 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering logfile ..."));
523
524 // now recover to last lsn in log
525
526 CegoLogRecord logRec;
527 while ( _pDBMng->logRead(tabSetId, logRec) && rstate != CegoRecoveryManager::RECPITREACHED )
528 {
529 // point-in-time recovery
530 if ( pit > 0 && logRec.getTS() > pit )
531 {
532 Datetime d(logRec.getTS());
533 _pDBMng->log(_modId, Logger::NOTICE, Chain("Required point in time reached, Ignoring logentry ") + Chain(logRec.getLSN())
534 + Chain(" ( ") + d.asChain() + Chain(" ) and higher"));
535
536 rstate = CegoRecoveryManager::RECPITREACHED;
537 }
538 else if ( logRec.getTS() < ts )
539 {
540 _pDBMng->log(_modId, Logger::NOTICE, Chain("Old log detected, ignoring logentry ") + Chain(logRec.getLSN()) + Chain(" and higher"));
541
542 rstate = CegoRecoveryManager::RECPITREACHED;
543 }
544 else
545 {
546 if ( logRec.getLSN() > _pDBMng->getCurrentLSN(tabSetId) )
547 {
548 // we got a valid lsn, so the log file seems to be complete and we can switch from incomplete to ok
549 rstate = CegoRecoveryManager::RECOK;
550
551 lsn=logRec.getLSN();
552 ts=logRec.getTS();
553 unsigned long long tid=logRec.getTID();
554 unsigned long long tastep=logRec.getTAStep();
555
556 _pDBMng->log(_modId, Logger::NOTICE, Chain("Recovering lsn ") + Chain(logRec.getLSN()) + Chain(" ..."));
557
558 // here comes the recovery
559
560 switch ( logRec.getAction() )
561 {
562 case CegoLogRecord::LOGREC_CREATE:
563 {
564 switch ( logRec.getObjType() )
565 {
566 case CegoObject::TABLE:
567 case CegoObject::SYSTEM:
568 {
569 CegoTableObject to;
570 to.decode(logRec.getData());
571 _pGTM->createDataTable(tabSetId, logRec.getObjName(), logRec.getObjType(), to.getSchema());
572 _pDBMng->addObject(tabSetId, logRec.getObjName(), CegoObject::TABLE);
573 break;
574 }
575 case CegoObject::AVLTREE:
576 case CegoObject::PAVLTREE:
577 case CegoObject::UAVLTREE:
578 {
579 CegoTableObject to;
580 to.decode(logRec.getData());
581 bool isCached=false;
582
583 if ( _pGTM->objectExists(tabSetId, logRec.getObjName(), logRec.getObjType() ) )
584 {
585 _pDBMng->log(_modId, Logger::NOTICE, Chain("Dropping suspect avl index ") + logRec.getObjName());
586 _pGTM->dropIndex(tabSetId, logRec.getObjName());
587 }
588
589 _pGTM->createIndexTable(tabSetId, logRec.getObjName(), to.getTabName(), to.getSchema(), logRec.getObjType(), isCached);
590 _pDBMng->addObject(tabSetId, logRec.getObjName(), logRec.getObjType());
591 break;
592 }
593 case CegoObject::BTREE:
594 case CegoObject::PBTREE:
595 case CegoObject::UBTREE:
596 {
597 CegoBTreeObject bo;
598 bo.decode(logRec.getData());
599 bool isCached=false;
600
601 // it may happen that the the btree objects already exists
602 // in this case, we drop any rebuild
603
604 if ( _pGTM->objectExists(tabSetId, logRec.getObjName(), logRec.getObjType() ) )
605 {
606 _pDBMng->log(_modId, Logger::NOTICE, Chain("Dropping suspect btree ") + logRec.getObjName());
607 _pGTM->dropBTree(tabSetId, logRec.getObjName());
608 }
609 _pGTM->createIndexTable(tabSetId, logRec.getObjName(), bo.getTabName(), bo.getSchema(), logRec.getObjType(), isCached);
610 _pDBMng->addObject(tabSetId, logRec.getObjName(), logRec.getObjType());
611 break;
612 }
613 case CegoObject::VIEW:
614 {
615 CegoViewObject vo;
616 vo.decode(logRec.getData());
617 _pGTM->createViewObject(vo);
618 _pDBMng->addObject(tabSetId, logRec.getObjName(), CegoObject::VIEW);
619 break;
620 }
621 case CegoObject::PROCEDURE:
622 {
623 CegoProcObject po;
624 po.decode(logRec.getData());
625 _pGTM->createProcObject(po);
626 _pDBMng->addObject(tabSetId, logRec.getObjName(), CegoObject::PROCEDURE);
627 break;
628 }
629 case CegoObject::FKEY:
630 {
631 CegoKeyObject ko;
632 ko.decode(logRec.getData());
633 _pGTM->createKeyObject(ko);
634 _pDBMng->addObject(tabSetId, logRec.getObjName(), CegoObject::FKEY);
635 break;
636 }
637 case CegoObject::CHECK:
638 {
639 CegoCheckObject co;
640 co.decode(logRec.getData());
641 _pGTM->createCheckObject(co);
642 _pDBMng->addObject(tabSetId, logRec.getObjName(), CegoObject::CHECK);
643 break;
644 }
645 case CegoObject::TRIGGER:
646 {
647 // TODO
648 break;
649 }
650 case CegoObject::ALIAS:
651 {
652 // TODO
653 break;
654 }
655 case CegoObject::RBSEG:
656 case CegoObject::JOIN:
657 case CegoObject::UNDEFINED:
658 // N/A
659 break;
660 }
661 break;
662 }
663 case CegoLogRecord::LOGREC_DROP:
664 {
665 switch ( logRec.getObjType() )
666 {
667 case CegoObject::TABLE:
668 _pGTM->dropTable(tabSetId, logRec.getObjName());
669 break;
670 case CegoObject::AVLTREE:
671 case CegoObject::UAVLTREE:
672 case CegoObject::PAVLTREE:
673 _pGTM->dropIndex(tabSetId, logRec.getObjName());
674 break;
675 case CegoObject::BTREE:
676 case CegoObject::UBTREE:
677 case CegoObject::PBTREE:
678 _pGTM->dropBTree(tabSetId, logRec.getObjName());
679 break;
680 case CegoObject::VIEW:
681 _pGTM->dropView(tabSetId, logRec.getObjName());
682 break;
683 case CegoObject::PROCEDURE:
684 _pGTM->dropProcedure(tabSetId, logRec.getObjName());
685 break;
686 case CegoObject::FKEY:
687 _pGTM->dropFKey(tabSetId, logRec.getObjName());
688 break;
689 case CegoObject::CHECK:
690 _pGTM->dropCheck(tabSetId, logRec.getObjName());
691 break;
692 case CegoObject::TRIGGER:
693 _pGTM->dropTrigger(tabSetId, logRec.getObjName());
694 break;
695 case CegoObject::ALIAS:
696 _pGTM->dropAlias(tabSetId, logRec.getObjName());
697 break;
698 case CegoObject::SYSTEM:
699 case CegoObject::RBSEG:
700 case CegoObject::JOIN:
701 case CegoObject::UNDEFINED:
702 break;
703 // ignore
704 // throw Exception(EXLOC, "Invalid object type for drop");
705 }
706 break;
707 }
708 case CegoLogRecord::LOGREC_TRUNCATE:
709 {
710 _pGTM->truncateTable(tabSetId, logRec.getObjName());
711 break;
712 }
713
714 case CegoLogRecord::LOGREC_ALTER:
715 {
716 CegoTableObject aoe;
717 aoe.decode(logRec.getData());
718 _pGTM->alterTableObject(tabSetId, logRec.getObjName(), logRec.getObjType(), aoe);
719 break;
720 }
721 case CegoLogRecord::LOGREC_RENAME:
722 {
723 Chain newObjName(logRec.getData(), logRec.getDataLen());
724 _pGTM->renameObject(tabSetId, logRec.getObjName(), logRec.getObjType(), newObjName );
725 _pDBMng->removeObject(tabSetId, logRec.getObjName(), logRec.getObjType());
726 _pDBMng->addObject(tabSetId, newObjName, logRec.getObjType());
727 break;
728 }
729 case CegoLogRecord::LOGREC_BEGIN:
730 {
731 // tid is already setup by log reacord information
732 // _pGTM->beginTransaction(tabSetId);
733 break;
734 }
735 case CegoLogRecord::LOGREC_COMMIT:
736 {
737 _pGTM->setTID(tabSetId, tid);
738 _pGTM->commitTransaction(tabSetId, false);
739 break;
740 }
741 case CegoLogRecord::LOGREC_ABORT:
742 {
743 _pGTM->setTID(tabSetId, tid);
744 _pGTM->rollbackTransaction(tabSetId, false);
745 break;
746 }
747 case CegoLogRecord::LOGREC_INSERT:
748 {
749 CegoTableObject oe;
750 _pGTM->getObject(tabSetId, logRec.getObjName(), CegoObject::TABLE, oe);
751
752
753 if ( _invalidatedList.Find(logRec.getObjName()) == 0 )
754 {
755 _pDBMng->log(_modId, Logger::NOTICE, Chain("Invalidating index objects for table ") + logRec.getObjName());
756 _pGTM->invalidateIndexForTable(tabSetId, logRec.getObjName());
757 _invalidatedList.Insert(logRec.getObjName());
758 }
759
760
761 ListT<CegoField> fvl = oe.getSchema();
762 ListT<CegoBlob> blobList;
763 ListT<CegoClob> clobList;
764
765 #ifdef CGDEBUG
766 _pDBMng->log(_modId, Logger::DEBUG, Chain("Decoding log record of len ") + Chain(logRec.getDataLen()));
767 #endif
768
769 unsigned long long tid;
770 unsigned long long tastep;
771 CegoTupleState ts;
772
773 int toff = CegoQueryHelper::decodeTupleHeader(tid, tastep, ts, logRec.getData());
774
775 char* tp = logRec.getData() + toff;
776 int tlen = logRec.getDataLen() - toff;
777
778 CegoQueryHelper::decodeFVL(fvl, blobList, clobList, tp, tlen);
779
780 #ifdef CGDEBUG
781 _pDBMng->log(_modId, Logger::DEBUG, Chain("Log record decoded"));
782 #endif
783
784 CegoBlob *pBlob = blobList.First();
785 CegoField *pF = fvl.First();
786 while ( pBlob && pF )
787 {
788 PageIdType pageId;
789
790 _pGTM->putBlobData(tabSetId, pBlob->getBufPtr(), pBlob->getSize(), pageId);
791
792 while ( pF->getValue().getType() != BLOB_TYPE && pF)
793 pF = fvl.Next();
794
795 if ( pF )
796 {
797 Chain blobRef = Chain("[") + Chain(pageId) + Chain("]");
798 CegoFieldValue fv(BLOB_TYPE, blobRef);
799 pF->setValue(fv);
800 }
801 else
802 {
803 throw Exception(EXLOC, "Cannot get blob reference");
804 }
805
806 pBlob->release();
807 pBlob = blobList.Next();
808 pF = fvl.Next();
809 }
810
811 CegoClob *pClob = clobList.First();
812 pF = fvl.First();
813 while ( pClob && pF )
814 {
815 PageIdType pageId;
816
817 _pGTM->putClobData(tabSetId, pClob->getBufPtr(), pClob->getSize(), pageId);
818
819 while ( pF->getValue().getType() != CLOB_TYPE && pF)
820 pF = fvl.Next();
821
822 if ( pF )
823 {
824 Chain clobRef = Chain("[") + Chain(pageId) + Chain("]");
825 CegoFieldValue fv(CLOB_TYPE, clobRef);
826 pF->setValue(fv);
827 }
828 else
829 {
830 throw Exception(EXLOC, "Cannot get clob reference");
831 }
832
833 pClob->release();
834 pClob = clobList.Next();
835 pF = fvl.Next();
836 }
837
838 bool doLogging = true;
839 bool flushLog = true;
840
841 CegoDataPointer dp;
842
843 _pGTM->setTID(tabSetId, tid);
844 _pGTM->setTAStep(tabSetId, tastep);
845
846 _pGTM->insertDataTable(oe, fvl, dp, doLogging, flushLog);
847
848 break;
849 }
850 case CegoLogRecord::LOGREC_DELETE:
851 {
852 CegoPredDesc *pPred = 0;
853 Chain tableAlias;
854
855 CegoQueryHelper::decodeDelRec(tableAlias, pPred, logRec.getData(), logRec.getDataLen(), _pGTM, tabSetId);
856
857 CegoTableObject oe;
858 _pGTM->getObject(tabSetId, logRec.getObjName(), CegoObject::TABLE, oe);
859
860
861 if ( _invalidatedList.Find(logRec.getObjName()) == 0 )
862 {
863 _pDBMng->log(_modId, Logger::NOTICE, Chain("Invalidating index objects for table ") + logRec.getObjName());
864 _pGTM->invalidateIndexForTable(tabSetId, logRec.getObjName());
865 _invalidatedList.Insert(logRec.getObjName());
866 }
867
868
869 _pGTM->setTID(tabSetId, tid);
870 _pGTM->setTAStep(tabSetId, tastep);
871
872 _pGTM->deleteDataTable(oe, pPred, 0, false);
873
874 break;
875 }
876 case CegoLogRecord::LOGREC_UPDATE:
877 {
878
879 ListT<CegoField> updList;
880 ListT<CegoExpr*> exprList;
881
882 CegoPredDesc *pPred = 0;
883 Chain tableAlias;
884
885 bool returnOnFirst;
886
887 CegoQueryHelper::decodeUpdRec(tableAlias, pPred, updList, exprList, returnOnFirst,
888 logRec.getData(), logRec.getDataLen(), _pGTM, tabSetId);
889
890 if ( _invalidatedList.Find(logRec.getObjName()) == 0 )
891 {
892 _pDBMng->log(_modId, Logger::NOTICE, Chain("Invalidating index objects for table ") + logRec.getObjName());
893 _pGTM->invalidateIndexForTable(tabSetId, logRec.getObjName());
894 _invalidatedList.Insert(logRec.getObjName());
895 }
896
897
898 // cout << "Setting tid = " << tid << endl;
899 _pGTM->setTID(tabSetId, tid);
900 _pGTM->setTAStep(tabSetId, tastep);
901
902 // cout << "Updating data table .." << endl;
903
904 ListT<CegoField> returnList;
905 _pGTM->updateDataTable(tabSetId, logRec.getObjName(),
906 tableAlias,
907 pPred,
908 updList,
909 exprList,
910 returnOnFirst,
911 returnList);
912 break;
913 }
914 case CegoLogRecord::LOGREC_ADDCOUNTER:
915 {
916 // counters are not completely transaction safe since during some checkpoint of any tableset
917 // counters could be already written down to XML configuration file
918 // For recovery, we set counters with force = true
919
920 Chain counterName ( logRec.getData() );
921 long initValue = 0;
922 _pDBMng->addCounter(tabSetId, counterName, initValue, true);
923 break;
924 }
925 case CegoLogRecord::LOGREC_DELCOUNTER:
926 {
927 Chain counterName ( logRec.getData() );
928 _pDBMng->removeCounter(tabSetId, counterName);
929 break;
930 }
931 case CegoLogRecord::LOGREC_SYNC:
932 case CegoLogRecord::LOGREC_BUPAGE:
933 case CegoLogRecord::LOGREC_BUFBM:
934 case CegoLogRecord::LOGREC_BUFIN:
935 {
936 // BUPAGE, BUFMB and BUFIN already handled in data file recovery
937 // for SYNC nothing to do
938 break;
939 }
940 }
941 }
942 else // logRec.getLSN() < _pDBMng->getLSN(tabSetId)
943 {
944 _pDBMng->log(_modId, Logger::NOTICE, Chain("Ignoring lsn ") + Chain(logRec.getLSN())
945 + Chain(" ( expected greater than ") + Chain(_pDBMng->getCurrentLSN(tabSetId)) + Chain(")"));
946
947 rstate = CegoRecoveryManager::RECOK;
948 }
949 }
950
951 if ( logRec.getData() )
952 free ( logRec.getData() ) ;
953 }
954
955 _pDBMng->log(_modId, Logger::NOTICE, Chain("Current logfile recovery finished with lsn ") + Chain(lsn));
956
957 return rstate;
958 }
959
960
restoreLogFile(const Chain & tableSet,unsigned long lsn)961 bool CegoRecoveryManager::restoreLogFile(const Chain& tableSet, unsigned long lsn)
962 {
963
964 bool fileProvided=false;
965 Chain archRestoreProg = _pDBMng->getArchRestoreProg();
966
967 if ( archRestoreProg != Chain("NONE") )
968 {
969
970 Chain archLogFileName = _pDBMng->getArchiveLogName(tableSet, lsn);
971
972 ListT<Chain> archIdList;
973 ListT<Chain> archPathList;
974
975 int tabSetId = _pDBMng->getTabSetId(tableSet);
976 _pDBMng->getArchLogInfo(tabSetId, archIdList, archPathList);
977
978 _pDBMng->log(_modId, Logger::NOTICE, Chain("Waiting for logfile ") + archLogFileName + Chain(" ..."));
979
980 Chain *pArchLogPath = archPathList.First();
981 Chain pathString;
982 while ( pArchLogPath )
983 {
984 pathString += *pArchLogPath;
985 pArchLogPath = archPathList.Next();
986 if ( pArchLogPath )
987 pathString += Chain(":");
988 }
989
990 CommandExecuter cmdExe(_shellCmd);
991
992 int archRestoreTimeout = _pDBMng->getArchRestoreTimeout();
993
994 Chain restoreCmd = archRestoreProg + Chain(" -t ") + tableSet + Chain(" -f ") + archLogFileName + Chain(" -p ") + pathString;
995
996 _pDBMng->log(_modId, Logger::NOTICE, Chain("Triggering external log manager with <") + Chain(restoreCmd) + Chain(">"));
997 int retCode = cmdExe.execute(restoreCmd, archRestoreTimeout);
998 _pDBMng->log(_modId, Logger::NOTICE, Chain("External log manager returned : <") + Chain(retCode) + Chain(">"));
999 if ( retCode == 0 )
1000 {
1001 // file could be provided
1002 fileProvided=true;
1003 }
1004 else if ( retCode == 1 )
1005 {
1006 fileProvided = false;
1007 }
1008 else // ( retCode > 1 )
1009 {
1010 Chain msg("External log manager failed");
1011 throw Exception(EXLOC, msg);
1012 }
1013 }
1014 else
1015 {
1016 fileProvided = false;
1017 }
1018
1019 // we don't have to check for online redo logs
1020 // if recovery starts, the current online redo logs are copied to archive location
1021 // so they are already available
1022
1023 return fileProvided;
1024 }
1025