FdoIFeatureReader *FdoRdbmsSelectCommand::Execute( bool distinct, FdoInt16 callerId  )
{
    if (!mConnection || !mFdoConnection || mFdoConnection->GetConnectionState() != FdoConnectionState_Open)
        throw FdoCommandException::Create(NlsMsgGet(FDORDBMS_13, "Connection not established"));
    // Flush out any outstanding modifications before selecting; so that the select
    // sees a current picture of the RDBMS.
    mIConnection->Flush();
    int                 qid = -1;
    bool                res = false;
    bool delStatement = true;
    GdbiStatement* statement = NULL;

    const FdoSmLpClassDefinition *classDefinition = mConnection->GetSchemaUtil()->GetClass(this->GetClassNameRef()->GetText());

    bool isFeatureClass = ( classDefinition != NULL &&  classDefinition->GetClassType() == FdoClassType_FeatureClass );
    bool isForUpdate = HasLobProperty( classDefinition );

    bool doNotUseSimpleSelect = mHasObjectProps && (mIdentifiers == NULL || mIdentifiers->GetCount() == 0);

    try
    {
        // for now we support only select with joins
        if (callerId == (FdoInt16)FdoCommandType_Select && !doNotUseSimpleSelect && !mHasObjectProps)
        {
            FdoPtr<FdoRdbmsSqlBuilder> sqlBuilder = mFdoConnection->GetSqlBuilder();
            if (sqlBuilder)
            {
                std::vector<NameOrderingPair> ordering;
                FdoPtr<FdoParameterValueCollection> params = GetParameterValues();
                FdoPtr<FdoJoinCriteriaCollection> jcColl = GetJoinCriteria();
                sqlBuilder->SetParameterValues(params);
                if (mOrderingIdentifiers && mOrderingIdentifiers->GetCount())
                {
                    for (int i=0; i<mOrderingIdentifiers->GetCount(); i++)
                    {
                        FdoPtr<FdoIdentifier> id = mOrderingIdentifiers->GetItem(i);
                        ordering.push_back(NameOrderingPair(id.p, ((int)mOrderingOptions.size() != mOrderingIdentifiers->GetCount()) ? mOrderingOption : mOrderingOptions[id->GetName()])); 
                    }
                }

                FdoString* sqlString = sqlBuilder->ToSelectSqlString(GetClassNameRef(), mAliasName, GetFilterRef(), mIdentifiers, ordering, jcColl);
                if (sqlString != NULL && *sqlString != '\0')
                {
                    statement = mConnection->GetGdbiConnection()->Prepare( sqlString );

                    std::vector< std::pair< FdoLiteralValue*, FdoInt64 > >* paramsUsed = sqlBuilder->GetUsedParameterValues();

                    if (paramsUsed != NULL && paramsUsed->size())
                    {
                        if (mBindParamsHelper == NULL)
                            mBindParamsHelper = new FdoRdbmsPropBindHelper(mConn);

                        mBindParamsHelper->BindParameters(statement, paramsUsed);
                    }

                    GdbiQueryResult *queryRslt = statement->ExecuteQuery();

                    delete statement;

                    if (mBindParamsHelper != NULL)
                        mBindParamsHelper->Clear();

                    // statement will be deleted in the reader.
                    delStatement = false;

                    return FdoRdbmsSimpleFeatureReader::Create(mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers);
                }
            }
        }

        FdoPtr<FdoRdbmsFilterProcessor>flterProcessor = mFdoConnection->GetFilterProcessor();
        FdoPtr<FdoParameterValueCollection> params = GetParameterValues();
        flterProcessor->SetParameterValues(params);

        FdoRdbmsFilterUtilConstrainDef filterConstrain;
        filterConstrain.distinct = distinct;
        filterConstrain.orderingOption = mOrderingOption;
        filterConstrain.selectedProperties = mIdentifiers;
        filterConstrain.groupByProperties = mGroupingCol;
        filterConstrain.orderByProperties = mOrderingIdentifiers;

   		// Verify if this is a special case we can optimize (no filter, no grouping filter,
		// and only aggregate functions Count() and/or SpatialExtents())
        FdoRdbmsFeatureReader *reader = GetOptimizedFeatureReader( classDefinition );
        if ( reader )
            return reader;

        // FDO supports expression functions that may not have native support in the
        // underlying system. If this is the case then the request has to be handled
        // by the Expression Engine.

        bool isValidFilter = true,
             isValidSelectList = true;

        if ( this->GetFilterRef() != NULL )
            isValidFilter = flterProcessor->IsValidExpression( this->GetFilterRef() );
        isValidSelectList = flterProcessor->IsValidExpression( mIdentifiers );

        if ( ( !isValidFilter ) || ( !isValidSelectList ) )
        {
            // Either the selected property list of the the filter is invalid. In any case
            // create a SQL statement that selects all properties for the current class.
            // If the filter is valid it is used to narrow the selected amount of data.

            FdoString *sqlStatement =
                    flterProcessor->FilterToSql( ((isValidFilter)
                                                        ? this->GetFilterRef()
                                                        : NULL),
                                                 this->GetClassNameRef()->GetText() );

            GdbiQueryResult *queryRslt = mConnection->GetGdbiConnection()->ExecuteQuery( sqlStatement );
            FdoPtr<FdoIFeatureReader> featureReader = new FdoRdbmsFeatureReader( mFdoConnection,
                                                                                 queryRslt,
                                                                                 isFeatureClass,
                                                                                 classDefinition,
                                                                                 NULL,
                                                                                 NULL,
                                                                                 0,
                                                                                 NULL );

            // The Expression Engine cannot work with the class definition of type
            // "FdoSmLpClassDefinition". Instead it is necessary to get the corresponding
            // definition of type "FdoClassDefinition". This is done next.

            const FdoSmLpSchema* schema = mConnection->GetSchema( this->GetClassNameRef()->GetText() );
            FdoFeatureSchemasP fdoFeatureSchemas = mFdoConnection->GetSchemaManager()->GetFdoSchemas( schema->GetName() );
            FdoClassesP classCol = (FdoClassCollection *)fdoFeatureSchemas->FindClass( this->GetClassNameRef()->GetText() );
            FdoClassDefinitionP classDef = classCol->GetItem(0);

			// Create the collection of custom functions.
			FdoSmLpSchemasP	schemas = ((FdoSmLpSchema *) schema)->GetSchemas();

			FdoExpressionEngineFunctionCollection *userDefinedFunctions = GetUserDefinedFunctions( schemas->GetSpatialContextMgr()->GetSpatialContexts(), classDef );

			return FdoExpressionEngineUtilFeatureReader::Create(
                                                        classDef,
                                                        featureReader, 
                                                        this->GetFilterRef(),
                                                        mIdentifiers,
                                                        userDefinedFunctions);
		}

        // The selected properties and the filter are both valid. Do the normal processing.

		// Validate the filter
		if ( this->GetFilterRef() != NULL )
		{
			FdoPtr<FdoIFilterCapabilities> filterCaps = mFdoConnection->GetFilterCapabilities();
	
			FdoExpressionEngine::ValidateFilter( NULL, this->GetFilterRef(), NULL, filterCaps);
		}

        // Call FilterToSql just to populate the filter's list of geometric conditions;
        FdoString * sqlString = flterProcessor->FilterToSql( this->GetFilterRef(),
                                                             this->GetClassNameRef()->GetText(),
                                                             SqlCommandType_Select,
                                                             FdoCommandType_Select,
                                                             &filterConstrain,
                                                             isForUpdate,
                                                             callerId );

        FdoPtr<FdoRdbmsFilterProcessor::BoundGeometryCollection> boundGeometries = flterProcessor->GetBoundGeometryValues();
        FdoPtr<FdoRdbmsSecondarySpatialFilterCollection> geometricConditions = flterProcessor->GetGeometricConditions();
   		vector<int> * logicalOps = flterProcessor->GetFilterLogicalOps();

        FdoPtr<FdoIdentifierCollection> idsWithGeoms = FdoIdentifierCollection::Create();

        if (( mIdentifiers && mIdentifiers->GetCount() > 0) )
        {
            // Make sure that the properties are listed for any geometric conditions that require secondary filtering.
            if (geometricConditions != NULL)
            {
                for (FdoInt32 i=0;  i < geometricConditions->GetCount();  i++)
                {
                    FdoPtr<FdoRdbmsSpatialSecondaryFilter> ssf = geometricConditions->GetItem(i);
                    FdoString * ssfPropertyName = ssf->GetPropertyName();
                    FdoPtr<FdoIdentifier> ssfId = mIdentifiers->FindItem(ssfPropertyName);
                    if (ssfId == NULL)
                    {
                        // Property wasn't selected by the caller.
                        ssfId = FdoIdentifier::Create(ssfPropertyName);
                        idsWithGeoms->Add(ssfId);
                    }
                }
                // Convert to SQL again if we added geometric properties.
                if (idsWithGeoms->GetCount() > 0)
                {
                    // Make our own copy of the original list, to avoid a side effect of changing
                    // mIdentifiers, which the user can effectively see.  Perserve original order,
                    // effectively creating a new list that appends the geometry names.
                    for (FdoInt32 i = mIdentifiers->GetCount()-1;  i >= 0;  i--)
                    {
                        FdoPtr<FdoIdentifier> id = mIdentifiers->GetItem(i);
                        idsWithGeoms->Insert(0, id);
                    }
                    filterConstrain.selectedProperties = idsWithGeoms;
                    sqlString = flterProcessor->FilterToSql( this->GetFilterRef(),
                                                             this->GetClassNameRef()->GetText(),
                                                             SqlCommandType_Select,
                                                             FdoCommandType_Select,
                                                             &filterConstrain,
                                                             isForUpdate,
                                                             callerId );
                }
            }
            if (callerId == FdoCommandType_SelectAggregates)
            {
                FdoSchemaManagerP pschemaManager = mConnection->GetSchemaUtil()->GetSchemaManager();
                FdoPtr<FdoFeatureSchemaCollection> schemas = pschemaManager->GetFdoSchemas(L"");
                FdoPtr<FdoCommonExpressionExecutor> expVer = FdoCommonExpressionExecutor::Create(schemas, GetClassNameRef());
                FdoPtr<FdoIExpressionCapabilities> expCap = mFdoConnection->GetExpressionCapabilities();
                expVer->ValidateIdentifiers(mIdentifiers, expCap);
            }
        }

        statement = mConnection->GetGdbiConnection()->Prepare( sqlString );

        std::vector< std::pair< FdoLiteralValue*, FdoInt64 > >* paramsUsed = flterProcessor->GetUsedParameterValues();

        if (paramsUsed != NULL && paramsUsed->size())
        {
            if (mBindParamsHelper == NULL)
                mBindParamsHelper = new FdoRdbmsPropBindHelper(mConn);

            mBindParamsHelper->BindParameters(statement, paramsUsed);
        }

        GdbiQueryResult *queryRslt = statement->ExecuteQuery();

        delete statement;

        if (mBindParamsHelper != NULL)
            mBindParamsHelper->Clear();

        // statement will be deleted in the reader.
        delStatement = false;

        // For now only SQL Spatial Server supports SupportsSimpleReader, later (after we add some unit tests) we can extend it to other providers
        if (!flterProcessor->ContainsCustomObjects() && flterProcessor->SupportsSimpleReader() && geometricConditions == NULL && callerId == (FdoInt16)FdoCommandType_Select && !doNotUseSimpleSelect)
        {
            return FdoRdbmsSimpleFeatureReader::Create(mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers);
        }
        else
        {
            if (( mIdentifiers && mIdentifiers->GetCount() > 0))
                return new FdoRdbmsFeatureSubsetReader (mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, mIdentifiers, geometricConditions, logicalOps );
            else
                return new FdoRdbmsFeatureReader (mFdoConnection, queryRslt, isFeatureClass, classDefinition, NULL, NULL, 0, geometricConditions, logicalOps ); // The feature reader should free the queryRslt
        }
    }

    catch (FdoCommandException *ex)
    {
        if (delStatement)
            delete statement;
        ex;
        SELECT_CLEANUP;
        throw;
    }
    catch (FdoException *ex)
    {
        if (delStatement)
            delete statement;
        SELECT_CLEANUP;
        // Wrap in FdoPtr to remove original reference to original exception
        throw FdoCommandException::Create(ex->GetExceptionMessage(), FdoPtr<FdoException>(ex), ex->GetNativeErrorCode());
    }

    catch ( ... )
    {
        if (delStatement)
            delete statement;
        SELECT_CLEANUP;
        throw;
    }
}