//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1994 - 2000.
//
//  File:       seqquery.cxx
//
//  Contents:   Declarations of classes which implement sequential ICursor
//              and related OLE DB interfaces over file stores.
//
//  Classes:    CSeqQuery - container of table/query for a set of seq cursors
//
//  History:    09-Jan-95       DwightKr    Created
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <tblalloc.hxx>
#include <rowseek.hxx>
#include <tblvarnt.hxx>

#include "tabledbg.hxx"
#include "seqquery.hxx"

//+---------------------------------------------------------------------------
//
//  Class:      CSeqQuery
//
//  Purpose:    Encapsulates a sequential query execution context, and PID
//              mapper for use by a row cursor.
//
//  Arguments:  [qopt]         - Query optimizer
//              [col]          - Initial set of columns to return
//              [pulCursors]   - cursor returned
//              [pidremap]     - prop ID mapping
//              [pDocStore]    - client doc store
//
//  History:    09 Jan 95       DwightKr    Created
//
//----------------------------------------------------------------------------

CSeqQuery::CSeqQuery( XQueryOptimizer & qopt,
                      XColumnSet & col,
                      ULONG *pulCursor,
                      XInterface<CPidRemapper> & pidremap,
                      ICiCDocStore *pDocStore
                    )
        : PQuery( ),
          _ref( 1 ),
          _pidremap( pidremap.Acquire() ),
          _QExec( 0 )
{
    //
    // Get ci manager and translator interfaces
    //
    ICiManager *pCiManager = 0;
    SCODE sc = pDocStore->GetContentIndex( &pCiManager );
    if ( FAILED( sc ) )
    {
        Win4Assert( !"Need to support GetContentIndex interface" );

        THROW( CException( sc ) );
    }
    _xCiManager.Set( pCiManager );

    ICiCDocNameToWorkidTranslator *pNameToWidTranslator;
    sc = pDocStore->QueryInterface( IID_ICiCDocNameToWorkidTranslator,
                                    (void **) &pNameToWidTranslator );
    if ( FAILED( sc ) )
    {
        Win4Assert( !"Need to support translator QI" );

        THROW( CException( sc ) );
    }
    _xNameToWidTranslator.Set( pNameToWidTranslator );

    _QExec.Set( new CQSeqExecute( qopt ) );
    *pulCursor = _CreateRowCursor();

    ciDebugOut(( DEB_USER1, "Using a sequential cursor.\n" ));
}

//+---------------------------------------------------------------------------
//
//  Member:     CSeqQuery::AddRef, public
//
//  Synopsis:   Reference the query.
//
//  History:    19-Jan-95   DwightKr    Created
//
//----------------------------------------------------------------------------
ULONG CSeqQuery::AddRef(void)
{
    return InterlockedIncrement( &_ref );
}


//+---------------------------------------------------------------------------
//
//  Member:     CSeqQuery::Release, public
//
//  Synopsis:   De-Reference the query.
//
//  Effects:    If the ref count goes to 0 then the query is deleted.
//
//  History:    19-Jan-95   DwightKr    Created
//
//----------------------------------------------------------------------------
ULONG CSeqQuery::Release(void)
{
    long l = InterlockedDecrement( &_ref );
    if ( l <= 0 )
    {
        tbDebugOut(( DEB_ITRACE, "CSeqQuery unreferenced.  Deleting.\n" ));
        delete this;
        return 0;
    }

    return l;
}

//+-------------------------------------------------------------------------
//
//  Member:     CSeqQuery::FetchDeferredValue
//
//  Synopsis:   Fetch value from property cache
//
//  Arguments:  [wid] -- Workid.
//              [ps]  -- Property to be fetched.
//              [var] -- Property returned here.
//
//  History:    Jun-1-95   KyleP   Created
//
//--------------------------------------------------------------------------

BOOL CSeqQuery::FetchDeferredValue( WORKID wid,
                                    CFullPropSpec const & ps,
                                    PROPVARIANT & var )
{
    return _QExec->FetchDeferredValue( wid, ps, var );
}

//
//  Methods supporting IRowset
//
//+---------------------------------------------------------------------------
//
//  Member:     CSeqQuery::SetBindings, public
//
//  Synopsis:   Set column bindings into a cursor
//
//  Arguments:  [hCursor] - the handle of the cursor to set bindings on
//              [cbRowLength] - the width of an output row
//              [cols] - a description of column bindings to be set
//              [pids] - a PID mapper which maps fake pids in cols to
//                      column IDs.
//
//  Returns:    nothing - failures are thrown.  E_FAIL
//                      is thrown if hCursor cannot be looked up,
//                      presumably an internal error.
//
//  History:    19-Jan-95   DwightKr    Created
//
//----------------------------------------------------------------------------
void CSeqQuery::SetBindings(
    ULONG             hCursor,
    ULONG             cbRowLength,
    CTableColumnSet & cols,
    CPidMapper &      pids )
{
    _VerifyHandle(hCursor);

    if (0 == cols.Count() ||
        0 == cbRowLength || cbRowLength >= USHRT_MAX)
        THROW( CException( E_INVALIDARG ));

    XPtr<CTableColumnSet> outset(new CTableColumnSet( cols.Count() ));

    for (unsigned iCol = 0; iCol < cols.Count(); iCol++)
    {
        CTableColumn * pCol = cols.Get( iCol );

        CFullPropSpec * propspec = pids.Get( pCol->PropId );

        //
        //  Convert the DBID to a PROPID
        //

//      Win4Assert( iCol+1 == pCol->PropId );   // Therefore pids is useless

        PROPID prop = _pidremap->NameToReal(propspec);

        if ( prop == pidInvalid )
            THROW( CException( DB_E_BADCOLUMNID ));

        if (pCol->IsCompressedCol())
            THROW( CException( E_INVALIDARG ));

        XPtr<CTableColumn> xpOutcol ( new CTableColumn( prop, pCol->GetStoredType() ) );

        if (pCol->IsValueStored())
            xpOutcol->SetValueField(pCol->GetStoredType(),
                                    pCol->GetValueOffset(),
                                    pCol->GetValueSize());

        Win4Assert( pCol->IsStatusStored() );
        xpOutcol->SetStatusField(pCol->GetStatusOffset(),
                                 (USHORT)pCol->GetStatusSize());

        if (pCol->IsLengthStored())
            xpOutcol->SetLengthField(pCol->GetLengthOffset(),
                                     (USHORT)pCol->GetLengthSize());

        outset->Add(xpOutcol, iCol);
    }

    SCODE sc = _cursor.SetBindings( cbRowLength, outset );

    if ( FAILED( sc ) )
        THROW( CException( sc ) );
} //SetBindings


//+-------------------------------------------------------------------------
//
//  Member:     CSeqQuery::RatioFinished, public
//
//  Synopsis:   Return the completion status as a fraction
//
//  Arguments:  [hCursor] - the handle of the cursor to check completion for
//              [rulDenominator] - on return, denominator of fraction
//              [rulNumerator] - on return, numerator of fraction
//              [rcRows] - on return, number of rows in cursor
//              [rfNewRows] - on return, TRUE if new rows available
//
//  Returns:    nothing
//
//  Notes:      A value of 0 for hCursor is allowed so that completion
//              can be checked before a handle exists.
//
//--------------------------------------------------------------------------

void  CSeqQuery::RatioFinished(
                                ULONG   hCursor,
                                DBCOUNTITEM & rulDenominator,
                                DBCOUNTITEM & rulNumerator,
                                DBCOUNTITEM & rcRows,
                                BOOL &  rfNewRows
                               )
{
    if ( 0 != hCursor )
        _VerifyHandle(hCursor);

    rulDenominator = 1;
    rulNumerator   = 1;
    rcRows = 0;                 // we don't know how many there are...
    rfNewRows      = FALSE;
}


//+---------------------------------------------------------------------------
//
//  Member:     CSeqQuery::GetRows, public
//
//  Synopsis:   Retrieve row data for a table cursor
//
//  Arguments:  [hCursor] - the handle of the cursor to fetch data for
//              [rSeekDesc] - row seek operation to be done before fetch
//              [rFetchParams] - row fetch parameters and buffer pointers
//
//  Returns:    SCODE - the status of the operation.  E_FAIL
//                      is thrown if hCursor cannot be looked up,
//                      presumably an internal error.
//
//  History:    19-Jan-95   DwightKr    Created
//
//----------------------------------------------------------------------------
SCODE CSeqQuery::GetRows(
                         ULONG hCursor,
                         const CRowSeekDescription& rSeekDesc,
                         CGetRowsParams& rFetchParams,
                         XPtr<CRowSeekDescription>& pSeekDescOut
                        )
{
    _VerifyHandle( hCursor );

    if ( ! rSeekDesc.IsCurrentRowSeek() )
        THROW( CException( E_FAIL ) );

    CRowSeekNext* pRowSeek = (CRowSeekNext*) &rSeekDesc;

    unsigned cRowsToSkip = pRowSeek->GetSkip();
    _cursor.ValidateBindings();

    SCODE scRet = _QExec->GetRows( _cursor.GetBindings(),
                                   cRowsToSkip,
                                   rFetchParams );

    if (FAILED(scRet))
    {
        tbDebugOut(( DEB_WARN, "CSeqQuery::GetRows got sc=%x\n",
                           scRet ));
    }

    if (DB_S_BLOCKLIMITEDROWS == scRet)
        pSeekDescOut.Set( new CRowSeekNext(pRowSeek->GetChapter(), 0) );

    return scRet;
} //GetRows


//+-------------------------------------------------------------------------
//
//  Member:     CSeqQuery::GetQueryStatus, public
//
//  Synopsis:   Return the query status
//
//  Arguments:  [hCursor] - the handle of the cursor to check completion for
//              [rdwStatus] - on return, the query status
//
//  Returns:    nothing
//
//--------------------------------------------------------------------------

void        CSeqQuery::GetQueryStatus(
    ULONG           hCursor,
    DWORD &         rdwStatus)
{
    _VerifyHandle( hCursor );

    rdwStatus = _QExec->Status();
}

//+-------------------------------------------------------------------------
//
//  Member:     CSeqQuery::GetQueryStatusEx, public
//
//  Synopsis:   Return the query status plus bonus information.  It's kind
//              of an odd assortment of info, but it saves net trips.
//
//  Arguments:  [hCursor] - handle of the cursor to check completion for
//              [rdwStatus] - returns the query status
//              [rcFilteredDocuments] - returns # of filtered docs
//              [rcDocumentsToFilter] - returns # of docs to filter
//              [rdwRatioFinishedDenominator] - ratio finished denom
//              [rdwRatioFinishedNumerator]   - ratio finished num
//              [bmk]                         - bmk to find
//              [riRowBmk]                    - index of bmk row
//              [rcRowsTotal]                 - # of rows in table
//
//  History:    Nov-9-96   dlee    Created
//
//--------------------------------------------------------------------------

void CSeqQuery::GetQueryStatusEx(
     ULONG           hCursor,
     DWORD &         rdwStatus,
     DWORD &         rcFilteredDocuments,
     DWORD &         rcDocumentsToFilter,
     DBCOUNTITEM &   rdwRatioFinishedDenominator,
     DBCOUNTITEM &   rdwRatioFinishedNumerator,
     CI_TBL_BMK      bmk,
     DBCOUNTITEM &   riRowBmk,
     DBCOUNTITEM &   rcRowsTotal )
{
    GetQueryStatus( hCursor, rdwStatus );

    CIF_STATE state;
    state.cbStruct = sizeof state;
    SCODE sc = _xCiManager->GetStatus( &state );
    if ( SUCCEEDED( sc ) )
    {
        rcFilteredDocuments = state.cFilteredDocuments;
        rcDocumentsToFilter = state.cDocuments;
    }
    else
    {
        ciDebugOut(( DEB_ERROR,
                     "CSeqQuery::GetQueryStatusEx, get status failed, 0x%x\n", sc ));

        rcFilteredDocuments = 0;
        rcDocumentsToFilter = 0;
    }

    // sequential query -- it's done

    rdwRatioFinishedDenominator = 1;
    rdwRatioFinishedNumerator = 1;

    // sequential query -- this info isn't available

    riRowBmk = 0;
    rcRowsTotal = 0;
} //GetQueryStatusEx

//+-------------------------------------------------------------------------
//
//  Member:     CAsyncQuery::WorkIdToPath
//
//  Synopsis:   Converts a wid to a path
//
//  Arguments:  [wid]       -- of the file to be translated
//              [funnyPath] -- resulting path
//
//  History:    Oct-5-96   dlee    Created
//
//--------------------------------------------------------------------------

void CSeqQuery::WorkIdToPath(
    WORKID          wid,
    CFunnyPath & funnyPath )
{
    ICiCDocName *pDocName;
    SCODE sc = _xNameToWidTranslator->QueryDocName( &pDocName );
    if ( SUCCEEDED( sc ) )
    {
        XInterface<ICiCDocName> xDocName( pDocName );

        sc = _xNameToWidTranslator->WorkIdToDocName( wid,
                                                     xDocName.GetPointer() );
        if ( SUCCEEDED( sc ) && sc != CI_S_WORKID_DELETED )
        {

            // PERFFIX: Here we are using two buffers XGrowable and
            // CFunnyPath.  This can be avoided if xDocName->Get can take
            // in CFunnyPath instead of WCHAR*

            XGrowable<WCHAR> xBuf(MAX_PATH);
            ULONG cb = xBuf.SizeOf();

            sc = xDocName->Get( (BYTE *) xBuf.Get(), &cb );
            if ( CI_E_BUFFERTOOSMALL == sc )
            {
                xBuf.SetSizeInBytes( cb );
                sc = xDocName->Get( (BYTE *) xBuf.Get(), &cb );
            }

            if ( SUCCEEDED( sc ) )
                funnyPath.SetPath( xBuf.Get() );
        }
    }
} //WorkIdToPath