Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / dbaccess / source / core / api / RowSetCache.cxx
blob34896882182e101a3be96691ff09788b74c6c654
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "BookmarkSet.hxx"
22 #include "CRowSetColumn.hxx"
23 #include "CRowSetDataColumn.hxx"
24 #include "KeySet.hxx"
25 #include "OptimisticSet.hxx"
26 #include "RowSetBase.hxx"
27 #include "RowSetCache.hxx"
28 #include "StaticSet.hxx"
29 #include "WrappedResultSet.hxx"
30 #include "core_resource.hrc"
31 #include "core_resource.hxx"
32 #include "dbastrings.hrc"
34 #include <com/sun/star/sdbc/ColumnValue.hpp>
35 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
36 #include <com/sun/star/sdbcx/CompareBookmark.hpp>
37 #include <com/sun/star/sdbcx/KeyType.hpp>
38 #include <com/sun/star/sdbcx/Privilege.hpp>
39 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
40 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
41 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
43 #include <comphelper/extract.hxx>
44 #include <comphelper/property.hxx>
45 #include <comphelper/seqstream.hxx>
46 #include <comphelper/uno3.hxx>
47 #include <connectivity/dbexception.hxx>
48 #include <connectivity/dbtools.hxx>
49 #include <connectivity/sqliterator.hxx>
50 #include <connectivity/sqlnode.hxx>
51 #include <connectivity/sqlparse.hxx>
52 #include <tools/debug.hxx>
53 #include <tools/diagnose_ex.h>
54 #include <osl/diagnose.h>
56 #include <algorithm>
58 using namespace dbaccess;
59 using namespace dbtools;
60 using namespace connectivity;
61 using namespace ::com::sun::star::uno;
62 using namespace ::com::sun::star::beans;
63 using namespace ::com::sun::star::sdbc;
64 using namespace ::com::sun::star::sdb;
65 using namespace ::com::sun::star::sdbcx;
66 using namespace ::com::sun::star::container;
67 using namespace ::com::sun::star::lang;
68 using namespace ::cppu;
69 using namespace ::osl;
71 #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!")
73 // This class calls m_pCacheSet->FOO_checked(..., sal_False)
74 // (where FOO is absolute, last, previous)
75 // when it does not immediately care about the values in the row's columns.
76 // As a corollary, m_pCacheSet may be left in an inconsistent state,
77 // and all ->fillFOO calls (and ->getFOO) may fail or give wrong results,
78 // until m_pCacheSet is moved (or refreshed) again.
79 // So always make sure m_pCacheSet is moved or refreshed before accessing column values.
81 DBG_NAME(ORowSetCache)
83 ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs,
84 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
85 const ::comphelper::ComponentContext& _rContext,
86 const ::rtl::OUString& _rUpdateTableName,
87 sal_Bool& _bModified,
88 sal_Bool& _bNew,
89 const ORowSetValueVector& _aParameterValueForCache,
90 const ::rtl::OUString& i_sRowSetFilter,
91 sal_Int32 i_nMaxRows)
92 :m_xSet(_xRs)
93 ,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY)->getMetaData())
94 ,m_aContext( _rContext )
95 ,m_pCacheSet(NULL)
96 ,m_pMatrix(NULL)
97 ,m_pInsertMatrix(NULL)
98 ,m_nLastColumnIndex(0)
99 ,m_nFetchSize(0)
100 ,m_nRowCount(0)
101 ,m_nPrivileges( Privilege::SELECT )
102 ,m_nPosition(0)
103 ,m_nStartPos(0)
104 ,m_nEndPos(0)
105 ,m_bRowCountFinal(sal_False)
106 ,m_bBeforeFirst(sal_True)
107 ,m_bAfterLast( sal_False )
108 ,m_bUpdated(sal_False)
109 ,m_bModified(_bModified)
110 ,m_bNew(_bNew)
112 DBG_CTOR(ORowSetCache,NULL);
114 // first try if the result can be used to do inserts and updates
115 Reference< XPropertySet> xProp(_xRs,UNO_QUERY);
116 Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo();
117 sal_Bool bBookmarkable = sal_False;
120 Reference< XResultSetUpdate> xUp(_xRs,UNO_QUERY_THROW);
121 bBookmarkable = xPropInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE) &&
122 any2bool(xProp->getPropertyValue(PROPERTY_ISBOOKMARKABLE)) && Reference< XRowLocate >(_xRs, UNO_QUERY).is();
123 if ( bBookmarkable )
125 xUp->moveToInsertRow();
126 xUp->cancelRowUpdates();
127 _xRs->beforeFirst();
128 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
129 m_pCacheSet = new WrappedResultSet(i_nMaxRows);
130 m_xCacheSet = m_pCacheSet;
131 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
132 return;
135 catch(const Exception& ex)
137 (void)ex;
141 if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) &&
142 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY)
143 _xRs->beforeFirst();
145 catch(const SQLException& e)
147 (void)e;
150 // check if all keys of the updateable table are fetched
151 sal_Bool bAllKeysFound = sal_False;
152 sal_Int32 nTablesCount = 0;
154 sal_Bool bNeedKeySet = !bBookmarkable || (xPropInfo->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
155 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY);
157 Reference< XIndexAccess> xUpdateTableKeys;
158 ::rtl::OUString aUpdateTableName = _rUpdateTableName;
159 Reference< XConnection> xConnection;
160 // first we need a connection
161 Reference< XStatement> xStmt(_xRs->getStatement(),UNO_QUERY);
162 if(xStmt.is())
163 xConnection = xStmt->getConnection();
164 else
166 Reference< XPreparedStatement> xPrepStmt(_xRs->getStatement(),UNO_QUERY);
167 xConnection = xPrepStmt->getConnection();
169 OSL_ENSURE(xConnection.is(),"No connection!");
170 if(_xAnalyzer.is())
174 Reference<XTablesSupplier> xTabSup(_xAnalyzer,UNO_QUERY);
175 OSL_ENSURE(xTabSup.is(),"ORowSet::execute composer isn't a tablesupplier!");
176 Reference<XNameAccess> xTables = xTabSup->getTables();
177 Sequence< ::rtl::OUString> aTableNames = xTables->getElementNames();
178 if ( aTableNames.getLength() > 1 && _rUpdateTableName.isEmpty() && bNeedKeySet )
179 {// here we have a join or union and nobody told us which table to update, so we update them all
180 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
181 OptimisticSet* pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
182 m_pCacheSet = pCursor;
183 m_xCacheSet = m_pCacheSet;
186 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
187 if ( pCursor->isReadOnly() )
188 m_nPrivileges = Privilege::SELECT;
189 m_aKeyColumns = pCursor->getJoinedKeyColumns();
190 return;
192 catch(const Exception&)
195 m_pCacheSet = NULL;
196 m_xCacheSet.clear();
198 else
200 if(!_rUpdateTableName.isEmpty() && xTables->hasByName(_rUpdateTableName))
201 xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable;
202 else if(xTables->getElementNames().getLength())
204 aUpdateTableName = xTables->getElementNames()[0];
205 xTables->getByName(aUpdateTableName) >>= m_aUpdateTable;
207 Reference<XIndexAccess> xIndexAccess(xTables,UNO_QUERY);
208 if(xIndexAccess.is())
209 nTablesCount = xIndexAccess->getCount();
210 else
211 nTablesCount = xTables->getElementNames().getLength();
213 if(m_aUpdateTable.is() && nTablesCount < 3) // for we can't handle more than 2 tables in our keyset
215 Reference<XPropertySet> xSet(m_aUpdateTable,UNO_QUERY);
216 const Reference<XNameAccess> xPrimaryKeyColumns = dbtools::getPrimaryKeyColumns_throw(xSet);
217 if ( xPrimaryKeyColumns.is() )
219 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
220 if ( xColSup.is() )
222 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
223 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
224 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() ? true : false);
225 ::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
226 bAllKeysFound = !aColumnNames.empty() && sal_Int32(aColumnNames.size()) == xPrimaryKeyColumns->getElementNames().getLength();
232 catch(Exception&)
237 // first check if resultset is bookmarkable
238 if(!bNeedKeySet)
242 m_pCacheSet = new OBookmarkSet(i_nMaxRows);
243 m_xCacheSet = m_pCacheSet;
244 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
246 // check privileges
247 m_nPrivileges = Privilege::SELECT;
248 if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
250 Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
251 if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
253 m_nPrivileges = 0;
254 xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
255 if(!m_nPrivileges)
256 m_nPrivileges = Privilege::SELECT;
260 catch(const SQLException&)
262 bNeedKeySet = sal_True;
266 if(bNeedKeySet)
268 // need to check if we could handle this select clause
269 bAllKeysFound = bAllKeysFound && (nTablesCount == 1 || checkJoin(xConnection,_xAnalyzer,aUpdateTableName));
271 if(!bAllKeysFound )
273 if ( bBookmarkable )
275 // here I know that we have a read only bookmarkable cursor
276 _xRs->beforeFirst();
277 m_nPrivileges = Privilege::SELECT;
278 m_pCacheSet = new WrappedResultSet(i_nMaxRows);
279 m_xCacheSet = m_pCacheSet;
280 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
281 return;
283 m_pCacheSet = new OStaticSet(i_nMaxRows);
284 m_xCacheSet = m_pCacheSet;
285 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
286 m_nPrivileges = Privilege::SELECT;
288 else
290 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
291 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() ? true : false);
292 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
293 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
294 Reference<XNameAccess> xColumns = m_aUpdateTable->getColumns();
295 ::dbaccess::getColumnPositions(xSelColumns,xColumns->getElementNames(),aUpdateTableName,aColumnNames);
297 // check privileges
298 m_nPrivileges = Privilege::SELECT;
299 sal_Bool bNoInsert = sal_False;
301 Sequence< ::rtl::OUString> aNames(xColumns->getElementNames());
302 const ::rtl::OUString* pIter = aNames.getConstArray();
303 const ::rtl::OUString* pEnd = pIter + aNames.getLength();
304 for(;pIter != pEnd;++pIter)
306 Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),UNO_QUERY);
307 OSL_ENSURE(xColumn.is(),"Column in table is null!");
308 if(xColumn.is())
310 sal_Int32 nNullable = 0;
311 xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
312 if(nNullable == ColumnValue::NO_NULLS && aColumnNames.find(*pIter) == aColumnNames.end())
313 { // we found a column where null is not allowed so we can't insert new values
314 bNoInsert = sal_True;
315 break; // one column is enough
320 OKeySet* pKeySet = new OKeySet(m_aUpdateTable,xUpdateTableKeys,aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
323 m_pCacheSet = pKeySet;
324 m_xCacheSet = m_pCacheSet;
325 pKeySet->construct(_xRs,i_sRowSetFilter);
327 if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
329 Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
330 if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
332 m_nPrivileges = 0;
333 xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
334 if(!m_nPrivileges)
335 m_nPrivileges = Privilege::SELECT;
338 if(bNoInsert)
339 m_nPrivileges |= ~Privilege::INSERT; // remove the insert privilege
341 catch(const SQLException&)
343 // we couldn't create a keyset here so we have to create a static cache
344 if ( m_pCacheSet )
345 m_pCacheSet = NULL;
346 m_xCacheSet = NULL;
347 m_pCacheSet = new OStaticSet(i_nMaxRows);
348 m_xCacheSet = m_pCacheSet;
349 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
350 m_nPrivileges = Privilege::SELECT;
355 // last check
356 if(!bAllKeysFound && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
357 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY)
358 m_nPrivileges = Privilege::SELECT;
361 ORowSetCache::~ORowSetCache()
363 m_pCacheSet = NULL;
364 m_xCacheSet = NULL;
365 if(m_pMatrix)
367 m_pMatrix->clear();
368 delete m_pMatrix;
371 if(m_pInsertMatrix)
373 m_pInsertMatrix->clear();
374 delete m_pInsertMatrix;
376 m_xSet = WeakReference< XResultSet>();
377 m_xMetaData = NULL;
378 m_aUpdateTable = NULL;
380 DBG_DTOR(ORowSetCache,NULL);
383 void ORowSetCache::setFetchSize(sal_Int32 _nSize)
385 if(_nSize == m_nFetchSize)
386 return;
388 m_nFetchSize = _nSize;
389 if(!m_pMatrix)
391 m_pMatrix = new ORowSetMatrix(_nSize);
392 m_aMatrixIter = m_pMatrix->end();
393 m_aMatrixEnd = m_pMatrix->end();
395 m_pInsertMatrix = new ORowSetMatrix(1); // a little bit overkill but ??? :-)
396 m_aInsertRow = m_pInsertMatrix->end();
398 else
400 // now correct the iterator in our iterator vector
401 ::std::vector<sal_Int32> aPositions;
402 ::std::map<sal_Int32,sal_Bool> aCacheIterToChange;
403 // first get the positions where they stand now
404 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
405 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
406 for(;aCacheIter != aCacheEnd;++aCacheIter)
408 aCacheIterToChange[aCacheIter->first] = sal_False;
409 if ( !aCacheIter->second.pRowSet->isInsertRow()
410 /*&& aCacheIter->second.aIterator != m_pMatrix->end()*/ && !m_bModified )
412 ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
413 aPositions.push_back(nDist);
414 aCacheIterToChange[aCacheIter->first] = sal_True;
417 sal_Int32 nKeyPos = (m_aMatrixIter - m_pMatrix->begin());
418 m_pMatrix->resize(_nSize);
420 if ( nKeyPos < _nSize )
421 m_aMatrixIter = m_pMatrix->begin() + nKeyPos;
422 else
423 m_aMatrixIter = m_pMatrix->end();
424 m_aMatrixEnd = m_pMatrix->end();
426 // now adjust their positions because a resize invalidates all iterators
427 ::std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
428 ::std::map<sal_Int32,sal_Bool>::const_iterator aPosChangeIter = aCacheIterToChange.begin();
429 for( aCacheIter = m_aCacheIterators.begin();
430 aPosChangeIter != aCacheIterToChange.end();
431 ++aPosChangeIter,++aCacheIter)
433 if ( aPosChangeIter->second )
435 CHECK_MATRIX_POS(*aIter);
436 if ( *aIter < _nSize )
437 aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++;
438 else
439 aCacheIter->second.aIterator = m_pMatrix->end();
443 if(!m_nPosition)
445 sal_Int32 nNewSt = 0;
446 fillMatrix(nNewSt,_nSize);
447 OSL_ENSURE(nNewSt == 0, "fillMatrix set new start to unexpected value");
448 m_nStartPos = 0;
449 m_nEndPos = _nSize;
451 else if (m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos)
453 sal_Int32 nNewSt = -1;
454 _nSize += m_nStartPos;
455 fillMatrix(nNewSt, _nSize);
456 if (nNewSt >= 0)
458 m_nStartPos = nNewSt;
459 m_nEndPos = _nSize;
460 m_aMatrixIter = calcPosition();
462 else
464 m_nEndPos = m_nStartPos + m_nFetchSize;
467 else
469 OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos");
470 // try to repair
471 moveWindow();
472 m_aMatrixIter = calcPosition();
476 // XResultSetMetaDataSupplier
477 Reference< XResultSetMetaData > ORowSetCache::getMetaData( )
479 return m_xMetaData;
482 static Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
484 switch ( i_aValue.getTypeKind() )
486 case DataType::TINYINT:
487 case DataType::SMALLINT:
488 case DataType::INTEGER:
489 return makeAny((sal_Int32)i_aValue);
490 default:
491 if ( i_pCacheSet && i_aValue.isNull())
492 i_aValue = i_pCacheSet->getBookmark();
493 return i_aValue.getAny();
497 // ::com::sun::star::sdbcx::XRowLocate
498 Any ORowSetCache::getBookmark( )
500 if(m_bAfterLast)
501 throwFunctionSequenceException(m_xSet.get());
503 if ( m_aMatrixIter >= m_pMatrix->end() || m_aMatrixIter < m_pMatrix->begin() || !(*m_aMatrixIter).is())
505 return Any(); // this is allowed here because the rowset knowns what it is doing
508 return lcl_getBookmark(((*m_aMatrixIter)->get())[0],m_pCacheSet);
511 sal_Bool ORowSetCache::moveToBookmark( const Any& bookmark )
513 if ( m_pCacheSet->moveToBookmark(bookmark) )
515 m_bBeforeFirst = sal_False;
516 m_nPosition = m_pCacheSet->getRow();
518 checkPositionFlags();
520 if(!m_bAfterLast)
522 moveWindow();
523 checkPositionFlags();
524 if ( !m_bAfterLast )
526 m_aMatrixIter = calcPosition();
527 OSL_ENSURE(m_aMatrixIter->is(),"Iterator after moveToBookmark not valid");
529 else
530 m_aMatrixIter = m_pMatrix->end();
532 else
533 m_aMatrixIter = m_pMatrix->end();
535 else
536 return sal_False;
538 return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
541 sal_Bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
543 sal_Bool bRet( moveToBookmark( bookmark ) );
544 if ( bRet )
546 m_nPosition = m_pCacheSet->getRow() + rows;
547 absolute(m_nPosition);
549 bRet = m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
552 return bRet;
555 sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second )
557 return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_pCacheSet->compareBookmarks(_first,_second);
560 sal_Bool ORowSetCache::hasOrderedBookmarks( )
562 return m_pCacheSet->hasOrderedBookmarks();
565 sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark )
567 return m_pCacheSet->hashBookmark(bookmark);
570 // XRowUpdate
571 void ORowSetCache::updateNull(sal_Int32 columnIndex,ORowSetValueVector::Vector& io_aRow
572 ,::std::vector<sal_Int32>& o_ChangedColumns
575 checkUpdateConditions(columnIndex);
577 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
578 if ( !rInsert[columnIndex].isNull() )
580 rInsert[columnIndex].setBound(sal_True);
581 rInsert[columnIndex].setNull();
582 rInsert[columnIndex].setModified();
583 io_aRow[columnIndex].setNull();
585 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
586 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
590 void ORowSetCache::updateValue(sal_Int32 columnIndex,const ORowSetValue& x
591 ,ORowSetValueVector::Vector& io_aRow
592 ,::std::vector<sal_Int32>& o_ChangedColumns
595 checkUpdateConditions(columnIndex);
597 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
598 if ( rInsert[columnIndex] != x )
600 rInsert[columnIndex].setBound(sal_True);
601 rInsert[columnIndex] = x;
602 rInsert[columnIndex].setModified();
603 io_aRow[columnIndex] = rInsert[columnIndex];
605 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
606 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
610 void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< ::com::sun::star::io::XInputStream >& x
611 , sal_Int32 length,ORowSetValueVector::Vector& io_aRow
612 ,::std::vector<sal_Int32>& o_ChangedColumns
615 checkUpdateConditions(columnIndex);
617 Sequence<sal_Int8> aSeq;
618 if(x.is())
619 x->readBytes(aSeq,length);
621 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
622 rInsert[columnIndex].setBound(sal_True);
623 rInsert[columnIndex] = aSeq;
624 rInsert[columnIndex].setModified();
625 io_aRow[columnIndex] = makeAny(x);
627 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
628 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
631 void ORowSetCache::updateObject( sal_Int32 columnIndex, const Any& x
632 ,ORowSetValueVector::Vector& io_aRow
633 ,::std::vector<sal_Int32>& o_ChangedColumns
636 checkUpdateConditions(columnIndex);
638 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
639 ORowSetValue aTemp;
640 aTemp.fill(x);
641 if ( rInsert[columnIndex] != aTemp )
643 rInsert[columnIndex].setBound(sal_True);
644 rInsert[columnIndex] = aTemp;
645 rInsert[columnIndex].setModified();
646 io_aRow[columnIndex] = rInsert[columnIndex];
648 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
649 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
653 void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/
654 ,ORowSetValueVector::Vector& io_aRow
655 ,::std::vector<sal_Int32>& o_ChangedColumns
658 checkUpdateConditions(columnIndex);
660 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
661 ORowSetValue aTemp;
662 aTemp.fill(x);
663 if ( rInsert[columnIndex] != aTemp )
665 rInsert[columnIndex].setBound(sal_True);
666 rInsert[columnIndex] = aTemp;
667 rInsert[columnIndex].setModified();
668 io_aRow[columnIndex] = rInsert[columnIndex];
670 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
671 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
675 // XResultSet
676 sal_Bool ORowSetCache::next( )
678 if(!isAfterLast())
680 m_bBeforeFirst = sal_False;
681 ++m_nPosition;
683 // after we increment the position we have to check if we are already after the last row
684 checkPositionFlags();
685 if(!m_bAfterLast)
687 moveWindow();
689 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
690 m_aMatrixIter = calcPosition();
691 checkPositionFlags();
695 return !m_bAfterLast;
698 sal_Bool ORowSetCache::isBeforeFirst( )
700 return m_bBeforeFirst;
703 sal_Bool ORowSetCache::isAfterLast( )
705 return m_bAfterLast;
708 sal_Bool ORowSetCache::isFirst( )
710 return m_nPosition == 1; // ask resultset for
713 sal_Bool ORowSetCache::isLast( )
715 return m_nPosition == m_nRowCount;
718 sal_Bool ORowSetCache::beforeFirst( )
720 if(!m_bBeforeFirst)
722 m_bAfterLast = sal_False;
723 m_nPosition = 0;
724 m_bBeforeFirst = sal_True;
725 m_pCacheSet->beforeFirst();
726 moveWindow();
727 m_aMatrixIter = m_pMatrix->end();
729 return sal_True;
732 sal_Bool ORowSetCache::afterLast( )
734 if(!m_bAfterLast)
736 m_bBeforeFirst = sal_False;
737 m_bAfterLast = sal_True;
739 if(!m_bRowCountFinal)
741 m_pCacheSet->last_checked(sal_False);
742 m_bRowCountFinal = sal_True;
743 m_nRowCount = m_pCacheSet->getRow();// + 1 removed
745 m_pCacheSet->afterLast();
747 m_nPosition = 0;
748 m_aMatrixIter = m_pMatrix->end();
750 return sal_True;
753 sal_Bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos, sal_Int32 &_nNewEndPos)
755 OSL_ENSURE(_nNewStartPos != _nNewEndPos,"ORowSetCache::fillMatrix: StartPos and EndPos can not be equal!");
756 // If _nNewStartPos >= 0, then fill the whole window with new data
757 // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos)
758 // Else, undefined (invalid argument)
759 OSL_ENSURE( _nNewStartPos >= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );
761 ORowSetMatrix::iterator aIter;
762 sal_Int32 i;
763 sal_Bool bCheck;
764 sal_Int32 requestedStartPos;
765 if ( _nNewStartPos == -1 )
767 aIter = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
768 i = m_nEndPos + 1;
769 requestedStartPos = m_nStartPos;
771 else
773 aIter = m_pMatrix->begin();
774 i = _nNewStartPos + 1;
775 requestedStartPos = _nNewStartPos;
777 bCheck = m_pCacheSet->absolute(i);
780 for(; i <= _nNewEndPos; ++i,++aIter)
782 if(bCheck)
784 if(!aIter->is())
785 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
786 m_pCacheSet->fillValueRow(*aIter,i);
788 else
789 { // there are no more rows found so we can fetch some before start
791 if(!m_bRowCountFinal)
793 if(m_pCacheSet->previous_checked(sal_False)) // because we stand after the last row
794 m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
795 if(!m_nRowCount)
796 m_nRowCount = i-1; // it can be that getRow return zero
797 m_bRowCountFinal = sal_True;
799 const ORowSetMatrix::iterator aEnd = aIter;
800 ORowSetMatrix::iterator aRealEnd = m_pMatrix->end();
801 sal_Int32 nPos;
802 if (m_nRowCount >= m_nFetchSize)
804 nPos = m_nRowCount - m_nFetchSize;
806 else
808 nPos = 0;
810 _nNewStartPos = nPos;
811 _nNewEndPos = m_nRowCount;
812 ++nPos;
813 bCheck = m_pCacheSet->absolute(nPos);
815 for(;bCheck && nPos <= requestedStartPos && aIter != aRealEnd; ++aIter, ++nPos)
817 if(!aIter->is())
818 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
819 m_pCacheSet->fillValueRow(*aIter, nPos);
820 bCheck = m_pCacheSet->next();
822 if(aIter != aEnd)
823 ::std::rotate(m_pMatrix->begin(),aEnd,aIter);
824 break;
826 bCheck = m_pCacheSet->next();
828 // we have to read one row forward to ensure that we know when we are on last row
829 // but only when we don't know it already
830 if(!m_bRowCountFinal)
832 if(!m_pCacheSet->next())
834 if(m_pCacheSet->previous_checked(sal_False)) // because we stand after the last row
835 m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
836 m_bRowCountFinal = sal_True;
838 else
839 m_nRowCount = std::max(i,m_nRowCount);
842 return bCheck;
845 // If m_nPosition is out of the current window,
846 // move it and update m_nStartPos and m_nEndPos
847 // Caller is responsible for updating m_aMatrixIter
848 sal_Bool ORowSetCache::moveWindow()
850 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
851 OSL_ENSURE(m_nEndPos >= m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
852 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
854 if ( m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos )
856 // just move inside the window
857 OSL_ENSURE((m_nPosition - m_nStartPos) <= (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
858 // make double plus sure that we have fetched that row
859 m_aMatrixIter = calcPosition();
860 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(), "New m_aMatrixIter is at end(), but should not.");
861 if(!m_aMatrixIter->is())
863 sal_Bool bOk( m_pCacheSet->absolute( m_nPosition ) );
864 if ( bOk )
866 *m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
867 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
868 // we have to read one row forward to ensure that we know when we are on last row
869 // but only when we don't know it already
870 if ( !m_bRowCountFinal )
872 bOk = m_pCacheSet->absolute_checked( m_nPosition + 1,sal_False );
873 if ( bOk )
874 m_nRowCount = std::max(sal_Int32(m_nPosition+1),m_nRowCount);
877 if(!bOk && !m_bRowCountFinal)
879 // because we stand after the last row
880 m_nRowCount = m_pCacheSet->previous_checked(sal_False) ? m_pCacheSet->getRow() : 0;
881 m_bRowCountFinal = sal_True;
884 return sal_True;
887 sal_Bool bRet = sal_True;
889 sal_Int32 nDiff = (m_nFetchSize - 1) / 2;
890 sal_Int32 nNewStartPos = (m_nPosition - nDiff) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
891 sal_Int32 nNewEndPos = nNewStartPos + m_nFetchSize;
893 if ( nNewStartPos < 0 )
895 // The computed new window crashes through the floor (begins before first row);
896 // nNew*Pos has to be shifted by -nNewStartPos
897 nNewEndPos -= nNewStartPos;
898 nNewStartPos = 0;
901 if ( nNewStartPos < m_nStartPos )
902 { // need to fill data *before* m_nStartPos
903 if ( nNewEndPos > m_nStartPos )
904 { // The two regions are overlapping.
905 // We'll first rotate the contents of m_pMatrix so that the overlap area
906 // is positioned right; in the old window it is at the beginning,
907 // it has to go to the end.
908 // then we fill in the rows between new and old start pos.
910 sal_Bool bCheck;
911 bCheck = m_pCacheSet->absolute(nNewStartPos + 1);
913 // m_nEndPos < nNewEndPos when window not filled (e.g. there are less rows in total than window size)
914 m_nEndPos = std::min(nNewEndPos, m_nEndPos);
915 const sal_Int32 nOverlapSize = m_nEndPos - m_nStartPos;
916 const sal_Int32 nStartPosOffset = m_nStartPos - nNewStartPos; // by how much m_nStartPos moves
917 m_nStartPos = nNewStartPos;
918 OSL_ENSURE( static_cast<ORowSetMatrix::size_type>(nOverlapSize) <= m_pMatrix->size(), "new window end is after end of cache matrix!" );
919 // the first position in m_pMatrix whose data we don't keep;
920 // content will be moved to m_pMatrix.begin()
921 ORowSetMatrix::iterator aEnd (m_pMatrix->begin() + nOverlapSize);
922 // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
923 ORowSetMatrix::iterator aNewEnd (aEnd + nStartPosOffset);
924 // *m_pMatrix now looks like:
925 // [0; nOverlapSize) i.e. [begin(); aEnd): data kept
926 // [nOverlapSize; nOverlapSize + nStartPosOffet) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos
927 // [nOverlapSize + nStartPosOffet; size()) i.e. [aNewEnd, end()): unused
928 // Note that nOverlapSize + nStartPosOffet == m_nEndPos - m_nStartPos (new values)
929 // When we are finished:
930 // [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos
931 // [nStartPosOffset; nOverlapSize + nStartPosOffet) i.e. [aEnd, aNewEnd): kept
932 // [nOverlapSize + nStartPosOffet; size()) i.e. [aNewEnd, end()): unused
934 if ( bCheck )
937 ORowSetMatrix::iterator aIter(aEnd);
938 sal_Int32 nPos = m_nStartPos + 1;
939 bCheck = fill(aIter, aNewEnd, nPos, bCheck);
942 ::std::rotate(m_pMatrix->begin(), aEnd, aNewEnd);
943 // now correct the iterator in our iterator vector
944 // rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment
945 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
946 const ORowSetCacheMap::const_iterator aCacheEnd = m_aCacheIterators.end();
947 for(;aCacheIter != aCacheEnd;++aCacheIter)
949 if ( !aCacheIter->second.pRowSet->isInsertRow()
950 && aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
952 const ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
953 if ( nDist >= nOverlapSize )
955 // That's from outside the overlap area; invalidate iterator.
956 aCacheIter->second.aIterator = m_pMatrix->end();
958 else
960 // Inside overlap area: move to correct position
961 CHECK_MATRIX_POS( (nDist + nStartPosOffset) );
962 aCacheIter->second.aIterator += nStartPosOffset;
963 OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
964 && aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
969 else
970 { // normally this should never happen
971 OSL_FAIL("What the hell is happen here!");
972 return sal_False;
975 else
976 {// no rows can be reused so fill again
977 bRet = reFillMatrix(nNewStartPos,nNewEndPos);
981 OSL_ENSURE(nNewStartPos >= m_nStartPos, "ORowSetCache::moveWindow internal error: new start pos before current start pos");
982 if ( m_nEndPos < nNewEndPos )
983 { // need to fill data *after* m_nEndPos
984 if( nNewStartPos < m_nEndPos )
985 { // The two regions are overlapping.
986 const sal_Int32 nRowsInCache = m_nEndPos - m_nStartPos;
987 if ( nRowsInCache < m_nFetchSize )
989 // There is some unused space in *m_pMatrix; fill it
990 CHECK_MATRIX_POS(nRowsInCache);
991 sal_Int32 nPos = m_nEndPos + 1;
992 sal_Bool bCheck = m_pCacheSet->absolute(nPos);
993 ORowSetMatrix::iterator aIter = m_pMatrix->begin() + nRowsInCache;
994 const sal_Int32 nRowsToFetch = std::min(nNewEndPos-m_nEndPos, m_nFetchSize-nRowsInCache);
995 const ORowSetMatrix::const_iterator aEnd = aIter + nRowsToFetch;
996 bCheck = fill(aIter, aEnd, nPos, bCheck);
997 m_nEndPos = nPos - 1;
998 OSL_ENSURE( (!bCheck && m_nEndPos <= nNewEndPos ) ||
999 ( bCheck && m_nEndPos == nNewEndPos ),
1000 "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
1003 // À priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive]
1004 // have to be refilled with new to-be-fetched rows.
1005 // The rows behind this can be reused
1006 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1007 const sal_Int32 nNewStartPosInMatrix = nNewStartPos - m_nStartPos;
1008 CHECK_MATRIX_POS( nNewStartPosInMatrix );
1009 // first position we reuse
1010 const ORowSetMatrix::const_iterator aEnd = m_pMatrix->begin() + nNewStartPosInMatrix;
1011 // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
1012 ORowSetMatrix::iterator aDataEnd = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
1014 sal_Int32 nPos = m_nEndPos + 1;
1015 sal_Bool bCheck = m_pCacheSet->absolute(nPos);
1016 bCheck = fill(aIter, aEnd, nPos, bCheck); // refill the region we don't need anymore
1017 //aIter and nPos are now the position *after* last filled in one!
1019 // bind end to front
1020 if(bCheck)
1022 OSL_ENSURE(aIter == aEnd, "fill() said went till end, but did not.");
1023 // rotate the end to the front
1024 ::std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
1025 // now correct the iterator in our iterator vector
1026 rotateCacheIterator( nNewStartPosInMatrix );
1027 m_nStartPos = nNewStartPos;
1028 m_nEndPos = nNewEndPos;
1029 // now I can say how many rows we have
1030 // we have to read one row forward to ensure that we know when we are on last row
1031 // but only when we don't know it already
1032 sal_Bool bOk = sal_True;
1033 if(!m_bRowCountFinal)
1034 bOk = m_pCacheSet->next();
1035 if(!bOk)
1037 m_pCacheSet->previous_checked(sal_False); // because we stand after the last row
1038 m_nRowCount = nPos; // here we have the row count
1039 OSL_ENSURE(nPos == m_pCacheSet->getRow(),"nPos is not valid!");
1040 m_bRowCountFinal = sal_True;
1042 else if(!m_bRowCountFinal)
1043 m_nRowCount = std::max(nPos+1, m_nRowCount); //+1 because we successfully moved to row after nPos
1044 else
1045 OSL_ENSURE(m_nRowCount >= nPos, "Final m_nRowCount is smaller than row I moved to!");
1047 else
1048 { // the end was reached before or at end() so we can set the start before or at nNewStartPos
1049 // and possibly keep more of m_pMatrix than planned.
1050 const ORowSetMatrix::iterator::difference_type nFetchedRows = aIter - m_pMatrix->begin();
1051 // *m_pMatrix now looks like:
1052 // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows
1053 // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???
1055 nPos -= 1;
1056 m_nStartPos += nFetchedRows;
1057 m_nEndPos = nPos;
1058 ::std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
1059 // now correct the iterator in our iterator vector
1060 rotateCacheIterator( nFetchedRows );
1062 if ( !m_bRowCountFinal )
1064 m_pCacheSet->previous_checked(sal_False); // because we stand after the last row
1065 m_nRowCount = std::max(m_nRowCount, nPos); // here we have the row count
1066 OSL_ENSURE(nPos == m_pCacheSet->getRow(),"nPos isn't valid!");
1067 m_bRowCountFinal = sal_True;
1071 // here we need only to check if the beginning row is valid. If not we have to fetch it.
1072 if(!m_pMatrix->begin()->is())
1074 aIter = m_pMatrix->begin();
1076 nPos = m_nStartPos + 1;
1077 bCheck = m_pCacheSet->absolute_checked(nPos, sal_True);
1078 for(; !aIter->is() && bCheck;++aIter, ++nPos)
1080 OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator");
1082 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
1083 m_pCacheSet->fillValueRow(*aIter, nPos);
1085 bCheck = m_pCacheSet->next();
1089 else // no rows can be reused so fill again
1090 bRet = reFillMatrix(nNewStartPos,nNewEndPos);
1093 if(!m_bRowCountFinal)
1094 m_nRowCount = std::max(m_nPosition,m_nRowCount);
1095 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
1096 OSL_ENSURE(m_nEndPos > m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
1097 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
1099 return bRet;
1102 sal_Bool ORowSetCache::first( )
1104 // First move to the first row.
1105 // Then check if the cache window is at the beginning.
1106 // If not, then position the window and fill it with data.
1107 // We move the window smartly, i.e. we clear only the rows that are out of range
1108 sal_Bool bRet = m_pCacheSet->first();
1109 if(bRet)
1111 m_bBeforeFirst = m_bAfterLast = sal_False;
1112 m_nPosition = 1;
1113 moveWindow();
1114 m_aMatrixIter = m_pMatrix->begin();
1116 else
1118 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = sal_True;
1119 m_nRowCount = m_nPosition = 0;
1121 OSL_ENSURE(m_bBeforeFirst || m_bNew,"ORowSetCache::first return false and BeforeFirst isn't true");
1122 m_aMatrixIter = m_pMatrix->end();
1124 return bRet;
1127 sal_Bool ORowSetCache::last( )
1129 sal_Bool bRet = m_pCacheSet->last();
1130 if(bRet)
1132 m_bBeforeFirst = m_bAfterLast = sal_False;
1133 if(!m_bRowCountFinal)
1135 m_bRowCountFinal = sal_True;
1136 m_nRowCount = m_pCacheSet->getRow(); // not + 1
1138 m_nPosition = m_pCacheSet->getRow();
1139 moveWindow();
1140 // we have to repositioning because moveWindow can modify the cache
1141 m_pCacheSet->last();
1142 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1143 m_aMatrixIter = calcPosition();
1145 else
1147 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = sal_True;
1148 m_nRowCount = m_nPosition = 0;
1149 OSL_ENSURE(m_bBeforeFirst,"ORowSetCache::last return false and BeforeFirst isn't true");
1150 m_aMatrixIter = m_pMatrix->end();
1152 #if OSL_DEBUG_LEVEL > 1
1153 if(bRet)
1155 OSL_ENSURE((*m_aMatrixIter).is(),"ORowSetCache::last: Row not valid!");
1157 #endif
1159 return bRet;
1162 sal_Int32 ORowSetCache::getRow( )
1164 return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition;
1167 sal_Bool ORowSetCache::absolute( sal_Int32 row )
1169 if(!row )
1170 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_ABS_ZERO),NULL,SQLSTATE_GENERAL,1000,Any() );
1172 if(row < 0)
1174 // here we have to scroll from the last row to backward so we have to go to last row and
1175 // and two the previous
1176 if(m_bRowCountFinal || last())
1178 m_nPosition = m_nRowCount + row + 1; // + row because row is negative and +1 because row==-1 means last row
1179 if(m_nPosition < 1)
1181 m_bBeforeFirst = sal_True;
1182 m_bAfterLast = sal_False;
1183 m_aMatrixIter = m_pMatrix->end();
1185 else
1187 m_bBeforeFirst = sal_False;
1188 m_bAfterLast = m_nPosition > m_nRowCount;
1189 moveWindow();
1190 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1191 m_aMatrixIter = calcPosition();
1194 else
1195 m_aMatrixIter = m_pMatrix->end();
1197 else
1199 m_nPosition = row;
1200 // the position flags
1201 m_bBeforeFirst = sal_False;
1202 checkPositionFlags();
1204 if(!m_bAfterLast)
1206 moveWindow();
1207 checkPositionFlags();
1208 if(!m_bAfterLast)
1209 m_aMatrixIter = calcPosition();
1210 else
1211 m_aMatrixIter = m_pMatrix->end();
1213 else
1214 m_aMatrixIter = m_pMatrix->end();
1217 return !(m_bAfterLast || m_bBeforeFirst);
1220 sal_Bool ORowSetCache::relative( sal_Int32 rows )
1222 sal_Bool bErg = sal_True;
1223 if(rows)
1225 sal_Int32 nNewPosition = m_nPosition + rows;
1227 if ( m_bBeforeFirst && rows > 0 )
1228 nNewPosition = rows;
1229 else if ( m_bRowCountFinal && m_bAfterLast && rows < 0 )
1230 nNewPosition = m_nRowCount + 1 + rows;
1231 else
1232 if ( m_bBeforeFirst || ( m_bRowCountFinal && m_bAfterLast ) )
1233 throw SQLException( DBACORE_RESSTRING( RID_STR_NO_RELATIVE ), NULL, SQLSTATE_GENERAL, 1000, Any() );
1234 if ( nNewPosition )
1236 bErg = absolute( nNewPosition );
1237 bErg = bErg && !isAfterLast() && !isBeforeFirst();
1239 else
1241 m_bBeforeFirst = sal_True;
1242 bErg = sal_False;
1245 return bErg;
1248 sal_Bool ORowSetCache::previous( )
1250 sal_Bool bRet = sal_False;
1251 if(!isBeforeFirst())
1253 if(m_bAfterLast) // we stand after the last row so one before is the last row
1254 bRet = last();
1255 else
1257 m_bAfterLast = sal_False;
1258 --m_nPosition;
1259 moveWindow();
1260 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1262 checkPositionFlags();
1264 if(!m_nPosition)
1266 m_bBeforeFirst = sal_True;
1267 m_aMatrixIter = m_pMatrix->end();
1269 else
1271 m_aMatrixIter = calcPosition();
1272 bRet = (*m_aMatrixIter).is();
1276 return bRet;
1279 void ORowSetCache::refreshRow( )
1281 if(isAfterLast())
1282 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_REFESH_AFTERLAST),NULL,SQLSTATE_GENERAL,1000,Any() );
1283 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!");
1284 m_pCacheSet->refreshRow();
1285 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1286 if ( m_bNew )
1288 cancelRowModification();
1292 sal_Bool ORowSetCache::rowUpdated( )
1294 return m_pCacheSet->rowUpdated();
1297 sal_Bool ORowSetCache::rowInserted( )
1299 return m_pCacheSet->rowInserted();
1302 // XResultSetUpdate
1303 sal_Bool ORowSetCache::insertRow(::std::vector< Any >& o_aBookmarks)
1305 if ( !m_bNew || !m_aInsertRow->is() )
1306 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_MOVETOINSERTROW_CALLED),NULL,SQLSTATE_GENERAL,1000,Any() );
1308 m_pCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable);
1310 sal_Bool bRet( rowInserted() );
1311 if ( bRet )
1313 ++m_nRowCount;
1314 Any aBookmark = ((*m_aInsertRow)->get())[0].makeAny();
1315 m_bAfterLast = m_bBeforeFirst = sal_False;
1316 if(aBookmark.hasValue())
1318 moveToBookmark(aBookmark);
1319 // update the cached values
1320 ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
1321 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1322 for(;aIter != m_pMatrix->end();++aIter)
1324 if ( m_aMatrixIter != aIter && aIter->is() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
1326 o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
1330 else
1332 OSL_FAIL("There must be a bookmark after the row was inserted!");
1335 return bRet;
1338 void ORowSetCache::resetInsertRow(sal_Bool _bClearInsertRow)
1340 if ( _bClearInsertRow )
1341 clearInsertRow();
1342 m_bNew = sal_False;
1343 m_bModified = sal_False;
1346 void ORowSetCache::cancelRowModification()
1348 // clear the insertrow references -> implies that the current row of the rowset changes as well
1349 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1350 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
1351 for(;aCacheIter != aCacheEnd;++aCacheIter)
1353 if ( aCacheIter->second.pRowSet->isInsertRow() && aCacheIter->second.aIterator == m_aInsertRow )
1354 aCacheIter->second.aIterator = m_pMatrix->end();
1356 resetInsertRow(sal_False);
1359 void ORowSetCache::updateRow( ORowSetMatrix::iterator& _rUpdateRow,::std::vector< Any >& o_aBookmarks )
1361 if(isAfterLast() || isBeforeFirst())
1362 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_UPDATEROW),NULL,SQLSTATE_GENERAL,1000,Any() );
1364 Any aBookmark = ((*_rUpdateRow)->get())[0].makeAny();
1365 OSL_ENSURE(aBookmark.hasValue(),"Bookmark must have a value!");
1366 // here we don't have to reposition our CacheSet, when we try to update a row,
1367 // the row was already fetched
1368 moveToBookmark(aBookmark);
1369 m_pCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable);
1370 // refetch the whole row
1371 (*m_aMatrixIter) = NULL;
1373 if ( moveToBookmark(aBookmark) )
1375 // update the cached values
1376 ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
1377 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1378 for(;aIter != m_pMatrix->end();++aIter)
1380 if ( m_aMatrixIter != aIter && aIter->is() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
1382 o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
1387 m_bModified = sal_False;
1390 bool ORowSetCache::deleteRow( )
1392 if(isAfterLast() || isBeforeFirst())
1393 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_DELETEROW),NULL,SQLSTATE_GENERAL,1000,Any() );
1395 m_pCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable);
1396 if ( !m_pCacheSet->rowDeleted() )
1397 return false;
1399 --m_nRowCount;
1400 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1401 ORowSetMatrix::iterator aPos = calcPosition();
1402 (*aPos) = NULL;
1404 ORowSetMatrix::iterator aEnd = m_pMatrix->end();
1405 for(++aPos;aPos != aEnd && aPos->is();++aPos)
1407 *(aPos-1) = *aPos;
1408 (*aPos) = NULL;
1410 m_aMatrixIter = m_pMatrix->end();
1412 --m_nPosition;
1413 return true;
1416 void ORowSetCache::cancelRowUpdates( )
1418 m_bNew = m_bModified = sal_False;
1419 if(!m_nPosition)
1421 OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0");
1422 ::dbtools::throwFunctionSequenceException(NULL);
1425 if(m_pCacheSet->absolute(m_nPosition))
1426 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1427 else
1429 OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
1430 ::dbtools::throwFunctionSequenceException(NULL);
1434 void ORowSetCache::moveToInsertRow( )
1436 m_bNew = sal_True;
1437 m_bUpdated = m_bAfterLast = sal_False;
1439 m_aInsertRow = m_pInsertMatrix->begin();
1440 if(!m_aInsertRow->is())
1441 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1443 // we don't unbound the bookmark column
1444 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
1445 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1446 for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i)
1448 aIter->setBound(sal_False);
1449 aIter->setModified(sal_False);
1450 aIter->setNull();
1451 aIter->setTypeKind(m_xMetaData->getColumnType(i));
1455 ORowSetCacheIterator ORowSetCache::createIterator(ORowSetBase* _pRowSet)
1457 ORowSetCacheIterator_Helper aHelper;
1458 aHelper.aIterator = m_pMatrix->end();
1459 aHelper.pRowSet = _pRowSet;
1460 return ORowSetCacheIterator(m_aCacheIterators.insert(m_aCacheIterators.begin(),ORowSetCacheMap::value_type(m_aCacheIterators.size()+1,aHelper)),this,_pRowSet);
1463 void ORowSetCache::deleteIterator(const ORowSetBase* _pRowSet)
1465 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1466 for(;aCacheIter != m_aCacheIterators.end();)
1468 if ( aCacheIter->second.pRowSet == _pRowSet )
1470 m_aCacheIterators.erase(aCacheIter);
1471 aCacheIter = m_aCacheIterators.begin();
1473 else
1474 ++aCacheIter;
1478 void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist)
1480 if(_nDist)
1482 // now correct the iterator in our iterator vector
1483 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1484 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
1485 for(;aCacheIter != aCacheEnd;++aCacheIter)
1487 if ( !aCacheIter->second.pRowSet->isInsertRow()
1488 && aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
1490 ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
1491 if(nDist < _nDist)
1493 aCacheIter->second.aIterator = m_pMatrix->end();
1495 else
1497 OSL_ENSURE((aCacheIter->second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!");
1498 aCacheIter->second.aIterator -= _nDist;
1499 OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
1500 && aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
1507 void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow)
1509 m_aInsertRow = m_pInsertMatrix->begin();
1510 if(!m_aInsertRow->is())
1511 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1513 (*(*m_aInsertRow)) = (*(*_rOriginalRow));
1514 // we don't unbound the bookmark column
1515 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin();
1516 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1517 for(;aIter != aEnd;++aIter)
1518 aIter->setModified(sal_False);
1521 void ORowSetCache::checkPositionFlags()
1523 if(m_bRowCountFinal)
1525 m_bAfterLast = m_nPosition > m_nRowCount;
1526 if(m_bAfterLast)
1527 m_nPosition = 0;//m_nRowCount;
1531 void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex)
1533 if(m_bAfterLast || columnIndex >= (sal_Int32)(*m_aInsertRow)->get().size())
1534 throwFunctionSequenceException(m_xSet.get());
1537 sal_Bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const ::rtl::OUString& _sUpdateTableName)
1539 sal_Bool bOk = sal_False;
1540 if (pNode->count() == 3 && // Ausdruck is geklammert
1541 SQL_ISPUNCTUATION(pNode->getChild(0),"(") &&
1542 SQL_ISPUNCTUATION(pNode->getChild(2),")"))
1544 bOk = checkInnerJoin(pNode->getChild(1),_xConnection,_sUpdateTableName);
1546 else if ((SQL_ISRULE(pNode,search_condition) || SQL_ISRULE(pNode,boolean_term)) && // AND/OR link
1547 pNode->count() == 3)
1549 // only allow an AND link
1550 if ( SQL_ISTOKEN(pNode->getChild(1),AND) )
1551 bOk = checkInnerJoin(pNode->getChild(0),_xConnection,_sUpdateTableName)
1552 && checkInnerJoin(pNode->getChild(2),_xConnection,_sUpdateTableName);
1554 else if (SQL_ISRULE(pNode,comparison_predicate))
1556 // only the comparison of columns is allowed
1557 OSL_ENSURE(pNode->count() == 3,"checkInnerJoin: Fehler im Parse Tree");
1558 if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
1559 SQL_ISRULE(pNode->getChild(2),column_ref) &&
1560 pNode->getChild(1)->getNodeType() == SQL_NODE_EQUAL))
1562 bOk = sal_False;
1564 ::rtl::OUString sColumnName,sTableRange;
1565 OSQLParseTreeIterator::getColumnRange( pNode->getChild(0), _xConnection, sColumnName, sTableRange );
1566 bOk = sTableRange == _sUpdateTableName;
1567 if ( !bOk )
1569 OSQLParseTreeIterator::getColumnRange( pNode->getChild(2), _xConnection, sColumnName, sTableRange );
1570 bOk = sTableRange == _sUpdateTableName;
1573 return bOk;
1576 sal_Bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection,
1577 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
1578 const ::rtl::OUString& _sUpdateTableName )
1580 sal_Bool bOk = sal_False;
1581 ::rtl::OUString sSql = _xAnalyzer->getQuery();
1582 ::rtl::OUString sErrorMsg;
1583 ::connectivity::OSQLParser aSqlParser( m_aContext.getUNOContext() );
1584 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1585 ::std::auto_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql));
1586 SAL_WNODEPRECATED_DECLARATIONS_POP
1587 if ( pSqlParseNode.get() && SQL_ISRULE(pSqlParseNode, select_statement) )
1589 OSQLParseNode* pTableRefCommalist = pSqlParseNode->getByRule(::connectivity::OSQLParseNode::table_ref_commalist);
1590 OSL_ENSURE(pTableRefCommalist,"NO tables why!?");
1591 if(pTableRefCommalist && pTableRefCommalist->count() == 1)
1593 // we found only one element so it must some kind of join here
1594 OSQLParseNode* pJoin = pTableRefCommalist->getByRule(::connectivity::OSQLParseNode::qualified_join);
1595 if(pJoin)
1596 { // we are only intereseted in qualified joins like RIGHT or LEFT
1597 OSQLParseNode* pJoinType = pJoin->getChild(1);
1598 OSQLParseNode* pOuterType = NULL;
1599 if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2)
1600 pOuterType = pJoinType->getChild(0);
1601 else if(SQL_ISRULE(pJoinType,outer_join_type))
1602 pOuterType = pJoinType;
1604 sal_Bool bCheck = sal_False;
1605 sal_Bool bLeftSide = sal_False;
1606 if(pOuterType)
1607 { // found outer join
1608 bLeftSide = SQL_ISTOKEN(pOuterType->getChild(0),LEFT);
1609 bCheck = bLeftSide || SQL_ISTOKEN(pOuterType->getChild(0),RIGHT);
1612 if(bCheck)
1613 { // here we know that we have to check on which side our table resides
1614 const OSQLParseNode* pTableRef = pJoin->getByRule(::connectivity::OSQLParseNode::qualified_join);
1615 if(bLeftSide)
1616 pTableRef = pJoin->getChild(0);
1617 else
1618 pTableRef = pJoin->getChild(3);
1619 OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");
1621 ::rtl::OUString sTableRange = OSQLParseNode::getTableRange(pTableRef);
1622 if(sTableRange.isEmpty())
1623 pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, NULL, sal_False, sal_False );
1624 bOk = sTableRange == _sUpdateTableName;
1628 else
1630 OSQLParseNode* pWhereOpt = pSqlParseNode->getChild(3)->getChild(1);
1631 if ( pWhereOpt && !pWhereOpt->isLeaf() )
1632 bOk = checkInnerJoin(pWhereOpt->getChild(1),_xConnection,_sUpdateTableName);
1635 return bOk;
1638 void ORowSetCache::clearInsertRow()
1640 // we don't unbound the bookmark column
1641 if ( m_aInsertRow != m_pInsertMatrix->end() && m_aInsertRow->is() )
1643 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
1644 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1645 for(;aIter != aEnd;++aIter)
1647 aIter->setBound(sal_False);
1648 aIter->setModified(sal_False);
1649 aIter->setNull();
1654 ORowSetMatrix::iterator ORowSetCache::calcPosition() const
1656 sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1;
1657 CHECK_MATRIX_POS(nValue);
1658 return ( nValue < 0 || nValue >= static_cast<sal_Int32>(m_pMatrix->size()) ) ? m_pMatrix->end() : (m_pMatrix->begin() + nValue);
1661 TORowSetOldRowHelperRef ORowSetCache::registerOldRow()
1663 TORowSetOldRowHelperRef pRef = new ORowSetOldRowHelper(ORowSetRow());
1664 m_aOldRows.push_back(pRef);
1665 return pRef;
1668 void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef& _rRow)
1670 TOldRowSetRows::iterator aOldRowEnd = m_aOldRows.end();
1671 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1673 if ( aOldRowIter->get() == _rRow.get() )
1675 m_aOldRows.erase(aOldRowIter);
1676 break;
1682 sal_Bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos,sal_Int32 _nNewEndPos)
1684 OSL_ENSURE( _nNewEndPos - _nNewStartPos == m_nFetchSize, "reFillMatrix called with Start/EndPos not m_nFetchSize apart");
1685 const TOldRowSetRows::const_iterator aOldRowEnd = m_aOldRows.end();
1686 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1688 if ( aOldRowIter->is() && (*aOldRowIter)->getRow().is() )
1689 (*aOldRowIter)->setRow(new ORowSetValueVector( *((*aOldRowIter)->getRow()) ) );
1691 sal_Int32 nNewSt = _nNewStartPos;
1692 sal_Bool bRet = fillMatrix(nNewSt,_nNewEndPos);
1693 m_nStartPos = nNewSt;
1694 m_nEndPos = _nNewEndPos;
1695 rotateCacheIterator(static_cast<ORowSetMatrix::difference_type>(m_nFetchSize+1)); // invalidate every iterator
1696 return bRet;
1699 sal_Bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter,const ORowSetMatrix::const_iterator& _aEnd,sal_Int32& _nPos,sal_Bool _bCheck)
1701 const sal_Int32 nColumnCount = m_xMetaData->getColumnCount();
1702 for(; _bCheck && _aIter != _aEnd; _aIter++, _nPos++)
1704 if ( !_aIter->is() )
1705 *_aIter = new ORowSetValueVector(nColumnCount);
1706 else
1708 const TOldRowSetRows::const_iterator aOldRowEnd = m_aOldRows.end();
1709 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1711 if ( (*aOldRowIter)->getRow() == *_aIter )
1712 *_aIter = new ORowSetValueVector(nColumnCount);
1715 m_pCacheSet->fillValueRow(*_aIter, _nPos);
1716 _bCheck = m_pCacheSet->next();
1718 return _bCheck;
1721 bool ORowSetCache::isResultSetChanged() const
1723 return m_pCacheSet->isResultSetChanged();
1726 void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet)
1728 m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY)->getMetaData());
1729 m_pCacheSet->reset(_xDriverSet);
1731 m_bRowCountFinal = sal_False;
1732 m_nRowCount = 0;
1733 reFillMatrix(m_nStartPos,m_nEndPos);
1736 void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow
1737 ,::std::vector<sal_Int32>& o_ChangedColumns)
1739 if ( o_ChangedColumns.size() > 1 )
1741 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1742 for(;aIter != m_pMatrix->end();++aIter)
1744 if ( aIter->is() && m_pCacheSet->updateColumnValues((*aIter)->get(),io_aRow,o_ChangedColumns))
1746 break;
1750 if ( aIter == m_pMatrix->end() )
1752 m_pCacheSet->fillMissingValues(io_aRow);
1756 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */