#include "config.h" #include #include #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; }