[Libreoffice-commits] .: Branch 'libreoffice-4-0' - 17 commits - connectivity/source dbaccess/source svx/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Thu Dec 6 10:45:22 PST 2012


 connectivity/source/drivers/odbcbase/OPreparedStatement.cxx |    1 
 connectivity/source/drivers/odbcbase/OResultSet.cxx         |  575 ++++++------
 connectivity/source/drivers/odbcbase/OTools.cxx             |  271 ++---
 connectivity/source/inc/odbc/OResultSet.hxx                 |   37 
 connectivity/source/inc/odbc/OTools.hxx                     |    4 
 dbaccess/source/core/api/KeySet.cxx                         |   16 
 dbaccess/source/core/api/RowSetBase.cxx                     |   16 
 svx/source/fmcomp/gridctrl.cxx                              |   14 
 8 files changed, 515 insertions(+), 419 deletions(-)

New commits:
commit 794c86df74e8aebfbfccf845bd86d216b41606fa
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Thu Dec 6 13:22:55 2012 +0100

    Avoid seeking a cursor to where it already is
    
    Change-Id: I817aef57f3e028d77cf13f8cca1ecc7afcea9725

diff --git a/svx/source/fmcomp/gridctrl.cxx b/svx/source/fmcomp/gridctrl.cxx
index 5e129e1..9a40816 100644
--- a/svx/source/fmcomp/gridctrl.cxx
+++ b/svx/source/fmcomp/gridctrl.cxx
@@ -2447,9 +2447,19 @@ sal_Bool DbGridControl::SeekCursor(long nRow, sal_Bool bAbsolute)
             if (!bSuccess)
             {
                 if (bAbsolute || nSteps > 0)
-                    bSuccess = m_pSeekCursor->last();
+                {
+                    if (m_pSeekCursor->isLast())
+                        bSuccess=sal_True;
+                    else
+                        bSuccess = m_pSeekCursor->last();
+                }
                 else
-                    bSuccess = m_pSeekCursor->first();
+                {
+                    if (m_pSeekCursor->isFirst())
+                        bSuccess = sal_True;
+                    else
+                        bSuccess = m_pSeekCursor->first();
+                }
             }
 
             if (bSuccess)
commit d20232a77565f46fedc0b556f4d50addff4d3559
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Thu Dec 6 13:22:06 2012 +0100

    Don't force refresh when higher up code did not request it
    
    Change-Id: I0f415c96fc05c1d776d14885751aef020c42f4ae

diff --git a/dbaccess/source/core/api/RowSetBase.cxx b/dbaccess/source/core/api/RowSetBase.cxx
index 7238e90..8b4087d 100644
--- a/dbaccess/source/core/api/RowSetBase.cxx
+++ b/dbaccess/source/core/api/RowSetBase.cxx
@@ -713,7 +713,6 @@ sal_Bool SAL_CALL ORowSetBase::isFirst(  ) throw(SQLException, RuntimeException)
     if ( impl_rowDeleted() )
         return ( m_nDeletedPosition == 1 );
 
-    positionCache( MOVE_NONE_REFRESH_ONLY );
     sal_Bool bIsFirst = m_pCache->isFirst();
 
     OSL_TRACE("DBACCESS ORowSetBase::isFirst() = %i Clone = %i",bIsFirst,m_bClone);
@@ -745,7 +744,6 @@ sal_Bool SAL_CALL ORowSetBase::isLast(  ) throw(SQLException, RuntimeException)
             return ( m_nDeletedPosition == impl_getRowCount() );
     }
 
-    positionCache( MOVE_NONE_REFRESH_ONLY );
     sal_Bool bIsLast = m_pCache->isLast();
 
     OSL_TRACE("DBACCESS ORowSetBase::isLast() = %i Clone = %i",bIsLast,m_bClone);
@@ -1111,14 +1109,6 @@ void ORowSetBase::setCurrentRow( sal_Bool _bMoved, sal_Bool _bDoNotify, const OR
         OSL_ENSURE(m_aCurrentRow->is(),"Currentrow isn't valid");
         OSL_ENSURE(m_aBookmark.hasValue(),"Bookmark has no value!");
 
-#if OSL_DEBUG_LEVEL > 0
-        sal_Int32 nOldRow = m_pCache->getRow();
-#endif
-        positionCache( MOVE_NONE_REFRESH_ONLY );
-#if OSL_DEBUG_LEVEL > 0
-        sal_Int32 nNewRow = m_pCache->getRow();
-        OSL_ENSURE(nOldRow == nNewRow,"Old position is not equal to new postion");
-#endif
         m_aCurrentRow   = m_pCache->m_aMatrixIter;
         m_bIsInsertRow  = sal_False;
         OSL_ENSURE(!m_aCurrentRow.isNull(),"CurrentRow is nul after positionCache!");
@@ -1328,7 +1318,11 @@ void ORowSetBase::positionCache( CursorMoveDirection _ePrepareForDirection )
     sal_Bool bSuccess = sal_False;
     if ( m_aBookmark.hasValue() )
     {
-        bSuccess = m_pCache->moveToBookmark( m_aBookmark );
+        if ( _ePrepareForDirection == MOVE_NONE_REFRESH_ONLY ||
+             m_pCache->compareBookmarks( m_aBookmark, m_pCache->getBookmark() ) != CompareBookmark::EQUAL )
+            bSuccess = m_pCache->moveToBookmark( m_aBookmark );
+        else
+            bSuccess = sal_True;
     }
     else
     {
commit 59375da788af698aa619cb763adaf0428a4bb81e
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 18:09:57 2012 +0100

    fdo#53281 Don't cache whole row in KeySet
    
    This was done for the sake of ODBC,
    but the cost was imposed on all backends.
    
    The ODBC problems are now solved cleanly (and more efficiently)
    in the SDBC<->ODBC layer.
    
    Change-Id: Ib8a864da08deaaacc96a379fb72b3b7cbb34598c

diff --git a/dbaccess/source/core/api/KeySet.cxx b/dbaccess/source/core/api/KeySet.cxx
index 057ebc0..21b897b 100644
--- a/dbaccess/source/core/api/KeySet.cxx
+++ b/dbaccess/source/core/api/KeySet.cxx
@@ -38,7 +38,6 @@
 #include <com/sun/star/sdbcx/KeyType.hpp>
 #include <connectivity/dbtools.hxx>
 #include <connectivity/dbexception.hxx>
-#include <boost/static_assert.hpp>
 #include <list>
 #include <algorithm>
 #include <string.h>
@@ -1432,23 +1431,10 @@ sal_Bool OKeySet::fetchRow()
         bRet = m_xDriverSet->next();
     if ( bRet )
     {
-        const int cc = m_xSetMetaData->getColumnCount();
-
         ORowSetRow aKeyRow = new connectivity::ORowVector< ORowSetValue >((*m_pKeyColumnNames).size() + m_pForeignColumnNames->size());
-        ORowSetRow aFullRow = new connectivity::ORowVector< ORowSetValue >(cc);
-
-        // Fetch the columns only once and in order, to satisfy restrictive backends such as ODBC
-        connectivity::ORowVector< ORowSetValue >::Vector::iterator aFRIter = aFullRow->get().begin();
-        // Column 0 is reserved for the bookmark; unused here.
-        ++aFRIter;
-        BOOST_STATIC_ASSERT(sizeof(int) >= sizeof(sal_Int32)); // "At least a 32 bit word expected"
-        for (int i = 1; i <= cc; ++i, ++aFRIter )
-        {
-            aFRIter->fill(i, m_xSetMetaData->getColumnType(i), m_xDriverRow);
-        }
 
         ::comphelper::disposeComponent(m_xSet);
-        m_xRow.set(new OPrivateRow(aFullRow->get()));
+        m_xRow.set(m_xDriverRow, UNO_QUERY_THROW);
 
         connectivity::ORowVector< ORowSetValue >::Vector::iterator aIter = aKeyRow->get().begin();
         // copy key columns
commit 570cb8ffd4e174aa936f15217fd4dbe163c44e6a
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 17:59:36 2012 +0100

    fdo#47520 ODBC: overhaul data fetching
    
    fetched data in the current row is always cached.
    This avoids trying to fetch the same data several times, which is tricky in several scenarios in ODBC.
    
    Numerous cleanups in passing.
    
    Change-Id: I17246aa614276e141161a64c716881242c559310

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index 375ed39..ddad30f 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -60,6 +60,11 @@ BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_ON );
 BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_FIXED );
 BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_VARIABLE );
 
+namespace
+{
+    const SQLLEN nMaxBookmarkLen = 20;
+}
+
 //------------------------------------------------------------------------------
 //  IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet");
 ::rtl::OUString SAL_CALL OResultSet::getImplementationName(  ) throw ( RuntimeException)
@@ -89,6 +94,7 @@ sal_Bool SAL_CALL OResultSet::supportsService( const ::rtl::OUString& _rServiceN
 // -------------------------------------------------------------------------
 OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :   OResultSet_BASE(m_aMutex)
                         ,OPropertySetHelper(OResultSet_BASE::rBHelper)
+                        ,m_bFetchDataInOrder(sal_True)
                         ,m_aStatementHandle(_pStatementHandle)
                         ,m_aConnectionHandle(pStmt->getConnectionHandle())
                         ,m_pStatement(pStmt)
@@ -98,7 +104,6 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
                         ,m_pRowStatusArray( NULL )
                         ,m_nTextEncoding(pStmt->getOwnConnection()->getTextEncoding())
                         ,m_nRowPos(0)
-                        ,m_nLastColumnPos(0)
                         ,m_nUseBookmarks(ODBC_SQL_NOT_DEFINED)
                         ,m_nCurrentFetchState(0)
                         ,m_bWasNull(sal_True)
@@ -106,7 +111,6 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
                         ,m_bLastRecord(sal_False)
                         ,m_bFreeHandle(sal_False)
                         ,m_bInserting(sal_False)
-                        ,m_bFetchData(sal_True)
                         ,m_bRowInserted(sal_False)
                         ,m_bRowDeleted(sal_False)
                         ,m_bUseFetchScroll(sal_False)
@@ -145,14 +149,12 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
         // 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
-        // TODO: not sure that forcing m_bFetchData for SQL_CURSOR_FORWARD_ONLY is sufficient.
-        //       the spec says SQLGetData should not be called *at* *all* if the fetch size is > 1 (for forward-only cursor)
         OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,NULL);
-        m_bFetchData = !((SQL_GD_ANY_ORDER & nValueLen) == SQL_GD_ANY_ORDER && nCurType != SQL_CURSOR_FORWARD_ONLY);
+        m_bFetchDataInOrder = !((SQL_GD_ANY_ORDER & nValueLen) == SQL_GD_ANY_ORDER);
     }
     catch(const Exception&)
-    { // we don't want our result destroy here
-        m_bFetchData = sal_True;
+    {
+        m_bFetchDataInOrder = sal_True;
     }
     try
     {
@@ -347,10 +349,14 @@ void OResultSet::allocBuffer()
     m_aBindVector.push_back(TVoidPtr(0,0)); // the first is reserved for the bookmark
     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);
 }
@@ -396,13 +402,43 @@ sal_Int32 SAL_CALL OResultSet::findColumn( const ::rtl::OUString& columnName ) t
     return i;
 }
 // -------------------------------------------------------------------------
+void OResultSet::ensureCacheForColumn(sal_Int32 columnIndex)
+{
+    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "lionel at 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() + uColumnIndex);
+    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*/ ) throw(SQLException, RuntimeException)
 {
     ::osl::MutexGuard aGuard( m_aMutex );
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
+    ::dbtools::throwFunctionNotSupportedException( "XRow::getBinaryStream", *this );
 
-    // TODO use getBytes instead of
     return NULL;
 }
 // -------------------------------------------------------------------------
@@ -411,160 +447,149 @@ Reference< XInputStream > SAL_CALL OResultSet::getCharacterStream( sal_Int32 /*c
     ::osl::MutexGuard aGuard( m_aMutex );
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
+    ::dbtools::throwFunctionNotSupportedException( "XRow::getBinaryStream", *this );
 
-    // TODO use getBytes instead of
     return NULL;
 }
 // -----------------------------------------------------------------------------
-const ORowSetValue& OResultSet::getValue(sal_Int32 _nColumnIndex,SQLSMALLINT _nType,void* _pValue,SQLINTEGER _rSize)
+template < typename T > T OResultSet::impl_getValue( const sal_Int32 _nColumnIndex, SQLSMALLINT nType )
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getValue" );
-    ::osl::MutexGuard aGuard( m_aMutex );
-    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+    T val;
 
-    if(m_bFetchData)
-    {
-        if(_nColumnIndex > m_nLastColumnPos)
-            fillRow(_nColumnIndex);
-        return m_aRow[_nColumnIndex];
-    }
-    else
-        OTools::getValue(m_pStatement->getOwnConnection(),m_aStatementHandle,_nColumnIndex,_nType,m_bWasNull,**this,_pValue,_rSize);
+    OTools::getValue(m_pStatement->getOwnConnection(), m_aStatementHandle, _nColumnIndex, nType, m_bWasNull, **this, &val, sizeof(val));
 
-    return m_aEmptyValue;
+    return val;
 }
 // -------------------------------------------------------------------------
+// this function exists for the implicit conversion to sal_Bool (compared to a direct call to impl_getValue)
+sal_Bool SAL_CALL OResultSet::impl_getBoolean( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
+    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 ) throw(SQLException, RuntimeException)
 {
-    sal_Int8 nVal(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_BIT,&nVal,sizeof nVal);
-    return (&aValue == &m_aEmptyValue) ? (sal_Bool)nVal : (sal_Bool)aValue;
+    return getValue<sal_Bool>( columnIndex );
 }
 // -------------------------------------------------------------------------
-
 sal_Int8 SAL_CALL OResultSet::getByte( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    sal_Int8 nRet(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_TINYINT,&nRet,sizeof nRet);
-    return (&aValue == &m_aEmptyValue) ? nRet : (sal_Int8)aValue;
+    return getValue<sal_Int8>( columnIndex );
 }
 // -------------------------------------------------------------------------
 
 Sequence< sal_Int8 > SAL_CALL OResultSet::getBytes( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getBytes" );
-
-    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
     ::osl::MutexGuard aGuard( m_aMutex );
+    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
+    fillColumn(columnIndex);
+    m_bWasNull = m_aRow[columnIndex].isNull();
 
-
-    if(m_bFetchData)
+    Sequence< sal_Int8 > nRet;
+    switch(m_aRow[columnIndex].getTypeKind())
     {
-        if(columnIndex > m_nLastColumnPos)
-            fillRow(columnIndex);
-        Sequence< sal_Int8 > nRet;
-        switch(m_aRow[columnIndex].getTypeKind())
-        {
-            case DataType::BINARY:
-            case DataType::VARBINARY:
-            case DataType::LONGVARBINARY:
-                nRet = m_aRow[columnIndex];
-                break;
-            default:
-            {
-                ::rtl::OUString sRet;
-                sRet = m_aRow[columnIndex].getString();
-                nRet = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(sRet.getStr()),sizeof(sal_Unicode)*sRet.getLength());
-            }
-        }
-        return nRet;
+    case DataType::BINARY:
+    case DataType::VARBINARY:
+    case DataType::LONGVARBINARY:
+        nRet = m_aRow[columnIndex];
+        break;
+    default:
+    {
+        rtl::OUString sRet;
+        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 > SAL_CALL OResultSet::impl_getBytes( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
     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:
-        {
-            ::rtl::OUString 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:
-            ;
+    case SQL_WVARCHAR:
+    case SQL_WCHAR:
+    case SQL_WLONGVARCHAR:
+    case SQL_VARCHAR:
+    case SQL_CHAR:
+    case SQL_LONGVARCHAR:
+    {
+        rtl::OUString 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);
     }
-    return OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,SQL_C_BINARY,m_bWasNull,**this);
 }
 // -------------------------------------------------------------------------
+Date SAL_CALL OResultSet::impl_getDate( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
+    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 ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getDate" );
-    DATE_STRUCT aDate;
-    aDate.day   = 0;
-    aDate.month = 0;
-    aDate.year  = 0;
-
-    const ORowSetValue& aValue = getValue(  columnIndex,
-                            m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_DATE : SQL_C_TYPE_DATE,
-                                            &aDate,sizeof aDate);
-    return (&aValue == &m_aEmptyValue)  ? Date(aDate.day,aDate.month,aDate.year) : (Date)aValue;
+    return getValue<Date>( columnIndex );
 }
 // -------------------------------------------------------------------------
 
 double SAL_CALL OResultSet::getDouble( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    double nRet(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_DOUBLE,&nRet,sizeof nRet);
-    return (&aValue == &m_aEmptyValue) ? nRet : (double)aValue;
+    return getValue<double>( columnIndex );
 }
 // -------------------------------------------------------------------------
 
 float SAL_CALL OResultSet::getFloat( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    float nRet(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_FLOAT,&nRet,sizeof nRet);
-    return (&aValue == &m_aEmptyValue) ? nRet : (float)aValue;
+    return getValue<float>( columnIndex );
 }
 // -------------------------------------------------------------------------
-
-sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    sal_Int32 nRet(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_LONG,&nRet,sizeof nRet);
-    return (&aValue == &m_aEmptyValue) ? nRet : (sal_Int32)aValue;
+    return getValue<sal_Int16>( columnIndex );
 }
 // -------------------------------------------------------------------------
-
-sal_Int32 SAL_CALL OResultSet::getRow(  ) throw(SQLException, RuntimeException)
+sal_Int32 SAL_CALL OResultSet::getInt( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    ::osl::MutexGuard aGuard( m_aMutex );
-    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
-
-    return m_pSkipDeletedSet ? m_pSkipDeletedSet->getMappedPosition(getDriverPos()) : getDriverPos();
+    return getValue<sal_Int32>( columnIndex );
 }
 // -------------------------------------------------------------------------
-
 sal_Int64 SAL_CALL OResultSet::getLong( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    sal_Int64 nRet(0);
+    return getValue<sal_Int64>( columnIndex );
+}
+sal_Int64 SAL_CALL OResultSet::impl_getLong( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
     try
     {
-        const ORowSetValue& aValue = getValue(columnIndex,SQL_C_SBIGINT,&nRet,sizeof nRet);
-        return (&aValue == &m_aEmptyValue) ? nRet : (sal_Int64)aValue;
+        return impl_getValue<sal_Int64>(columnIndex, SQL_C_SBIGINT);
     }
     catch(const SQLException&)
     {
-        nRet = getString(columnIndex).toInt64();
+        return getString(columnIndex).toInt64();
     }
-    return nRet;
 }
 // -------------------------------------------------------------------------
+sal_Int32 SAL_CALL OResultSet::getRow(  ) throw(SQLException, RuntimeException)
+{
+    ::osl::MutexGuard aGuard( m_aMutex );
+    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
+    return m_pSkipDeletedSet ? m_pSkipDeletedSet->getMappedPosition(getDriverPos()) : getDriverPos();
+}
+// -------------------------------------------------------------------------
 Reference< XResultSetMetaData > SAL_CALL OResultSet::getMetaData(  ) throw(SQLException, RuntimeException)
 {
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getMetaData" );
@@ -607,69 +632,50 @@ Reference< XRef > SAL_CALL OResultSet::getRef( sal_Int32 /*columnIndex*/ ) throw
 
 Any SAL_CALL OResultSet::getObject( sal_Int32 columnIndex, const Reference< ::com::sun::star::container::XNameAccess >& /*typeMap*/ ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getObject" );
-    ::osl::MutexGuard aGuard( m_aMutex );
-    checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
-
-    fillRow(columnIndex);
-    return m_aRow[columnIndex].makeAny();
+    return getValue<ORowSetValue>( columnIndex ).makeAny();
 }
 // -------------------------------------------------------------------------
-
-sal_Int16 SAL_CALL OResultSet::getShort( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+::rtl::OUString SAL_CALL OResultSet::impl_getString( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    sal_Int16 nRet(0);
-    const ORowSetValue& aValue = getValue(columnIndex,SQL_C_SHORT,&nRet,sizeof nRet);
-    return (&aValue == &m_aEmptyValue) ? nRet : (sal_Int16)aValue;
+    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);
 }
-// -------------------------------------------------------------------------
-
-
 ::rtl::OUString SAL_CALL OResultSet::getString( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getString" );
-    ::osl::MutexGuard aGuard( m_aMutex );
-
-    ::rtl::OUString nRet;
-    if ( m_bFetchData )
-        nRet = getValue(columnIndex,0,NULL,0);
-    else
-    {
-        checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
-        const SWORD nColumnType = impl_getColumnType_nothrow(columnIndex);
-        nRet = OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,columnIndex,nColumnType,m_bWasNull,**this,m_nTextEncoding);
-    }
-    return nRet;
+    return getValue<rtl::OUString>( columnIndex );
 }
 // -------------------------------------------------------------------------
+Time SAL_CALL OResultSet::impl_getTime( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
+    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);
+}
 Time SAL_CALL OResultSet::getTime( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getTime" );
-    TIME_STRUCT aTime={0,0,0};
-    const ORowSetValue& aValue = getValue(columnIndex,
-        m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIME : SQL_C_TYPE_TIME,
-        &aTime,sizeof aTime);
-    return (&aValue == &m_aEmptyValue) ? Time(0,aTime.second,aTime.minute,aTime.hour) : (Time)aValue;
+    return getValue<Time>( columnIndex );
 }
 // -------------------------------------------------------------------------
+DateTime SAL_CALL OResultSet::impl_getTimestamp( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
+{
+    TIMESTAMP_STRUCT aTime = impl_getValue< TIMESTAMP_STRUCT > ( columnIndex,
+                                                                 m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP );
 
-
+    return DateTime(static_cast<sal_uInt16>(aTime.fraction/ODBC_FRACTION_UNITS_PER_HSECOND),
+                    aTime.second,
+                    aTime.minute,
+                    aTime.hour,
+                    aTime.day,
+                    aTime.month,
+                    aTime.year);
+}
 DateTime SAL_CALL OResultSet::getTimestamp( sal_Int32 columnIndex ) throw(SQLException, RuntimeException)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getTimestamp" );
-    TIMESTAMP_STRUCT aTime={0,0,0,0,0,0,0};
-    const ORowSetValue& aValue = getValue(columnIndex,
-        m_pStatement->getOwnConnection()->useOldDateFormat() ? SQL_C_TIMESTAMP : SQL_C_TYPE_TIMESTAMP,
-        &aTime,sizeof aTime);
-    return (&aValue == &m_aEmptyValue)
-            ?
-            DateTime(static_cast<sal_uInt16>(aTime.fraction/ODBC_FRACTION_UNITS_PER_HSECOND),aTime.second,aTime.minute,aTime.hour,aTime.day,aTime.month,aTime.year)
-            :
-            (DateTime)aValue;
+    return getValue<DateTime>( columnIndex );
 }
 // -------------------------------------------------------------------------
-
 sal_Bool SAL_CALL OResultSet::isBeforeFirst(  ) throw(SQLException, RuntimeException)
 {
     ::osl::MutexGuard aGuard( m_aMutex );
@@ -824,8 +830,7 @@ sal_Bool SAL_CALL OResultSet::wasNull(  ) throw(SQLException, RuntimeException)
     ::osl::MutexGuard aGuard( m_aMutex );
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
-
-    return m_bFetchData ? m_aRow[m_nLastColumnPos].isNull() : m_bWasNull;
+    return m_bWasNull;
 }
 // -------------------------------------------------------------------------
 
@@ -854,20 +859,19 @@ void SAL_CALL OResultSet::insertRow(  ) throw(SQLException, RuntimeException)
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
 
-    const SQLLEN nMaxLen = 20;
     SQLLEN nRealLen = 0;
-    Sequence<sal_Int8> aBookmark(nMaxLen);
-    assert (static_cast<size_t>(nMaxLen) >= sizeof(SQLLEN));
+    Sequence<sal_Int8> aBookmark(nMaxBookmarkLen);
+    BOOST_STATIC_ASSERT(static_cast<size_t>(nMaxBookmarkLen) >= sizeof(SQLLEN));
 
     SQLRETURN nRet = N3SQLBindCol(m_aStatementHandle,
                                 0,
                                 SQL_C_VARBOOKMARK,
                                 aBookmark.getArray(),
-                                nMaxLen,
+                                nMaxBookmarkLen,
                                 &nRealLen
                                 );
-    //  Sequence<sal_Int8> aRealBookmark(nMaxLen);
 
+    aBookmark.realloc(nRealLen);
     sal_Bool bPositionByBookmark = ( NULL != getOdbcFunction( ODBC3SQLBulkOperations ) );
     if ( bPositionByBookmark )
     {
@@ -907,7 +911,6 @@ void SAL_CALL OResultSet::insertRow(  ) throw(SQLException, RuntimeException)
 
     if(m_pSkipDeletedSet)
     {
-        aBookmark.realloc(nRealLen);
         if(moveToBookmark(makeAny(aBookmark)))
         {
             sal_Int32 nRowPos = getDriverPos();
@@ -935,19 +938,21 @@ void SAL_CALL OResultSet::updateRow(  ) throw(SQLException, RuntimeException)
     SQLRETURN nRet;
 
     sal_Bool bPositionByBookmark = ( NULL != getOdbcFunction( ODBC3SQLBulkOperations ) );
+    Sequence<sal_Int8> aBookmark(nMaxBookmarkLen);
     if ( bPositionByBookmark )
     {
         SQLLEN nRealLen = 0;
         nRet = N3SQLBindCol(m_aStatementHandle,
                             0,
                             SQL_C_VARBOOKMARK,
-                            m_aBookmark.getArray(),
-                            m_aBookmark.getLength(),
+                            aBookmark.getArray(),
+                            aBookmark.getLength(),
                             &nRealLen
                             );
         fillNeededData(nRet = N3SQLBulkOperations(m_aStatementHandle, SQL_UPDATE_BY_BOOKMARK));
-        // LEM TODO: need to allow for change of not only bookmark value, but also bookmark length
-        assert(nRealLen == m_aBookmark.getLength());
+        aBookmark.realloc(nRealLen);
+        m_aRow[0]=aBookmark;
+        m_aRow[0].setBound(true);
     }
     else
         fillNeededData(nRet = N3SQLSetPos(m_aStatementHandle,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE));
@@ -996,7 +1001,7 @@ void SAL_CALL OResultSet::moveToInsertRow(  ) throw(SQLException, RuntimeExcepti
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
 
-    m_nLastColumnPos = 0;
+    invalidateCache();
     // first unbound all columns
     OSL_VERIFY_EQUALS( unbind(), SQL_SUCCESS, "Could not unbind columns!" );
     //  SQLRETURN nRet = N3SQLSetStmtAttr(m_aStatementHandle,SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1,SQL_IS_INTEGER);
@@ -1006,7 +1011,7 @@ void SAL_CALL OResultSet::moveToInsertRow(  ) throw(SQLException, RuntimeExcepti
 
 void SAL_CALL OResultSet::moveToCurrentRow(  ) throw(SQLException, RuntimeException)
 {
-    m_nLastColumnPos = 0;
+    invalidateCache();
 }
 // -------------------------------------------------------------------------
 void OResultSet::updateValue(sal_Int32 columnIndex,SQLSMALLINT _nType,void* _pValue) throw(SQLException, RuntimeException)
@@ -1085,6 +1090,7 @@ void SAL_CALL OResultSet::updateString( sal_Int32 columnIndex, const ::rtl::OUSt
     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,(void*)&x);
 }
 // -------------------------------------------------------------------------
@@ -1094,6 +1100,7 @@ void SAL_CALL OResultSet::updateBytes( sal_Int32 columnIndex, const Sequence< sa
     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,(void*)&x);
 }
 // -------------------------------------------------------------------------
@@ -1161,8 +1168,14 @@ void SAL_CALL OResultSet::updateNumericObject( sal_Int32 columnIndex, const Any&
 // XRowLocate
 Any SAL_CALL OResultSet::getBookmark(  ) throw( SQLException,  RuntimeException)
 {
+    fillColumn(0);
+    if(m_aRow[0].isNull())
+        throw SQLException();
+    return m_aRow[0].makeAny();
+}
+Sequence<sal_Int8> SAL_CALL OResultSet::impl_getBookmark(  ) throw( SQLException,  RuntimeException)
+{
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::getBookmark" );
-     ::osl::MutexGuard aGuard( m_aMutex );
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
     TBookmarkPosMap::iterator aFind = ::std::find_if(m_aPosToBookmarks.begin(),m_aPosToBookmarks.end(),
@@ -1177,13 +1190,16 @@ Any SAL_CALL OResultSet::getBookmark(  ) throw( SQLException,  RuntimeException)
         if(m_nUseBookmarks == SQL_UB_OFF)
             throw SQLException();
 
-        m_aBookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
-        m_aPosToBookmarks[m_aBookmark] = m_nRowPos;
-        OSL_ENSURE(m_aBookmark.getLength(),"Invalid bookmark from length 0!");
+        fillColumn(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!");
+        return bookmark;
     }
     else
-        m_aBookmark = aFind->first;
-    return makeAny(m_aBookmark);
+    {
+        return aFind->first;
+    }
 }
 // -------------------------------------------------------------------------
 sal_Bool SAL_CALL OResultSet::moveToBookmark( const  Any& bookmark ) throw( SQLException,  RuntimeException)
@@ -1192,18 +1208,19 @@ sal_Bool SAL_CALL OResultSet::moveToBookmark( const  Any& bookmark ) throw( SQLE
     ::osl::MutexGuard aGuard( m_aMutex );
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
-    m_nLastColumnPos = 0;
-    bookmark >>= m_aBookmark;
-    OSL_ENSURE(m_aBookmark.getLength(),"Invalid bookmark from length 0!");
-    if(m_aBookmark.getLength())
+    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*>(m_aBookmark.getArray()));
+        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::iterator aFind = m_aPosToBookmarks.find(m_aBookmark);
+            TBookmarkPosMap::iterator aFind = m_aPosToBookmarks.find(aBookmark);
             if(aFind != m_aPosToBookmarks.end())
                 m_nRowPos = aFind->second;
             else
@@ -1221,9 +1238,10 @@ sal_Bool SAL_CALL OResultSet::moveRelativeToBookmark( const  Any& bookmark, sal_
     checkDisposed(OResultSet_BASE::rBHelper.bDisposed);
 
 
-    m_nLastColumnPos = 0;
-    bookmark >>= m_aBookmark;
-    SQLRETURN nReturn = setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(m_aBookmark.getArray()));
+    invalidateCache();
+    Sequence<sal_Int8> aBookmark;
+    bookmark >>= aBookmark;
+    SQLRETURN nReturn = setStmtOption<SQLLEN*, SQL_IS_POINTER>(SQL_ATTR_FETCH_BOOKMARK_PTR, reinterpret_cast<SQLLEN*>(aBookmark.getArray()));
     OSL_UNUSED( nReturn );
 
     m_nCurrentFetchState = N3SQLFetchScroll(m_aStatementHandle,SQL_FETCH_BOOKMARK,rows);
@@ -1383,6 +1401,8 @@ sal_Bool  OResultSet::isBookmarkable() const
 //------------------------------------------------------------------------------
 void OResultSet::setFetchDirection(sal_Int32 _par0)
 {
+    ::dbtools::throwFunctionNotSupportedException( "setFetchDirection", *this );
+
     OSL_ENSURE(_par0>0,"Illegal fetch direction!");
     if ( _par0 > 0 )
     {
@@ -1395,7 +1415,7 @@ void OResultSet::setFetchSize(sal_Int32 _par0)
     OSL_ENSURE(_par0>0,"Illegal fetch size!");
     if ( _par0 != 1 )
     {
-        throw ::com::sun::star::beans::PropertyVetoException("SDBC/ODBC layer not prepared for fetchSize > 1", this);
+        throw ::com::sun::star::beans::PropertyVetoException("SDBC/ODBC layer not prepared for fetchSize > 1", *this);
     }
     if ( _par0 > 0 )
     {
@@ -1503,85 +1523,124 @@ void OResultSet::getFastPropertyValue(
     }
 }
 // -------------------------------------------------------------------------
-void OResultSet::fillRow(const sal_Int32 _nToColumn)
+void OResultSet::fillColumn(const sal_Int32 _nColumn)
 {
-    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::fillRow" );
-    if((sal_Int32)m_aRow.size() <= _nToColumn)
+    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::fillColumn" );
+
+    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
     {
-        m_aRow.resize(_nToColumn+1);
+        curCol = _nColumn;
     }
-    m_bFetchData = sal_False;
 
-    sal_Int32          nColumn      = m_nLastColumnPos + 1;
-    TDataRow::iterator pColumn      = m_aRow.begin() + nColumn;
-    TDataRow::iterator pColumnEnd   = m_aRow.begin() + _nToColumn + 1;
+    TDataRow::iterator pColumn      = m_aRow.begin() + curCol;
+    TDataRow::iterator pColumnEnd   = m_aRow.begin() + _nColumn + 1;
 
-    for (; pColumn < pColumnEnd; ++nColumn, ++pColumn)
+    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:
-                {
-                    const SWORD nColumnType = impl_getColumnType_nothrow(nColumn);
-                    *pColumn = OTools::getStringValue(m_pStatement->getOwnConnection(),m_aStatementHandle,nColumn,nColumnType,m_bWasNull,**this,m_nTextEncoding);
-                }
-                break;
-            case DataType::BIGINT:
-                *pColumn = getLong(nColumn);
-                break;
-            case DataType::REAL:
-            case DataType::DOUBLE:
-                *pColumn = getDouble(nColumn);
-                break;
-            case DataType::LONGVARBINARY:
-            case DataType::BLOB:
-                *pColumn = getBytes(nColumn);
-                break;
-            case DataType::DATE:
-                *pColumn = getDate(nColumn);
-                break;
-            case DataType::TIME:
-                *pColumn = getTime(nColumn);
-                break;
-            case DataType::TIMESTAMP:
-                *pColumn = getTimestamp(nColumn);
-                break;
-            case DataType::BIT:
-                *pColumn = getBoolean(nColumn);
-                break;
-            case DataType::TINYINT:
-                *pColumn = getByte(nColumn);
-                break;
-            case DataType::SMALLINT:
-                *pColumn = getShort(nColumn);
-                break;
-            case DataType::INTEGER:
-                *pColumn = getInt(nColumn);
-                break;
-            case DataType::FLOAT:
-                *pColumn = getFloat(nColumn);
-                break;
-            case DataType::BINARY:
-            case DataType::VARBINARY:
-                *pColumn = getBytes(nColumn);
-                break;
+        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:
+            OSL_FAIL("Unknown DataType");
         }
 
         if ( m_bWasNull )
             pColumn->setNull();
+        pColumn->setBound(true);
         if(nType != pColumn->getTypeKind())
         {
             pColumn->setTypeKind(nType);
         }
     }
-    m_nLastColumnPos = _nToColumn;
-    m_bFetchData = sal_True;
 }
 // -----------------------------------------------------------------------------
 void SAL_CALL OResultSet::acquire() throw()
@@ -1638,7 +1697,7 @@ sal_Bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32
     }
 
     m_bEOF = sal_False;
-    m_nLastColumnPos = 0;
+    invalidateCache();
 
     SQLRETURN nOldFetchStatus = m_nCurrentFetchState;
     // TODO FIXME: both of these will misbehave for
@@ -1681,13 +1740,19 @@ sal_Bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32
         {
             m_nUseBookmarks = getStmtOption<SQLULEN, SQL_IS_UINTEGER>(SQL_ATTR_USE_BOOKMARKS, SQL_UB_OFF);
         }
-        if ( m_nUseBookmarks != SQL_UB_OFF )
+        if ( m_nUseBookmarks == SQL_UB_OFF )
+        {
+            m_aRow[0].setNull();
+        }
+        else
         {
-            RTL_LOGFILE_CONTEXT_TRACE( aLogger, "OTools::getBytesValue" );
-            m_aBookmark = OTools::getBytesValue(m_pStatement->getOwnConnection(),m_aStatementHandle,0,SQL_C_VARBOOKMARK,m_bWasNull,**this);
-            m_aPosToBookmarks[m_aBookmark] = m_nRowPos;
-            OSL_ENSURE(m_aBookmark.getLength(),"Invalid bookmark from length 0!");
+            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
diff --git a/connectivity/source/inc/odbc/OResultSet.hxx b/connectivity/source/inc/odbc/OResultSet.hxx
index d31b409..c6ef45b 100644
--- a/connectivity/source/inc/odbc/OResultSet.hxx
+++ b/connectivity/source/inc/odbc/OResultSet.hxx
@@ -119,10 +119,18 @@ namespace connectivity
             TVoidVector                                 m_aBindVector;
             ::std::vector<SQLLEN>                       m_aLengthVector;
             ::std::map<sal_Int32,SWORD>                 m_aODBCColumnTypes;
-            ::com::sun::star::uno::Sequence<sal_Int8>   m_aBookmark;
 
-            TDataRow                                    m_aRow; // only used when SQLGetData can't be called in any order
-            ORowSetValue                                m_aEmptyValue;  // needed for the getValue method when no prefetch is used
+            // In baseline ODBC, SQLGetData can only be called on monotonically increasing column numbers.
+            // additionally, any variable-length data can be fetched only once (possibly in parts);
+            // after that, SQLGetData returns SQL_NO_DATA.
+            // In order to insulate our callers from these restrictions,
+            // we cache the current row in m_aRow.
+            // If the driver claims to support the GD_ANY_ORDER extension,
+            // we read and cache only the columns requested by a caller.
+            // Else, we read and cache all columns whose number is <= a requested column.
+            // m_aRow[colNumber].getBound() says if it contains an up-to-date value or not.
+            TDataRow                                    m_aRow;
+            sal_Bool                                    m_bFetchDataInOrder;
             SQLHANDLE                                   m_aStatementHandle;
             SQLHANDLE                                   m_aConnectionHandle;
             OStatement_Base*                            m_pStatement;
@@ -132,7 +140,6 @@ namespace connectivity
             SQLUSMALLINT*                               m_pRowStatusArray;
             rtl_TextEncoding                            m_nTextEncoding;
             sal_Int32                                   m_nRowPos;
-            sal_Int32                                   m_nLastColumnPos;       // used for m_aRow just to know where we are
             mutable sal_uInt32                          m_nUseBookmarks;
             SQLRETURN                                   m_nCurrentFetchState;
             sal_Bool                                    m_bWasNull;
@@ -140,7 +147,6 @@ namespace connectivity
             sal_Bool                                    m_bLastRecord;
             sal_Bool                                    m_bFreeHandle;
             sal_Bool                                    m_bInserting;
-            sal_Bool                                    m_bFetchData;           // false when SQLGetData can be called in any order or when fetching data for m_aRow
             sal_Bool                                    m_bRowInserted;
             sal_Bool                                    m_bRowDeleted;
             sal_Bool                                    m_bUseFetchScroll;
@@ -158,17 +164,34 @@ namespace connectivity
             template < typename T, SQLINTEGER BufferLength > SQLRETURN setStmtOption (SQLINTEGER fOption, T value) const;
 
 
-            void fillRow(sal_Int32 _nToColumn);
+            void ensureCacheForColumn(sal_Int32 columnIndex);
+            void invalidateCache();
+            void fillColumn(sal_Int32 _nToColumn);
             void allocBuffer();
             void releaseBuffer();
             void updateValue(sal_Int32 columnIndex,SQLSMALLINT _nType,void* _pValue) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
             void fillNeededData(SQLRETURN _nRet);
-            const ORowSetValue& getValue(sal_Int32 _nColumnIndex,SQLSMALLINT _nType,void* _pValue,SQLINTEGER _rSize);
             sal_Bool moveImpl(IResultSetHelper::Movement _eCursorPosition, sal_Int32 _nOffset, sal_Bool _bRetrieveData);
             TVoidPtr allocBindColumn(sal_Int32 _nType,sal_Int32 _nColumnIndex);
             SQLRETURN unbind(sal_Bool _bUnbindHandle = sal_True);
             SWORD impl_getColumnType_nothrow(sal_Int32 columnIndex);
 
+            // helper to implement XRow::getXXX in simple cases
+            template < typename T > T getValue( sal_Int32 columnIndex );
+            // impl_getXXX are the functions that do the actual fetching from ODBC, ignoring the cache
+            // for simple cases
+            template < typename T > T impl_getValue( const sal_Int32 _nColumnIndex, SQLSMALLINT nType );
+            // these cases need some special treatment
+            sal_Bool impl_getBoolean( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::com::sun::star::uno::Sequence< sal_Int8 > impl_getBytes( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::com::sun::star::util::Date impl_getDate( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::com::sun::star::util::Time impl_getTime( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::com::sun::star::util::DateTime impl_getTimestamp( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            sal_Int64 SAL_CALL impl_getLong( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::rtl::OUString SAL_CALL impl_getString( sal_Int32 columnIndex ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+            ::com::sun::star::uno::Sequence<sal_Int8> SAL_CALL impl_getBookmark(  ) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
+
+
             // OPropertyArrayUsageHelper
             virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const;
             // OPropertySetHelper
commit efbd1e7b3cab3a266e631f0a196c44c3d466050e
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 10:54:11 2012 +0100

    comments
    
    Change-Id: I1702ea167ac6d23b1bdfcda2ea1ad7815e4474b9

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index a29b445..375ed39 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -1690,8 +1690,10 @@ sal_Bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32
         }
     }
     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;
commit de2c6c2e3991bef898a9a308e64b74c0d771c313
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 10:53:49 2012 +0100

    Refuse to set fetchSize > 1, the rest of the code is not prepared for it
    
    Change-Id: Id49a9db96cdb0aaf901a00a5439b36c1d0386c41

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index 1e70fc8..a29b445 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -22,6 +22,7 @@
 #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>
@@ -134,13 +135,18 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
     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
+        // TODO: not sure that forcing m_bFetchData for SQL_CURSOR_FORWARD_ONLY is sufficient.
+        //       the spec says SQLGetData should not be called *at* *all* if the fetch size is > 1 (for forward-only cursor)
         OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,NULL);
         m_bFetchData = !((SQL_GD_ANY_ORDER & nValueLen) == SQL_GD_ANY_ORDER && nCurType != SQL_CURSOR_FORWARD_ONLY);
     }
@@ -150,6 +156,10 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
     }
     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(ODBC3SQLGetFunctions) )
         {
             SQLUSMALLINT nSupported = 0;
@@ -1383,6 +1393,10 @@ void OResultSet::setFetchDirection(sal_Int32 _par0)
 void OResultSet::setFetchSize(sal_Int32 _par0)
 {
     OSL_ENSURE(_par0>0,"Illegal fetch size!");
+    if ( _par0 != 1 )
+    {
+        throw ::com::sun::star::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);
@@ -1627,12 +1641,15 @@ sal_Bool OResultSet::move(IResultSetHelper::Movement _eCursorPosition, sal_Int32
     m_nLastColumnPos = 0;
 
     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);
 
-    OSL_TRACE( __FILE__": OSkipDeletedSet::OResultSet::move(%d,%d), FetchState = %d",nFetchOrientation,_nOffset,m_nCurrentFetchState);
+    OSL_TRACE( __FILE__": OResultSet::move(%d,%d), FetchState = %d",nFetchOrientation,_nOffset,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;
commit 7ecf02cb4a07e19e6475d790905a9b4db41e0e3f
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:46:37 2012 +0100

    comment meaning inverted wrt to reality in code
    
    Change-Id: I9974921bd62bf4f57e13ffb681b2a90e1403c90b

diff --git a/connectivity/source/inc/odbc/OResultSet.hxx b/connectivity/source/inc/odbc/OResultSet.hxx
index 1e3f72c..d31b409 100644
--- a/connectivity/source/inc/odbc/OResultSet.hxx
+++ b/connectivity/source/inc/odbc/OResultSet.hxx
@@ -140,7 +140,7 @@ namespace connectivity
             sal_Bool                                    m_bLastRecord;
             sal_Bool                                    m_bFreeHandle;
             sal_Bool                                    m_bInserting;
-            sal_Bool                                    m_bFetchData;           // true when SQLGetaData can be called in any order or when fetching data for m_aRow
+            sal_Bool                                    m_bFetchData;           // false when SQLGetData can be called in any order or when fetching data for m_aRow
             sal_Bool                                    m_bRowInserted;
             sal_Bool                                    m_bRowDeleted;
             sal_Bool                                    m_bUseFetchScroll;
commit 59583dfc30431bca6fe662cfb69ffc85b2408c9a
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:46:22 2012 +0100

    setBound(true) is the default
    
    Anyway, setting bound only on the *last* column we are going to fetch does not make sense.
    Either all of of them or none.
    
    Change-Id: I54e8e6b7d33863d1662167b370739cb7d39c0bc4

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index 7d32459..1e70fc8 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -1495,7 +1495,6 @@ void OResultSet::fillRow(const sal_Int32 _nToColumn)
     if((sal_Int32)m_aRow.size() <= _nToColumn)
     {
         m_aRow.resize(_nToColumn+1);
-        m_aRow[_nToColumn].setBound(sal_True);
     }
     m_bFetchData = sal_False;
 
commit 51712cf6df127be827e23692786358ab8c83dd09
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:37:41 2012 +0100

    janitorial: const annotation
    
    Change-Id: I6ac4f2700e15bc53a9a666aa5e2222748618dbf1

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index 45e5fea..7d32459 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -1489,7 +1489,7 @@ void OResultSet::getFastPropertyValue(
     }
 }
 // -------------------------------------------------------------------------
-void OResultSet::fillRow(sal_Int32 _nToColumn)
+void OResultSet::fillRow(const sal_Int32 _nToColumn)
 {
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OResultSet::fillRow" );
     if((sal_Int32)m_aRow.size() <= _nToColumn)
commit 121fc7307cb5ef0959fff4cd6cdd336644b48f19
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:11:55 2012 +0100

    janitorial: alignment
    
    Change-Id: Ibfe513ac8fbcd982784110ac1d3fe84061228bf9

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index b40361c..45e5fea 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -1499,7 +1499,7 @@ void OResultSet::fillRow(sal_Int32 _nToColumn)
     }
     m_bFetchData = sal_False;
 
-    sal_Int32           nColumn     = m_nLastColumnPos + 1;
+    sal_Int32          nColumn      = m_nLastColumnPos + 1;
     TDataRow::iterator pColumn      = m_aRow.begin() + nColumn;
     TDataRow::iterator pColumnEnd   = m_aRow.begin() + _nToColumn + 1;
 
commit b77b55bbef331fc4cae628a552edebcd9fcbc56f
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:07:11 2012 +0100

    ODBC: clean up our use of SQLGetData
    
    Change-Id: I813efb928a88eb0a78faaba4ba0d4186c8a9413c

diff --git a/connectivity/source/drivers/odbcbase/OTools.cxx b/connectivity/source/drivers/odbcbase/OTools.cxx
index 8533152..a868b0b 100644
--- a/connectivity/source/drivers/odbcbase/OTools.cxx
+++ b/connectivity/source/drivers/odbcbase/OTools.cxx
@@ -25,6 +25,7 @@
 #include "diagnose_ex.h"
 #include <rtl/logfile.hxx>
 #include <rtl/ustrbuf.hxx>
+#include <boost/static_assert.hpp>
 
 
 #include <string.h>
@@ -360,49 +361,48 @@ Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection,
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OTools::getBytesValue" );
     sal_Int8 aCharArray[2048];
     // First try to fetch the data with the little Buffer:
-    SQLLEN nMaxLen = sizeof aCharArray - 1;
-    //  GETDATA(SQL_C_CHAR,aCharArray,nMaxLen);
-    SQLLEN pcbValue = 0;
-    OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                        (SQLUSMALLINT)columnIndex,
-                                        _fSqlType,
-                                        (SQLPOINTER)aCharArray,
-                                        nMaxLen,
-                                        &pcbValue),
-                            _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
+    const SQLLEN nMaxLen = sizeof aCharArray;
+    SQLLEN pcbValue = SQL_NO_TOTAL;
+    Sequence<sal_Int8> aData;
 
-    _bWasNull = pcbValue == SQL_NULL_DATA;
-    if(_bWasNull)
-        return Sequence<sal_Int8>();
-
-    SQLINTEGER nBytes = pcbValue != SQL_NO_TOTAL ? std::min(pcbValue, nMaxLen) : nMaxLen;
-    if ( ((pcbValue == SQL_NO_TOTAL) || pcbValue > nMaxLen) && aCharArray[nBytes-1] == 0  && nBytes > 0 )
-        --nBytes;
-    Sequence<sal_Int8> aData((sal_Int8*)aCharArray, nBytes);
-
-    // It is about Binariy Data, a String, that for StarView is to long or
-    // the driver kan't predict the length of the data - as well as save the
-    // MemoryStream.
-    while ((pcbValue == SQL_NO_TOTAL) || pcbValue > nMaxLen)
+    // >= because if the data is nMaxLen long, our buffer is actually ONE byte short,
+    // for the null byte terminator!
+    while (pcbValue == SQL_NO_TOTAL || pcbValue >= nMaxLen)
     {
-        // At Strings the Buffer won't be completly used
-        // (The last Byte is always a NULL-Byte, however it won't be counted with pcbValue)
-        if (pcbValue != SQL_NO_TOTAL && (pcbValue - nMaxLen) < nMaxLen)
-            nBytes = pcbValue - nMaxLen;
+        OTools::ThrowException(_pConnection,
+                               (*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(
+                                   _aStatementHandle,
+                                   (SQLUSMALLINT)columnIndex,
+                                   _fSqlType,
+                                   (SQLPOINTER)aCharArray,
+                                   nMaxLen,
+                                   &pcbValue),
+                               _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
+
+        _bWasNull = pcbValue == SQL_NULL_DATA;
+        if(_bWasNull)
+            return Sequence<sal_Int8>();
+
+        SQLLEN nReadBytes;
+        // After the SQLGetData that wrote out to aCharArray the last byte of the data,
+        // pcbValue will not be SQL_NO_TOTAL -> we have a reliable count
+        if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) )
+        {
+            // we filled the buffer; remove the terminating null byte
+            nReadBytes = nMaxLen-1;
+            if ( aCharArray[nReadBytes] != 0)
+            {
+                OSL_FAIL("Buggy ODBC driver? Did not null-terminate (variable length) data!");
+                ++nReadBytes;
+            }
+        }
         else
-            nBytes = nMaxLen;
-
-        // While there is a "truncation"-Warning, proceed with fetching Data.
-        OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                        (SQLUSMALLINT)columnIndex,
-                                        SQL_C_BINARY,
-                                        &aCharArray,
-                                        (SQLINTEGER)nBytes,
-                                        &pcbValue),
-                            _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
-        sal_Int32 nLen = aData.getLength();
-        aData.realloc(nLen + nBytes);
-        memcpy(aData.getArray() + nLen, aCharArray, nBytes);
+        {
+            nReadBytes = pcbValue;
+        }
+        const sal_Int32 nLen = aData.getLength();
+        aData.realloc(nLen + nReadBytes);
+        memcpy(aData.getArray() + nLen, aCharArray, nReadBytes);
     }
     return aData;
 }
@@ -422,106 +422,94 @@ Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection,
     case SQL_WVARCHAR:
     case SQL_WCHAR:
     case SQL_WLONGVARCHAR:
+    {
+        sal_Unicode waCharArray[2048];
+        // we assume everyone (LibO & ODBC) uses UTF-16; see OPreparedStatement::setParameter
+        BOOST_STATIC_ASSERT(sizeof(sal_Unicode) == 2);
+        BOOST_STATIC_ASSERT(sizeof(SQLWCHAR)    == 2);
+        // read the unicode data
+        const SQLLEN nMaxLen = sizeof(waCharArray) / sizeof(sal_Unicode);
+        SQLLEN pcbValue = SQL_NO_TOTAL;
+
+        while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxLen) )
         {
-            sal_Unicode waCharArray[2048];
-            // read the unicode data
-            SQLLEN nMaxLen = (sizeof(waCharArray) / sizeof(sal_Unicode)) - 1;
-
-            SQLLEN pcbValue=0;
-            OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                                (SQLUSMALLINT)columnIndex,
-                                                SQL_C_WCHAR,
-                                                &waCharArray,
-                                                (SQLLEN)nMaxLen*sizeof(sal_Unicode),
-                                                &pcbValue),
-                                    _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
+            OTools::ThrowException(_pConnection,
+                                   (*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(
+                                       _aStatementHandle,
+                                       (SQLUSMALLINT)columnIndex,
+                                       SQL_C_WCHAR,
+                                       &waCharArray,
+                                       (SQLLEN)nMaxLen*sizeof(sal_Unicode),
+                                       &pcbValue),
+                                   _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
             _bWasNull = pcbValue == SQL_NULL_DATA;
             if(_bWasNull)
                 return ::rtl::OUString();
-            // at failure the GETDATA-Makro will stop with returning,
-            // at NULL with break!
-            SQLLEN nRealSize = 0;
-            if ( pcbValue > -1 )
-                nRealSize = pcbValue / sizeof(sal_Unicode);
-            SQLLEN nLen = pcbValue != SQL_NO_TOTAL ? std::min(nRealSize, nMaxLen) : (nMaxLen-1);
-            waCharArray[nLen] = 0;
-            aData.append(waCharArray,nLen);
-
-            // It is about Binariy Data, a String, that for StarView is to long or
-            // the driver kan't predict the length of the data - as well as save the
-            // MemoryStream.
-            while ((pcbValue == SQL_NO_TOTAL ) || nLen > nMaxLen)
+
+            SQLLEN nReadChars;
+            if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) )
             {
-                // At Strings the Buffer won't be completly used
-                // (The last Byte is always a NULL-Byte, however it won't be counted with pcbValue)
-                if (pcbValue != SQL_NO_TOTAL && (pcbValue - nMaxLen) < nMaxLen)
-                    nLen = pcbValue - nMaxLen;
-                else
-                    nLen = nMaxLen;
-
-                // While there is a "truncation"-Warning, proceed with fetching Data.
-                OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                                (SQLUSMALLINT)columnIndex,
-                                                SQL_C_WCHAR,
-                                                &waCharArray,
-                                                (SQLLEN)nLen+1,
-                                                &pcbValue),
-                                    _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
-                nRealSize = 0;
-                if ( pcbValue > -1 )
-                    nRealSize = pcbValue / sizeof(sal_Unicode);
-                nLen = pcbValue != SQL_NO_TOTAL ? std::min(nRealSize, nMaxLen) : (nMaxLen-1);
-                waCharArray[nLen] = 0;
-
-                aData.append(::rtl::OUString(waCharArray));
+                // we filled the buffer; remove the terminating null character
+                nReadChars = nMaxLen-1;
+                if ( waCharArray[nReadChars] != 0)
+                {
+                    OSL_FAIL("Buggy ODBC driver? Did not null-terminate (variable length) data!");
+                    ++nReadChars;
+                }
             }
+            else
+            {
+                nReadChars = pcbValue;
+            }
+
+            aData.append(waCharArray, nReadChars);
+
         }
         break;
-        default:
+    }
+    default:
+    {
+        char aCharArray[2048];
+        // read the unicode data
+        const SQLLEN nMaxLen = sizeof(aCharArray);
+        SQLLEN pcbValue = SQL_NO_TOTAL;
+
+        while ((pcbValue == SQL_NO_TOTAL ) || (pcbValue >= nMaxLen) )
         {
-            char aCharArray[2048];
-            // First try to fetch the data with the little Buffer:
-            SQLLEN nMaxLen = sizeof aCharArray - 1;
-            SQLLEN pcbValue = 0;
-            OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                                (SQLUSMALLINT)columnIndex,
-                                                SQL_C_CHAR,
-                                                &aCharArray,
-                                                nMaxLen,
-                                                &pcbValue),
-                                    _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
+            OTools::ThrowException(_pConnection,
+                                   (*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(
+                                       _aStatementHandle,
+                                       (SQLUSMALLINT)columnIndex,
+                                       SQL_C_CHAR,
+                                       &aCharArray,
+                                       nMaxLen,
+                                       &pcbValue),
+                                   _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
             _bWasNull = pcbValue == SQL_NULL_DATA;
             if(_bWasNull)
                 return ::rtl::OUString();
 
-            SQLLEN nLen = pcbValue != SQL_NO_TOTAL ? std::min(pcbValue, nMaxLen) : (nMaxLen-1);
-            aCharArray[nLen] = 0;
-            if ( ((pcbValue == SQL_NO_TOTAL) || pcbValue > nMaxLen) && aCharArray[nLen-1] == 0 && nLen > 0 )
-                --nLen;
-            aData.append(::rtl::OUString((const sal_Char*)aCharArray,nLen, _nTextEncoding));
-
-            // It is about Binary Data, a String, that for StarView is too long or
-            // the driver can't predict the length of the data - as well as save the
-            // MemoryStream.
-            while ((pcbValue == SQL_NO_TOTAL) || pcbValue > nMaxLen)
+            SQLLEN nReadChars;
+            if ( (pcbValue == SQL_NO_TOTAL) || (pcbValue >= nMaxLen) )
+            {
+                // we filled the buffer; remove the terminating null character
+                nReadChars = nMaxLen-1;
+                if ( aCharArray[nReadChars] != 0)
+                {
+                    OSL_FAIL("Buggy ODBC driver? Did not null-terminate (variable length) data!");
+                    ++nReadChars;
+                }
+            }
+            else
             {
-                // While there is a "truncation"-Warning, proceed with fetching Data.
-                OTools::ThrowException(_pConnection,(*(T3SQLGetData)_pConnection->getOdbcFunction(ODBC3SQLGetData))(_aStatementHandle,
-                                                (SQLUSMALLINT)columnIndex,
-                                                SQL_C_CHAR,
-                                                &aCharArray,
-                                                (SQLINTEGER)nMaxLen,
-                                                &pcbValue),
-                                    _aStatementHandle,SQL_HANDLE_STMT,_xInterface);
-                nLen = pcbValue != SQL_NO_TOTAL ? std::min(pcbValue, nMaxLen) : (nMaxLen-1);
-                if ( ((pcbValue == SQL_NO_TOTAL) || pcbValue > nMaxLen) && aCharArray[nLen-1] == 0 && nLen > 0 )
-                    --nLen;
-                aCharArray[nLen] = 0;
-
-                aData.append(::rtl::OUString((const sal_Char*)aCharArray,nLen,_nTextEncoding));
+                nReadChars = pcbValue;
             }
 
+            aData.append(::rtl::OUString(aCharArray, nReadChars, _nTextEncoding));
+
         }
+        break;
+    }
     }
 
     return aData.makeStringAndClear();
commit 6f554567af933f1f437792aae02e378e545ecce4
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:05:58 2012 +0100

    stylistic: use sal_Int8 rather than char for binary data
    
    Change-Id: Ic3c7d8d2a27e1835513790523ddd3bdc8f7c2101

diff --git a/connectivity/source/drivers/odbcbase/OTools.cxx b/connectivity/source/drivers/odbcbase/OTools.cxx
index 7c2630e..8533152 100644
--- a/connectivity/source/drivers/odbcbase/OTools.cxx
+++ b/connectivity/source/drivers/odbcbase/OTools.cxx
@@ -358,7 +358,7 @@ Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection,
                                          const Reference< XInterface >& _xInterface) throw(SQLException, RuntimeException)
 {
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "odbc", "Ocke.Janssen at sun.com", "OTools::getBytesValue" );
-    char aCharArray[2048];
+    sal_Int8 aCharArray[2048];
     // First try to fetch the data with the little Buffer:
     SQLLEN nMaxLen = sizeof aCharArray - 1;
     //  GETDATA(SQL_C_CHAR,aCharArray,nMaxLen);
commit 4500daf6aa91b0019234a8e2dd70a830b4654c13
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:04:22 2012 +0100

    const-ify arguments of some OTools function members
    
    Change-Id: Ie19a5277a9b847a9e440d137cf7ee18943c77832

diff --git a/connectivity/source/drivers/odbcbase/OTools.cxx b/connectivity/source/drivers/odbcbase/OTools.cxx
index 90d0791..7c2630e 100644
--- a/connectivity/source/drivers/odbcbase/OTools.cxx
+++ b/connectivity/source/drivers/odbcbase/OTools.cxx
@@ -290,13 +290,13 @@ void OTools::bindValue( OConnection* _pConnection,
     OTools::ThrowException(_pConnection,nRetcode,_aStatementHandle,SQL_HANDLE_STMT,_xInterface);
 }
 // -----------------------------------------------------------------------------
-void OTools::ThrowException(OConnection* _pConnection,
-                            SQLRETURN _rRetCode,
-                            SQLHANDLE _pContext,
-                            SQLSMALLINT _nHandleType,
+void OTools::ThrowException(const OConnection* _pConnection,
+                            const SQLRETURN _rRetCode,
+                            const SQLHANDLE _pContext,
+                            const SQLSMALLINT _nHandleType,
                             const Reference< XInterface >& _xInterface,
-                            sal_Bool _bNoFound,
-                            rtl_TextEncoding _nTextEncoding) throw(SQLException)
+                            const sal_Bool _bNoFound,
+                            const rtl_TextEncoding _nTextEncoding) throw(SQLException)
 {
     switch(_rRetCode)
     {
@@ -350,10 +350,10 @@ void OTools::ThrowException(OConnection* _pConnection,
 
 }
 // -------------------------------------------------------------------------
-Sequence<sal_Int8> OTools::getBytesValue(OConnection* _pConnection,
-                                         SQLHANDLE _aStatementHandle,
-                                         sal_Int32 columnIndex,
-                                         SQLSMALLINT _fSqlType,
+Sequence<sal_Int8> OTools::getBytesValue(const OConnection* _pConnection,
+                                         const SQLHANDLE _aStatementHandle,
+                                         const sal_Int32 columnIndex,
+                                         const SQLSMALLINT _fSqlType,
                                          sal_Bool &_bWasNull,
                                          const Reference< XInterface >& _xInterface) throw(SQLException, RuntimeException)
 {
diff --git a/connectivity/source/inc/odbc/OTools.hxx b/connectivity/source/inc/odbc/OTools.hxx
index a971d4c..68278d6 100644
--- a/connectivity/source/inc/odbc/OTools.hxx
+++ b/connectivity/source/inc/odbc/OTools.hxx
@@ -97,7 +97,7 @@ namespace connectivity
         class OOO_DLLPUBLIC_ODBCBASE OTools
         {
         public:
-            static void ThrowException( OConnection* _pConnection,
+            static void ThrowException( const OConnection* _pConnection,
                                         SQLRETURN _rRetCode,
                                         SQLHANDLE _pContext,
                                         SQLSMALLINT _nHandleType,
@@ -191,7 +191,7 @@ namespace connectivity
                                                     const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& _xInterface,
                                                     rtl_TextEncoding _nTextEncoding) throw(::com::sun::star::sdbc::SQLException, ::com::sun::star::uno::RuntimeException);
 
-            static  ::com::sun::star::uno::Sequence<sal_Int8> getBytesValue(OConnection* _pConnection,
+            static  ::com::sun::star::uno::Sequence<sal_Int8> getBytesValue(const OConnection* _pConnection,
                                                                             SQLHANDLE _aStatementHandle,
                                                                             sal_Int32 columnIndex,
                                                                             SQLSMALLINT _fSqlType,
commit d1b8e44c548b8cabaa319198bf4c3be3452d7100
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 06:00:52 2012 +0100

    Variable-sized datatype -> cannot predict length of needed buffer
    
    Change-Id: I29b8c0352b06fb86e924aaf8108de6835eb9cb89

diff --git a/connectivity/source/drivers/odbcbase/OTools.cxx b/connectivity/source/drivers/odbcbase/OTools.cxx
index 3e32780..90d0791 100644
--- a/connectivity/source/drivers/odbcbase/OTools.cxx
+++ b/connectivity/source/drivers/odbcbase/OTools.cxx
@@ -41,10 +41,6 @@ size_t sqlTypeLen ( SQLSMALLINT _nType )
 {
     switch (_nType)
     {
-    case SQL_C_CHAR:
-        return sizeof(SQLCHAR *);
-    case SQL_C_WCHAR:
-        return sizeof(SQLWCHAR *);
     case SQL_C_SSHORT:
     case SQL_C_SHORT:
         return sizeof(SQLSMALLINT);
@@ -74,10 +70,6 @@ size_t sqlTypeLen ( SQLSMALLINT _nType )
     /* UnixODBC gives this the same value as SQL_C_UBIGINT
     case SQL_C_BOOKMARK:
         return sizeof(BOOKMARK); */
-    case SQL_C_BINARY:
-    // UnixODBC gives these the same value
-    //case SQL_C_VARBOOKMARK:
-        return sizeof(SQLCHAR*);
     case SQL_C_TYPE_DATE:
     case SQL_C_DATE:
         return sizeof(SQL_DATE_STRUCT);
@@ -105,6 +97,13 @@ size_t sqlTypeLen ( SQLSMALLINT _nType )
     case SQL_C_INTERVAL_HOUR_TO_SECOND:
     case SQL_C_INTERVAL_MINUTE_TO_SECOND:
         return sizeof(SQL_INTERVAL_STRUCT);
+    // ** Variable-sized datatypes -> cannot predict length
+    case SQL_C_CHAR:
+    case SQL_C_WCHAR:
+    case SQL_C_BINARY:
+    // UnixODBC gives this the same value as SQL_C_BINARY
+    //case SQL_C_VARBOOKMARK:
+    // Unknown datatype -> cannot predict length
     default:
         return static_cast<size_t>(-1);
     }
commit 752270bceb5f736c71ea116822b9a5bbb4a87629
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 05:58:54 2012 +0100

    comment on our usage of SQLGetData extensions
    
    Change-Id: I9580fa4828db8f19b84ce8f88f8cbc7ab72985b6

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index fbad2df..b40361c 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -134,6 +134,13 @@ OResultSet::OResultSet(SQLHANDLE _pStatementHandle ,OStatement_Base* pStmt) :
     try
     {
         SQLUINTEGER nValueLen = 0;
+        // 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?
+        // 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.
         OTools::GetInfo(m_pStatement->getOwnConnection(),m_aConnectionHandle,SQL_GETDATA_EXTENSIONS,nValueLen,NULL);
         m_bFetchData = !((SQL_GD_ANY_ORDER & nValueLen) == SQL_GD_ANY_ORDER && nCurType != SQL_CURSOR_FORWARD_ONLY);
     }
commit 07b4dd2e44cacb3f473259260d0d2b59a3a370c0
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 05:58:21 2012 +0100

    statically assert our own ODBC_SQL_NOT_DEFINED does not conflict with SQL_UB_*
    
    Change-Id: I2f571e06fd498ebe9378105030215ccb134bf974

diff --git a/connectivity/source/drivers/odbcbase/OResultSet.cxx b/connectivity/source/drivers/odbcbase/OResultSet.cxx
index 4a04f06..fbad2df 100644
--- a/connectivity/source/drivers/odbcbase/OResultSet.cxx
+++ b/connectivity/source/drivers/odbcbase/OResultSet.cxx
@@ -36,6 +36,7 @@
 #include "connectivity/dbexception.hxx"
 #include "diagnose_ex.h"
 #include <rtl/logfile.hxx>
+#include <boost/static_assert.hpp>
 
 #include <o3tl/compat_functional.hxx>
 
@@ -53,6 +54,10 @@ using namespace com::sun::star::io;
 using namespace com::sun::star::util;
 
 #define ODBC_SQL_NOT_DEFINED    99UL
+BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_OFF );
+BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_ON );
+BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_FIXED );
+BOOST_STATIC_ASSERT( ODBC_SQL_NOT_DEFINED != SQL_UB_VARIABLE );
 
 //------------------------------------------------------------------------------
 //  IMPLEMENT_SERVICE_INFO(OResultSet,"com.sun.star.sdbcx.OResultSet","com.sun.star.sdbc.ResultSet");
commit d45cbcd1203776bc9d42e528541eae5f0086561b
Author: Lionel Elie Mamane <lionel at mamane.lu>
Date:   Wed Dec 5 05:57:07 2012 +0100

    statically assert that ODBC uses UTF-16
    
    Change-Id: I13a8a152d7bfba351632e50d440fba8af375bec3

diff --git a/connectivity/source/drivers/odbcbase/OPreparedStatement.cxx b/connectivity/source/drivers/odbcbase/OPreparedStatement.cxx
index f5c05b8..1e46793 100644
--- a/connectivity/source/drivers/odbcbase/OPreparedStatement.cxx
+++ b/connectivity/source/drivers/odbcbase/OPreparedStatement.cxx
@@ -334,6 +334,7 @@ void OPreparedStatement::setParameter(const sal_Int32 parameterIndex, const sal_
          * Our internal OUString storage is always UTF-16, so no conversion to do here.
          */
         BOOST_STATIC_ASSERT( sizeof(sal_Unicode) == 2 );
+        BOOST_STATIC_ASSERT( sizeof(SQLWCHAR)    == 2 );
         nCharLen = _sData.getLength();
         nByteLen = nCharLen * sizeof(sal_Unicode);
         pData = allocBindBuf(parameterIndex, nByteLen);


More information about the Libreoffice-commits mailing list