Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1548 lines
43 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: qryperf.CXX
  7. //
  8. // Contents: performance test program
  9. //
  10. // History: 16 March 1996 dlee Created (from fsdbdrt)
  11. //
  12. //--------------------------------------------------------------------------
  13. #include "pch.cxx"
  14. #pragma hdrstop
  15. #define STRESS
  16. #ifdef STRESS
  17. #define GET_DATA_TOO
  18. unsigned cThreads = 8;
  19. const unsigned cLoopTimes = 0xffffffff;
  20. WCHAR *pwcCatalog = L"system";
  21. WCHAR *pwcMachine = L".";
  22. BOOL g_fStopNow = FALSE;
  23. const unsigned cmsSleep = 3000; // 0
  24. unsigned g_cmsSleep;
  25. BOOL g_fFetchRowsAtWill = TRUE;
  26. #else
  27. const unsigned cThreads = 8;
  28. const unsigned cLoopTimes = 1000;
  29. WCHAR *pwcCatalog = L"encarta";
  30. WCHAR *pwcMachine = L".";
  31. #endif
  32. BOOL g_fSQL = FALSE;
  33. #define DBINITCONSTANTS
  34. #include <crt\io.h>
  35. #include <time.h>
  36. #include <process.h>
  37. #include <propvar.h>
  38. #include <olectl.h>
  39. #include <doquery.hxx>
  40. WCHAR g_awcCatalog[ MAX_PATH ];
  41. WCHAR g_awcMachine[ MAX_PATH ];
  42. BOOL g_fSequential = FALSE;
  43. template<class T> T Rand2( T t ) { return (T) abs( (int) ( rand() % t ) ); }
  44. template<class T> T Rand( T t ) { return (T) abs( GetTickCount() % t ); }
  45. CCoTaskAllocator CoTaskAllocator;
  46. void * CCoTaskAllocator::Allocate(ULONG cbSize)
  47. {
  48. return CoTaskMemAlloc( cbSize );
  49. }
  50. void CCoTaskAllocator::Free( void *pv )
  51. {
  52. CoTaskMemFree( pv );
  53. }
  54. BOOL isEven(unsigned n)
  55. {
  56. return ( 0 == ( n & 1 ) );
  57. }
  58. static const GUID guidSystem = PSGUID_STORAGE;
  59. static const GUID guidQuery = PSGUID_QUERY;
  60. static const GUID guidRowsetProps = DBPROPSET_ROWSET;
  61. static CDbColId psName( guidSystem, PID_STG_NAME );
  62. static CDbColId psPath( guidSystem, PID_STG_PATH );
  63. static CDbColId psSize( guidSystem, PID_STG_SIZE );
  64. static CDbColId psWriteTime( guidSystem, PID_STG_WRITETIME );
  65. static CDbColId psContents( guidSystem, PID_STG_CONTENTS );
  66. static CDbColId psRank( guidQuery, DISPID_QUERY_RANK );
  67. static CDbColId * aColIds[] =
  68. {
  69. &psName, &psPath, &psSize, &psWriteTime, &psRank
  70. };
  71. static ULONG cColIds = sizeof aColIds / sizeof aColIds[0];
  72. CDbCmdTreeNode * FormQueryTree( CDbCmdTreeNode * pRst,
  73. CDbColumns & Cols,
  74. CDbSortSet * pSort );
  75. IRowsetScroll * InstantiateRowset(
  76. ICommand *pCommandIn,
  77. DWORD dwDepth,
  78. LPWSTR pwszScope,
  79. XPtr<CDbCmdTreeNode> & xTree,
  80. WCHAR const * pwcQuery,
  81. REFIID riid,
  82. BOOL fAsynchronous );
  83. HACCESSOR MapColumns(
  84. IUnknown * pUnknown,
  85. ULONG cCols,
  86. DBBINDING * pBindings,
  87. const DBID * pColIds );
  88. void ReleaseAccessor( IUnknown * pUnknown, HACCESSOR hAcc );
  89. DWORD __stdcall RunPerfTest(void *pv);
  90. void LogError( char const * pszFormat, ... );
  91. void GetProcessInfo(
  92. WCHAR * pwcImage,
  93. LARGE_INTEGER & liUserTime,
  94. LARGE_INTEGER & liKernelTime,
  95. ULONG & cHandles,
  96. ULONGLONG & cbWorkingSet,
  97. ULONGLONG & cbPeakWorkingSet,
  98. ULONGLONG & cbPeakVirtualSize,
  99. ULONGLONG & cbNonPagedPoolUsage,
  100. ULONGLONG & cbPeakNonPagedPoolUsage );
  101. inline _int64 mkTime( FILETIME ft )
  102. {
  103. return (_int64) ft.dwLowDateTime +
  104. ( ( (_int64 ) ft.dwHighDateTime ) << 32 );
  105. } //mkTime
  106. inline _int64 mkTime( FILETIME ftK, FILETIME ftU )
  107. {
  108. return mkTime( ftK ) + mkTime( ftU );
  109. } //mkTime
  110. inline _int64 mkTime( LARGE_INTEGER li )
  111. {
  112. return (_int64) li.LowPart +
  113. ( ( (_int64) li.HighPart ) << 32 );
  114. } //mkTime
  115. inline _int64 mkTime( LARGE_INTEGER liK, LARGE_INTEGER liU )
  116. {
  117. return mkTime( liK ) + mkTime( liU );
  118. } //mkTime
  119. void GetCiSvcTimes(
  120. LARGE_INTEGER & liCiSvcKernelTime,
  121. LARGE_INTEGER & liCiSvcUserTime )
  122. {
  123. ULONG cHandles;
  124. ULONGLONG cbWorkingSet;
  125. ULONGLONG cbPeakWorkingSet;
  126. ULONGLONG cbPeakVirtualSize;
  127. ULONGLONG cbNonPagedPoolUsage;
  128. ULONGLONG cbPeakNonPagedPoolUsage;
  129. GetProcessInfo( L"cisvc.exe",
  130. liCiSvcUserTime,
  131. liCiSvcKernelTime,
  132. cHandles,
  133. cbWorkingSet,
  134. cbPeakWorkingSet,
  135. cbPeakVirtualSize,
  136. cbNonPagedPoolUsage,
  137. cbPeakNonPagedPoolUsage );
  138. } //GetCiSvcTimes
  139. void RunQuerySuite()
  140. {
  141. HANDLE ah[ 200 ];
  142. DWORD dwID;
  143. for ( unsigned x = 0; x < cThreads; x++ )
  144. {
  145. #ifdef STRESS
  146. Sleep( GetCurrentThreadId() );
  147. #endif // STRESS
  148. ah[x] = CreateThread( 0,
  149. 65536,
  150. RunPerfTest,
  151. 0,
  152. 0,
  153. &dwID );
  154. }
  155. WaitForMultipleObjects( cThreads, ah, TRUE, INFINITE );
  156. for ( x = 0; x < cThreads; x++ )
  157. CloseHandle( ah[x] );
  158. } //RunQuerySuite
  159. void Usage()
  160. {
  161. printf( "usage: qryperf [-c:catalog] [-m:machine] [-d:delay(ms)] [-q(onarch)] [-s:(0|1)] [-t:threads] -f(etch at will)\n" );
  162. exit(1);
  163. } //Usage
  164. extern "C" int __cdecl wmain( int argc, WCHAR * argv[] )
  165. {
  166. HRESULT hr = CoInitialize( 0 );
  167. if ( FAILED( hr ) )
  168. exit( 1 );
  169. wcscpy( g_awcCatalog, pwcCatalog );
  170. wcscpy( g_awcMachine, pwcMachine );
  171. #ifdef STRESS
  172. g_cmsSleep = cmsSleep;
  173. #endif
  174. for ( int i = 1; i < argc; i++ )
  175. {
  176. if ( '/' == argv[i][0] || '-' == argv[i][0] )
  177. {
  178. WCHAR c = (WCHAR) tolower( argv[i][1] );
  179. if ( L':' != argv[i][2] && c != L'q' && c != 'f' )
  180. Usage();
  181. if ( L'c' == c )
  182. wcscpy( g_awcCatalog, argv[i] + 3 );
  183. else if ( L'm' == c )
  184. wcscpy( g_awcMachine, argv[i] + 3 );
  185. #ifdef STRESS
  186. else if ( L'd' == c )
  187. g_cmsSleep = _wtoi( argv[i] + 3 );
  188. else if ( 't' == c )
  189. cThreads = _wtoi( argv[i] + 3 );
  190. else if ( 'f' == c )
  191. g_fFetchRowsAtWill = FALSE;
  192. #endif
  193. else if ( 'q' == c )
  194. g_fSQL = TRUE;
  195. else if ( L's' == c )
  196. g_fSequential = _wtoi( argv[i] + 3 );
  197. else
  198. Usage();
  199. }
  200. else
  201. Usage();
  202. }
  203. HANDLE hproc = GetCurrentProcess();
  204. FILETIME ftCreate,ftExit,ftKernel,ftUser;
  205. GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser );
  206. _int64 openTime = mkTime( ftKernel, ftUser );
  207. {
  208. CI_STATE state;
  209. state.cbStruct = sizeof state;
  210. CIState( g_awcCatalog, g_awcMachine, &state );
  211. }
  212. LARGE_INTEGER liCiSvcUserTime;
  213. LARGE_INTEGER liCiSvcKernelTime;
  214. GetCiSvcTimes( liCiSvcKernelTime, liCiSvcUserTime );
  215. _int64 ciStartTime = mkTime( liCiSvcKernelTime, liCiSvcUserTime );
  216. GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser );
  217. _int64 startTime = mkTime( ftKernel, ftUser );
  218. RunQuerySuite();
  219. CIShutdown();
  220. GetProcessTimes( hproc, &ftCreate, &ftExit, &ftKernel, &ftUser );
  221. _int64 endTime = mkTime( ftKernel, ftUser );
  222. GetCiSvcTimes( liCiSvcKernelTime, liCiSvcUserTime );
  223. _int64 ciEndTime = mkTime( liCiSvcKernelTime, liCiSvcUserTime );
  224. #ifdef STRESS
  225. printf( "stress test client time: %d ms cisvc time: %d ms\n",
  226. (DWORD) ((endTime - startTime) / 10000 ),
  227. (DWORD) ((ciEndTime - ciStartTime) / 10000 ) );
  228. #else
  229. printf( "%s test client time: %d ms cisvc time: %d ms\n",
  230. g_fSequential ? "sequential" : "non-sequential",
  231. (DWORD) ((endTime - startTime) / 10000 ),
  232. (DWORD) ((ciEndTime - ciStartTime) / 10000 ) );
  233. #endif
  234. CoUninitialize();
  235. return 0;
  236. } //main
  237. #ifdef GET_DATA_TOO
  238. class CBookMark
  239. {
  240. public:
  241. CBookMark() : cbBmk (0) {}
  242. CBookMark( DBBOOKMARK bmkSpecial ) : cbBmk (1)
  243. {
  244. abBmk[0] = (BYTE) bmkSpecial;
  245. }
  246. BOOL IsValid() const { return 0 != cbBmk; }
  247. void Invalidate () { cbBmk = 0; }
  248. BOOL IsEqual ( CBookMark& bmk)
  249. {
  250. if (cbBmk != bmk.cbBmk)
  251. return FALSE;
  252. return memcmp ( abBmk, bmk.abBmk, cbBmk ) == 0;
  253. }
  254. void MakeFirst()
  255. {
  256. cbBmk = sizeof (BYTE);
  257. abBmk[0] = (BYTE) DBBMK_FIRST;
  258. }
  259. BOOL IsFirst()
  260. {
  261. return cbBmk == sizeof(BYTE) && abBmk[0] == (BYTE) DBBMK_FIRST;
  262. }
  263. DBLENGTH cbBmk;
  264. BYTE abBmk[ 50 ];
  265. };
  266. void FetchAtWill(
  267. IRowset * pRowset,
  268. IUnknown * pAccessor,
  269. HACCESSOR hAccessor,
  270. DBCOUNTITEM cHits )
  271. {
  272. if ( 0 == cHits )
  273. return;
  274. XInterface<IRowsetScroll> xRS;
  275. SCODE sc = pRowset->QueryInterface( IID_IRowsetScroll, xRS.GetQIPointer() );
  276. if ( FAILED( sc ) )
  277. {
  278. LogError( "Can't QI for IID_IRowsetScroll\n" );
  279. return;
  280. }
  281. const DBROWCOUNT cMaxToGet = 8;
  282. HROW aHRows[ cMaxToGet ];
  283. HROW * paHRows = aHRows;
  284. // Fetch relative to first
  285. const BYTE bmkFirst = (BYTE) DBBMK_FIRST;
  286. for ( unsigned i = 0; i < 5 && !g_fStopNow; i++ )
  287. {
  288. DBCOUNTITEM cRows;
  289. DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 );
  290. DBROWOFFSET iStart = Rand2( cHits );
  291. sc = xRS->GetRowsAt( 0, 0, 1, &bmkFirst, iStart, cToGet, &cRows, &paHRows );
  292. if ( SUCCEEDED( sc ) )
  293. xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 );
  294. else
  295. LogError( "can't get %d rows at %d out of %d\n", cToGet, iStart, cHits );
  296. }
  297. // Fetch relative to last
  298. const BYTE bmkLast = (BYTE) DBBMK_LAST;
  299. for ( i = 0; i < 5 && !g_fStopNow; i++ )
  300. {
  301. DBCOUNTITEM cRows;
  302. DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 );
  303. DBROWOFFSET iStart = Rand2( cHits );
  304. sc = xRS->GetRowsAt( 0, 0, 1, &bmkLast, -iStart, cToGet, &cRows, &paHRows );
  305. if ( SUCCEEDED( sc ) )
  306. xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 );
  307. else
  308. LogError( "can't get %d rows at %d from last out of %d\n", cToGet, -iStart, cHits );
  309. }
  310. // Fetch relative to a random location
  311. static GUID guidBmk = DBBMKGUID;
  312. static CDbColId dbcolBookMark( guidBmk, PROPID_DBBMK_BOOKMARK );
  313. DBBINDING aBmkColumn[] = { 0, sizeof DBLENGTH, 0, 0, 0, 0, 0,
  314. DBPART_VALUE | DBPART_LENGTH,
  315. DBMEMOWNER_CLIENTOWNED,
  316. DBPARAMIO_NOTPARAM,
  317. 50,
  318. 0,
  319. DBTYPE_BYTES,
  320. 0, 0 };
  321. XInterface<IAccessor> xAccessor;
  322. sc = xRS->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() );
  323. if ( FAILED( sc ) )
  324. {
  325. LogError( "can't create bookmark accessor IAccessor: %#x\n", sc );
  326. return;
  327. }
  328. HACCESSOR hBmkAccessor;
  329. sc = xAccessor->CreateAccessor( DBACCESSOR_ROWDATA,
  330. 1,
  331. aBmkColumn,
  332. 0,
  333. &hBmkAccessor,
  334. 0 );
  335. if ( FAILED(sc) )
  336. {
  337. LogError( "can't create accessor\n" );
  338. return;
  339. }
  340. HROW aBmkHRows[ 1 ];
  341. HROW * paBmkHRows = aBmkHRows;
  342. DBROWOFFSET iBmkStart = Rand2( cHits );
  343. DBCOUNTITEM cBmkRows;
  344. sc = xRS->GetRowsAt( 0, 0, 1, &bmkFirst, iBmkStart, 1, &cBmkRows, &paBmkHRows );
  345. if ( SUCCEEDED( sc ) )
  346. {
  347. CBookMark bmk;
  348. sc = xRS->GetData( aBmkHRows[0], hBmkAccessor, &bmk );
  349. if ( SUCCEEDED( sc ) && ( DB_S_ERRORSOCCURRED != sc ) )
  350. {
  351. for ( unsigned i = 0; i < 5 && !g_fStopNow; i++ )
  352. {
  353. DBCOUNTITEM cRows;
  354. DBROWCOUNT cToGet = 1 + Rand2( cMaxToGet - 1 );
  355. DBROWOFFSET iStart = Rand2( cHits ) - iBmkStart;
  356. sc = xRS->GetRowsAt( 0, 0, bmk.cbBmk, bmk.abBmk, iStart,
  357. cToGet, &cRows, &paHRows );
  358. if ( SUCCEEDED( sc ) )
  359. xRS->ReleaseRows( cRows, aHRows, 0, 0, 0 );
  360. else
  361. LogError( "can't getrowsat %d rows %d relative to bmk at %d, rowset has %d: %#x\n",
  362. cToGet, iStart, iBmkStart, cHits, sc );
  363. }
  364. }
  365. else
  366. LogError( "can't GetData the bmk row: %#x\n", sc );
  367. xRS->ReleaseRows( 1, aBmkHRows, 0, 0, 0 );
  368. }
  369. else
  370. LogError( "can't GetRowsAt the bmk row: %#x\n", sc );
  371. ReleaseAccessor( pAccessor, hBmkAccessor );
  372. } //FetchAtWill
  373. #endif
  374. static DBBINDING aPropTestCols[] =
  375. {
  376. { 0,(sizeof ULONG_PTR)*0,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  377. { 0,(sizeof ULONG_PTR)*1,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  378. { 0,(sizeof ULONG_PTR)*2,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  379. { 0,(sizeof ULONG_PTR)*3,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  380. { 0,(sizeof ULONG_PTR)*4,0,0,0,0,0, DBPART_VALUE, DBMEMOWNER_PROVIDEROWNED, DBPARAMIO_NOTPARAM, 0, 0, DBTYPE_VARIANT|DBTYPE_BYREF, 0, 0 },
  381. };
  382. void RunPerfQuery(
  383. CDbRestriction & CiRst,
  384. WCHAR const * pwcQuery,
  385. unsigned cExpectedHits,
  386. ICommand * pCommand,
  387. BOOL fSeq,
  388. BOOL fAsynchronous )
  389. {
  390. CDbColumns cols( 5 );
  391. BOOL fOk = cols.Add( psName, 0 );
  392. if ( fOk )
  393. cols.Add( psSize, 1 );
  394. if ( fOk )
  395. cols.Add( psWriteTime, 2 );
  396. if ( fOk )
  397. cols.Add( psPath, 3 );
  398. if ( fOk )
  399. cols.Add( psRank, 4 );
  400. if ( !fOk )
  401. {
  402. LogError(" can't create column specification\n" );
  403. return;
  404. }
  405. unsigned cRetries = 0;
  406. DBCOUNTITEM cRowsReturned = 0;
  407. IRowset * pRowset = 0;
  408. {
  409. CDbSortSet ss( 1 );
  410. #ifdef STRESS
  411. int x = Rand( cColIds );
  412. int y = rand();
  413. fOk = ss.Add( *aColIds[x],
  414. isEven( rand() ) ? QUERY_SORTDESCEND : QUERY_SORTASCEND,
  415. 0 );
  416. #else
  417. fOk = ss.Add( psRank, QUERY_SORTDESCEND, 0);
  418. #endif
  419. if ( !fOk )
  420. {
  421. LogError(" can't create sort specification\n" );
  422. return;
  423. }
  424. XPtr<CDbCmdTreeNode> xCmdTree( FormQueryTree( &CiRst,
  425. cols,
  426. fSeq ? 0 : &ss ) );
  427. if ( xCmdTree.IsNull() )
  428. return;
  429. pRowset = InstantiateRowset( pCommand,
  430. QUERY_DEEP, // Depth
  431. L"\\", // Scope
  432. xCmdTree, // DBCOMMANDTREE
  433. pwcQuery,
  434. fSeq ? IID_IRowset :
  435. IID_IRowsetScroll,
  436. fAsynchronous );
  437. if ( 0 == pRowset )
  438. {
  439. LogError(" can't get rowset\n" );
  440. return;
  441. }
  442. XInterface<IRowset> xRowset( pRowset );
  443. #ifdef GET_DATA_TOO
  444. // Get data
  445. DBID aDbCols[5];
  446. aDbCols[0] = psName;
  447. aDbCols[1] = psSize;
  448. aDbCols[2] = psWriteTime;
  449. aDbCols[3] = psPath;
  450. aDbCols[4] = psRank;
  451. IUnknown * pAccessor = pRowset;
  452. HACCESSOR hAccessor = MapColumns( pAccessor,
  453. 5,
  454. aPropTestCols,
  455. aDbCols );
  456. if ( 0 == hAccessor )
  457. return;
  458. #endif // GET_DATA_TOO
  459. DBCOUNTITEM cTotal = 0;
  460. #ifdef STRESS
  461. const unsigned cFetchPasses = g_fFetchRowsAtWill ? 1000 : 5;
  462. #else
  463. const unsigned cFetchPasses = 3;
  464. #endif
  465. for ( unsigned i = 0; i < cFetchPasses; i++ )
  466. {
  467. #ifdef STRESS
  468. if ( g_fStopNow )
  469. break;
  470. #endif
  471. HROW aHRow[10];
  472. HROW * pgrhRows = aHRow;
  473. SCODE sc = pRowset->GetNextRows(0, 0, 10, &cRowsReturned, &pgrhRows);
  474. if ( FAILED( sc ) )
  475. {
  476. LogError( "'%ws' IRowset->GetNextRows returned 0x%x, cTotal: %d, cRowsReturned: %d\n",
  477. pwcQuery, sc, cTotal, cRowsReturned );
  478. break;
  479. }
  480. cTotal += cRowsReturned;
  481. #ifdef GET_DATA_TOO
  482. PROPVARIANT* data[5];
  483. for ( ULONG r = 0; r < cRowsReturned; r++ )
  484. {
  485. SCODE sc = pRowset->GetData( pgrhRows[r],
  486. hAccessor,
  487. &data );
  488. }
  489. #endif // GET_DATA_TOO
  490. if ( 0 != cRowsReturned )
  491. pRowset->ReleaseRows( cRowsReturned, pgrhRows, 0, 0, 0 );
  492. if ( DB_S_ENDOFROWSET == sc )
  493. break;
  494. }
  495. #ifdef GET_DATA_TOO
  496. if ( !fSeq && g_fFetchRowsAtWill )
  497. FetchAtWill( pRowset, pAccessor, hAccessor, cTotal );
  498. ReleaseAccessor( pAccessor, hAccessor );
  499. //printf( "query %ws returned %d hits\n", pwcQuery, cTotal );
  500. #endif // GET_DATA_TOO
  501. if ( 0 == cTotal )
  502. printf( "query %ws returned no hits\n", pwcQuery );
  503. #if 0
  504. if ( cTotal < __min( 30, cExpectedHits ) )
  505. printf( "query %ws returned %d hits, expecting %d\n",
  506. pwcQuery, cTotal, cExpectedHits );
  507. #endif
  508. }
  509. } //RunPerfQuery
  510. struct SQuery
  511. {
  512. WCHAR const * pwcQuery;
  513. unsigned cEncartaHits;
  514. };
  515. static SQuery aQueries[] =
  516. {
  517. #ifdef STRESS
  518. { L"stereo", 4 },
  519. { L"flex", 2 },
  520. { L"agassi", 1 },
  521. { L"detroit", 108 },
  522. { L"miami", 60 },
  523. { L"edison", 25 },
  524. { L"bulb", 33 },
  525. { L"elephant", 87 },
  526. { L"radius", 43 },
  527. { L"amplifier", 17 },
  528. { L"drunk", 10 },
  529. { L"grunt", 3 },
  530. { L"war", 4241 },
  531. { L"peace", 812 },
  532. { L"river", 3402 },
  533. { L"not", 4002 },
  534. { L"city", 5567 },
  535. { L"century", 4470 },
  536. #else
  537. { L"stereo", 4 },
  538. { L"flex", 2 },
  539. { L"agassi", 1 },
  540. { L"detroit", 108 },
  541. { L"miami", 60 },
  542. { L"edison", 25 },
  543. { L"web", 57 },
  544. { L"bulb", 33 },
  545. { L"microsoft", 15 },
  546. { L"elephant", 87 },
  547. { L"radius", 43 },
  548. { L"amplifier", 17 },
  549. { L"drunk", 10 },
  550. { L"grunt", 3 },
  551. #endif // STRESS
  552. };
  553. DWORD __stdcall RunPerfTest(void *pv)
  554. {
  555. static long cQueriesSoFar = 0;
  556. // #ifdef STRESS
  557. // srand( GetTickCount() + GetCurrentThreadId() );
  558. // #endif
  559. XInterface<ICommand> xCommand;
  560. do
  561. {
  562. ICommand * pCommand = 0;
  563. SCODE sc = CICreateCommand( (IUnknown **) &pCommand,
  564. 0,
  565. IID_ICommand,
  566. g_awcCatalog,
  567. g_awcMachine );
  568. if ( FAILED( sc ) )
  569. LogError( "CICreateCommand failed: 0x%x\n", sc );
  570. else
  571. xCommand.Set( pCommand );
  572. } while ( xCommand.IsNull() );
  573. for ( int x = 0; x < cLoopTimes; x++ )
  574. {
  575. try
  576. {
  577. const int cQueries = sizeof aQueries / sizeof aQueries[0];
  578. #ifdef STRESS
  579. int j = Rand( cQueries );
  580. #else
  581. int j = ( x % cQueries );
  582. #endif // STRESS
  583. CDbContentRestriction CiRst( aQueries[j].pwcQuery, psContents );
  584. if ( !CiRst.IsValid() )
  585. continue;
  586. #ifdef STRESS
  587. if ( 0 != g_cmsSleep )
  588. Sleep( Rand( g_cmsSleep ) );
  589. ICommand *pCmd = isEven( x ) ? xCommand.GetPointer() : 0;
  590. BOOL fSeq = ( Rand( 100 ) < 70 );
  591. BOOL fAsynchronous = FALSE;
  592. if ( !fSeq )
  593. fAsynchronous = ( Rand( 100 ) < 30 );
  594. RunPerfQuery( CiRst,
  595. aQueries[j].pwcQuery,
  596. aQueries[j].cEncartaHits,
  597. pCmd,
  598. fSeq,
  599. fAsynchronous );
  600. InterlockedIncrement( &cQueriesSoFar );
  601. if ( 0 == ( cQueriesSoFar % 10 ) )
  602. printf( "%d queries on catalog '%ws', machine '%ws'\n",
  603. cQueriesSoFar, g_awcCatalog, g_awcMachine );
  604. if ( g_fStopNow )
  605. return 0;
  606. #else
  607. RunPerfQuery( CiRst,
  608. aQueries[j].pwcQuery,
  609. aQueries[j].cEncartaHits,
  610. xCommand.GetPointer(),
  611. g_fSequential,
  612. FALSE );
  613. #endif //STRESS
  614. }
  615. catch( CException & e )
  616. {
  617. // ignore
  618. }
  619. }
  620. return 0;
  621. } //RunPerfTest
  622. //+---------------------------------------------------------------------------
  623. //
  624. // Function: FormQueryTree
  625. //
  626. // Synopsis: Forms a query tree consisting of the projection nodes,
  627. // sort node(s), selection node and the restriction tree.
  628. //
  629. // Arguments: [pRst] - pointer to Restriction tree describing the query
  630. // [Cols] - Columns in the resulting table
  631. // [pSort] - pointer to sort set; may be null
  632. //
  633. // Returns: A pointer to the query tree. It is the responsibility of
  634. // the caller to later free it.
  635. //
  636. // History: 06 July 1995 AlanW Created
  637. //
  638. //----------------------------------------------------------------------------
  639. CDbCmdTreeNode * FormQueryTree( CDbCmdTreeNode * pRst,
  640. CDbColumns & Cols,
  641. CDbSortSet * pSort )
  642. {
  643. CDbCmdTreeNode * pTree = 0; // return value
  644. if ( 0 != pRst )
  645. {
  646. //
  647. // First create a selection node and append the restriction tree to it
  648. //
  649. CDbSelectNode * pSelect = new CDbSelectNode();
  650. if ( 0 == pSelect )
  651. {
  652. LogError("FormQueryTree: can't make CDbSelectNode\n" );
  653. return 0;
  654. }
  655. pTree = pSelect;
  656. if ( !pSelect->IsValid() )
  657. {
  658. delete pTree;
  659. LogError("FormQueryTree: select node isn't valid\n" );
  660. return 0;
  661. }
  662. //
  663. // Clone the restriction and use it.
  664. //
  665. CDbCmdTreeNode * pExpr = pRst->Clone();
  666. if ( 0 == pExpr )
  667. {
  668. delete pTree;
  669. LogError("FormQueryTree: can't clone the restriction\n" );
  670. return 0;
  671. }
  672. #ifdef STRESS
  673. else
  674. {
  675. CDbContentRestriction * p = (CDbContentRestriction *) pExpr;
  676. if ( !p->IsValid() )
  677. {
  678. LogError( "clone failed illegally!\n" );
  679. DebugBreak();
  680. }
  681. }
  682. #endif
  683. //
  684. // Now make the restriction a child of the selection node.
  685. //
  686. pSelect->AddRestriction( pExpr );
  687. }
  688. else
  689. {
  690. //
  691. // No restriction. Just use table ID node as start of tree.
  692. //
  693. pTree = new CDbTableId();
  694. if ( 0 == pTree )
  695. {
  696. LogError("FormQueryTree: can't make CDbTableId\n" );
  697. return 0;
  698. }
  699. }
  700. //
  701. // Next create the projection nodes
  702. //
  703. CDbProjectNode * pProject = new CDbProjectNode();
  704. if ( 0 == pProject )
  705. {
  706. delete pTree;
  707. LogError("FormQueryTree: can't make CDbProjectNode\n" );
  708. return 0;
  709. }
  710. //
  711. // Make the selection a child of the projection node.
  712. //
  713. pProject->AddTable( pTree );
  714. pTree = pProject;
  715. //
  716. // Next add all the columns in the state.
  717. //
  718. unsigned int cCol = Cols.Count();
  719. for ( unsigned int i = 0; i < cCol; i++ )
  720. {
  721. if ( !pProject->AddProjectColumn( Cols.Get(i) ))
  722. {
  723. delete pTree;
  724. LogError("FormQueryTree: can't add project column\n" );
  725. return 0;
  726. }
  727. }
  728. //
  729. // Next add a sort node and make the project node a child of the
  730. // sort node
  731. //
  732. if (pSort && pSort->Count())
  733. {
  734. unsigned int cSortProp = pSort->Count();
  735. CDbSortNode * pSortNode = new CDbSortNode();
  736. if ( 0 == pSortNode )
  737. {
  738. delete pTree;
  739. LogError("FormQueryTree: create sort node\n" );
  740. return 0;
  741. }
  742. //
  743. // Make the project node a child of the sort node.
  744. //
  745. if ( ! pSortNode->AddTable( pTree ) )
  746. {
  747. delete pTree;
  748. delete pSortNode;
  749. LogError( "FormQueryTree: can't add table to sortnode\n" );
  750. return 0;
  751. }
  752. pTree = pSortNode;
  753. for( i = 0; i < cSortProp; i++ )
  754. {
  755. //
  756. // Add the sort column.
  757. //
  758. CDbSortKey const &key = pSort->Get( i );
  759. #ifdef STRESS
  760. if ( 0 == &key )
  761. {
  762. LogError( "0 sort key!\n" );
  763. DebugBreak();
  764. }
  765. #endif
  766. if ( !pSortNode->AddSortColumn( key ) )
  767. {
  768. delete pTree;
  769. LogError("FormQueryTree: can't add sort column\n");
  770. return 0;
  771. }
  772. #ifdef STRESS
  773. DBCOMMANDTREE *p = (DBCOMMANDTREE *) (void *) pSortNode;
  774. p = p->pctFirstChild;
  775. p = p->pctNextSibling;
  776. p = p->pctFirstChild;
  777. if ( DBOP_sort_list_element != p->op ||
  778. DBVALUEKIND_SORTINFO != p->wKind ||
  779. 0 == p->value.pdbsrtinfValue )
  780. {
  781. LogError( "p: %#p, bad sort element!\n", p );
  782. DebugBreak();
  783. }
  784. #endif
  785. }
  786. }
  787. return pTree;
  788. } //FormQueryTree
  789. //+---------------------------------------------------------------------------
  790. //
  791. // Class: CAsynchNotify
  792. //
  793. // Synopsis: Class for the IDBAsynchNotify callbacks
  794. //
  795. // History: 07 May 1999 dlee Created
  796. //
  797. //----------------------------------------------------------------------------
  798. class CAsynchNotify : public IDBAsynchNotify
  799. {
  800. public:
  801. CAsynchNotify() :
  802. _cRef( 1 ),
  803. _cLowResource( 0 ),
  804. _hEvent( 0 )
  805. {
  806. _hEvent = CreateEventW( 0, TRUE, FALSE, 0 );
  807. if ( 0 == _hEvent )
  808. LogError( "can't create notify event, %d\n", GetLastError() );
  809. }
  810. ~CAsynchNotify()
  811. {
  812. if ( 0 != _cRef )
  813. LogError( "CAsynchNotify refcounting is broken: %d\n", _cRef );
  814. if ( 0 != _hEvent )
  815. CloseHandle( _hEvent );
  816. }
  817. BOOL IsValid() const { return 0 != _hEvent; }
  818. //
  819. // IUnknown methods.
  820. //
  821. STDMETHOD(QueryInterface) ( REFIID riid, LPVOID *ppiuk )
  822. {
  823. *ppiuk = (void **) this; // hold our breath and jump
  824. AddRef();
  825. return S_OK;
  826. }
  827. STDMETHOD_( ULONG, AddRef ) () { return InterlockedIncrement( &_cRef ); }
  828. STDMETHOD_( ULONG, Release) () { return InterlockedDecrement( &_cRef ); }
  829. //
  830. // IDBAsynchNotify methods
  831. //
  832. STDMETHOD( OnLowResource ) ( DB_DWRESERVE dwReserved )
  833. {
  834. _cLowResource++;
  835. // If we've failed a few times due to low resource, give up
  836. // on the query since there may not be sufficient resources
  837. // to ever get an OnStop call.
  838. if ( _cLowResource >= 5 )
  839. SetEvent( _hEvent );
  840. return S_OK;
  841. }
  842. STDMETHOD( OnProgress ) ( HCHAPTER hChap, DBASYNCHOP ulOp,
  843. DBCOUNTITEM ulProg, DBCOUNTITEM ulProgMax,
  844. DBASYNCHPHASE ulStat, LPOLESTR pwszStatus )
  845. {
  846. return S_OK;
  847. }
  848. STDMETHOD( OnStop ) ( HCHAPTER hChap, ULONG ulOp,
  849. HRESULT hrStat, LPOLESTR pwszStatus )
  850. {
  851. // If the query is complete (successfully or not), set the event
  852. if ( DBASYNCHOP_OPEN == ulOp )
  853. SetEvent( _hEvent );
  854. return S_OK;
  855. }
  856. void Wait()
  857. {
  858. WaitForSingleObject( _hEvent, INFINITE );
  859. }
  860. private:
  861. LONG _cRef;
  862. LONG _cLowResource;
  863. HANDLE _hEvent;
  864. };
  865. //+---------------------------------------------------------------------------
  866. //
  867. // Function: WaitForQueryToComplete
  868. //
  869. // Synopsis: Waits for the query to complete.
  870. //
  871. // Arguments: [pRowset] -- the asynchronous rowset
  872. //
  873. // History: 07 May 1999 dlee Created
  874. //
  875. //----------------------------------------------------------------------------
  876. SCODE WaitForQueryCompletion( IRowset * pRowset )
  877. {
  878. SCODE sc = S_OK;
  879. if ( Rand( 100 ) < 50 )
  880. {
  881. // Register for notifications
  882. XInterface<IConnectionPointContainer> xCPC;
  883. sc = pRowset->QueryInterface( IID_IConnectionPointContainer,
  884. xCPC.GetQIPointer() );
  885. if (FAILED(sc))
  886. {
  887. LogError( "Can't QI for IConnectionPointContainer: %#x\n",sc );
  888. return sc;
  889. }
  890. XInterface<IConnectionPoint> xCP;
  891. sc = xCPC->FindConnectionPoint( IID_IDBAsynchNotify,
  892. xCP.GetPPointer() );
  893. if (FAILED(sc) && CONNECT_E_NOCONNECTION != sc )
  894. {
  895. LogError( "FindConnectionPoint failed: %#x\n",sc );
  896. return sc;
  897. }
  898. CAsynchNotify Notify;
  899. if ( !Notify.IsValid() )
  900. return HRESULT_FROM_WIN32( GetLastError() );
  901. DWORD dwAdviseID;
  902. sc = xCP->Advise( (IUnknown *) &Notify, &dwAdviseID );
  903. if (FAILED(sc))
  904. {
  905. LogError( "IConnectionPoint->Advise failed: %#x\n",sc );
  906. return sc;
  907. }
  908. //
  909. // In a real app, we'd be off doing other work rather than waiting
  910. // for the query to complete, but this will do.
  911. // MsgWaitForSingleObject is a good choice for a GUI app. You could
  912. // also post a user-defined windows message when a notification is
  913. // received.
  914. //
  915. Notify.Wait();
  916. sc = xCP->Unadvise( dwAdviseID );
  917. if ( S_OK != sc )
  918. {
  919. LogError( "IConnectionPoint->Unadvise returned %#x\n", sc );
  920. return sc;
  921. }
  922. Notify.Release();
  923. }
  924. else
  925. {
  926. // Poll. In a real app, real work would happen between checks.
  927. XInterface<IDBAsynchStatus> xIDBAsynch;
  928. sc = pRowset->QueryInterface( IID_IDBAsynchStatus,
  929. xIDBAsynch.GetQIPointer() );
  930. if ( FAILED( sc ) )
  931. return sc;
  932. do
  933. {
  934. DBCOUNTITEM Numerator, Denominator;
  935. DBASYNCHPHASE Phase;
  936. sc = xIDBAsynch->GetStatus( DB_NULL_HCHAPTER,
  937. DBASYNCHOP_OPEN,
  938. &Numerator,
  939. &Denominator,
  940. &Phase,
  941. 0 );
  942. if ( FAILED( sc ) || ( DBASYNCHPHASE_COMPLETE == Phase ) )
  943. break;
  944. Sleep( 20 ); // Give the query a chance to run
  945. } while ( TRUE );
  946. }
  947. return sc;
  948. } //WaitForQueryCompletion
  949. //+---------------------------------------------------------------------------
  950. //
  951. // Function: InstantiateRowset
  952. //
  953. // Synopsis: Forms a query tree consisting of the projection nodes,
  954. // sort node(s), selection node and the restriction tree.
  955. //
  956. // Arguments: [dwDepth] - Query depth, one of QUERY_DEEP or QUERY_SHALLOW
  957. // [pswzScope] - Query scope
  958. // [pTree] - pointer to DBCOMMANDTREE for the query
  959. // [riid] - Interface ID of the desired rowset interface
  960. //
  961. // Returns: IRowsetScroll* - a pointer to an instantiated rowset
  962. //
  963. // History: 22 July 1995 AlanW Created
  964. //
  965. // Notes: Although the returned pointer is to IRowsetScroll, the
  966. // returned pointer may only support IRowset, depending
  967. // upon the riid parameter.
  968. //
  969. // Ownership of the query tree is given to the ICommandTree
  970. // object. The caller does not need to delete it.
  971. //
  972. // Use InstantiateMultipleRowsets for categorized queries.
  973. //
  974. //----------------------------------------------------------------------------
  975. static const DBID dbcolNull = { {0,0,0,{0,0,0,0,0,0,0,0}},DBKIND_GUID_PROPID,0};
  976. static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
  977. IRowsetScroll * InstantiateRowset(
  978. ICommand * pCommandIn,
  979. DWORD dwDepth,
  980. LPWSTR pwszScope,
  981. XPtr<CDbCmdTreeNode> & xTree,
  982. WCHAR const * pwcQuery,
  983. REFIID riid,
  984. BOOL fAsynchronous )
  985. {
  986. ICommand * pCommand;
  987. XInterface<ICommand> xCommand;
  988. if ( 0 == pCommandIn )
  989. {
  990. SCODE sc = CICreateCommand( (IUnknown **) &pCommand,
  991. 0,
  992. IID_ICommand,
  993. g_awcCatalog,
  994. g_awcMachine );
  995. if ( FAILED( sc ) )
  996. {
  997. LogError( "InstantiateRowset - error 0x%x, Unable to create icommand'\n", sc );
  998. return 0;
  999. }
  1000. xCommand.Set( pCommand );
  1001. }
  1002. else
  1003. {
  1004. pCommand = pCommandIn;
  1005. }
  1006. if ( 0 == pCommand )
  1007. return 0;
  1008. if ( g_fSQL )
  1009. {
  1010. XInterface<ICommandText> xCommandText;
  1011. SCODE sc = pCommand->QueryInterface( IID_ICommandText,
  1012. xCommandText.GetQIPointer() );
  1013. if ( FAILED( sc ) )
  1014. {
  1015. LogError( "InstantiateRowset error %#x, can't qi ICommandText\n", sc );
  1016. return 0;
  1017. }
  1018. WCHAR awc[ 300 ];
  1019. swprintf( awc,
  1020. L"SELECT %ws FROM %ws..SCOPE('\"%ws\"') WHERE CONTAINS('%ws')",
  1021. L"Filename, Size, Write, Path, Rank",
  1022. //g_awcMachine,
  1023. g_awcCatalog,
  1024. pwszScope,
  1025. pwcQuery );
  1026. sc = xCommandText->SetCommandText( DBGUID_SQL, awc );
  1027. if ( FAILED( sc ) )
  1028. {
  1029. LogError( "InstantiateRowset error %#x, can't set text\n", sc );
  1030. return 0;
  1031. }
  1032. #if 1
  1033. XInterface<ICommandProperties> xCommandProperties;
  1034. sc = xCommandText->QueryInterface( IID_ICommandProperties,
  1035. xCommandProperties.GetQIPointer() );
  1036. if ( FAILED (sc) )
  1037. {
  1038. LogError( "InstantiateRowset error %#x, can't qi commandprops\n", sc );
  1039. return 0;
  1040. }
  1041. // set the machine name
  1042. DBPROPSET PropSet;
  1043. DBPROP Prop;
  1044. const GUID guidQueryCorePropset = DBPROPSET_CIFRMWRKCORE_EXT;
  1045. PropSet.rgProperties = &Prop;
  1046. PropSet.cProperties = 1;
  1047. PropSet.guidPropertySet = guidQueryCorePropset;
  1048. Prop.dwPropertyID = DBPROP_MACHINE;
  1049. Prop.colid = DB_NULLID;
  1050. Prop.vValue.vt = VT_BSTR;
  1051. Prop.vValue.bstrVal = SysAllocString( g_awcMachine );
  1052. if ( 0 == Prop.vValue.bstrVal )
  1053. {
  1054. LogError( "InstantiateRowset error %#x, can't allocate sql machine\n", sc );
  1055. return 0;
  1056. }
  1057. sc = xCommandProperties->SetProperties ( 1, &PropSet );
  1058. VariantClear( &Prop.vValue );
  1059. if ( FAILED (sc) )
  1060. {
  1061. LogError( "InstantiateRowset error %#x can't set sql machine\n", sc );
  1062. return 0;
  1063. }
  1064. #endif
  1065. XInterface<ICommandPrepare> xCommandPrepare;
  1066. sc = xCommandText->QueryInterface( IID_ICommandPrepare,
  1067. xCommandPrepare.GetQIPointer() );
  1068. if ( FAILED (sc) )
  1069. {
  1070. LogError( "InstantiateRowset error %#x, can't qi prepare\n", sc );
  1071. return 0;
  1072. }
  1073. sc = xCommandPrepare->Prepare( 1 );
  1074. if ( FAILED (sc) )
  1075. {
  1076. LogError( "InstantiateRowset error %#x, can't prepare\n", sc );
  1077. return 0;
  1078. }
  1079. }
  1080. else
  1081. {
  1082. XInterface<ICommandTree> xCmdTree;
  1083. HRESULT sc = pCommand->QueryInterface( IID_ICommandTree,
  1084. xCmdTree.GetQIPointer() );
  1085. if (FAILED (sc) )
  1086. {
  1087. LogError( "QI for ICommandTree failed %#x\n", sc );
  1088. return 0;
  1089. }
  1090. DBCOMMANDTREE * pRoot = xTree->CastToStruct();
  1091. sc = xCmdTree->SetCommandTree( &pRoot, DBCOMMANDREUSE_NONE, FALSE);
  1092. if (FAILED (sc) )
  1093. {
  1094. LogError("SetCommandTree failed, %08x\n", sc);
  1095. return 0;
  1096. }
  1097. xTree.Acquire();
  1098. }
  1099. #ifdef GET_DATA_TOO
  1100. {
  1101. const unsigned MAX_PROPS = 8;
  1102. DBPROPSET aPropSet[MAX_PROPS];
  1103. DBPROP aProp[MAX_PROPS];
  1104. ULONG cProps = 0;
  1105. // We can handle PROPVARIANTs
  1106. aProp[cProps].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
  1107. aProp[cProps].dwOptions = DBPROPOPTIONS_OPTIONAL;
  1108. aProp[cProps].dwStatus = 0;
  1109. aProp[cProps].colid = dbcolNull;
  1110. aProp[cProps].vValue.vt = VT_BOOL;
  1111. aProp[cProps].vValue.boolVal = VARIANT_TRUE;
  1112. aPropSet[cProps].rgProperties = &aProp[cProps];
  1113. aPropSet[cProps].cProperties = 1;
  1114. aPropSet[cProps].guidPropertySet = guidQueryExt;
  1115. cProps++;
  1116. XInterface<ICommandProperties> xCmdProp;
  1117. SCODE sc = pCommand->QueryInterface( IID_ICommandProperties,
  1118. xCmdProp.GetQIPointer() );
  1119. if (FAILED (sc) )
  1120. {
  1121. LogError( "can't qi to commandprops\n", sc );
  1122. return 0;
  1123. }
  1124. sc = xCmdProp->SetProperties( cProps, aPropSet );
  1125. if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc )
  1126. {
  1127. LogError( "can't set commandprops: 0x%lx\n", sc );
  1128. return 0;
  1129. }
  1130. }
  1131. #endif // GET_DATA_TOO
  1132. #ifdef STRESS
  1133. {
  1134. const unsigned MAX_PROPS = 1;
  1135. DBPROPSET aPropSet[MAX_PROPS];
  1136. DBPROP aProp[MAX_PROPS];
  1137. ULONG cProps = 0;
  1138. // Mark the icommand as synch or asynch. Note that we always have
  1139. // to set it since we may have an old ICommand that previously had
  1140. // a different state set.
  1141. aProp[cProps].dwPropertyID = DBPROP_IDBAsynchStatus;
  1142. aProp[cProps].dwOptions = DBPROPOPTIONS_REQUIRED;
  1143. aProp[cProps].dwStatus = 0;
  1144. aProp[cProps].colid = dbcolNull;
  1145. aProp[cProps].vValue.vt = VT_BOOL;
  1146. aProp[cProps].vValue.boolVal = fAsynchronous ? VARIANT_TRUE : VARIANT_FALSE;
  1147. aPropSet[cProps].rgProperties = &aProp[cProps];
  1148. aPropSet[cProps].cProperties = 1;
  1149. aPropSet[cProps].guidPropertySet = guidRowsetProps;
  1150. cProps++;
  1151. XInterface<ICommandProperties> xCmdProp;
  1152. SCODE sc = pCommand->QueryInterface( IID_ICommandProperties,
  1153. xCmdProp.GetQIPointer() );
  1154. if (FAILED (sc) )
  1155. {
  1156. LogError( "can't qi to commandprops\n", sc );
  1157. return 0;
  1158. }
  1159. sc = xCmdProp->SetProperties( cProps, aPropSet );
  1160. if (FAILED (sc) || DB_S_ERRORSOCCURRED == sc )
  1161. {
  1162. LogError( "can't set commandprops: 0x%lx\n", sc );
  1163. return 0;
  1164. }
  1165. }
  1166. #endif
  1167. XInterface<IRowsetScroll> xRowset;
  1168. SCODE sc = pCommand->Execute( 0, // no aggr. IUnknown
  1169. riid, // IID for i/f to return
  1170. 0, // disp. params
  1171. 0, // chapter
  1172. (IUnknown **) xRowset.GetPPointer() );
  1173. if ( FAILED (sc) )
  1174. {
  1175. LogError("ICommand::Execute failed, %08x\n", sc);
  1176. if ( !xRowset.IsNull() )
  1177. LogError( "pRowset is 0x%x when it should be 0\n", xRowset.GetPointer() );
  1178. }
  1179. if ( SUCCEEDED( sc ) && fAsynchronous )
  1180. {
  1181. sc = WaitForQueryCompletion( xRowset.GetPointer() );
  1182. if ( FAILED( sc ) )
  1183. xRowset.Free();
  1184. }
  1185. return xRowset.Acquire();
  1186. } //InstantiateRowset
  1187. //+-------------------------------------------------------------------------
  1188. //
  1189. // Function: MapColumns, public
  1190. //
  1191. // Synopsis: Map column IDs in column bindings. Create an accessor
  1192. // for the binding array.
  1193. //
  1194. // Arguments: [pUnknown] -- Interface capable of returning IColumnsInfo and
  1195. // IAccessor
  1196. // [cCols] -- number of columns in arrays
  1197. // [pBindings] -- column data binding array
  1198. // [pDbCols] -- column IDs array
  1199. //
  1200. // Returns: HACCESSOR - a read accessor for the column bindings.
  1201. //
  1202. // History: 18 May 1995 AlanW Created
  1203. //
  1204. //--------------------------------------------------------------------------
  1205. static DBORDINAL aMappedColumnIDs[20];
  1206. HACCESSOR MapColumns(
  1207. IUnknown * pUnknown,
  1208. ULONG cCols,
  1209. DBBINDING * pBindings,
  1210. const DBID * pDbCols )
  1211. {
  1212. XInterface<IColumnsInfo> xColumnsInfo;
  1213. SCODE sc = pUnknown->QueryInterface( IID_IColumnsInfo,
  1214. xColumnsInfo.GetQIPointer() );
  1215. if ( FAILED( sc ) )
  1216. {
  1217. LogError( "IUnknown::QueryInterface for IColumnsInfo returned 0x%lx\n", sc );
  1218. return 0;
  1219. }
  1220. sc = xColumnsInfo->MapColumnIDs(cCols, pDbCols, aMappedColumnIDs);
  1221. if (S_OK != sc)
  1222. {
  1223. LogError( "IColumnsInfo->MapColumnIDs returned 0x%lx\n",sc);
  1224. return 0;
  1225. }
  1226. for (ULONG i = 0; i < cCols; i++)
  1227. pBindings[i].iOrdinal = aMappedColumnIDs[i];
  1228. XInterface<IAccessor> xAccessor;
  1229. sc = pUnknown->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() );
  1230. if ( FAILED( sc ) )
  1231. {
  1232. LogError( "IRowset::QueryInterface for IAccessor returned 0x%lx\n", sc );
  1233. return 0;
  1234. }
  1235. HACCESSOR hAcc = 0;
  1236. sc = xAccessor->CreateAccessor( DBACCESSOR_ROWDATA,
  1237. cCols, pBindings, 0, &hAcc, 0 );
  1238. if (S_OK != sc)
  1239. LogError( "IAccessor->CreateAccessor returned 0x%lx\n",sc);
  1240. return hAcc;
  1241. } //MapColumns
  1242. //+-------------------------------------------------------------------------
  1243. //
  1244. // Function: ReleaseAccessor, public
  1245. //
  1246. // Synopsis: Release an accessor obtained from MapColumns
  1247. //
  1248. // Arguments: [pUnknown] -- Something that we can QI the IAccessor on
  1249. // [hAcc] -- Accessor handle to be released.
  1250. //
  1251. // Returns: nothing
  1252. //
  1253. // History: 14 June 1995 AlanW Created
  1254. //
  1255. //--------------------------------------------------------------------------
  1256. void ReleaseAccessor( IUnknown * pUnknown, HACCESSOR hAcc )
  1257. {
  1258. XInterface<IAccessor> xAccessor;
  1259. SCODE sc = pUnknown->QueryInterface( IID_IAccessor, xAccessor.GetQIPointer() );
  1260. if ( FAILED( sc ) )
  1261. {
  1262. LogError( "IUnknown::QueryInterface for IAccessor returned 0x%lx\n", sc );
  1263. return;
  1264. }
  1265. sc = xAccessor->ReleaseAccessor( hAcc, 0 );
  1266. if (S_OK != sc)
  1267. LogError( "IAccessor->ReleaseAccessor returned 0x%lx\n",sc);
  1268. } //ReleaseAccessor
  1269. //+-------------------------------------------------------------------------
  1270. //
  1271. // Function: LogError, public
  1272. //
  1273. // Synopsis: Prints a verbose-mode message.
  1274. //
  1275. // Arguments: [pszfmt] -- Format string
  1276. //
  1277. // History: 13-Jul-93 KyleP Created
  1278. //
  1279. //--------------------------------------------------------------------------
  1280. void LogError( char const * pszfmt, ... )
  1281. {
  1282. va_list pargs;
  1283. va_start(pargs, pszfmt);
  1284. vprintf( pszfmt, pargs );
  1285. va_end(pargs);
  1286. _flushall();
  1287. } //LogError