//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995-2000.
//
// File:        DisQuery.cxx
//
// Contents:    PIInternalQuery for distributed implementation.
//
// Classes:     CDistributedQuery
//
// History:     05-Jun-95     KyleP       Created
//              14-JAN-97     KrishnaN    Undefined CI_INETSRV, related changes
//
// Notes:
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <coldesc.hxx>
#include <rstprop.hxx>
#include <pidmap.hxx>
#include <cmdprutl.hxx>
#include <proputl.hxx> 

#include "disquery.hxx"
#include "seqser.hxx"
#include "seqsort.hxx"
#include "scrlsort.hxx"

//+-------------------------------------------------------------------------
//
//  Function:   EvalDistributedQuery, public
//
//  Synopsis:   Simulates bind to IInternalQuery for a ci object store
//
//  Arguments:  [ppQuery]       -- Returns a PIInternalQuery
//              [cScope]        -- Count of [awcsScope]
//              [pdwDepths]     -- Array of scope depths
//              [awcsScope]     -- Array of scopes to query
//              [awcsCat]       -- Array of overrides for catalog location
//              [fVirtualPaths] -- TRUE if [wcsScope] is a virtual scope
//                                 instead of a physical scope.
//
//  Returns:    SCODE result
//
//  History:    08-Feb-96    KyleP     Added header
//              08-Feb-96    KyleP     Add virtual path support
//              14-May-97    MohamedN  hidden core and fs properties 
//
//  Notes:      Scaffolding
//
//--------------------------------------------------------------------------

SCODE EvalDistributedQuery(
    PIInternalQuery **    ppQuery,
    CGetCmdProps    &     getCmdProps )

{
    *ppQuery                   = 0;
    CDistributedQuery * pQuery = 0;
    SCODE               sc     = S_OK;

    TRY
    {
        ULONG  cChildren = getCmdProps.GetCardinality();

        pQuery = new CDistributedQuery( cChildren );

        for ( unsigned i = 0; i < cChildren ; i++ )
        {

            PIInternalQuery   *        pChild = 0;
            CDbProperties              idbProps;

            getCmdProps.PopulateDbProps( &idbProps, i );

            SCODE s = EvalQuery(&pChild, idbProps );

            if ( FAILED( s ) )
            {
                vqDebugOut(( DEB_ERROR,
                             "Error 0x%x getting PIInternalQuery for %ws\n",
                             s, getCmdProps.GetCatalog(i) ));

                THROW( CException( s ) );
            }

            pQuery->Add( pChild, i );
        }

    }
    CATCH( CException, e )
    {
        if ( 0 != pQuery )
        {
            pQuery->Release();
            pQuery = 0;
        }
        sc = GetOleError( e );
    }
    END_CATCH

    *ppQuery = pQuery;
    return sc;
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::CDistributedQuery, public
//
//  Synopsis:   Constructor.
//
//  Arguments:  [cChild] -- Number of child scopes.
//
//  History:    07-Jun-95 KyleP     Created
//
//--------------------------------------------------------------------------

CDistributedQuery::CDistributedQuery( unsigned cChild )
        : _aChild( cChild ),
          PIInternalQuery( 1 )
{
    RtlZeroMemory( _aChild.GetPointer(), cChild * sizeof(PIInternalQuery *) );
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::~CDistributedQuery, public
//
//  Synopsis:   Destructor.
//
//  History:    07-Jun-95 KyleP     Created
//
//--------------------------------------------------------------------------

CDistributedQuery::~CDistributedQuery()
{
    for ( unsigned i = 0; i < _aChild.Count(); i++ )
    {
        if ( _aChild[i] )
        {
            _aChild[i]->Release();
        }
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::QueryInterface, public
//
//  Arguments:  [ifid]  -- Interface id
//              [ppiuk] -- Interface return pointer
//
//  Returns:    Error.  No rebind from this class is supported.
//
//  History:    01-Oct-92 KyleP     Created
//
//--------------------------------------------------------------------------

STDMETHODIMP CDistributedQuery::QueryInterface( REFIID ifid, void ** ppiuk )
{
    if ( ifid == IID_IUnknown )
    {
        AddRef();
        *ppiuk = (void *)((IUnknown *)this);
        return S_OK;
    }
    else
    {
        *ppiuk = 0;
        return E_NOINTERFACE;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::AddRef, public
//
//  Synopsis:   Reference the virtual table.
//
//  History:    01-Oct-92 KyleP     Created
//
//--------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CDistributedQuery::AddRef(void)
{
    return InterlockedIncrement( (long *)&_ref );
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::Release, public
//
//  Synopsis:   De-Reference the virtual table.
//
//  Effects:    If the ref count goes to 0 then the table is deleted.
//
//  History:    01-Oct-92 KyleP     Created
//
//--------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CDistributedQuery::Release(void)
{
    long l = InterlockedDecrement( (long *)&_ref );

    if ( l <= 0 )
    {
        delete this;
        return 0;
    }

    return l;
}

//+-------------------------------------------------------------------------
//
//  Member:     CDistributedQuery::Execute, public
//
//  Synopsis:   Executes a query.  Helper method for ICommand::Execute.
//
//  Arguments:  [pUnkOuter]    -- Outer unknown
//              [pRestriction] -- Query restriction
//              [pidmap]       -- pid mapper for output, sort, category columns
//              [rColumns]     -- Output columns in IRowset
//              [rSort]        -- Initial sort
//              [Props]        -- Rowset properties
//              [rCateg]       -- Categorization specification
//              [cRowsets]     -- # of rowsets
//              [ppUnknowns]   -- IUnknown pointers returned here
//              [aAccessors]   -- Bag of accessors which rowsets need to inherit
//
//  Returns:    Error code
//
//  History:    07-Jun-95      KyleP    Created
//
//  Notes:
//
//--------------------------------------------------------------------------

void CDistributedQuery::Execute( IUnknown *             pUnkOuter,
                                 RESTRICTION *          pRestriction,
                                 CPidMapperWithNames &  pidmap,
                                 CColumnSet &           rColumns,
                                 CSortSet &             rSort,
                                 XPtr<CMRowsetProps>  & xProps,
                                 CCategorizationSet &   rCateg,
                                 ULONG                  cRowsets,
                                 IUnknown **            ppUnknowns,
                                 CAccessorBag &         aAccessors,
                                 IUnknown *             pCreatorUnk )
{
    SCODE sc = S_OK;

    unsigned iChild = 0;
    IRowset ** aRowset = 0;

    TRY
    {
        if ( (xProps->GetPropertyFlags() & eLocatable) != 0 )
        {
            //
            // If no sort was specified for scrollable query, then add one
            // based on workid.
            //

            if ( 0 == rSort.Count() )
            {
                // This whole process could be streamlined if it was
                // possible to query bindings *before* ExecQuery was
                // called.  Then we could potentially find an existing
                // column suitable for sorting.

                //
                // Setup sort.
                //

                GUID guidQuery = DBQUERYGUID;
                CFullPropSpec propWorkId( guidQuery, DISPID_QUERY_WORKID );
                SSortKey SortKey( pidmap.NameToPid(propWorkId), QUERY_SORTASCEND );

                rSort.Add( SortKey, 0 );
            }
        }

        //
        // Since we need to bind to sort columns in order to compare rows, all
        // sort columns must be in the binding set.
        // At the same time, create a CSort from the CSortSet for use by the
        // rowset implementation class.
        //

        unsigned cOriginalColumns = rColumns.Count();
        CSort SortDup;

        if ( 0 != rSort.Count() )
        {
            for ( unsigned i = 0; i < rSort.Count(); i++ )
            {
                PROPID prop = rSort.Get( i ).pidColumn;

                CSortKey sk( *pidmap.PidToName(prop),
                             rSort.Get(i).dwOrder,
                             rSort.Get(i).locale );
                SortDup.Add(sk, i);

                for ( unsigned j = 0; j < rColumns.Count(); j++ )
                {
                    if ( rColumns.Get(j) == prop )
                        break;
                }

                if ( j == rColumns.Count() )
                {
                    rColumns.Add( prop, j );
                }
            }
        }

        //
        // This array is passed to distributed rowset.
        //

        Win4Assert(1 == cRowsets );  // allocs below only account for 1 cRowset

        XArray<IUnknown *> xUnknowns( _aChild.Count() );
        aRowset = new IRowset * [_aChild.Count()];
        
        //
        // Create the children.
        //

        CAccessorBag aEmpty(this);

        for ( iChild = 0; iChild < _aChild.Count(); iChild++ )
        {
            //
            // If we're going non-sequential, go all the way.
            //

            XPtr<CMRowsetProps> xPropsTemp( new CMRowsetProps( xProps.GetReference() ) );
            if ( (xPropsTemp->GetPropertyFlags() & eLocatable) != 0 )
                xPropsTemp->SetImpliedProperties( IID_IRowsetScroll, cRowsets );

            // For distributed rowsets, always set holdrows to true
            xPropsTemp->SetPropertyFlags( eHoldRows );

            _aChild[iChild]->Execute( 0,     // children are private and aren't aggregated
                                      pRestriction,
                                      pidmap,
                                      rColumns,
                                      rSort,
                                      xPropsTemp,
                                      rCateg,
                                      cRowsets,
                                      &xUnknowns[iChild],
                                      aEmpty,       // these guys don't really need aAccessors
                                      pCreatorUnk );  

            if ( FAILED(sc) )
            {
                vqDebugOut(( DEB_ERROR, "CDistributedQuery: Error 0x%x calling Execute for child %u\n",
                             sc, iChild ));
                THROW( CException(sc) );
            }

            // get IRowset pointer
            xUnknowns[iChild]->QueryInterface(IID_IRowset, (void **) &aRowset[iChild]);
            xUnknowns[iChild]->Release();  // release extra ref
        }

        //
        // First case: sequential cursors
        //

        IUnknown * pIUnkInner = 0;
        XInterface<IUnknown> xIUnkInner(pIUnkInner);
        IRowset * pTemp;  
    
        if ( 0 == (xProps->GetPropertyFlags() & eLocatable) )
        {
            if ( 0 != rSort.Count() )
            {
                pTemp = (IRowset *)
                        new CSequentialSorted(  pUnkOuter,
                                                (IUnknown **)xIUnkInner.GetQIPointer(),
                                                aRowset,
                                                _aChild.Count(),
                                                xProps.GetReference(),
                                                cOriginalColumns,
                                                SortDup,
                                                aAccessors );
            }
            else
            {
                pTemp = (IRowset *)
                        new CSequentialSerial(  pUnkOuter,
                                                (IUnknown **)xIUnkInner.GetQIPointer(),
                                                aRowset,
                                                _aChild.Count(),
                                                xProps.GetReference(),
                                                cOriginalColumns,
                                                aAccessors );
            }
        }

        //
        // Other case: Scrollable
        //

        else
        {
            Win4Assert( 0 != rSort.Count() );

            pTemp = (IRowset *)
                    new CScrollableSorted( pUnkOuter,
                                           (IUnknown **)xIUnkInner.GetQIPointer(),
                                           (IRowsetScroll **)aRowset,
                                           _aChild.Count(),
                                           xProps.GetReference(),
                                           cOriginalColumns,
                                           SortDup, 
                                           aAccessors );
        }
        *ppUnknowns = xIUnkInner.Acquire();
        Win4Assert(*ppUnknowns);

    }
    CATCH( CException, e )
    {
        sc = e.GetErrorCode();
   
        for ( ; iChild > 0; iChild-- )
            aRowset[iChild-1]->Release();
 
        delete [] aRowset;

        RETHROW( );
    }
    END_CATCH
}