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
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;
|
|
}
|