//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: qryperf.CXX // // Contents: performance test program // // History: 16 March 1996 dlee Created (from fsdbdrt) // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #define STRESS #ifdef STRESS #define GET_DATA_TOO unsigned cThreads = 8; const unsigned cLoopTimes = 0xffffffff; WCHAR *pwcCatalog = L"system"; WCHAR *pwcMachine = L"."; BOOL g_fStopNow = FALSE; const unsigned cmsSleep = 3000; // 0 unsigned g_cmsSleep; BOOL g_fFetchRowsAtWill = TRUE; #else const unsigned cThreads = 8; const unsigned cLoopTimes = 1000; WCHAR *pwcCatalog = L"encarta"; WCHAR *pwcMachine = L"."; #endif BOOL g_fSQL = FALSE; #define DBINITCONSTANTS #include #include #include #include #include #include WCHAR g_awcCatalog[ MAX_PATH ]; WCHAR g_awcMachine[ MAX_PATH ]; BOOL g_fSequential = FALSE; template T Rand2( T t ) { return (T) abs( (int) ( rand() % t ) ); } template T Rand( T t ) { return (T) abs( GetTickCount() % t ); } CCoTaskAllocator CoTaskAllocator; void * CCoTaskAllocator::Allocate(ULONG cbSize) { return CoTaskMemAlloc( cbSize ); } void CCoTaskAllocator::Free( void *pv ) { CoTaskMemFree( pv ); } BOOL isEven(unsigned n) { return ( 0 == ( n & 1 ) ); } static const GUID guidSystem = PSGUID_STORAGE; static const GUID guidQuery = PSGUID_QUERY; static const GUID guidRowsetProps = DBPROPSET_ROWSET; static CDbColId psName( guidSystem, PID_STG_NAME ); static CDbColId psPath( guidSystem, PID_STG_PATH ); static CDbColId psSize( guidSystem, PID_STG_SIZE ); static CDbColId psWriteTime( guidSystem, PID_STG_WRITETIME ); static CDbColId psContents( guidSystem, PID_STG_CONTENTS ); static CDbColId psRank( guidQuery, DISPID_QUERY_RANK ); static CDbColId * aColIds[] = { &psName, &psPath, &psSize, &psWriteTime, &psRank }; static ULONG cColIds = sizeof aColIds / sizeof aColIds[0]; CDbCmdTreeNode * FormQueryTree( CDbCmdTreeNode * pRst, CDbColumns & Cols, CDbSortSet * pSort ); IRowsetScroll * InstantiateRowset( ICommand *pCommandIn, DWORD dwDepth, LPWSTR pwszScope, XPtr & xTree, WCHAR const * pwcQuery, REFIID riid, BOOL fAsynchronous ); HACCESSOR MapColumns( IUnknown * pUnknown, ULONG cCols, DBBINDING * pBindings, const DBID * pColIds ); void ReleaseAccessor( IUnknown * pUnknown, HACCESSOR hAcc ); DWORD __stdcall RunPerfTest(void *pv); void LogError( char const * pszFormat, ... ); void GetProcessInfo( WCHAR * pwcImage, LARGE_INTEGER & liUserTime, LARGE_INTEGER & liKernelTime, ULONG & cHandles, ULONGLONG & cbWorkingSet, ULONGLONG & cbPeakWorkingSet, ULONGLONG & cbPeakVirtualSize, ULONGLONG & cbNonPagedPoolUsage, ULONGLONG & cbPeakNonPagedPoolUsage ); inline _int64 mkTime( FILETIME ft ) { return (_int64) ft.dwLowDateTime + ( ( (_int64 ) ft.dwHighDateTime ) << 32 ); } //mkTime inline _int64 mkTime( FILETIME ftK, FILETIME ftU ) { return mkTime( ftK ) + mkTime( ftU ); } //mkTime inline _int64 mkTime( LARGE_INTEGER li ) { return (_int64) li.LowPart + ( ( (_int64) li.HighPart ) << 32 ); } //mkTime inline _int64 mkTime( LARGE_INTEGER liK, LARGE_INTEGER liU ) { return mkTime( liK ) + mkTime( liU ); } //mkTime void GetCiSvcTimes( LARGE_INTEGER & liCiSvcKernelTime, LARGE_INTEGER & liCiSvcUserTime ) { ULONG cHandles; ULONGLONG cbWorkingSet; ULONGLONG cbPeakWorkingSet; ULONGLONG cbPeakVirtualSize; ULONGLONG cbNonPagedPoolUsage; ULONGLONG cbPeakNonPagedPoolUsage; GetProcessInfo( L"cisvc.exe", liCiSvcUserTime, liCiSvcKernelTime, cHandles, cbWorkingSet, cbPeakWorkingSet, cbPeakVirtualSize, cbNonPagedPoolUsage, cbPeakNonPagedPoolUsage ); } //GetCiSvcTimes void RunQuerySuite() { HANDLE ah[ 200 ]; DWORD dwID; for ( unsigned x = 0; x < cThreads; x++ ) { #ifdef STRESS Sleep( GetCurrentThreadId() ); #endif // STRESS ah[x] = CreateThread( 0, 65536, RunPerfTest, 0, 0, &dwID ); } WaitForMultipleObjects( cThreads, ah, TRUE, INFINITE ); for ( x = 0; x < cThreads; x++ ) CloseHandle( ah[x] ); } //RunQuerySuite void Usage() { printf( "usage: qryperf [-c:catalog] [-m:machine] [-d:delay(ms)] [-q(onarch)] [-s:(0|1)] [-t:threads] -f(etch at will)\n" ); exit(1); } //Usage extern "C" int __cdecl wmain( int argc, WCHAR * argv[] ) { HRESULT hr = CoInitialize( 0 ); if ( FAILED( hr ) ) exit( 1 ); wcscpy( g_awcCatalog, pwcCatalog ); wcscpy( g_awcMachine, pwcMachine ); #ifdef STRESS g_cmsSleep = cmsSleep; #endif for ( int i = 1; i < argc; i++ ) { if ( '/' == argv[i][0] || '-' == argv[i][0] ) { WCHAR c = (WCHAR) tolower( argv[i][1] ); if ( L':' != argv[i][2] && c != L'q' && c != 'f' ) Usage(); if ( L'c' == c ) wcscpy( g_awcCatalog, argv[i] + 3 ); else if ( L'm' == c ) wcscpy( g_awcMachine, argv[i] + 3 ); #ifdef STRESS else if ( L'd' == c ) g_cmsSleep = _wtoi( argv[i] + 3 ); else if ( 't' == c ) cThreads = _wtoi( argv[i] + 3 ); else if ( 'f' == c ) g_fFetchRowsAtWill = FALSE; #endif else if ( 'q' == c ) g_fSQL = TRUE; else if ( L's' == c ) g_fSequential = _wtoi( argv[i] + 3 ); else Usage(); } else Usage(); } HANDLE hproc = GetCurrentProcess(); FILETIME ftCreate,ftExit,ftKernel,ftUser; GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser ); _int64 openTime = mkTime( ftKernel, ftUser ); { CI_STATE state; state.cbStruct = sizeof state; CIState( g_awcCatalog, g_awcMachine, &state ); } LARGE_INTEGER liCiSvcUserTime; LARGE_INTEGER liCiSvcKernelTime; GetCiSvcTimes( liCiSvcKernelTime, liCiSvcUserTime ); _int64 ciStartTime = mkTime( liCiSvcKernelTime, liCiSvcUserTime ); GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser ); _int64 startTime = mkTime( ftKernel, ftUser ); RunQuerySuite(); CIShutdown(); GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser ); _int64 endTime = mkTime( ftKernel, ftUser ); GetCiSvcTimes( liCiSvcKernelTime, liCiSvcUserTime ); _int64 ciEndTime = mkTime( liCiSvcKernelTime, liCiSvcUserTime ); #ifdef STRESS printf( "stress test client time: %d ms cisvc time: %d ms\n", (DWORD) ((endTime - startTime) / 10000 ), (DWORD) ((ciEndTime - ciStartTime) / 10000 ) ); #else printf( "%s test client time: %d ms cisvc time: %d ms\n", g_fSequential ? "sequential" : "non-sequential", (DWORD) ((endTime - startTime) / 10000 ), (DWORD) ((ciEndTime - ciStartTime) / 10000 ) ); #endif CoUninitialize(); return 0; } //main #ifdef GET_DATA_TOO class CBookMark { public: CBookMark() : cbBmk (0) {} CBookMark( DBBOOKMARK bmkSpecial ) : cbBmk (1) { abBmk[0] = (BYTE) bmkSpecial; } BOOL IsValid() const { return 0 != cbBmk; } void Invalidate () { cbBmk = 0; } BOOL IsEqual ( CBookMark& bmk) { if (cbBmk != bmk.cbBmk) return FALSE; return memcmp ( abBmk, bmk.abBmk, cbBmk ) == 0; } void MakeFirst() { cbBmk = sizeof (BYTE); abBmk[0] = (BYTE) DBBMK_FIRST; } BOOL IsFirst() { return cbBmk == sizeof(BYTE) && abBmk[0] == (BYTE) DBBMK_FIRST; } DBLENGTH cbBmk; BYTE abBmk[ 50 ]; }; void FetchAtWill( IRowset * pRowset, IUnknown * pAccessor, HACCESSOR hAccessor, DBCOUNTITEM cHits ) { if ( 0 == cHits ) return; XInterface xRS; SCODE sc = pRowset->QueryInterface( IID_IRowsetScroll, xRS.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "Can't QI for IID_IRowsetScroll\n" ); return; } const DBROWCOUNT cMaxToGet = 8; HROW aHRows[ cMaxToGet ]; HROW * paHRows = aHRows; // Fetch relative to first const BYTE bmkFirst = (BYTE) DBBMK_FIRST; for ( unsigned i = 0; i < 5 && !g_fStopNow; i++ ) { DBCOUNTITEM cRows; DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 ); DBROWOFFSET iStart = Rand2( cHits ); sc = xRS->GetRowsAt( 0, 0, 1, &bmkFirst, iStart, cToGet, &cRows, &paHRows ); if ( SUCCEEDED( sc ) ) xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 ); else LogError( "can't get %d rows at %d out of %d\n", cToGet, iStart, cHits ); } // Fetch relative to last const BYTE bmkLast = (BYTE) DBBMK_LAST; for ( i = 0; i < 5 && !g_fStopNow; i++ ) { DBCOUNTITEM cRows; DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 ); DBROWOFFSET iStart = Rand2( cHits ); sc = xRS->GetRowsAt( 0, 0, 1, &bmkLast, -iStart, cToGet, &cRows, &paHRows ); if ( SUCCEEDED( sc ) ) xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 ); else LogError( "can't get %d rows at %d from last out of %d\n", cToGet, -iStart, cHits ); } // Fetch relative to a random location static GUID guidBmk = DBBMKGUID; static CDbColId dbcolBookMark( guidBmk, PROPID_DBBMK_BOOKMARK ); DBBINDING aBmkColumn[] = { 0, sizeof DBLENGTH, 0, 0, 0, 0, 0, DBPART_VALUE | DBPART_LENGTH, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM, 50, 0, DBTYPE_BYTES, 0, 0 }; XInterface xAccessor; sc = xRS->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "can't create bookmark accessor IAccessor: %#x\n", sc ); return; } HACCESSOR hBmkAccessor; sc = xAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, aBmkColumn, 0, &hBmkAccessor, 0 ); if ( FAILED(sc) ) { LogError( "can't create accessor\n" ); return; } HROW aBmkHRows[ 1 ]; HROW * paBmkHRows = aBmkHRows; DBROWOFFSET iBmkStart = Rand2( cHits ); DBCOUNTITEM cBmkRows; sc = xRS->GetRowsAt( 0, 0, 1, &bmkFirst, iBmkStart, 1, &cBmkRows, &paBmkHRows ); if ( SUCCEEDED( sc ) ) { CBookMark bmk; sc = xRS->GetData( aBmkHRows[0], hBmkAccessor, &bmk ); if ( SUCCEEDED( sc ) && ( DB_S_ERRORSOCCURRED != sc ) ) { for ( unsigned i = 0; i < 5 && !g_fStopNow; i++ ) { DBCOUNTITEM cRows; DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 ); DBROWOFFSET iStart = Rand2( cHits ) - iBmkStart; sc = xRS->GetRowsAt( 0, 0, bmk.cbBmk, bmk.abBmk, iStart, cToGet, &cRows, &paHRows ); if ( SUCCEEDED( sc ) ) xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 ); else LogError( "can't getrowsat %d rows %d relative to bmk at %d, rowset has %d: %#x\n", cToGet, iStart, iBmkStart, cHits, sc ); } } else LogError( "can't GetData the bmk row: %#x\n", sc ); xRS->ReleaseRows( 1, aBmkHRows, 0, 0, 0 ); } else LogError( "can't GetRowsAt the bmk row: %#x\n", sc ); ReleaseAccessor( pAccessor, hBmkAccessor ); } //FetchAtWill #endif static DBBINDING aPropTestCols[] = { { 0,(sizeof ULONG_PTR)*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,(sizeof ULONG_PTR)*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,(sizeof ULONG_PTR)*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,(sizeof ULONG_PTR)*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, { 0,(sizeof ULONG_PTR)*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 }, }; void RunPerfQuery( CDbRestriction & CiRst, WCHAR const * pwcQuery, unsigned cExpectedHits, ICommand * pCommand, BOOL fSeq, BOOL fAsynchronous ) { CDbColumns cols( 5 ); BOOL fOk = cols.Add( psName, 0 ); if ( fOk ) cols.Add( psSize, 1 ); if ( fOk ) cols.Add( psWriteTime, 2 ); if ( fOk ) cols.Add( psPath, 3 ); if ( fOk ) cols.Add( psRank, 4 ); if ( !fOk ) { LogError(" can't create column specification\n" ); return; } unsigned cRetries = 0; DBCOUNTITEM cRowsReturned = 0; IRowset * pRowset = 0; { CDbSortSet ss( 1 ); #ifdef STRESS int x = Rand( cColIds ); int y = rand(); fOk = ss.Add( *aColIds[x], isEven( rand() ) ? QUERY_SORTDESCEND : QUERY_SORTASCEND, 0 ); #else fOk = ss.Add( psRank, QUERY_SORTDESCEND, 0); #endif if ( !fOk ) { LogError(" can't create sort specification\n" ); return; } XPtr xCmdTree( FormQueryTree( &CiRst, cols, fSeq ? 0 : &ss ) ); if ( xCmdTree.IsNull() ) return; pRowset = InstantiateRowset( pCommand, QUERY_DEEP, // Depth L"\\", // Scope xCmdTree, // DBCOMMANDTREE pwcQuery, fSeq ? IID_IRowset : IID_IRowsetScroll, fAsynchronous ); if ( 0 == pRowset ) { LogError(" can't get rowset\n" ); return; } XInterface xRowset( pRowset ); #ifdef GET_DATA_TOO // Get data DBID aDbCols[5]; aDbCols[0] = psName; aDbCols[1] = psSize; aDbCols[2] = psWriteTime; aDbCols[3] = psPath; aDbCols[4] = psRank; IUnknown * pAccessor = pRowset; HACCESSOR hAccessor = MapColumns( pAccessor, 5, aPropTestCols, aDbCols ); if ( 0 == hAccessor ) return; #endif // GET_DATA_TOO DBCOUNTITEM cTotal = 0; #ifdef STRESS const unsigned cFetchPasses = g_fFetchRowsAtWill ? 1000 : 5; #else const unsigned cFetchPasses = 3; #endif for ( unsigned i = 0; i < cFetchPasses; i++ ) { #ifdef STRESS if ( g_fStopNow ) break; #endif HROW aHRow[10]; HROW * pgrhRows = aHRow; SCODE sc = pRowset->GetNextRows(0, 0, 10, &cRowsReturned, &pgrhRows); if ( FAILED( sc ) ) { LogError( "'%ws' IRowset->GetNextRows returned 0x%x, cTotal: %d, cRowsReturned: %d\n", pwcQuery, sc, cTotal, cRowsReturned ); break; } cTotal += cRowsReturned; #ifdef GET_DATA_TOO PROPVARIANT* data[5]; for ( ULONG r = 0; r < cRowsReturned; r++ ) { SCODE sc = pRowset->GetData( pgrhRows[r], hAccessor, &data ); } #endif // GET_DATA_TOO if ( 0 != cRowsReturned ) pRowset->ReleaseRows( cRowsReturned, pgrhRows, 0, 0, 0 ); if ( DB_S_ENDOFROWSET == sc ) break; } #ifdef GET_DATA_TOO if ( !fSeq && g_fFetchRowsAtWill ) FetchAtWill( pRowset, pAccessor, hAccessor, cTotal ); ReleaseAccessor( pAccessor, hAccessor ); //printf( "query %ws returned %d hits\n", pwcQuery, cTotal ); #endif // GET_DATA_TOO if ( 0 == cTotal ) printf( "query %ws returned no hits\n", pwcQuery ); #if 0 if ( cTotal < __min( 30, cExpectedHits ) ) printf( "query %ws returned %d hits, expecting %d\n", pwcQuery, cTotal, cExpectedHits ); #endif } } //RunPerfQuery struct SQuery { WCHAR const * pwcQuery; unsigned cEncartaHits; }; static SQuery aQueries[] = { #ifdef STRESS { L"stereo", 4 }, { L"flex", 2 }, { L"agassi", 1 }, { L"detroit", 108 }, { L"miami", 60 }, { L"edison", 25 }, { L"bulb", 33 }, { L"elephant", 87 }, { L"radius", 43 }, { L"amplifier", 17 }, { L"drunk", 10 }, { L"grunt", 3 }, { L"war", 4241 }, { L"peace", 812 }, { L"river", 3402 }, { L"not", 4002 }, { L"city", 5567 }, { L"century", 4470 }, #else { L"stereo", 4 }, { L"flex", 2 }, { L"agassi", 1 }, { L"detroit", 108 }, { L"miami", 60 }, { L"edison", 25 }, { L"web", 57 }, { L"bulb", 33 }, { L"microsoft", 15 }, { L"elephant", 87 }, { L"radius", 43 }, { L"amplifier", 17 }, { L"drunk", 10 }, { L"grunt", 3 }, #endif // STRESS }; DWORD __stdcall RunPerfTest(void *pv) { static long cQueriesSoFar = 0; // #ifdef STRESS // srand( GetTickCount() + GetCurrentThreadId() ); // #endif XInterface xCommand; do { ICommand * pCommand = 0; SCODE sc = CICreateCommand( (IUnknown **) &pCommand, 0, IID_ICommand, g_awcCatalog, g_awcMachine ); if ( FAILED( sc ) ) LogError( "CICreateCommand failed: 0x%x\n", sc ); else xCommand.Set( pCommand ); } while ( xCommand.IsNull() ); for ( int x = 0; x < cLoopTimes; x++ ) { try { const int cQueries = sizeof aQueries / sizeof aQueries[0]; #ifdef STRESS int j = Rand( cQueries ); #else int j = ( x % cQueries ); #endif // STRESS CDbContentRestriction CiRst( aQueries[j].pwcQuery, psContents ); if ( !CiRst.IsValid() ) continue; #ifdef STRESS if ( 0 != g_cmsSleep ) Sleep( Rand( g_cmsSleep ) ); ICommand *pCmd = isEven( x ) ? xCommand.GetPointer() : 0; BOOL fSeq = ( Rand( 100 ) < 70 ); BOOL fAsynchronous = FALSE; if ( !fSeq ) fAsynchronous = ( Rand( 100 ) < 30 ); RunPerfQuery( CiRst, aQueries[j].pwcQuery, aQueries[j].cEncartaHits, pCmd, fSeq, fAsynchronous ); InterlockedIncrement( &cQueriesSoFar ); if ( 0 == ( cQueriesSoFar % 10 ) ) printf( "%d queries on catalog '%ws', machine '%ws'\n", cQueriesSoFar, g_awcCatalog, g_awcMachine ); if ( g_fStopNow ) return 0; #else RunPerfQuery( CiRst, aQueries[j].pwcQuery, aQueries[j].cEncartaHits, xCommand.GetPointer(), g_fSequential, FALSE ); #endif //STRESS } catch( CException & e ) { // ignore } } return 0; } //RunPerfTest //+--------------------------------------------------------------------------- // // Function: FormQueryTree // // Synopsis: Forms a query tree consisting of the projection nodes, // sort node(s), selection node and the restriction tree. // // Arguments: [pRst] - pointer to Restriction tree describing the query // [Cols] - Columns in the resulting table // [pSort] - pointer to sort set; may be null // // Returns: A pointer to the query tree. It is the responsibility of // the caller to later free it. // // History: 06 July 1995 AlanW Created // //---------------------------------------------------------------------------- CDbCmdTreeNode * FormQueryTree( CDbCmdTreeNode * pRst, CDbColumns & Cols, CDbSortSet * pSort ) { CDbCmdTreeNode * pTree = 0; // return value if ( 0 != pRst ) { // // First create a selection node and append the restriction tree to it // CDbSelectNode * pSelect = new CDbSelectNode(); if ( 0 == pSelect ) { LogError("FormQueryTree: can't make CDbSelectNode\n" ); return 0; } pTree = pSelect; if ( !pSelect->IsValid() ) { delete pTree; LogError("FormQueryTree: select node isn't valid\n" ); return 0; } // // Clone the restriction and use it. // CDbCmdTreeNode * pExpr = pRst->Clone(); if ( 0 == pExpr ) { delete pTree; LogError("FormQueryTree: can't clone the restriction\n" ); return 0; } #ifdef STRESS else { CDbContentRestriction * p = (CDbContentRestriction *) pExpr; if ( !p->IsValid() ) { LogError( "clone failed illegally!\n" ); DebugBreak(); } } #endif // // Now make the restriction a child of the selection node. // pSelect->AddRestriction( pExpr ); } else { // // No restriction. Just use table ID node as start of tree. // pTree = new CDbTableId(); if ( 0 == pTree ) { LogError("FormQueryTree: can't make CDbTableId\n" ); return 0; } } // // Next create the projection nodes // CDbProjectNode * pProject = new CDbProjectNode(); if ( 0 == pProject ) { delete pTree; LogError("FormQueryTree: can't make CDbProjectNode\n" ); return 0; } // // Make the selection a child of the projection node. // pProject->AddTable( pTree ); pTree = pProject; // // Next add all the columns in the state. // unsigned int cCol = Cols.Count(); for ( unsigned int i = 0; i < cCol; i++ ) { if ( !pProject->AddProjectColumn( Cols.Get(i) )) { delete pTree; LogError("FormQueryTree: can't add project column\n" ); return 0; } } // // Next add a sort node and make the project node a child of the // sort node // if (pSort && pSort->Count()) { unsigned int cSortProp = pSort->Count(); CDbSortNode * pSortNode = new CDbSortNode(); if ( 0 == pSortNode ) { delete pTree; LogError("FormQueryTree: create sort node\n" ); return 0; } // // Make the project node a child of the sort node. // if ( ! pSortNode->AddTable( pTree ) ) { delete pTree; delete pSortNode; LogError( "FormQueryTree: can't add table to sortnode\n" ); return 0; } pTree = pSortNode; for( i = 0; i < cSortProp; i++ ) { // // Add the sort column. // CDbSortKey const &key = pSort->Get( i ); #ifdef STRESS if ( 0 == &key ) { LogError( "0 sort key!\n" ); DebugBreak(); } #endif if ( !pSortNode->AddSortColumn( key ) ) { delete pTree; LogError("FormQueryTree: can't add sort column\n"); return 0; } #ifdef STRESS DBCOMMANDTREE *p = (DBCOMMANDTREE *) (void *) pSortNode; p = p->pctFirstChild; p = p->pctNextSibling; p = p->pctFirstChild; if ( DBOP_sort_list_element != p->op || DBVALUEKIND_SORTINFO != p->wKind || 0 == p->value.pdbsrtinfValue ) { LogError( "p: %#p, bad sort element!\n", p ); DebugBreak(); } #endif } } return pTree; } //FormQueryTree //+--------------------------------------------------------------------------- // // Class: CAsynchNotify // // Synopsis: Class for the IDBAsynchNotify callbacks // // History: 07 May 1999 dlee Created // //---------------------------------------------------------------------------- class CAsynchNotify : public IDBAsynchNotify { public: CAsynchNotify() : _cRef( 1 ), _cLowResource( 0 ), _hEvent( 0 ) { _hEvent = CreateEventW( 0, TRUE, FALSE, 0 ); if ( 0 == _hEvent ) LogError( "can't create notify event, %d\n", GetLastError() ); } ~CAsynchNotify() { if ( 0 != _cRef ) LogError( "CAsynchNotify refcounting is broken: %d\n", _cRef ); if ( 0 != _hEvent ) CloseHandle( _hEvent ); } BOOL IsValid() const { return 0 != _hEvent; } // // IUnknown methods. // STDMETHOD(QueryInterface) ( REFIID riid, LPVOID *ppiuk ) { *ppiuk = (void **) this; // hold our breath and jump AddRef(); return S_OK; } STDMETHOD_( ULONG, AddRef ) () { return InterlockedIncrement( &_cRef ); } STDMETHOD_( ULONG, Release) () { return InterlockedDecrement( &_cRef ); } // // IDBAsynchNotify methods // STDMETHOD( OnLowResource ) ( DB_DWRESERVE dwReserved ) { _cLowResource++; // If we've failed a few times due to low resource, give up // on the query since there may not be sufficient resources // to ever get an OnStop call. if ( _cLowResource >= 5 ) SetEvent( _hEvent ); return S_OK; } STDMETHOD( OnProgress ) ( HCHAPTER hChap, DBASYNCHOP ulOp, DBCOUNTITEM ulProg, DBCOUNTITEM ulProgMax, DBASYNCHPHASE ulStat, LPOLESTR pwszStatus ) { return S_OK; } STDMETHOD( OnStop ) ( HCHAPTER hChap, ULONG ulOp, HRESULT hrStat, LPOLESTR pwszStatus ) { // If the query is complete (successfully or not), set the event if ( DBASYNCHOP_OPEN == ulOp ) SetEvent( _hEvent ); return S_OK; } void Wait() { WaitForSingleObject( _hEvent, INFINITE ); } private: LONG _cRef; LONG _cLowResource; HANDLE _hEvent; }; //+--------------------------------------------------------------------------- // // Function: WaitForQueryToComplete // // Synopsis: Waits for the query to complete. // // Arguments: [pRowset] -- the asynchronous rowset // // History: 07 May 1999 dlee Created // //---------------------------------------------------------------------------- SCODE WaitForQueryCompletion( IRowset * pRowset ) { SCODE sc = S_OK; if ( Rand( 100 ) < 50 ) { // Register for notifications XInterface xCPC; sc = pRowset->QueryInterface( IID_IConnectionPointContainer, xCPC.GetQIPointer() ); if (FAILED(sc)) { LogError( "Can't QI for IConnectionPointContainer: %#x\n",sc ); return sc; } XInterface xCP; sc = xCPC->FindConnectionPoint( IID_IDBAsynchNotify, xCP.GetPPointer() ); if (FAILED(sc) && CONNECT_E_NOCONNECTION != sc ) { LogError( "FindConnectionPoint failed: %#x\n",sc ); return sc; } CAsynchNotify Notify; if ( !Notify.IsValid() ) return HRESULT_FROM_WIN32( GetLastError() ); DWORD dwAdviseID; sc = xCP->Advise( (IUnknown *) &Notify, &dwAdviseID ); if (FAILED(sc)) { LogError( "IConnectionPoint->Advise failed: %#x\n",sc ); return sc; } // // In a real app, we'd be off doing other work rather than waiting // for the query to complete, but this will do. // MsgWaitForSingleObject is a good choice for a GUI app. You could // also post a user-defined windows message when a notification is // received. // Notify.Wait(); sc = xCP->Unadvise( dwAdviseID ); if ( S_OK != sc ) { LogError( "IConnectionPoint->Unadvise returned %#x\n", sc ); return sc; } Notify.Release(); } else { // Poll. In a real app, real work would happen between checks. XInterface xIDBAsynch; sc = pRowset->QueryInterface( IID_IDBAsynchStatus, xIDBAsynch.GetQIPointer() ); if ( FAILED( sc ) ) return sc; do { DBCOUNTITEM Numerator, Denominator; DBASYNCHPHASE Phase; sc = xIDBAsynch->GetStatus( DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, &Numerator, &Denominator, &Phase, 0 ); if ( FAILED( sc ) || ( DBASYNCHPHASE_COMPLETE == Phase ) ) break; Sleep( 20 ); // Give the query a chance to run } while ( TRUE ); } return sc; } //WaitForQueryCompletion //+--------------------------------------------------------------------------- // // Function: InstantiateRowset // // Synopsis: Forms a query tree consisting of the projection nodes, // sort node(s), selection node and the restriction tree. // // Arguments: [dwDepth] - Query depth, one of QUERY_DEEP or QUERY_SHALLOW // [pswzScope] - Query scope // [pTree] - pointer to DBCOMMANDTREE for the query // [riid] - Interface ID of the desired rowset interface // // Returns: IRowsetScroll* - a pointer to an instantiated rowset // // History: 22 July 1995 AlanW Created // // Notes: Although the returned pointer is to IRowsetScroll, the // returned pointer may only support IRowset, depending // upon the riid parameter. // // Ownership of the query tree is given to the ICommandTree // object. The caller does not need to delete it. // // Use InstantiateMultipleRowsets for categorized queries. // //---------------------------------------------------------------------------- static const DBID dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0}; static const GUID guidQueryExt = DBPROPSET_QUERYEXT; IRowsetScroll * InstantiateRowset( ICommand * pCommandIn, DWORD dwDepth, LPWSTR pwszScope, XPtr & xTree, WCHAR const * pwcQuery, REFIID riid, BOOL fAsynchronous ) { ICommand * pCommand; XInterface xCommand; if ( 0 == pCommandIn ) { SCODE sc = CICreateCommand( (IUnknown **) &pCommand, 0, IID_ICommand, g_awcCatalog, g_awcMachine ); if ( FAILED( sc ) ) { LogError( "InstantiateRowset - error 0x%x, Unable to create icommand'\n", sc ); return 0; } xCommand.Set( pCommand ); } else { pCommand = pCommandIn; } if ( 0 == pCommand ) return 0; if ( g_fSQL ) { XInterface xCommandText; SCODE sc = pCommand->QueryInterface( IID_ICommandText, xCommandText.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "InstantiateRowset error %#x, can't qi ICommandText\n", sc ); return 0; } WCHAR awc[ 300 ]; swprintf( awc, L"SELECT %ws FROM %ws..SCOPE('\"%ws\"') WHERE CONTAINS('%ws')", L"Filename, Size, Write, Path, Rank", //g_awcMachine, g_awcCatalog, pwszScope, pwcQuery ); sc = xCommandText->SetCommandText( DBGUID_SQL, awc ); if ( FAILED( sc ) ) { LogError( "InstantiateRowset error %#x, can't set text\n", sc ); return 0; } #if 1 XInterface xCommandProperties; sc = xCommandText->QueryInterface( IID_ICommandProperties, xCommandProperties.GetQIPointer() ); if ( FAILED (sc) ) { LogError( "InstantiateRowset error %#x, can't qi commandprops\n", sc ); return 0; } // set the machine name DBPROPSET PropSet; DBPROP Prop; const GUID guidQueryCorePropset = DBPROPSET_CIFRMWRKCORE_EXT; PropSet.rgProperties = &Prop; PropSet.cProperties = 1; PropSet.guidPropertySet = guidQueryCorePropset; Prop.dwPropertyID = DBPROP_MACHINE; Prop.colid = DB_NULLID; Prop.vValue.vt = VT_BSTR; Prop.vValue.bstrVal = SysAllocString( g_awcMachine ); if ( 0 == Prop.vValue.bstrVal ) { LogError( "InstantiateRowset error %#x, can't allocate sql machine\n", sc ); return 0; } sc = xCommandProperties->SetProperties ( 1, &PropSet ); VariantClear( &Prop.vValue ); if ( FAILED (sc) ) { LogError( "InstantiateRowset error %#x can't set sql machine\n", sc ); return 0; } #endif XInterface xCommandPrepare; sc = xCommandText->QueryInterface( IID_ICommandPrepare, xCommandPrepare.GetQIPointer() ); if ( FAILED (sc) ) { LogError( "InstantiateRowset error %#x, can't qi prepare\n", sc ); return 0; } sc = xCommandPrepare->Prepare( 1 ); if ( FAILED (sc) ) { LogError( "InstantiateRowset error %#x, can't prepare\n", sc ); return 0; } } else { XInterface xCmdTree; HRESULT sc = pCommand->QueryInterface( IID_ICommandTree, xCmdTree.GetQIPointer() ); if (FAILED (sc) ) { LogError( "QI for ICommandTree failed %#x\n", sc ); return 0; } DBCOMMANDTREE * pRoot = xTree->CastToStruct(); sc = xCmdTree->SetCommandTree( &pRoot, DBCOMMANDREUSE_NONE, FALSE); if (FAILED (sc) ) { LogError("SetCommandTree failed, %08x\n", sc); return 0; } xTree.Acquire(); } #ifdef GET_DATA_TOO { const unsigned MAX_PROPS = 8; DBPROPSET aPropSet[MAX_PROPS]; DBPROP aProp[MAX_PROPS]; ULONG cProps = 0; // We can handle PROPVARIANTs aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES; aProp[cProps].dwOptions = DBPROPOPTIONS_OPTIONAL; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = VARIANT_TRUE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidQueryExt; cProps++; XInterface xCmdProp; SCODE sc = pCommand->QueryInterface( IID_ICommandProperties, xCmdProp.GetQIPointer() ); if (FAILED (sc) ) { LogError( "can't qi to commandprops\n", sc ); return 0; } sc = xCmdProp->SetProperties( cProps, aPropSet ); if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc ) { LogError( "can't set commandprops: 0x%lx\n", sc ); return 0; } } #endif // GET_DATA_TOO #ifdef STRESS { const unsigned MAX_PROPS = 1; DBPROPSET aPropSet[MAX_PROPS]; DBPROP aProp[MAX_PROPS]; ULONG cProps = 0; // Mark the icommand as synch or asynch. Note that we always have // to set it since we may have an old ICommand that previously had // a different state set. aProp[cProps].dwPropertyID = DBPROP_IDBAsynchStatus; aProp[cProps].dwOptions = DBPROPOPTIONS_REQUIRED; aProp[cProps].dwStatus = 0; aProp[cProps].colid = dbcolNull; aProp[cProps].vValue.vt = VT_BOOL; aProp[cProps].vValue.boolVal = fAsynchronous ? VARIANT_TRUE : VARIANT_FALSE; aPropSet[cProps].rgProperties = &aProp[cProps]; aPropSet[cProps].cProperties = 1; aPropSet[cProps].guidPropertySet = guidRowsetProps; cProps++; XInterface xCmdProp; SCODE sc = pCommand->QueryInterface( IID_ICommandProperties, xCmdProp.GetQIPointer() ); if (FAILED (sc) ) { LogError( "can't qi to commandprops\n", sc ); return 0; } sc = xCmdProp->SetProperties( cProps, aPropSet ); if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc ) { LogError( "can't set commandprops: 0x%lx\n", sc ); return 0; } } #endif XInterface xRowset; SCODE sc = pCommand->Execute( 0, // no aggr. IUnknown riid, // IID for i/f to return 0, // disp. params 0, // chapter (IUnknown **) xRowset.GetPPointer() ); if ( FAILED (sc) ) { LogError("ICommand::Execute failed, %08x\n", sc); if ( !xRowset.IsNull() ) LogError( "pRowset is 0x%x when it should be 0\n", xRowset.GetPointer() ); } if ( SUCCEEDED( sc ) && fAsynchronous ) { sc = WaitForQueryCompletion( xRowset.GetPointer() ); if ( FAILED( sc ) ) xRowset.Free(); } return xRowset.Acquire(); } //InstantiateRowset //+------------------------------------------------------------------------- // // Function: MapColumns, public // // Synopsis: Map column IDs in column bindings. Create an accessor // for the binding array. // // Arguments: [pUnknown] -- Interface capable of returning IColumnsInfo and // IAccessor // [cCols] -- number of columns in arrays // [pBindings] -- column data binding array // [pDbCols] -- column IDs array // // Returns: HACCESSOR - a read accessor for the column bindings. // // History: 18 May 1995 AlanW Created // //-------------------------------------------------------------------------- static DBORDINAL aMappedColumnIDs[20]; HACCESSOR MapColumns( IUnknown * pUnknown, ULONG cCols, DBBINDING * pBindings, const DBID * pDbCols ) { XInterface xColumnsInfo; SCODE sc = pUnknown->QueryInterface( IID_IColumnsInfo, xColumnsInfo.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "IUnknown::QueryInterface for IColumnsInfo returned 0x%lx\n", sc ); return 0; } sc = xColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs); if (S_OK != sc) { LogError( "IColumnsInfo->MapColumnIDs returned 0x%lx\n",sc); return 0; } for (ULONG i = 0; i < cCols; i++) pBindings[i].iOrdinal = aMappedColumnIDs[i]; XInterface xAccessor; sc = pUnknown->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "IRowset::QueryInterface for IAccessor returned 0x%lx\n", sc ); return 0; } HACCESSOR hAcc = 0; sc = xAccessor->CreateAccessor( DBACCESSOR_ROWDATA, cCols, pBindings, 0, &hAcc, 0 ); if (S_OK != sc) LogError( "IAccessor->CreateAccessor returned 0x%lx\n",sc); return hAcc; } //MapColumns //+------------------------------------------------------------------------- // // Function: ReleaseAccessor, public // // Synopsis: Release an accessor obtained from MapColumns // // Arguments: [pUnknown] -- Something that we can QI the IAccessor on // [hAcc] -- Accessor handle to be released. // // Returns: nothing // // History: 14 June 1995 AlanW Created // //-------------------------------------------------------------------------- void ReleaseAccessor( IUnknown * pUnknown, HACCESSOR hAcc ) { XInterface xAccessor; SCODE sc = pUnknown->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() ); if ( FAILED( sc ) ) { LogError( "IUnknown::QueryInterface for IAccessor returned 0x%lx\n", sc ); return; } sc = xAccessor->ReleaseAccessor( hAcc, 0 ); if (S_OK != sc) LogError( "IAccessor->ReleaseAccessor returned 0x%lx\n",sc); } //ReleaseAccessor //+------------------------------------------------------------------------- // // Function: LogError, public // // Synopsis: Prints a verbose-mode message. // // Arguments: [pszfmt] -- Format string // // History: 13-Jul-93 KyleP Created // //-------------------------------------------------------------------------- void LogError( char const * pszfmt, ... ) { va_list pargs; va_start(pargs, pszfmt); vprintf( pszfmt, pargs ); va_end(pargs); _flushall(); } //LogError