//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1994 - 2000.
//
//  File:       proprst.cxx
//
//  Contents:   This object handles rowset/command properties
//
//  Classes:    CMRowsetProps   : public CUtlProps
//
//  History:    10-28-97    danleg      Created
//
//----------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#define DBINITCONSTANTS
#include <propglob.hxx>
#undef DBINITCONSTANTS

#include <ocidl.h>
#include <ntverp.h>
#include <proprst.hxx>
#include <rstprop.hxx>
#include "propinfr.h"
#include "proptbl.hxx"

//
// Constants
//
static const LCID   InvalidLCID             = 0xFFFFFFFF;

//
//  This array describes all the known rowset properties
//
const SPropDescription CMRowsetProps::aPropDescriptions[] = {
    { DBPROP_ABORTPRESERVE,             FALSE, eNotSupported,   -1 },
    { DBPROP_APPENDONLY,                FALSE, eNotSupported,   -1 },
    { DBPROP_BLOCKINGSTORAGEOBJECTS,    FALSE, eDefaultFalse,   -1 },
    { DBPROP_BOOKMARKS,                 TRUE,  eLocatable,      eid_PROPVAL_BOOKMARKS },
    { DBPROP_BOOKMARKSKIPPED,           FALSE, eDefaultFalse,   -1 },
    { DBPROP_BOOKMARKTYPE,              FALSE, eNumeric,        -1 },
    { DBPROP_CACHEDEFERRED,             FALSE, eColumnProp,     -1 },
    { DBPROP_CANFETCHBACKWARDS,         FALSE, eLocatable,      -1 },
    { DBPROP_CANHOLDROWS,               TRUE,  eHoldRows,       eid_PROPVAL_CANHOLDROWS },
    { DBPROP_CANSCROLLBACKWARDS,        FALSE, eLocatable,      -1 },
    { DBPROP_CHANGEINSERTEDROWS,        FALSE, eNotSupported,   -1 },
    { DBPROP_COLUMNRESTRICT,            FALSE, eDefaultFalse,   -1 },
//    { DBPROP_CHAPTERED,               FALSE, eChaptered,        -1 },
    { DBPROP_COMMANDTIMEOUT,            TRUE,  eNumeric,        eid_PROPVAL_COMMANDTIMEOUT },
    { DBPROP_COMMITPRESERVE,            FALSE, eNotSupported,   -1 },
    { DBPROP_DEFERRED,                  FALSE, eColumnProp,     -1 },
    { DBPROP_DELAYSTORAGEOBJECTS,       FALSE, eNotSupported,   -1 },
    { DBPROP_IMMOBILEROWS,              FALSE, eNotSupported,   -1 },
    { DBPROP_LITERALBOOKMARKS,          FALSE, eDefaultFalse,   -1 },
    { DBPROP_LITERALIDENTITY,           FALSE, eLocatable,      -1 },
    { DBPROP_MAXOPENROWS,               FALSE, eNumeric,        -1 },
    { DBPROP_MAXPENDINGROWS,            FALSE, eNotSupported,   -1 },
    { DBPROP_MAXROWS,                   TRUE,  eNumeric,        eid_PROPVAL_MAXROWS },
    { DBPROP_FIRSTROWS,                 TRUE,  eFirstRows,      eid_PROPVAL_FIRSTROWS },
    { DBPROP_MAYWRITECOLUMN,            FALSE, eColumnProp,     -1 },
    { DBPROP_MEMORYUSAGE,               TRUE,  eNumeric,        eid_PROPVAL_MEMORYUSAGE },
//    { DBPROP_MULTICHAPTERED,            FALSE, eChaptered,      -1 },
//    { DBPROP_MULTIPLEACCESSORS,         FALSE, eDefaultTrue,    -1 },
//    { DBPROP_MULTIPLERESULTSETS,        FALSE, eNotSupported,   -1 },
//    { DBPROP_NOTIFICATIONGRANULARITY,   FALSE, eNumeric,        -1 },
    { DBPROP_NOTIFICATIONPHASES,        FALSE, eNumeric,        -1 },
    { DBPROP_NOTIFYROWSETRELEASE,       FALSE, eNumeric,        -1 },
    { DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE,FALSE, eNumeric,   -1 },
//   9 additional notification properties, all unsupported

    { DBPROP_ORDEREDBOOKMARKS,          FALSE, eLocatable,      -1 }, 
    { DBPROP_OTHERINSERT,               FALSE, eAsynchronous,   -1 },
    { DBPROP_OTHERUPDATEDELETE,         FALSE, eAsynchronous,   -1 },
    { DBPROP_OWNINSERT,                 FALSE, eNotSupported,   -1 },
    { DBPROP_OWNUPDATEDELETE,           FALSE, eNotSupported,   -1 },
    { DBPROP_QUICKRESTART,              FALSE, eLocatable,      -1 },
    { DBPROP_REENTRANTEVENTS,           FALSE, eDefaultFalse,   -1 },
    { DBPROP_REMOVEDELETED,             FALSE, eDefaultTrue,    -1 },
    { DBPROP_REPORTMULTIPLECHANGES,     FALSE, eNotSupported,   -1 },
    { DBPROP_RETURNPENDINGINSERTS,      FALSE, eNotSupported,   -1 },
    { DBPROP_ROWRESTRICT,               FALSE, eDefaultTrue,    -1 },
    { DBPROP_ROWTHREADMODEL,            FALSE, eNumeric,        -1 },
    { DBPROP_SERVERCURSOR,              FALSE, eDefaultTrue,    -1 },
    { DBPROP_STRONGIDENTITY,            FALSE, eLocatable,      -1 },
    { DBPROP_TRANSACTEDOBJECT,          FALSE, eColumnProp,     -1 },
    { DBPROP_UPDATABILITY,              FALSE, eNumeric,        -1 },
    { DBPROP_ROWSET_ASYNCH,             TRUE,  eNumeric,        eid_PROPVAL_ROWSET_ASYNCH },

    //
    //  Supported interfaces
    //
    { DBPROP_IAccessor,                 FALSE, eDefaultTrue,    -1 },
    { DBPROP_IColumnsInfo,              FALSE, eDefaultTrue,    -1 },
    { DBPROP_IColumnsRowset,            FALSE, eNotSupported,   -1 },
    { DBPROP_IConnectionPointContainer, FALSE, eDefaultTrue,    -1 },
    { DBPROP_IConvertType,              FALSE, eDefaultTrue,    -1 },
    { DBPROP_IRowset,                   FALSE, eDefaultTrue,    -1 },
    { DBPROP_IRowsetIdentity,           TRUE,  eLocatable,      eid_PROPVAL_IRowsetIdentity },
    { DBPROP_IRowsetInfo,               FALSE, eDefaultTrue,    -1 },
    { DBPROP_IRowsetLocate,             TRUE,  eLocatable,      eid_PROPVAL_IRowsetLocate },
    { DBPROP_IRowsetScroll,             TRUE,  eScrollable,     eid_PROPVAL_IRowsetScroll },
    { DBPROP_IRowsetExactScroll,        TRUE,  eScrollable,     eid_PROPVAL_IRowsetExactScroll },
    { DBPROP_IDBAsynchStatus,           TRUE,  eAsynchronous,   eid_PROPVAL_IDBAsynchStatus },
    { DBPROP_IRowsetAsynch,             TRUE,  eAsynchronous,   eid_PROPVAL_IRowsetAsynch },  // deprecated
    { DBPROP_IRowsetWatchAll,           TRUE,  eWatchable,      eid_PROPVAL_IRowsetWatchAll },
    { DBPROP_IRowsetWatchRegion,        TRUE,  eWatchable,      eid_PROPVAL_IRowsetWatchRegion },
    { DBPROP_ISupportErrorInfo,         FALSE, eDefaultTrue,    -1 },
    { DBPROP_IChapteredRowset,          FALSE, eChaptered,      -1 },
};

const ULONG CMRowsetProps::cPropDescriptions =
                sizeof CMRowsetProps::aPropDescriptions /
                sizeof CMRowsetProps::aPropDescriptions[0];

//
//  This array gives the Index Server property extensions
//
const SPropDescription CMRowsetProps::aQueryExtPropDescriptions[] = {
    { DBPROP_USECONTENTINDEX,           TRUE, eUseCI,           eid_PROPVAL_USECONTENTINDEX },
    { DBPROP_DEFERNONINDEXEDTRIMMING,   TRUE, eDeferTrimming,   eid_PROPVAL_DEFERNONINDEXEDTRIMMING },
    { DBPROP_USEEXTENDEDDBTYPES,        TRUE, eExtendedTypes,   eid_PROPVAL_USEEXTENDEDDBTYPES },
};

const ULONG CMRowsetProps::cQueryExtPropDescriptions =
                sizeof CMRowsetProps::aQueryExtPropDescriptions /
                sizeof CMRowsetProps::aQueryExtPropDescriptions[0];


//
// This is used by CMRowsetProps.
//
static const UPROPSET s_rgRowsetPropSets[] =
{
&DBPROPSET_ROWSET,                  NUMELEM(s_rgdbPropRowset),          s_rgdbPropRowset,       0,
&DBPROPSET_MSIDXS_ROWSET_EXT,       NUMELEM(s_rgdbPropMSIDXSExt),       s_rgdbPropMSIDXSExt,    0,
&DBPROPSET_QUERY_EXT,               NUMELEM(s_rgdbPropQueryExt),        s_rgdbPropQueryExt,     0,
};

//
//  CMRowsetProps methods
//

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::CMRowsetProps, public
//
//  Synopsis:   Constructor
//
//  History:    11-12-97    danleg      Created from Monarch
//
//----------------------------------------------------------------------------

CMRowsetProps::CMRowsetProps
    (
    LCID            lcidInit
    ) 
    :   CUtlProps(ARGCHK_PROPERTIESINERROR),
        _dwBooleanOptions ( 0)
{
    if ( lcidInit )
        _lcidInit = lcidInit;
    else
        _lcidInit = GetSystemDefaultLCID();

    FInit();
}


//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::CMRowsetProps, public
//
//  Synopsis:   Copy Constructor
//
//  History:    11-12-97    danleg      Created from Monarch
//
//----------------------------------------------------------------------------

CMRowsetProps::CMRowsetProps
    (
    const CMRowsetProps & propSrc
    ) 
    : CUtlProps(ARGCHK_PROPERTIESINERROR) 
{
    FInit( (CUtlProps*) &propSrc );
    _lcidInit = propSrc._lcidInit;
    _dwBooleanOptions = propSrc._dwBooleanOptions;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::SetChaptered, public
//
//  Arguments:  [fSet]  - set or unset this property
//
//  Synopsis:   
//
//  History:    02-20-98    danleg      Created
//
//----------------------------------------------------------------------------

DWORD CMRowsetProps::SetChaptered( BOOL fSet )
{
    SetValBool( CMRowsetProps::eid_DBPROPSET_ROWSET,
                CMRowsetProps::eid_PROPVAL_IChapteredRowset,
                fSet ? VARIANT_TRUE : VARIANT_FALSE );

    if ( fSet )
        _dwBooleanOptions |= eChaptered;
    else
        _dwBooleanOptions &= ~eChaptered;

    return _dwBooleanOptions;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::SetFirstRows, public
//
//  Arguments:  [ulFirstRows] - value of first rows
//
//  Synopsis:   Sets the FirstRows property
//
//  History:    07-11-2000    KitmanH      Created
//
//----------------------------------------------------------------------------

void CMRowsetProps::SetFirstRows( ULONG ulFirstRows )
{
    SetValLong( CMRowsetProps::eid_DBPROPSET_ROWSET,
                CMRowsetProps::eid_PROPVAL_FIRSTROWS,
                ulFirstRows );

    _dwBooleanOptions |= eFirstRows;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::SetImpliedProperties, public
//
//  Arguments:  [riidRowset]  - iid requested
//              [cRowsets]    - indication on whethere this is a chaptered 
//                              rowset
//  Synopsis: This routine does not set any properties in the property table.
//
//  History:    02-20-98    danleg      Created
//
//----------------------------------------------------------------------------

DWORD CMRowsetProps::SetImpliedProperties
    (
    REFIID riidRowset,
    ULONG cRowsets 
    )
{
    SPropDescription const * pPropDesc =  FindInterfaceDescription( riidRowset );

    if ( pPropDesc == 0 )
        THROW( CException(E_NOINTERFACE) );

    if ( pPropDesc->fSettable )
    {
        Win4Assert( pPropDesc->dwIndicator >= eSequential &&
                    pPropDesc->dwIndicator <= eWatchable );
        _dwBooleanOptions |= pPropDesc->dwIndicator;

        // make sure that the eid_ in aPropDescriptions is in the range

        Win4Assert( pPropDesc->uIndex < eid_PROPVAL_ROWSET_NUM );

        //
        // Set this property in the CMRowsetProps cache so we can later return it.
        //
        SetValBool( eid_DBPROPSET_ROWSET, pPropDesc->uIndex, VARIANT_TRUE );
    }
    else if ( pPropDesc->dwIndicator != eDefaultTrue &&
              !(pPropDesc->dwIndicator & _dwBooleanOptions) )
    {
        THROW( CException(E_NOINTERFACE) );
    }
    else
    {
        Win4Assert( pPropDesc->dwProp == DBPROP_IRowset ||
                    riidRowset == IID_IRowsetIdentity ||
                    riidRowset == IID_IRowsetInfo ||
                    riidRowset == IID_IColumnsInfo ||
                    riidRowset == IID_IConvertType ||
                    riidRowset == IID_IAccessor ||
                    riidRowset == IID_IConnectionPointContainer );
        _dwBooleanOptions |= eSequential;
    }

    if ( cRowsets > 1 )
        _dwBooleanOptions |= eChaptered;

    //
    //  Interface indicators are arranged in a hierarchy from most general
    //  to most specialized.  Arrange that all more general interfaces are
    //  included in the properties.
    //
    for ( unsigned i = eWatchable; i > eSequential; 
          i = (i >> 1) )
    {
        if ( _dwBooleanOptions & i )
            _dwBooleanOptions |= (i >> 1);
    }

    Win4Assert( _dwBooleanOptions & eSequential );
 
    //
    // Scrollable
    // IRowsetScroll or IRowsetExactScroll were set.  IRowsetLocate is implied.
    //
    if ( _dwBooleanOptions & eScrollable )
    {
        // IRowsetScroll is user settable.  Make sure that it wasn't explicitly unset.
        if ( DBPROPOPTIONS_REQUIRED != GetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetScroll) ) 
        {
            SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetScroll, VARIANT_TRUE );
            SetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetScroll, DBPROPOPTIONS_REQUIRED );
        }

        // IRowsetLocate is settable.  Make sure it wasn't explicitly unset.
        if ( DBPROPOPTIONS_REQUIRED != GetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) )
        {
            DWORD dwOption = GetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetScroll);
            SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate, VARIANT_TRUE );
            SetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate, dwOption );
        }
    }

    //
    // Locatable 
    //
    if ( _dwBooleanOptions & eLocatable )
    {
        // these are not user settable
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_CANFETCHBACKWARDS, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_CANSCROLLBACKWARDS, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_LITERALIDENTITY, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_ORDEREDBOOKMARKS, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_QUICKRESTART, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_STRONGIDENTITY, VARIANT_TRUE );

        // eLocateable (IRowsetLocate or IRowsetIdentity are set).  IRowsetLocate implies BOOKMARKS
        if ( VARIANT_TRUE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) )
        {
            // BOOKMARKs is settable.  Make sure it hasn't already been set to FALSE/REQUIRED
            if ( DBPROPOPTIONS_REQUIRED != GetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) )
            {
                DWORD dwOption = GetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate );
                SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS, VARIANT_TRUE );
                SetPropOption( eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS, dwOption );
            }
        }
    }
    else
    {
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_CANFETCHBACKWARDS, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_CANSCROLLBACKWARDS, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_LITERALIDENTITY, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_ORDEREDBOOKMARKS, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_QUICKRESTART, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_STRONGIDENTITY, VARIANT_FALSE );
    }

    //
    // Asynchronous
    //
    if ( _dwBooleanOptions & eAsynchronous )
    {
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_OTHERINSERT, VARIANT_TRUE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_OTHERUPDATEDELETE, VARIANT_TRUE );
    }
    else
    {
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_OTHERINSERT, VARIANT_FALSE );
        SetValBool( eid_DBPROPSET_ROWSET, eid_PROPVAL_OTHERUPDATEDELETE, VARIANT_FALSE );
    }

    return _dwBooleanOptions;
}


//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::FindPropertyDescription, private
//
//  Synopsis:   Return description of a propery.
//
//  Arguments:  [rgPropDesc]  - array of property descriptions for propset
//              [cPropDesc]   - count of property descriptions in rgPropDesc
//              [dwPropId]    - ID of the property description desired
//
//  Returns:    pointer to SPropDescription or NULL
//
//  History:    19 Nov 95   AlanW       Created
//
//----------------------------------------------------------------------------

SPropDescription const *  CMRowsetProps::FindPropertyDescription(
    SPropDescription const * rgPropDesc,
    unsigned cPropDesc,
    DBPROPID dwPropId )
{
    SPropDescription const * pPropDesc = rgPropDesc;

    for (unsigned i=0; i < cPropDesc; i++, pPropDesc++)
    {
        if (pPropDesc->dwProp == dwPropId)
            return pPropDesc;
    }
    return 0;
}


const struct SIidLookup {
    const GUID * riid;
    DBPROPID dwPropId;
} aIidLookup[] =  {
    { &IID_IAccessor, DBPROP_IAccessor },
    { &IID_IColumnsInfo, DBPROP_IColumnsInfo },
    { &IID_IColumnsRowset, DBPROP_IColumnsRowset },
    { &IID_IConnectionPointContainer, DBPROP_IConnectionPointContainer },
    { &IID_IConvertType, DBPROP_IConvertType },
//    { &IID_ICopyColumn, DBPROP_ICopyColumn },
    { &IID_IRowset, DBPROP_IRowset },
    { &IID_IRowsetIdentity, DBPROP_IRowsetIdentity },
    { &IID_IRowsetInfo, DBPROP_IRowsetInfo },
    { &IID_IRowsetLocate, DBPROP_IRowsetLocate },
    { &IID_IRowsetScroll, DBPROP_IRowsetScroll },
    { &IID_IRowsetExactScroll, DBPROP_IRowsetExactScroll }, // deprecated
    { &IID_IDBAsynchStatus, DBPROP_IDBAsynchStatus },
    { &IID_IRowsetAsynch, DBPROP_IRowsetAsynch },        // deprecated
    { &IID_IRowsetWatchAll, DBPROP_IRowsetWatchAll },
    { &IID_IRowsetWatchRegion, DBPROP_IRowsetWatchRegion },
    { &IID_IUnknown, DBPROP_IRowset },
    { &IID_NULL, DBPROP_IRowset },
};

const unsigned cIidLookup = sizeof aIidLookup / sizeof aIidLookup[0];

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::FindInterfaceDescription, private
//
//  Synopsis:   Return property description corresponding to an interface
//
//  Arguments:  [riid]  - REFIID of interface to be looked up
//
//  Returns:    pointer to SPropDescription or NULL
//
//  History:    26 Sep 96   AlanW       Created
//
//----------------------------------------------------------------------------

SPropDescription const *  CMRowsetProps::FindInterfaceDescription(
    REFIID riid )
{
    for (unsigned i=0; i < cIidLookup; i++)
    {
        if (riid == *aIidLookup[i].riid)
            return FindPropertyDescription( aPropDescriptions,
                                            cPropDescriptions,
                                            aIidLookup[i].dwPropId );
    }
    return 0;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::UpdateBooleanOptions, private
//
//  Synopsis:   Update _dwBooleanOptions to reflect newly set properties.
//              Look for conflicting properties and mark them as such.
//
//  History:    03-02-98    danleg      Created
//
//----------------------------------------------------------------------------

void CMRowsetProps::UpdateBooleanOptions
    (
    const ULONG cPropertySets, 
    const DBPROPSET rgPropertySets[] 
    )
{
    SCODE sc = S_OK;

    DWORD dwSetOptions = 0;
    DWORD dwUnsetOptions = 0;
    DWORD dwOption = 0;

    // 
    // table for boolean options
    //
    for ( unsigned i=0; i<cPropertySets; i++ )
    {
        SPropDescription const * pPropDesc = 0;
        unsigned cPropDesc = 0;

        unsigned cProp = rgPropertySets[i].cProperties;
        DBPROP * pProp = rgPropertySets[i].rgProperties;

        if ( DBPROPSET_ROWSET == rgPropertySets[i].guidPropertySet )
        {
            pPropDesc = aPropDescriptions;
            cPropDesc = cPropDescriptions;
        }
        else if ( DBPROPSET_QUERY_EXT == rgPropertySets[i].guidPropertySet )
        {
            pPropDesc = aQueryExtPropDescriptions;
            cPropDesc = cQueryExtPropDescriptions;
        }
        else
        {
            // no properties here of interest to boolean options
            continue;
        }

        for ( unsigned j=0; j<cProp; j++, pProp++ )
        {
            if ( DBPROPSTATUS_OK == pProp->dwStatus )
            {
                //
                // determine the flag to set for this property
                //
                SPropDescription const * pFoundDesc =
                    FindPropertyDescription ( pPropDesc,
                                              cPropDesc,
                                              pProp->dwPropertyID );
                if ( 0 != pFoundDesc )
                {
                    // Setting read-only props to their default value succeeds
                    if ( !pFoundDesc->fSettable )
                        continue;

                    dwOption = pFoundDesc->dwIndicator;

                    //
                    // Settable properties with special option flags
                    //
                    switch ( pProp->dwPropertyID )
                    {
                        case DBPROP_COMMANDTIMEOUT:
                        case DBPROP_MAXROWS:
                        case DBPROP_MEMORYUSAGE:
                            continue;
                            
                        case DBPROP_FIRSTROWS:
                            dwOption = eFirstRows;
                            break;

                        case DBPROP_ROWSET_ASYNCH:
                            dwOption = eAsynchronous;
                            break;
                            
                        default:
                            // no other settable props that need special handling
                            break;
                    }

                    //
                    // determine if the property was set or unset
                    //
                    switch ( V_VT(&(pProp->vValue)) )
                    {
                        case VT_BOOL:
                            if ( VARIANT_FALSE == V_BOOL(&(pProp->vValue)) )
                                dwUnsetOptions |= dwOption;
                            else
                                dwSetOptions |= dwOption;
                            break;

                        case VT_I4:
                            if ( DBPROPSET_ROWSET == rgPropertySets[i].guidPropertySet  &&
                                 ( DBPROP_ROWSET_ASYNCH == pProp->dwPropertyID ||
                                   DBPROP_FIRSTROWS == pProp->dwPropertyID ) )
                            {
                                if ( 0 == V_I4(&(pProp->vValue)) )
                                    dwUnsetOptions |= dwOption;
                                else
                                    dwSetOptions |= dwOption;
                            }
                            break;

                        case VT_EMPTY:
                            // assume to mean unset
                            dwUnsetOptions |= dwOption;
                            break;

                        default:
                            break;
                    }
                }
                else
                {
                    // This should never get hit since the property was set successfully.
                    Win4Assert( ! "_rgdbPropRowset and aPropDescriptions are out of sync." );
                }
            }
        }
    }

    Win4Assert( (dwSetOptions & dwUnsetOptions) == 0 );
    if ( dwSetOptions & eScrollable )
    {
        dwUnsetOptions |= eSequential;
    }
    else if ( dwSetOptions & eSequential )
    {
        dwUnsetOptions |= eScrollable;
    }

    //
    // If IRowsetLocate and/or BOOKMARKS were explicitly set, and
    // IRowsetIdentity is being unset, don't take of the eLocatable bit
    //
    if ( dwUnsetOptions & eLocatable )
    {
        if ( VARIANT_TRUE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) ||
             VARIANT_TRUE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) )
            dwUnsetOptions &= ~eLocatable;
    }

    _dwBooleanOptions &= ~dwUnsetOptions;
    _dwBooleanOptions |= dwSetOptions;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::SetProperties, public
//
//  Synopsis:   
//
//  History:    02-15-98    danleg      Created
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::SetProperties
    ( 
    const ULONG cPropertySets, 
    const DBPROPSET rgPropertySets[] 
    )
{
    SCODE sc        = S_OK;
    ULONG cPropSets = cPropertySets;

    sc = CUtlProps::SetProperties( cPropertySets, rgPropertySets );

    if ( FAILED(sc) )
        return sc;

    //
    // Some or all of the properties were set successfully.  
    // Update _dwBooleanOptions.
    //
    UpdateBooleanOptions( cPropertySets, rgPropertySets );

    return sc;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::ArePropsInError, public
//
//  Synopsis:   Update _dwBooleanOptions to reflect newly set properties.
//              Look for conflicting properties and mark them as such.
//
//  History:    03-02-98    danleg      Created
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::ArePropsInError
    (
    CMRowsetProps & rProps
    )
{
    unsigned cErrors = 0;

    //
    // DBPROP_IRowsetScroll --> DBPROP_IRowsetLocate
    //
    if( VARIANT_TRUE == GetValBool(eid_DBPROPSET_ROWSET,eid_PROPVAL_IRowsetScroll) &&
        DBPROPOPTIONS_REQUIRED == GetPropOption(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetScroll) )
    {
        // IRowsetLocate should also be set to TRUE/REQUIRED
        if ( VARIANT_FALSE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) &&
             DBPROPOPTIONS_REQUIRED == GetPropOption(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) )
        {
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_IRowsetScroll );
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_IRowsetLocate );
            cErrors++;
        }

        // BOOKMARKS should also be set to TRUE/REQUIRED
        if ( VARIANT_FALSE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) &&
             DBPROPOPTIONS_REQUIRED == GetPropOption(eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) )
        {
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_BOOKMARKS );
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_IRowsetScroll );
            cErrors++;
        }
    }

    //
    // DBPROP_IRowsteLocate --> DBPROP_BOOKMARKS
    //
    if( VARIANT_TRUE == GetValBool(eid_DBPROPSET_ROWSET,eid_PROPVAL_IRowsetLocate) &&
        DBPROPOPTIONS_REQUIRED == GetPropOption(eid_DBPROPSET_ROWSET, eid_PROPVAL_IRowsetLocate) )
    {
        // BOOKMARKS should also be set to TRUE/REQUIRED
        if ( VARIANT_FALSE == GetValBool(eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) &&
             DBPROPOPTIONS_REQUIRED == GetPropOption(eid_DBPROPSET_ROWSET, eid_PROPVAL_BOOKMARKS) )
        {
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_BOOKMARKS );
            rProps.SetPropertyInError( eid_DBPROPSET_ROWSET, eid_PROP_IRowsetLocate );
            cErrors++;
        }
    }

    return ( cErrors ) ? DB_E_ERRORSOCCURRED 
                       : S_OK;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::InitAvailUPropsets, private
//
//  Synopsis:   Provide property set information to the base class
//
//  History:    11-12-97    danleg      Created from Monarch
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::InitAvailUPropSets
    (
    ULONG*          pcUPropSet, 
    UPROPSET**      ppUPropSet,
    ULONG*          pcElemPerSupported
    )
{
    Win4Assert( pcUPropSet && ppUPropSet);
    Win4Assert( NUMELEM(s_rgdbPropRowset) == eid_ROWSET_PROPS_NUM );
    Win4Assert( NUMELEM(s_rgdbPropQueryExt) == eid_QUERYEXT_PROPS_NUM );
    Win4Assert( NUMELEM(s_rgdbPropMSIDXSExt) == eid_MSIDXS_PROPS_NUM );

    *pcUPropSet = NUMELEM(s_rgRowsetPropSets);
    *ppUPropSet = (UPROPSET*)s_rgRowsetPropSets;
    *pcElemPerSupported = DWORDSNEEDEDPERSET; 

    return S_OK;
}


//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::InitUPropSetsSupported, private
//
//  Synopsis:   Build the required supported property bitmask for the property
//              set supported by this class
//
//  History:    11-12-97    danleg      Created from Monarch
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::InitUPropSetsSupported
    (
    DWORD* rgdwSupported
    )
{
    Win4Assert( rgdwSupported );

    // Initialize the bitmask to indicate all properties are supported
    RtlFillMemory( rgdwSupported, 
                   DWORDSNEEDEDPERSET * NUMELEM(s_rgRowsetPropSets) * sizeof(DWORD), 
                   0xFF ); 

    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::GetDefaultValue, private
//
//  Synopsis:   Retrieve the initial value for a propid.
//              DEVNOTE:  Using the index from 0 to (GetCountofAvailPropSets-1)
//                        and an index of 0 to (GetCountofAvailPropidsInPropset-1)
//                        within that propertyset, return the correct information.
//                        NOTE: pVar should be initialized prior to this routine.
//
//  History:    11-12-97    danleg      Created from Monarch
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::GetDefaultValue
    (
    ULONG       iCurSet,
    DBPROPID    dwPropId,
    DWORD*      pdwOption,
    VARIANT*    pvValue
    )
{
    Win4Assert( V_VT(pvValue) == VT_EMPTY );

    switch( iCurSet )
    {
        case eid_DBPROPSET_ROWSET:
            *pdwOption = DBPROPOPTIONS_REQUIRED;
            switch ( dwPropId )
            {
                // default TRUE values, non-writable
                case DBPROP_IAccessor:
                case DBPROP_IColumnsInfo:
                case DBPROP_IConnectionPointContainer:
                case DBPROP_IConvertType:
                case DBPROP_IRowset:
                case DBPROP_IRowsetInfo:
                case DBPROP_ISupportErrorInfo:
                case DBPROP_REMOVEDELETED:
                case DBPROP_ROWRESTRICT:
                case DBPROP_SERVERCURSOR:
                    V_VT( pvValue )     = VT_BOOL;
                    V_BOOL( pvValue )   = VARIANT_TRUE;
                    break;

                case DBPROP_IChapteredRowset:
                    V_VT( pvValue )     = VT_BOOL;
                    V_BOOL( pvValue )   = VARIANT_FALSE;
                    break;
                // default FALSE non-writable
                case DBPROP_BLOCKINGSTORAGEOBJECTS:
                case DBPROP_BOOKMARKSKIPPED:
                case DBPROP_CANFETCHBACKWARDS:
                case DBPROP_CANSCROLLBACKWARDS:
                case DBPROP_COLUMNRESTRICT:
                case DBPROP_LITERALBOOKMARKS:
                case DBPROP_LITERALIDENTITY:
                case DBPROP_ORDEREDBOOKMARKS:
                case DBPROP_OTHERINSERT:
                case DBPROP_OTHERUPDATEDELETE:
                case DBPROP_REENTRANTEVENTS:
                case DBPROP_STRONGIDENTITY:
                case DBPROP_QUICKRESTART:
                    V_VT( pvValue )     = VT_BOOL;
                    V_BOOL( pvValue )   = VARIANT_FALSE;
                    break;

                // default FALSE, writable
                case DBPROP_BOOKMARKS:
                case DBPROP_CANHOLDROWS:
                case DBPROP_IDBAsynchStatus:
                case DBPROP_IRowsetAsynch:
                case DBPROP_IRowsetExactScroll:
                case DBPROP_IRowsetIdentity:
                case DBPROP_IRowsetScroll:
                case DBPROP_IRowsetWatchAll:
                case DBPROP_IRowsetWatchRegion:
                case DBPROP_IRowsetLocate:
                    *pdwOption          = DBPROPOPTIONS_OPTIONAL;
                    V_VT( pvValue )     = VT_BOOL;
                    V_BOOL( pvValue )   = VARIANT_FALSE;
                    break;

                case DBPROP_BOOKMARKTYPE:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = DBPROPVAL_BMK_NUMERIC;
                    break;

                case DBPROP_COMMANDTIMEOUT:
                case DBPROP_MAXOPENROWS:
                case DBPROP_MAXROWS:
                case DBPROP_FIRSTROWS:
                case DBPROP_MEMORYUSAGE:
                case DBPROP_ROWSET_ASYNCH:
                case DBPROP_UPDATABILITY:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = 0;        
                    break;

                case DBPROP_ROWTHREADMODEL:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = DBPROPVAL_RT_FREETHREAD;
                    break;

                case DBPROP_NOTIFICATIONPHASES:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = DBPROPVAL_NP_OKTODO |
                                          DBPROPVAL_NP_ABOUTTODO |
                                          DBPROPVAL_NP_FAILEDTODO |
                                          DBPROPVAL_NP_DIDEVENT;
                    break;

                case DBPROP_NOTIFYROWSETRELEASE:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = 0;
                    break;

                case DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE:
                    V_VT( pvValue )     = VT_I4;
                    V_I4( pvValue )     = DBPROPVAL_NP_OKTODO |
                                          DBPROPVAL_NP_ABOUTTODO;
                    break;

                case DBPROP_CACHEDEFERRED:
                case DBPROP_DEFERRED:
                default:
                    // one of the unsupported properties
                    VariantClear( pvValue );
                    break;

            }
            break;

        case eid_DBPROPSET_QUERY_EXT:
            *pdwOption          = DBPROPOPTIONS_REQUIRED;

            // 
            // All three properties under this property set are BOOL/FALSE by default
            //
            V_VT( pvValue )     = VT_BOOL;
            V_BOOL( pvValue )   = VARIANT_FALSE;
            break;

        case eid_DBPROPSET_MSIDXS_ROWSET_EXT:
            *pdwOption = DBPROPOPTIONS_REQUIRED;
            switch( dwPropId )
            {
                case MSIDXSPROP_ROWSETQUERYSTATUS:
                    V_VT(pvValue)   = VT_I4;
                    V_I4(pvValue)   = 0;
                    break;
                case MSIDXSPROP_COMMAND_LOCALE_STRING:
                    *pdwOption = DBPROPOPTIONS_OPTIONAL;
                    V_VT(pvValue)   = VT_BSTR;
                    WCHAR awcLocale[100];

                    GetStringFromLCID(_lcidInit, awcLocale );
                    V_BSTR(pvValue) = SysAllocString( awcLocale );

                    if ( 0 == V_BSTR(pvValue) )
                        return E_OUTOFMEMORY;
                    break;
                case MSIDXSPROP_QUERY_RESTRICTION:
                    V_VT(pvValue)   = VT_BSTR;
                    V_BSTR(pvValue) = SysAllocString(L"");
                    if ( 0 == V_BSTR(pvValue) )
                        return E_OUTOFMEMORY;
                    break;
                default:
                    //Indicate that value is unknown
                    VariantClear(pvValue);
                    break;
            }
            break;

        default:
            // Invalid Property Set
            Win4Assert( ! "Invalid property set in GetDefaultValue.");
            return E_FAIL;
    }

    return S_OK;
}


//+---------------------------------------------------------------------------
//
//  Method:     CMRowsetProps::IsValidValue, private
//
//  Synopsis:   Validate that the variant contains legal values for its 
//              particular type and for the particular PROPID in this propset.
//              Devnote: This routine has to apply to writable properties only.
//
//  History:    11-12-97    danleg      Created from Monarch
//              02-01-98    danleg      Added support for DBPROPSET_ROWSET
//                                      and DBPROPSET_QUERYEXT
//
//----------------------------------------------------------------------------

SCODE CMRowsetProps::IsValidValue
    (
    ULONG       iCurSet,
    DBPROP*     pDBProp
    )
{
    switch ( V_VT(&(pDBProp->vValue)) )
    {
        case VT_BOOL:
            if ( !( VARIANT_TRUE  == V_BOOL(&(pDBProp->vValue)) ||
                    VARIANT_FALSE == V_BOOL(&(pDBProp->vValue)) ) )
                return S_FALSE;
            break;

        case VT_I4:
            switch ( iCurSet )
            {
                case eid_DBPROPSET_ROWSET:
                    switch ( pDBProp->dwPropertyID )
                    {
                        case DBPROP_BOOKMARKTYPE:
                            if ( !(DBPROPVAL_BMK_NUMERIC == V_I4(&(pDBProp->vValue)) ||
                                   DBPROPVAL_BMK_KEY     == V_I4(&(pDBProp->vValue)) ) )
                                return S_FALSE;
                            break;

                        case DBPROP_ROWSET_ASYNCH:
                            switch ( V_I4(&(pDBProp->vValue)) )
                            {
                                case ( DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION |
                                       DBPROPVAL_ASYNCH_RANDOMPOPULATION ):
                                case DBPROPVAL_ASYNCH_RANDOMPOPULATION:
                                case 0:  // means sequential init and population
                                    break;

                                case DBPROPVAL_ASYNCH_INITIALIZE:
                                case DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION:
                                default:
                                    return S_FALSE;

                            }
                            break;

                        case DBPROP_ROWTHREADMODEL:
                            // this is a r/o property, set to FREETHREAD by default.
                            switch ( V_I4(&(pDBProp->vValue)) )
                            {
                                case DBPROPVAL_RT_FREETHREAD:
                                case DBPROPVAL_RT_APTMTTHREAD:
                                case DBPROPVAL_RT_SINGLETHREAD:
                                    break;

                                default:
                                    return S_FALSE;
                            }
                            break;

                        case DBPROP_MEMORYUSAGE:
                            if ( 0  > V_I4(&(pDBProp->vValue)) ||
                                 99 < V_I4(&(pDBProp->vValue)) )
                                 return S_FALSE;
                            break;

                        case DBPROP_COMMANDTIMEOUT:
                        case DBPROP_MAXROWS:
                        case DBPROP_FIRSTROWS:
                            if ( 0 > V_I4(&(pDBProp->vValue)) )
                                return S_FALSE;
                            break;

                        case DBPROP_UPDATABILITY:
                            // this is the only other supported VT_I4 property
                            break;

                        default:
                            //  no other VT_I4 properties in DBPROPSET_ROWSET
                            return S_FALSE;
                    }
                    break;

                case eid_DBPROPSET_MSIDXS_ROWSET_EXT:
                    if ( MSIDXSPROP_ROWSETQUERYSTATUS != pDBProp->dwPropertyID )
                        return S_FALSE;
                    break;

                default:
                    return S_FALSE;
            }
            break; // VT_I4

        case VT_BSTR:
            switch ( iCurSet )
            {
                case eid_DBPROPSET_MSIDXS_ROWSET_EXT:
                    switch ( pDBProp->dwPropertyID )
                    {
                        case MSIDXSPROP_COMMAND_LOCALE_STRING:
                            if ( 0 == V_BSTR(&(pDBProp->vValue)) ||
                                 InvalidLCID == GetLCIDFromString(V_BSTR(&(pDBProp->vValue))) )
                                return S_FALSE;
                            break;

                        case MSIDXSPROP_QUERY_RESTRICTION:
                            // any bstr is valid
                            break;

                        default:
                            // there are no other bstr properties in this property set
                            return S_FALSE;
                    }
                    break;

                default:
                    // the other two prop sets don't have any VT_BSTR properties
                    return S_FALSE;
            }
            break;

        case VT_EMPTY:
            // always valid
            break;

        default:
            // no other types are supported
            return S_FALSE;
    } 

    return S_OK;    // Is valid
}