#include "config.h" #include #include #include "daedef.h" #include "util.h" #include "pib.h" #include "page.h" #include "ssib.h" #include "fmp.h" #include "fucb.h" #include "stapi.h" #include "dirapi.h" #include "fcb.h" #include "fdb.h" #include "idb.h" #include "scb.h" #include "recapi.h" #include "recint.h" #include "nver.h" #include "logapi.h" #include "fileint.h" #include "sortapi.h" #include "fileapi.h" DeclAssertFile; /* Declare file name for assert macros */ CRIT critTempDBName; static ULONG ulTempNum = 0; ULONG NEAR ulTempNameGen() { ULONG ulNum; SgSemRequest(critTempDBName); ulNum = ulTempNum++; SgSemRelease(critTempDBName); return(ulNum); } /*================================================================= ErrIsamSortMaterialize Description: Converts a SORT file into a temporary file so that it may be accessed using the normal file access functions. /* 1. create temporary table /* 2. use DIR operations to convert SORT data to FILE data /* 3. fake SORT cursor to be FILE cursor /* 4. close SORT cursor and return SORT resources /**/ /* Parameters: FUCB *pfucbSort pointer to the FUCB for the sort file Return Value: standard error return Errors/Warnings: Side Effects: =================================================================*/ ERR VTAPI ErrIsamSortMaterialize( PIB *ppib, FUCB *pfucbSort, BOOL fIndex ) { ERR err; INT crun; INT irun; INT cPages; RUN *rgrun; FUCB *pfucbTable = pfucbNil; FCB *pfcbTable; FCB *pfcbSort; FDB *pfdb; IDB *pidb; BYTE szName[JET_cbNameMost+1]; CheckPIB( ppib ); CheckSort( ppib, pfucbSort ); Assert( ppib->level < levelMax ); Assert( pfucbSort->ppib == ppib ); Assert( !( FFUCBIndex( pfucbSort ) ) ); /* causes remaining runs to be flushed to disk /**/ if ( FSCBInsert( pfucbSort->u.pscb ) ) { CallR( ErrSORTEndRead( pfucbSort ) ); } CallR( ErrDIRBeginTransaction( ppib ) ); crun = pfucbSort->u.pscb->crun; if ( crun > 0 ) { rgrun = pfucbSort->u.pscb->rgrun; for (irun = 0, cPages=0; irun < crun; irun++) { cPages += rgrun[irun].cbfRun; } } else { cPages = 4; } /* generate temporary file name /**/ sprintf(szName, "TEMP%lu", ulTempNameGen()); /* create table /**/ Call( ErrFILECreateTable( ppib, dbidTemp, szName, 16, 100, &pfucbTable ) ); /* move to DATA root /**/ DIRGotoDataRoot( pfucbTable ); pfcbSort = &(pfucbSort->u.pscb->fcb); pfcbTable = pfucbTable->u.pfcb; err = ErrSORTFirst( pfucbSort ); if ( fIndex ) { while ( err >= 0 ) { Call( ErrDIRInsert( pfucbTable, &pfucbSort->lineData, &pfucbSort->keyNode, fDIRVersion | fDIRBackToFather ) ); err = ErrSORTNext( pfucbSort ); } } else { DBK dbk = 0; BYTE rgbDbk[4]; KEY keyDbk; keyDbk.cb = sizeof(DBK); keyDbk.pb = rgbDbk; while ( err >= 0 ) { keyDbk.pb[0] = (BYTE)(dbk >> 24); keyDbk.pb[1] = (BYTE)((dbk >> 16) & 0xff); keyDbk.pb[2] = (BYTE)((dbk >> 8) & 0xff); keyDbk.pb[3] = (BYTE)(dbk & 0xff); dbk++; Call( ErrDIRInsert( pfucbTable, &pfucbSort->lineData, &keyDbk, fDIRVersion | fDIRBackToFather ) ); err = ErrSORTNext(pfucbSort); } pfcbTable->dbkMost = dbk; } if ( err < 0 && err != JET_errNoCurrentRecord ) { goto HandleError; } Call( ErrDIRCommitTransaction( ppib ) ); /* convert sort cursor into table cursor by changing flags. /**/ Assert( pfcbTable->pfcbNextIndex == pfcbNil ); Assert( pfcbTable->dbid == dbidTemp ); pfcbTable->cbDensityFree = 0; pfcbTable->wFlags = fFCBTemporaryTable | fFCBClusteredIndex; /* switch sort and table FDP so FDP preserved and ErrFILECloseTable. /**/ pfdb = (FDB *)pfcbSort->pfdb; pfcbSort->pfdb = pfcbTable->pfdb; pfcbTable->pfdb = pfdb; /* switch sort and table IDB so IDB preserved and ErrFILECloseTable, /* only if fIndex. /**/ if ( fIndex ) { pidb = pfcbSort->pidb; pfcbSort->pidb = pfcbTable->pidb; pfcbTable->pidb = pidb; } /* convert sort cursor flags to table flags, with fFUCBOrignallySort /**/ Assert( pfucbSort->dbid == dbidTemp ); Assert( pfucbSort->pfucbCurIndex == pfucbNil ); FUCBSetIndex( pfucbSort ); FUCBResetSort( pfucbSort ); /* release SCB and close table cursor /**/ SORTClosePscb( pfucbSort->u.pscb ); FCBLink( pfucbSort, pfcbTable ); CallS( ErrFILECloseTable( ppib, pfucbTable ) ); pfucbTable = pfucbNil; /* move to the first record ignoring error if table empty /**/ err = ErrIsamMove( ppib, pfucbSort, JET_MoveFirst, 0 ); if ( err < 0 ) { if ( err != JET_errNoCurrentRecord ) goto HandleError; } Assert( err == JET_errSuccess || err == JET_errNoCurrentRecord ); return err; HandleError: if ( pfucbTable != pfucbNil ) CallS( ErrFILECloseTable( ppib, pfucbTable ) ); CallS( ErrDIRRollback( ppib ) ); return err; } /*================================================================= ErrIsamMove Description: Retrieves the first, last, (nth) next, or (nth) previous record from the specified file. Parameters: PIB *ppib PIB of user FUCB *pfucb FUCB for file LONG crow number of rows to move JET_GRBIT grbit options Return Value: standard error return Errors/Warnings: Side Effects: =================================================================*/ ERR VTAPI ErrIsamMove( PIB *ppib, FUCB *pfucb, LONG crow, JET_GRBIT grbit ) { ERR err = JET_errSuccess; FUCB *pfucb2ndIdx; // FUCB for secondary index (if any) FUCB *pfucbIdx; // FUCB of selected index (pri or sec) SRID srid; // bookmark of record DIB dib; // Information block for DirMan CheckPIB( ppib ); CheckTable( ppib, pfucb ); CheckNonClustered( pfucb ); if ( FFUCBUpdatePrepared( pfucb ) ) { CallR( ErrIsamPrepareUpdate( ppib, pfucb, JET_prepCancel ) ); } #ifdef INPAGE /* check to see if search can cross page boundary /* and set flag accordingly. /**/ if ( grbit & JET_bitMoveInPage ) dib.fFlags = fDIRInPage; else dib.fFlags = fDIRNull; #else Assert( ( grbit & JET_bitMoveInPage ) == 0 ); dib.fFlags = fDIRNull; #endif // Get secondary index FUCB if any pfucb2ndIdx = pfucb->pfucbCurIndex; if ( pfucb2ndIdx == pfucbNil ) pfucbIdx = pfucb; else pfucbIdx = pfucb2ndIdx; if ( crow == JET_MoveLast ) { DIRResetIndexRange( pfucb ); dib.pos = posLast; dib.fFlags |= fDIRPurgeParent; /* move to DATA root /**/ DIRGotoDataRoot( pfucbIdx ); err = ErrDIRDown( pfucbIdx, &dib ); } else if ( crow > 0 ) { LONG crowT = crow; if ( ( grbit & JET_bitMoveKeyNE ) != 0 ) dib.fFlags |= fDIRNeighborKey; // Move forward number of rows given while ( crowT-- > 0 ) { err = ErrDIRNext( pfucbIdx, &dib ); if (err < 0) break; } } else if ( crow == JET_MoveFirst ) { DIRResetIndexRange( pfucb ); dib.pos = posFirst; dib.fFlags |= fDIRPurgeParent; /* move to DATA root /**/ DIRGotoDataRoot( pfucbIdx ); err = ErrDIRDown( pfucbIdx, &dib ); } else if ( crow == 0 ) { err = ErrDIRGet( pfucb ); } else { LONG crowT = crow; if ( ( grbit & JET_bitMoveKeyNE ) != 0) dib.fFlags |= fDIRNeighborKey; while ( crowT++ < 0 ) { err = ErrDIRPrev( pfucbIdx, &dib ); if ( err < 0 ) break; } } /* if the movement was successful and a non-clustered index is /* in use, then position clustered index to record. /**/ if ( err == JET_errSuccess && pfucb2ndIdx != pfucbNil && crow != 0 ) { Assert( pfucb2ndIdx->lineData.pb != NULL ); Assert( pfucb2ndIdx->lineData.cb >= sizeof(SRID) ); srid = PcsrCurrent( pfucb2ndIdx )->item; DIRDeferGotoBookmark( pfucb, srid ); Assert( PgnoOfSrid( srid ) != pgnoNull ); } if ( err == JET_errSuccess ) return err; if ( err == JET_errPageBoundary ) return JET_errNoCurrentRecord; if ( crow > 0 ) { PcsrCurrent(pfucbIdx)->csrstat = csrstatAfterLast; PcsrCurrent(pfucb)->csrstat = csrstatAfterLast; } else if ( crow < 0 ) { PcsrCurrent(pfucbIdx)->csrstat = csrstatBeforeFirst; PcsrCurrent(pfucb)->csrstat = csrstatBeforeFirst; } switch ( err ) { case JET_errRecordNotFound: err = JET_errNoCurrentRecord; case JET_errNoCurrentRecord: case JET_errRecordDeleted: break; default: PcsrCurrent( pfucbIdx )->csrstat = csrstatBeforeFirst; if ( pfucb2ndIdx != pfucbNil ) PcsrCurrent( pfucb2ndIdx )->csrstat = csrstatBeforeFirst; } return err; } /*================================================================= ErrIsamSeek Description: Retrieve the record specified by the given key or the one just after it (SeekGT or SeekGE) or the one just before it (SeekLT or SeekLE). Parameters: PIB *ppib PIB of user FUCB *pfucb FUCB for file JET_GRBIT grbit grbit Return Value: standard error return Errors/Warnings: Side Effects: =================================================================*/ ERR VTAPI ErrIsamSeek( PIB *ppib, FUCB *pfucb, JET_GRBIT grbit ) { ERR err = JET_errSuccess; KEY key; // key KEY *pkey = &key; // pointer to the input key FUCB *pfucb2ndIdx; // pointer to index FUCB (if any) BOOL fFoundLess; SRID srid; // bookmark of record JET_GRBIT grbitMove = 0; CheckPIB( ppib ); CheckTable( ppib, pfucb ); CheckNonClustered( pfucb ); if ( ! ( FKSPrepared( pfucb ) ) ) { return(JET_errKeyNotMade); } /* Reset copy buffer status /**/ if ( FFUCBUpdatePrepared( pfucb ) ) { CallR( ErrIsamPrepareUpdate( ppib, pfucb, JET_prepCancel ) ); } /* reset index range limit /**/ DIRResetIndexRange( pfucb ); /* ignore segment counter /**/ pkey->pb = pfucb->pbKey + 1; pkey->cb = pfucb->cbKey - 1; pfucb2ndIdx = pfucb->pfucbCurIndex; if ( pfucb2ndIdx == pfucbNil ) { err = ErrDIRDownFromDATA( pfucb, pkey ); } else { Assert( FFUCBNonClustered( pfucb2ndIdx ) ); err = ErrDIRDownFromDATA( pfucb2ndIdx, pkey ); /* if the movement was successful and a non-clustered index is /* in use, then position clustered index to record. /**/ if ( err == JET_errSuccess ) { Assert(pfucb2ndIdx->lineData.pb != NULL); Assert(pfucb2ndIdx->lineData.cb >= sizeof(SRID)); srid = PcsrCurrent( pfucb2ndIdx )->item; DIRDeferGotoBookmark( pfucb, srid ); Assert( PgnoOfSrid( srid ) != pgnoNull ); } } if ( err == JET_errSuccess && ( grbit & JET_bitSeekEQ ) != 0 ) { /* found equal on seek equal. If index range grbit is /* set then set index range upper inclusive. /**/ if ( grbit & JET_bitSetIndexRange ) { CallR( ErrIsamSetIndexRange( ppib, pfucb, JET_bitRangeInclusive | JET_bitRangeUpperLimit ) ); } /* reset key status. /**/ KSReset( pfucb ); return err; } /* reset key status. /**/ KSReset( pfucb ); /* remember if found less. /**/ fFoundLess = ( err == wrnNDFoundLess ); if ( err == wrnNDFoundLess || err == wrnNDFoundGreater ) { err = JET_errRecordNotFound; } else if ( err < 0 ) { PcsrCurrent( pfucb )->csrstat = csrstatBeforeFirst; if ( pfucb2ndIdx != pfucbNil ) { PcsrCurrent( pfucb2ndIdx )->csrstat = csrstatBeforeFirst; } } #define bitSeekAll (JET_bitSeekEQ | JET_bitSeekGE | JET_bitSeekGT | \ JET_bitSeekLE | JET_bitSeekLT) /* adjust currency for seek request. /**/ switch ( grbit & bitSeekAll ) { case JET_bitSeekEQ: return err; case JET_bitSeekGE: if ( err != JET_errRecordNotFound ) return err; err = ErrIsamMove( ppib, pfucb, +1L, grbitMove ); if ( err == JET_errNoCurrentRecord ) return JET_errRecordNotFound; else return JET_wrnSeekNotEqual; case JET_bitSeekGT: if ( err < 0 && err != JET_errRecordNotFound ) return err; if ( err >= 0 || fFoundLess ) grbitMove |= JET_bitMoveKeyNE; err = ErrIsamMove( ppib, pfucb, +1L, grbitMove ); if ( err == JET_errNoCurrentRecord ) return JET_errRecordNotFound; else return err; case JET_bitSeekLE: if ( err != JET_errRecordNotFound ) return err; err = ErrIsamMove( ppib, pfucb, JET_MovePrevious, grbitMove ); if ( err == JET_errNoCurrentRecord ) return JET_errRecordNotFound; else return JET_wrnSeekNotEqual; case JET_bitSeekLT: if ( err < 0 && err != JET_errRecordNotFound ) return err; if ( err >= 0 || !fFoundLess ) grbitMove |= JET_bitMoveKeyNE; err = ErrIsamMove( ppib, pfucb, JET_MovePrevious, grbitMove ); if ( err == JET_errNoCurrentRecord ) return JET_errRecordNotFound; else return err; } } ERR VTAPI ErrIsamGotoBookmark( PIB *ppib, FUCB *pfucb, BYTE *pbBookmark, ULONG cbBookmark ) { ERR err; LINE key; CheckPIB( ppib ); CheckTable( ppib, pfucb ); Assert( FFUCBIndex( pfucb ) ); CheckNonClustered( pfucb ); if ( cbBookmark != sizeof(SRID) ) return JET_errInvalidBookmark; Assert( cbBookmark == sizeof(SRID) ); /* reset copy buffer status /**/ if ( FFUCBUpdatePrepared( pfucb ) ) { CallR( ErrIsamPrepareUpdate( ppib, pfucb, JET_prepCancel ) ); } /* reset index range limit /**/ DIRResetIndexRange( pfucb ); /* get node, and return error if this node is not there for caller. /**/ DIRGotoBookmark( pfucb, *(SRID *)pbBookmark ); Call( ErrDIRGet( pfucb ) ); /* bookmark must be for node in table cursor is on /**/ Assert( PgnoPMPgnoFDPOfPage( pfucb->ssib.pbf->ppage ) == pfucb->u.pfcb->pgnoFDP ); /* goto bookmark record build key for secondary index /* to bookmark record /**/ if ( pfucb->pfucbCurIndex != pfucbNil ) { /* get non-clustered index cursor /**/ FUCB *pfucbIdx = pfucb->pfucbCurIndex; /* allocate goto bookmark resources /**/ if ( pfucb->pbKey == NULL ) { pfucb->pbKey = LAlloc( 1L, JET_cbKeyMost ); if ( pfucb->pbKey == NULL ) return JET_errOutOfMemory; } /* make key for record for non-clustered index /**/ key.pb = pfucb->pbKey; Call( ErrRECExtractKey( pfucb, (FDB *)pfucb->u.pfcb->pfdb, pfucbIdx->u.pfcb->pidb, &pfucb->lineData, &key, 1 ) ); Assert( err != wrnFLDOutOfKeys ); /* record must honor index no NULL segment requirements /**/ Assert( !( pfucbIdx->u.pfcb->pidb->fidb & fidbNoNullSeg ) || ( err != wrnFLDNullSeg && err != wrnFLDNullKey ) ); /* if item is not index, /* then move before first instead of seeking /**/ if ( ( err == wrnFLDNullKey && !( pfucbIdx->u.pfcb->pidb->fidb & fidbAllowAllNulls ) ) || ( err == wrnFLDNullSeg && !( pfucbIdx->u.pfcb->pidb->fidb & fidbAllowSomeNulls ) ) ) { /* This assumes that NULLs sort low. /**/ DIRBeforeFirst( pfucbIdx ); err = JET_errNoCurrentRecord; } else { /* move to DATA root /**/ DIRGotoDataRoot( pfucbIdx ); /* seek on secondary key /**/ Call( ErrDIRDownKeyBookmark( pfucbIdx, &key, *(SRID *)pbBookmark ) ); Assert( err == JET_errSuccess ); /* item must be same as bookmark and /* clustered cursor must be on record. /**/ Assert( pfucbIdx->lineData.pb != NULL ); Assert( pfucbIdx->lineData.cb >= sizeof(SRID) ); Assert( PcsrCurrent( pfucbIdx )->csrstat == csrstatOnCurNode ); Assert( PcsrCurrent( pfucbIdx )->item == *(SRID *)pbBookmark ); } } HandleError: KSReset( pfucb ); return err; } ERR VTAPI ErrIsamGotoPosition( PIB *ppib, FUCB *pfucb, JET_RECPOS *precpos ) { ERR err; FUCB *pfucb2ndIdx; SRID srid; CheckPIB( ppib ); CheckTable( ppib, pfucb ); CheckNonClustered( pfucb ); /* Reset copy buffer status /**/ if ( FFUCBUpdatePrepared( pfucb ) ) { CallR( ErrIsamPrepareUpdate( ppib, pfucb, JET_prepCancel ) ); } /* reset index range limit /**/ DIRResetIndexRange( pfucb ); /* reset key stat /**/ KSReset( pfucb ); /* set non clustered index pointer, may be null /**/ pfucb2ndIdx = pfucb->pfucbCurIndex; if ( pfucb2ndIdx == pfucbNil ) { /* move to DATA root /**/ DIRGotoDataRoot( pfucb ); err = ErrDIRGotoPosition( pfucb, precpos->centriesLT, precpos->centriesTotal ); } else { /* move to DATA root /**/ DIRGotoDataRoot( pfucb2ndIdx ); err = ErrDIRGotoPosition( pfucb2ndIdx, precpos->centriesLT, precpos->centriesTotal ); /* if the movement was successful and a non-clustered index is /* in use, then position clustered index to record. /**/ if ( err == JET_errSuccess ) { Assert( pfucb2ndIdx->lineData.pb != NULL ); Assert( pfucb2ndIdx->lineData.cb >= sizeof(SRID) ); srid = PcsrCurrent( pfucb2ndIdx )->item; DIRDeferGotoBookmark( pfucb, srid ); Assert( PgnoOfSrid( srid ) != pgnoNull ); } } /* if no records then return JET_errRecordNotFound /* otherwise return error from called routine /**/ if ( err < 0 ) { PcsrCurrent( pfucb )->csrstat = csrstatBeforeFirst; if ( pfucb2ndIdx != pfucbNil ) { PcsrCurrent( pfucb2ndIdx )->csrstat = csrstatBeforeFirst; } } else { Assert (err==JET_errSuccess || err==wrnNDFoundLess || err==wrnNDFoundGreater ); err = JET_errSuccess; } return err; } ERR VTAPI ErrIsamSetIndexRange( PIB *ppib, FUCB *pfucb, JET_GRBIT grbit ) { ERR err; FUCB *pfucbIdx; /* ppib is not used in this function. /**/ NotUsed( ppib ); /* must be on index /**/ if ( pfucb->u.pfcb->pidb == pidbNil && pfucb->pfucbCurIndex == pfucbNil ) return JET_errNoCurrentIndex; /* key must be prepared /**/ if ( ! ( FKSPrepared( pfucb ) ) ) return JET_errKeyNotMade; /* get cursor for current index. If non-clustered index, /* then copy index range key to non-clustered index. /**/ if ( pfucb->pfucbCurIndex != pfucbNil ) { pfucbIdx = pfucb->pfucbCurIndex; if ( pfucbIdx->pbKey == NULL ) { pfucbIdx->pbKey = LAlloc( 1L, JET_cbKeyMost ); if ( pfucbIdx->pbKey == NULL ) return JET_errOutOfMemory; } pfucbIdx->cbKey = pfucb->cbKey; memcpy( pfucbIdx->pbKey, pfucb->pbKey, pfucbIdx->cbKey ); } else pfucbIdx = pfucb; /* set index range and check current position. /**/ DIRSetIndexRange( pfucbIdx, grbit ); err = ErrDIRCheckIndexRange( pfucbIdx ); /* reset key status. /**/ KSReset( pfucb ); return err; }