Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/dbaccess/source/core/api/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 62 kB image not shown  

Quelle  RowSetCache.cxx   Sprache: C

 
/* -*- 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 <memory>
#include "BookmarkSet.hxx"
#include "KeySet.hxx"
#include "OptimisticSet.hxx"
#include "RowSetBase.hxx"
#include "RowSetCache.hxx"
#include "StaticSet.hxx"
#include "WrappedResultSet.hxx"
#include <core_resource.hxx>
#include <strings.hrc>
#include <strings.hxx>

#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>
#include <com/sun/star/sdbcx/CompareBookmark.hpp>
#include <com/sun/star/sdbcx/Privilege.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>

#include <comphelper/extract.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbexception.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sqliterator.hxx>
#include <connectivity/sqlnode.hxx>
#include <connectivity/sqlparse.hxx>
#include <sqlbison.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>

#include <algorithm>

using namespace dbaccess;
using namespace dbtools;
using namespace connectivity;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::container;
using namespace ::cppu;

// This class calls m_pCacheSet->FOO_checked(..., sal_False)
// (where FOO is absolute, last, previous)
// when it does not immediately care about the values in the row's columns.
// As a corollary, m_pCacheSet may be left in an inconsistent state,
// and all ->fillFOO calls (and ->getFOO) may fail or give wrong results,
// until m_pCacheSet is moved (or refreshed) again.
// So always make sure m_pCacheSet is moved or refreshed before accessing column values.


ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs,
                           const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
                           const Reference<XComponentContext>& _rContext,
                           const OUString& _rUpdateTableName,
                           bool&    _bModified,
                           bool&    _bNew,
                           const ORowSetValueVector& _aParameterValueForCache,
                           const OUString& i_sRowSetFilter,
                           sal_Int32 i_nMaxRows)
    :m_xSet(_xRs)
    ,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY_THROW)->getMetaData())
    ,m_aContext( _rContext )
    ,m_nFetchSize(0)
    ,m_nRowCount(0)
    ,m_nPrivileges( Privilege::SELECT )
    ,m_nPosition(0)
    ,m_nStartPos(0)
    ,m_nEndPos(0)
    ,m_bRowCountFinal(false)
    ,m_bBeforeFirst(true)
    ,m_bAfterLast( false )
    ,m_bModified(_bModified)
    ,m_bNew(_bNew)
{

    // first try if the result can be used to do inserts and updates
    Reference< XPropertySet> xProp(_xRs,UNO_QUERY);
    Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo();
    bool bBookmarkable = false;
    try
    {
        Reference< XResultSetUpdate> xUp(_xRs,UNO_QUERY_THROW);
        bBookmarkable = xPropInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE) &&
                                any2bool(xProp->getPropertyValue(PROPERTY_ISBOOKMARKABLE)) && Reference< XRowLocate >(_xRs, UNO_QUERY).is();
        if ( bBookmarkable )
        {
            xUp->moveToInsertRow();
            xUp->cancelRowUpdates();
            _xRs->beforeFirst();
            m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
            m_xCacheSet = new WrappedResultSet(i_nMaxRows);
            m_xCacheSet->construct(_xRs,i_sRowSetFilter);
            return;
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess.core");
    }
    try
    {
        if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) &&
                            ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY)
            _xRs->beforeFirst();
    }
    catch(const SQLException&)
    {
        TOOLS_WARN_EXCEPTION("dbaccess.core""ORowSetCache");
    }

    // check if all keys of the updateable table are fetched
    bool bAllKeysFound = false;
    sal_Int32 nTablesCount = 0;

    bool bNeedKeySet = !bBookmarkable || (xPropInfo->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
                            ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY);

    OUString aUpdateTableName = _rUpdateTableName;
    Reference< XConnection> xConnection;
    // first we need a connection
    Reference< XStatement> xStmt(_xRs->getStatement(),UNO_QUERY);
    if(xStmt.is())
        xConnection = xStmt->getConnection();
    else
    {
        Reference< XPreparedStatement> xPrepStmt(_xRs->getStatement(),UNO_QUERY);
        xConnection = xPrepStmt->getConnection();
    }
    OSL_ENSURE(xConnection.is(),"No connection!");
    if(_xAnalyzer.is())
    {
        try
        {
            Reference<XTablesSupplier> xTabSup(_xAnalyzer,UNO_QUERY);
            OSL_ENSURE(xTabSup.is(),"ORowSet::execute composer isn't a tablesupplier!");
            Reference<XNameAccess> xTables = xTabSup->getTables();
            Sequence< OUString> aTableNames = xTables->getElementNames();
            if ( aTableNames.getLength() > 1 && _rUpdateTableName.isEmpty() && bNeedKeySet )
            {// here we have a join or union and nobody told us which table to update, so we update them all
                m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
                rtl::Reference<OptimisticSet> pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
                m_xCacheSet = pCursor;
                try
                {
                    m_xCacheSet->construct(_xRs,i_sRowSetFilter);
                    if ( pCursor->isReadOnly() )
                        m_nPrivileges = Privilege::SELECT;
                    m_aKeyColumns = pCursor->getJoinedKeyColumns();
                    return;
                }
                catch (const Exception&)
                {
                    TOOLS_WARN_EXCEPTION("dbaccess.core""ORowSetCache");
                }
                m_xCacheSet.clear();
            }
            else
            {
                if(!_rUpdateTableName.isEmpty() && xTables->hasByName(_rUpdateTableName))
                    xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable;
                else if(xTables->getElementNames().hasElements())
                {
                    aUpdateTableName = xTables->getElementNames()[0];
                    xTables->getByName(aUpdateTableName) >>= m_aUpdateTable;
                }
                Reference<XIndexAccess> xIndexAccess(xTables,UNO_QUERY);
                if(xIndexAccess.is())
                    nTablesCount = xIndexAccess->getCount();
                else
                    nTablesCount = xTables->getElementNames().getLength();

                if(m_aUpdateTable.is() && nTablesCount < 3) // for we can't handle more than 2 tables in our keyset
                {
                    Reference<XPropertySet> xSet(m_aUpdateTable,UNO_QUERY);
                    const Reference<XNameAccess> xPrimaryKeyColumns = dbtools::getPrimaryKeyColumns_throw(xSet);
                    if ( xPrimaryKeyColumns.is() )
                    {
                        Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
                        if ( xColSup.is() )
                        {
                            Reference<XNameAccess> xSelColumns = xColSup->getColumns();
                            Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
                            SelectColumnsMetaData aColumnNames(comphelper::UStringMixLess(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()));
                            ::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
                            bAllKeysFound = !aColumnNames.empty() && aColumnNames.size() == o3tl::make_unsigned(xPrimaryKeyColumns->getElementNames().getLength());
                        }
                    }
                }
            }
        }
        catch (Exception const&)
        {
            TOOLS_WARN_EXCEPTION("dbaccess.core""ORowSetCache");
        }
    }

    // first check if resultset is bookmarkable
    if(!bNeedKeySet)
    {
        try
        {
            m_xCacheSet = new OBookmarkSet(i_nMaxRows);
            m_xCacheSet->construct(_xRs,i_sRowSetFilter);

            // check privileges
            m_nPrivileges = Privilege::SELECT;
            if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is())  // this interface is optional so we have to check it
            {
                Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
                if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
                {
                    m_nPrivileges = 0;
                    xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
                    if(!m_nPrivileges)
                        m_nPrivileges = Privilege::SELECT;
                }
            }
        }
        catch (const SQLException&)
        {
            TOOLS_WARN_EXCEPTION("dbaccess.core""ORowSetCache");
            bNeedKeySet = true;
        }

    }
    if(bNeedKeySet)
    {
        // need to check if we could handle this select clause
        bAllKeysFound = bAllKeysFound && (nTablesCount == 1 || checkJoin(xConnection,_xAnalyzer,aUpdateTableName));

        if(!bAllKeysFound )
        {
            if ( bBookmarkable )
            {
                // here I know that we have a read only bookmarkable cursor
                _xRs->beforeFirst();
                m_nPrivileges = Privilege::SELECT;
                m_xCacheSet = new WrappedResultSet(i_nMaxRows);
                m_xCacheSet->construct(_xRs,i_sRowSetFilter);
                return;
            }
            m_xCacheSet = new OStaticSet(i_nMaxRows);
            m_xCacheSet->construct(_xRs,i_sRowSetFilter);
            m_nPrivileges = Privilege::SELECT;
        }
        else
        {
            Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
            SelectColumnsMetaData aColumnNames(comphelper::UStringMixLess(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()));
            Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
            Reference<XNameAccess> xSelColumns  = xColSup->getColumns();
            Reference<XNameAccess> xColumns     = m_aUpdateTable->getColumns();
            ::dbaccess::getColumnPositions(xSelColumns,xColumns->getElementNames(),aUpdateTableName,aColumnNames);

            // check privileges
            m_nPrivileges = Privilege::SELECT;
            bool bNoInsert = false;

            for (auto& columnName : xColumns->getElementNames())
            {
                Reference<XPropertySet> xColumn(xColumns->getByName(columnName), UNO_QUERY);
                OSL_ENSURE(xColumn.is(),"Column in table is null!");
                if(xColumn.is())
                {
                    sal_Int32 nNullable = 0;
                    xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
                    if(nNullable == ColumnValue::NO_NULLS && aColumnNames.find(columnName) == aColumnNames.end())
                    { // we found a column where null is not allowed so we can't insert new values
                        bNoInsert = true;
                        break// one column is enough
                    }
                }
            }

            rtl::Reference<OKeySet> pKeySet = new OKeySet(m_aUpdateTable, aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
            try
            {
                m_xCacheSet = pKeySet;
                pKeySet->construct(_xRs,i_sRowSetFilter);

                if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is())  // this interface is optional so we have to check it
                {
                    Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
                    if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
                    {
                        m_nPrivileges = 0;
                        xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
                        if(!m_nPrivileges)
                            m_nPrivileges = Privilege::SELECT;
                    }
                }
                if(bNoInsert)
                    m_nPrivileges |= ~Privilege::INSERT; // remove the insert privilege
            }
            catch (const SQLException&)
            {
                TOOLS_WARN_EXCEPTION("dbaccess.core""ORowSetCache");
                // we couldn't create a keyset here so we have to create a static cache
                m_xCacheSet = new OStaticSet(i_nMaxRows);
                m_xCacheSet->construct(_xRs,i_sRowSetFilter);
                m_nPrivileges = Privilege::SELECT;
            }
        }

    }
    // last check
    if(!bAllKeysFound && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
        ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY)
        m_nPrivileges = Privilege::SELECT;
}

ORowSetCache::~ORowSetCache()
{
    m_xCacheSet.clear();
    if(m_pMatrix)
    {
        m_pMatrix->clear();
        m_pMatrix.reset();
    }

    if(m_pInsertMatrix)
    {
        m_pInsertMatrix->clear();
        m_pInsertMatrix.reset();
    }
    m_xSet          = WeakReference< XResultSet>();
    m_xMetaData     = nullptr;
    m_aUpdateTable  = nullptr;
}

void ORowSetCache::setFetchSize(sal_Int32 _nSize)
{
    if(_nSize == m_nFetchSize)
        return;

    m_nFetchSize = _nSize;
    if(!m_pMatrix)
    {
        m_pMatrix.reset( new ORowSetMatrix(_nSize) );
        m_aMatrixIter = m_pMatrix->end();
        m_aMatrixEnd = m_pMatrix->end();

        m_pInsertMatrix.reset( new ORowSetMatrix(1) ); // a little bit overkill but ??? :-)
        m_aInsertRow    = m_pInsertMatrix->end();
    }
    else
    {
        // now correct the iterator in our iterator vector
        std::vector<sal_Int32> aPositions;
        std::map<sal_Int32,bool> aCacheIterToChange;
        // first get the positions where they stand now
        for(const auto& [rIndex, rHelper] : m_aCacheIterators)
        {
            aCacheIterToChange[rIndex] = false;
            if ( !rHelper.pRowSet->isInsertRow()
                /*&& rHelper.aIterator != m_pMatrix->end()*/ && !m_bModified )
            {
                ptrdiff_t nDist = rHelper.aIterator - m_pMatrix->begin();
                aPositions.push_back(nDist);
                aCacheIterToChange[rIndex] = true;
            }
        }
        sal_Int32 nKeyPos = m_aMatrixIter - m_pMatrix->begin();
        m_pMatrix->resize(_nSize);

        if ( nKeyPos < _nSize )
            m_aMatrixIter = m_pMatrix->begin() + nKeyPos;
        else
            m_aMatrixIter = m_pMatrix->end();
        m_aMatrixEnd = m_pMatrix->end();

        // now adjust their positions because a resize invalidates all iterators
        std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
        ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
        for(const auto& rPosChange : aCacheIterToChange)
        {
            if ( rPosChange.second )
            {
                OSL_ENSURE((*aIter >= static_cast<ORowSetMatrix::difference_type>(0)) && (*aIter < static_cast<sal_Int32>(m_pMatrix->size())),"Position is invalid!");
                if ( *aIter < _nSize )
                    aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++;
                else
                    aCacheIter->second.aIterator = m_pMatrix->end();
            }
            ++aCacheIter;
        }
    }
    if(!m_nPosition)
    {
        sal_Int32 nNewSt = 0;
        fillMatrix(nNewSt,_nSize);
        OSL_ENSURE(nNewSt == 0, "fillMatrix set new start to unexpected value");
        m_nStartPos = 0;
        m_nEndPos = _nSize;
    }
    else if (m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos)
    {
        sal_Int32 nNewSt = -1;
        _nSize += m_nStartPos;
        fillMatrix(nNewSt, _nSize);
        if (nNewSt >= 0)
        {
            m_nStartPos = nNewSt;
            m_nEndPos =  _nSize;
            m_aMatrixIter = calcPosition();
        }
        else
        {
            m_nEndPos = m_nStartPos + m_nFetchSize;
        }
    }
    else
    {
        OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos");
        // try to repair
        moveWindow();
        m_aMatrixIter = calcPosition();
    }
}

// XResultSetMetaDataSupplier

static Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
{
    switch ( i_aValue.getTypeKind() )
    {
        case DataType::TINYINT:
        case DataType::SMALLINT:
        case DataType::INTEGER:
            return Any(i_aValue.getInt32());
        default:
            if ( i_pCacheSet && i_aValue.isNull())
                i_aValue = i_pCacheSet->getBookmark();
            return i_aValue.getAny();
    }
}

// css::sdbcx::XRowLocate
Any ORowSetCache::getBookmark(  )
{
    if(m_bAfterLast)
        throwFunctionSequenceException(m_xSet.get());

    if ( m_aMatrixIter >= m_pMatrix->end() || m_aMatrixIter < m_pMatrix->begin() || !(*m_aMatrixIter).is())
    {
        return Any(); // this is allowed here because the rowset knows what it is doing
    }

    return lcl_getBookmark((**m_aMatrixIter)[0],m_xCacheSet.get());
}

bool ORowSetCache::moveToBookmark( const Any& bookmark )
{
    if ( m_xCacheSet->moveToBookmark(bookmark) )
    {
        m_bBeforeFirst = false;
        m_nPosition = m_xCacheSet->getRow();

        checkPositionFlags();

        if(!m_bAfterLast)
        {
            moveWindow();
            checkPositionFlags();
            if ( !m_bAfterLast )
            {
                m_aMatrixIter = calcPosition();
                OSL_ENSURE(m_aMatrixIter->is(),"Iterator after moveToBookmark not valid");
            }
            else
                m_aMatrixIter = m_pMatrix->end();
        }
        else
            m_aMatrixIter = m_pMatrix->end();
    }
    else
        return false;

    return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
}

bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
{
    bool bRet( moveToBookmark( bookmark ) );
    if ( bRet )
    {
        m_nPosition = m_xCacheSet->getRow() + rows;
        absolute(m_nPosition);

        bRet = m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
    }

    return bRet;
}

sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second )
{
    return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_xCacheSet->compareBookmarks(_first,_second);
}

bool ORowSetCache::hasOrderedBookmarks(  )
{
    return m_xCacheSet->hasOrderedBookmarks();
}

sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark )
{
    return m_xCacheSet->hashBookmark(bookmark);
}

// XRowUpdate
void ORowSetCache::updateNull(sal_Int32 columnIndex,ORowSetValueVector::Vector& ;io_aRow
                              ,std::vector<sal_Int32>& o_ChangedColumns
                              )
{
    checkUpdateConditions(columnIndex);

    ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
    if ( !rInsert[columnIndex].isNull() )
    {
        rInsert[columnIndex].setBound(true);
        rInsert[columnIndex].setNull();
        rInsert[columnIndex].setModified(true);
        io_aRow[columnIndex].setNull();

        m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
        impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
    }
}

void ORowSetCache::updateValue(sal_Int32 columnIndex,const ORowSetValue& x
                               ,ORowSetValueVector::Vector& io_aRow
                               ,std::vector<sal_Int32>& o_ChangedColumns
                               )
{
    checkUpdateConditions(columnIndex);

    ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
    if ( rInsert[columnIndex] != x )
    {
        rInsert[columnIndex].setBound(true);
        rInsert[columnIndex] = x;
        rInsert[columnIndex].setModified(true);
        io_aRow[columnIndex] = rInsert[columnIndex];

        m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
        impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
    }
}

void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x
                                         , sal_Int32 length,ORowSetValueVector::Vector& io_aRow
                                         ,std::vector<sal_Int32>& o_ChangedColumns
                                         )
{
    checkUpdateConditions(columnIndex);

    Sequence<sal_Int8> aSeq;
    if(x.is())
        x->readBytes(aSeq,length);

    ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
    rInsert[columnIndex].setBound(true);
    rInsert[columnIndex] = aSeq;
    rInsert[columnIndex].setModified(true);
    io_aRow[columnIndex] = Any(x);

    m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
    impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
}

void ORowSetCache::updateObject( sal_Int32 columnIndex, const Any& x
                                ,ORowSetValueVector::Vector& io_aRow
                                ,std::vector<sal_Int32>& o_ChangedColumns
                                )
{
    checkUpdateConditions(columnIndex);

    ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
    ORowSetValue aTemp;
    aTemp.fill(x);
    if ( rInsert[columnIndex] != aTemp )
    {
        rInsert[columnIndex].setBound(true);
        rInsert[columnIndex] = std::move(aTemp);
        rInsert[columnIndex].setModified(true);
        io_aRow[columnIndex] = rInsert[columnIndex];

        m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
        impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
    }
}

void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x
                                       ,ORowSetValueVector::Vector& io_aRow
                                       ,std::vector<sal_Int32>& o_ChangedColumns
                                       )
{
    checkUpdateConditions(columnIndex);

    ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
    ORowSetValue aTemp;
    aTemp.fill(x);
    if ( rInsert[columnIndex] != aTemp )
    {
        rInsert[columnIndex].setBound(true);
        rInsert[columnIndex] = std::move(aTemp);
        rInsert[columnIndex].setModified(true);
        io_aRow[columnIndex] = rInsert[columnIndex];

        m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
        impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
    }
}

// XResultSet
bool ORowSetCache::next(  )
{
    if(!isAfterLast())
    {
        m_bBeforeFirst = false;
        ++m_nPosition;

        // after we increment the position we have to check if we are already after the last row
        checkPositionFlags();
        if(!m_bAfterLast)
        {
            moveWindow();

            OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
            m_aMatrixIter = calcPosition();
            checkPositionFlags();
        }
    }

    return !m_bAfterLast;
}


bool ORowSetCache::isFirst(  ) const
{
    return m_nPosition == 1; // ask resultset for
}

bool ORowSetCache::isLast(  ) const
{
    return m_nPosition == m_nRowCount;
}

void ORowSetCache::beforeFirst(  )
{
    if(!m_bBeforeFirst)
    {
        m_bAfterLast    = false;
        m_nPosition     = 0;
        m_bBeforeFirst  = true;
        m_xCacheSet->beforeFirst();
        moveWindow();
        m_aMatrixIter = m_pMatrix->end();
    }
}

void ORowSetCache::afterLast(  )
{
    if(m_bAfterLast)
        return;

    m_bBeforeFirst = false;
    m_bAfterLast = true;

    if(!m_bRowCountFinal)
    {
        m_xCacheSet->last();
        m_bRowCountFinal = true;
        m_nRowCount = m_xCacheSet->getRow();// + 1 removed
    }
    m_xCacheSet->afterLast();

    m_nPosition = 0;
    m_aMatrixIter = m_pMatrix->end();
}

bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos, sal_Int32 &_nNewEndPos)
{
    OSL_ENSURE((_nNewStartPos != _nNewEndPos) || (_nNewStartPos == 0 && _nNewEndPos == 0 && m_nRowCount == 0),
               "ORowSetCache::fillMatrix: StartPos and EndPos can not be equal (unless the recordset is empty)!");
    // If _nNewStartPos >= 0, then fill the whole window with new data
    // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos)
    // Else, undefined (invalid argument)
    OSL_ENSURE( _nNewStartPos >= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );

    ORowSetMatrix::iterator aIter;
    sal_Int32 i;
    bool bCheck;
    sal_Int32 requestedStartPos;
    if ( _nNewStartPos == -1 )
    {
        aIter = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
        i = m_nEndPos + 1;
        requestedStartPos = m_nStartPos;
    }
    else
    {
        aIter = m_pMatrix->begin();
        i = _nNewStartPos + 1;
        requestedStartPos = _nNewStartPos;
    }
    bCheck = m_xCacheSet->absolute(i);


    for(; i <= _nNewEndPos; ++i,++aIter)
    {
        if(bCheck)
        {
            if(!aIter->is())
                *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
            m_xCacheSet->fillValueRow(*aIter,i);
        }
        else
        {   // there are no more rows found so we can fetch some before start

            if(!m_bRowCountFinal)
            {
                if(m_xCacheSet->previous()) // because we stand after the last row
                    m_nRowCount = m_xCacheSet->getRow(); // here we have the row count
                if(!m_nRowCount)
                    m_nRowCount = i-1; // it can be that getRow return zero
                m_bRowCountFinal = true;
            }
            const ORowSetMatrix::iterator aEnd = aIter;
            ORowSetMatrix::const_iterator aRealEnd = m_pMatrix->end();
            sal_Int32 nPos;
            if (m_nRowCount >= m_nFetchSize)
            {
                nPos = m_nRowCount - m_nFetchSize;
            }
            else
            {
                nPos = 0;
            }
            _nNewStartPos = nPos;
            _nNewEndPos = m_nRowCount;
            ++nPos;
            bCheck = m_xCacheSet->absolute(nPos);

            for(;bCheck && nPos <= requestedStartPos && aIter != aRealEnd; ++aIter, ++nPos)
            {
                if(!aIter->is())
                    *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
                m_xCacheSet->fillValueRow(*aIter, nPos);
                bCheck = m_xCacheSet->next();
            }
            if(aIter != aEnd)
                std::rotate(m_pMatrix->begin(),aEnd,aIter);
            break;
        }
        bCheck = m_xCacheSet->next();
    }
    // we have to read one row forward to ensure that we know when we are on last row
    // but only when we don't know it already
    if(!m_bRowCountFinal)
    {
        if(!m_xCacheSet->next())
        {
            if(m_xCacheSet->previous()) // because we stand after the last row
                m_nRowCount = m_xCacheSet->getRow(); // here we have the row count
            m_bRowCountFinal = true;
        }
        else
           m_nRowCount = std::max(i,m_nRowCount);

    }
    return bCheck;
}

// If m_nPosition is out of the current window,
// move it and update m_nStartPos and m_nEndPos
// Caller is responsible for updating m_aMatrixIter
void ORowSetCache::moveWindow()
{
    OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
    OSL_ENSURE(m_nEndPos >= m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
    OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");

    if ( m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos )
    {
        // just move inside the window
        OSL_ENSURE((m_nPosition - m_nStartPos) <= static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
        // make double plus sure that we have fetched that row
        m_aMatrixIter = calcPosition();
        OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(), "New m_aMatrixIter is at end(), but should not.");
        if(!m_aMatrixIter->is())
        {
            bool bOk( m_xCacheSet->absolute( m_nPosition ) );
            if ( bOk )
            {
                *m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
                m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
                // we have to read one row forward to ensure that we know when we are on last row
                // but only when we don't know it already
                if ( !m_bRowCountFinal )
                {
                    bOk = m_xCacheSet->absolute( m_nPosition + 1 );
                    if ( bOk )
                        m_nRowCount = std::max(sal_Int32(m_nPosition+1),m_nRowCount);
                }
            }
            if(!bOk && !m_bRowCountFinal)
            {
                // because we stand after the last row
                m_nRowCount = m_xCacheSet->previous() ? m_xCacheSet->getRow() : 0;
                m_bRowCountFinal = true;
            }
        }
        return;
    }

    sal_Int32 nDiff = (m_nFetchSize - 1) / 2;
    sal_Int32 nNewStartPos  = (m_nPosition - nDiff) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
    sal_Int32 nNewEndPos    = nNewStartPos + m_nFetchSize;

    if ( nNewStartPos < 0 )
    {
        // The computed new window crashes through the floor (begins before first row);
        // nNew*Pos has to be shifted by -nNewStartPos
        nNewEndPos -= nNewStartPos;
        nNewStartPos = 0;
    }

    if ( nNewStartPos < m_nStartPos )
    {   // need to fill data *before* m_nStartPos
        if ( nNewEndPos > m_nStartPos )
        {   // The two regions are overlapping.
            // We'll first rotate the contents of m_pMatrix so that the overlap area
            // is positioned right; in the old window it is at the beginning,
            // it has to go to the end.
            // then we fill in the rows between new and old start pos.

            bool bCheck;
            bCheck = m_xCacheSet->absolute(nNewStartPos + 1);

            // m_nEndPos < nNewEndPos when window not filled (e.g. there are fewer rows in total than window size)
            m_nEndPos = std::min(nNewEndPos, m_nEndPos);
            const sal_Int32 nOverlapSize = m_nEndPos - m_nStartPos;
            const sal_Int32 nStartPosOffset = m_nStartPos - nNewStartPos; // by how much m_nStartPos moves
            m_nStartPos = nNewStartPos;
            OSL_ENSURE( o3tl::make_unsigned(nOverlapSize) <= m_pMatrix->size(), "new window end is after end of cache matrix!" );
            // the first position in m_pMatrix whose data we don't keep;
            // content will be moved to m_pMatrix.begin()
            ORowSetMatrix::iterator aEnd (m_pMatrix->begin() + nOverlapSize);
            // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
            ORowSetMatrix::iterator aNewEnd (aEnd + nStartPosOffset);
            // *m_pMatrix now looks like:
            //   [0; nOverlapSize) i.e. [begin(); aEnd): data kept
            //   [nOverlapSize; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos
            //   [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
            // Note that nOverlapSize + nStartPosOffset == m_nEndPos - m_nStartPos (new values)
            // When we are finished:
            //   [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos
            //   [nStartPosOffset; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): kept
            //   [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused

            if ( bCheck )
            {
                {
                    ORowSetMatrix::iterator aIter(aEnd);
                    sal_Int32 nPos = m_nStartPos + 1;
                    fill(aIter, aNewEnd, nPos, bCheck);
                }

                std::rotate(m_pMatrix->begin(), aEnd, aNewEnd);
                if (!m_bModified)
                {
                    // now correct the iterator in our iterator vector
                    //  rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment
                    for(auto& rCacheIter : m_aCacheIterators)
                    {
                        if ( !rCacheIter.second.pRowSet->isInsertRow()
                            && rCacheIter.second.aIterator != m_pMatrix->end() )
                        {
                            const ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin();
                            if ( nDist >= nOverlapSize )
                            {
                                // That's from outside the overlap area; invalidate iterator.
                                rCacheIter.second.aIterator = m_pMatrix->end();
                            }
                            else
                            {
                                // Inside overlap area: move to correct position
                                OSL_ENSURE(((nDist + nStartPosOffset) >= static_cast<ORowSetMatrix::difference_type>(0)) &&
                                    ((nDist + nStartPosOffset) < static_cast<sal_Int32>(m_pMatrix->size())),"Position is invalid!");
                                rCacheIter.second.aIterator += nStartPosOffset;
                                OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin()
                                    && rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!");
                            }
                        }
                    }
                }
            }
            else
            { // normally this should never happen
                OSL_FAIL("What the hell is happen here!");
                return;
            }
        }
        else
        {// no rows can be reused so fill again
            reFillMatrix(nNewStartPos,nNewEndPos);
        }
    }

    OSL_ENSURE(nNewStartPos >= m_nStartPos, "ORowSetCache::moveWindow internal error: new start pos before current start pos");
    if ( m_nEndPos < nNewEndPos )
    {   // need to fill data *after* m_nEndPos
        if( nNewStartPos < m_nEndPos )
        {   // The two regions are overlapping.
            const sal_Int32 nRowsInCache = m_nEndPos - m_nStartPos;
            if ( nRowsInCache < m_nFetchSize )
            {
                // There is some unused space in *m_pMatrix; fill it
                OSL_ENSURE((nRowsInCache >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nRowsInCache) < m_pMatrix->size()),"Position is invalid!");
                sal_Int32 nPos = m_nEndPos + 1;
                bool bCheck = m_xCacheSet->absolute(nPos);
                ORowSetMatrix::iterator aIter = m_pMatrix->begin() + nRowsInCache;
                const sal_Int32 nRowsToFetch = std::min(nNewEndPos-m_nEndPos, m_nFetchSize-nRowsInCache);
                const ORowSetMatrix::const_iterator aEnd = aIter + nRowsToFetch;
                bCheck = fill(aIter, aEnd, nPos, bCheck);
                m_nEndPos = nPos - 1;
                OSL_ENSURE( (!bCheck && m_nEndPos <= nNewEndPos ) ||
                            ( bCheck && m_nEndPos == nNewEndPos ),
                             "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
            }

            // A priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive]
            // have to be refilled with new to-be-fetched rows.
            // The rows behind this can be reused
            ORowSetMatrix::iterator aIter = m_pMatrix->begin();
            const sal_Int32 nNewStartPosInMatrix = nNewStartPos - m_nStartPos;
            OSL_ENSURE((nNewStartPosInMatrix >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nNewStartPosInMatrix) < m_pMatrix->size()),"Position is invalid!");
            // first position we reuse
            const ORowSetMatrix::const_iterator aEnd  = m_pMatrix->begin() + nNewStartPosInMatrix;
            // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
            ORowSetMatrix::iterator aDataEnd  = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);

            sal_Int32 nPos = m_nEndPos + 1;
            bool bCheck = m_xCacheSet->absolute(nPos);
            bCheck = fill(aIter, aEnd, nPos, bCheck); // refill the region we don't need anymore
            //aIter and nPos are now the position *after* last filled in one!

            // bind end to front
            if(bCheck)
            {
                OSL_ENSURE(aIter == aEnd, "fill() said went till end, but did not.");
                // rotate the end to the front
                std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
                // now correct the iterator in our iterator vector
                rotateCacheIterator( nNewStartPosInMatrix );
                m_nStartPos = nNewStartPos;
                m_nEndPos = nNewEndPos;
                // now I can say how many rows we have
                // we have to read one row forward to ensure that we know when we are on last row
                // but only when we don't know it already
                bool bOk = true;
                if(!m_bRowCountFinal)
                    bOk = m_xCacheSet->next();
                if(!bOk)
                {
                    m_xCacheSet->previous(); // because we stand after the last row
                    m_nRowCount      = nPos; // here we have the row count
                    OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos is not valid!");
                    m_bRowCountFinal = true;
                }
                else if(!m_bRowCountFinal)
                    m_nRowCount = std::max(nPos+1, m_nRowCount); //+1 because we successfully moved to row after nPos
                else
                    OSL_ENSURE(m_nRowCount >= nPos, "Final m_nRowCount is smaller than row I moved to!");
            }
            else
            {   // the end was reached before or at end() so we can set the start before or at nNewStartPos
                // and possibly keep more of m_pMatrix than planned.
                const ORowSetMatrix::const_iterator::difference_type nFetchedRows  = aIter - m_pMatrix->begin();
                // *m_pMatrix now looks like:
                // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows
                // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???

                nPos -= 1;
                m_nStartPos += nFetchedRows;
                m_nEndPos = nPos;
                std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
                // now correct the iterator in our iterator vector
                rotateCacheIterator( nFetchedRows );

                if ( !m_bRowCountFinal )
                {
                    m_xCacheSet->previous();                   // because we stand after the last row
                    m_nRowCount      = std::max(m_nRowCount, nPos);    // here we have the row count
                    OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos isn't valid!");
                    m_bRowCountFinal = true;
                }

            }
            // here we need only to check if the beginning row is valid. If not we have to fetch it.
            if(!m_pMatrix->begin()->is())
            {
                aIter = m_pMatrix->begin();

                nPos    = m_nStartPos + 1;
                bCheck  = m_xCacheSet->absolute(nPos);
                for(; !aIter->is() && bCheck;++aIter, ++nPos)
                {
                    OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator");

                    *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
                    m_xCacheSet->fillValueRow(*aIter, nPos);

                    bCheck = m_xCacheSet->next();
                }
            }
        }
        else // no rows can be reused so fill again
            reFillMatrix(nNewStartPos,nNewEndPos);
    }

    if(!m_bRowCountFinal)
       m_nRowCount = std::max(m_nPosition,m_nRowCount);
    OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
    OSL_ENSURE(m_nEndPos > m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
    OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
}

bool ORowSetCache::first(  )
{
    // First move to the first row.
    // Then check if the cache window is at the beginning.
    // If not, then position the window and fill it with data.
    // We move the window smartly, i.e. we clear only the rows that are out of range
    bool bRet = m_xCacheSet->first();
    if(bRet)
    {
        m_bBeforeFirst  = m_bAfterLast = false;
        m_nPosition     = 1;
        moveWindow();
        m_aMatrixIter   = m_pMatrix->begin();
    }
    else
    {
        m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
        m_nRowCount = m_nPosition = 0;

        OSL_ENSURE(m_bBeforeFirst || m_bNew,"ORowSetCache::first return false and BeforeFirst isn't true");
        m_aMatrixIter = m_pMatrix->end();
    }
    return bRet;
}

bool ORowSetCache::last(  )
{
    bool bRet = m_xCacheSet->last();
    if(bRet)
    {
        m_bBeforeFirst = m_bAfterLast = false;
        if(!m_bRowCountFinal)
        {
            m_bRowCountFinal = true;
            m_nRowCount = m_xCacheSet->getRow(); // not  + 1
        }
        m_nPosition = m_xCacheSet->getRow();
        moveWindow();
        // we have to repositioning because moveWindow can modify the cache
        m_xCacheSet->last();
        OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
        m_aMatrixIter = calcPosition();
    }
    else
    {
        m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
        m_nRowCount = m_nPosition = 0;
        OSL_ENSURE(m_bBeforeFirst,"ORowSetCache::last return false and BeforeFirst isn't true");
        m_aMatrixIter = m_pMatrix->end();
    }
#if OSL_DEBUG_LEVEL > 0
    if(bRet)
    {
        assert((*m_aMatrixIter).is() && "ORowSetCache::last: Row not valid!");
    }
#endif

    return bRet;
}

sal_Int32 ORowSetCache::getRow(  ) const
{
    return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition;
}

bool ORowSetCache::absolute( sal_Int32 row )
{
    if(!row )
        throw SQLException(DBA_RES(RID_STR_NO_ABS_ZERO),nullptr,SQLSTATE_GENERAL,1000,Any() );

    if(row < 0)
    {
        // here we have to scroll from the last row to backward so we have to go to last row and
        // and to the previous
        if(m_bRowCountFinal || last())
        {
            m_nPosition = m_nRowCount + row + 1; // + row because row is negative and +1 because row==-1 means last row
            if(m_nPosition < 1)
            {
                m_bBeforeFirst = true;
                m_bAfterLast = false;
                m_aMatrixIter = m_pMatrix->end();
            }
            else
            {
                m_bBeforeFirst  = false;
                m_bAfterLast    = m_nPosition > m_nRowCount;
                moveWindow();
                OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
                m_aMatrixIter = calcPosition();
            }
        }
        else
            m_aMatrixIter = m_pMatrix->end();
    }
    else
    {
        m_nPosition = row;
        // the position flags
        m_bBeforeFirst  = false;
        checkPositionFlags();

        if(!m_bAfterLast)
        {
            moveWindow();
            checkPositionFlags();
            if(!m_bAfterLast)
                m_aMatrixIter = calcPosition();
            else
                m_aMatrixIter = m_pMatrix->end();
        }
        else
            m_aMatrixIter = m_pMatrix->end();
    }

    return !(m_bAfterLast || m_bBeforeFirst);
}

bool ORowSetCache::relative( sal_Int32 rows )
{
    bool bErg = true;
    if(rows)
    {
        sal_Int32 nNewPosition = m_nPosition + rows;

        if ( m_bBeforeFirst && rows > 0 )
            nNewPosition = rows;
        else if ( m_bRowCountFinal && m_bAfterLast && rows < 0 )
            nNewPosition = m_nRowCount + 1 + rows;
        else
            if ( m_bBeforeFirst || ( m_bRowCountFinal && m_bAfterLast ) )
                throw SQLException( DBA_RES( RID_STR_NO_RELATIVE ), nullptr, SQLSTATE_GENERAL, 1000, Any() );
        if ( nNewPosition )
        {
            bErg = absolute( nNewPosition );
            bErg = bErg && !isAfterLast() && !isBeforeFirst();
        }
        else
        {
            m_bBeforeFirst = true;
            bErg = false;
        }
    }
    return bErg;
}

bool ORowSetCache::previous(  )
{
    bool bRet = false;
    if(!isBeforeFirst())
    {
        if(m_bAfterLast)   // we stand after the last row so one before is the last row
            bRet = last();
        else
        {
            m_bAfterLast = false;
            --m_nPosition;
            moveWindow();
            OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");

            checkPositionFlags();

            if(!m_nPosition)
            {
                m_bBeforeFirst = true;
                m_aMatrixIter = m_pMatrix->end();
            }
            else
            {
                m_aMatrixIter = calcPosition();
                bRet = (*m_aMatrixIter).is();
            }
        }
    }
    return bRet;
}

void ORowSetCache::refreshRow(  )
{
    if(isAfterLast())
        throw SQLException(DBA_RES(RID_STR_NO_REFRESH_AFTERLAST),nullptr,SQLSTATE_GENERAL,1000,Any() );
    OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!");
    m_xCacheSet->refreshRow();
    m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
    if ( m_bNew )
    {
        cancelRowModification();
    }
}

bool ORowSetCache::rowUpdated(  )
{
    return m_xCacheSet->rowUpdated();
}

bool ORowSetCache::rowInserted(  )
{
    return m_xCacheSet->rowInserted();
}

// XResultSetUpdate
bool ORowSetCache::insertRow(std::vector< Any >& o_aBookmarks)
{
    if ( !m_bNew || !m_aInsertRow->is() )
        throw SQLException(DBA_RES(RID_STR_NO_MOVETOINSERTROW_CALLED),nullptr,SQLSTATE_GENERAL,1000,Any() );

    m_xCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable);

    bool bRet( rowInserted() );
    if ( bRet )
    {
        ++m_nRowCount;
        Any aBookmark = (**m_aInsertRow)[0].makeAny();
        m_bAfterLast = m_bBeforeFirst = false;
        if(aBookmark.hasValue())
        {
            moveToBookmark(aBookmark);
            // update the cached values
            ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter;
            ORowSetMatrix::const_iterator aIter = m_pMatrix->begin();
            for(;aIter != m_pMatrix->end();++aIter)
            {
                if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) )
                {
                    o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get()));
                }
            }
        }
        else
        {
            OSL_FAIL("There must be a bookmark after the row was inserted!");
        }
    }
    return bRet;
}

void ORowSetCache::resetInsertRow(bool _bClearInsertRow)
{
    if ( _bClearInsertRow )
        clearInsertRow();
    m_bNew      = false;
    m_bModified = false;
}

void ORowSetCache::cancelRowModification()
{
    // clear the insertrow references   -> implies that the current row of the rowset changes as well
    for(auto& rCacheIter : m_aCacheIterators)
    {
        if ( rCacheIter.second.pRowSet->isInsertRow() && rCacheIter.second.aIterator == m_aInsertRow )
            rCacheIter.second.aIterator = m_pMatrix->end();
    }
    resetInsertRow(false);
}

void ORowSetCache::updateRow( ORowSetMatrix::iterator const & _rUpdateRow, std::vector< Any >& o_aBookmarks )
{
    if(isAfterLast() || isBeforeFirst())
        throw SQLException(DBA_RES(RID_STR_NO_UPDATEROW),nullptr,SQLSTATE_GENERAL,1000,Any() );

    Any aBookmark = (**_rUpdateRow)[0].makeAny();
    OSL_ENSURE(aBookmark.hasValue(),"Bookmark must have a value!");
    // here we don't have to reposition our CacheSet, when we try to update a row,
    // the row was already fetched
    moveToBookmark(aBookmark);
    m_xCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable);
    // refetch the whole row
    (*m_aMatrixIter) = nullptr;

    if ( moveToBookmark(aBookmark) )
    {
        // update the cached values
        ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter;
        ORowSetMatrix::const_iterator aIter = m_pMatrix->begin();
        for(;aIter != m_pMatrix->end();++aIter)
        {
            if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) )
            {
                o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get()));
            }
        }
    }

    m_bModified = false;
}

bool ORowSetCache::deleteRow(  )
{
    if(isAfterLast() || isBeforeFirst())
        throw SQLException(DBA_RES(RID_STR_NO_DELETEROW),nullptr,SQLSTATE_GENERAL,1000,Any() );

    m_xCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable);
    if ( !m_xCacheSet->rowDeleted() )
        return false;

    --m_nRowCount;
    OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
    ORowSetMatrix::iterator aPos = calcPosition();
    (*aPos)   = nullptr;

    ORowSetMatrix::const_iterator aEnd = m_pMatrix->end();
    for(++aPos;aPos != aEnd && aPos->is();++aPos)
    {
        *(aPos-1) = *aPos;
        (*aPos)   = nullptr;
    }
    m_aMatrixIter = m_pMatrix->end();

    --m_nPosition;
    return true;
}

void ORowSetCache::cancelRowUpdates(  )
{
    m_bNew = m_bModified = false;
    if(!m_nPosition)
    {
        OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0");
        ::dbtools::throwFunctionSequenceException(nullptr);
    }

    if(m_xCacheSet->absolute(m_nPosition))
        m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
    else
    {
        OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
        ::dbtools::throwFunctionSequenceException(nullptr);
    }
}

void ORowSetCache::moveToInsertRow(  )
{
    m_bNew      = true;
    m_bAfterLast = false;

    m_aInsertRow = m_pInsertMatrix->begin();
    if(!m_aInsertRow->is())
        *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());

    // we don't unbound the bookmark column
    ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1;
    ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end();
    for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i)
    {
        aIter->setBound(false);
        aIter->setModified(false);
        aIter->setNull();
        aIter->setTypeKind(m_xMetaData->getColumnType(i));
    }
}

ORowSetCacheIterator ORowSetCache::createIterator(ORowSetBase* _pRowSet)
{
    ORowSetCacheIterator_Helper aHelper;
    aHelper.aIterator = m_pMatrix->end();
    aHelper.pRowSet = _pRowSet;
    return ORowSetCacheIterator(m_aCacheIterators.insert(m_aCacheIterators.begin(),ORowSetCacheMap::value_type(m_aCacheIterators.size()+1,aHelper)),this,_pRowSet);
}

void ORowSetCache::deleteIterator(const ORowSetBase* _pRowSet)
{
    ORowSetCacheMap::const_iterator aCacheIter = m_aCacheIterators.begin();
    for(;aCacheIter != m_aCacheIterators.end();)
    {
        if ( aCacheIter->second.pRowSet == _pRowSet )
        {
            aCacheIter = m_aCacheIterators.erase(aCacheIter);
        }
        else
            ++aCacheIter;
    }
}

void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist)
{
    if (m_bModified)
        return;

    if(!_nDist)
        return;

    // now correct the iterator in our iterator vector
    for(auto& rCacheIter : m_aCacheIterators)
    {
        if ( !rCacheIter.second.pRowSet->isInsertRow()
            && rCacheIter.second.aIterator != m_pMatrix->end())
        {
            ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin();
            if(nDist < _nDist)
            {
                rCacheIter.second.aIterator = m_pMatrix->end();
            }
            else
            {
                OSL_ENSURE((rCacheIter.second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!");
                rCacheIter.second.aIterator -= _nDist;
                OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin()
                        && rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!");
            }
        }
    }
}

void ORowSetCache::rotateAllCacheIterators()
{
    if (m_bModified)
        return;

    // now correct the iterator in our iterator vector
    for (auto& rCacheIter : m_aCacheIterators)
    {
        if (!rCacheIter.second.pRowSet->isInsertRow())
        {
            rCacheIter.second.aIterator = m_pMatrix->end();
        }
    }
}

void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow)
{
    m_aInsertRow = m_pInsertMatrix->begin();
    if(!m_aInsertRow->is())
        *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());

    (*(*m_aInsertRow)) = *(*_rOriginalRow);
    // we don't unbound the bookmark column
    for(auto& rItem : **m_aInsertRow)
        rItem.setModified(false);
}

void ORowSetCache::checkPositionFlags()
{
    if(m_bRowCountFinal)
    {
        m_bAfterLast    = m_nPosition > m_nRowCount;
        if(m_bAfterLast)
            m_nPosition = 0;//m_nRowCount;
    }
}

void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex)
{
    if(m_bAfterLast || columnIndex >= static_cast<sal_Int32>((*m_aInsertRow)->size()))
        throwFunctionSequenceException(m_xSet.get());
}

bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const OUString& _sUpdateTableName)
{
    bool bOk = false;
    if (pNode->count() == 3 &&  // expression in parentheses
        SQL_ISPUNCTUATION(pNode->getChild(0),"(") &&
        SQL_ISPUNCTUATION(pNode->getChild(2),")"))
    {
        bOk = checkInnerJoin(pNode->getChild(1),_xConnection,_sUpdateTableName);
    }
    else if ((SQL_ISRULE(pNode,search_condition) || SQL_ISRULE(pNode,boolean_term)) && // AND/OR link
                pNode->count() == 3)
    {
        // only allow an AND link
        if ( SQL_ISTOKEN(pNode->getChild(1),AND) )
            bOk = checkInnerJoin(pNode->getChild(0),_xConnection,_sUpdateTableName)
                && checkInnerJoin(pNode->getChild(2),_xConnection,_sUpdateTableName);
    }
    else if (SQL_ISRULE(pNode,comparison_predicate))
    {
        // only the comparison of columns is allowed
        OSL_ENSURE(pNode->count() == 3,"checkInnerJoin: Error in Parse Tree");
        if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
                SQL_ISRULE(pNode->getChild(2),column_ref) &&
                pNode->getChild(1)->getNodeType() == SQLNodeType::Equal))
        {
            bOk = false;
        }
        else
        {
            OUString sColumnName,sTableRange;
            OSQLParseTreeIterator::getColumnRange( pNode->getChild(0), _xConnection, sColumnName, sTableRange );
            bOk = sTableRange == _sUpdateTableName;
            if ( !bOk )
            {
                OSQLParseTreeIterator::getColumnRange( pNode->getChild(2), _xConnection, sColumnName, sTableRange );
                bOk =  sTableRange == _sUpdateTableName;
            }
        }
    }
    return bOk;
}

bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection,
                             const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
                             const OUString& _sUpdateTableName )
{
    bool bOk = false;
    OUString sSql = _xAnalyzer->getQuery();
    OUString sErrorMsg;
    ::connectivity::OSQLParser aSqlParser( m_aContext );
    std::unique_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql));
    if ( pSqlParseNode && SQL_ISRULE(pSqlParseNode, select_statement) )
    {
        OSQLParseNode* pTableRefCommalist = pSqlParseNode->getByRule(::connectivity::OSQLParseNode::table_ref_commalist);
        OSL_ENSURE(pTableRefCommalist,"NO tables why!?");
        if(pTableRefCommalist && pTableRefCommalist->count() == 1)
        {
            // we found only one element so it must some kind of join here
            OSQLParseNode* pJoin = pTableRefCommalist->getByRule(::connectivity::OSQLParseNode::qualified_join);
            if(pJoin)
            { // we are only interested in qualified joins like RIGHT or LEFT
                OSQLParseNode* pJoinType    = pJoin->getChild(1);
                OSQLParseNode* pOuterType   = nullptr;
                if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2)
                    pOuterType = pJoinType->getChild(0);
                else if(SQL_ISRULE(pJoinType,outer_join_type))
                    pOuterType = pJoinType;

                bool bCheck     = false;
                bool bLeftSide  = false;
                if(pOuterType)
                { // found outer join
                    bLeftSide = SQL_ISTOKEN(pOuterType->getChild(0),LEFT);
                    bCheck = bLeftSide || SQL_ISTOKEN(pOuterType->getChild(0),RIGHT);
                }

                if(bCheck)
                { // here we know that we have to check on which side our table resides
                    const OSQLParseNode* pTableRef;
                    if(bLeftSide)
                        pTableRef = pJoin->getChild(0);
                    else
                        pTableRef = pJoin->getChild(3);
                    OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");

                    OUString sTableRange = OSQLParseNode::getTableRange(pTableRef);
                    if(sTableRange.isEmpty())
                        pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, nullptr, falsefalse );
                    bOk =  sTableRange == _sUpdateTableName;
                }
            }
        }
        else
        {
            OSQLParseNode* pWhereOpt = pSqlParseNode->getChild(3)->getChild(1);
            if ( pWhereOpt && !pWhereOpt->isLeaf() )
                bOk = checkInnerJoin(pWhereOpt->getChild(1),_xConnection,_sUpdateTableName);
        }
    }
    return bOk;
}

void ORowSetCache::clearInsertRow()
{
    // we don't unbound the bookmark column
    if ( m_aInsertRow != m_pInsertMatrix->end() && m_aInsertRow->is() )
    {
        ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1;
        ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end();
        for(;aIter != aEnd;++aIter)
        {
            aIter->setBound(false);
            aIter->setModified(false);
            aIter->setNull();
        }
    }
}

ORowSetMatrix::iterator ORowSetCache::calcPosition() const
{
    sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1;
    OSL_ENSURE((nValue >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nValue) < m_pMatrix->size()),"Position is invalid!");
    return ( nValue < 0 || o3tl::make_unsigned(nValue) >= m_pMatrix->size() ) ? m_pMatrix->end() : (m_pMatrix->begin() + nValue);
}

TORowSetOldRowHelperRef ORowSetCache::registerOldRow()
{
    TORowSetOldRowHelperRef pRef = new ORowSetOldRowHelper(ORowSetRow());
    m_aOldRows.push_back(pRef);
    return pRef;
}

void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef& _rRow)
{
    TOldRowSetRows::iterator aOldRowIter = std::find_if(m_aOldRows.begin(), m_aOldRows.end(),
        [&_rRow](const TORowSetOldRowHelperRef& rxOldRow) { return rxOldRow.get() == _rRow.get(); });
    if (aOldRowIter != m_aOldRows.end())
        m_aOldRows.erase(aOldRowIter);
}

bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos, sal_Int32 _nNewEndPos)
{
    for (const auto& rxOldRow : m_aOldRows)
    {
        if ( rxOldRow.is() && rxOldRow->getRow().is() )
            rxOldRow->setRow(new ORowSetValueVector( *(rxOldRow->getRow()) ) );
    }
    sal_Int32 nNewSt = _nNewStartPos;
    bool bRet = fillMatrix(nNewSt,_nNewEndPos);
    m_nStartPos = nNewSt;
    m_nEndPos = _nNewEndPos;
    rotateAllCacheIterators(); // invalidate every iterator
    return bRet;
}

bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter, const ORowSetMatrix::const_iterator& _aEnd, sal_Int32& _nPos, bool _bCheck)
{
    const sal_Int32 nColumnCount = m_xMetaData->getColumnCount();
    for (; _bCheck && _aIter != _aEnd; ++_aIter, ++_nPos)
    {
        if ( !_aIter->is() )
            *_aIter = new ORowSetValueVector(nColumnCount);
        else
        {
            for (const auto& rxOldRow : m_aOldRows)
            {
                if ( rxOldRow->getRow() == *_aIter )
                    *_aIter = new ORowSetValueVector(nColumnCount);
            }
        }
        m_xCacheSet->fillValueRow(*_aIter, _nPos);
        _bCheck = m_xCacheSet->next();
    }
    return _bCheck;
}

bool ORowSetCache::isResultSetChanged() const
{
    return m_xCacheSet->isResultSetChanged();
}

void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet)
{
    m_xSet = _xDriverSet;
    m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY_THROW)->getMetaData());
    m_xCacheSet->reset(_xDriverSet);

    m_bRowCountFinal = false;
    m_nRowCount = 0;
    reFillMatrix(m_nStartPos,m_nEndPos);
}

void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow
                                           ,std::vector<sal_Int32> const & o_ChangedColumns)
{
    if ( o_ChangedColumns.size() > 1 )
    {
        for (auto const& elem : *m_pMatrix)
        {
            if ( elem.is() && m_xCacheSet->updateColumnValues(*elem,io_aRow,o_ChangedColumns))
            {
                return;
            }
        }
        m_xCacheSet->fillMissingValues(io_aRow);
    }
}

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

93%


¤ Dauer der Verarbeitung: 0.38 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.