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.

452 lines
13 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 2000.
  5. //
  6. // File: SeqExec.cxx
  7. //
  8. // Contents: Sequential Query execution class
  9. //
  10. // Classes: CQSeqExecute
  11. //
  12. // History: 22-Jan-95 DwightKr Created
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <query.hxx>
  18. #include "seqexec.hxx"
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Member: CQSeqExecute::CQSeqExecute, public
  22. //
  23. // Synopsis: Start a query
  24. //
  25. // Arguments: [qopt] -- Query optimizer
  26. //
  27. // History: 20-Jan-95 DwightKr Created
  28. //
  29. //----------------------------------------------------------------------------
  30. CQSeqExecute::CQSeqExecute( XQueryOptimizer & xOpt )
  31. : _status(STAT_BUSY),
  32. _fCursorOnNewObject( TRUE ),
  33. _fAbort( FALSE ),
  34. _TimeLimit( xOpt->GetTimeLimit() ),
  35. _cRowsToReturnMax( xOpt->MaxResults() ),
  36. _xOpt( xOpt )
  37. {
  38. Win4Assert( !_xOpt->IsMultiCursor() && _xOpt->IsFullySorted() );
  39. if ( 0 != _xOpt->FirstRows() )
  40. _cRowsToReturnMax = _xOpt->FirstRows();
  41. if (_cRowsToReturnMax == 0)
  42. _cRowsToReturnMax = 0xFFFFFFFF;
  43. TRY
  44. {
  45. _objs.Set( _xOpt->QueryNextCursor( _status, _fAbort ) );
  46. }
  47. CATCH(CException, e)
  48. {
  49. if ( e.GetErrorCode() == QUERY_E_TIMEDOUT )
  50. {
  51. //
  52. // Execution time limit has been exceeded, not a catastrophic error
  53. //
  54. _status = ( STAT_DONE |
  55. QUERY_RELIABILITY_STATUS( Status() ) |
  56. STAT_TIME_LIMIT_EXCEEDED );
  57. //
  58. // Update local copy of execution time
  59. //
  60. _TimeLimit.SetExecutionTime( 0 );
  61. }
  62. else
  63. RETHROW();
  64. }
  65. END_CATCH
  66. if ( !_objs.IsNull() )
  67. {
  68. _objs->Quiesce();
  69. _objs->SwapOutWorker();
  70. }
  71. } //CQSeqExecute
  72. //+---------------------------------------------------------------------------
  73. //
  74. // Member: CQSeqExecute::GetRows, public
  75. //
  76. // Synopsis: Get the next set of rows from a query
  77. //
  78. // History: 20-Jan-95 DwightKr Created.
  79. //
  80. //----------------------------------------------------------------------------
  81. SCODE CQSeqExecute::GetRows( CTableColumnSet const & OutColumns,
  82. unsigned &cRowsToSkip,
  83. CGetRowsParams & GetParams )
  84. {
  85. if ( _TimeLimit.IsTimedOut() )
  86. return DB_S_STOPLIMITREACHED;
  87. if ( ( STAT_DONE == QUERY_FILL_STATUS( _status ) ) ||
  88. _objs.IsNull() )
  89. return DB_S_ENDOFROWSET;
  90. _TimeLimit.SetBaselineTime( );
  91. SCODE scResult = S_OK;
  92. TRY
  93. {
  94. //
  95. // If this is not the first call to GetRows, then advance to the
  96. // next object. We've already seen the current object.
  97. //
  98. if ( !_fCursorOnNewObject )
  99. _objs->NextWorkId();
  100. //
  101. // Skip the # of rows specified
  102. //
  103. for ( WORKID widEnd = _objs->WorkId();
  104. cRowsToSkip > 0 && widEnd != widInvalid;
  105. widEnd = _objs->NextWorkId() )
  106. {
  107. cRowsToSkip--;
  108. if (_cRowsToReturnMax == 0)
  109. {
  110. vqDebugOut(( DEB_IWARN,
  111. "Query row limit exceeded for %08x\n",
  112. this ));
  113. scResult = DB_S_ENDOFROWSET;
  114. widEnd = widInvalid;
  115. break;
  116. }
  117. _cRowsToReturnMax--;
  118. if ( CheckExecutionTime() )
  119. {
  120. scResult = DB_S_STOPLIMITREACHED;
  121. widEnd = widInvalid;
  122. break;
  123. }
  124. }
  125. //
  126. // Get the # of rows requested
  127. //
  128. BYTE * pbBuf = 0;
  129. while ( widEnd != widInvalid )
  130. {
  131. if ( _cRowsToReturnMax == 0 )
  132. {
  133. vqDebugOut(( DEB_IWARN,
  134. "Query row limit exceeded for %08x\n",
  135. this ));
  136. scResult = DB_S_ENDOFROWSET;
  137. break;
  138. }
  139. if ( ( 0 == ( GetParams.RowsTransferred() % 5 ) ) &&
  140. ( CheckExecutionTime() ) )
  141. {
  142. scResult = DB_S_STOPLIMITREACHED;
  143. break;
  144. }
  145. if ( 0 == pbBuf )
  146. pbBuf = (BYTE *) GetParams.GetFixedVarAllocator().AllocFixed();
  147. scResult = GetRowInfo( OutColumns, GetParams, pbBuf );
  148. //
  149. // If object is deleted, just skip to the next one and re-use
  150. // the row buffer space from the GetParam's allocator.
  151. //
  152. if (scResult == S_OK)
  153. {
  154. // fetched a row -- record that fact
  155. GetParams.IncrementRowCount();
  156. Win4Assert(_cRowsToReturnMax != 0);
  157. _cRowsToReturnMax--;
  158. pbBuf = 0;
  159. }
  160. else
  161. {
  162. if ( scResult == STATUS_FILE_DELETED )
  163. scResult = S_OK;
  164. else
  165. break;
  166. }
  167. // Do we need to get another?
  168. if ( 0 == GetParams.RowsToTransfer() )
  169. break;
  170. widEnd = _objs->NextWorkId();
  171. }
  172. if ( widEnd == widInvalid )
  173. {
  174. scResult = cRowsToSkip ? DB_E_BADSTARTPOSITION : DB_S_ENDOFROWSET;
  175. _status = STAT_DONE | QUERY_RELIABILITY_STATUS(_status);
  176. }
  177. }
  178. CATCH( CException, e )
  179. {
  180. _fCursorOnNewObject = TRUE;
  181. //
  182. // A common error returned here is E_OUTOFMEMORY. This needs
  183. // to be translated and passed back to the caller to be
  184. // handled appropriately. It is a non-fatal error.
  185. //
  186. if ( STATUS_BUFFER_TOO_SMALL == e.GetErrorCode() )
  187. {
  188. if ( GetParams.RowsTransferred() > 0 )
  189. scResult = DB_S_BLOCKLIMITEDROWS;
  190. else
  191. scResult = STATUS_BUFFER_TOO_SMALL;
  192. }
  193. else if ( QUERY_E_TIMEDOUT == e.GetErrorCode() )
  194. {
  195. //
  196. // Execution time limit has been exceeded, not a catastrophic error
  197. //
  198. _status = ( STAT_DONE |
  199. QUERY_RELIABILITY_STATUS( Status() ) |
  200. STAT_TIME_LIMIT_EXCEEDED );
  201. scResult = DB_S_STOPLIMITREACHED;
  202. //
  203. // Update local copy of execution time
  204. //
  205. _TimeLimit.SetExecutionTime( 0 );
  206. }
  207. else
  208. {
  209. vqDebugOut(( DEB_ERROR,
  210. "Exception 0x%x caught in CQSeqExecute::GetRows\n",
  211. e.GetErrorCode() ));
  212. scResult = e.GetErrorCode();
  213. }
  214. }
  215. END_CATCH
  216. _objs->Quiesce();
  217. _objs->SwapOutWorker();
  218. if ( S_OK == scResult )
  219. _fCursorOnNewObject = FALSE;
  220. return scResult;
  221. } //GetRows
  222. //+---------------------------------------------------------------------------
  223. //
  224. // Member: CQSeqExecute::GetRowInfo, private
  225. //
  226. // Synopsis: Get the next row from a query without advancing the cursor
  227. //
  228. // History: 20-Jan-95 DwightKr Created.
  229. //
  230. //----------------------------------------------------------------------------
  231. SCODE CQSeqExecute::GetRowInfo(
  232. CTableColumnSet const & OutColumns,
  233. CGetRowsParams & GetParams,
  234. BYTE * pbBuf )
  235. {
  236. SCODE scResult = S_OK;
  237. //
  238. // Get the current row from the cursor
  239. //
  240. Win4Assert( !_objs.IsNull() );
  241. CRetriever & obj = _objs.GetReference();
  242. //
  243. // Transfer each of the columns to the output buffer
  244. //
  245. for ( unsigned iColumn=0;
  246. iColumn < OutColumns.Size();
  247. iColumn++ )
  248. {
  249. CTableColumn & rColumn = *OutColumns.Get(iColumn);
  250. Win4Assert( VT_EMPTY != rColumn.GetStoredType() );
  251. ULONG cbBuf = sizeof _abValueBuf;
  252. CTableVariant* pVarnt = (CTableVariant *) &_abValueBuf;
  253. RtlZeroMemory( pVarnt, sizeof CTableVariant );
  254. GetValueResult eGvr = obj.GetPropertyValue( rColumn.PropId,
  255. pVarnt,
  256. &cbBuf );
  257. CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
  258. XPtr<CTableVariant> sVarnt(0);
  259. // Really large values must be deferred in cisvc due to the limit on
  260. // the size of named pipe buffers.
  261. if ( ( GVRNotEnoughSpace == eGvr ) &&
  262. ( cbBuf <= cbMaxNonDeferredValueSize ) )
  263. {
  264. sVarnt.Set( (CTableVariant *) new BYTE[cbBuf] ); // Smart pointer
  265. pVarnt = sVarnt.GetPointer();
  266. eGvr = obj.GetPropertyValue( rColumn.PropId, pVarnt, &cbBuf );
  267. }
  268. if ( GVRSuccess != eGvr )
  269. {
  270. if ( GVRNotAvailable == eGvr )
  271. {
  272. pVarnt->vt = VT_EMPTY;
  273. stat = CTableColumn::StoreStatusNull;
  274. }
  275. else if ( GVRSharingViolation == eGvr )
  276. {
  277. pVarnt->vt = VT_EMPTY;
  278. stat = CTableColumn::StoreStatusNull;
  279. // don't set this until it's implemented everywhere
  280. //_status |= STAT_SHARING_VIOLATION;
  281. }
  282. else if ( GVRNotEnoughSpace == eGvr )
  283. {
  284. stat = CTableColumn::StoreStatusDeferred;
  285. }
  286. }
  287. if ( rColumn.IsLengthStored() )
  288. rColumn.SetLength( pbBuf, cbBuf );
  289. Win4Assert( rColumn.IsValueStored() );
  290. if ( ( GVRSuccess == eGvr ) ||
  291. ( GVRNotAvailable == eGvr ) ||
  292. ( GVRSharingViolation == eGvr ) )
  293. {
  294. BYTE *pRowColDataBuf = pbBuf + rColumn.GetValueOffset();
  295. DBLENGTH ulTemp;
  296. DBSTATUS dbStatus = pVarnt->CopyOrCoerce(
  297. pRowColDataBuf,
  298. rColumn.GetValueSize(),
  299. rColumn.GetStoredType(),
  300. ulTemp,
  301. GetParams.GetVarAllocator() );
  302. if ( DBSTATUS_S_OK != dbStatus )
  303. {
  304. vqDebugOut(( DEB_ITRACE,
  305. "Sequential query column 0x%x copy failed "
  306. "srcvt 0x%x, workid 0x%x, dbstat 0x%x\n",
  307. iColumn,
  308. pVarnt->vt,
  309. _objs->WorkId(),
  310. dbStatus ));
  311. stat = CTableColumn::StoreStatusNull;
  312. }
  313. }
  314. else if ( GVRNotEnoughSpace != eGvr ) // deferred
  315. {
  316. vqDebugOut(( DEB_WARN,
  317. "Sequential fetch of property for row (wid = 0x%x) failed %d\n",
  318. _objs->WorkId(), eGvr ));
  319. scResult = E_FAIL;
  320. break;
  321. }
  322. Win4Assert( rColumn.IsStatusStored() );
  323. rColumn.SetStatus( pbBuf, stat );
  324. }
  325. return scResult;
  326. } //GetRowInfo
  327. //+-------------------------------------------------------------------------
  328. //
  329. // Member: CQSeqExecute::CheckExecutionTime, private
  330. //
  331. // Synopsis: Check for CPU time limit exceeded
  332. //
  333. // Arguments: NONE
  334. //
  335. // Returns: TRUE if execution time limit exceeded.
  336. //
  337. // Notes: The CPU time spent executing a query since the last
  338. // check is computed and compared with the remaining time
  339. // in the CPU time limit. If the time limit is exceeded,
  340. // the query is aborted and a status bit is set indicating
  341. // that. Otherwise, the remaining time and the input time
  342. // snapshots are updated.
  343. //
  344. // History: 08 Apr 96 AlanW Created
  345. //
  346. //--------------------------------------------------------------------------
  347. BOOL CQSeqExecute::CheckExecutionTime( void )
  348. {
  349. if ( _TimeLimit.CheckExecutionTime() )
  350. {
  351. vqDebugOut(( DEB_IWARN,
  352. "Execution time limit exceeded for %08x\n",
  353. this ));
  354. _status = ( STAT_DONE |
  355. QUERY_RELIABILITY_STATUS( Status() ) |
  356. STAT_TIME_LIMIT_EXCEEDED );
  357. return TRUE;
  358. }
  359. return FALSE;
  360. }
  361. //+-------------------------------------------------------------------------
  362. //
  363. // Member: CQSeqExecute::FetchDeferredValue
  364. //
  365. // Synopsis: Checks if read access is permitted
  366. //
  367. // Arguments: [wid] - Workid
  368. // [ps] -- Property to be fetched
  369. // [var] -- Property returned here
  370. //
  371. // History: 12-Jan-97 SitaramR Created
  372. //
  373. //--------------------------------------------------------------------------
  374. BOOL CQSeqExecute::FetchDeferredValue( WORKID wid,
  375. CFullPropSpec const & ps,
  376. PROPVARIANT & var )
  377. {
  378. return _xOpt->FetchDeferredValue( wid, ps, var );
  379. }