//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1995 - 2000.
//
//  File:       qryspec.hxx
//
//  Contents:   ICommandTree implementation for OFS file stores
//
//  Classes:    CQuerySpec
//
//  History:    30 Jun 1995   AlanW   Created
//
//----------------------------------------------------------------------------

#pragma once

#include <rowset.hxx>
#include <rstprop.hxx>
#include <oldquery.hxx>
#include <dberror.hxx>
#include <impiunk.hxx>

#include <srvprvdr.h>           // IServiceProperties

#include <proputl.hxx>
#include <proprst.hxx>
#include <session.hxx>

class CColumnsInfo;
class CColumnSet;

// 
// CRootQueryStatus::_dwStatus flags
//
enum COMMAND_STATUS_FLAG {
    CMD_TEXT_SET            = 0x00000001,   // Command text was set

    CMD_TEXT_PREPARED       = 0x00000002,   // Command is prepared
    CMD_TEXT_TOTREE         = 0x00000004,   // Tells SetCommandTree not to delete the command text
                                            
    CMD_TREE_BUILT          = 0x00000008,   
    CMD_OWNS_TREE           = 0x00000010,   // fCopy was FALSE during SetCommandTree 

    CMD_COLINFO_NOTPREPARED = 0x00000020,   // ColumnsInfo should return DB_E_NOTPREPARED

    CMD_EXEC_RUNNING        = 0x10000000,   // Command is executing
};

//+---------------------------------------------------------------------------
//
//  Class:      CRootQuerySpec
//
//  Purpose:    Base query spec, implements OLE-DB command object
//
//  History:    30 Jun 1995     AlanW   Created
//              10-31-97        danleg  ICommandText & ICommandPrepare added
//----------------------------------------------------------------------------

typedef BOOL (*PFNCHECKTREENODE) (const CDbCmdTreeNode * pNode );

class CRootQuerySpec :  public ICommandText, public ICommandPrepare,
                   /* public ICommandTree, */ public ICommandProperties,
                   /* public ICommandValidate, */  public IQuery,
                   public IAccessor, public IConvertType,
                   public IServiceProperties
{
public:

    //
    // IUnknown methods.
    //

    STDMETHOD(QueryInterface) ( THIS_ REFIID riid,
                                LPVOID *ppiuk )
                           { 
                           return _pControllingUnknown->QueryInterface(riid,ppiuk);
                           }

    STDMETHOD_(ULONG, AddRef) (THIS) { return _pControllingUnknown->AddRef(); }

    STDMETHOD_(ULONG, Release) (THIS) {return _pControllingUnknown->Release(); }

    //
    //  ICommand methods
    //

    STDMETHOD(Cancel)               ( );

    STDMETHOD(Execute)              ( IUnknown *             pUnkOuter,
                                      REFIID                 riid,
                                      DBPARAMS *             pParams,
                                      DBROWCOUNT *           pcRowsAffected,
                                      IUnknown * *           ppRowset);

    STDMETHOD(GetDBSession)         ( REFIID                 riid,
                                      IUnknown **            ppSession );

    //
    // ICommandText methods
    //
    STDMETHOD(GetCommandText)       ( GUID *                 pguidDialect,
                                      LPOLESTR *             ppwszCommand );

    STDMETHOD(SetCommandText)       ( REFGUID                rguidDialect,
                                      LPCOLESTR              pwszCommand );

    //
    // ICommandPrepare methods
    //
    STDMETHOD(Prepare)              ( ULONG                 cExpectedRuns );
    
    STDMETHOD(Unprepare)            (  );

    //
    //  ICommandTree methods
    //
    STDMETHOD(FindErrorNodes)       ( const DBCOMMANDTREE*   pRoot,
                                      ULONG *                pcErrorNodes,
                                      DBCOMMANDTREE ***      prgErrorNodes);

    STDMETHOD(FreeCommandTree)      ( DBCOMMANDTREE **       ppRoot );

    STDMETHOD(GetCommandTree)       ( DBCOMMANDTREE **       ppRoot );

    STDMETHOD(SetCommandTree)       ( DBCOMMANDTREE * *      ppRoot,
                                      DBCOMMANDREUSE         dwCommandReuse,
                                      BOOL                   fCopy);

#if 0 
    // ICommandValidate not yet implemented
    //
    //  ICommandValidate methods
    //
    STDMETHOD(ValidateCompletely)  ( );

    STDMETHOD(ValidateSyntax)  ( );
#endif // 0     // not implemented now.

    //
    //  IQuery methods
    //
    STDMETHOD(AddPostProcessing)  ( DBCOMMANDTREE * *      ppRoot,
                                    BOOL                   fCopy);

    STDMETHOD(GetCardinalityEstimate) (
                                    DBORDINAL *               pulCardinality);

    //
    //  ICommandProperties methods
    //
    STDMETHOD(GetProperties)  ( const ULONG            cPropertySetIDs,
                                const DBPROPIDSET      rgPropertySetIDs[],
                                ULONG *                pcPropertySets,
                                DBPROPSET **           prgPropertySets);

    STDMETHOD(SetProperties)  ( ULONG                  cPropertySets,
                                DBPROPSET              rgPropertySets[]);

    //
    // IAccessor methods
    //

    STDMETHOD(AddRefAccessor)       (HACCESSOR         hAccessor,
                                     ULONG *           pcRefCount);

    STDMETHOD(CreateAccessor)       (DBACCESSORFLAGS   dwBindIO,
                                     DBCOUNTITEM       cBindings,
                                     const DBBINDING   rgBindings[],
                                     DBLENGTH          cbRowSize,
                                     HACCESSOR *       phAccessor,
                                     DBBINDSTATUS      rgStatus[]);

    STDMETHOD(GetBindings)          (HACCESSOR         hAccessor,
                                     DBACCESSORFLAGS * pdwBindIO,
                                     DBCOUNTITEM *     pcBindings,
                                     DBBINDING * *     prgBindings) /*const*/;

    STDMETHOD(ReleaseAccessor)      (HACCESSOR         hAccessor,
                                     ULONG *           pcRefCount);

    //
    // IConvertType methods
    //

    STDMETHOD(CanConvert)           (DBTYPE wFromType,
                                     DBTYPE wToType,
                                     DBCONVERTFLAGS dwConvertFlags );



    //
    // IServiceProperties methods
    //

    STDMETHOD(GetPropertyInfo)      ( ULONG cPropertyIDSets,
                                      const DBPROPIDSET rgPropertyIDSets[  ],
                                      ULONG *pcPropertyInfoSets,
                                      DBPROPINFOSET **prgPropertyInfoSets,
                                      OLECHAR **ppDescBuffer );

    STDMETHOD(SetRequestedProperties) ( ULONG cPropertySets,
                                        DBPROPSET rgPropertySets[  ] );

    STDMETHOD(SetSuppliedProperties) ( ULONG cPropertySets,
                                       DBPROPSET rgPropertySets[  ]);

    // 
    // Non-interface public methods
    //
    inline BOOL IsRowsetOpen() { return (HaveQuery() && _pInternalQuery->IsQueryActive()); }

    //
    // Build a Query Tree from SQL text
    //
    SCODE       BuildTree( );

    inline BOOL IsCommandSet()      { return (_dwStatus & CMD_TEXT_SET); }

    inline void ImpersonateOpenRowset() { _fGenByOpenRowset = TRUE; }

    inline BOOL IsGenByOpenRowset()     { return _fGenByOpenRowset; }

    inline static BOOL IsValidFromVariantType( DBTYPE wTypeIn )
    {
        DBTYPE wType = wTypeIn & VT_TYPEMASK;

        return (! ((wType > VT_DECIMAL && wType < VT_I1) ||
                   (wType > VT_LPWSTR && wType < VT_FILETIME && wType != VT_RECORD) ||
                   (wType > VT_CLSID)) );
    }

    inline static BOOL IsVariableLengthType( DBTYPE  wTypeIn )
    {
        DBTYPE wType = wTypeIn & VT_TYPEMASK;

        return wType == DBTYPE_STR       ||
               wType == DBTYPE_BYTES     ||
               wType == DBTYPE_WSTR      ||
               wType == DBTYPE_VARNUMERIC;
    }
    
protected:

    //
    // Ctor / Dtor
    //
             CRootQuerySpec (IUnknown * pUnkOuter, IUnknown ** ppMyUnk, CDBSession * pSession=0);
             CRootQuerySpec ( CRootQuerySpec & src );
    virtual ~CRootQuerySpec ();
    
    SCODE RealQueryInterface( REFIID ifid, void * *ppiuk );  // used by _pControllingUnknown 
             // in aggregation - does QI without delegating to outer unknown   

    //
    // Scope access.
    //

    void  SetDepth( DWORD dwDepth ) { _dwDepth = dwDepth; }
    DWORD Depth()                   { return _dwDepth; }

    virtual PIInternalQuery * QueryInternalQuery() = 0;

    void ReleaseInternalQuery()
    {
        if ( 0 != _pInternalQuery )
        {
            _pInternalQuery->Release();
            _pInternalQuery = 0;
        }
    }


    //
    // Syncronize access to the command object
    //
    CMutexSem           _mtxCmd;

    //
    // Support Ole DB error handling
    //
    CCIOleDBError       _DBErrorObj;

    //
    // Execution status flags
    //
    ULONG               _dwStatus;

    //
    // Current SQL text, if any
    //
    WCHAR*              _pwszSQLText;

    //
    // GUID for dialect of current text or tree
    //
    GUID                _guidCmdDialect;

    //
    // Session that created this command, if any
    //
    XInterface<CDBSession>     _xSession;
    XInterface<IParserSession> _xpIPSession;
   
    BOOL                _fGenByOpenRowset;

private:

    void         CreateParser();

    void        _FindTreeNodes( const CDbCmdTreeNode * pRoot,
                                ULONG &                rcMatchingNodes,
                                XArrayOLE<DBCOMMANDTREE *> & rpMatchingNodes,
                                PFNCHECKTREENODE       pfnCheck,
                                unsigned               iDepth = 0);

    void        _CheckRootNode( const DBCOMMANDTREE*   pRoot);

    BOOL        HaveQuery() { return ( 0 != _pInternalQuery ); }

    CColumnsInfo * GetColumnsInfo();

    void        InitColumns( );

    DWORD               _dwDepth;               // query depth
    PIInternalQuery *   _pInternalQuery;        // PIInternalQuery to create rowsets

    CDbCmdTreeNode *    _pQueryTree;            // the query tree

    //
    // For implementing ICommandProperties
    //
    CMRowsetProps       _RowsetProps;

    //
    // IServiceProperties::GetPropertyInfo
    //
    CMDSPropInfo        _PropInfo;
    //
    // For implementing IColumnsInfo
    //
    CColumnsInfo *      _pColumnsInfo;          // implements IColumnsInfo

    //
    // Keeps track of accessors handed out by this object.
    //
    CAccessorBag        _aAccessors;
    
    IUnknown *  _pControllingUnknown;   // outer unknown
    friend class CImpIUnknown<CRootQuerySpec>;    
    CImpIUnknown<CRootQuerySpec> _impIUnknown;

    XInterface<IParser> _xIParser;

    //
    // Default catalog.  
    //
    XArray<WCHAR>       _xpwszCatalog;
};