windows-nt/Source/XPSP1/NT/inetsrv/query/cifrmwrk/dll/fatquery.cxx
2020-09-26 16:20:57 +08:00

683 lines
22 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: FATQuery.cxx
//
// Contents: IInternalQuery interface
//
// History: 18-Jun-93 KyleP Created
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <fatquery.hxx>
#include <pidmap.hxx>
#include <coldesc.hxx>
#include <lang.hxx>
#include <qparse.hxx>
#include <cci.hxx>
#include <rowset.hxx>
#include <query.hxx>
#include <seqquery.hxx>
#include <qoptimiz.hxx>
#include <cifailte.hxx>
#include <dbprputl.hxx>
#include <dbprpini.hxx>
#include <ciframe.hxx>
#include <pidcvt.hxx>
#include <proprst.hxx>
static const GUID guidQuery = PSGUID_QUERY;
extern CLocateDocStore g_DocStoreLocator;
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::QueryInterface, public
//
// Arguments: [ifid] -- Interface id
// [ppiuk] -- Interface return pointer
//
// Returns: Error. No rebind from this class is supported.
//
// History: 01-Oct-92 KyleP Created
//
//--------------------------------------------------------------------------
STDMETHODIMP CGenericQuery::QueryInterface( REFIID ifid, void ** ppiuk )
{
if ( IID_IUnknown == ifid )
{
*ppiuk = (void *)((IUnknown *)this);
AddRef();
return S_OK;
}
else
{
*ppiuk = 0;
return E_NOINTERFACE;
}
}
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::AddRef, public
//
// Synopsis: Reference the virtual table.
//
// History: 01-Oct-92 KyleP Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGenericQuery::AddRef(void)
{
return InterlockedIncrement( (long *)&_ref );
}
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::Release, public
//
// Synopsis: De-Reference the virtual table.
//
// Effects: If the ref count goes to 0 then the table is deleted.
//
// History: 01-Oct-92 KyleP Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CGenericQuery::Release(void)
{
long l = InterlockedDecrement( (long *)&_ref );
if ( l <= 0 )
{
// delete self
vqDebugOut(( DEB_ITRACE,
"FAT IInternalQuery unreferenced. Deleting.\n" ));
delete this;
return 0;
}
return l;
}
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::Execute, public
//
// Synopsis: Executes a query. Helper for ICommand::Execute.
//
// Arguments: [pUnkOuter] -- Outer unknown
// [pRestriction] -- Query restriction
// [pidmap] -- pid mapper for output, sort, category columns
// [rColumns] -- Output columns in IRowset
// [rSort] -- Initial sort
// [xProps] -- Rowset properties (query flags)
// [rCateg] -- Categorization specification
// [cRowsets] -- # of rowsets
// [ppUnknowns] -- IUnknown pointers returned here
// [aAccessors] -- Bag of accessors which rowsets need to inherit
//
// Returns: Throws on error
//
// History: 26 Nov 1995 AlanW Created
//
//--------------------------------------------------------------------------
void CGenericQuery::Execute( IUnknown * pUnkOuter,
RESTRICTION * pRestriction,
CPidMapperWithNames & pidmap,
CColumnSet & rColumns,
CSortSet & rSort,
XPtr<CMRowsetProps> & xProps,
CCategorizationSet & rCateg,
ULONG cRowsets,
IUnknown ** ppUnknowns,
CAccessorBag & aAccessors,
IUnknown * pUnkCreator )
{
#if 1
Win4Assert( FALSE );
#else
SCODE sc = S_OK;
TRY
{
if (0 == ppUnknowns)
THROW( CException( E_INVALIDARG ));
if ( cRowsets != 1 + (rCateg.Count() ? rCateg.Count() : 0) )
THROW( CException( E_INVALIDARG ));
if (_QueryUnknown.IsQueryActive())
{
vqDebugOut(( DEB_ERROR,
"CGenericQuery: Query already active.\n" ));
// pec variance: only if query changed
THROW( CException( DB_E_OBJECTOPEN ));
}
//
// Parameter checking.
//
*ppUnknowns = 0; // in case of error or exception
//
// Convert parsed query into low-level query understood by
// the Query/CI engine. This process expands the query tree and
// also maps GUID\DISPID and GUID\NAME style property names to
// a ULONG pid.
//
//
// Duplicate the output column set.
//
XColumnSet ColDup( new CColumnSet ( rColumns.Count() ) );
for ( unsigned i = 0; i < rColumns.Count(); i++ )
{
ColDup->Add( rColumns.Get(i), i );
}
//
// get a pointer to CLangList
//
CLangList * pLangList = 0;
ICiManager * pICiManager = 0;
ICiFrameworkQuery * pICiFrameworkQuery = 0;
XInterface<ICiManager> xICiManager;
XInterface<ICiFrameworkQuery> xICiFrameworkQuery;
SCODE sc = _xDocStore->GetContentIndex(&pICiManager);
if ( FAILED(sc) )
{
THROW( CException(sc) );
}
else
{
xICiManager.Set(pICiManager);
}
sc = xICiManager->QueryInterface(IID_ICiFrameworkQuery,(void **)&pICiFrameworkQuery);
if ( FAILED(sc) )
{
THROW( CException(sc) );
}
else
{
xICiFrameworkQuery.Set(pICiFrameworkQuery);
}
sc = xICiFrameworkQuery->GetLangList( (void **) &pLangList);
if ( FAILED(sc) )
{
THROW( CException(sc) );
}
//
// end of getting a pointer to the langlist
//
//
// Set up a property mapper. Used for restriction parsing and sort/output
// column translation.
//
XInterface<IPropertyMapper> xPropMapper;
sc = _xDocStore->GetPropertyMapper( xPropMapper.GetPPointer() );
if ( FAILED( sc ) )
{
vqDebugOut(( DEB_ERROR, "CGenericQuery::Execute, GetPropertyMapper error 0x%x\n", sc ));
THROW( CException( sc ) );
}
//
// Adjust pidmap to translate properties. NOTE: Undone in CATCH for failure case.
//
CPidConverter PidConverter( xPropMapper.GetPointer() );
pidmap.SetPidConverter( &PidConverter );
XRestriction rstParsed;
DWORD dwQueryStatus = 0;
if ( pRestriction )
{
CQParse qparse( pidmap, *pLangList ); // Maps name to pid
rstParsed.Set( qparse.Parse( (CRestriction *)pRestriction ) );
DWORD dwParseStatus = qparse.GetStatus();
if ( ( 0 != ( dwParseStatus & CI_NOISE_PHRASE ) ) &&
( ((CRestriction *)pRestriction)->Type() != RTVector ) )
{
vqDebugOut(( DEB_WARN, "Query contains phrase composed "
"entirely of noise words.\n" ));
THROW( CException( QUERY_E_ALLNOISE ) );
}
const DWORD dwCiNoise = CI_NOISE_IN_PHRASE | CI_NOISE_PHRASE;
if ( 0 != ( dwCiNoise & dwParseStatus ) )
dwQueryStatus |= STAT_NOISE_WORDS;
}
//
// Duplicate the sort definition.
//
XSortSet SortDup;
// Only create a sort duplicate if we have a sort spec AND it
// actually contains anything.
if ( 0 != rSort.Count() )
{
SortDup.Set( new CSortSet ( rSort.Count() ) );
for ( unsigned i = 0; i < rSort.Count(); i++ )
{
SortDup->Add( rSort.Get(i), i );
}
}
//
// Duplicate the categorization specification
//
XCategorizationSet CategDup;
if (cRowsets > 1)
CategDup.Set( new CCategorizationSet ( rCateg ) );
//
// Re-map property ids.
//
//
// TODO: Get rid of this whole pid remap thing. We should
// really be able to do it earlier now that the pidmap
// can be set up to convert fake to real pids.
//
XInterface<CPidRemapper> pidremap( new CPidRemapper( pidmap,
xPropMapper,
0, // rstParsed.GetPointer(),
ColDup.GetPointer(),
SortDup.GetPointer() ) );
//
// WorkID may be added to the columns requested in SetBindings.
// Be sure it's in the pidremap from the beginning.
//
CFullPropSpec psWorkId(guidQuery, DISPID_QUERY_WORKID);
pidremap->NameToReal( &psWorkId );
XInterface<PQuery> xQuery;
XArray<ULONG> aCursors(cRowsets);
ICiQueryPropertyMapper *pQueryPropMapper;
sc = pidremap->QueryInterface( IID_ICiQueryPropertyMapper,
(void **) &pQueryPropMapper );
if ( FAILED(sc) )
{
vqDebugOut(( DEB_ERROR, "DoCreateQuery - QI for property mapper failed 0x%x\n", sc ));
THROW ( CException( sc ) ) ;
}
XInterface<ICiQueryPropertyMapper> xQueryPropMapper( pQueryPropMapper );
ICiCQuerySession *pQuerySession;
SCODE scode = _xDocStore->GetQuerySession( &pQuerySession );
if ( FAILED(scode) )
{
vqDebugOut(( DEB_ERROR, "CGenericQuery::Execute - QI failed 0x%x\n", scode ));
THROW ( CException( scode ) ) ;
}
XInterface<ICiCQuerySession> xQuerySession( pQuerySession );
//
// Initialize the query session
//
xQuerySession->Init( pidmap.Count(),
(FULLPROPSPEC const * const *)pidmap.GetPointer(),
_xDbProperties.GetPointer(),
xQueryPropMapper.GetPointer() );
//
// Optimize query.
//
CRowsetProperties Props;
Props.SetDefaults( xProps->GetPropertyFlags(),
xProps->GetMaxOpenRows(),
xProps->GetMemoryUsage(),
xProps->GetMaxResults(),
xProps->GetCommandTimeout() );
XQueryOptimizer xqopt( new CQueryOptimizer( xQuerySession,
_xDocStore.GetPointer(),
rstParsed,
ColDup.GetReference(),
SortDup.GetPointer(),
pidremap.GetReference(),
Props,
dwQueryStatus ) );
vqDebugOut(( DEB_ITRACE, "Query has %s1 component%s\n",
xqopt->IsMultiCursor() ? "> " : "",
xqopt->IsMultiCursor() ? "(s)" : "" ));
vqDebugOut(( DEB_ITRACE, "Current component of query %s fully sorted\n",
xqopt->IsFullySorted() ? "is" : "is not" ));
vqDebugOut(( DEB_ITRACE, "Current component of query %s positionable\n",
xqopt->IsPositionable() ? "is" : "is not" ));
if ( (xProps->GetPropertyFlags() & eLocatable) == 0 )
{
//
// If all components are fully sorted, then
// it doesn't matter how many there are. A
// merge could be done. But be careful, you
// need an ordering even with a null sort.
//
// Categorized queries must go through bigtable for now
// If someday we support categorizations that don't require
// sorting then change this check.
//
if ( ( !xqopt->IsMultiCursor() ) &&
( xqopt->IsFullySorted() ) &&
( 1 == cRowsets ) )
{
xQuery.Set( new CSeqQuery( xqopt,
ColDup,
aCursors.GetPointer(),
pidremap,
_xDocStore.GetPointer() ) );
}
else
{
xQuery.Set( new CAsyncQuery( xqopt,
ColDup,
SortDup,
CategDup,
cRowsets,
aCursors.GetPointer(),
pidremap,
FALSE,
_xDocStore.GetPointer(),
0 ) );
}
}
else
{
xQuery.Set( new CAsyncQuery( xqopt,
ColDup,
SortDup,
CategDup,
cRowsets,
aCursors.GetPointer(),
pidremap,
(xProps->GetPropertyFlags() & eWatchable) != 0,
_xDocStore.GetPointer(),
0 ) );
}
//
// If we've been instructed to create a non-asynchronous cursor,
// then we don't return to the caller until we have the first
// row. In the case of a sorted sequential cursor (which is
// implemented with a table), we wait until the query is
// complete.
//
if ( (xProps->GetPropertyFlags() & eAsynchronous) == 0 )
{
//
// An event would be better than these Sleeps...
//
ULONG sleepTime = 500;
while ( TRUE )
{
DBCOUNTITEM ulDenominator;
DBCOUNTITEM ulNumerator;
DBCOUNTITEM cRows;
BOOL fNewRows;
xQuery->RatioFinished( 0,
ulDenominator,
ulNumerator,
cRows,
fNewRows );
if ( ulNumerator == ulDenominator )
break;
Sleep( sleepTime );
sleepTime *= 2;
sleepTime = min( sleepTime, 4000 );
}
}
// Make rowsets for each level in the hierarchy (usually 1).
// Rowset 0 is the top of the hierarchy.
_QueryUnknown.ReInit();
CRowsetArray apRowsets( cRowsets );
XArray<IUnknown *> xapUnknowns( cRowsets );
CMRowsetProps & OrigProps = xProps.GetReference();
#ifdef CI_FAILTEST
NTSTATUS failStatus = CI_CORRUPT_CATALOG;
ciFAILTEST( failStatus );
#endif // CI_FAILTEST
for (unsigned r = 0; r < cRowsets; r++)
{
XPtr<CMRowsetProps> xTmp;
if ( 0 != r )
{
xTmp.Set( new CMRowsetProps( OrigProps ) );
xTmp->SetChaptered( TRUE );
}
if ( 1 != cRowsets && 0 == r )
xProps->SetChaptered( FALSE );
apRowsets[r] = new CRowset( pUnkOuter,
&xapUnknowns[r],
(r == (cRowsets - 1)) ?
rColumns :
rCateg.Get(r)->GetColumnSet(),
pidmap,
xQuery.GetReference(),
(IUnknown &) _QueryUnknown,
0 != r,
( 0 == r ) ? xProps : xTmp,
aCursors[r],
aAccessors,
pUnkCreator );
}
for (r = 0; r < cRowsets; r++)
{
if (r < cRowsets-1)
apRowsets[r]->SetRelatedRowset( apRowsets[r+1] );
ppUnknowns[r] = (IRowset *) xapUnknowns[r];
}
// xQuery goes out of scope, doing a Release() on it.
// Each rowset above has done an AddRef() on it and they own it.
_QueryUnknown.ReInit( cRowsets, apRowsets.Acquire() );
}
CATCH( CException,e )
{
ciDebugOut(( DEB_ERROR,
"Error 0x%X while executing query\n",
e.GetErrorCode() ));
pidmap.SetPidConverter( 0 );
ICiManager *pCiManager = 0;
sc = _xDocStore->GetContentIndex( &pCiManager );
if ( SUCCEEDED( sc ) )
{
//
// GetContentIndex may fail during shutdown
//
ICiFrameworkQuery * pIFwQuery = 0;
sc = pCiManager->QueryInterface( IID_ICiFrameworkQuery,
(void **) &pIFwQuery );
pCiManager->Release();
if ( pIFwQuery )
{
pIFwQuery->ProcessError( e.GetErrorCode() );
pIFwQuery->Release();
}
}
RETHROW();
}
END_CATCH
pidmap.SetPidConverter( 0 );
#endif
} //Execute
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::CGenericQuery, public
//
// Synopsis: Opens file system directory for query
//
// Arguments: [pDbProperties] -- Properties to be used for this query
//
// History: 18-Jun-93 KyleP Created
// 22-Apr-97 KrishnaN Changed header block
//
//--------------------------------------------------------------------------
CGenericQuery::CGenericQuery( IDBProperties * pDbProperties )
: PIInternalQuery( 0 ),
#pragma warning(disable : 4355) // 'this' in a constructor
_QueryUnknown( * ((IUnknown *) this) )
#pragma warning(default : 4355) // 'this' in a constructor
{
pDbProperties->AddRef();
_xDbProperties.Set( pDbProperties );
//
// Locate the DocStore for the given set of properties.
//
ICiCDocStoreLocator * pLocator = g_DocStoreLocator.Get();
if ( 0 == pLocator )
{
//
// Determine the GUID of the docstore locator from the query and
// use it.
//
CGetDbProps dbProps;
dbProps.GetProperties( pDbProperties,
CGetDbProps::eClientGuid );
GUID const * pGuidClient = dbProps.GetClientGuid();
if ( 0 == pGuidClient )
{
ciDebugOut(( DEB_ERROR, "The DocStoreLocator Guid is invalid\n" ));
THROW( CException( E_INVALIDARG ) );
}
pLocator = g_DocStoreLocator.Get( *pGuidClient );
}
Win4Assert( pLocator != 0 );
XInterface<ICiCDocStoreLocator> xLocator( pLocator );
ICiCDocStore *pDocStore;
SCODE sc = xLocator->LookUpDocStore( pDbProperties, &pDocStore, TRUE );
if ( FAILED(sc) )
{
ciDebugOut(( DEB_ITRACE, "Failed to locate doc store. Error (0x%X)\n",
sc ));
THROW( CException(sc) );
}
Win4Assert( pDocStore != 0 );
_xDocStore.Set( pDocStore );
AddRef();
} //CGenericQuery
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::CGenericQuery, public
//
// Synopsis: Opens file system directory for query
//
// Arguments: [pDbProperties] -- Properties to be used for this query
// [pDocStore] -- Docstore to query against
//
// History: 22-Apr-97 KrishnaN Created
//
//--------------------------------------------------------------------------
CGenericQuery::CGenericQuery( IDBProperties * pDbProperties,
ICiCDocStore * pDocStore )
: PIInternalQuery( 0 ),
#pragma warning(disable : 4355) // 'this' in a constructor
_QueryUnknown( * ((IUnknown *) this) )
#pragma warning(default : 4355) // 'this' in a constructor
{
pDbProperties->AddRef();
_xDbProperties.Set( pDbProperties );
Win4Assert( pDocStore != 0 );
pDocStore->AddRef();
_xDocStore.Set( pDocStore );
AddRef();
} //CGenericQuery
//+-------------------------------------------------------------------------
//
// Member: CGenericQuery::~CGenericQuery, public
//
// Synopsis: Required virtual destructor
//
// History: 17-Jul-93 KyleP Created
//
//--------------------------------------------------------------------------
CGenericQuery::~CGenericQuery()
{
}