windows-nt/Source/XPSP1/NT/inetsrv/query/bigtable/seqexec.cxx

453 lines
13 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// 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 );
}