Windows-Server-2003/inetsrv/query/bigtable/query.cxx

940 lines
28 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000.
//
// File: query.cxx
//
// Contents: Class encapsulating all the context for a running
// query, including the query execution context, the
// cached query results, and all cursors over the
// results.
// Dispatches requests to the appropriate subobject.
//
// Classes: CAsyncQuery
// CGetRowsParams
//
// History: 31 May 94 AlanW Created
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <query.hxx>
#include <srequest.hxx>
#include <rowseek.hxx>
#include <tbrowkey.hxx>
#include <oleprop.hxx>
#include "tabledbg.hxx"
//+-------------------------------------------------------------------------
//
// Member: CGetRowsParams::GetRowBuffer, public inline
//
// Synopsis: Return a pointer to the row buffer. If this is the first
// reference, get it from the fixed allocator.
//
// Arguments: - none -
//
// Returns: PBYTE - a pointer to the row buffer.
//
// Notes: We don't put this in query.hxx since we don't want to
// have to refer to PFixedVarAllocator there.
//
//--------------------------------------------------------------------------
PBYTE CGetRowsParams::GetRowBuffer( void )
{
if (0 == _pData)
_pData = _rAllocator.AllocFixed();
return (PBYTE) _pData;
}
PBYTE CGetRowsParams::GetBuffer( ) const
{ return _rAllocator.BufferAddr(); }
//+---------------------------------------------------------------------------
//
// Member: CAsyncQuery::CAsyncQuery, public
//
// Synopsis: Creates a locally accessible Query
//
// Arguments: [qopt] - Query optimizer
// [col] - Initial set of columns to return
// [sort] - Initial sort
// [categ] - Categorization specification
// [cCursors] - # of cursors to create
// [aCursors] - array of cursors returned
// [pidremap] - prop ID mapping
// [fEnableNotification] - if TRUE, allow watches
// [pDocStore] - client doc store
// [pEvtComplete] - completion event
//
//----------------------------------------------------------------------------
CAsyncQuery::CAsyncQuery( XQueryOptimizer & qopt,
XColumnSet & col,
XSortSet & sort,
XCategorizationSet &categ,
unsigned cCursors,
ULONG * aCursors,
XInterface<CPidRemapper> & pidremap,
BOOL fEnableNotification,
ICiCDocStore *pDocStore,
CRequestServer * pQuiesce
)
: _fCanDoWorkidToPath( !qopt->IsWorkidUnique() ),
PQuery( ),
_ref( 1 ),
_pidremap( pidremap.Acquire() ),
_Table( col,
sort,
cCursors - 1,
_mutex,
!_fCanDoWorkidToPath,
pQuiesce ),
_cRowsLastAsked (0),
_aCursors( ),
_aCategorize( cCursors - 1 )
{
//
// Get ci manager and translator interfaces
//
ICiManager *pCiManager = 0;
SCODE sc = pDocStore->GetContentIndex( &pCiManager );
if ( FAILED( sc ) )
{
Win4Assert( !"Need to support GetContentIndex interface" );
THROW( CException( sc ) );
}
_xCiManager.Set( pCiManager );
ICiCDocNameToWorkidTranslator *pNameToWidTranslator;
sc = pDocStore->QueryInterface( IID_ICiCDocNameToWorkidTranslator,
(void **) &pNameToWidTranslator );
if ( FAILED( sc ) )
{
Win4Assert( !"Need to support translator QI" );
THROW( CException( sc ) );
}
_xNameToWidTranslator.Set( pNameToWidTranslator );
Win4Assert( 0 != cCursors );
// make a cursor for the main table and each categorization level
for (unsigned cursor = 0; cursor < cCursors; cursor++)
aCursors[cursor] = _CreateRowCursor();
// the last cursor is associated with the main table
_aCursors.Lookup(aCursors[cCursors - 1]).SetSource( &_Table );
if (cCursors > 1)
{
//
// NOTE: pidWorkid is always added to the sort set in the table,
// so it should always be sorted. That's why it's sort count-1
// SPECDEVIATION - if workid was already in the sort, this test
// is incorrect. Who would want to categorize on workid though?
//
if ( ! _Table.IsSorted() ||
cCursors-1 > _Table.GetSortSet()->Count()-1 )
{
// CLEANCODE: Should this check be handled in the categorizers?
// Is it specific to the unique categorization?
tbDebugOut(( DEB_WARN, "Query contains too few sort specs "
"for categorization spec\n" ));
THROW( CException( E_INVALIDARG ) );
}
// this is a categorized table, so set up the categorizers
for ( unsigned cat = 0; cat < (cCursors - 1); cat++ )
{
_aCategorize[cat] = new CCategorize( * (categ->Get(cat)),
cat + 1,
cat ? _aCategorize[cat-1] : 0,
_mutex );
_aCursors.Lookup(aCursors[cat]).SetSource( _aCategorize[cat] );
}
_Table.SetCategorizer(_aCategorize[cCursors - 2]);
// now tell each categorizer what it is categorizing over
for ( cat = 0; cat < (cCursors - 2); cat++ )
_aCategorize[cat]->SetChild( _aCategorize[ cat + 1 ] );
_aCategorize[ cCursors - 2 ]->SetChild( & _Table );
}
// Without the lock, the query can be complete and destructed
// BEFORE the constructor finishes
CLock lock( _mutex );
//
// Since we can be swapping rows, we may need singleton cursor(s)
//
qopt->EnableSingletonCursors();
if ( 0 != pQuiesce )
pQuiesce->SetPQuery( (PQuery *) this );
_QExec.Set( new CQAsyncExecute( qopt, fEnableNotification, _Table, pDocStore ) );
_Table.SetQueryExecute( _QExec.GetPointer() );
ciDebugOut(( DEB_USER1, "Using an asynchronous cursor.\n" ));
END_CONSTRUCTION( CAsyncQuery );
}
//+---------------------------------------------------------------------------
//
// Member: CAsyncQuery::~CAsyncQuery, public
//
// Synopsis: Destroy the query
//
//----------------------------------------------------------------------------
CAsyncQuery::~CAsyncQuery()
{
CLock lock( _mutex );
//Win4Assert( _ref == 0 ); This isn't true when queries are aborted
_Table.ReleaseQueryExecute();
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::AddRef, public
//
// Synopsis: Reference the query.
//
//--------------------------------------------------------------------------
ULONG CAsyncQuery::AddRef(void)
{
return InterlockedIncrement( &_ref );
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::Release, public
//
// Synopsis: De-Reference the query.
//
// Effects: If the ref count goes to 0 then the query is deleted.
//
//--------------------------------------------------------------------------
ULONG CAsyncQuery::Release(void)
{
long l = InterlockedDecrement( &_ref );
if ( l <= 0 )
{
tbDebugOut(( DEB_ITRACE, "CAsyncQuery unreferenced. Deleting.\n" ));
delete this;
return 0;
}
return l;
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::_CreateRowCursor, private
//
// Synopsis: Create a new CTableCursor, return a handle to it.
//
// Arguments: - none -
//
// Returns: ULONG - handle associated with the new cursor. Will
// be zero if the cursor could not be created.
//
// Notes: There needs to be a subsequent call to SetBindings prior
// to any call to GetRows.
//
//--------------------------------------------------------------------------
ULONG CAsyncQuery::_CreateRowCursor( )
{
CTableCursor * pCursor = new CTableCursor;
ULONG hCursor = 0;
_aCursors.Add(pCursor, hCursor);
// By default, make the cursor refer to the real table,
// not categorization
pCursor->SetSource(&_Table);
Win4Assert(hCursor != 0);
return hCursor;
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::FreeCursor, public
//
// Synopsis: Free a handle to a CTableCursor
//
// Arguments: [hCursor] - handle to the cursor to be freed
//
// Returns: # of cursors left
//
//--------------------------------------------------------------------------
unsigned CAsyncQuery::FreeCursor(
ULONG hCursor )
{
Win4Assert( hCursor != 0 );
_aCursors.Release(hCursor);
return _aCursors.Count();
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::SetBindings, public
//
// Synopsis: Set column bindings into a cursor
//
// Arguments: [hCursor] - the handle of the cursor to set bindings on
// [cbRowLength] - the width of an output row
// [cols] - a description of column bindings to be set
// [pids] - a PID mapper which maps fake pids in cols to
// column IDs.
//
// Returns: nothing
//
//--------------------------------------------------------------------------
void
CAsyncQuery::SetBindings(
ULONG hCursor,
ULONG cbRowLength,
CTableColumnSet & cols,
CPidMapper & pids )
{
CTableCursor& rCursor = _aCursors.Lookup(hCursor);
if (0 == cols.Count() ||
0 == cbRowLength || cbRowLength >= USHRT_MAX)
THROW( CException( E_INVALIDARG ));
XPtr<CTableColumnSet> outset(new CTableColumnSet( cols.Count() ));
for (unsigned iCol = 0; iCol < cols.Count(); iCol++)
{
CTableColumn * pCol = cols.Get( iCol );
CFullPropSpec * propspec = pids.Get( pCol->PropId );
//
// Convert the DBID to a PROPID
//
// Win4Assert( iCol+1 == pCol->PropId ); // therefore pids is useless
PROPID prop = _pidremap->NameToReal(propspec);
if ( prop == pidInvalid || _Table.IsColumnInTable(prop) == FALSE )
{
tbDebugOut(( DEB_ERROR, "Column unavailable: prop = 0x%x\n", prop ));
THROW( CException( DB_E_BADCOLUMNID ));
}
if (pCol->IsCompressedCol())
THROW( CException( E_INVALIDARG ));
XPtr<CTableColumn> xpOutcol ( new CTableColumn( prop, pCol->GetStoredType() ) );
if (pCol->IsValueStored())
xpOutcol->SetValueField(pCol->GetStoredType(),
pCol->GetValueOffset(),
pCol->GetValueSize());
Win4Assert( pCol->IsStatusStored() );
xpOutcol->SetStatusField(pCol->GetStatusOffset(),
(USHORT)pCol->GetStatusSize());
if (pCol->IsLengthStored())
xpOutcol->SetLengthField(pCol->GetLengthOffset(),
(USHORT)pCol->GetLengthSize());
outset->Add(xpOutcol, iCol);
}
SCODE sc = rCursor.SetBindings( cbRowLength, outset );
if ( FAILED( sc ) )
THROW( CException( sc ) );
} //SetBindings
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetRows, public
//
// Synopsis: Retrieve row data for a table cursor
//
// Arguments: [hCursor] - the handle of the cursor to fetch data for
// [rSeekDesc] - row seek operation to be done before fetch
// [rFetchParams] - row fetch parameters and buffer pointers
// [pSeekDescOut] - row seek description for restart
//
// Returns: SCODE - the status of the operation. E_HANDLE
// is returned if hCursor cannot be looked up,
// presumably an internal error.
//
// History: 24 Jan 1995 Alanw Created
//
//--------------------------------------------------------------------------
SCODE
CAsyncQuery::GetRows(
ULONG hCursor,
const CRowSeekDescription & rSeekDesc,
CGetRowsParams& rFetchParams,
XPtr<CRowSeekDescription> & pSeekDescOut)
{
CTableCursor & rCursor = _aCursors.Lookup(hCursor);
rCursor.ValidateBindings();
CTableSource & rSource = rCursor.GetSource();
Win4Assert(rCursor.GetRowWidth() == rFetchParams.GetRowWidth());
return rSeekDesc.GetRows( rCursor,
rSource,
rFetchParams,
pSeekDescOut );
} //GetRows
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::RestartPosition, public
//
// Synopsis: Reset fetch position for chapter to the start
//
// Arguments: [hCursor] - the handle of the cursor to restart
// [chapter] - the chapter to restart
//
// Returns: SCODE - the status of the operation.
//
// Notes:
//
// History: 17 Apr 1997 EmilyB Created
//
//--------------------------------------------------------------------------
void
CAsyncQuery::RestartPosition(
ULONG hCursor,
CI_TBL_CHAPT chapter)
{
CTableCursor & rCursor = _aCursors.Lookup(hCursor);
CTableSource & rSource = rCursor.GetSource();
rSource.RestartPosition ( chapter );
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::RatioFinished, public
//
// Synopsis: Return the completion status as a fraction
//
// Arguments: [hCursor] - the handle of the cursor to check completion for
// [rulDenominator] - on return, denominator of fraction
// [rulNumerator] - on return, numerator of fraction
// [rcRows] - on return, number of rows in cursor
// [rfNewRows] - on return, TRUE if new rows available
//
// Returns: nothing
//
// Notes: A handle of zero can be passed for use with sequential
// cursors to check completion before a handle exists.
//
//--------------------------------------------------------------------------
void CAsyncQuery::RatioFinished(
ULONG hCursor,
DBCOUNTITEM & rulDenominator,
DBCOUNTITEM & rulNumerator,
DBCOUNTITEM & rcRows,
BOOL & rfNewRows
) {
if (hCursor != 0)
CTableCursor& rCursor = _aCursors.Lookup(hCursor); // For error check
rulDenominator = 1;
rulNumerator = 0;
rfNewRows = FALSE;
unsigned status = QUERY_FILL_STATUS(_Table.Status());
//
// SPECDEVIATION: should do something more meaningful with STAT_ERROR
// RatioFinished should probably fail in this case.
//
if (STAT_DONE == status ||
STAT_ERROR == status ||
STAT_REFRESH == status)
{
rulNumerator = 1;
rcRows = _Table.RowCount();
if (rcRows != _cRowsLastAsked)
{
_cRowsLastAsked = rcRows;
rfNewRows = TRUE;
}
return;
}
_Table.RatioFinished (rulDenominator, rulNumerator, rcRows);
if (rcRows != _cRowsLastAsked)
{
_cRowsLastAsked = rcRows;
rfNewRows = TRUE;
}
Win4Assert( rulDenominator >= rulNumerator );
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::Compare, public
//
// Synopsis: Return the approximate current position as a fraction
//
// Arguments: [hCursor] - the handle of the cursor to compare bmks from
// [bmkFirst] - First bookmark to compare
// [bmkSecond] - Second bookmark to compare
// [rdwComparison] - on return, comparison value
//
// Returns: nothing
//
//--------------------------------------------------------------------------
void CAsyncQuery::Compare(
ULONG hCursor,
CI_TBL_CHAPT chapt,
CI_TBL_BMK bmkFirst,
CI_TBL_BMK bmkSecond,
DWORD & rdwComparison
) {
rdwComparison = DBCOMPARE_NOTCOMPARABLE;
CTableCursor& rCursor = _aCursors.Lookup(hCursor);
CTableSource & rSource = rCursor.GetSource();
DBCOUNTITEM ulNum1, ulDen1;
SCODE scRet = rSource.GetApproximatePosition( chapt,
bmkFirst,
&ulNum1,
&ulDen1);
DBCOUNTITEM ulNum2, ulDen2;
if (SUCCEEDED(scRet))
{
scRet = rSource.GetApproximatePosition( chapt,
bmkSecond,
&ulNum2,
&ulDen2);
}
if (SUCCEEDED(scRet))
{
Win4Assert(ulDen1 == ulDen2);
if (ulNum1 < ulNum2)
rdwComparison = DBCOMPARE_LT;
else if (ulNum1 > ulNum2)
rdwComparison = DBCOMPARE_GT;
else // ulNum1 == ulNum2
rdwComparison = DBCOMPARE_EQ;
}
// CLEANCODE - have the source table THROW if errors in GetApproximatePosition
if (scRet != S_OK)
THROW(CException(scRet));
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetApproximatePosition, public
//
// Synopsis: Return the approximate current position as a fraction
//
// Arguments: [hCursor] - the handle of the cursor to retrieve info. from
// [bmk] - bookmark of row to get position of
// [pulNumerator] - on return, numerator of fraction
// [pulDenominator] - on return, denominator of fraction
//
// Returns: nothing
//
//--------------------------------------------------------------------------
void
CAsyncQuery::GetApproximatePosition(
ULONG hCursor,
CI_TBL_CHAPT chapt,
CI_TBL_BMK bmk,
DBCOUNTITEM * pulNumerator,
DBCOUNTITEM * pulDenominator
) {
CTableCursor& rCursor = _aCursors.Lookup(hCursor);
CTableSource & rSource = rCursor.GetSource();
SCODE scRet = rSource.GetApproximatePosition( chapt,
bmk,
pulNumerator,
pulDenominator);
// CLEANCODE - have the source table THROW if errors in GetApproximatePosition
if (scRet != S_OK)
THROW(CException(scRet));
}
//+---------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetNotifications, private
//
// Synopsis: Retrieves the notification info from the query object
// row data.
//
// Arguments: [rSync] -- notification synchronization info
// [rParams] -- notification data info
//
// Returns: SCODE
//
// History: 10-24-94 dlee created
//
//----------------------------------------------------------------------------
SCODE
CAsyncQuery::GetNotifications(
CNotificationSync & rSync,
DBWATCHNOTIFY & changeType )
{
return _Table.GetNotifications(rSync,changeType);
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::SetWatchMode
//
// Synopsis: Stub implementation
//
// Arguments: [phRegion] -- handle to watch region
// [mode] -- watch mode
//
// History: May-2-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::SetWatchMode (
HWATCHREGION* phRegion,
ULONG mode)
{
if (*phRegion == watchRegionInvalid)
_Table.CreateWatchRegion (mode, phRegion);
else
_Table.ChangeWatchMode (*phRegion, mode);
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetWatchInfo
//
// Synopsis: Stub implementation
//
// Arguments: [hRegion] -- handle to watch region
// [pMode] -- watch mode
// [pChapter] -- chapter
// [pBookmark] -- bookmark
// [pcRows] -- number of rows
//
// History: May-2-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::GetWatchInfo (
HWATCHREGION hRegion,
ULONG* pMode,
CI_TBL_CHAPT* pChapter,
CI_TBL_BMK* pBookmark,
DBCOUNTITEM* pcRows)
{
_Table.GetWatchRegionInfo (hRegion, pChapter, pBookmark, (DBROWCOUNT *)pcRows);
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::ShrinkWatchRegion
//
// Synopsis: Stub implementation
//
// Arguments: [hRegion] -- handle to watch region
// [pChapter] -- chapter
// [pBookmark] -- bookmark
// [cRows] -- number of rows
//
// History: May-2-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::ShrinkWatchRegion (
HWATCHREGION hRegion,
CI_TBL_CHAPT chapter,
CI_TBL_BMK bookmark,
LONG cRows )
{
if (cRows == 0)
_Table.DeleteWatchRegion (hRegion);
else
_Table.ShrinkWatchRegion (hRegion, chapter, bookmark, cRows);
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::Refresh
//
// Synopsis: Stub implementation
//
// Arguments: [] --
//
// History: Arp-4-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::Refresh()
{
_Table.Refresh();
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetQueryStatus, public
//
// Synopsis: Return the query status
//
// Arguments: [hCursor] - the handle of the cursor to check completion for
// [rdwStatus] - on return, the query status
//
// Returns: nothing
//
//--------------------------------------------------------------------------
void CAsyncQuery::GetQueryStatus(
ULONG hCursor,
DWORD & rdwStatus)
{
rdwStatus = _Table.Status();
}
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::GetQueryStatusEx, public
//
// Synopsis: Return the query status plus bonus information. It's kind
// of an odd assortment of info, but it saves net trips.
//
// Arguments: [hCursor] - handle of the cursor to check completion for
// [rdwStatus] - returns the query status
// [rcFilteredDocuments] - returns # of filtered docs
// [rcDocumentsToFilter] - returns # of docs to filter
// [rdwRatioFinishedDenominator] - ratio finished denom
// [rdwRatioFinishedNumerator] - ratio finished num
// [bmk] - bmk to find
// [riRowBmk] - index of bmk row
// [rcRowsTotal] - # of rows in table
//
// History: Nov-9-96 dlee Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::GetQueryStatusEx(
ULONG hCursor,
DWORD & rdwStatus,
DWORD & rcFilteredDocuments,
DWORD & rcDocumentsToFilter,
DBCOUNTITEM & rdwRatioFinishedDenominator,
DBCOUNTITEM & rdwRatioFinishedNumerator,
CI_TBL_BMK bmk,
DBCOUNTITEM & riRowBmk,
DBCOUNTITEM & rcRowsTotal )
{
rdwStatus = _Table.Status();
CIF_STATE state;
state.cbStruct = sizeof state;
SCODE sc = _xCiManager->GetStatus( &state );
if ( SUCCEEDED( sc ) )
{
rcFilteredDocuments = state.cFilteredDocuments;
rcDocumentsToFilter = state.cDocuments;
}
else
{
ciDebugOut(( DEB_ERROR, "CAsyncQuery::GetQueryStatusEx, get status failed, 0x%x\n", sc ));
rcFilteredDocuments = 0;
rcDocumentsToFilter = 0;
}
DBCOUNTITEM cRows;
BOOL fNewRows;
RatioFinished( hCursor,
rdwRatioFinishedDenominator,
rdwRatioFinishedNumerator,
cRows,
fNewRows );
GetApproximatePosition( hCursor,
0,
bmk,
& riRowBmk,
& rcRowsTotal );
} //GetQueryStatusEx
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::WorkIdToPath
//
// Synopsis: Converts a wid to a path
//
// Arguments: [wid] -- of the file to be translated
// [funnyPath] -- resulting path
//
// History: Jun-1-95 dlee Created
//
//--------------------------------------------------------------------------
void CAsyncQuery::WorkIdToPath( WORKID wid, CFunnyPath & funnyPath )
{
if ( _fCanDoWorkidToPath )
{
ULONG cbBuf = MAX_PATH * sizeof WCHAR; // first guess -- it may be more
XArray<BYTE> xBuf( cbBuf );
CInlineVariant * pVariant = (CInlineVariant *) xBuf.GetPointer();
if (! _Table.WorkIdToPath( wid, *pVariant, cbBuf ) )
{
if ( 0 != cbBuf )
{
BYTE *pb = xBuf.Acquire();
delete [] pb;
cbBuf += sizeof CInlineVariant;
xBuf.Init( cbBuf );
_Table.WorkIdToPath( wid, *pVariant, cbBuf );
}
}
if ( 0 != cbBuf )
{
WCHAR *pwc = (WCHAR *) pVariant->GetVarBuffer();
funnyPath.SetPath( pwc );
}
}
else
{
ICiCDocName *pDocName;
SCODE sc = _xNameToWidTranslator->QueryDocName( &pDocName );
if ( SUCCEEDED( sc ) )
{
XInterface<ICiCDocName> xDocName( pDocName );
sc = _xNameToWidTranslator->WorkIdToDocName( wid,
xDocName.GetPointer() );
if ( SUCCEEDED( sc ) && sc != CI_S_WORKID_DELETED )
{
// PERFFIX: Here we are using two buffers XGrowable and CFunnyPath.
// This can be avoided if xDocName->Get can take in CFunnyPath instead of WCHAR*
XGrowable<WCHAR> xBuf(MAX_PATH);
ULONG cb = xBuf.SizeOf();
sc = xDocName->Get( (BYTE *) xBuf.Get(), &cb );
if ( CI_E_BUFFERTOOSMALL == sc )
{
xBuf.SetSizeInBytes( cb );
sc = xDocName->Get( (BYTE *) xBuf.Get(), &cb );
}
if ( SUCCEEDED( sc ) )
{
funnyPath.SetPath( xBuf.Get() );
}
}
}
}
} //WorkIdToPath
BOOL CAsyncQuery::CanDoWorkIdToPath()
{
return _fCanDoWorkidToPath;
} //CanDoWorkIdToPath
//+-------------------------------------------------------------------------
//
// Member: CAsyncQuery::FetchDeferredValue
//
// Synopsis: Fetch deferred value from property cache
//
// Arguments: [wid] -- Workid.
// [ps] -- Property to be fetched.
// [var] -- Property returned here.
//
// History: Jun-1-95 KyleP Created
//
//--------------------------------------------------------------------------
BOOL CAsyncQuery::FetchDeferredValue(
WORKID wid,
CFullPropSpec const & ps,
PROPVARIANT & var )
{
//
// If using a NULL catalog, assume a file system and go get the value.
// The NULL catalog case is only supported by fsci, anyway, so it's
// ok to do this hack.
//
if ( _fCanDoWorkidToPath )
{
CFunnyPath funnyPath;
WorkIdToPath( wid, funnyPath );
COLEPropManager propMgr;
propMgr.Open( funnyPath );
return propMgr.ReadProperty( ps, var );
}
return _QExec->FetchDeferredValue( wid, ps, var );
} //FetchDeferredValue