Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1102 lines
31 KiB

#include "config.h"
#include <string.h>
#include <stdlib.h>
#include "daedef.h"
#include "util.h"
#include "pib.h"
#include "fmp.h"
#include "page.h"
#include "ssib.h"
#include "fcb.h"
#include "fucb.h"
#include "stapi.h"
#include "nver.h"
#include "dirapi.h"
#include "fdb.h"
#include "idb.h"
#include "spaceapi.h"
#include "recapi.h"
#include "recint.h"
#include "logapi.h"
DeclAssertFile; /* Declare file name for assert macros */
/**************************** INTERNAL STUFF ***************************/
typedef struct ATIPB { /*** AddToIndexParameterBlock ***/
FUCB *pfucb;
FUCB *pfucbIdx; // index's FUCB (can be pfucbNil)
LINE lineNewData; // data to extract key from
SRID srid; // srid of data record
BOOL fFreeFUCB; // free index FUCB?
} ATIPB;
/* define semaphore to guard dbk counter
/**/
SgSemDefine( semDBK );
typedef struct UIPB { /*** UpdateIndexParameterBlock ***/
FUCB *pfucb;
FUCB *pfucbIdx; // index's FUCB (can be pfucbNil)
LINE lineOldData; // old data record
SRID srid; // SRID of record
LINE lineNewData; // new data record
BOOL fOpenFUCB; // open index FUCB?
BOOL fFreeFUCB; // free index FUCB?
} UIPB;
INLINE LOCAL ERR ErrRECInsert( PIB *ppib, FUCB *pfucb, SRID *psrid );
INLINE LOCAL ERR ErrRECIAddToIndex( FCB *pfcbIdx, ATIPB *patipb );
INLINE LOCAL ERR ErrRECReplace( PIB *ppib, FUCB *pfucb );
INLINE LOCAL ERR ErrRECIUpdateIndex( FCB *pfcbIdx, UIPB *puipb );
ERR VTAPI
ErrIsamUpdate( PIB *ppib, FUCB *pfucb, BYTE *pb, ULONG cbMax, ULONG *pcbActual )
{
ERR err;
SRID srid;
CheckPIB( ppib );
CheckTable( ppib, pfucb );
CheckNonClustered( pfucb );
if ( pcbActual != NULL )
*pcbActual = sizeof(srid);
if ( FFUCBReplacePrepared( pfucb ) )
{
if ( cbMax > 0 )
{
FUCBSetGetBookmark( pfucb );
CallR( ErrDIRGetBookmark( pfucb, &srid ) );
memcpy( pb, &srid, min( cbMax, sizeof(srid) ) );
}
err = ErrRECReplace( ppib, pfucb );
}
else if ( FFUCBInsertPrepared( pfucb ) )
{
err = ErrRECInsert( ppib, pfucb, &srid );
if ( pb != NULL && cbMax > 0 && err >= 0 )
{
FUCBSetGetBookmark( pfucb );
memcpy( pb, &srid, min( cbMax, sizeof(srid) ) );
}
}
else
err = JET_errUpdateNotPrepared;
Assert( err != errDIRNotSynchronous );
return err;
}
//+local
// ErrRECInsert
// ========================================================================
// ErrRECInsert( PIB *ppib, FUCB *pfucb, OUTLINE *plineBookmark )
//
// Adds a record to a data file. All indexes on the data file are
// updated to reflect the addition.
//
// PARAMETERS ppib PIB of user
// pfucb FUCB for file
// plineBookmark if this parameter is not NULL,
// then bookmark of record is returned
//
// RETURNS Error code, one of the following:
// JET_errSuccess Everything went OK.
// -KeyDuplicate The record being added causes
// an illegal duplicate entry in an index.
// -NullKeyDisallowed A key of the new record is NULL.
// -RecordNoCopy There is no working buffer to add from.
// -NullInvalid The record being added contains
// at least one null-valued field
// which is defined as NotNull.
// SIDE EFFECTS
// After addition, file currency is left on the new record.
// Index currency (if any) is left on the new index entry.
// On failure, the currencies are returned to their initial states.
//
// COMMENTS
// No currency is needed to add a record.
// Index entries are not made for entirely-null keys.
// A transaction is wrapped around this function. Thus, any
// work done will be undone if a failure occurs.
// Adding a record to a sequential file increments the
// file's highest Database Key (DBK) and uses that DBK as
// the key for the new record. However, if the PUT fails,
// the max DBK is not reset to its former value. This can
// create gaps in the DBK sequence.
// This routine is also the interface for adding record to a
// SORT process. The sort key is extracted and passed with
// the data record to SORTAdd.
// For temporary files, transaction logging is deactivated
// for the duration of the routine.
//-
INLINE LOCAL ERR ErrRECInsert( PIB *ppib, FUCB *pfucb, SRID *psrid )
{
ERR err = JET_errSuccess; // error code of various utility
KEY keyToAdd; // key of new data record
BYTE rgbKeyBuf[ JET_cbKeyMost ]; // key buffer
FCB *pfcbFile; // file's FCB
FDB *pfdb; // field descriptor info
FCB *pfcbIdx; // loop variable for each index on file
ATIPB atipb; // parm block to ErrRECIAddToIndex
FUCB *pfucbT;
LINE *plineData;
DBK dbk;
LINE line;
ULONG ulRecordAutoIncrement;
ULONG ulTableAutoIncrement;
BOOL fPrepareInsertIndex = fFalse;
BOOL fCommit = fFalse;
CheckPIB( ppib );
CheckTable( ppib, pfucb );
CheckNonClustered( pfucb );
/* should have been checked in PrepareUpdate
/**/
Assert( FFUCBUpdatable( pfucb ) );
Assert( FFUCBInsertPrepared( pfucb ) );
/* assert reset rglineDiff delta logging
/**/
Assert( pfucb->clineDiff == 0 );
Assert( pfucb->fCmprsLg == fFalse );
/* efficiency variables
/**/
pfcbFile = pfucb->u.pfcb;
Assert( pfcbFile != pfcbNil );
pfdb = (FDB *)pfcbFile->pfdb;
Assert( pfdb != pfdbNil );
/* record to use for put
/**/
plineData = &pfucb->lineWorkBuf;
Assert( !( FLineNull( plineData ) ) );
if ( FRECIIllegalNulls( pfdb, plineData ) )
return JET_errNullInvalid;
/* if necessary, start a transaction in case anything fails
/**/
if ( ppib->level == 0 || !FPIBAggregateTransaction( ppib ) )
{
CallR( ErrDIRBeginTransaction( ppib ) );
fCommit = fTrue;
}
/* open temp FUCB on data file
/**/
CallJ( ErrDIROpen( ppib, pfcbFile, 0, &pfucbT ), Abort );
Assert(pfucbT != pfucbNil);
FUCBSetIndex( pfucbT );
/* abort if index is being built on file
/**/
if ( FFCBDenyDDL( pfcbFile, ppib ) )
{
err = JET_errWriteConflict;
goto HandleError;
}
/* set version and autoinc fields
/**/
Assert( pfcbFile != pfcbNil );
if ( pfdb->fidVersion != 0 && ! ( FFUCBColumnSet( pfucb, pfdb->fidVersion - fidFixedLeast ) ) )
{
LINE lineField;
ULONG ul = 0;
/* set field to zero
/**/
lineField.pb = (BYTE *)&ul;
lineField.cb = sizeof(ul);
Call( ErrRECIModifyField( pfucb, pfdb->fidVersion, 0, &lineField ) );
}
if ( pfdb->fidAutoInc != 0 )
{
Assert( FFUCBColumnSet( pfucb, pfdb->fidAutoInc - fidFixedLeast ) );
// get the value of autoinc that the user sets to
Call( ErrRECIRetrieve( pfucb, &pfdb->fidAutoInc, 0, &line, JET_bitRetrieveCopy ) );
Assert( line.cb == sizeof(ulRecordAutoIncrement) );
ulRecordAutoIncrement = *(UNALIGNED ULONG *)line.pb;
/* move to FDP root and seek to autoincrement
/**/
DIRGotoFDPRoot( pfucbT );
err = ErrDIRSeekPath( pfucbT, 1, pkeyAutoInc, fDIRPurgeParent );
if ( err != JET_errSuccess )
{
if ( err > 0 )
err = JET_errDatabaseCorrupted;
goto HandleError;
}
Call( ErrDIRGet( pfucbT ) );
Assert( pfucbT->lineData.cb == sizeof(ulTableAutoIncrement) );
ulTableAutoIncrement = *(UNALIGNED ULONG *)pfucbT->lineData.pb;
Assert( ulTableAutoIncrement != 0 );
/* update FDP autoinc to be one greater than set value.
/**/
if ( ulRecordAutoIncrement >= ulTableAutoIncrement)
{
ulTableAutoIncrement = ulRecordAutoIncrement + 1;
line.pb = (BYTE *)&ulTableAutoIncrement;
line.cb = sizeof(ulTableAutoIncrement);
Call( ErrDIRReplace( pfucbT, &line, fDIRNoVersion ) );
}
}
/* get key to add with new record
/**/
keyToAdd.pb = rgbKeyBuf;
if ( pfcbFile->pidb == pidbNil )
{
/* file is sequential
/**/
SgSemRequest( semDBK );
dbk = ++pfcbFile->dbkMost;
SgSemRelease( semDBK );
keyToAdd.cb = sizeof(DBK);
keyToAdd.pb[0] = (BYTE)((dbk >> 24) & 0xff);
keyToAdd.pb[1] = (BYTE)((dbk >> 16) & 0xff);
keyToAdd.pb[2] = (BYTE)((dbk >> 8) & 0xff);
keyToAdd.pb[3] = (BYTE)(dbk & 0xff);
}
else
{
/* file is clustered
/**/
Call( ErrRECExtractKey( pfucbT, pfdb, pfcbFile->pidb, plineData, &keyToAdd, 1 ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( ( pfcbFile->pidb->fidb & fidbNoNullSeg ) && ( err == wrnFLDNullKey || err == wrnFLDNullSeg ) )
Error( JET_errNullKeyDisallowed, HandleError )
}
/* insert record. Move to DATA root.
/**/
DIRGotoDataRoot( pfucbT );
if ( pfcbFile->pidb == pidbNil )
{
/* file is sequential
/**/
Call( ErrDIRInsert( pfucbT, plineData, &keyToAdd,
fDIRVersion | fDIRPurgeParent ) );
}
else
{
Call( ErrDIRInsert( pfucbT, plineData, &keyToAdd,
fDIRVersion | fDIRPurgeParent |
( pfcbFile->pidb->fidb&fidbUnique ? 0 : fDIRDuplicate ) ) );
}
/* return bookmark of inserted record
/**/
DIRGetBookmark( pfucbT, psrid );
/* insert item in non-clustered indexes
/**/
for ( pfcbIdx = pfcbFile->pfcbNextIndex;
pfcbIdx != pfcbNil;
pfcbIdx = pfcbIdx->pfcbNextIndex )
{
if ( !fPrepareInsertIndex )
{
/* get SRID of inserted record
/**/
DIRGetBookmark( pfucbT, &atipb.srid );
/* set atipb for index insertion.
/**/
atipb.pfucb = pfucbT;
atipb.lineNewData = *plineData;
// atipb.pfucbIdx = pfucbNil;
atipb.fFreeFUCB = fFalse;
fPrepareInsertIndex = fTrue;
}
atipb.fFreeFUCB = pfcbIdx->pfcbNextIndex == pfcbNil;
Call( ErrRECIAddToIndex( pfcbIdx, &atipb ) );
}
/* commit transaction
/**/
if ( fCommit )
{
Call( ErrDIRCommitTransaction( ppib ) );
}
FUCBResetUpdateSeparateLV( pfucb );
FUCBResetCbstat( pfucb );
/* discard temp FUCB
/**/
DIRClose( pfucbT );
return err;
HandleError:
Assert( err < 0 );
DIRClose( pfucbT );
Abort:
if ( fCommit )
CallS( ErrDIRRollback( ppib ) );
return err;
}
//+local
// ErrRECIAddToIndex
// ========================================================================
// ERR ErrRECIAddToIndex( FCB *pfcbIdx, ATIPB patipb )
//
// Extracts key from data record, opens the index, adds that key with
// the given SRID to the index, and closes the index.
//
// PARAMETERS pfcbIdx FCB of index to insert into
// patipb->ppib who is calling this routine
// patipb->pfucbIdx pointer to index's FUCB. If pfucbNil,
// an FUCB will be allocated by DIROpen.
// patipb->lineNewData.cb length of data record
// patipb->lineNewData.pb data to extract key from
// patipb->srid SRID of data record
// patipb->fFreeFUCB free index FUCB?
//
// RETURNS JET_errSuccess, or error code from failing routine
//
// SIDE EFFECTS if patipb->pfucbIdx==pfucbNil, ErrDIROpen will allocate
// an FUCB and it will be pointed at it.
// If fFreeFUCB is fFalse, patipb->pfucbIdx should
// be used in a subsequent ErrDIROpen.
// SEE ALSO Insert
//-
INLINE LOCAL ERR ErrRECIAddToIndex( FCB *pfcbIdx, ATIPB *patipb )
{
ERR err = JET_errSuccess; // error code of various utility
CSR **ppcsrIdx; // index's currency
KEY keyToAdd; // key to add to secondary index
BYTE rgbKeyBuf[ JET_cbKeyMost ]; // key extracted from data
LINE lineSRID; // SRID to add to index
ULONG itagSequence; // used to extract keys
ULONG ulAddFlags; // flags to DIRAdd
BOOL fNullKey = fFalse; // extracted NullTaggedKey -- so no more keys to extract
Assert( pfcbIdx != pfcbNil );
Assert( pfcbIdx->pfdb != pfdbNil );
Assert( pfcbIdx->pidb != pidbNil );
Assert( patipb != NULL );
Assert( !FLineNull( &patipb->lineNewData ) );
Assert( patipb->pfucb != pfucbNil );
Assert( patipb->pfucb->ppib != ppibNil );
Assert( patipb->pfucb->ppib->level < levelMax );
/* open FUCB on this index
/**/
CallR( ErrDIROpen( patipb->pfucb->ppib, pfcbIdx, 0, &patipb->pfucbIdx ) )
Assert( patipb->pfucbIdx != pfucbNil );
/* cursor on non-clustering index
/**/
FUCBSetIndex( patipb->pfucbIdx );
FUCBSetNonClustered( patipb->pfucbIdx );
ppcsrIdx = &PcsrCurrent( patipb->pfucbIdx );
Assert( *ppcsrIdx != pcsrNil );
lineSRID.cb = sizeof(SRID);
lineSRID.pb = (BYTE *) &patipb->srid;
ulAddFlags = ( pfcbIdx->pidb->fidb&fidbUnique ?
0 : fDIRDuplicate ) | fDIRPurgeParent;
/* add all keys for this index from new data record
/**/
keyToAdd.pb = rgbKeyBuf;
for ( itagSequence = 1; ; itagSequence++ )
{
Call( ErrDIRGet( patipb->pfucb ) );
patipb->lineNewData = patipb->pfucb->lineData;
Call( ErrRECExtractKey( patipb->pfucb, (FDB *)pfcbIdx->pfdb, pfcbIdx->pidb, &patipb->lineNewData, &keyToAdd, itagSequence ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( err == wrnFLDOutOfKeys )
{
Assert( itagSequence > 1 );
break;
}
if ( ( pfcbIdx->pidb->fidb & fidbNoNullSeg ) && ( err == wrnFLDNullKey || err == wrnFLDNullSeg ) )
{
err = JET_errNullKeyDisallowed;
goto HandleError;
}
if ( err == wrnFLDNullKey )
{
if ( pfcbIdx->pidb->fidb & fidbAllowAllNulls )
{
ulAddFlags |= fDIRDuplicate;
fNullKey = fTrue;
}
else
break;
}
else
{
if ( err == wrnFLDNullSeg && !( pfcbIdx->pidb->fidb & fidbAllowSomeNulls ) )
break;
}
/* move to DATA root and insert index node
/**/
DIRGotoDataRoot( patipb->pfucbIdx );
Call( ErrDIRInsert( patipb->pfucbIdx, &lineSRID, &keyToAdd, fDIRVersion | ulAddFlags ) )
/* dont keep extracting for keys with no tagged segments
/**/
if ( !( pfcbIdx->pidb->fidb & fidbHasMultivalue ) || fNullKey )
break;
}
/* supress warnings
/**/
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
err = JET_errSuccess;
HandleError:
/* close the FUCB
/**/
DIRClose( patipb->pfucbIdx );
patipb->pfucbIdx = pfucbNil;
Assert( err < 0 || err == JET_errSuccess );
return err;
}
//+local
// ErrRECReplace
// ========================================================================
// ErrRECReplace( PIB *ppib, FUCB *pfucb )
//
// Updates a record in a data file. All indexes on the data file are
// updated to reflect the updated data record.
//
// PARAMETERS ppib PIB of this user
// pfucb FUCB for file
// RETURNS Error code, one of the following:
// JET_errSuccess Everything went OK.
// -NoCurrentRecord There is no current record
// to update.
// -RecordNoCopy There is no working buffer
// to update from.
// -KeyDuplicate The new record data causes an
// illegal duplicate index entry
// to be generated.
// -RecordClusteredChanged The new data causes the clustered
// key to change.
// SIDE EFFECTS
// After update, file currency is left on the updated record.
// Similar for index currency.
// The effect of a GetNext or GetPrevious operation will be
// the same in either case. On failure, the currencies are
// returned to their initial states.
// If there is a working buffer for SetField commands,
// it is discarded.
//
// COMMENTS
// If currency is not ON a record, the update will fail.
// A transaction is wrapped around this function. Thus, any
// work done will be undone if a failure occurs.
// For temporary files, transaction logging is deactivated
// for the duration of the routine.
// Index entries are not made for entirely-null keys.
//-
INLINE LOCAL ERR ErrRECReplace( PIB *ppib, FUCB *pfucb )
{
ERR err = JET_errSuccess; // error code of various utility
FCB *pfcbFile; // file's FCB
FCB *pfcbIdx; // loop variable for each index on file
FCB *pfcbCurIdx; // FCB of current index (if any)
IDB *pidbFile; // IDB of table (if any)
UIPB uipb; // parameter block to ErrRECIUpdateIndex
BOOL fTaggedChanged; // SetField done on any tagged field?
BF *pbf = pbfNil;
LINE *plineNewData;
FID fidFixedLast;
FID fidVarLast;
FID fid;
BOOL fUpdateIndex;
BOOL fCommit = fFalse;
INT fFlags;
CheckPIB( ppib );
CheckTable( ppib, pfucb );
CheckNonClustered( pfucb );
/* should have been checked in PrepareUpdate
/**/
Assert( FFUCBUpdatable( pfucb ) );
Assert( FFUCBReplacePrepared( pfucb ) );
/* efficiency variables
/**/
fTaggedChanged = FFUCBTaggedSet( pfucb );
pfcbFile = pfucb->u.pfcb;
Assert( pfcbFile != pfcbNil );
fidFixedLast = pfcbFile->pfdb->fidFixedLast;
fidVarLast = pfcbFile->pfdb->fidVarLast;
/* must initialize pfucb for error handling.
/**/
uipb.pfucbIdx = pfucbNil;
/* record to use for update
/**/
plineNewData = &pfucb->lineWorkBuf;
Assert( !( FLineNull( plineNewData ) ) );
/* start a transaction in case anything fails
/**/
if ( ppib->level == 0 || !FPIBAggregateTransaction( ppib ) )
{
CallR( ErrDIRBeginTransaction( ppib ) );
fCommit = fTrue;
}
/* optimistic locking -- ensure that record has not changed since PrepareUpdate
/**/
if ( FFUCBReplaceNoLockPrepared( pfucb ) )
{
Call( ErrDIRGet( pfucb ) );
if ( !FChecksum( pfucb ) )
Call( JET_errWriteConflict );
}
/* abort if index is being built on file
/**/
if ( FFCBDenyDDL( pfcbFile, ppib ) )
{
Call( JET_errWriteConflict );
}
/* if need to update indexes, then cache old record.
/**/
fUpdateIndex = ( pfcbFile->fAllIndexTagged && fTaggedChanged ) ||
FIndexedFixVarChanged( pfcbFile->rgbitAllIndex,
pfucb->rgbitSet, fidFixedLast, fidVarLast );
if ( fUpdateIndex )
{
/* get a temp buffer to hold old data.
/**/
Call( ErrBFAllocTempBuffer( &pbf ) );
Assert( pbf->ppage != 0 );
uipb.lineOldData.pb = (BYTE*)pbf->ppage;
/* refresh currency.
/**/
Call( ErrDIRGet( pfucb ) );
/* copy old data for index update.
/**/
LineCopy( &uipb.lineOldData, &pfucb->lineData );
/* make sure clustered key did not change.
/**/
pidbFile = pfcbFile->pidb;
if ( pidbFile != pidbNil )
{
/*
* Quick check for unchanged key: if no fixed or var index segment
* has changed, and either (1) there are no tagged index segments,
* or (2) there is a tagged segment, but no tagged field (indexed
* or not) has changed, then the key has not changed.
*/
if ( ( ( pidbFile->fidb & fidbHasTagged ) && fTaggedChanged ) ||
FIndexedFixVarChanged( pidbFile->rgbitIdx, pfucb->rgbitSet, fidFixedLast, fidVarLast ) )
{
KEY keyOld;
KEY keyNew;
BYTE rgbOldKeyBuf[ JET_cbKeyMost ];
BYTE rgbNewKeyBuf[ JET_cbKeyMost ];
Assert( fUpdateIndex );
/* get new key from copy buffer
/**/
keyNew.pb = rgbNewKeyBuf;
Call( ErrRECExtractKey( pfucb, (FDB *)pfcbFile->pfdb, pidbFile, plineNewData, &keyNew, 1 ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
/* get the old key from the node
/**/
keyOld.pb = rgbOldKeyBuf;
Call( ErrRECExtractKey( pfucb, (FDB *)pfcbFile->pfdb, pidbFile, &uipb.lineOldData, &keyOld, 1 ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
/* record must honor index no NULL segment requirements
/**/
Assert( !( pidbFile->fidb & fidbNoNullSeg ) ||
( err != wrnFLDNullSeg && err != wrnFLDNullKey ) );
if ( keyOld.cb != keyNew.cb || memcmp( keyOld.pb, keyNew.pb, keyOld.cb ) != 0 )
{
Error( JET_errRecordClusteredChanged, HandleError )
}
}
}
}
/* set autoinc and version fields if they are present
/**/
Assert( FFUCBIndex( pfucb ) );
fid = pfcbFile->pfdb->fidVersion;
if ( fid != 0 )
{
LINE lineField;
ULONG ul;
/* increment field from value in current record
/**/
Call( ErrRECIRetrieve( pfucb, &fid, 0, &lineField, 0 ) );
/* handle case where field is NULL when column added
/* to table with records present
/**/
if ( lineField.cb == 0 )
{
ul = 1;
lineField.cb = sizeof(ul);
lineField.pb = (BYTE *)&ul;
}
else
{
Assert( lineField.cb == sizeof(ULONG) );
++*(UNALIGNED ULONG *)lineField.pb;
}
Call( ErrRECIModifyField( pfucb, fid, 0, &lineField ) );
}
/* replace data.
/* Do not version if before image already exists and record size
/* has not changed.
/**/
// UNDONE: reenable optimization on not versioning when already
// versioned for pessomistic locking but with indication
// to loggging recovery for rollback.
// if ( FFUCBReplaceNoLockPrepared( pfucb ) )
// {
fFlags = fDIRVersion;
// }
// else
// {
// Assert( pfucb->cbRecord > 0 );
// if ( pfucb->cbRecord != plineNewData->cb )
// fFlags = fDIRVersion;
// else
// fFlags = 0;
// }
/* if updated separate long values then delete
/* removed long values.
/**/
if ( FFUCBUpdateSeparateLV( pfucb ) )
{
Call( ErrRECAffectLongFields( pfucb, NULL, fDereferenceRemoved ) );
}
Call( ErrDIRReplace( pfucb, plineNewData, fFlags ) );
/* update indexes
/**/
if ( fUpdateIndex )
{
uipb.pfucb = pfucb;
uipb.lineNewData = *plineNewData;
uipb.fOpenFUCB = fTrue;
uipb.fFreeFUCB = fFalse;
/* get SRID of record
/**/
DIRGetBookmark( pfucb, &uipb.srid );
pfcbCurIdx = pfucb->pfucbCurIndex != pfucbNil ? pfucb->pfucbCurIndex->u.pfcb : pfcbNil;
for ( pfcbIdx = pfcbFile->pfcbNextIndex;
pfcbIdx != pfcbNil;
pfcbIdx = pfcbIdx->pfcbNextIndex )
{
if ( ( pfcbIdx->pidb->fidb & fidbHasTagged && fTaggedChanged ) ||
FIndexedFixVarChanged( pfcbIdx->pidb->rgbitIdx,
pfucb->rgbitSet,
fidFixedLast,
fidVarLast ) )
{
Call( ErrRECIUpdateIndex( pfcbIdx, &uipb ) );
}
}
}
/* commit transaction
/**/
if ( fCommit )
{
Call( ErrDIRCommitTransaction( ppib ) );
}
FUCBResetUpdateSeparateLV( pfucb );
FUCBResetCbstat( pfucb );
/* reset rglineDiff delta logging
/**/
pfucb->clineDiff = 0;
pfucb->fCmprsLg = fFalse;
HandleError:
if ( uipb.pfucbIdx != pfucbNil )
DIRClose( uipb.pfucbIdx );
if ( pbf != pbfNil )
BFSFree( pbf );
/* rollback if necessary
/**/
if ( err < 0 && fCommit )
{
CallS( ErrDIRRollback( ppib ) );
}
return err;
}
BOOL FIndexedFixVarChanged( BYTE *rgbitIdx, BYTE *rgbitSet, FID fidFixedLast, FID fidVarLast )
{
LONG *plIdx;
LONG *plIdxMax;
LONG *plSet;
/* check fixed fields (only those defined)
/**/
plIdx = (LONG *)rgbitIdx;
plSet = (LONG *)rgbitSet;
plIdxMax = plIdx + ( fidFixedLast + 31 ) / 32;
while ( plIdx < plIdxMax && !( *plIdx & *plSet ) )
plIdx++, plSet++;
/* indexed fixed field changed
/**/
if ( plIdx < plIdxMax )
return fTrue;
/* check variable fields (only those defined)
/**/
plIdx = (LONG *)rgbitIdx + 4;
plSet = (LONG *)rgbitSet + 4;
plIdxMax = plIdx + ( fidVarLast - ( fidVarLeast - 1 ) + 31 ) / 32;
while ( plIdx < plIdxMax && !( *plIdx & *plSet ) )
plIdx++, plSet++;
return ( plIdx < plIdxMax );
}
//+local
// ErrRECIUpdateIndex
// ========================================================================
// ERR ErrRECIUpdateIndex(pfcbIdx, puipb)
// FCB *pfcbIdx; // IN FCB of index to insert into
// UIPB *puipb; // INOUT parameter block
// Extracts keys from old and new data records, and if they are different,
// opens the index, adds the new index entry, deletes the old index entry,
// and closes the index.
//
// PARAMETERS
// pfcbIdx FCB of index to insert into
// puipb->ppib who is calling this routine
// puipb->pfucbIdx pointer to index's FUCB. If pfucbNil,
// an FUCB will be allocated by DIROpen.
// puipb->lineOldData.cb length of old data record
// puipb->lineOldData.pb old data to extract old key from
// puipb->srid SRID of record
// puipb->lineNewData.cb length of new data record
// puipb->lineNewData.pb new data to extract new key from
// puipb->fFreeFUCB free index FUCB?
// RETURNS JET_errSuccess, or error code from failing routine
// SIDE EFFECTS if patipb->pfucbIdx==pfucbNil, ErrDIROpen will allocate
// an FUCB and it will be pointed at it.
// If fFreeFUCB is fFalse, patipb->pfucbIdx should
// be used in a subsequent ErrDIROpen.
// SEE ALSO Replace
//-
INLINE LOCAL ERR ErrRECIUpdateIndex( FCB *pfcbIdx, UIPB *puipb )
{
ERR err = JET_errSuccess; // error code of various utility
LINE lineSRID; // SRID to add to index
KEY keyOld; // key extracted from old record
BYTE rgbOldKeyBuf[ JET_cbKeyMost]; // buffer for old key
KEY keyNew; // key extracted from new record
BYTE rgbNewKeyBuf[ JET_cbKeyMost ]; // buffer for new key
ULONG itagSequenceOld; // used to extract keys
ULONG itagSequenceNew; // used to extract keys
BOOL fHasMultivalue; // index has tagged segment?
BOOL fMustDelete; // record no longer generates key?
BOOL fMustAdd; // record now generates this key?
BOOL fAllowNulls; // this index allows NULL keys?
BOOL fAllowSomeNulls; // this index allows keys with NULL segment(s)?
BOOL fNoNullSeg; // this index prohibits any NULL key segment?
BOOL fDoOldNullKey;
BOOL fDoNewNullKey;
Assert( pfcbIdx != pfcbNil );
Assert( pfcbIdx->pfdb != pfdbNil );
Assert( pfcbIdx->pidb != pidbNil );
Assert( puipb != NULL );
Assert( puipb->pfucb != pfucbNil );
Assert( puipb->pfucb->ppib != ppibNil );
Assert( puipb->pfucb->ppib->level < levelMax );
Assert( !FLineNull( &puipb->lineOldData ) );
Assert( !FLineNull( &puipb->lineNewData ) );
/*** open FUCB on this index ***/
CallR( ErrDIROpen( puipb->pfucb->ppib, pfcbIdx, 0, &puipb->pfucbIdx ) );
Assert( puipb->pfucbIdx != pfucbNil );
FUCBSetIndex( puipb->pfucbIdx );
FUCBSetNonClustered( puipb->pfucbIdx );
fHasMultivalue = pfcbIdx->pidb->fidb & fidbHasMultivalue;
fAllowNulls = pfcbIdx->pidb->fidb & fidbAllowAllNulls;
fAllowSomeNulls = pfcbIdx->pidb->fidb & fidbAllowSomeNulls;
fNoNullSeg = pfcbIdx->pidb->fidb & fidbNoNullSeg;
#if DBFORMATCHANGE
Assert( ( fAllowNulls || fAllowSomeNulls ) ^ fNoNullSeg );
// if fAllowNulls, then fAllowSomeNulls needs to be true
Assert( !fAllowNulls || fAllowSomeNulls );
#endif
keyOld.pb = rgbOldKeyBuf;
keyNew.pb = rgbNewKeyBuf;
/* delete the old key from the index
/**/
fDoOldNullKey = fFalse;
for ( itagSequenceOld = 1; ; itagSequenceOld++ )
{
Call( ErrRECExtractKey( puipb->pfucb, (FDB *)pfcbIdx->pfdb, pfcbIdx->pidb, &puipb->lineOldData, &keyOld, itagSequenceOld ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( err == wrnFLDOutOfKeys )
{
Assert( itagSequenceOld > 1 );
break;
}
/* record must honor index no NULL segment requirements
/**/
Assert( !fNoNullSeg || ( err != wrnFLDNullSeg && err != wrnFLDNullKey ) );
if ( err == wrnFLDNullKey )
{
if ( fAllowNulls )
fDoOldNullKey = fTrue;
else
break;
}
else
{
if ( err == wrnFLDNullSeg && !fAllowSomeNulls )
break;
}
fMustDelete = fTrue;
fDoNewNullKey = fFalse;
for ( itagSequenceNew = 1; ; itagSequenceNew++ )
{
/* extract key from new data in copy buffer
/**/
Call( ErrRECExtractKey( puipb->pfucb, (FDB *)pfcbIdx->pfdb, pfcbIdx->pidb, &puipb->lineNewData, &keyNew, itagSequenceNew ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( err == wrnFLDOutOfKeys )
{
Assert( itagSequenceNew > 1 );
break;
}
if ( err == wrnFLDNullKey )
{
if ( fAllowNulls )
fDoNewNullKey = fTrue;
else
break;
}
else
{
if ( err == wrnFLDNullSeg && !fAllowSomeNulls )
break;
}
if ( keyOld.cb == keyNew.cb && memcmp( keyOld.pb, keyNew.pb, keyOld.cb ) == 0 )
{
fMustDelete = fFalse;
break;
}
if ( !fHasMultivalue || fDoNewNullKey )
break;
}
if ( fMustDelete )
{
/* move to DATA root. Seek to index entry.
/**/
DIRGotoDataRoot( puipb->pfucbIdx );
Call( ErrDIRDownKeyBookmark( puipb->pfucbIdx, &keyOld, puipb->srid ) );
Call( ErrDIRDelete( puipb->pfucbIdx, fDIRVersion ) );
}
if ( !fHasMultivalue || fDoOldNullKey )
break;
}
/* insert the new key into the index
/**/
lineSRID.cb = sizeof(SRID);
lineSRID.pb = (BYTE *)&puipb->srid;
fDoNewNullKey = fFalse;
for ( itagSequenceNew = 1; ; itagSequenceNew++ )
{
/* extract key from new data in copy buffer
/**/
Call( ErrRECExtractKey( puipb->pfucb, (FDB *)pfcbIdx->pfdb, pfcbIdx->pidb,
&puipb->lineNewData, &keyNew, itagSequenceNew ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( err == wrnFLDOutOfKeys )
{
Assert( itagSequenceNew > 1 );
break;
}
if ( fNoNullSeg && ( err == wrnFLDNullSeg || err == wrnFLDNullKey ) )
{
err = JET_errNullKeyDisallowed;
goto HandleError;
}
if ( err == wrnFLDNullKey )
{
if ( fAllowNulls )
fDoNewNullKey = fTrue;
else
break;
}
else
{
if ( err == wrnFLDNullSeg && !fAllowSomeNulls )
break;
}
fMustAdd = fTrue;
fDoOldNullKey = fFalse;
for ( itagSequenceOld = 1; ; itagSequenceOld++ )
{
Call( ErrRECExtractKey( puipb->pfucb, (FDB *)pfcbIdx->pfdb, pfcbIdx->pidb,
&puipb->lineOldData, &keyOld, itagSequenceOld ) );
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
if ( err == wrnFLDOutOfKeys )
{
Assert( itagSequenceOld > 1 );
break;
}
/* record must honor index no NULL segment requirements
/**/
Assert( !( pfcbIdx->pidb->fidb & fidbNoNullSeg ) ||
( err != wrnFLDNullSeg && err != wrnFLDNullKey ) );
if ( err == wrnFLDNullKey )
{
if ( fAllowNulls )
fDoOldNullKey = fTrue;
else
break;
}
else
{
if ( err == wrnFLDNullSeg && !fAllowSomeNulls )
break;
}
if ( keyOld.cb == keyNew.cb &&
memcmp( keyOld.pb, keyNew.pb, keyOld.cb ) ==0 )
{
fMustAdd = fFalse;
break;
}
if ( !fHasMultivalue || fDoOldNullKey )
break;
}
if ( fMustAdd )
{
BOOL fAllowDupls = fDoNewNullKey || !(pfcbIdx->pidb->fidb & fidbUnique);
/* move to DATA root and insert new index entry.
/**/
DIRGotoDataRoot( puipb->pfucbIdx );
Call( ErrDIRInsert(puipb->pfucbIdx, &lineSRID, &keyNew,
(fAllowDupls ? fDIRDuplicate : 0) |
fDIRPurgeParent | fDIRVersion ) );
}
if ( !fHasMultivalue || fDoNewNullKey )
break;
}
/* supress warnings
/**/
Assert( err == wrnFLDNullKey ||
err == wrnFLDOutOfKeys ||
err == wrnFLDNullSeg ||
err == JET_errSuccess );
err = JET_errSuccess;
HandleError:
/* close the FUCB.
/**/
DIRClose( puipb->pfucbIdx );
puipb->pfucbIdx = pfucbNil;
Assert( err < 0 || err == JET_errSuccess );
return err;
}