You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
496 lines
12 KiB
496 lines
12 KiB
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "daedef.h"
|
|
#include "ssib.h"
|
|
#include "pib.h"
|
|
#include "util.h"
|
|
#include "page.h"
|
|
#include "fucb.h"
|
|
#include "stapi.h"
|
|
#include "dirapi.h"
|
|
#include "logapi.h"
|
|
#include "log.h"
|
|
|
|
#include "fileapi.h"
|
|
#include "dbapi.h"
|
|
DeclAssertFile; /* Declare file name for assert macros */
|
|
|
|
|
|
/* variables used in redo only */
|
|
BYTE *pbNext; // redo only - location of next buffer entry
|
|
BYTE *pbRead; // redo only - location of next rec to flush
|
|
INT isecRead; /* redo only - next disk to read. */
|
|
BOOL fOnLastSec; /* redo only - on last sector of cur lg file */
|
|
|
|
LGPOS lgposLastRec; /* mark for end of rec */
|
|
|
|
|
|
VOID GetLgposOfPbNext(LGPOS *plgpos)
|
|
{
|
|
char *pb = PbSecAligned(pbNext);
|
|
int ib = (int)(pbNext - pb);
|
|
int isec;
|
|
|
|
if (pb > pbRead)
|
|
isec = (int)(pbRead + csecLGBuf * cbSec - pb) / cbSec;
|
|
else
|
|
isec = (int)(pbRead - pb) / cbSec;
|
|
isec = isecRead - isec;
|
|
|
|
plgpos->isec = (USHORT)isec;
|
|
plgpos->ib = (USHORT)ib;
|
|
plgpos->usGeneration = plgfilehdrGlobal->lgposLastMS.usGeneration;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* calculate the lgpos of the LR */
|
|
VOID PrintLgposReadLR ( VOID )
|
|
{
|
|
LGPOS lgpos;
|
|
|
|
GetLgposOfPbNext(&lgpos);
|
|
PrintF2("\n%2u,%3u,%3u",
|
|
plgfilehdrGlobal->lgposLastMS.usGeneration,
|
|
lgpos.isec, lgpos.ib);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Read first record pointed by plgposFirst.
|
|
* Initialize isecRead, pbRead, and pbNext.
|
|
*/
|
|
ERR ErrLGLocateFirstRedoLogRec(
|
|
LGPOS *plgposPrevMS,
|
|
LGPOS *plgposFirst, /* lgpos for first redo record */
|
|
BYTE **ppbLR)
|
|
{
|
|
ERR err;
|
|
char *pbNextT;
|
|
|
|
/* read first sector, actually we read 2 pages such that we can
|
|
* guarrantee that when calculate the length of the record, the
|
|
* fixed part of the record is read in.
|
|
*/
|
|
if (pbLGBufMin + 3 * cbSec > pbLGBufMax)
|
|
return JET_errLogBufferTooSmall;
|
|
|
|
if ( plgposPrevMS && plgposPrevMS->isec != 0 )
|
|
{
|
|
CallR(ErrLGRead(hfLog, plgposPrevMS->isec, pbLGBufMin, 1))
|
|
CallS(ErrLGRead(hfLog, plgposPrevMS->isec + 1, pbLGBufMin + cbSec, 1));
|
|
isecRead = plgposPrevMS->isec + 1; /* sector next to read. */
|
|
pbRead = pbLGBufMin + cbSec;
|
|
pbNext = pbLGBufMin + plgposPrevMS->ib;
|
|
pbLastMSFlush = pbNext;
|
|
lgposLastMSFlush = *plgposPrevMS;
|
|
}
|
|
else
|
|
{
|
|
CallR(ErrLGRead(hfLog, plgposFirst->isec, pbLGBufMin, 1))
|
|
CallS(ErrLGRead(hfLog, plgposFirst->isec + 1, pbLGBufMin + cbSec, 1));
|
|
isecRead = plgposFirst->isec + 1; /* sector next to read. */
|
|
pbRead = pbLGBufMin + cbSec;
|
|
pbNext = pbLGBufMin + plgposFirst->ib;
|
|
|
|
/* initialize global variables */
|
|
if ( lgposLastMSFlush.isec == plgposFirst->isec )
|
|
{
|
|
pbLastMSFlush = pbLGBufMin + lgposLastMSFlush.ib;
|
|
}
|
|
else
|
|
{
|
|
pbLastMSFlush = 0;
|
|
memset( &lgposLastMSFlush, 0, sizeof(lgposLastMSFlush) );
|
|
}
|
|
}
|
|
|
|
/* continue reading more sectors till next MS log record or
|
|
* a Fill log record is reached.
|
|
*/
|
|
pbNextT = pbNext;
|
|
while (*(LRTYP*)pbNextT != lrtypFill)
|
|
{
|
|
char *pbAligned;
|
|
|
|
/* goto next record */
|
|
pbNextT += (ULONG) CbLGSizeOfRec((LR*)pbNextT);
|
|
|
|
if (pbNextT > pbLGBufMax)
|
|
return JET_errLogFileCorrupt;
|
|
|
|
pbAligned = PbSecAligned(pbNextT);
|
|
if (pbAligned >= pbRead)
|
|
{
|
|
int csecToRead;
|
|
|
|
if ( plgposFirst->isec <= 2 &&
|
|
plgfilehdrGlobal->lgposLastMS.ib == 0 &&
|
|
plgfilehdrGlobal->lgposLastMS.isec <= 2 )
|
|
{
|
|
/* a special case where we tried to scan through this page and
|
|
* realize that no MS or fill record is read. Should not read
|
|
* beyond this page. Do not continue reading.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/* physically read one more page to guarrantee that
|
|
* the fix part of the log record is read in the
|
|
* memory.
|
|
*/
|
|
csecToRead = (int)(pbAligned - pbRead) / cbSec + 1;
|
|
if (pbRead + csecToRead * cbSec > pbLGBufMax)
|
|
return JET_errLogBufferTooSmall;
|
|
|
|
CallR( ErrLGRead(hfLog, isecRead, pbRead, csecToRead ))
|
|
isecRead += csecToRead;
|
|
pbRead += csecToRead * cbSec;
|
|
|
|
CallS( ErrLGRead(hfLog, isecRead, pbRead, 1 ));
|
|
}
|
|
|
|
/* reach next MS, break */
|
|
if ( *(LRTYP*)pbNextT == lrtypMS )
|
|
break;
|
|
}
|
|
|
|
if ( plgposPrevMS && plgposPrevMS->isec != 0 )
|
|
{
|
|
pbNext = pbLGBufMin + cbSec * ( plgposFirst->isec - plgposPrevMS->isec ); pbNext += plgposFirst->ib;
|
|
}
|
|
|
|
/* set up returned value. */
|
|
*ppbLR = pbNext;
|
|
|
|
return JET_errSuccess;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set pbNext to next available log record.
|
|
*/
|
|
ERR ErrLGGetNextRec( BYTE **ppbLR )
|
|
{
|
|
ERR err;
|
|
int cb;
|
|
char *pb;
|
|
LR *plr;
|
|
LGPOS lgposT;
|
|
BYTE *pbNextOld;
|
|
|
|
/* caller should have taken care of the Fill case. */
|
|
Assert (*(LRTYP *)pbNext != lrtypFill);
|
|
|
|
/* move to next log record. */
|
|
pbNextOld = pbNext;
|
|
pbNext += (ULONG) CbLGSizeOfRec((LR*)pbNext);
|
|
|
|
/* check if next log record is out of buffer range. */
|
|
|
|
if (pbNext == pbRead)
|
|
{
|
|
/* the record ends on the sector boundary */
|
|
pbNext = pbLGBufMin;
|
|
pbRead = pbNext;
|
|
|
|
/* read in one more page. */
|
|
if (pbLGBufMin + cbSec > pbLGBufMax)
|
|
return JET_errLogBufferTooSmall;
|
|
|
|
CallR(ErrLGRead(hfLog, isecRead, pbLGBufMin, 1))
|
|
isecRead += 1;
|
|
pbRead = pbLGBufMin + cbSec;
|
|
}
|
|
|
|
if (pbNext > pbRead)
|
|
{
|
|
pbNext = pbNextOld;
|
|
return errLGNoMoreRecords;
|
|
}
|
|
|
|
GetLgposOfPbNext(&lgposT);
|
|
if ( CmpLgpos( &lgposT, &lgposLastRec ) > 0 )
|
|
{
|
|
pbNext = pbNextOld;
|
|
return errLGNoMoreRecords;
|
|
}
|
|
|
|
plr = (LR *) pbNext;
|
|
|
|
if (plr->lrtyp == lrtypFill)
|
|
{
|
|
/* end of current log file. */
|
|
goto Done;
|
|
}
|
|
else if (plr->lrtyp == lrtypMS)
|
|
{
|
|
LRMS *plrms = (LRMS *)plr;
|
|
|
|
#ifdef DEBUG
|
|
// same as TraceRedo() in redo.c
|
|
if (fDBGTraceRedo)
|
|
{
|
|
PrintLgposReadLR();
|
|
ShowLR(plr);
|
|
}
|
|
#endif
|
|
|
|
/* check if this MS was done completely by reading
|
|
* the whole sector in. If it fails, then the sector
|
|
* is the last sector available in the log file.
|
|
*/
|
|
fOnLastSec = ( plrms->isecForwardLink == 0 ||
|
|
( lgposLastMSFlush.isec != 0 &&
|
|
(
|
|
plrms->isecBackLink != lgposLastMSFlush.isec ||
|
|
plrms->ibBackLink != lgposLastMSFlush.ib
|
|
) ) );
|
|
|
|
/* The MS were read in successfully, reset LastMSFlush
|
|
* so that when switching from read mode to write mode,
|
|
* we will have a correct LastMSFlush pointers.
|
|
*/
|
|
pbLastMSFlush = (CHAR *) plrms;
|
|
lgposLastMSFlush = lgposT;
|
|
|
|
if ( !fOnLastSec )
|
|
{
|
|
if (isecRead <= plrms->isecForwardLink)
|
|
{
|
|
int csecToRead = plrms->isecForwardLink - isecRead + 1;
|
|
|
|
Assert( csecToRead > 0 );
|
|
|
|
pb = PbSecAligned(pbNext);
|
|
cb = (int)(pbRead - pb);
|
|
if (csecToRead + isecRead > csecLGBuf)
|
|
{
|
|
/* the multiple sector will not fit in rest of */
|
|
/* the available buffer. Shift the buffer. */
|
|
memmove(pbLGBufMin, pb, cb);
|
|
|
|
pbRead = pbLGBufMin + cb; /* pbRead */
|
|
pbNext = pbNext - pb + pbLGBufMin; /* pbNext */
|
|
pbLastMSFlush = (CHAR *) plrms - pb + pbLGBufMin;
|
|
}
|
|
|
|
/* bring in multiple sectors */
|
|
if (pbRead + csecToRead * cbSec > pbLGBufMax)
|
|
{
|
|
BYTE *pbLGBufMinT = pbLGBufMin;
|
|
CallR( ErrLGInitLogBuffers( lLogBuffers ) );
|
|
memcpy( pbLGBufMin, pbLGBufMinT, cb );
|
|
|
|
pbRead = pbRead - pbLGBufMinT + pbLGBufMin;
|
|
pbNext = pbNext - pbLGBufMinT + pbLGBufMin;
|
|
pbLastMSFlush = pbLastMSFlush - pbLGBufMinT + pbLGBufMin;
|
|
|
|
SysFree( pbLGBufMinT );
|
|
}
|
|
|
|
err = ErrLGRead(hfLog, isecRead, pbRead, csecToRead);
|
|
if (err < 0)
|
|
fOnLastSec = fTrue;
|
|
else
|
|
{
|
|
/* Get pb of new lrms
|
|
/*/
|
|
CHAR *pbLrmsNew = pbRead + ( csecToRead - 1 ) * cbSec + ((LRMS *)pbLastMSFlush)->ibForwardLink;
|
|
LRMS *plrmsNew = (LRMS *) pbLrmsNew;
|
|
|
|
/* check if the check sum is correct
|
|
/*/
|
|
if ( plrmsNew->ulCheckSum != UlLGMSCheckSum( pbLrmsNew ) )
|
|
fOnLastSec = fTrue;
|
|
else
|
|
{
|
|
isecRead += csecToRead;
|
|
pbRead += csecToRead * cbSec;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* skip MS and continue to read next record. */
|
|
pbNextOld = pbNext;
|
|
pbNext += CbLGSizeOfRec((LR*)pbNext);
|
|
|
|
/* nomal end of generation */
|
|
if ( fOnLastSec && plgfilehdrGlobal->fEndWithMS )
|
|
return errLGNoMoreRecords;
|
|
|
|
/* or abnormal end of log file */
|
|
if ( fOnLastSec && PbSecAligned(pbNextOld) != PbSecAligned(pbNext))
|
|
{
|
|
pbNext = pbNextOld;
|
|
return errLGNoMoreRecords;
|
|
}
|
|
}
|
|
Done:
|
|
*ppbLR = pbNext;
|
|
return JET_errSuccess;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// CbLGSizeOfRec
|
|
// =======================================================================
|
|
//
|
|
// ERR CbLGSizeOfRec( plgrec )
|
|
//
|
|
// Returns the length of a log record.
|
|
//
|
|
// PARAMETER plgrec pointer to log record
|
|
//
|
|
// RETURNS size of log record in bytes
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
INT mplrtypcb[ lrtypMax ] = {
|
|
/* 0 NOP */ sizeof( LRTYP ),
|
|
/* 1 Start */ 0,
|
|
/* 2 Quit */ 0,
|
|
/* 3 MS */ sizeof( LRMS ),
|
|
/* 4 Fill */ sizeof( LRTYP ),
|
|
|
|
/* 5 Begin */ sizeof( LRBEGIN ),
|
|
/* 6 Commit */ sizeof( LRCOMMIT ),
|
|
/* 7 Abort */ sizeof( LRABORT ),
|
|
|
|
/* 8 CreateDB */ 0,
|
|
/* 9 AttachDB */ 0,
|
|
/* 10 DetachDB */ 0,
|
|
|
|
/* 11 InitFDP */ sizeof( LRINITFDPPAGE ),
|
|
|
|
/* 12 Split */ 0,
|
|
/* 13 EmptyPage*/ sizeof( LREMPTYPAGE ),
|
|
/* 14 PageMerge*/ 0,
|
|
|
|
/* 15 InsertND */ 0,
|
|
/* 16 InsertIL */ 0,
|
|
/* 17 Replace */ 0,
|
|
/* 18 ReplaceC */ 0,
|
|
/* 19 FDelete */ sizeof( LRFLAGDELETE ),
|
|
/* 20 LockRec */ 0,
|
|
|
|
/* 21 UpdtHdr */ sizeof( LRUPDATEHEADER ),
|
|
/* 22 InsertI */ sizeof( LRINSERTITEM ),
|
|
/* 23 InsertIS */ 0,
|
|
/* 24 FDeleteI */ sizeof( LRFLAGITEM ),
|
|
/* 25 FInsertI */ sizeof( LRFLAGITEM ),
|
|
/* 26 DeleteI */ sizeof( LRDELETEITEM ),
|
|
/* 27 SplitItm */ sizeof( LRSPLITITEMLISTNODE ),
|
|
|
|
/* 28 Delta */ sizeof( LRDELTA ),
|
|
|
|
/* 29 DelNode */ sizeof( LRDELETE ),
|
|
/* 30 ELC */ sizeof( LRELC ),
|
|
|
|
/* 31 FreeSpace*/ sizeof( LRFREESPACE ),
|
|
/* 32 Undo */ sizeof( LRUNDO ),
|
|
/* 33 RcvrUndo1*/ 0,
|
|
/* 34 RcvrQuit1*/ 0,
|
|
/* 35 RcvrUndo2*/ 0,
|
|
/* 36 RcvrQuit2*/ 0,
|
|
/* 37 FullBkUp */ 0,
|
|
/* 38 IncBkUp */ 0,
|
|
/* 39 CheckPage */ sizeof( LRCHECKPAGE ),
|
|
};
|
|
|
|
|
|
INT CbLGSizeOfRec( LR *plr )
|
|
{
|
|
INT cb;
|
|
|
|
Assert( plr->lrtyp < lrtypMax );
|
|
|
|
if ( ( cb = mplrtypcb[plr->lrtyp] ) != 0 )
|
|
return cb;
|
|
|
|
switch ( plr->lrtyp )
|
|
{
|
|
case lrtypStart:
|
|
return sizeof(LRSTART);
|
|
|
|
case lrtypQuit:
|
|
case lrtypRecoveryQuit1:
|
|
case lrtypRecoveryQuit2:
|
|
return sizeof(LRQUITREC);
|
|
|
|
case lrtypRecoveryUndo1:
|
|
case lrtypRecoveryUndo2:
|
|
case lrtypFullBackup:
|
|
case lrtypIncBackup:
|
|
{
|
|
LRLOGRESTORE *plrlogrestore = (LRLOGRESTORE *) plr;
|
|
return sizeof(LRLOGRESTORE) + plrlogrestore->cbPath;
|
|
}
|
|
|
|
case lrtypCreateDB:
|
|
{
|
|
LRCREATEDB *plrcreatedb = (LRCREATEDB *)plr;
|
|
Assert( plrcreatedb->cb != 0 );
|
|
return sizeof(LRCREATEDB) + plrcreatedb->cb;
|
|
}
|
|
case lrtypAttachDB:
|
|
{
|
|
LRATTACHDB *plrattachdb = (LRATTACHDB *)plr;
|
|
Assert( plrattachdb->cb != 0 );
|
|
return sizeof(LRATTACHDB) + plrattachdb->cb;
|
|
}
|
|
case lrtypDetachDB:
|
|
{
|
|
LRDETACHDB *plrdetachdb = (LRDETACHDB *)plr;
|
|
Assert( plrdetachdb->cb != 0 );
|
|
return sizeof( LRDETACHDB ) + plrdetachdb->cb;
|
|
}
|
|
case lrtypSplit:
|
|
{
|
|
LRSPLIT *plrsplit = (LRSPLIT *) plr;
|
|
return sizeof( LRSPLIT ) + plrsplit->cbKey + plrsplit->cbKeyMac +
|
|
sizeof( BKLNK ) * plrsplit->cbklnk;
|
|
}
|
|
case lrtypMerge:
|
|
{
|
|
LRMERGE *plrmerge = (LRMERGE *) plr;
|
|
return sizeof( LRMERGE ) + sizeof( BKLNK ) * plrmerge->cbklnk;
|
|
}
|
|
case lrtypInsertNode:
|
|
case lrtypInsertItemList:
|
|
{
|
|
LRINSERTNODE *plrinsertnode = (LRINSERTNODE *) plr;
|
|
return sizeof(LRINSERTNODE) +
|
|
plrinsertnode->cbKey + plrinsertnode->cbData;
|
|
}
|
|
case lrtypInsertItems:
|
|
{
|
|
LRINSERTITEMS *plrinsertitems = (LRINSERTITEMS *) plr;
|
|
return sizeof(LRINSERTITEMS) +
|
|
plrinsertitems->citem * sizeof(ITEM);
|
|
}
|
|
case lrtypReplace:
|
|
case lrtypReplaceC:
|
|
{
|
|
LRREPLACE *plrreplace = (LRREPLACE *) plr;
|
|
return sizeof(LRREPLACE) + plrreplace->cb +
|
|
( plrreplace->fOld ? plrreplace->cbOldData : 0 );
|
|
}
|
|
case lrtypLockRec:
|
|
{
|
|
LRLOCKREC *plrlockrec = (LRLOCKREC *) plr;
|
|
return sizeof(LRLOCKREC) + plrlockrec->cbOldData;
|
|
}
|
|
default:
|
|
Assert( fFalse );
|
|
}
|
|
return 0;
|
|
}
|
|
|