mirror of https://github.com/tongzx/nt5src
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
452 lines
13 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995 - 2000.
|
|
//
|
|
// File: SeqExec.cxx
|
|
//
|
|
// Contents: Sequential Query execution class
|
|
//
|
|
// Classes: CQSeqExecute
|
|
//
|
|
// History: 22-Jan-95 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <query.hxx>
|
|
|
|
#include "seqexec.hxx"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CQSeqExecute::CQSeqExecute, public
|
|
//
|
|
// Synopsis: Start a query
|
|
//
|
|
// Arguments: [qopt] -- Query optimizer
|
|
//
|
|
// History: 20-Jan-95 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CQSeqExecute::CQSeqExecute( XQueryOptimizer & xOpt )
|
|
: _status(STAT_BUSY),
|
|
_fCursorOnNewObject( TRUE ),
|
|
_fAbort( FALSE ),
|
|
_TimeLimit( xOpt->GetTimeLimit() ),
|
|
_cRowsToReturnMax( xOpt->MaxResults() ),
|
|
_xOpt( xOpt )
|
|
{
|
|
Win4Assert( !_xOpt->IsMultiCursor() && _xOpt->IsFullySorted() );
|
|
|
|
if ( 0 != _xOpt->FirstRows() )
|
|
_cRowsToReturnMax = _xOpt->FirstRows();
|
|
|
|
if (_cRowsToReturnMax == 0)
|
|
_cRowsToReturnMax = 0xFFFFFFFF;
|
|
|
|
TRY
|
|
{
|
|
_objs.Set( _xOpt->QueryNextCursor( _status, _fAbort ) );
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
if ( e.GetErrorCode() == QUERY_E_TIMEDOUT )
|
|
{
|
|
//
|
|
// Execution time limit has been exceeded, not a catastrophic error
|
|
//
|
|
_status = ( STAT_DONE |
|
|
QUERY_RELIABILITY_STATUS( Status() ) |
|
|
STAT_TIME_LIMIT_EXCEEDED );
|
|
|
|
//
|
|
// Update local copy of execution time
|
|
//
|
|
_TimeLimit.SetExecutionTime( 0 );
|
|
}
|
|
else
|
|
RETHROW();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( !_objs.IsNull() )
|
|
{
|
|
_objs->Quiesce();
|
|
_objs->SwapOutWorker();
|
|
}
|
|
} //CQSeqExecute
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CQSeqExecute::GetRows, public
|
|
//
|
|
// Synopsis: Get the next set of rows from a query
|
|
//
|
|
// History: 20-Jan-95 DwightKr Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CQSeqExecute::GetRows( CTableColumnSet const & OutColumns,
|
|
unsigned &cRowsToSkip,
|
|
CGetRowsParams & GetParams )
|
|
{
|
|
if ( _TimeLimit.IsTimedOut() )
|
|
return DB_S_STOPLIMITREACHED;
|
|
|
|
if ( ( STAT_DONE == QUERY_FILL_STATUS( _status ) ) ||
|
|
_objs.IsNull() )
|
|
return DB_S_ENDOFROWSET;
|
|
|
|
_TimeLimit.SetBaselineTime( );
|
|
SCODE scResult = S_OK;
|
|
|
|
TRY
|
|
{
|
|
//
|
|
// If this is not the first call to GetRows, then advance to the
|
|
// next object. We've already seen the current object.
|
|
//
|
|
|
|
if ( !_fCursorOnNewObject )
|
|
_objs->NextWorkId();
|
|
|
|
//
|
|
// Skip the # of rows specified
|
|
//
|
|
|
|
for ( WORKID widEnd = _objs->WorkId();
|
|
cRowsToSkip > 0 && widEnd != widInvalid;
|
|
widEnd = _objs->NextWorkId() )
|
|
{
|
|
cRowsToSkip--;
|
|
|
|
if (_cRowsToReturnMax == 0)
|
|
{
|
|
vqDebugOut(( DEB_IWARN,
|
|
"Query row limit exceeded for %08x\n",
|
|
this ));
|
|
|
|
scResult = DB_S_ENDOFROWSET;
|
|
widEnd = widInvalid;
|
|
break;
|
|
}
|
|
_cRowsToReturnMax--;
|
|
|
|
if ( CheckExecutionTime() )
|
|
{
|
|
scResult = DB_S_STOPLIMITREACHED;
|
|
widEnd = widInvalid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the # of rows requested
|
|
//
|
|
|
|
BYTE * pbBuf = 0;
|
|
|
|
while ( widEnd != widInvalid )
|
|
{
|
|
if ( _cRowsToReturnMax == 0 )
|
|
{
|
|
vqDebugOut(( DEB_IWARN,
|
|
"Query row limit exceeded for %08x\n",
|
|
this ));
|
|
|
|
scResult = DB_S_ENDOFROWSET;
|
|
break;
|
|
}
|
|
if ( ( 0 == ( GetParams.RowsTransferred() % 5 ) ) &&
|
|
( CheckExecutionTime() ) )
|
|
{
|
|
scResult = DB_S_STOPLIMITREACHED;
|
|
break;
|
|
}
|
|
|
|
if ( 0 == pbBuf )
|
|
pbBuf = (BYTE *) GetParams.GetFixedVarAllocator().AllocFixed();
|
|
|
|
scResult = GetRowInfo( OutColumns, GetParams, pbBuf );
|
|
|
|
//
|
|
// If object is deleted, just skip to the next one and re-use
|
|
// the row buffer space from the GetParam's allocator.
|
|
//
|
|
|
|
if (scResult == S_OK)
|
|
{
|
|
// fetched a row -- record that fact
|
|
|
|
GetParams.IncrementRowCount();
|
|
Win4Assert(_cRowsToReturnMax != 0);
|
|
_cRowsToReturnMax--;
|
|
pbBuf = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( scResult == STATUS_FILE_DELETED )
|
|
scResult = S_OK;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Do we need to get another?
|
|
|
|
if ( 0 == GetParams.RowsToTransfer() )
|
|
break;
|
|
|
|
widEnd = _objs->NextWorkId();
|
|
}
|
|
|
|
if ( widEnd == widInvalid )
|
|
{
|
|
scResult = cRowsToSkip ? DB_E_BADSTARTPOSITION : DB_S_ENDOFROWSET;
|
|
_status = STAT_DONE | QUERY_RELIABILITY_STATUS(_status);
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
_fCursorOnNewObject = TRUE;
|
|
|
|
//
|
|
// A common error returned here is E_OUTOFMEMORY. This needs
|
|
// to be translated and passed back to the caller to be
|
|
// handled appropriately. It is a non-fatal error.
|
|
//
|
|
|
|
if ( STATUS_BUFFER_TOO_SMALL == e.GetErrorCode() )
|
|
{
|
|
if ( GetParams.RowsTransferred() > 0 )
|
|
scResult = DB_S_BLOCKLIMITEDROWS;
|
|
else
|
|
scResult = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else if ( QUERY_E_TIMEDOUT == e.GetErrorCode() )
|
|
{
|
|
//
|
|
// Execution time limit has been exceeded, not a catastrophic error
|
|
//
|
|
_status = ( STAT_DONE |
|
|
QUERY_RELIABILITY_STATUS( Status() ) |
|
|
STAT_TIME_LIMIT_EXCEEDED );
|
|
scResult = DB_S_STOPLIMITREACHED;
|
|
|
|
//
|
|
// Update local copy of execution time
|
|
//
|
|
_TimeLimit.SetExecutionTime( 0 );
|
|
}
|
|
else
|
|
{
|
|
vqDebugOut(( DEB_ERROR,
|
|
"Exception 0x%x caught in CQSeqExecute::GetRows\n",
|
|
e.GetErrorCode() ));
|
|
|
|
scResult = e.GetErrorCode();
|
|
}
|
|
}
|
|
END_CATCH
|
|
|
|
_objs->Quiesce();
|
|
|
|
_objs->SwapOutWorker();
|
|
|
|
if ( S_OK == scResult )
|
|
_fCursorOnNewObject = FALSE;
|
|
|
|
return scResult;
|
|
} //GetRows
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CQSeqExecute::GetRowInfo, private
|
|
//
|
|
// Synopsis: Get the next row from a query without advancing the cursor
|
|
//
|
|
// History: 20-Jan-95 DwightKr Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
SCODE CQSeqExecute::GetRowInfo(
|
|
CTableColumnSet const & OutColumns,
|
|
CGetRowsParams & GetParams,
|
|
BYTE * pbBuf )
|
|
{
|
|
SCODE scResult = S_OK;
|
|
|
|
//
|
|
// Get the current row from the cursor
|
|
//
|
|
|
|
Win4Assert( !_objs.IsNull() );
|
|
CRetriever & obj = _objs.GetReference();
|
|
|
|
//
|
|
// Transfer each of the columns to the output buffer
|
|
//
|
|
for ( unsigned iColumn=0;
|
|
iColumn < OutColumns.Size();
|
|
iColumn++ )
|
|
{
|
|
CTableColumn & rColumn = *OutColumns.Get(iColumn);
|
|
|
|
Win4Assert( VT_EMPTY != rColumn.GetStoredType() );
|
|
|
|
ULONG cbBuf = sizeof _abValueBuf;
|
|
CTableVariant* pVarnt = (CTableVariant *) &_abValueBuf;
|
|
RtlZeroMemory( pVarnt, sizeof CTableVariant );
|
|
|
|
GetValueResult eGvr = obj.GetPropertyValue( rColumn.PropId,
|
|
pVarnt,
|
|
&cbBuf );
|
|
|
|
CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
|
|
XPtr<CTableVariant> sVarnt(0);
|
|
|
|
// Really large values must be deferred in cisvc due to the limit on
|
|
// the size of named pipe buffers.
|
|
|
|
if ( ( GVRNotEnoughSpace == eGvr ) &&
|
|
( cbBuf <= cbMaxNonDeferredValueSize ) )
|
|
{
|
|
sVarnt.Set( (CTableVariant *) new BYTE[cbBuf] ); // Smart pointer
|
|
pVarnt = sVarnt.GetPointer();
|
|
eGvr = obj.GetPropertyValue( rColumn.PropId, pVarnt, &cbBuf );
|
|
}
|
|
|
|
if ( GVRSuccess != eGvr )
|
|
{
|
|
if ( GVRNotAvailable == eGvr )
|
|
{
|
|
pVarnt->vt = VT_EMPTY;
|
|
stat = CTableColumn::StoreStatusNull;
|
|
}
|
|
else if ( GVRSharingViolation == eGvr )
|
|
{
|
|
pVarnt->vt = VT_EMPTY;
|
|
stat = CTableColumn::StoreStatusNull;
|
|
|
|
// don't set this until it's implemented everywhere
|
|
//_status |= STAT_SHARING_VIOLATION;
|
|
}
|
|
else if ( GVRNotEnoughSpace == eGvr )
|
|
{
|
|
stat = CTableColumn::StoreStatusDeferred;
|
|
}
|
|
}
|
|
|
|
if ( rColumn.IsLengthStored() )
|
|
rColumn.SetLength( pbBuf, cbBuf );
|
|
|
|
Win4Assert( rColumn.IsValueStored() );
|
|
|
|
if ( ( GVRSuccess == eGvr ) ||
|
|
( GVRNotAvailable == eGvr ) ||
|
|
( GVRSharingViolation == eGvr ) )
|
|
{
|
|
BYTE *pRowColDataBuf = pbBuf + rColumn.GetValueOffset();
|
|
DBLENGTH ulTemp;
|
|
|
|
DBSTATUS dbStatus = pVarnt->CopyOrCoerce(
|
|
pRowColDataBuf,
|
|
rColumn.GetValueSize(),
|
|
rColumn.GetStoredType(),
|
|
ulTemp,
|
|
GetParams.GetVarAllocator() );
|
|
|
|
if ( DBSTATUS_S_OK != dbStatus )
|
|
{
|
|
vqDebugOut(( DEB_ITRACE,
|
|
"Sequential query column 0x%x copy failed "
|
|
"srcvt 0x%x, workid 0x%x, dbstat 0x%x\n",
|
|
iColumn,
|
|
pVarnt->vt,
|
|
_objs->WorkId(),
|
|
dbStatus ));
|
|
|
|
stat = CTableColumn::StoreStatusNull;
|
|
}
|
|
}
|
|
else if ( GVRNotEnoughSpace != eGvr ) // deferred
|
|
{
|
|
vqDebugOut(( DEB_WARN,
|
|
"Sequential fetch of property for row (wid = 0x%x) failed %d\n",
|
|
_objs->WorkId(), eGvr ));
|
|
scResult = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
Win4Assert( rColumn.IsStatusStored() );
|
|
rColumn.SetStatus( pbBuf, stat );
|
|
}
|
|
|
|
return scResult;
|
|
} //GetRowInfo
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CQSeqExecute::CheckExecutionTime, private
|
|
//
|
|
// Synopsis: Check for CPU time limit exceeded
|
|
//
|
|
// Arguments: NONE
|
|
//
|
|
// Returns: TRUE if execution time limit exceeded.
|
|
//
|
|
// Notes: The CPU time spent executing a query since the last
|
|
// check is computed and compared with the remaining time
|
|
// in the CPU time limit. If the time limit is exceeded,
|
|
// the query is aborted and a status bit is set indicating
|
|
// that. Otherwise, the remaining time and the input time
|
|
// snapshots are updated.
|
|
//
|
|
// History: 08 Apr 96 AlanW Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CQSeqExecute::CheckExecutionTime( void )
|
|
{
|
|
if ( _TimeLimit.CheckExecutionTime() )
|
|
{
|
|
vqDebugOut(( DEB_IWARN,
|
|
"Execution time limit exceeded for %08x\n",
|
|
this ));
|
|
|
|
_status = ( STAT_DONE |
|
|
QUERY_RELIABILITY_STATUS( Status() ) |
|
|
STAT_TIME_LIMIT_EXCEEDED );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CQSeqExecute::FetchDeferredValue
|
|
//
|
|
// Synopsis: Checks if read access is permitted
|
|
//
|
|
// Arguments: [wid] - Workid
|
|
// [ps] -- Property to be fetched
|
|
// [var] -- Property returned here
|
|
//
|
|
// History: 12-Jan-97 SitaramR Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CQSeqExecute::FetchDeferredValue( WORKID wid,
|
|
CFullPropSpec const & ps,
|
|
PROPVARIANT & var )
|
|
{
|
|
return _xOpt->FetchDeferredValue( wid, ps, var );
|
|
}
|
|
|
|
|