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