#include "config.h" #include #include #include #include "daedef.h" #include "pib.h" #include "page.h" #include "fmp.h" #include "ssib.h" #include "fucb.h" #include "fcb.h" #include "stapi.h" #include "fdb.h" #include "idb.h" #include "dirapi.h" #include "recint.h" #ifdef ANGEL #include "utilwin.h" #else #include "recapi.h" #endif #ifndef ANGEL DeclAssertFile; /* Declare file name for assert macros */ LOCAL INLINE ERR ErrRECIRetrieveColumns( FUCB *pfucb, JET_RETRIEVECOLUMN *pretcols, ULONG cretcols ); #endif //+API // ErrRECExtractField // ======================================================================== // ErrRECExtractField( FDB *pfdb, LINE *plineRec, FID *pfid, ULONG itagSequence, ULONG *pitagSequence, LINE *plineField) // // Extracts a field from a record. This amounts to returning a pointer // into the record (to where the field data starts) and a count of the // number of bytes in the field data. // // PARAMETERS pfdb field descriptors for this record // plineRec record to extract field from // pfid field id of field to extract // If this parameter is zero, then the // tagged fields are scanned without // regard to their field ids, and occurance // number "itagSequence" is returned. This can // be used to sequentially scan all values in // the tagged area of the record. The field // id of the field value returned is placed // in *pfid as an output parameter. // itagSequence if a tagged field is being extracted, // this parameter specifies which occurance // of the tagged field to extract. Tagged // field occurances are number consecutively // starting with 1. Occurance numbers greater // than the maximum occurance in the record // are returned as NULL-valued fields. // plineField Receives extracted field. plineField->pb // will point into the record, at the start // of the field. plineField->cb will be set // to the length of the field data. // If the field requested contains a NULL // value, then plineField->pb will be set to // NULL and plineField->cb will be set to 0. // Additionally, JET_wrnColumnNull would be returned // RETURNS // JET_errSuccess Everything worked. // JET_errColumnInvalid The field id given does not // correspond to a defined field. // JET_wrnColumnNull Extracted column has null value //- ERR ErrRECExtractField( FDB *pfdb, LINE *plineRec, FID *pfid, ULONG *pitagSequence, ULONG itagSequence, LINE *plineField ) { FID fid; // field to extract ULONG ulNumOccurances; // counts field occurances /* the following field used only when *pfid is 0 /**/ FID fidCur; // field to return ULONG ulNumCurOccurances; // returned field occurances count BYTE *pbRec; // efficiency var: ptr to record data FID fidFixedLastInRec; // highest fixed fid actually in record FID fidVarLastInRec; // highest var fid actually in record UNALIGNED WORD *pibVarOffs; // pointer to var field offsets BYTE *pbRecMax; // end of current data record TAGFLD *ptagfld; // pointer to tagged field /*** Efficiency variables ***/ Assert( pfid != NULL ); fid = *pfid; Assert( pfdb != pfdbNil ); Assert( !FLineNull( plineRec ) ); Assert( plineRec->cb >= 4 ); pbRec = plineRec->pb; Assert( plineField != NULL ); fidFixedLastInRec = ((RECHDR*)pbRec)->fidFixedLastInRec; Assert( fidFixedLastInRec <= fidFixedMost ); /*** ---------EXTRACTING FIXED FIELD-------- ***/ if ( FFixedFid( fid ) ) { BYTE *prgbitNullity; // pointer to fixed field bitmap WORD *pibFixOffs; // fixed field offsets if ( fid > pfdb->fidFixedLast ) return JET_errColumnNotFound; if ( pfdb->pfieldFixed[fid-fidFixedLeast].coltyp == JET_coltypNil ) return JET_errColumnNotFound; /*** Field not represented in record: field is NULL ***/ if ( fid > fidFixedLastInRec ) goto NullField; /*** Adjust fid to an index ***/ fid -= fidFixedLeast; /*** Byte containing bit representing fid's nullity ***/ pibFixOffs = pfdb->pibFixedOffsets; prgbitNullity = pbRec + pibFixOffs[fidFixedLastInRec] + fid/8; /*** Bit is not set: field is NULL ***/ if (!(*prgbitNullity & (1 << fid % 8))) goto NullField; /*** Set output parameter to length and address of field ***/ plineField->cb = pibFixOffs[fid+1] - pibFixOffs[fid]; plineField->pb = pbRec + pibFixOffs[fid]; return JET_errSuccess; } /*** More efficiency variables ***/ fidVarLastInRec = ((RECHDR*)pbRec)->fidVarLastInRec; pibVarOffs = (WORD *)(pbRec + pfdb->pibFixedOffsets[fidFixedLastInRec] + (fidFixedLastInRec + 7) / 8); Assert(pibVarOffs[fidVarLastInRec+1-fidVarLeast] <= plineRec->cb); /*** ---------EXTRACTING VARIABLE FIELD-------- ***/ if ( FVarFid( fid ) ) { if (fid > pfdb->fidVarLast) return JET_errColumnNotFound; if (pfdb->pfieldVar[fid-fidVarLeast].coltyp == JET_coltypNil) return JET_errColumnNotFound; /*** Field not represented in record: field is NULL ***/ if (fid > fidVarLastInRec ) goto NullField; /*** Adjust fid to an index ***/ fid -= fidVarLeast; /*** Set output parameter: field length ***/ plineField->cb = ibVarOffset( pibVarOffs[fid+1] ) - ibVarOffset( pibVarOffs[fid] ); Assert(plineField->cb <= plineRec->cb); /*** Field is set to Null ***/ if ( FVarNullBit( pibVarOffs[fid] ) ) { Assert( plineField->cb == 0 ); goto NullField; } /*** Length is zero: return success [zero-length non-null values are allowed] ***/ if (plineField->cb == 0) { plineField->pb = NULL; return JET_errSuccess; } /*** Set output parameter: field address ***/ plineField->pb = pbRec + ibVarOffset( pibVarOffs[fid] ); Assert(plineField->pb >= pbRec && plineField->pb <= pbRec+plineRec->cb); return JET_errSuccess; } /*** ---------EXTRACTING TAGGED FIELD-------- ***/ /* for the first occurrence, itagSequence must be 1, not 0 /**/ if ( itagSequence == 0 ) return JET_errBadItagSequence; if (fid > pfdb->fidTaggedLast) return JET_errColumnNotFound; Assert(FTaggedFid(fid) || fid == 0); if (fid != 0 && pfdb->pfieldTagged[fid - fidTaggedLeast].coltyp == JET_coltypNil) return JET_errColumnNotFound; /*** Scan tagged fields, counting occurances of desired field ***/ pbRecMax = pbRec + plineRec->cb; ptagfld = (TAGFLD*)(pbRec + ibVarOffset( pibVarOffs[fidVarLastInRec+1-fidVarLeast] ) ); ulNumOccurances = 0; fidCur = 0; while ( (BYTE*)ptagfld < pbRecMax ) { if ( fid == 0 ) { /* if we are scanning the whole tag fields, count proper * occurrence of current fid. */ if ( fidCur == ptagfld->fid ) ulNumCurOccurances++; else { fidCur = ptagfld->fid; ulNumCurOccurances = 1; } /* set possible returned values /**/ *pfid = fidCur; *pitagSequence = ulNumCurOccurances; } if ( fid == 0 || ptagfld->fid == fid ) { if ( ++ulNumOccurances == itagSequence ) { BOOL fLongField; plineField->cb = ptagfld->cb; plineField->pb = ptagfld->rgb; fLongField = pfdb->pfieldTagged[*pfid - fidTaggedLeast].coltyp == JET_coltypLongText || pfdb->pfieldTagged[*pfid - fidTaggedLeast].coltyp == JET_coltypLongBinary; return fLongField ? wrnRECLongField : JET_errSuccess; } } ptagfld = (TAGFLD*)((BYTE*)(ptagfld+1) + ptagfld->cb); Assert((BYTE*)ptagfld <= pbRecMax); } /*** Occurance not found: field is NULL, fall through ***/ NullField: /*** Null Field common exit point ***/ plineField->cb = 0; plineField->pb = NULL; return JET_wrnColumnNull; } #ifndef ANGEL /* counts number of columns for a given column id in a given record. /**/ ERR ErrRECCountColumn( FUCB *pfucb, FID fid, INT *pccolumn, JET_GRBIT grbit ) { ERR err = JET_errSuccess; LINE lineRec; FDB *pfdb = (FDB *)pfucb->u.pfcb->pfdb; INT ccolumn = 0; BYTE *pbRec; // efficiency var: ptr to record data FID fidFixedLastInRec; // highest fixed fid actually in record FID fidVarLastInRec; // highest var fid actually in record UNALIGNED WORD *pibVarOffs; // pointer to var field offsets BYTE *pbRecMax; // end of current data record TAGFLD *ptagfld; // pointer to tagged field Assert( pfdb != pfdbNil ); /* get record /**/ if ( ( grbit & JET_bitRetrieveCopy ) && FFUCBRetPrepared( pfucb ) ) { /* only index cursors have copy buffers. /**/ Assert( FFUCBIndex( pfucb ) ); lineRec = pfucb->lineWorkBuf; } else { if ( FFUCBIndex( pfucb ) ) { CallR( ErrDIRGet( pfucb ) ); } else { Assert( PcsrCurrent( pfucb )->csrstat == csrstatOnCurNode || PcsrCurrent( pfucb )->csrstat == csrstatBeforeFirst || PcsrCurrent( pfucb )->csrstat == csrstatAfterLast ); if ( PcsrCurrent( pfucb )->csrstat != csrstatOnCurNode ) return JET_errNoCurrentRecord; Assert( pfucb->lineData.cb != 0 || FFUCBIndex( pfucb ) ); } lineRec = pfucb->lineData; } Assert( lineRec.cb >= 4 ); pbRec = lineRec.pb; fidFixedLastInRec = ((RECHDR *)pbRec)->fidFixedLastInRec; Assert( fidFixedLastInRec <= fidFixedMost ); /*** ---------EXTRACTING FIXED FIELD-------- ***/ if ( FFixedFid( fid ) ) { BYTE *prgbitNullity; // pointer to fixed field bitmap WORD *pibFixOffs; // fixed field offsets if ( fid > pfdb->fidFixedLast ) return JET_errColumnNotFound; if ( pfdb->pfieldFixed[fid-fidFixedLeast].coltyp == JET_coltypNil ) return JET_errColumnNotFound; /* column is NULL /**/ if ( fid > fidFixedLastInRec ) goto NullField; /* adjust fid to index /**/ fid -= fidFixedLeast; /* byte containing bit representing fid's nullity /**/ pibFixOffs = pfdb->pibFixedOffsets; prgbitNullity = pbRec + pibFixOffs[fidFixedLastInRec] + fid/8; /* column is NULL /**/ if ( !( *prgbitNullity & ( 1 << fid % 8 ) ) ) goto NullField; *pccolumn = 1; return JET_errSuccess; } /*** More efficiency variables ***/ fidVarLastInRec = ((RECHDR*)pbRec)->fidVarLastInRec; pibVarOffs = (WORD *)(pbRec + pfdb->pibFixedOffsets[fidFixedLastInRec] + (fidFixedLastInRec + 7) / 8); Assert(pibVarOffs[fidVarLastInRec+1-fidVarLeast] <= lineRec.cb); /*** ---------EXTRACTING VARIABLE FIELD-------- ***/ if ( FVarFid( fid ) ) { if ( fid > pfdb->fidVarLast ) return JET_errColumnNotFound; if ( pfdb->pfieldVar[fid-fidVarLeast].coltyp == JET_coltypNil ) return JET_errColumnNotFound; /* column is NULL /**/ if ( fid > fidVarLastInRec ) goto NullField; /* adjust fid to an index /**/ fid -= fidVarLeast; /* column is set to Null /**/ if ( FVarNullBit( pibVarOffs[fid] ) ) { goto NullField; } *pccolumn = 1; return JET_errSuccess; } /*** ---------EXTRACTING TAGGED FIELD-------- ***/ if ( fid > pfdb->fidTaggedLast ) return JET_errColumnNotFound; Assert( FTaggedFid( fid ) || fid == 0 ); if ( fid != 0 && pfdb->pfieldTagged[fid - fidTaggedLeast].coltyp == JET_coltypNil ) return JET_errColumnNotFound; /* scan tagged fields, counting occurances of desired field /**/ pbRecMax = pbRec + lineRec.cb; ptagfld = (TAGFLD*)(pbRec + ibVarOffset( pibVarOffs[fidVarLastInRec+1-fidVarLeast] ) ); while ( (BYTE*)ptagfld < pbRecMax ) { if ( fid == 0 || ptagfld->fid == fid ) { ++ccolumn; } ptagfld = (TAGFLD*)((BYTE*)(ptagfld+1) + ptagfld->cb); Assert((BYTE*)ptagfld <= pbRecMax); } NullField: *pccolumn = ccolumn; return JET_errSuccess; } /* checks if field at lSeqNum is a separated long value /* if yes, returns fSeparated = fTrue and lid of LV. /**/ ERR ErrRECExtrinsicLong( JET_VTID tableid, ULONG itagSequence, BOOL *pfSeparated, LONG *plid, ULONG *plrefcnt, ULONG grbit ) { ERR err; FID fid = 0; LINE lineField; CallR( ErrRECIRetrieve( (FUCB *)tableid, &fid, itagSequence, &lineField, grbit ) ); if ( err != wrnRECLongField ) { *pfSeparated = fFalse; } else { *pfSeparated = ( (LV *) lineField.pb )->fSeparated; if ( *pfSeparated ) { Assert( lineField.cb == sizeof(LV) ); *plid = ( (LV *) lineField.pb )->lid; } } // UNDONE: returning ref count return JET_errSuccess; } ERR ErrRECIRetrieve( FUCB *pfucb, FID *pfid, ULONG itagSequence, LINE *plineField, ULONG grbit ) { ERR err; FDB *pfdb; ULONG itagSequenceT; /* set pfdb. pfdb is same for indexes and for sorts. /**/ Assert( pfucb->u.pfcb->pfdb == ((FCB*)pfucb->u.pscb)->pfdb ); pfdb = (FDB *)pfucb->u.pfcb->pfdb; Assert( pfdb != pfdbNil ); /* if retrieving from copy buffer. /**/ if ( ( grbit & JET_bitRetrieveCopy ) && FFUCBRetPrepared( pfucb ) ) { /* only index cursors have copy buffers. /**/ Assert( FFUCBIndex( pfucb ) ); err = ErrRECExtractField( pfdb, &pfucb->lineWorkBuf, pfid, &itagSequenceT, itagSequence, plineField ); return err; } /* get current data for index cursors. Sorts always have /* current data cached. /**/ if ( FFUCBIndex( pfucb ) ) { CallR( ErrDIRGet( pfucb ) ); } else { Assert( PcsrCurrent( pfucb )->csrstat == csrstatOnCurNode || PcsrCurrent( pfucb )->csrstat == csrstatBeforeFirst || PcsrCurrent( pfucb )->csrstat == csrstatAfterLast ); if ( PcsrCurrent( pfucb )->csrstat != csrstatOnCurNode ) return JET_errNoCurrentRecord; Assert( pfucb->lineData.cb != 0 || FFUCBIndex( pfucb ) ); } err = ErrRECExtractField( pfdb, &pfucb->lineData, pfid, &itagSequenceT, itagSequence, plineField ); return err; } ERR ErrRECIRetrieveFromIndex( FUCB *pfucb, FID fid, ULONG *pitagSequence, BYTE *pb, ULONG cbMax, ULONG *pcbActual, ULONG ibGraphic, JET_GRBIT grbit ) { ERR err; FUCB *pfucbIdx = pfucb->pfucbCurIndex; FDB *pfdb = (FDB *)pfucb->u.pfcb->pfdb; IDB *pidb; BOOL fText = fFalse; BOOL fTagged = fFalse; BOOL fLongValue = fFalse; BOOL fUnicode = fFalse; INT iidxseg; LINE rglineColumns[JET_ccolKeyMost]; BYTE rgb[JET_cbKeyMost]; ULONG cbReturned; KEY key; INT itagSequence; /* if on clustered index, then return code indicating that /* retrieve should be from record. Note, sequential files /* having no indexes, will be natually handled this way. /**/ if ( pfucbIdx == pfucbNil ) { /* the itagSequence should not be important since /* clustered indexes are not allowed over multi-value /* columns. /**/ return errDIRNoShortCircuit; } /* determine column type so that long value warning can be returned. /* this warning is used by the caller to support byte range /* retrieval. Also, if coltype is Unicode, retrieve from Record only /**/ if ( FFixedFid( fid ) ) { fUnicode = ( pfdb->pfieldFixed[fid - fidFixedLeast].cp == usUniCodePage ); fText = ( pfdb->pfieldFixed[fid - fidFixedLeast].coltyp == JET_coltypText ); } else if ( FVarFid( fid ) ) { fUnicode = ( pfdb->pfieldVar[fid - fidVarLeast].cp == usUniCodePage ); fText = ( pfdb->pfieldVar[fid - fidVarLeast].coltyp == JET_coltypText ); } else { fTagged = fTrue; fUnicode = ( pfdb->pfieldTagged[fid - fidTaggedLeast].cp == usUniCodePage ); fLongValue = pfdb->pfieldTagged[fid - fidTaggedLeast].coltyp == JET_coltypLongText || pfdb->pfieldTagged[fid - fidTaggedLeast].coltyp == JET_coltypLongBinary; fText = ( pfdb->pfieldTagged[fid - fidTaggedLeast].coltyp == JET_coltypLongText ); } /* check for valid currency /**/ Call( ErrDIRGet( pfucbIdx ) ); /* find index segment for given column id /**/ pidb = pfucbIdx->u.pfcb->pidb; for ( iidxseg = 0; iidxseg <= JET_ccolKeyMost; iidxseg++ ) { if ( pidb->rgidxseg[iidxseg] == fid || pidb->rgidxseg[iidxseg] == -fid ) { break; } } if ( iidxseg > JET_ccolKeyMost ) return JET_errColumnNotFound; /* if key may have been truncated, then return code indicating /* that retrieve should be from record. /**/ if ( pfucbIdx->keyNode.cb == JET_cbKeyMost ) { err = errDIRNoShortCircuit; goto ComputeItag; } #ifndef NJETNT /* retrieval from index returns caseless information /* for text and long text. If JET_bitRetrieveCase is /* given then retrieve from record. /**/ if ( fText && grbit & JET_bitRetrieveCase ) { err = errDIRNoShortCircuit; goto ComputeItag; } #endif if ( fUnicode ) { err = errDIRNoShortCircuit; goto ComputeItag; } // UNDONE: only denormalize the column to be retrieved // UNDONE: NULL support /* initialize column array /**/ memset( rglineColumns, '\0', sizeof( rglineColumns ) ); rglineColumns[0].pb = rgb; Call( ErrRECDenormalizeKey( pfdb, pidb, &pfucbIdx->keyNode, rglineColumns ) ); /* column may not have been in key, even though key lenght is less than /* JET_cbKeyMost, so if NULL, then no short circuit. /**/ if ( rglineColumns[iidxseg].pb == NULL ) { err = errDIRNoShortCircuit; goto ComputeItag; } /* first character is bogus /**/ rglineColumns[0].pb += 1; rglineColumns[0].cb -= 1; /* if long value then effect offset /**/ if ( fLongValue ) { if ( pcbActual ) { if ( ibGraphic >= rglineColumns[iidxseg].cb ) *pcbActual = 0; else *pcbActual = rglineColumns[iidxseg].cb - ibGraphic; } if ( rglineColumns[iidxseg].cb == 0 ) { Assert( err == JET_errSuccess || err == JET_wrnColumnNull ); goto ComputeItag; } if ( ibGraphic >= rglineColumns[iidxseg].cb ) { // rglineColumns[iidxseg].pb = NULL; rglineColumns[iidxseg].cb = 0; } else { rglineColumns[iidxseg].pb += ibGraphic; rglineColumns[iidxseg].cb -= ibGraphic; } } /* set return values /**/ if ( pcbActual ) *pcbActual = rglineColumns[iidxseg].cb; if ( rglineColumns[iidxseg].cb == 0 ) { Assert( err == JET_errSuccess || err == JET_wrnColumnNull ); goto ComputeItag; } if ( rglineColumns[iidxseg].cb <= cbMax ) { cbReturned = rglineColumns[iidxseg].cb; Assert( err == JET_errSuccess ); } else { cbReturned = cbMax; err = JET_wrnBufferTruncated; } memcpy( pb, rglineColumns[iidxseg].pb, (size_t)cbReturned ); ComputeItag: if ( err == errDIRNoShortCircuit || ( grbit & JET_bitRetrieveTag ) ) { ERR errT = err; /* extract keys from record and compare against current key /* to compute itag for tagged column instance, responsible for /* this index key. /**/ Assert( fTagged || *pitagSequence == 1 ); if ( fTagged ) { key.pb = rgb; for ( itagSequence = 1; ;itagSequence++ ) { /* get record for key extraction /**/ Call( ErrDIRGet( pfucb ) ); Call( ErrRECExtractKey( pfucb, pfdb, pidb, &pfucb->lineData, &key, itagSequence ) ); if ( memcmp( pfucbIdx->keyNode.pb, key.pb, min( pfucbIdx->keyNode.cb, key.cb ) ) == 0 ) break; } err = errDIRNoShortCircuit; *pitagSequence = itagSequence; } err = errT; } HandleError: return err; } ERR VTAPI ErrIsamRetrieveColumn( PIB *ppib, FUCB *pfucb, JET_COLUMNID columnid, BYTE *pb, ULONG cbMax, ULONG *pcbActual, JET_GRBIT grbit, JET_RETINFO *pretinfo ) { ERR err; LINE line; FID fid = (FID)columnid; ULONG itagSequence; ULONG ibGraphic; ULONG cbReturned; CheckPIB( ppib ); CheckFUCB( ppib, pfucb ); #ifdef JETSER if ( grbit == JET_bitRetrieveRecord ) { err = ErrIsamRetrieveRecords( ppib, pfucb, pb, cbMax, pcbActual, pretinfo->itagSequence ); return err; } if ( grbit == JET_bitRetrieveFDB ) { err = ErrIsamRetrieveFDB( ppib, pfucb, pb, cbMax, pcbActual, pretinfo->ibLongValue ); return err; } if ( grbit == JET_bitRetrieveBookmarks ) { err = ErrIsamRetrieveBookmarks( ppib, pfucb, pb, cbMax, pcbActual ); return err; } #endif if ( pretinfo != NULL ) { if ( pretinfo->cbStruct < sizeof(JET_RETINFO) ) return JET_errInvalidParameter; itagSequence = pretinfo->itagSequence; ibGraphic = pretinfo->ibLongValue; } else { itagSequence = 1; ibGraphic = 0; } if ( grbit & JET_bitRetrieveFromIndex ) { err = ErrRECIRetrieveFromIndex( pfucb, fid, &itagSequence, pb, cbMax, pcbActual, ibGraphic, grbit ); /* return itagSequence if requested /**/ if ( pretinfo != NULL && ( grbit & JET_bitRetrieveTag ) ) { pretinfo->itagSequence = itagSequence; } if ( err != errDIRNoShortCircuit ) { return err; } } CallR( ErrRECIRetrieve( pfucb, &fid, itagSequence, &line, grbit ) ); if ( err == wrnRECLongField ) { /* use line.cb to determine if long field /* is intrinsic or separated /**/ if ( line.cb >= sizeof(LV) && FFieldIsSLong( line.pb ) ) { ULONG cbActual; Assert( line.cb == sizeof( LV ) ); CallR( ErrRECRetrieveSLongField( pfucb, LidOfLV( line.pb ), ibGraphic, pb, cbMax, &cbActual ) ); /* set return values /**/ if ( pretinfo != NULL ) pretinfo->columnidNextTagged = fid; if ( pcbActual ) *pcbActual = cbActual; return cbMax < cbActual ? JET_wrnBufferTruncated : JET_errSuccess; } else { /* adjust line to intrinsic long field /**/ line.pb += offsetof( LV, rgb ); line.cb -= offsetof( LV, rgb ); if ( pcbActual ) { if ( ibGraphic >= line.cb ) *pcbActual = 0; else *pcbActual = line.cb - ibGraphic; } if ( ibGraphic >= line.cb ) { // line.pb = NULL; line.cb = 0; } else { line.pb += ibGraphic; line.cb -= ibGraphic; } /* change err to JET_errSuccess /**/ Assert( err == wrnRECLongField ); err = JET_errSuccess; } } /*** Set return values ***/ if ( pcbActual ) *pcbActual = line.cb; if ( pretinfo != NULL ) pretinfo->columnidNextTagged = fid; if ( line.cb <= cbMax ) { cbReturned = line.cb; } else { cbReturned = cbMax; err = JET_wrnBufferTruncated; } memcpy( pb, line.pb, (size_t)cbReturned ); return err; } /* This routine is mainly for reducing the number of calls to DIRGet /* while extracting many columns from the same record /* retrieves many columns from a record and returns value in pretcol /* pcolinfo is used for passing intermediate info /**/ LOCAL INLINE ERR ErrRECIRetrieveColumns( FUCB *pfucb, JET_RETRIEVECOLUMN *pretcol, ULONG cretcol ) { ERR err; ULONG cbReturned; BOOL fBufferTruncated = fFalse; JET_RETRIEVECOLUMN *pretcolMax = pretcol + cretcol; JET_RETRIEVECOLUMN *pretcolT; /* set pfdb, pfdb is same for indexes and for sorts /**/ Assert( pfucb->u.pfcb->pfdb == ((FCB*)pfucb->u.pscb)->pfdb ); Assert( pfucb->u.pfcb->pfdb != pfdbNil ); /* get current data for index cursors, /* sorts always have current data cached. /**/ if ( FFUCBIndex( pfucb ) ) { CallR( ErrDIRGet( pfucb ) ); } else { Assert( PcsrCurrent( pfucb )->csrstat == csrstatOnCurNode || PcsrCurrent( pfucb )->csrstat == csrstatBeforeFirst || PcsrCurrent( pfucb )->csrstat == csrstatAfterLast ); if ( PcsrCurrent( pfucb )->csrstat != csrstatOnCurNode ) return JET_errNoCurrentRecord; Assert( pfucb->lineData.cb != 0 || FFUCBIndex( pfucb ) ); } for ( pretcolT = pretcol; pretcolT < pretcolMax; pretcolT++ ) { /* efficiency variables /**/ FID fid; ULONG cbMax; ULONG ibLongValue; ULONG ulT; LINE line; /* those columns needing retrieval will have error set /* to JET_errNullInvalid. Any other value indicates column /* has already been retrieved from index or copy buffer or as count. /**/ if ( pretcolT->err != JET_errNullInvalid ) continue; /* set efficiency variables /**/ fid = (FID)pretcolT->columnid; cbMax = pretcolT->cbData; ibLongValue = pretcolT->ibLongValue; CallR( ErrRECExtractField( (FDB *)pfucb->u.pfcb->pfdb, &pfucb->lineData, &fid, &ulT, pretcolT->itagSequence, &line ) ); if ( err == wrnRECLongField ) { /* use line.cb to determine if long field /* is intrinsic or separated /**/ if ( FFieldIsSLong( line.pb ) ) { Assert( line.cb == sizeof( LV ) ); CallR( ErrRECRetrieveSLongField( pfucb, LidOfLV( line.pb ), ibLongValue, pretcolT->pvData, cbMax, &pretcolT->cbActual ) ); /* set return values /**/ if ( err != JET_wrnColumnNull ) err = JET_errSuccess; pretcolT->err = err; pretcolT->columnidNextTagged = (JET_COLUMNID)fid; /* must recache record if may have given up critical section /**/ if ( FFUCBIndex( pfucb ) ) { CallR( ErrDIRGet( pfucb ) ); } continue; } else { /* adjust line to intrinsic long field /**/ line.pb += offsetof( LV, rgb ); line.cb -= offsetof( LV, rgb ); pretcolT->cbActual = ( ibLongValue >= line.cb ) ? 0 : line.cb - ibLongValue; if ( ibLongValue >= line.cb ) { line.cb = 0; } else { line.pb += ibLongValue; line.cb -= ibLongValue; } } } else { /* set cbActual /**/ pretcolT->cbActual = line.cb; } /* set return values /**/ pretcolT->columnidNextTagged = (JET_COLUMNID)fid; if ( err == JET_wrnColumnNull ) { pretcolT->err = err; continue; } if ( line.cb <= cbMax ) { pretcolT->err = JET_errSuccess; cbReturned = line.cb; } else { pretcolT->err = JET_wrnBufferTruncated; cbReturned = cbMax; fBufferTruncated = fTrue; } memcpy( pretcolT->pvData, line.pb, (size_t)cbReturned ); } return fBufferTruncated ? JET_wrnBufferTruncated : JET_errSuccess; } ERR VTAPI ErrIsamRetrieveColumns( PIB *ppib, FUCB *pfucb, JET_RETRIEVECOLUMN *pretcol, ULONG cretcol ) { ERR err = JET_errSuccess; ERR wrn = JET_errSuccess; BOOL fRetrieveFromRecord = fFalse; JET_RETRIEVECOLUMN *pretcolT; JET_RETRIEVECOLUMN *pretcolMax = pretcol + cretcol; CheckPIB( ppib ); CheckFUCB( ppib, pfucb ); for ( pretcolT = pretcol; pretcolT < pretcolMax; pretcolT++ ) { /* if itagSequence is 0 then count columns instead of retrieving. /**/ if ( pretcolT->itagSequence == 0 ) { Call( ErrRECCountColumn( pfucb, (FID)pretcolT->columnid, &pretcolT->itagSequence, pretcolT->grbit ) ); Assert( err != JET_errNullInvalid ); pretcolT->cbActual = 0; pretcolT->columnidNextTagged = pretcolT->columnid; Assert( err != JET_wrnBufferTruncated ); pretcolT->err = err; continue; } /* try to retrieve from index; if no short circuit, RECIRetrieveMany /* will take retrieve record /**/ if ( pretcolT->grbit & JET_bitRetrieveFromIndex ) { err = ErrRECIRetrieveFromIndex( pfucb, (FID)pretcolT->columnid, &pretcolT->itagSequence, (BYTE *)pretcolT->pvData, pretcolT->cbData, &(pretcolT->cbActual), pretcolT->ibLongValue, pretcolT->grbit ); if ( err != errDIRNoShortCircuit ) { if ( err < 0 ) goto HandleError; else { pretcolT->columnidNextTagged = pretcolT->columnid; Assert( err != JET_errNullInvalid ); pretcolT->err = err; if ( err == JET_wrnBufferTruncated ) wrn = err; continue; } } } /* if retrieving from copy buffer. /**/ if ( pretcolT->grbit & JET_bitRetrieveCopy ) { JET_RETINFO retinfo; retinfo.cbStruct = sizeof(retinfo); retinfo.itagSequence = pretcolT->itagSequence; retinfo.ibLongValue = pretcolT->ibLongValue; Call( ErrIsamRetrieveColumn( ppib, pfucb, pretcolT->columnid, pretcolT->pvData, pretcolT->cbData, &pretcolT->cbActual, pretcolT->grbit, &retinfo ) ); pretcolT->columnidNextTagged = retinfo.columnidNextTagged; Assert( err != JET_errNullInvalid ); pretcolT->err = err; if ( err == JET_wrnBufferTruncated ) wrn = err; continue; } fRetrieveFromRecord = fTrue; pretcolT->err = JET_errNullInvalid; } /* retrieve columns with no short circuit /**/ if ( fRetrieveFromRecord ) { Call( ErrRECIRetrieveColumns( pfucb, pretcol, cretcol ) ); if ( err == JET_wrnBufferTruncated ) wrn = err; } HandleError: return ( ( err < 0 ) ? err : wrn ); } #endif