    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <odbc/OResultSet.hxx>
    #include <odbc/OTools.hxx>
    #include <odbc/OResultSetMetaData.hxx>
    #include <com/sun/star/sdbc/DataType.hpp>
    #include <com/sun/star/beans/PropertyAttribute.hpp>
    #include <com/sun/star/beans/PropertyVetoException.hpp>
    #include <com/sun/star/sdbcx/CompareBookmark.hpp>
    #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
    #include <com/sun/star/sdbc/FetchDirection.hpp>
    #include <com/sun/star/sdbc/ResultSetType.hpp>
    #include <comphelper/property.hxx>
    #include <comphelper/sequence.hxx>
    #include <cppuhelper/typeprovider.hxx>
    #include <cppuhelper/supportsservice.hxx>
    #include <com/sun/star/lang/DisposedException.hpp>
    #include <comphelper/types.hxx>
    #include <connectivity/dbtools.hxx>
    #include <connectivity/dbexception.hxx>
    #include <sal/log.hxx>

    using namespace ::comphelper;
    using namespace connectivity;
    using namespace connectivity::odbc;
    using namespace cppu;
    using namespace com::sun::star::uno;
    using namespace com::sun::star::lang;
    using namespace com::sun::star::beans;
    using namespace com::sun::star::sdbc;
    using namespace com::sun::star::sdbcx;
    using namespace com::sun::star::container;
    using namespace com::sun::star::io;
    using namespace com::sun::star::util;

    #define ODBC_SQL_NOT_DEFINED    99UL
    static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_OFF, "ODBC_SQL_NOT_DEFINED must be unique");
    static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_ON, "ODBC_SQL_NOT_DEFINED must be unique");
    static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_FIXED, "ODBC_SQL_NOT_DEFINED must be unique");
    static_assert(ODBC_SQL_NOT_DEFINED != SQL_UB_VARIABLE, "ODBC_SQL_NOT_DEFINED must be unique");

    namespace
    {
        const SQLLEN nMaxBookmarkLen = 20;
    }

    //  IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet");
    OUString SAL_CALL OResultSet::getImplementationName(  )
    {
        return OUString("com.sun.star.sdbcx.odbc.ResultSet");
    }

     Sequence< OUString > SAL_CALL OResultSet::getSupportedServiceNames(  )
    {
         Sequence< OUString > aSupported(2);
        aSupported[0] = "com.sun.star.sdbc.ResultSet";
        aSupported[1] = "com.sun.star.sdbcx.ResultSet";
        return aSupported;
    }

    sal_Bool SAL_CALL OResultSet::supportsService( const OUString& _rServiceName )
    {
        return cppu::supportsService(this, _rServiceName);
    }

    OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :   OResultSet_BASE(m_aMutex)
                            ,OPropertySetHelper(OResultSet_BASE::rBHelper)
                            ,m_bFetchDataInOrder(true)
                            ,m_aStatementHandle(_pStatementHandle)
                            ,m_aConnectionHandle(pStmt->getConnectionHandle())
                            ,m_pStatement(pStmt)
                            ,m_xStatement(*pStmt)
                            ,m_nTextEncoding(pStmt->getOwnConnection()->getTextEncoding())
                            ,m_nRowPos(0)
                            ,m_nUseBookmarks(ODBC_SQL_NOT_DEFINED)
                            ,m_nCurrentFetchState(0)
                            ,m_bWasNull(true)
                            ,m_bEOF(true)
                            ,m_bRowInserted(false)
                            ,m_bRowDeleted(false)
                            ,m_bUseFetchScroll(false)
    {
        osl_atomic_increment( &m_refCount );
        try
        {
            m_pRowStatusArray.reset( new SQLUSMALLINT[1] ); // the default value
            setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get());
        }
        catch(const Exception&)
        { // we don't want our result destroy here
        }
        SQLULEN nCurType = 0;
        try
        {
            nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);
            SQLUINTEGER nValueLen = m_pStatement->getCursorProperties(nCurType,false);
            if( (nValueLen & SQL_CA2_SENSITIVITY_DELETIONS) != SQL_CA2_SENSITIVITY_DELETIONS ||
                (nValueLen & SQL_CA2_CRC_EXACT) != SQL_CA2_CRC_EXACT)
                m_pSkipDeletedSet.reset( new OSkipDeletedSet(this) );
        }
        catch(const Exception&)
        { // we don't want our result destroy here
        }
        try
        {
            SQLUINTEGER nValueLen = 0;
            // Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441%28v=vs.85%29.aspx
            // LibreOffice ODBC binds columns only on update, so we don't care about SQL_GD_ANY_COLUMN / SQL_GD_BOUND
            // TODO: maybe a problem if a column is updated, then an earlier column fetched?
            //       an updated column is bound...
            // TODO: aren't we assuming SQL_GD_OUTPUT_PARAMS?
            //       If yes, we should at least OSL_ENSURE it,
            //       even better throw an exception any OUT parameter registration if !SQL_GD_OUTPUT_PARAMS.
            // If !SQL_GD_ANY_ORDER, cache the whole row so that callers can access columns in any order.
            // In other words, isolate them from ODBC restrictions.
            // TODO: we assume SQL_GD_BLOCK, unless fetchSize is 1
            OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,nullptr);
            m_bFetchDataInOrder = ((SQL_GD_ANY_ORDER & nValueLen) != SQL_GD_ANY_ORDER);
        }
        catch(const Exception&)
        {
            m_bFetchDataInOrder = true;
        }
        try
        {
            // TODO: this does *not* do what it appears.
            //       We use SQLFetchScroll unconditionally in several places
            //       the *only* difference this makes is whether ::next() uses SQLFetchScroll or SQLFetch
            //       so this test seems pointless
            if ( getOdbcFunction(ODBC3SQLFunctionId::GetFunctions) )
            {
                SQLUSMALLINT nSupported = 0;
                m_bUseFetchScroll = ( N3SQLGetFunctions(m_aConnectionHandle,SQL_API_SQLFETCHSCROLL,&nSupported) == SQL_SUCCESS && nSupported == 1 );
            }
        }
        catch(const Exception&)
        {
            m_bUseFetchScroll = false;
        }

        osl_atomic_decrement( &m_refCount );
    }

    OResultSet::~OResultSet()
    {
    }

    void OResultSet::construct()
    {
        osl_atomic_increment( &m_refCount );
        allocBuffer();
        osl_atomic_decrement( &m_refCount );
    }

    void OResultSet::disposing()
    {
        N3SQLCloseCursor(m_aStatementHandle);
        OPropertySetHelper::disposing();

        ::osl::MutexGuard aGuard(m_aMutex);
        releaseBuffer();

        setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, nullptr);
        m_xStatement.clear();
        m_xMetaData.clear();
    }

    SQLRETURN OResultSet::unbind(bool _bUnbindHandle)
    {
        SQLRETURN nRet = 0;
        if ( _bUnbindHandle )
            nRet = N3SQLFreeStmt(m_aStatementHandle,SQL_UNBIND);

        if ( m_aBindVector.size() > 0 )
        {
            TVoidVector::iterator pValue = m_aBindVector.begin();
            TVoidVector::const_iterator pEnd = m_aBindVector.end();
            for(; pValue != pEnd; ++pValue)
            {
                switch (pValue->second)
                {
                    case DataType::CHAR:
                    case DataType::VARCHAR:
                        delete static_cast< OString* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::BIGINT:
                        delete static_cast< sal_Int64* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::DECIMAL:
                    case DataType::NUMERIC:
                        delete static_cast< OString* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::REAL:
                    case DataType::DOUBLE:
                        delete static_cast< double* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::LONGVARCHAR:
                    case DataType::CLOB:
                        delete [] static_cast< char* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::LONGVARBINARY:
                    case DataType::BLOB:
                        delete [] static_cast< char* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::DATE:
                        delete static_cast< DATE_STRUCT* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::TIME:
                        delete static_cast< TIME_STRUCT* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::TIMESTAMP:
                        delete static_cast< TIMESTAMP_STRUCT* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::BIT:
                    case DataType::TINYINT:
                        delete static_cast< sal_Int8* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::SMALLINT:
                        delete static_cast< sal_Int16* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::INTEGER:
                        delete static_cast< sal_Int32* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::FLOAT:
                        delete static_cast< float* >(reinterpret_cast< void * >(pValue->first));
                        break;
                    case DataType::BINARY:
                    case DataType::VARBINARY:
                        delete static_cast< sal_Int8* >(reinterpret_cast< void * >(pValue->first));
                        break;
                }
            }
            m_aBindVector.clear();
        }
        return nRet;
    }

    TVoidPtr OResultSet::allocBindColumn(sal_Int32 _nType,sal_Int32 _nColumnIndex)
    {
        TVoidPtr aPair;
        switch (_nType)
        {
            case DataType::CHAR:
            case DataType::VARCHAR:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType);
                break;
            case DataType::BIGINT:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int64(0)),_nType);
                break;
            case DataType::DECIMAL:
            case DataType::NUMERIC:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new OString()),_nType);
                break;
            case DataType::REAL:
            case DataType::DOUBLE:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new double(0.0)),_nType);
                break;
            case DataType::LONGVARCHAR:
            case DataType::CLOB:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType);  // only for finding
                break;
            case DataType::LONGVARBINARY:
            case DataType::BLOB:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new char[2]),_nType);  // only for finding
                break;
            case DataType::DATE:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new DATE_STRUCT),_nType);
                break;
            case DataType::TIME:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIME_STRUCT),_nType);
                break;
            case DataType::TIMESTAMP:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new TIMESTAMP_STRUCT),_nType);
                break;
            case DataType::BIT:
            case DataType::TINYINT:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8(0)),_nType);
                break;
            case DataType::SMALLINT:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int16(0)),_nType);
                break;
            case DataType::INTEGER:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int32(0)),_nType);
                break;
            case DataType::FLOAT:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new float(0)),_nType);
                break;
            case DataType::BINARY:
            case DataType::VARBINARY:
                aPair = TVoidPtr(reinterpret_cast< sal_Int64 >(new sal_Int8[m_aRow[_nColumnIndex].getSequence().getLength()]),_nType);
                break;
            default:
                SAL_WARN( "connectivity.odbc", "Unknown type");
                aPair = TVoidPtr(0,_nType);
        }
        return aPair;
    }

    void OResultSet::allocBuffer()
    {
        Reference< XResultSetMetaData > xMeta = getMetaData();
        sal_Int32 nLen = xMeta->getColumnCount();

        m_aBindVector.reserve(nLen);
        m_aRow.resize(nLen+1);

        m_aRow[0].setTypeKind(DataType::VARBINARY);
        m_aRow[0].setBound( false );

        for(sal_Int32 i = 1;i<=nLen;++i)
        {
            sal_Int32 nType = xMeta->getColumnType(i);
            m_aRow[i].setTypeKind( nType );
            m_aRow[i].setBound( false );
        }
        m_aLengthVector.resize(nLen + 1);
    }

    void OResultSet::releaseBuffer()
    {
        unbind(false);
        m_aLengthVector.clear();
    }

    Any SAL_CALL OResultSet::queryInterface( const Type & rType )
    {
        Any aRet = OPropertySetHelper::queryInterface(rType);
        return aRet.hasValue() ? aRet : OResultSet_BASE::queryInterface(rType);
    }

     Sequence<  Type > SAL_CALL OResultSet::getTypes(  )
    {
        OTypeCollection aTypes( cppu::UnoType<css::beans::XMultiPropertySet>::get(),
                                cppu::UnoType<css::beans::XFastPropertySet>::get(),
                                cppu::UnoType<css::beans::XPropertySet>::get());

        return ::comphelper::concatSequences(aTypes.getTypes(),OResultSet_BASE::getTypes());
    }

    sal_Int32 SAL_CALL OResultSet::findColumn( const OUString& columnName )
    {
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        ::osl::MutexGuard aGuard( m_aMutex );

        Reference< XResultSetMetaData > xMeta = getMetaData();
        sal_Int32 nLen = xMeta->getColumnCount();
        sal_Int32 i = 1;
        for(;i<=nLen;++i)
        {
            if(xMeta->isCaseSensitive(i) ? columnName == xMeta->getColumnName(i) :
                    columnName.equalsIgnoreAsciiCase(xMeta->getColumnName(i)))
                return i;
        }

        ::dbtools::throwInvalidColumnException( columnName, *this );
        assert(false);
        return 0; // Never reached
    }

    void OResultSet::ensureCacheForColumn(sal_Int32 columnIndex)
    {
        SAL_INFO( "connectivity.odbc", "odbc  lionel@mamane.lu OResultSet::ensureCacheForColumn" );

        assert(columnIndex >= 0);

        const TDataRow::size_type oldCacheSize = m_aRow.size();
        const TDataRow::size_type uColumnIndex = static_cast<TDataRow::size_type>(columnIndex);

        if (oldCacheSize > uColumnIndex)
            // nothing to do
            return;

        m_aRow.resize(columnIndex + 1);
        TDataRow::iterator i (m_aRow.begin() + oldCacheSize);
        const TDataRow::const_iterator end(m_aRow.end());
        for (; i != end; ++i)
        {
            i->setBound(false);
        }
    }
    void OResultSet::invalidateCache()
    {
        const TDataRow::const_iterator end = m_aRow.end();
        for(TDataRow::iterator i=m_aRow.begin(); i!=end; ++i)
        {
            i->setBound(false);
        }
    }

    Reference< XInputStream > SAL_CALL OResultSet::getBinaryStream( sal_Int32 /*columnIndex*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this );

        return nullptr;
    }

    Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*columnIndex*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBinaryStream", *this );

        return nullptr;
    }

    template < typename T > T OResultSet::impl_getValue( const sal_Int32 _nColumnIndex, SQLSMALLINT nType )
    {
        T val;

        OTools::getValue(m_pStatement->getOwnConnection(), m_aStatementHandle, _nColumnIndex, nType, m_bWasNull, **this, &val, sizeof(val));

        return val;
    }

    // this function exists for the implicit conversion to sal_Bool (compared to a direct call to impl_getValue)
    bool OResultSet::impl_getBoolean( sal_Int32 columnIndex )
    {
        return impl_getValue<sal_Int8>(columnIndex, SQL_C_BIT);
    }

    template < typename T > T OResultSet::getValue( sal_Int32 columnIndex )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        fillColumn(columnIndex);
        m_bWasNull = m_aRow[columnIndex].isNull();
        return m_aRow[columnIndex];
    }
    sal_Bool SAL_CALL OResultSet::getBoolean( sal_Int32 columnIndex )
    {
        return getValue<bool>( columnIndex );
    }

    sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex )
    {
        return getValue<sal_Int8>( columnIndex );
    }

    Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        fillColumn(columnIndex);
        m_bWasNull = m_aRow[columnIndex].isNull();

        Sequence< sal_Int8 > nRet;
        switch(m_aRow[columnIndex].getTypeKind())
        {
        case DataType::BINARY:
        case DataType::VARBINARY:
        case DataType::LONGVARBINARY:
            nRet = m_aRow[columnIndex];
            break;
        default:
        {
            OUString const & sRet = m_aRow[columnIndex].getString();
            nRet = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(sRet.getStr()),sizeof(sal_Unicode)*sRet.getLength());
        }
        }
        return nRet;
    }
    Sequence< sal_Int8 > OResultSet::impl_getBytes( sal_Int32 columnIndex )
    {
        const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex);

        switch(nColumnType)
        {
        case SQL_WVARCHAR:
        case SQL_WCHAR:
        case SQL_WLONGVARCHAR:
        case SQL_VARCHAR:
        case SQL_CHAR:
        case SQL_LONGVARCHAR:
        {
            OUString const & aRet = OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding);
            return Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),sizeof(sal_Unicode)*aRet.getLength());
        }
        default:
            return OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this);
        }
    }

    Date OResultSet::impl_getDate( sal_Int32 columnIndex )
    {
        DATE_STRUCT aDate = impl_getValue< DATE_STRUCT> ( columnIndex,
                                                          m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_DATE : SQL_C_TYPE_DATE  );

        return Date(aDate.day, aDate.month, aDate.year);
    }

    Date SAL_CALL OResultSet::getDate( sal_Int32 columnIndex )
    {
        return getValue<Date>( columnIndex );
    }

    double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex )
    {
        return getValue<double>( columnIndex );
    }

    float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex )
    {
        return getValue<float>( columnIndex );
    }

    sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex )
    {
        return getValue<sal_Int16>( columnIndex );
    }

    sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex )
    {
        return getValue<sal_Int32>( columnIndex );
    }

    sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex )
    {
        return getValue<sal_Int64>( columnIndex );
    }
    sal_Int64 OResultSet::impl_getLong( sal_Int32 columnIndex )
    {
        try
        {
            return impl_getValue<sal_Int64>(columnIndex, SQL_C_SBIGINT);
        }
        catch(const SQLException&)
        {
            return getString(columnIndex).toInt64();
        }
    }

    sal_Int32 SAL_CALL OResultSet::getRow(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_pSkipDeletedSet ? m_pSkipDeletedSet->getMappedPosition(getDriverPos()) : getDriverPos();
    }

    Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        if(!m_xMetaData.is())
            m_xMetaData = new OResultSetMetaData(m_pStatement->getOwnConnection(),m_aStatementHandle);
        return m_xMetaData;
    }

    Reference< XArray > SAL_CALL OResultSet::getArray( sal_Int32 /*columnIndex*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getArray", *this );
        return nullptr;
    }

    Reference< XClob > SAL_CALL OResultSet::getClob( sal_Int32 /*columnIndex*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getClob", *this );
        return nullptr;
    }

    Reference< XBlob > SAL_CALL OResultSet::getBlob( sal_Int32 /*columnIndex*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getBlob", *this );
        return nullptr;
    }

    Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRow::getRef", *this );
        return nullptr;
    }

    Any SAL_CALL OResultSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& /*typeMap*/ )
    {
        return getValue<ORowSetValue>( columnIndex ).makeAny();
    }

    OUString OResultSet::impl_getString( sal_Int32 columnIndex )
    {
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex);
        return OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding);
    }
    OUString OResultSet::getString( sal_Int32 columnIndex )
    {
        return getValue<OUString>( columnIndex );
    }

    Time OResultSet::impl_getTime( sal_Int32 columnIndex )
    {
        TIME_STRUCT aTime = impl_getValue< TIME_STRUCT > ( columnIndex,
                                                          m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIME : SQL_C_TYPE_TIME );

        return Time(0, aTime.second,aTime.minute,aTime.hour, false);
    }
    Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex )
    {
        return getValue<Time>( columnIndex );
    }

    DateTime OResultSet::impl_getTimestamp( sal_Int32 columnIndex )
    {
        TIMESTAMP_STRUCT aTime = impl_getValue< TIMESTAMP_STRUCT > ( columnIndex,
                                                                     m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP );

        return DateTime(aTime.fraction,
                        aTime.second,
                        aTime.minute,
                        aTime.hour,
                        aTime.day,
                        aTime.month,
                        aTime.year,
                        false);
    }
    DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex )
    {
        return getValue<DateTime>( columnIndex );
    }

    sal_Bool SAL_CALL OResultSet::isBeforeFirst(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        return m_nRowPos == 0;
    }

    sal_Bool SAL_CALL OResultSet::isAfterLast(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_nRowPos != 0 && m_nCurrentFetchState == SQL_NO_DATA;
    }

    sal_Bool SAL_CALL OResultSet::isFirst(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_nRowPos == 1;
    }

    sal_Bool SAL_CALL OResultSet::isLast(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_bEOF && m_nCurrentFetchState != SQL_NO_DATA;
    }

    void SAL_CALL OResultSet::beforeFirst(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        if(first())
            previous();
        m_nCurrentFetchState = SQL_SUCCESS;
    }

    void SAL_CALL OResultSet::afterLast(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        if(last())
            next();
        m_bEOF = true;
    }

    void SAL_CALL OResultSet::close(  )
    {
        {
            ::osl::MutexGuard aGuard( m_aMutex );
            checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        }
        dispose();
    }

    sal_Bool SAL_CALL OResultSet::first(  )
    {
        return moveImpl(IResultSetHelper::FIRST,0);
    }

    sal_Bool SAL_CALL OResultSet::last(  )
    {
        return moveImpl(IResultSetHelper::LAST,0);
    }

    sal_Bool SAL_CALL OResultSet::absolute( sal_Int32 row )
    {
        return moveImpl(IResultSetHelper::ABSOLUTE1,row);
    }

    sal_Bool SAL_CALL OResultSet::relative( sal_Int32 row )
    {
        return moveImpl(IResultSetHelper::RELATIVE1,row);
    }

    sal_Bool SAL_CALL OResultSet::previous(  )
    {
        return moveImpl(IResultSetHelper::PRIOR,0);
    }

    Reference< XInterface > SAL_CALL OResultSet::getStatement(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        return m_xStatement;
    }

    sal_Bool SAL_CALL OResultSet::rowDeleted()
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        bool bRet = m_bRowDeleted;
        m_bRowDeleted = false;

        return bRet;
    }

    sal_Bool SAL_CALL OResultSet::rowInserted(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        bool bInserted = m_bRowInserted;
        m_bRowInserted = false;

        return bInserted;
    }

    sal_Bool SAL_CALL OResultSet::rowUpdated(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_pRowStatusArray[0] == SQL_ROW_UPDATED;
    }

    sal_Bool SAL_CALL OResultSet::next(  )
    {
        return moveImpl(IResultSetHelper::NEXT,1);
    }

    sal_Bool SAL_CALL OResultSet::wasNull(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return m_bWasNull;
    }

    void SAL_CALL OResultSet::cancel(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        N3SQLCancel(m_aStatementHandle);
    }

    void SAL_CALL OResultSet::clearWarnings(  )
    {
    }

    Any SAL_CALL OResultSet::getWarnings(  )
    {
        return Any();
    }

    void SAL_CALL OResultSet::insertRow(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        SQLLEN nRealLen = 0;
        Sequence<sal_Int8> aBookmark(nMaxBookmarkLen);
        static_assert(static_cast<size_t>(nMaxBookmarkLen) >= sizeof(SQLLEN), "must be larger");

        SQLRETURN nRet = N3SQLBindCol(m_aStatementHandle,
                                    0,
                                    SQL_C_VARBOOKMARK,
                                    aBookmark.getArray(),
                                    nMaxBookmarkLen,
                                    &nRealLen
                                    );

        bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) );
        if ( bPositionByBookmark )
        {
            nRet = N3SQLBulkOperations( m_aStatementHandle, SQL_ADD );
            fillNeededData( nRet );
        }
        else
        {
            if(isBeforeFirst())
                next(); // must be done
            nRet = N3SQLSetPos( m_aStatementHandle, 1, SQL_ADD, SQL_LOCK_NO_CHANGE );
            fillNeededData( nRet );
        }
        aBookmark.realloc(nRealLen);
        try
        {
            OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
        }
        catch(const SQLException&)
        {
            nRet = unbind();
            throw;
        }

        nRet = unbind();
        OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);

        if ( bPositionByBookmark )
        {
            setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));

            nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0);
        }
        else
            nRet = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0); // OJ 06.03.2004
        // sometimes we got an error but we are not interested in anymore #106047# OJ
        //  OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);

        if(m_pSkipDeletedSet)
        {
            if(moveToBookmark(makeAny(aBookmark)))
            {
                sal_Int32 nRowPos = getDriverPos();
                if ( -1 == m_nRowPos )
                {
                    nRowPos = m_aPosToBookmarks.size() + 1;
                }
                if ( nRowPos == m_nRowPos )
                    ++nRowPos;
                m_nRowPos = nRowPos;
                m_pSkipDeletedSet->insertNewPosition(nRowPos);
                m_aPosToBookmarks[aBookmark] = nRowPos;
            }
        }
        m_bRowInserted = true;

    }

    void SAL_CALL OResultSet::updateRow(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        SQLRETURN nRet;

        try
        {
            bool bPositionByBookmark = ( nullptr != getOdbcFunction( ODBC3SQLFunctionId::BulkOperations ) );
            if ( bPositionByBookmark )
            {
                getBookmark();
                assert(m_aRow[0].isBound());
                Sequence<sal_Int8> aBookmark(m_aRow[0].getSequence());
                SQLLEN nRealLen = aBookmark.getLength();
                nRet = N3SQLBindCol(m_aStatementHandle,
                                    0,
                                    SQL_C_VARBOOKMARK,
                                    aBookmark.getArray(),
                                    aBookmark.getLength(),
                                    &nRealLen
                                    );
                OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
                fillNeededData(nRet = N3SQLBulkOperations(m_aStatementHandle, SQL_UPDATE_BY_BOOKMARK));
                // the driver should not have touched this
                // (neither the contents of aBookmark FWIW)
                assert(nRealLen == aBookmark.getLength());
            }
            else
            {
                fillNeededData(nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE));
            }
            OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);
            // unbind all columns so we can fetch all columns again with SQLGetData
            // (and also so that our buffers don't clobber anything, and
            //  so that a subsequent fetch does not overwrite m_aRow[0])
            invalidateCache();
            nRet = unbind();
            OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after success");
        }
        catch(...)
        {
            // unbind all columns so that a subsequent fetch does not overwrite m_aRow[0]
            nRet = unbind();
            OSL_ENSURE(nRet == SQL_SUCCESS,"ODBC insert could not unbind the columns after failure");
            throw;
        }
    }

    void SAL_CALL OResultSet::deleteRow(  )
    {
        SQLRETURN nRet = SQL_SUCCESS;
        sal_Int32 nPos = getDriverPos();
        nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_DELETE,SQL_LOCK_NO_CHANGE);
        OTools::ThrowException(m_pStatement->getOwnConnection(),nRet,m_aStatementHandle,SQL_HANDLE_STMT,*this);

        m_bRowDeleted = ( m_pRowStatusArray[0] == SQL_ROW_DELETED );
        if ( m_bRowDeleted )
        {
            TBookmarkPosMap::iterator aIter = m_aPosToBookmarks.begin();
            TBookmarkPosMap::const_iterator aEnd = m_aPosToBookmarks.end();
            for (; aIter != aEnd; ++aIter)
            {
                if ( aIter->second == nPos )
                {
                    m_aPosToBookmarks.erase(aIter);
                    break;
                }
            }
        }
        if ( m_pSkipDeletedSet )
            m_pSkipDeletedSet->deletePosition(nPos);
    }

    void SAL_CALL OResultSet::cancelRowUpdates(  )
    {
    }

    void SAL_CALL OResultSet::moveToInsertRow(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        invalidateCache();
        // first unbound all columns
        OSL_VERIFY( unbind() == SQL_SUCCESS );
        //  SQLRETURN nRet = N3SQLSetStmtAttr(m_aStatementHandle,SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1,SQL_IS_INTEGER);
    }

    void SAL_CALL OResultSet::moveToCurrentRow(  )
    {
        invalidateCache();
    }

    void OResultSet::updateValue(sal_Int32 columnIndex, SQLSMALLINT _nType, void const * _pValue)
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        m_aBindVector.push_back(allocBindColumn(OTools::MapOdbcType2Jdbc(_nType),columnIndex));
        void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first);
        OSL_ENSURE(pData != nullptr,"Data for update is NULL!");
        OTools::bindValue(  m_pStatement->getOwnConnection(),
                            m_aStatementHandle,
                            columnIndex,
                            _nType,
                            0,
                            _pValue,
                            pData,
                            &m_aLengthVector[columnIndex],
                            **this,
                            m_nTextEncoding,
                            m_pStatement->getOwnConnection()->useOldDateFormat());
    }

    void SAL_CALL OResultSet::updateNull( sal_Int32 columnIndex )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        m_aBindVector.push_back(allocBindColumn(DataType::CHAR,columnIndex));
        void* pData = reinterpret_cast<void*>(m_aBindVector.rbegin()->first);
        OTools::bindValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_CHAR,0,nullptr,pData,&m_aLengthVector[columnIndex],**this,m_nTextEncoding,m_pStatement->getOwnConnection()->useOldDateFormat());
    }

    void SAL_CALL OResultSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x )
    {
        updateValue(columnIndex,SQL_BIT,&x);
    }

    void SAL_CALL OResultSet::updateByte( sal_Int32 columnIndex, sal_Int8 x )
    {
        updateValue(columnIndex,SQL_CHAR,&x);
    }

    void SAL_CALL OResultSet::updateShort( sal_Int32 columnIndex, sal_Int16 x )
    {
        updateValue(columnIndex,SQL_TINYINT,&x);
    }

    void SAL_CALL OResultSet::updateInt( sal_Int32 columnIndex, sal_Int32 x )
    {
        updateValue(columnIndex,SQL_INTEGER,&x);
    }

    void SAL_CALL OResultSet::updateLong( sal_Int32 /*columnIndex*/, sal_Int64 /*x*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRowUpdate::updateLong", *this );
    }

    void SAL_CALL OResultSet::updateFloat( sal_Int32 columnIndex, float x )
    {
        updateValue(columnIndex,SQL_REAL,&x);
    }

    void SAL_CALL OResultSet::updateDouble( sal_Int32 columnIndex, double x )
    {
        updateValue(columnIndex,SQL_DOUBLE,&x);
    }

    void SAL_CALL OResultSet::updateString( sal_Int32 columnIndex, const OUString& x )
    {
        sal_Int32 nType = m_aRow[columnIndex].getTypeKind();
        SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType);
        m_aRow[columnIndex] = x;
        m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarchar will be recognized by fillNeededData
        m_aRow[columnIndex].setBound(true);
        updateValue(columnIndex,nOdbcType, &x);
    }

    void SAL_CALL OResultSet::updateBytes( sal_Int32 columnIndex, const Sequence< sal_Int8 >& x )
    {
        sal_Int32 nType = m_aRow[columnIndex].getTypeKind();
        SQLSMALLINT nOdbcType = OTools::jdbcTypeToOdbc(nType);
        m_aRow[columnIndex] = x;
        m_aRow[columnIndex].setTypeKind(nType); // OJ: otherwise longvarbinary will be recognized by fillNeededData
        m_aRow[columnIndex].setBound(true);
        updateValue(columnIndex,nOdbcType, &x);
    }

    void SAL_CALL OResultSet::updateDate( sal_Int32 columnIndex, const Date& x )
    {
        DATE_STRUCT aVal = OTools::DateToOdbcDate(x);
        updateValue(columnIndex,SQL_DATE,&aVal);
    }

    void SAL_CALL OResultSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x )
    {
        TIME_STRUCT aVal = OTools::TimeToOdbcTime(x);
        updateValue(columnIndex,SQL_TIME,&aVal);
    }

    void SAL_CALL OResultSet::updateTimestamp( sal_Int32 columnIndex, const DateTime& x )
    {
        TIMESTAMP_STRUCT aVal = OTools::DateTimeToTimestamp(x);
        updateValue(columnIndex,SQL_TIMESTAMP,&aVal);
    }

    void SAL_CALL OResultSet::updateBinaryStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length )
    {
        if(!x.is())
            ::dbtools::throwFunctionSequenceException(*this);

        Sequence<sal_Int8> aSeq;
        x->readBytes(aSeq,length);
        updateBytes(columnIndex,aSeq);
    }

    void SAL_CALL OResultSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< XInputStream >& x, sal_Int32 length )
    {
        updateBinaryStream(columnIndex,x,length);
    }

    void SAL_CALL OResultSet::refreshRow(  )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        //  SQLRETURN nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_REFRESH,SQL_LOCK_NO_CHANGE);
        m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_RELATIVE,0);
        OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
    }

    void SAL_CALL OResultSet::updateObject( sal_Int32 columnIndex, const Any& x )
    {
        if (!::dbtools::implUpdateObject(this, columnIndex, x))
            throw SQLException();
    }

    void SAL_CALL OResultSet::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/ )
    {
        if (!::dbtools::implUpdateObject(this, columnIndex, x))
            throw SQLException();
    }

    // XRowLocate
    Any SAL_CALL OResultSet::getBookmark(  )
    {
        fillColumn(0);
        if(m_aRow[0].isNull())
            throw SQLException();
        return m_aRow[0].makeAny();
    }
    Sequence<sal_Int8> OResultSet::impl_getBookmark(  )
    {
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        TBookmarkPosMap::const_iterator aFind = std::find_if(m_aPosToBookmarks.begin(),m_aPosToBookmarks.end(),
            [this] (const TBookmarkPosMap::value_type& bookmarkPos) {
                return bookmarkPos.second == m_nRowPos;
            });

        if ( aFind == m_aPosToBookmarks.end() )
        {
            if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
            {
                m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
            }
            if(m_nUseBookmarks == SQL_UB_OFF)
                throw SQLException();

            Sequence<sal_Int8> bookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
            m_aPosToBookmarks[bookmark] = m_nRowPos;
            OSL_ENSURE(bookmark.getLength(),"Invalid bookmark from length 0!");
            return bookmark;
        }
        else
        {
            return aFind->first;
        }
    }

    sal_Bool SAL_CALL OResultSet::moveToBookmark( const  Any& bookmark )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        invalidateCache();
        Sequence<sal_Int8> aBookmark;
        bookmark >>= aBookmark;
        OSL_ENSURE(aBookmark.getLength(),"Invalid bookmark from length 0!");
        if(aBookmark.getLength())
        {
            SQLRETURN nReturn = setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));

            if ( SQL_INVALID_HANDLE != nReturn && SQL_ERROR != nReturn )
            {
                m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,0);
                OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
                TBookmarkPosMap::const_iterator aFind = m_aPosToBookmarks.find(aBookmark);
                if(aFind != m_aPosToBookmarks.end())
                    m_nRowPos = aFind->second;
                else
                    m_nRowPos = -1;
                return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
            }
        }
        return false;
    }

    sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark( const  Any& bookmark, sal_Int32 rows )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        invalidateCache();
        Sequence<sal_Int8> aBookmark;
        bookmark >>= aBookmark;
        setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));

        m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,rows);
        OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);
        return m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
    }

    sal_Int32 SAL_CALL OResultSet::compareBookmarks( const Any& lhs, const  Any& rhs )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);

        return (lhs == rhs) ? CompareBookmark::EQUAL : CompareBookmark::NOT_EQUAL;
    }

    sal_Bool SAL_CALL OResultSet::hasOrderedBookmarks(  )
    {
        return false;
    }

    sal_Int32 SAL_CALL OResultSet::hashBookmark( const  Any& /*bookmark*/ )
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "XRowLocate::hashBookmark", *this );
        return 0;
    }

    // XDeleteRows
    Sequence< sal_Int32 > SAL_CALL OResultSet::deleteRows( const  Sequence<  Any >& rows )
    {
        Sequence< sal_Int32 > aRet(rows.getLength());
        sal_Int32 *pRet = aRet.getArray();

        const Any *pBegin   = rows.getConstArray();
        const Any *pEnd     = pBegin + rows.getLength();

        for(;pBegin != pEnd;++pBegin,++pRet)
        {
            try
            {
                if(moveToBookmark(*pBegin))
                {
                    deleteRow();
                    *pRet = 1;
                }
            }
            catch(const SQLException&)
            {
                *pRet = 0;
            }
        }
        return aRet;
    }

    template < typename T, SQLINTEGER BufferLength > T OResultSet::getStmtOption (SQLINTEGER fOption) const
    {
        T result (0);
        OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!");
        N3SQLGetStmtAttr(m_aStatementHandle, fOption, &result, BufferLength, nullptr);
        return result;
    }
    template < typename T, SQLINTEGER BufferLength > SQLRETURN OResultSet::setStmtOption (SQLINTEGER fOption, T value) const
    {
        OSL_ENSURE(m_aStatementHandle,"StatementHandle is null!");
        SQLPOINTER sv = reinterpret_cast<SQLPOINTER>(value);
        return N3SQLSetStmtAttr(m_aStatementHandle, fOption, sv, BufferLength);
    }

    sal_Int32 OResultSet::getResultSetConcurrency() const
    {
        sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CONCURRENCY);
        if(SQL_CONCUR_READ_ONLY == nValue)
            nValue = ResultSetConcurrency::READ_ONLY;
        else
            nValue = ResultSetConcurrency::UPDATABLE;

        return nValue;
    }

    sal_Int32 OResultSet::getResultSetType() const
    {
        sal_uInt32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_SENSITIVITY);
        if(SQL_SENSITIVE == nValue)
            nValue = ResultSetType::SCROLL_SENSITIVE;
        else if(SQL_INSENSITIVE == nValue)
            nValue = ResultSetType::SCROLL_INSENSITIVE;
        else
        {
            SQLULEN nCurType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);
            if(SQL_CURSOR_KEYSET_DRIVEN == nCurType)
                nValue = ResultSetType::SCROLL_SENSITIVE;
            else if(SQL_CURSOR_STATIC  == nCurType)
                nValue = ResultSetType::SCROLL_INSENSITIVE;
            else if(SQL_CURSOR_FORWARD_ONLY == nCurType)
                nValue = ResultSetType::FORWARD_ONLY;
            else if(SQL_CURSOR_DYNAMIC == nCurType)
                nValue = ResultSetType::SCROLL_SENSITIVE;
        }
        return nValue;
    }

    sal_Int32 OResultSet::getFetchSize() const
    {
        return getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE);
    }

    OUString OResultSet::getCursorName() const
    {
        SQLCHAR pName[258];
        SQLSMALLINT nRealLen = 0;
        N3SQLGetCursorName(m_aStatementHandle,pName,256,&nRealLen);
        return OUString::createFromAscii(reinterpret_cast<char*>(pName));
    }

    bool  OResultSet::isBookmarkable() const
    {
        if(!m_aConnectionHandle)
            return false;

        const SQLULEN nCursorType = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE);

        sal_Int32 nAttr = 0;
        try
        {
            switch(nCursorType)
            {
            case SQL_CURSOR_FORWARD_ONLY:
                return false;
            case SQL_CURSOR_STATIC:
                OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_STATIC_CURSOR_ATTRIBUTES1,nAttr,nullptr);
                break;
            case SQL_CURSOR_KEYSET_DRIVEN:
                OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_KEYSET_CURSOR_ATTRIBUTES1,nAttr,nullptr);
                break;
            case SQL_CURSOR_DYNAMIC:
                OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_DYNAMIC_CURSOR_ATTRIBUTES1,nAttr,nullptr);
                break;
            }
        }
        catch(const Exception&)
        {
            return false;
        }

        if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
        {
            m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
        }

        return (m_nUseBookmarks != SQL_UB_OFF) && (nAttr & SQL_CA1_BOOKMARK) == SQL_CA1_BOOKMARK;
    }

    void OResultSet::setFetchDirection(sal_Int32 _par0)
    {
        ::dbtools::throwFunctionNotSupportedSQLException( "setFetchDirection", *this );

        OSL_ENSURE(_par0>0,"Illegal fetch direction!");
        if ( _par0 > 0 )
        {
            setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_CURSOR_TYPE, _par0);
        }
    }

    void OResultSet::setFetchSize(sal_Int32 _par0)
    {
        OSL_ENSURE(_par0>0,"Illegal fetch size!");
        if ( _par0 != 1 )
        {
            throw css::beans::PropertyVetoException("SDBC/ODBC layer not prepared for fetchSize > 1", *this);
        }
        if ( _par0 > 0 )
        {
            setStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_ARRAY_SIZE, _par0);
            m_pRowStatusArray.reset( new SQLUSMALLINT[_par0] );
            setStmtOption<SQLUSMALLINT*, SQL_IS_POINTER>(SQL_ATTR_ROW_STATUS_PTR, m_pRowStatusArray.get());
        }
    }

    IPropertyArrayHelper* OResultSet::createArrayHelper( ) const
    {
        Sequence< Property > aProps(6);
        Property* pProperties = aProps.getArray();
        sal_Int32 nPos = 0;
        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_CURSORNAME),
            PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY);

        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHDIRECTION),
            PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0);

        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_FETCHSIZE),
            PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0);

        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_ISBOOKMARKABLE),
            PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY);

        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETCONCURRENCY),
            PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY);

        pProperties[nPos++] = css::beans::Property(::connectivity::OMetaConnection::getPropMap().getNameByIndex(PROPERTY_ID_RESULTSETTYPE),
            PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY);

        return new OPropertyArrayHelper(aProps);
    }

    IPropertyArrayHelper & OResultSet::getInfoHelper()
    {
        return *getArrayHelper();
    }

    sal_Bool OResultSet::convertFastPropertyValue(
                                Any & rConvertedValue,
                                Any & rOldValue,
                                sal_Int32 nHandle,
                                const Any& rValue )
    {
        switch(nHandle)
        {
            case PROPERTY_ID_ISBOOKMARKABLE:
            case PROPERTY_ID_CURSORNAME:
            case PROPERTY_ID_RESULTSETCONCURRENCY:
            case PROPERTY_ID_RESULTSETTYPE:
                throw css::lang::IllegalArgumentException();
            case PROPERTY_ID_FETCHDIRECTION:
                return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchDirection());
            case PROPERTY_ID_FETCHSIZE:
                return ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, getFetchSize());
            default:
                ;
        }
        return false;
    }

    void OResultSet::setFastPropertyValue_NoBroadcast(
                                    sal_Int32 nHandle,
                                    const Any& rValue
                                                     )
    {
        switch(nHandle)
        {
            case PROPERTY_ID_ISBOOKMARKABLE:
            case PROPERTY_ID_CURSORNAME:
            case PROPERTY_ID_RESULTSETCONCURRENCY:
            case PROPERTY_ID_RESULTSETTYPE:
                throw Exception("cannot set prop " + OUString::number(nHandle), nullptr);
            case PROPERTY_ID_FETCHDIRECTION:
                setFetchDirection(getINT32(rValue));
                break;
            case PROPERTY_ID_FETCHSIZE:
                setFetchSize(getINT32(rValue));
                break;
            default:
                ;
        }
    }

    void OResultSet::getFastPropertyValue(
                                    Any& rValue,
                                    sal_Int32 nHandle
                                         ) const
    {
        switch(nHandle)
        {
            case PROPERTY_ID_ISBOOKMARKABLE:
                rValue <<= isBookmarkable();
                break;
            case PROPERTY_ID_CURSORNAME:
                rValue <<= getCursorName();
                break;
            case PROPERTY_ID_RESULTSETCONCURRENCY:
                rValue <<= getResultSetConcurrency();
                break;
            case PROPERTY_ID_RESULTSETTYPE:
                rValue <<= getResultSetType();
                break;
            case PROPERTY_ID_FETCHDIRECTION:
                rValue <<= getFetchDirection();
                break;
            case PROPERTY_ID_FETCHSIZE:
                rValue <<= getFetchSize();
                break;
        }
    }

    void OResultSet::fillColumn(const sal_Int32 _nColumn)
    {
        ensureCacheForColumn(_nColumn);

        if (m_aRow[_nColumn].isBound())
            return;

        sal_Int32 curCol;
        if(m_bFetchDataInOrder)
        {
            // m_aRow necessarily has a prefix of bound values, then all unbound values
            // EXCEPT for column 0
            // so use binary search to find the earliest unbound value before or at _nColumn
            sal_Int32 lower=0;
            sal_Int32 upper=_nColumn;

            while (lower < upper)
            {
                const sal_Int32 middle=(upper-lower)/2 + lower;
                if(m_aRow[middle].isBound())
                {
                    lower=middle+1;
                }
                else
                {
                    upper=middle;
                }
            }

            curCol = upper;
        }
        else
        {
            curCol = _nColumn;
        }

        TDataRow::iterator pColumn      = m_aRow.begin() + curCol;
        const TDataRow::const_iterator pColumnEnd   = m_aRow.begin() + _nColumn + 1;

        if(curCol==0)
        {
            try
            {
                *pColumn=impl_getBookmark();
            }
            catch (SQLException &)
            {
                pColumn->setNull();
            }
            pColumn->setBound(true);
            ++curCol;
            ++pColumn;
        }

        for (; pColumn != pColumnEnd; ++curCol, ++pColumn)
        {
            const sal_Int32 nType = pColumn->getTypeKind();
            switch (nType)
            {
            case DataType::CHAR:
            case DataType::VARCHAR:
            case DataType::DECIMAL:
            case DataType::NUMERIC:
            case DataType::LONGVARCHAR:
            case DataType::CLOB:
                *pColumn=impl_getString(curCol);
                break;
            case DataType::FLOAT:
                *pColumn = impl_getValue<float>(curCol, SQL_C_FLOAT);
                break;
            case DataType::REAL:
            case DataType::DOUBLE:
                *pColumn = impl_getValue<double>(curCol, SQL_C_DOUBLE);
                break;
            case DataType::BINARY:
            case DataType::VARBINARY:
            case DataType::LONGVARBINARY:
            case DataType::BLOB:
                *pColumn = impl_getBytes(curCol);
                break;
            case DataType::DATE:
                *pColumn = impl_getDate(curCol);
                break;
            case DataType::TIME:
                *pColumn = impl_getTime(curCol);
                break;
            case DataType::TIMESTAMP:
                *pColumn = impl_getTimestamp(curCol);
                break;
            case DataType::BIT:
                *pColumn = impl_getBoolean(curCol);
                break;
            case DataType::TINYINT:
                *pColumn = impl_getValue<sal_Int8>(curCol, SQL_C_TINYINT);
                break;
            case DataType::SMALLINT:
                *pColumn = impl_getValue<sal_Int16>(curCol, SQL_C_SHORT);
                break;
            case DataType::INTEGER:
                *pColumn = impl_getValue<sal_Int32>(curCol, SQL_C_LONG);
                break;
            case DataType::BIGINT:
                *pColumn = impl_getLong(curCol);
                break;
            default:
                SAL_WARN( "connectivity.odbc","Unknown DataType");
            }

            if ( m_bWasNull )
                pColumn->setNull();
            pColumn->setBound(true);
            if(nType != pColumn->getTypeKind())
            {
                pColumn->setTypeKind(nType);
            }
        }
    }

    void SAL_CALL OResultSet::acquire() throw()
    {
        OResultSet_BASE::acquire();
    }

    void SAL_CALL OResultSet::release() throw()
    {
        OResultSet_BASE::release();
    }

    css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OResultSet::getPropertySetInfo(  )
    {
        return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper());
    }

    bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, bool /*_bRetrieveData*/)
    {
        SQLSMALLINT nFetchOrientation = SQL_FETCH_NEXT;
        switch(_eCursorPosition)
        {
            case IResultSetHelper::NEXT:
                nFetchOrientation = SQL_FETCH_NEXT;
                break;
            case IResultSetHelper::PRIOR:
                nFetchOrientation = SQL_FETCH_PRIOR;
                break;
            case IResultSetHelper::FIRST:
                nFetchOrientation = SQL_FETCH_FIRST;
                break;
            case IResultSetHelper::LAST:
                nFetchOrientation = SQL_FETCH_LAST;
                break;
            case IResultSetHelper::RELATIVE1:
                nFetchOrientation = SQL_FETCH_RELATIVE;
                break;
            case IResultSetHelper::ABSOLUTE1:
                nFetchOrientation = SQL_FETCH_ABSOLUTE;
                break;
            case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers
            {
                TBookmarkPosMap::const_iterator aIter = m_aPosToBookmarks.begin();
                TBookmarkPosMap::const_iterator aEnd = m_aPosToBookmarks.end();
                for (; aIter != aEnd; ++aIter)
                {
                    if ( aIter->second == _nOffset )
                        return moveToBookmark(makeAny(aIter->first));
                }
                SAL_WARN( "connectivity.odbc", "Bookmark not found!");
            }
            return false;
        }

        m_bEOF = false;
        invalidateCache();

        SQLRETURN nOldFetchStatus = m_nCurrentFetchState;
        // TODO FIXME: both of these will misbehave for
        // _eCursorPosition == IResultSetHelper::NEXT/PREVIOUS
        // when fetchSize > 1
        if ( !m_bUseFetchScroll && _eCursorPosition == IResultSetHelper::NEXT )
            m_nCurrentFetchState = N3SQLFetch(m_aStatementHandle);
        else
            m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,nFetchOrientation,_nOffset);

        SAL_INFO(
            "connectivity.odbc",
            "move(" << nFetchOrientation << "," << _nOffset << "), FetchState = "
                << m_nCurrentFetchState);
        OTools::ThrowException(m_pStatement->getOwnConnection(),m_nCurrentFetchState,m_aStatementHandle,SQL_HANDLE_STMT,*this);

        const bool bSuccess = m_nCurrentFetchState == SQL_SUCCESS || m_nCurrentFetchState == SQL_SUCCESS_WITH_INFO;
        if ( bSuccess )
        {
            switch(_eCursorPosition)
            {
                case IResultSetHelper::NEXT:
                    ++m_nRowPos;
                    break;
                case IResultSetHelper::PRIOR:
                    --m_nRowPos;
                    break;
                case IResultSetHelper::FIRST:
                    m_nRowPos = 1;
                    break;
                case IResultSetHelper::LAST:
                    m_bEOF = true;
                    break;
                case IResultSetHelper::RELATIVE1:
                    m_nRowPos += _nOffset;
                    break;
                case IResultSetHelper::ABSOLUTE1:
                case IResultSetHelper::BOOKMARK: // special case here because we are only called with position numbers
                    m_nRowPos = _nOffset;
                    break;
            } // switch(_eCursorPosition)
            if ( m_nUseBookmarks == ODBC_SQL_NOT_DEFINED )
            {
                m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS);
            }
            if ( m_nUseBookmarks == SQL_UB_OFF )
            {
                m_aRow[0].setNull();
            }
            else
            {
                ensureCacheForColumn(0);
                Sequence<sal_Int8> bookmark  = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
                m_aPosToBookmarks[bookmark] = m_nRowPos;
                OSL_ENSURE(bookmark.getLength(),"Invalid bookmark from length 0!");
                m_aRow[0] = bookmark;
            }
            m_aRow[0].setBound(true);
        }
        else if ( IResultSetHelper::PRIOR == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA )
            // we went beforeFirst
            m_nRowPos = 0;
        else if(IResultSetHelper::NEXT == _eCursorPosition && m_nCurrentFetchState == SQL_NO_DATA && nOldFetchStatus != SQL_NO_DATA)
            // we went afterLast
            ++m_nRowPos;

        return bSuccess;
    }

    sal_Int32 OResultSet::getDriverPos() const
    {
        sal_Int32 nValue = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_ROW_NUMBER);
        SAL_INFO(
            "connectivity.odbc",
            "RowNum = " << nValue << ", RowPos = " << m_nRowPos);
        return nValue ? nValue : m_nRowPos;
    }

    bool OResultSet::isRowDeleted() const
    {
        return m_pRowStatusArray[0] == SQL_ROW_DELETED;
    }

    bool OResultSet::moveImpl(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset)
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
        return (m_pSkipDeletedSet != nullptr)
                    ?   m_pSkipDeletedSet->skipDeleted(_eCursorPosition,_nOffset,true/*_bRetrieveData*/)
                    :   move(_eCursorPosition,_nOffset,true/*_bRetrieveData*/);
    }

    void OResultSet::fillNeededData(SQLRETURN _nRet)
    {
        SQLRETURN nRet = _nRet;
        if( nRet == SQL_NEED_DATA)
        {
            void* pColumnIndex = nullptr;
            nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex);

            do
            {
                if (nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO && nRet != SQL_NEED_DATA)
                    break;

                sal_IntPtr nColumnIndex ( reinterpret_cast<sal_IntPtr>(pColumnIndex));
                Sequence< sal_Int8 > aSeq;
                switch(m_aRow[nColumnIndex].getTypeKind())
                {
                    case DataType::BINARY:
                    case DataType::VARBINARY:
                    case DataType::LONGVARBINARY:
                    case DataType::BLOB:
                        aSeq = m_aRow[nColumnIndex];
                        N3SQLPutData (m_aStatementHandle, aSeq.getArray(), aSeq.getLength());
                        break;
                    case SQL_WLONGVARCHAR:
                    {
                        OUString const & sRet = m_aRow[nColumnIndex].getString();
                        N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<sal_Unicode *>(sRet.getStr())), sizeof(sal_Unicode)*sRet.getLength());
                        break;
                    }
                    case DataType::LONGVARCHAR:
                    case DataType::CLOB:
                    {
                        OUString sRet;
                        sRet = m_aRow[nColumnIndex].getString();
                        OString aString(OUStringToOString(sRet,m_nTextEncoding));
                        N3SQLPutData (m_aStatementHandle, static_cast<SQLPOINTER>(const_cast<char *>(aString.getStr())), aString.getLength());
                        break;
                    }
                    default:
                        SAL_WARN( "connectivity.odbc", "Not supported at the moment!");
                }
                nRet = N3SQLParamData(m_aStatementHandle,&pColumnIndex);
            }
            while (nRet == SQL_NEED_DATA);
        }
    }

    SWORD OResultSet::impl_getColumnType_nothrow(sal_Int32 columnIndex)
    {
        std::map<sal_Int32,SWORD>::const_iterator aFind = m_aODBCColumnTypes.find(columnIndex);
        if ( aFind == m_aODBCColumnTypes.end() )
            aFind = m_aODBCColumnTypes.emplace(
                               columnIndex,
                               OResultSetMetaData::getColumnODBCType(m_pStatement->getOwnConnection(),m_aStatementHandle,*this,columnIndex)
                            ).first;
        return aFind->second;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression '_par0 > 0' is always true.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.