//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995 - 2000.
//
// File:        QOptimiz.hxx
//
// Contents:    Query optimizer.  Chooses indexes and table implementations.
//
// Classes:     CQueryOptimizer
//
// History:     21-Jun-95       KyleP       Created
//
//----------------------------------------------------------------------------

#pragma once

#include <coldesc.hxx>
#include <xpr.hxx>
#include <cqueue.hxx>
#include <strategy.hxx>
#include <qiterate.hxx>
#include <rstprop.hxx>
#include <timlimit.hxx>
#include <ciintf.h>
#include <frmutils.hxx>

class CPidRemapper;

DECLARE_SMARTP( Columns );
DECLARE_SMARTP( Sort );

class CGenericCursor;
class CSingletonCursor;

const MAX_HITS_FOR_FAST_SORT_BY_RANK_OPT = 2000;     // Internal cutoff on # results for
                                                     // sorted-by-rank optimization


//+---------------------------------------------------------------------------
//
//  Class:      CQueryOptimizer
//
//  Purpose:    Chooses indexes and table strategies.
//
//  History:    21-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

class CQueryOptimizer
{
public:

    CQueryOptimizer( XInterface<ICiCQuerySession>& xQuerySession,
                     ICiCDocStore *pDocStore,
                     XRestriction & rst,
                     CColumnSet const & cols,
                     CSortSet const * psort,
                     CPidRemapper const & pidmap,
                     CRowsetProperties const & rProps,
                     DWORD dwQueryStatus
                   );

    ~CQueryOptimizer();


    //
    // Table choices.
    //

    inline BOOL IsMultiCursor() const;
    inline BOOL IsFullySorted() const;
    inline BOOL IsPositionable() const;

    //
    // Cursor retrieval
    //


    inline BOOL RequiresCI() const;
    inline void GetCurrentComponentIndex( unsigned & iCurrent,
                                          unsigned & cTotal ) const;
    inline BOOL CQueryOptimizer::IsWorkidUnique()
    {
        //
        // If we have a null catalog, we do not have wids in a
        // persistent store. So we have to return FALSE here to cause
        // the bigtable to associate wids with the matching docs and
        // use them wherever wids are needed.
        //

        return !_fNullCatalog;
    }

    CGenericCursor *          QueryNextCursor( ULONG & status, BOOL& fAbort );
    CSingletonCursor *        QuerySingletonCursor( BOOL& fAbort );
    CTimeLimit &              GetTimeLimit();

    //
    // Singleton support
    //

    inline void EnableSingletonCursors();

#   ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#   endif

    ULONG MaxResults() const { return _cMaxResults; }

    ULONG FirstRows() const { return _cFirstRows; }                                                

    BOOL FetchDeferredValue( WORKID wid,
                             CFullPropSpec const & ps,
                             PROPVARIANT & var );

    BOOL CanPartialDefer()
    {
        // We cannot partial defer if:
        // We have a null catalog OR
        // enumOption == CI_ENUM_MUST_NEVER_DEFER
            
        if ( _fNullCatalog )
        {
            return FALSE;
        }

        CI_ENUM_OPTIONS enumOption;

        SCODE sc = _xQuerySession->GetEnumOption( &enumOption );
        if ( FAILED( sc ) )
        {
            vqDebugOut(( DEB_ERROR, "CanPartialDefer - GetEnumOption returned 0x%x\n", sc ));
            return FALSE;
        }
        return ( enumOption != CI_ENUM_MUST_NEVER_DEFER );
    }

private:

    //
    // Validation
    //

    BOOL  Validate();


    //
    // Query optimization
    //

    void  ChooseIndexStrategy( CSortSet const * psort, CColumnSet const & cols );

    CGenericCursor * ApplyIndexStrategy( ULONG & status, BOOL& fAbort );

    ACCESS_MASK          _AccessMask() const;

    BOOL                 _fMulti;                // TRUE if query requires > 1 cursor.
    BOOL                 _fAtEnd;                // TRUE if out of cursors.
    BOOL                 _fFullySorted;          // TRUE if current component will iterate
                                                 //   in sorted order.
    DWORD                _dwQueryStatus;         // The original status
    XInterface<ICiCQuerySession> _xQuerySession; // Query session object
    XInterface<ICiCDeferredPropRetriever> _xDefPropRetriever;  // Deferred property retriever
    XInterface<ICiManager>       _xCiManager;                  // Content index
    CCiFrameworkParams           _frameworkParams;             // Admin parameters

    BOOL                 _fSortedByRankOpt;      // TRUE if can use CSortedByRankCursor
    BOOL                 _fRankVectorProp;       // TRUE if rank vector is in the output column
    ULONG                _cMaxResults;           // Limit on # query results
    ULONG                _cFirstRows;            // only sort and return the first _cFristRows rows

#   if CIDBG == 1
        XRestriction     _xrstOriginal;          // For debugging only.
#   endif

    CPidRemapper const & _pidmap;                // VPID/PID/PROPSPEC translations

    CTimeLimit           _TimeLimit;             // execution time limit (must precede _xXpr)
    XXpr                 _xXpr;                  // Expression (test against object)
    XRestriction         _xFullyResolvableRst;   // Content query
                                                 // _TimeLimit must be before _qRstIterator

    CQueryRstIterator    _qRstIterator;          // Iterator over various components of OR query

    XColumnSet           _xcols;                 // Used for multi-part queries.
    XSortSet             _xsort;                 // Used for multi-part queries.

    //
    // Singleton support.
    //

    BOOL                 _fNeedSingleton;        // TRUE if QuerySingletonCursor may be called.
    XXpr                 _xxprSingleton;         // Copy of current expression for singleton.

    //
    // Content queries
    //

    BOOL                 _fUseCI;                // If TRUE, always use CI value index for property queries.
    BOOL                 _fDeferTrimming;        // If TRUE, sorted rank cursor will trim results *after* full fetch from CI
    BOOL                 _fCIRequiredGlobal;     // Does the any component of query require a CI ?
    BOOL                 _fCIRequired;           // Does the current component of OR query require a CI ?

    ACCESS_MASK          _am;

    CMutexSem            _mtxSem;                // To serialize when creating deferred prop retriever
    BOOL                 _fNullCatalog;          // Do we have a null catalog?
};

inline BOOL CQueryOptimizer::IsMultiCursor() const
{
    return _fMulti;
}

inline BOOL CQueryOptimizer::IsFullySorted() const
{
    return _fFullySorted || _fSortedByRankOpt;
}

inline BOOL CQueryOptimizer::IsPositionable() const
{
    return FALSE;
}

inline void CQueryOptimizer::EnableSingletonCursors()
{
    _fNeedSingleton = TRUE;
}

inline BOOL CQueryOptimizer::RequiresCI() const
{
    return _fCIRequiredGlobal || _fUseCI;
}

inline void CQueryOptimizer::GetCurrentComponentIndex( unsigned & iCurrent,
                                                       unsigned & cTotal ) const
{
    _qRstIterator.GetCurComponentIndex( iCurrent, cTotal );

    //
    // If we're not done, then there is a component waiting inside CQueryOptimizer.
    //

    if ( !_fAtEnd )
    {
        Win4Assert( iCurrent >= 2 );
        iCurrent--;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CQueryOptimizer::_AccessMask, private
//
//  Returns:    Access mask appropriate given properties in query
//
//  History:    23-Oct-95   dlee  Created
//
//--------------------------------------------------------------------------

inline ACCESS_MASK CQueryOptimizer::_AccessMask() const
{
    return _am;
}

inline CTimeLimit & CQueryOptimizer::GetTimeLimit()
{
    return _TimeLimit;
}

DECLARE_SMARTP( QueryOptimizer )