/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"


#include "BookmarkSet.hxx"
#include "CRowSetColumn.hxx"
#include "CRowSetDataColumn.hxx"
#include "KeySet.hxx"
#include "OptimisticSet.hxx"
#include "RowSetBase.hxx"
#include "RowSetCache.hxx"
#include "StaticSet.hxx"
#include "WrappedResultSet.hxx"
#include "core_resource.hrc"
#include "core_resource.hxx"
#include "dbastrings.hrc"

/** === begin UNO includes === **/
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/sdbcx/CompareBookmark.hpp>
#include <com/sun/star/sdbcx/KeyType.hpp>
#include <com/sun/star/sdbcx/Privilege.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XKeysSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
/** === end UNO includes === **/

#include <comphelper/extract.hxx>
#include <comphelper/property.hxx>
#include <comphelper/seqstream.hxx>
#include <comphelper/uno3.hxx>
#include <connectivity/dbexception.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sqliterator.hxx>
#include <connectivity/sqlnode.hxx>
#include <connectivity/sqlparse.hxx>
#include <tools/debug.hxx>
#include <tools/diagnose_ex.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 ::com::sun::star::lang;
using namespace ::cppu;
using namespace ::osl;

#define CHECK_MATRIX_POS(M) OSL_ENSURE(((M) >= static_cast<ORowSetMatrix::difference_type>(0)) && ((M) < static_cast<sal_Int32>(m_pMatrix->size())),"Position is invalid!")

DBG_NAME(ORowSetCache)
// -------------------------------------------------------------------------
ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs,
						   const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
                           const ::comphelper::ComponentContext& _rContext,
						   const ::rtl::OUString& _rUpdateTableName,
						   sal_Bool&	_bModified,
						   sal_Bool&	_bNew,
                           const ORowSetValueVector& _aParameterValueForCache,
                           const ::rtl::OUString& i_sRowSetFilter,
                           sal_Int32 i_nMaxRows)
	:m_xSet(_xRs)
	,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY)->getMetaData())
	,m_aContext( _rContext )
    ,m_pCacheSet(NULL)
	,m_pMatrix(NULL)
	,m_pInsertMatrix(NULL)
    ,m_nLastColumnIndex(0)
	,m_nFetchSize(0)
	,m_nRowCount(0)
    ,m_nPrivileges( Privilege::SELECT )
	,m_nPosition(0)
    ,m_nStartPos(0)
	,m_nEndPos(0)
	,m_bRowCountFinal(sal_False)
    ,m_bBeforeFirst(sal_True)
	,m_bAfterLast( sal_False )
	,m_bUpdated(sal_False)
	,m_bModified(_bModified)
	,m_bNew(_bNew)
{
    DBG_CTOR(ORowSetCache,NULL);

    // first try if the result can be used to do inserts and updates
    Reference< XPropertySet> xProp(_xRs,UNO_QUERY);
    Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo();
	sal_Bool bBookmarkable = sal_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_pCacheSet = new WrappedResultSet(i_nMaxRows);
            m_xCacheSet = m_pCacheSet;
		    m_pCacheSet->construct(_xRs,i_sRowSetFilter);
            return;
        }
    }
    catch(const Exception& ex)
    {
        (void)ex;
    }
    try
    {
        if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) &&
							::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY)
            _xRs->beforeFirst();
    }
    catch(const SQLException& e)
    {
        (void)e;
    }

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

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

    Reference< XIndexAccess> xUpdateTableKeys;
	::rtl::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< ::rtl::OUString> aTableNames = xTables->getElementNames();
            if ( aTableNames.getLength() > 1 && !_rUpdateTableName.getLength() && 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;
                OptimisticSet* pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
                m_pCacheSet = pCursor;
                m_xCacheSet = m_pCacheSet;
                try
                {
		            m_pCacheSet->construct(_xRs,i_sRowSetFilter);
                    if ( pCursor->isReadOnly() )
                        m_nPrivileges = Privilege::SELECT;
                    m_aKeyColumns = pCursor->getJoinedKeyColumns();
                    return;
                }
                catch(const Exception&)
                {
                    // DBG_UNHANDLED_EXCEPTION();
                }
                m_pCacheSet = NULL;
                m_xCacheSet.clear();
            }
            else
            {
			    if(_rUpdateTableName.getLength() && xTables->hasByName(_rUpdateTableName))
				    xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable;
			    else if(xTables->getElementNames().getLength())
			    {
				    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(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() ? true : false);
						    ::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
						    bAllKeysFound = !aColumnNames.empty() && sal_Int32(aColumnNames.size()) == xPrimaryKeyColumns->getElementNames().getLength();
					    }
				    }
			    }
            }
		}
		catch(Exception&)
		{
		}
	}

	// first check if resultset is bookmarkable
	if(!bNeedKeySet)
	{
		try
		{
			m_pCacheSet = new OBookmarkSet(i_nMaxRows);
			m_xCacheSet = m_pCacheSet;
			m_pCacheSet->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&)
		{
			bNeedKeySet = sal_True;
		}

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

		// || !(comphelper::hasProperty(PROPERTY_CANUPDATEINSERTEDROWS,xProp) && any2bool(xProp->getPropertyValue(PROPERTY_CANUPDATEINSERTEDROWS)))

		// oj removed because keyset uses only the next// || (xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETTYPE) && comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) == ResultSetType::FORWARD_ONLY)
		if(!bAllKeysFound )
		{
			if ( bBookmarkable )
			{ 
				// here I know that we have a read only bookmarable cursor
				_xRs->beforeFirst();
				m_nPrivileges = Privilege::SELECT;
				m_pCacheSet = new WrappedResultSet(i_nMaxRows);
				m_xCacheSet = m_pCacheSet;
				m_pCacheSet->construct(_xRs,i_sRowSetFilter);
				return;
			}
			m_pCacheSet = new OStaticSet(i_nMaxRows);
			m_xCacheSet = m_pCacheSet;
			m_pCacheSet->construct(_xRs,i_sRowSetFilter);
			m_nPrivileges = Privilege::SELECT;
		}
		else
		{
			Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
            SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() ? true : false);
			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;
			sal_Bool bNoInsert = sal_False;

			Sequence< ::rtl::OUString> aNames(xColumns->getElementNames());
			const ::rtl::OUString* pIter	= aNames.getConstArray();
			const ::rtl::OUString* pEnd		= pIter + aNames.getLength();
			for(;pIter != pEnd;++pIter)
			{
				Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),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(*pIter) == aColumnNames.end())
					{ // we found a column where null is not allowed so we can't insert new values
						bNoInsert = sal_True;
						break; // one column is enough
					}
				}
			}

			OKeySet* pKeySet = new OKeySet(m_aUpdateTable,xUpdateTableKeys,aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
			try
			{
				m_pCacheSet = pKeySet;
				m_xCacheSet = m_pCacheSet;
				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&)
			{
				// we couldn't create a keyset here so we have to create a static cache
				if ( m_pCacheSet )
					m_pCacheSet = NULL;
				m_xCacheSet = NULL;
				m_pCacheSet = new OStaticSet(i_nMaxRows);
				m_xCacheSet = m_pCacheSet;
				m_pCacheSet->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_pCacheSet = NULL;
	m_xCacheSet = NULL;
	if(m_pMatrix)
	{
		m_pMatrix->clear();
		delete m_pMatrix;
	}

	if(m_pInsertMatrix)
	{
		m_pInsertMatrix->clear();
		delete m_pInsertMatrix;
	}
	m_xSet			= WeakReference< XResultSet>();
	m_xMetaData		= NULL;
	m_aUpdateTable	= NULL;

    DBG_DTOR(ORowSetCache,NULL);
}

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

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

		m_pInsertMatrix = 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,sal_Bool> aCacheIterToChange;
		// first get the positions where they stand now
		ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
        ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
		for(;aCacheIter != aCacheEnd;++aCacheIter)
		{
			aCacheIterToChange[aCacheIter->first] = sal_False;
			if ( !aCacheIter->second.pRowSet->isInsertRow()
				/*&& aCacheIter->second.aIterator != m_pMatrix->end()*/ && !m_bModified )
			{
				ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
				aPositions.push_back(nDist);
				aCacheIterToChange[aCacheIter->first] = sal_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 invalid all iterators
		::std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
		::std::map<sal_Int32,sal_Bool>::const_iterator aPosChangeIter = aCacheIterToChange.begin();
		for(	aCacheIter = m_aCacheIterators.begin();
				aPosChangeIter != aCacheIterToChange.end();
				++aPosChangeIter,++aCacheIter)
		{
			if ( aPosChangeIter->second )
            {
                CHECK_MATRIX_POS(*aIter);
                if ( *aIter < _nSize )
				    aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++;
                else
                    aCacheIter->second.aIterator = m_pMatrix->end();
            }
		}
	}
	if(!m_nPosition)
	{
		sal_Int32 nNewSt = 1;
		fillMatrix(nNewSt,_nSize+1);
		m_nStartPos = 0;
		m_nEndPos = _nSize;
	}
    else if (m_nStartPos < m_nPosition && m_nPosition < m_nEndPos)
    {
        sal_Int32 nNewSt = -1;
        fillMatrix(nNewSt,_nSize+1);
		m_nStartPos = 0;
		m_nEndPos = _nSize;
    }
}
// -------------------------------------------------------------------------

// XResultSetMetaDataSupplier
Reference< XResultSetMetaData > ORowSetCache::getMetaData(  )
{
	return m_xMetaData;
}
// -------------------------------------------------------------------------
Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
{
    switch ( i_aValue.getTypeKind() )
	{
		case DataType::TINYINT:
		case DataType::SMALLINT:
		case DataType::INTEGER:
			return makeAny((sal_Int32)i_aValue);
		default:
			if ( i_pCacheSet && i_aValue.isNull())
				i_aValue = i_pCacheSet->getBookmark();
			return i_aValue.getAny();
	}
}
// -------------------------------------------------------------------------
// ::com::sun::star::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).isValid())
	{
		return Any(); // this is allowed here because the rowset knowns what it is doing
	}

	return lcl_getBookmark(((*m_aMatrixIter)->get())[0],m_pCacheSet);
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::moveToBookmark( const Any& bookmark )
{
	if ( m_pCacheSet->moveToBookmark(bookmark) )
	{
		m_bBeforeFirst = sal_False;
		m_nPosition	= m_pCacheSet->getRow();

		checkPositionFlags();

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

	return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).isValid();
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
{
	sal_Bool bRet( moveToBookmark( bookmark ) );
	if ( bRet )
	{
		m_nPosition = m_pCacheSet->getRow() + rows;
		absolute(m_nPosition);
		//	for(sal_Int32 i=0;i<rows && m_aMatrixIter != m_pMatrix->end();++i,++m_aMatrixIter) ;

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

	return bRet;
}
// -------------------------------------------------------------------------
sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second )
{
	return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_pCacheSet->compareBookmarks(_first,_second);
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::hasOrderedBookmarks(  )
{
	return m_pCacheSet->hasOrderedBookmarks();
}
// -------------------------------------------------------------------------
sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark )
{
	return m_pCacheSet->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)->get());
	if ( !rInsert[columnIndex].isNull() )
	{
		rInsert[columnIndex].setBound(sal_True);
		rInsert[columnIndex].setNull();
		rInsert[columnIndex].setModified();
    	io_aRow[columnIndex].setNull();

	    m_pCacheSet->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)->get());
	if ( rInsert[columnIndex] != x )
	{
		rInsert[columnIndex].setBound(sal_True);
		rInsert[columnIndex] = x;
		rInsert[columnIndex].setModified();
		io_aRow[columnIndex] = rInsert[columnIndex];

		m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
		impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
	}
}
// -------------------------------------------------------------------------
void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< ::com::sun::star::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)->get());
    rInsert[columnIndex].setBound(sal_True);
	rInsert[columnIndex] = aSeq;
	rInsert[columnIndex].setModified();
    io_aRow[columnIndex] = makeAny(x);

    m_pCacheSet->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)->get());
	ORowSetValue aTemp;
	aTemp.fill(x);
	if ( rInsert[columnIndex] != aTemp )
	{
		rInsert[columnIndex].setBound(sal_True);
		rInsert[columnIndex] = aTemp;
		rInsert[columnIndex].setModified();
    	io_aRow[columnIndex] = rInsert[columnIndex];

	    m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
    	impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
	}
}
// -------------------------------------------------------------------------
void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/
                                       ,ORowSetValueVector::Vector& io_aRow
                                       ,::std::vector<sal_Int32>& o_ChangedColumns
                                       )
{
	checkUpdateConditions(columnIndex);

    ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
	ORowSetValue aTemp;
	aTemp.fill(x);
	if ( rInsert[columnIndex] != aTemp )
	{
		rInsert[columnIndex].setBound(sal_True);
		rInsert[columnIndex] = aTemp;
		rInsert[columnIndex].setModified();
    	io_aRow[columnIndex] = rInsert[columnIndex];

	    m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
    	impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
	}
}
// -------------------------------------------------------------------------
// XResultSet
sal_Bool ORowSetCache::next(  )
{
	if(!isAfterLast())
	{
		m_bBeforeFirst = sal_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) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
			m_aMatrixIter = calcPosition();
			checkPositionFlags();
		}
	}

	return !m_bAfterLast;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::isBeforeFirst(  )
{
	return m_bBeforeFirst;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::isAfterLast(  )
{
	return m_bAfterLast;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::isFirst(  )
{
	return m_nPosition == 1; // ask resultset for
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::isLast(  )
{
	return m_nPosition == m_nRowCount;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::beforeFirst(  )
{
	if(!m_bBeforeFirst)
	{
		m_bAfterLast	= sal_False;
		m_nPosition		= 0;
		m_bBeforeFirst	= sal_True;
		m_pCacheSet->beforeFirst();
		moveWindow();
		m_aMatrixIter = m_pMatrix->end();
	}
    return sal_True;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::afterLast(  )
{
	if(!m_bAfterLast)
	{
		m_bBeforeFirst = sal_False;
		m_bAfterLast = sal_True;

		if(!m_bRowCountFinal)
		{
			m_pCacheSet->last_checked(sal_False);
			m_bRowCountFinal = sal_True;
			m_nRowCount = m_pCacheSet->getRow();// + 1 removed
		}
		m_pCacheSet->afterLast();

		m_nPosition = 0;
		m_aMatrixIter = m_pMatrix->end();
	}
    return sal_True;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos,sal_Int32 _nNewEndPos)
{
	OSL_ENSURE(_nNewStartPos != _nNewEndPos,"ORowSetCache::fillMatrix: StartPos and EndPos can not be equal!");
	// fill the whole window with new data
    ORowSetMatrix::iterator aIter;
    sal_Int32 i;
    sal_Bool bCheck;
    if ( _nNewStartPos == -1 )
    {
	    aIter = m_pMatrix->begin() + m_nEndPos;
        i = m_nEndPos+1;
    }
    else
    {
        aIter = m_pMatrix->begin();
        i = _nNewStartPos;
    }
    bCheck = m_pCacheSet->absolute(i); // -1 no need to

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

			if(!m_bRowCountFinal)
			{
				if(m_pCacheSet->previous_checked(sal_False)) // because we stand after the last row
					m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
				if(!m_nRowCount)
					m_nRowCount = i-1; // it can be that getRow return zero
				m_bRowCountFinal = sal_True;
			}
			if(m_nRowCount > m_nFetchSize)
			{
				ORowSetMatrix::iterator aEnd = aIter;
                ORowSetMatrix::iterator aRealEnd = m_pMatrix->end();
				sal_Int32 nPos = m_nRowCount - m_nFetchSize + 1;
				_nNewStartPos = nPos;
				bCheck = m_pCacheSet->absolute(_nNewStartPos);

				for(;bCheck && aIter != aRealEnd;++aIter)
				{
					if(bCheck)
					{
						if(!aIter->isValid())
							*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
						m_pCacheSet->fillValueRow(*aIter,nPos++);
					}
					bCheck = m_pCacheSet->next();
				}
				if(aIter != aEnd)
					::std::rotate(m_pMatrix->begin(),aEnd,aRealEnd);
			}
			break;
		}
        if ( i < (_nNewEndPos-1) )
		    bCheck = m_pCacheSet->next();
	}
	//	m_nStartPos = _nNewStartPos;
	// 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_pCacheSet->next())
		{
			if(m_pCacheSet->previous_checked(sal_False)) // because we stand after the last row
				m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
			m_bRowCountFinal = sal_True;
		}
		else
		   m_nRowCount = std::max(i,m_nRowCount);

	}
    */
	return bCheck;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::moveWindow()
{

	sal_Bool bRet = sal_True;

	sal_Int32 nDiff = (sal_Int32)(m_nFetchSize*0.5 -0.5);
	sal_Int32 nNewStartPos	= (m_nPosition - nDiff);
	//	sal_Int32 nNewEndPos	= (m_nPosition+m_nFetchSize*0.5);
	sal_Int32 nNewEndPos	= nNewStartPos + m_nFetchSize;

	if ( m_nPosition <= m_nStartPos )
	{	// the window is behind the new start pos
		if(!m_nStartPos)
			return sal_False;
		// the new position should be the nPos - nFetchSize/2
		if ( nNewEndPos > m_nStartPos )
		{	// but the two regions are overlapping
			// fill the rows behind the new end

			ORowSetMatrix::iterator aEnd; // the iterator we need for rotate
			ORowSetMatrix::iterator aIter; // the iterator we fill with new values

			sal_Bool bCheck = sal_True;
			if ( nNewStartPos < 1 )
			{
				bCheck = m_pCacheSet->first();
				OSL_ENSURE((nNewEndPos - m_nStartPos - nNewStartPos) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
				aEnd = m_pMatrix->begin() + (nNewEndPos - m_nStartPos - nNewStartPos);
				aIter = aEnd;
				m_nStartPos = 0;
			}
			else
			{
				OSL_ENSURE((nNewEndPos - m_nStartPos -1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
				aEnd = m_pMatrix->begin() + ((nNewEndPos - m_nStartPos)-1);
				aIter = m_pMatrix->begin() + ((nNewEndPos - m_nStartPos)-1);
				bCheck = m_pCacheSet->absolute(nNewStartPos);
				m_nStartPos = nNewStartPos -1;
			}

			if ( bCheck )
			{
				sal_Int32 nPos = m_nStartPos;
				bCheck = fill(aIter,m_pMatrix->end(),nPos,bCheck);

				::std::rotate(m_pMatrix->begin(),aEnd,m_pMatrix->end());
				// 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
				ptrdiff_t nNewDist = aEnd - m_pMatrix->begin();
				ptrdiff_t nOffSet = m_pMatrix->end() - aEnd;
				ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
				ORowSetCacheMap::iterator aCacheEnd  = m_aCacheIterators.end();
		        for(;aCacheIter != aCacheEnd;++aCacheIter)
				{
					if ( !aCacheIter->second.pRowSet->isInsertRow()
						&& aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
					{
						ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
						if ( nDist >= nNewDist )
						{
							aCacheIter->second.aIterator = m_pMatrix->end();
						}
						else
						{
#if OSL_DEBUG_LEVEL > 0
							ORowSetMatrix::iterator aOldPos;
                            aOldPos = aCacheIter->second.aIterator;
#endif
                            CHECK_MATRIX_POS( ((aOldPos - m_pMatrix->begin()) + nOffSet) );
							aCacheIter->second.aIterator += nOffSet;
#if OSL_DEBUG_LEVEL > 0
							ORowSetMatrix::iterator aCurrentPos;
                            aCurrentPos = aCacheIter->second.aIterator;
#endif
							OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
									&& aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
						}
					}
				}
			}
			else
			{ // normaly this should never happen
				OSL_ENSURE(0,"What the hell is happen here!");
				return sal_False;
			}
		}
		else
		{// no rows can be reused so fill again
			if(nNewStartPos < 1) // special case
			{
				m_nStartPos = 0;

				rotateCacheIterator(static_cast<sal_Int16>(m_nFetchSize+1)); // static_cast<sal_Int16>(m_nFetchSize+1)

				m_pCacheSet->beforeFirst();

				sal_Bool bCheck;
				ORowSetMatrix::iterator aIter = m_pMatrix->begin();
				for(sal_Int32 i=0;i<m_nFetchSize;++i,++aIter)
				{
                    bCheck = m_pCacheSet->next();
					if ( bCheck )
					{
						if(!aIter->isValid())
							*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
						m_pCacheSet->fillValueRow(*aIter,i+1);
					}
					else
						*aIter = NULL;
				}
			}
			else
				bRet = reFillMatrix(nNewStartPos,nNewEndPos);
		}
	}
	else if(m_nPosition > m_nStartPos)
	{	// the new start pos is above the startpos of the window

		if(m_nPosition <= (m_nStartPos+m_nFetchSize))
		{	// position in window
			OSL_ENSURE((m_nPosition - m_nStartPos -1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
			m_aMatrixIter = calcPosition();
			if(!m_aMatrixIter->isValid())
			{
				sal_Bool bOk( m_pCacheSet->absolute( m_nPosition ) );
				if ( bOk )
				{
					*m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
					m_pCacheSet->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_pCacheSet->absolute_checked( m_nPosition + 1,sal_False );
                        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_pCacheSet->previous_checked(sal_False) ? m_pCacheSet->getRow() : 0;//  + 1 removed
					m_bRowCountFinal = sal_True;
				}
			}
		}
		else if(nNewStartPos < (m_nStartPos+m_nFetchSize))
		{	// position behind window but the region is overlapping
			// the rows from begin() to (begin + nNewStartPos - m_nStartPos) can be refilled with the new rows
			// the rows behind this can be reused
			ORowSetMatrix::iterator aIter = m_pMatrix->begin();
            CHECK_MATRIX_POS(nNewStartPos - m_nStartPos - 1);
			ORowSetMatrix::iterator aEnd  = m_pMatrix->begin() + (nNewStartPos - m_nStartPos - 1);

			sal_Int32 nPos = m_nStartPos + m_nFetchSize + 1;
			sal_Bool bCheck = m_pCacheSet->absolute(nPos);
			bCheck = fill(aIter,aEnd,nPos,bCheck); // refill the region wew don't need anymore

			// we have to read one row forward to enshure that we know when we are on last row
			// but only when we don't know it already
			sal_Bool bOk = sal_True;
			if(bCheck && !m_bRowCountFinal)
				bOk = m_pCacheSet->next();
			// bind end to front
			if(bCheck)
			{	// rotate the end to the front
				::std::rotate(m_pMatrix->begin(),aIter,m_pMatrix->end());
				// now correct the iterator in our iterator vector
				rotateCacheIterator( (sal_Int16)( aIter - m_pMatrix->begin() ) );
				m_nStartPos = nNewStartPos - 1; // must be -1
				// now I can say how many rows we have
				if(!bOk)
				{
					m_pCacheSet->previous_checked(sal_False); // because we stand after the last row
					m_nRowCount		 = nPos; // here we have the row count
					m_bRowCountFinal = sal_True;
				}
				else if(!m_bRowCountFinal)
					m_nRowCount = std::max(++nPos,m_nRowCount);
			}
			else
			{	// the end was reached before end() so we can set the start before nNewStartPos

				m_nStartPos += (aIter - m_pMatrix->begin());
				//	m_nStartPos = (aIter - m_pMatrix->begin());
				::std::rotate(m_pMatrix->begin(),aIter,m_pMatrix->end());
				// now correct the iterator in our iterator vector
				rotateCacheIterator( (sal_Int16)( aIter - m_pMatrix->begin() ) );

				if ( !m_bRowCountFinal )
				{
					m_pCacheSet->previous_checked(sal_False);					// because we stand after the last row
					m_nRowCount		 = std::max(m_nRowCount,--nPos);	// here we have the row count
					OSL_ENSURE(nPos == m_pCacheSet->getRow(),"nPos isn't valid!");
					m_bRowCountFinal = sal_True;
				}
				// TODO check
				//	m_nStartPos = (nNewStartPos+m_nRowCount) - m_nFetchSize ;
				if(m_nStartPos < 0)
					m_nStartPos = 0;
			}
			// here we need only to check if the begining row is valid. If not we have to fetch it.
			if(!m_pMatrix->begin()->isValid())
			{
				aIter = m_pMatrix->begin();

				nPos	= m_nStartPos;
				bCheck	= m_pCacheSet->absolute_checked(m_nStartPos,sal_False);
				for(; !aIter->isValid() && bCheck;++aIter)
				{
                    OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator");
                    bCheck = m_pCacheSet->next();
					if ( bCheck ) // resultset stands on right position
					{
						*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
						m_pCacheSet->fillValueRow(*aIter,++nPos);
					}
				}
			}
		}
		else // no rows can be reused so fill again
			bRet = 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!");

	return bRet;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::first(  )
{
	// first move to the first row
	// then check if the cache window is at the begining
	// when not postionize the window and fill it with data
	// smart moving of the window -> clear only the rows whom are out of range
	sal_Bool bRet = m_pCacheSet->first();
	if(bRet)
	{
		m_bBeforeFirst	= m_bAfterLast = sal_False;
		m_nPosition		= 1;
		moveWindow();
		m_aMatrixIter	= m_pMatrix->begin();
	}
	else
	{
		m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = sal_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;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::last(  )
{
	sal_Bool bRet = m_pCacheSet->last();
	if(bRet)
	{
		m_bBeforeFirst = m_bAfterLast = sal_False;
		if(!m_bRowCountFinal)
		{
			m_bRowCountFinal = sal_True;
			m_nRowCount = m_nPosition = m_pCacheSet->getRow(); // not  + 1
		}
		m_nPosition = m_pCacheSet->getRow();
		moveWindow();
		// we have to repositioning because moveWindow can modify the cache
		m_pCacheSet->last();
//		if(m_nPosition > m_nFetchSize)
//			m_aMatrixIter = m_pMatrix->end() -1;
//		else
//			m_aMatrixIter = m_pMatrix->begin() + m_nPosition - 1;
		OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
		m_aMatrixIter = calcPosition();
	}
	else
	{
		m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = sal_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 > 1
	if(bRet)
	{
		OSL_ENSURE((*m_aMatrixIter).isValid(),"ORowSetCache::last: Row not valid!");
	}
#endif

	return bRet;
}
// -------------------------------------------------------------------------
sal_Int32 ORowSetCache::getRow(  )
{
	return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::absolute( sal_Int32 row )
{
	if(!row )
		throw SQLException(DBACORE_RESSTRING(RID_STR_NO_ABS_ZERO),NULL,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 two 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 = sal_True;
				m_bAfterLast = sal_False;
				m_aMatrixIter = m_pMatrix->end();
			}
			else
			{
				m_bBeforeFirst	= sal_False;
				m_bAfterLast	= m_nPosition > m_nRowCount;
				moveWindow();
				OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (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	= sal_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);
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::relative( sal_Int32 rows )
{
	sal_Bool bErg = sal_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( DBACORE_RESSTRING( RID_STR_NO_RELATIVE ), NULL, SQLSTATE_GENERAL, 1000, Any() );
		if ( nNewPosition )
		{
			bErg = absolute( nNewPosition );
			bErg = bErg && !isAfterLast() && !isBeforeFirst();
		}
		else
        {
            m_bBeforeFirst = sal_True;
			bErg = sal_False;
        }
	}
	return bErg;
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::previous(  )
{
	sal_Bool bRet = sal_False;
	if(!isBeforeFirst())
	{
		if(m_bAfterLast)   // we stand after the last row so one before is the last row
			bRet = last();
		else
		{
			m_bAfterLast = sal_False;
			--m_nPosition;
			moveWindow();
			OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");

			checkPositionFlags();

			if(!m_nPosition)
			{
				m_bBeforeFirst = sal_True;
				m_aMatrixIter = m_pMatrix->end();
			}
			else
			{
				m_aMatrixIter = calcPosition();
				bRet = (*m_aMatrixIter).isValid();
			}
		}
	}
	return bRet;
}
// -------------------------------------------------------------------------
void ORowSetCache::refreshRow(  )
{
	if(isAfterLast())
		throw SQLException(DBACORE_RESSTRING(RID_STR_NO_REFESH_AFTERLAST),NULL,SQLSTATE_GENERAL,1000,Any() );
	OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!");
	m_pCacheSet->refreshRow();
	m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
	if ( m_bNew )
	{
		cancelRowModification();
	}
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::rowUpdated(  )
{
	return m_pCacheSet->rowUpdated();
}
// -------------------------------------------------------------------------
sal_Bool ORowSetCache::rowInserted(  )
{
	return m_pCacheSet->rowInserted();
}
// -------------------------------------------------------------------------
// XResultSetUpdate
sal_Bool ORowSetCache::insertRow(::std::vector< Any >& o_aBookmarks)
{
	if ( !m_bNew || !m_aInsertRow->isValid() )
		throw SQLException(DBACORE_RESSTRING(RID_STR_NO_MOVETOINSERTROW_CALLED),NULL,SQLSTATE_GENERAL,1000,Any() );

	m_pCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable);

	sal_Bool bRet( rowInserted() );
	if ( bRet )
	{
		++m_nRowCount;
		Any aBookmark = ((*m_aInsertRow)->get())[0].makeAny();
		m_bAfterLast = m_bBeforeFirst = sal_False;
		if(aBookmark.hasValue())
        {
			moveToBookmark(aBookmark);
            // update the cached values
            ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
            ORowSetMatrix::iterator aIter = m_pMatrix->begin();
            for(;aIter != m_pMatrix->end();++aIter)
            {
                if ( m_aMatrixIter != aIter && aIter->isValid() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
                {
                    o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
                }
            }
        }
		else
		{
			OSL_ENSURE(0,"There must be a bookmark after the row was inserted!");
		}
	}
	return bRet;
}
// -------------------------------------------------------------------------
void ORowSetCache::resetInsertRow(sal_Bool _bClearInsertRow)
{
	if ( _bClearInsertRow )
		clearInsertRow();
	m_bNew		= sal_False;
	m_bModified = sal_False;
}
// -------------------------------------------------------------------------
void ORowSetCache::cancelRowModification()
{
	// clear the insertrow references	-> implies that the current row of the rowset changes as well
	ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
	ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
	for(;aCacheIter != aCacheEnd;++aCacheIter)
	{
		if ( aCacheIter->second.pRowSet->isInsertRow() && aCacheIter->second.aIterator == m_aInsertRow )
			aCacheIter->second.aIterator = m_pMatrix->end();
	} // for(;aCacheIter != aCacheEnd;++aCacheIter)
	resetInsertRow(sal_False);
}
// -------------------------------------------------------------------------
void ORowSetCache::updateRow( ORowSetMatrix::iterator& _rUpdateRow,::std::vector< Any >& o_aBookmarks )
{
	if(isAfterLast() || isBeforeFirst())
		throw SQLException(DBACORE_RESSTRING(RID_STR_NO_UPDATEROW),NULL,SQLSTATE_GENERAL,1000,Any() );

	Any aBookmark = ((*_rUpdateRow)->get())[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_pCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable);
	// refetch the whole row
	(*m_aMatrixIter) = NULL;
    
	if ( moveToBookmark(aBookmark) )
    {
        // update the cached values
        ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
        ORowSetMatrix::iterator aIter = m_pMatrix->begin();
        for(;aIter != m_pMatrix->end();++aIter)
        {
            if ( m_aMatrixIter != aIter && aIter->isValid() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
            {
                o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
            }
        }
    }

	m_bModified = sal_False;
}
// -------------------------------------------------------------------------
bool ORowSetCache::deleteRow(  )
{
	if(isAfterLast() || isBeforeFirst())
		throw SQLException(DBACORE_RESSTRING(RID_STR_NO_DELETEROW),NULL,SQLSTATE_GENERAL,1000,Any() );

	//	m_pCacheSet->absolute(m_nPosition);
	m_pCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable);
    if ( !m_pCacheSet->rowDeleted() )
        return false;

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

    ORowSetMatrix::iterator aEnd = m_pMatrix->end();
    for(++aPos;aPos != aEnd && aPos->isValid();++aPos)
    {
        *(aPos-1) = *aPos;
        (*aPos)	  = NULL;
    }
    m_aMatrixIter = m_pMatrix->end();

    --m_nPosition;
    return true;
}
// -------------------------------------------------------------------------
void ORowSetCache::cancelRowUpdates(  )
{
	m_bNew = m_bModified = sal_False;
	if(!m_nPosition)
	{
		OSL_ENSURE(0,"cancelRowUpdates:Invalid positions pos == 0");
		::dbtools::throwFunctionSequenceException(NULL);
	}

	if(m_pCacheSet->absolute(m_nPosition))
		m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
	else
	{
		OSL_ENSURE(0,"cancelRowUpdates couldn't position right with absolute");
		::dbtools::throwFunctionSequenceException(NULL);
	}
}
// -------------------------------------------------------------------------
void ORowSetCache::moveToInsertRow(  )
{
	m_bNew		= sal_True;
	m_bUpdated	= m_bAfterLast = sal_False;

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

	// we don't unbound the bookmark column
	ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
    ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
	for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i)
	{
		aIter->setBound(sal_False);
		aIter->setModified(sal_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::iterator aCacheIter = m_aCacheIterators.begin();
	for(;aCacheIter != m_aCacheIterators.end();)
    {
        if ( aCacheIter->second.pRowSet == _pRowSet )
        {
            m_aCacheIterators.erase(aCacheIter);
            aCacheIter = m_aCacheIterators.begin();
        } // if ( aCacheIter->second.pRowSet == _pRowSet )
        else
            ++aCacheIter;
    }
}
// -----------------------------------------------------------------------------
void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist)
{
	if(_nDist)
	{
		// now correct the iterator in our iterator vector
		ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
        ORowSetCacheMap::iterator aCacheEnd  = m_aCacheIterators.end();
		for(;aCacheIter != aCacheEnd;++aCacheIter)
		{
			if ( !aCacheIter->second.pRowSet->isInsertRow()
				&& aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
			{
				ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
				if(nDist < _nDist)
				{
					aCacheIter->second.aIterator = m_pMatrix->end();
				}
				else
				{
                    OSL_ENSURE((aCacheIter->second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!");
					aCacheIter->second.aIterator -= _nDist;
					OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
							&& aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
				}
			}
		}
	}
}
// -------------------------------------------------------------------------
void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow)
{
	m_aInsertRow = m_pInsertMatrix->begin();
	if(!m_aInsertRow->isValid())
		*m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());

	(*(*m_aInsertRow)) = (*(*_rOriginalRow));
	// we don't unbound the bookmark column
	ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin();
    ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
	for(;aIter != aEnd;++aIter)
		aIter->setModified(sal_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 >= (sal_Int32)(*m_aInsertRow)->get().size())
		throwFunctionSequenceException(m_xSet.get());
}
//------------------------------------------------------------------------------
sal_Bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const ::rtl::OUString& _sUpdateTableName)
{
	sal_Bool bOk = sal_False;
	if (pNode->count() == 3 &&	// Ausdruck is geklammert
		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-Verknuepfung:
				pNode->count() == 3)
	{
		// nur AND Verknüpfung zulassen
		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
		DBG_ASSERT(pNode->count() == 3,"checkInnerJoin: Fehler im Parse Tree");
		if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
				SQL_ISRULE(pNode->getChild(2),column_ref) &&
				pNode->getChild(1)->getNodeType() == SQL_NODE_EQUAL))
		{
			bOk = sal_False;
		}
		::rtl::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;
}
// -----------------------------------------------------------------------------
sal_Bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection,
								 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
								 const ::rtl::OUString& _sUpdateTableName )
{
	sal_Bool bOk = sal_False;
	::rtl::OUString sSql = _xAnalyzer->getQuery();
	::rtl::OUString sErrorMsg;
	::connectivity::OSQLParser aSqlParser( m_aContext.getLegacyServiceFactory() );
	::std::auto_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql));
	if ( pSqlParseNode.get() && 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 intereseted in qualified joins like RIGHT or LEFT
				OSQLParseNode* pJoinType	= pJoin->getChild(1);
				OSQLParseNode* pOuterType	= NULL;
				if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2)
					pOuterType = pJoinType->getChild(0);
				else if(SQL_ISRULE(pJoinType,outer_join_type))
					pOuterType = pJoinType;

				sal_Bool bCheck		= sal_False;
				sal_Bool bLeftSide	= sal_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 = pJoin->getByRule(::connectivity::OSQLParseNode::qualified_join);
					if(bLeftSide)
						pTableRef = pJoin->getChild(0);
					else
						pTableRef = pJoin->getChild(3);
					OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");

                    ::rtl::OUString sTableRange = OSQLParseNode::getTableRange(pTableRef);
					if(!sTableRange.getLength())
						pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, NULL, sal_False, sal_False );
					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->isValid() )
	{
		ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
		ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
		for(;aIter != aEnd;++aIter)
		{
			aIter->setBound(sal_False);
			aIter->setModified(sal_False);
			aIter->setNull();
		} // for(;aIter != (*m_aInsertRow)->end();++aIter)
	}
}
// -----------------------------------------------------------------------------
ORowSetMatrix::iterator	ORowSetCache::calcPosition() const
{
	sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1;
    CHECK_MATRIX_POS(nValue);
	return ( nValue < 0 || nValue >= static_cast<sal_Int32>(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 aOldRowEnd = m_aOldRows.end();
	for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
	{
		if ( aOldRowIter->getBodyPtr() == _rRow.getBodyPtr() )
		{
			m_aOldRows.erase(aOldRowIter);
			break;
		}

	}
}
// -----------------------------------------------------------------------------
sal_Bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos,sal_Int32 _nNewEndPos)
{
    TOldRowSetRows::iterator aOldRowEnd = m_aOldRows.end();
	for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
	{
		if ( aOldRowIter->isValid() && aOldRowIter->getBody().getRow().isValid() )
			aOldRowIter->getBody().setRow(new ORowSetValueVector(aOldRowIter->getBody().getRow().getBody()) );
	}
	sal_Int32 nNewSt = _nNewStartPos;
	sal_Bool bRet = fillMatrix(nNewSt,_nNewEndPos);
	m_nStartPos = nNewSt - 1;
	rotateCacheIterator(static_cast<sal_Int16>(m_nFetchSize+1)); // forces that every iterator will be set to null
	return bRet;
}
// -----------------------------------------------------------------------------
sal_Bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter,const ORowSetMatrix::iterator& _aEnd,sal_Int32& _nPos,sal_Bool _bCheck)
{
	sal_Int32 nColumnCount = m_xMetaData->getColumnCount();
	for(; _bCheck && _aIter != _aEnd;)
	{
		if ( !_aIter->isValid() )
			*_aIter = new ORowSetValueVector(nColumnCount);
		else
		{
            TOldRowSetRows::iterator aOldRowEnd = m_aOldRows.end();
	        for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
			{
				if ( aOldRowIter->getBody().getRow().isEqualBody(*_aIter) )
					*_aIter = new ORowSetValueVector(nColumnCount);
			}
		}
		m_pCacheSet->fillValueRow(*_aIter++,++_nPos);
		_bCheck = m_pCacheSet->next();
	}
	return _bCheck;
}
// -----------------------------------------------------------------------------
bool ORowSetCache::isResultSetChanged() const
{
    return m_pCacheSet->isResultSetChanged();
}
// -----------------------------------------------------------------------------
void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet)
{
    m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY)->getMetaData());
    m_pCacheSet->reset(_xDriverSet);

    m_bRowCountFinal = sal_False;
    m_nRowCount = 0;
	reFillMatrix(m_nStartPos+1,m_nEndPos+1);
}
// -----------------------------------------------------------------------------
void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow
                                           ,::std::vector<sal_Int32>& o_ChangedColumns)
{
    if ( o_ChangedColumns.size() > 1 )
    {
        ORowSetMatrix::iterator aIter = m_pMatrix->begin();
        for(;aIter != m_pMatrix->end();++aIter)
        {
            if ( aIter->isValid() && m_pCacheSet->updateColumnValues((*aIter)->get(),io_aRow,o_ChangedColumns))
            {
                break;
            }
        }
        
        if ( aIter == m_pMatrix->end() )
        {
            m_pCacheSet->fillMissingValues(io_aRow);
        }
    }
}
// -----------------------------------------------------------------------------
