Bump version to 5.0-14
[LibreOffice.git] / dbaccess / source / core / api / RowSetCache.cxx
blob2daf08137172954a0352593b0999e08afc08ab8b
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 .
20 #include "BookmarkSet.hxx"
21 #include "CRowSetColumn.hxx"
22 #include "CRowSetDataColumn.hxx"
23 #include "KeySet.hxx"
24 #include "OptimisticSet.hxx"
25 #include "RowSetBase.hxx"
26 #include "RowSetCache.hxx"
27 #include "StaticSet.hxx"
28 #include "WrappedResultSet.hxx"
29 #include "core_resource.hrc"
30 #include "core_resource.hxx"
31 #include "dbastrings.hrc"
33 #include <com/sun/star/sdbc/ColumnValue.hpp>
34 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
35 #include <com/sun/star/sdbcx/CompareBookmark.hpp>
36 #include <com/sun/star/sdbcx/KeyType.hpp>
37 #include <com/sun/star/sdbcx/Privilege.hpp>
38 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
39 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
40 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
42 #include <comphelper/extract.hxx>
43 #include <comphelper/property.hxx>
44 #include <comphelper/seqstream.hxx>
45 #include <comphelper/uno3.hxx>
46 #include <connectivity/dbexception.hxx>
47 #include <connectivity/dbtools.hxx>
48 #include <connectivity/sqliterator.hxx>
49 #include <connectivity/sqlnode.hxx>
50 #include <connectivity/sqlparse.hxx>
51 #include "sqlbison.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.
82 ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs,
83 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
84 const Reference<XComponentContext>& _rContext,
85 const OUString& _rUpdateTableName,
86 bool& _bModified,
87 bool& _bNew,
88 const ORowSetValueVector& _aParameterValueForCache,
89 const OUString& i_sRowSetFilter,
90 sal_Int32 i_nMaxRows)
91 :m_xSet(_xRs)
92 ,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY)->getMetaData())
93 ,m_aContext( _rContext )
94 ,m_pCacheSet(NULL)
95 ,m_pMatrix(NULL)
96 ,m_pInsertMatrix(NULL)
97 ,m_nLastColumnIndex(0)
98 ,m_nFetchSize(0)
99 ,m_nRowCount(0)
100 ,m_nPrivileges( Privilege::SELECT )
101 ,m_nPosition(0)
102 ,m_nStartPos(0)
103 ,m_nEndPos(0)
104 ,m_bRowCountFinal(false)
105 ,m_bBeforeFirst(true)
106 ,m_bAfterLast( false )
107 ,m_bUpdated(false)
108 ,m_bModified(_bModified)
109 ,m_bNew(_bNew)
112 // first try if the result can be used to do inserts and updates
113 Reference< XPropertySet> xProp(_xRs,UNO_QUERY);
114 Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo();
115 bool bBookmarkable = false;
118 Reference< XResultSetUpdate> xUp(_xRs,UNO_QUERY_THROW);
119 bBookmarkable = xPropInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE) &&
120 any2bool(xProp->getPropertyValue(PROPERTY_ISBOOKMARKABLE)) && Reference< XRowLocate >(_xRs, UNO_QUERY).is();
121 if ( bBookmarkable )
123 xUp->moveToInsertRow();
124 xUp->cancelRowUpdates();
125 _xRs->beforeFirst();
126 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
127 m_pCacheSet = new WrappedResultSet(i_nMaxRows);
128 m_xCacheSet = m_pCacheSet;
129 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
130 return;
133 catch(const Exception& ex)
135 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << ex.Message);
139 if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) &&
140 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY)
141 _xRs->beforeFirst();
143 catch(const SQLException& e)
145 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << e.Message);
148 // check if all keys of the updateable table are fetched
149 bool bAllKeysFound = false;
150 sal_Int32 nTablesCount = 0;
152 bool bNeedKeySet = !bBookmarkable || (xPropInfo->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
153 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY);
155 Reference< XIndexAccess> xUpdateTableKeys;
156 OUString aUpdateTableName = _rUpdateTableName;
157 Reference< XConnection> xConnection;
158 // first we need a connection
159 Reference< XStatement> xStmt(_xRs->getStatement(),UNO_QUERY);
160 if(xStmt.is())
161 xConnection = xStmt->getConnection();
162 else
164 Reference< XPreparedStatement> xPrepStmt(_xRs->getStatement(),UNO_QUERY);
165 xConnection = xPrepStmt->getConnection();
167 OSL_ENSURE(xConnection.is(),"No connection!");
168 if(_xAnalyzer.is())
172 Reference<XTablesSupplier> xTabSup(_xAnalyzer,UNO_QUERY);
173 OSL_ENSURE(xTabSup.is(),"ORowSet::execute composer isn't a tablesupplier!");
174 Reference<XNameAccess> xTables = xTabSup->getTables();
175 Sequence< OUString> aTableNames = xTables->getElementNames();
176 if ( aTableNames.getLength() > 1 && _rUpdateTableName.isEmpty() && bNeedKeySet )
177 {// here we have a join or union and nobody told us which table to update, so we update them all
178 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
179 OptimisticSet* pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
180 m_pCacheSet = pCursor;
181 m_xCacheSet = m_pCacheSet;
184 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
185 if ( pCursor->isReadOnly() )
186 m_nPrivileges = Privilege::SELECT;
187 m_aKeyColumns = pCursor->getJoinedKeyColumns();
188 return;
190 catch (const Exception& e)
192 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << e.Message);
194 m_pCacheSet = NULL;
195 m_xCacheSet.clear();
197 else
199 if(!_rUpdateTableName.isEmpty() && xTables->hasByName(_rUpdateTableName))
200 xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable;
201 else if(xTables->getElementNames().getLength())
203 aUpdateTableName = xTables->getElementNames()[0];
204 xTables->getByName(aUpdateTableName) >>= m_aUpdateTable;
206 Reference<XIndexAccess> xIndexAccess(xTables,UNO_QUERY);
207 if(xIndexAccess.is())
208 nTablesCount = xIndexAccess->getCount();
209 else
210 nTablesCount = xTables->getElementNames().getLength();
212 if(m_aUpdateTable.is() && nTablesCount < 3) // for we can't handle more than 2 tables in our keyset
214 Reference<XPropertySet> xSet(m_aUpdateTable,UNO_QUERY);
215 const Reference<XNameAccess> xPrimaryKeyColumns = dbtools::getPrimaryKeyColumns_throw(xSet);
216 if ( xPrimaryKeyColumns.is() )
218 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
219 if ( xColSup.is() )
221 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
222 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
223 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
224 ::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
225 bAllKeysFound = !aColumnNames.empty() && sal_Int32(aColumnNames.size()) == xPrimaryKeyColumns->getElementNames().getLength();
231 catch (Exception const& e)
233 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << e.Message);
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& e)
262 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << e.Message);
263 bNeedKeySet = true;
267 if(bNeedKeySet)
269 // need to check if we could handle this select clause
270 bAllKeysFound = bAllKeysFound && (nTablesCount == 1 || checkJoin(xConnection,_xAnalyzer,aUpdateTableName));
272 if(!bAllKeysFound )
274 if ( bBookmarkable )
276 // here I know that we have a read only bookmarkable cursor
277 _xRs->beforeFirst();
278 m_nPrivileges = Privilege::SELECT;
279 m_pCacheSet = new WrappedResultSet(i_nMaxRows);
280 m_xCacheSet = m_pCacheSet;
281 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
282 return;
284 m_pCacheSet = new OStaticSet(i_nMaxRows);
285 m_xCacheSet = m_pCacheSet;
286 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
287 m_nPrivileges = Privilege::SELECT;
289 else
291 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
292 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
293 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
294 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
295 Reference<XNameAccess> xColumns = m_aUpdateTable->getColumns();
296 ::dbaccess::getColumnPositions(xSelColumns,xColumns->getElementNames(),aUpdateTableName,aColumnNames);
298 // check privileges
299 m_nPrivileges = Privilege::SELECT;
300 bool bNoInsert = false;
302 Sequence< OUString> aNames(xColumns->getElementNames());
303 const OUString* pIter = aNames.getConstArray();
304 const OUString* pEnd = pIter + aNames.getLength();
305 for(;pIter != pEnd;++pIter)
307 Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),UNO_QUERY);
308 OSL_ENSURE(xColumn.is(),"Column in table is null!");
309 if(xColumn.is())
311 sal_Int32 nNullable = 0;
312 xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
313 if(nNullable == ColumnValue::NO_NULLS && aColumnNames.find(*pIter) == aColumnNames.end())
314 { // we found a column where null is not allowed so we can't insert new values
315 bNoInsert = true;
316 break; // one column is enough
321 OKeySet* pKeySet = new OKeySet(m_aUpdateTable,xUpdateTableKeys,aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
324 m_pCacheSet = pKeySet;
325 m_xCacheSet = m_pCacheSet;
326 pKeySet->construct(_xRs,i_sRowSetFilter);
328 if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
330 Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
331 if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
333 m_nPrivileges = 0;
334 xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
335 if(!m_nPrivileges)
336 m_nPrivileges = Privilege::SELECT;
339 if(bNoInsert)
340 m_nPrivileges |= ~Privilege::INSERT; // remove the insert privilege
342 catch (const SQLException& e)
344 SAL_WARN("dbaccess.core", "ORowSetCache: exception: " << e.Message);
345 // we couldn't create a keyset here so we have to create a static cache
346 if ( m_pCacheSet )
347 m_pCacheSet = NULL;
348 m_xCacheSet = NULL;
349 m_pCacheSet = new OStaticSet(i_nMaxRows);
350 m_xCacheSet = m_pCacheSet;
351 m_pCacheSet->construct(_xRs,i_sRowSetFilter);
352 m_nPrivileges = Privilege::SELECT;
357 // last check
358 if(!bAllKeysFound && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
359 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY)
360 m_nPrivileges = Privilege::SELECT;
363 ORowSetCache::~ORowSetCache()
365 m_pCacheSet = NULL;
366 m_xCacheSet = NULL;
367 if(m_pMatrix)
369 m_pMatrix->clear();
370 delete m_pMatrix;
373 if(m_pInsertMatrix)
375 m_pInsertMatrix->clear();
376 delete m_pInsertMatrix;
378 m_xSet = WeakReference< XResultSet>();
379 m_xMetaData = NULL;
380 m_aUpdateTable = NULL;
384 void ORowSetCache::setFetchSize(sal_Int32 _nSize)
386 if(_nSize == m_nFetchSize)
387 return;
389 m_nFetchSize = _nSize;
390 if(!m_pMatrix)
392 m_pMatrix = new ORowSetMatrix(_nSize);
393 m_aMatrixIter = m_pMatrix->end();
394 m_aMatrixEnd = m_pMatrix->end();
396 m_pInsertMatrix = new ORowSetMatrix(1); // a little bit overkill but ??? :-)
397 m_aInsertRow = m_pInsertMatrix->end();
399 else
401 // now correct the iterator in our iterator vector
402 ::std::vector<sal_Int32> aPositions;
403 ::std::map<sal_Int32,sal_Bool> aCacheIterToChange;
404 // first get the positions where they stand now
405 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
406 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
407 for(;aCacheIter != aCacheEnd;++aCacheIter)
409 aCacheIterToChange[aCacheIter->first] = sal_False;
410 if ( !aCacheIter->second.pRowSet->isInsertRow()
411 /*&& aCacheIter->second.aIterator != m_pMatrix->end()*/ && !m_bModified )
413 ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
414 aPositions.push_back(nDist);
415 aCacheIterToChange[aCacheIter->first] = sal_True;
418 sal_Int32 nKeyPos = (m_aMatrixIter - m_pMatrix->begin());
419 m_pMatrix->resize(_nSize);
421 if ( nKeyPos < _nSize )
422 m_aMatrixIter = m_pMatrix->begin() + nKeyPos;
423 else
424 m_aMatrixIter = m_pMatrix->end();
425 m_aMatrixEnd = m_pMatrix->end();
427 // now adjust their positions because a resize invalidates all iterators
428 ::std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
429 ::std::map<sal_Int32,sal_Bool>::const_iterator aPosChangeIter = aCacheIterToChange.begin();
430 for( aCacheIter = m_aCacheIterators.begin();
431 aPosChangeIter != aCacheIterToChange.end();
432 ++aPosChangeIter,++aCacheIter)
434 if ( aPosChangeIter->second )
436 CHECK_MATRIX_POS(*aIter);
437 if ( *aIter < _nSize )
438 aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++;
439 else
440 aCacheIter->second.aIterator = m_pMatrix->end();
444 if(!m_nPosition)
446 sal_Int32 nNewSt = 0;
447 fillMatrix(nNewSt,_nSize);
448 OSL_ENSURE(nNewSt == 0, "fillMatrix set new start to unexpected value");
449 m_nStartPos = 0;
450 m_nEndPos = _nSize;
452 else if (m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos)
454 sal_Int32 nNewSt = -1;
455 _nSize += m_nStartPos;
456 fillMatrix(nNewSt, _nSize);
457 if (nNewSt >= 0)
459 m_nStartPos = nNewSt;
460 m_nEndPos = _nSize;
461 m_aMatrixIter = calcPosition();
463 else
465 m_nEndPos = m_nStartPos + m_nFetchSize;
468 else
470 OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos");
471 // try to repair
472 moveWindow();
473 m_aMatrixIter = calcPosition();
477 // XResultSetMetaDataSupplier
479 static Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
481 switch ( i_aValue.getTypeKind() )
483 case DataType::TINYINT:
484 case DataType::SMALLINT:
485 case DataType::INTEGER:
486 return makeAny((sal_Int32)i_aValue);
487 default:
488 if ( i_pCacheSet && i_aValue.isNull())
489 i_aValue = i_pCacheSet->getBookmark();
490 return i_aValue.getAny();
494 // ::com::sun::star::sdbcx::XRowLocate
495 Any ORowSetCache::getBookmark( )
497 if(m_bAfterLast)
498 throwFunctionSequenceException(m_xSet.get());
500 if ( m_aMatrixIter >= m_pMatrix->end() || m_aMatrixIter < m_pMatrix->begin() || !(*m_aMatrixIter).is())
502 return Any(); // this is allowed here because the rowset knowns what it is doing
505 return lcl_getBookmark(((*m_aMatrixIter)->get())[0],m_pCacheSet);
508 bool ORowSetCache::moveToBookmark( const Any& bookmark )
510 if ( m_pCacheSet->moveToBookmark(bookmark) )
512 m_bBeforeFirst = false;
513 m_nPosition = m_pCacheSet->getRow();
515 checkPositionFlags();
517 if(!m_bAfterLast)
519 moveWindow();
520 checkPositionFlags();
521 if ( !m_bAfterLast )
523 m_aMatrixIter = calcPosition();
524 OSL_ENSURE(m_aMatrixIter->is(),"Iterator after moveToBookmark not valid");
526 else
527 m_aMatrixIter = m_pMatrix->end();
529 else
530 m_aMatrixIter = m_pMatrix->end();
532 else
533 return false;
535 return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
538 bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
540 bool bRet( moveToBookmark( bookmark ) );
541 if ( bRet )
543 m_nPosition = m_pCacheSet->getRow() + rows;
544 absolute(m_nPosition);
546 bRet = m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
549 return bRet;
552 sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second )
554 return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_pCacheSet->compareBookmarks(_first,_second);
557 bool ORowSetCache::hasOrderedBookmarks( )
559 return m_pCacheSet->hasOrderedBookmarks();
562 sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark )
564 return m_pCacheSet->hashBookmark(bookmark);
567 // XRowUpdate
568 void ORowSetCache::updateNull(sal_Int32 columnIndex,ORowSetValueVector::Vector& io_aRow
569 ,::std::vector<sal_Int32>& o_ChangedColumns
572 checkUpdateConditions(columnIndex);
574 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
575 if ( !rInsert[columnIndex].isNull() )
577 rInsert[columnIndex].setBound(true);
578 rInsert[columnIndex].setNull();
579 rInsert[columnIndex].setModified();
580 io_aRow[columnIndex].setNull();
582 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
583 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
587 void ORowSetCache::updateValue(sal_Int32 columnIndex,const ORowSetValue& x
588 ,ORowSetValueVector::Vector& io_aRow
589 ,::std::vector<sal_Int32>& o_ChangedColumns
592 checkUpdateConditions(columnIndex);
594 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
595 if ( rInsert[columnIndex] != x )
597 rInsert[columnIndex].setBound(true);
598 rInsert[columnIndex] = x;
599 rInsert[columnIndex].setModified();
600 io_aRow[columnIndex] = rInsert[columnIndex];
602 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
603 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
607 void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< ::com::sun::star::io::XInputStream >& x
608 , sal_Int32 length,ORowSetValueVector::Vector& io_aRow
609 ,::std::vector<sal_Int32>& o_ChangedColumns
612 checkUpdateConditions(columnIndex);
614 Sequence<sal_Int8> aSeq;
615 if(x.is())
616 x->readBytes(aSeq,length);
618 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
619 rInsert[columnIndex].setBound(true);
620 rInsert[columnIndex] = aSeq;
621 rInsert[columnIndex].setModified();
622 io_aRow[columnIndex] = makeAny(x);
624 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
625 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
628 void ORowSetCache::updateObject( sal_Int32 columnIndex, const Any& x
629 ,ORowSetValueVector::Vector& io_aRow
630 ,::std::vector<sal_Int32>& o_ChangedColumns
633 checkUpdateConditions(columnIndex);
635 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
636 ORowSetValue aTemp;
637 aTemp.fill(x);
638 if ( rInsert[columnIndex] != aTemp )
640 rInsert[columnIndex].setBound(true);
641 rInsert[columnIndex] = aTemp;
642 rInsert[columnIndex].setModified();
643 io_aRow[columnIndex] = rInsert[columnIndex];
645 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
646 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
650 void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/
651 ,ORowSetValueVector::Vector& io_aRow
652 ,::std::vector<sal_Int32>& o_ChangedColumns
655 checkUpdateConditions(columnIndex);
657 ORowSetValueVector::Vector& rInsert = ((*m_aInsertRow)->get());
658 ORowSetValue aTemp;
659 aTemp.fill(x);
660 if ( rInsert[columnIndex] != aTemp )
662 rInsert[columnIndex].setBound(true);
663 rInsert[columnIndex] = aTemp;
664 rInsert[columnIndex].setModified();
665 io_aRow[columnIndex] = rInsert[columnIndex];
667 m_pCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
668 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
672 // XResultSet
673 bool ORowSetCache::next( )
675 if(!isAfterLast())
677 m_bBeforeFirst = false;
678 ++m_nPosition;
680 // after we increment the position we have to check if we are already after the last row
681 checkPositionFlags();
682 if(!m_bAfterLast)
684 moveWindow();
686 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
687 m_aMatrixIter = calcPosition();
688 checkPositionFlags();
692 return !m_bAfterLast;
697 bool ORowSetCache::isFirst( )
699 return m_nPosition == 1; // ask resultset for
702 bool ORowSetCache::isLast( )
704 return m_nPosition == m_nRowCount;
707 bool ORowSetCache::beforeFirst( )
709 if(!m_bBeforeFirst)
711 m_bAfterLast = false;
712 m_nPosition = 0;
713 m_bBeforeFirst = true;
714 m_pCacheSet->beforeFirst();
715 moveWindow();
716 m_aMatrixIter = m_pMatrix->end();
718 return true;
721 bool ORowSetCache::afterLast( )
723 if(!m_bAfterLast)
725 m_bBeforeFirst = false;
726 m_bAfterLast = true;
728 if(!m_bRowCountFinal)
730 m_pCacheSet->last_checked(false);
731 m_bRowCountFinal = true;
732 m_nRowCount = m_pCacheSet->getRow();// + 1 removed
734 m_pCacheSet->afterLast();
736 m_nPosition = 0;
737 m_aMatrixIter = m_pMatrix->end();
739 return true;
742 bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos, sal_Int32 &_nNewEndPos)
744 OSL_ENSURE(_nNewStartPos != _nNewEndPos,"ORowSetCache::fillMatrix: StartPos and EndPos can not be equal!");
745 // If _nNewStartPos >= 0, then fill the whole window with new data
746 // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos)
747 // Else, undefined (invalid argument)
748 OSL_ENSURE( _nNewStartPos >= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );
750 ORowSetMatrix::iterator aIter;
751 sal_Int32 i;
752 bool bCheck;
753 sal_Int32 requestedStartPos;
754 if ( _nNewStartPos == -1 )
756 aIter = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
757 i = m_nEndPos + 1;
758 requestedStartPos = m_nStartPos;
760 else
762 aIter = m_pMatrix->begin();
763 i = _nNewStartPos + 1;
764 requestedStartPos = _nNewStartPos;
766 bCheck = m_pCacheSet->absolute(i);
769 for(; i <= _nNewEndPos; ++i,++aIter)
771 if(bCheck)
773 if(!aIter->is())
774 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
775 m_pCacheSet->fillValueRow(*aIter,i);
777 else
778 { // there are no more rows found so we can fetch some before start
780 if(!m_bRowCountFinal)
782 if(m_pCacheSet->previous_checked(false)) // because we stand after the last row
783 m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
784 if(!m_nRowCount)
785 m_nRowCount = i-1; // it can be that getRow return zero
786 m_bRowCountFinal = true;
788 const ORowSetMatrix::iterator aEnd = aIter;
789 ORowSetMatrix::iterator aRealEnd = m_pMatrix->end();
790 sal_Int32 nPos;
791 if (m_nRowCount >= m_nFetchSize)
793 nPos = m_nRowCount - m_nFetchSize;
795 else
797 nPos = 0;
799 _nNewStartPos = nPos;
800 _nNewEndPos = m_nRowCount;
801 ++nPos;
802 bCheck = m_pCacheSet->absolute(nPos);
804 for(;bCheck && nPos <= requestedStartPos && aIter != aRealEnd; ++aIter, ++nPos)
806 if(!aIter->is())
807 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
808 m_pCacheSet->fillValueRow(*aIter, nPos);
809 bCheck = m_pCacheSet->next();
811 if(aIter != aEnd)
812 ::std::rotate(m_pMatrix->begin(),aEnd,aIter);
813 break;
815 bCheck = m_pCacheSet->next();
817 // we have to read one row forward to ensure that we know when we are on last row
818 // but only when we don't know it already
819 if(!m_bRowCountFinal)
821 if(!m_pCacheSet->next())
823 if(m_pCacheSet->previous_checked(false)) // because we stand after the last row
824 m_nRowCount = m_pCacheSet->getRow(); // here we have the row count
825 m_bRowCountFinal = true;
827 else
828 m_nRowCount = std::max(i,m_nRowCount);
831 return bCheck;
834 // If m_nPosition is out of the current window,
835 // move it and update m_nStartPos and m_nEndPos
836 // Caller is responsible for updating m_aMatrixIter
837 bool ORowSetCache::moveWindow()
839 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
840 OSL_ENSURE(m_nEndPos >= m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
841 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
843 if ( m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos )
845 // just move inside the window
846 OSL_ENSURE((m_nPosition - m_nStartPos) <= (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
847 // make double plus sure that we have fetched that row
848 m_aMatrixIter = calcPosition();
849 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(), "New m_aMatrixIter is at end(), but should not.");
850 if(!m_aMatrixIter->is())
852 bool bOk( m_pCacheSet->absolute( m_nPosition ) );
853 if ( bOk )
855 *m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
856 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
857 // we have to read one row forward to ensure that we know when we are on last row
858 // but only when we don't know it already
859 if ( !m_bRowCountFinal )
861 bOk = m_pCacheSet->absolute_checked( m_nPosition + 1,false );
862 if ( bOk )
863 m_nRowCount = std::max(sal_Int32(m_nPosition+1),m_nRowCount);
866 if(!bOk && !m_bRowCountFinal)
868 // because we stand after the last row
869 m_nRowCount = m_pCacheSet->previous_checked(false) ? m_pCacheSet->getRow() : 0;
870 m_bRowCountFinal = true;
873 return true;
876 bool bRet = true;
878 sal_Int32 nDiff = (m_nFetchSize - 1) / 2;
879 sal_Int32 nNewStartPos = (m_nPosition - nDiff) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
880 sal_Int32 nNewEndPos = nNewStartPos + m_nFetchSize;
882 if ( nNewStartPos < 0 )
884 // The computed new window crashes through the floor (begins before first row);
885 // nNew*Pos has to be shifted by -nNewStartPos
886 nNewEndPos -= nNewStartPos;
887 nNewStartPos = 0;
890 if ( nNewStartPos < m_nStartPos )
891 { // need to fill data *before* m_nStartPos
892 if ( nNewEndPos > m_nStartPos )
893 { // The two regions are overlapping.
894 // We'll first rotate the contents of m_pMatrix so that the overlap area
895 // is positioned right; in the old window it is at the beginning,
896 // it has to go to the end.
897 // then we fill in the rows between new and old start pos.
899 bool bCheck;
900 bCheck = m_pCacheSet->absolute(nNewStartPos + 1);
902 // m_nEndPos < nNewEndPos when window not filled (e.g. there are less rows in total than window size)
903 m_nEndPos = std::min(nNewEndPos, m_nEndPos);
904 const sal_Int32 nOverlapSize = m_nEndPos - m_nStartPos;
905 const sal_Int32 nStartPosOffset = m_nStartPos - nNewStartPos; // by how much m_nStartPos moves
906 m_nStartPos = nNewStartPos;
907 OSL_ENSURE( static_cast<ORowSetMatrix::size_type>(nOverlapSize) <= m_pMatrix->size(), "new window end is after end of cache matrix!" );
908 // the first position in m_pMatrix whose data we don't keep;
909 // content will be moved to m_pMatrix.begin()
910 ORowSetMatrix::iterator aEnd (m_pMatrix->begin() + nOverlapSize);
911 // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
912 ORowSetMatrix::iterator aNewEnd (aEnd + nStartPosOffset);
913 // *m_pMatrix now looks like:
914 // [0; nOverlapSize) i.e. [begin(); aEnd): data kept
915 // [nOverlapSize; nOverlapSize + nStartPosOffet) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos
916 // [nOverlapSize + nStartPosOffet; size()) i.e. [aNewEnd, end()): unused
917 // Note that nOverlapSize + nStartPosOffet == m_nEndPos - m_nStartPos (new values)
918 // When we are finished:
919 // [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos
920 // [nStartPosOffset; nOverlapSize + nStartPosOffet) i.e. [aEnd, aNewEnd): kept
921 // [nOverlapSize + nStartPosOffet; size()) i.e. [aNewEnd, end()): unused
923 if ( bCheck )
926 ORowSetMatrix::iterator aIter(aEnd);
927 sal_Int32 nPos = m_nStartPos + 1;
928 bCheck = fill(aIter, aNewEnd, nPos, bCheck);
931 ::std::rotate(m_pMatrix->begin(), aEnd, aNewEnd);
932 // now correct the iterator in our iterator vector
933 // rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment
934 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
935 const ORowSetCacheMap::const_iterator aCacheEnd = m_aCacheIterators.end();
936 for(;aCacheIter != aCacheEnd;++aCacheIter)
938 if ( !aCacheIter->second.pRowSet->isInsertRow()
939 && aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
941 const ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
942 if ( nDist >= nOverlapSize )
944 // That's from outside the overlap area; invalidate iterator.
945 aCacheIter->second.aIterator = m_pMatrix->end();
947 else
949 // Inside overlap area: move to correct position
950 CHECK_MATRIX_POS( (nDist + nStartPosOffset) );
951 aCacheIter->second.aIterator += nStartPosOffset;
952 OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
953 && aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
958 else
959 { // normally this should never happen
960 OSL_FAIL("What the hell is happen here!");
961 return false;
964 else
965 {// no rows can be reused so fill again
966 bRet = reFillMatrix(nNewStartPos,nNewEndPos);
970 OSL_ENSURE(nNewStartPos >= m_nStartPos, "ORowSetCache::moveWindow internal error: new start pos before current start pos");
971 if ( m_nEndPos < nNewEndPos )
972 { // need to fill data *after* m_nEndPos
973 if( nNewStartPos < m_nEndPos )
974 { // The two regions are overlapping.
975 const sal_Int32 nRowsInCache = m_nEndPos - m_nStartPos;
976 if ( nRowsInCache < m_nFetchSize )
978 // There is some unused space in *m_pMatrix; fill it
979 CHECK_MATRIX_POS(nRowsInCache);
980 sal_Int32 nPos = m_nEndPos + 1;
981 bool bCheck = m_pCacheSet->absolute(nPos);
982 ORowSetMatrix::iterator aIter = m_pMatrix->begin() + nRowsInCache;
983 const sal_Int32 nRowsToFetch = std::min(nNewEndPos-m_nEndPos, m_nFetchSize-nRowsInCache);
984 const ORowSetMatrix::const_iterator aEnd = aIter + nRowsToFetch;
985 bCheck = fill(aIter, aEnd, nPos, bCheck);
986 m_nEndPos = nPos - 1;
987 OSL_ENSURE( (!bCheck && m_nEndPos <= nNewEndPos ) ||
988 ( bCheck && m_nEndPos == nNewEndPos ),
989 "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
992 // A priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive]
993 // have to be refilled with new to-be-fetched rows.
994 // The rows behind this can be reused
995 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
996 const sal_Int32 nNewStartPosInMatrix = nNewStartPos - m_nStartPos;
997 CHECK_MATRIX_POS( nNewStartPosInMatrix );
998 // first position we reuse
999 const ORowSetMatrix::const_iterator aEnd = m_pMatrix->begin() + nNewStartPosInMatrix;
1000 // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
1001 ORowSetMatrix::iterator aDataEnd = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
1003 sal_Int32 nPos = m_nEndPos + 1;
1004 bool bCheck = m_pCacheSet->absolute(nPos);
1005 bCheck = fill(aIter, aEnd, nPos, bCheck); // refill the region we don't need anymore
1006 //aIter and nPos are now the position *after* last filled in one!
1008 // bind end to front
1009 if(bCheck)
1011 OSL_ENSURE(aIter == aEnd, "fill() said went till end, but did not.");
1012 // rotate the end to the front
1013 ::std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
1014 // now correct the iterator in our iterator vector
1015 rotateCacheIterator( nNewStartPosInMatrix );
1016 m_nStartPos = nNewStartPos;
1017 m_nEndPos = nNewEndPos;
1018 // now I can say how many rows we have
1019 // we have to read one row forward to ensure that we know when we are on last row
1020 // but only when we don't know it already
1021 bool bOk = true;
1022 if(!m_bRowCountFinal)
1023 bOk = m_pCacheSet->next();
1024 if(!bOk)
1026 m_pCacheSet->previous_checked(false); // because we stand after the last row
1027 m_nRowCount = nPos; // here we have the row count
1028 OSL_ENSURE(nPos == m_pCacheSet->getRow(),"nPos is not valid!");
1029 m_bRowCountFinal = true;
1031 else if(!m_bRowCountFinal)
1032 m_nRowCount = std::max(nPos+1, m_nRowCount); //+1 because we successfully moved to row after nPos
1033 else
1034 OSL_ENSURE(m_nRowCount >= nPos, "Final m_nRowCount is smaller than row I moved to!");
1036 else
1037 { // the end was reached before or at end() so we can set the start before or at nNewStartPos
1038 // and possibly keep more of m_pMatrix than planned.
1039 const ORowSetMatrix::iterator::difference_type nFetchedRows = aIter - m_pMatrix->begin();
1040 // *m_pMatrix now looks like:
1041 // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows
1042 // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???
1044 nPos -= 1;
1045 m_nStartPos += nFetchedRows;
1046 m_nEndPos = nPos;
1047 ::std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
1048 // now correct the iterator in our iterator vector
1049 rotateCacheIterator( nFetchedRows );
1051 if ( !m_bRowCountFinal )
1053 m_pCacheSet->previous_checked(false); // because we stand after the last row
1054 m_nRowCount = std::max(m_nRowCount, nPos); // here we have the row count
1055 OSL_ENSURE(nPos == m_pCacheSet->getRow(),"nPos isn't valid!");
1056 m_bRowCountFinal = true;
1060 // here we need only to check if the beginning row is valid. If not we have to fetch it.
1061 if(!m_pMatrix->begin()->is())
1063 aIter = m_pMatrix->begin();
1065 nPos = m_nStartPos + 1;
1066 bCheck = m_pCacheSet->absolute_checked(nPos, true);
1067 for(; !aIter->is() && bCheck;++aIter, ++nPos)
1069 OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator");
1071 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
1072 m_pCacheSet->fillValueRow(*aIter, nPos);
1074 bCheck = m_pCacheSet->next();
1078 else // no rows can be reused so fill again
1079 bRet = reFillMatrix(nNewStartPos,nNewEndPos);
1082 if(!m_bRowCountFinal)
1083 m_nRowCount = std::max(m_nPosition,m_nRowCount);
1084 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
1085 OSL_ENSURE(m_nEndPos > m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
1086 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
1088 return bRet;
1091 bool ORowSetCache::first( )
1093 // First move to the first row.
1094 // Then check if the cache window is at the beginning.
1095 // If not, then position the window and fill it with data.
1096 // We move the window smartly, i.e. we clear only the rows that are out of range
1097 bool bRet = m_pCacheSet->first();
1098 if(bRet)
1100 m_bBeforeFirst = m_bAfterLast = false;
1101 m_nPosition = 1;
1102 moveWindow();
1103 m_aMatrixIter = m_pMatrix->begin();
1105 else
1107 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
1108 m_nRowCount = m_nPosition = 0;
1110 OSL_ENSURE(m_bBeforeFirst || m_bNew,"ORowSetCache::first return false and BeforeFirst isn't true");
1111 m_aMatrixIter = m_pMatrix->end();
1113 return bRet;
1116 bool ORowSetCache::last( )
1118 bool bRet = m_pCacheSet->last();
1119 if(bRet)
1121 m_bBeforeFirst = m_bAfterLast = false;
1122 if(!m_bRowCountFinal)
1124 m_bRowCountFinal = true;
1125 m_nRowCount = m_pCacheSet->getRow(); // not + 1
1127 m_nPosition = m_pCacheSet->getRow();
1128 moveWindow();
1129 // we have to repositioning because moveWindow can modify the cache
1130 m_pCacheSet->last();
1131 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1132 m_aMatrixIter = calcPosition();
1134 else
1136 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
1137 m_nRowCount = m_nPosition = 0;
1138 OSL_ENSURE(m_bBeforeFirst,"ORowSetCache::last return false and BeforeFirst isn't true");
1139 m_aMatrixIter = m_pMatrix->end();
1141 #if OSL_DEBUG_LEVEL > 1
1142 if(bRet)
1144 OSL_ENSURE((*m_aMatrixIter).is(),"ORowSetCache::last: Row not valid!");
1146 #endif
1148 return bRet;
1151 sal_Int32 ORowSetCache::getRow( )
1153 return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition;
1156 bool ORowSetCache::absolute( sal_Int32 row )
1158 if(!row )
1159 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_ABS_ZERO),NULL,SQLSTATE_GENERAL,1000,Any() );
1161 if(row < 0)
1163 // here we have to scroll from the last row to backward so we have to go to last row and
1164 // and two the previous
1165 if(m_bRowCountFinal || last())
1167 m_nPosition = m_nRowCount + row + 1; // + row because row is negative and +1 because row==-1 means last row
1168 if(m_nPosition < 1)
1170 m_bBeforeFirst = true;
1171 m_bAfterLast = false;
1172 m_aMatrixIter = m_pMatrix->end();
1174 else
1176 m_bBeforeFirst = false;
1177 m_bAfterLast = m_nPosition > m_nRowCount;
1178 moveWindow();
1179 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1180 m_aMatrixIter = calcPosition();
1183 else
1184 m_aMatrixIter = m_pMatrix->end();
1186 else
1188 m_nPosition = row;
1189 // the position flags
1190 m_bBeforeFirst = false;
1191 checkPositionFlags();
1193 if(!m_bAfterLast)
1195 moveWindow();
1196 checkPositionFlags();
1197 if(!m_bAfterLast)
1198 m_aMatrixIter = calcPosition();
1199 else
1200 m_aMatrixIter = m_pMatrix->end();
1202 else
1203 m_aMatrixIter = m_pMatrix->end();
1206 return !(m_bAfterLast || m_bBeforeFirst);
1209 bool ORowSetCache::relative( sal_Int32 rows )
1211 bool bErg = true;
1212 if(rows)
1214 sal_Int32 nNewPosition = m_nPosition + rows;
1216 if ( m_bBeforeFirst && rows > 0 )
1217 nNewPosition = rows;
1218 else if ( m_bRowCountFinal && m_bAfterLast && rows < 0 )
1219 nNewPosition = m_nRowCount + 1 + rows;
1220 else
1221 if ( m_bBeforeFirst || ( m_bRowCountFinal && m_bAfterLast ) )
1222 throw SQLException( DBACORE_RESSTRING( RID_STR_NO_RELATIVE ), NULL, SQLSTATE_GENERAL, 1000, Any() );
1223 if ( nNewPosition )
1225 bErg = absolute( nNewPosition );
1226 bErg = bErg && !isAfterLast() && !isBeforeFirst();
1228 else
1230 m_bBeforeFirst = true;
1231 bErg = false;
1234 return bErg;
1237 bool ORowSetCache::previous( )
1239 bool bRet = false;
1240 if(!isBeforeFirst())
1242 if(m_bAfterLast) // we stand after the last row so one before is the last row
1243 bRet = last();
1244 else
1246 m_bAfterLast = false;
1247 --m_nPosition;
1248 moveWindow();
1249 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1251 checkPositionFlags();
1253 if(!m_nPosition)
1255 m_bBeforeFirst = true;
1256 m_aMatrixIter = m_pMatrix->end();
1258 else
1260 m_aMatrixIter = calcPosition();
1261 bRet = (*m_aMatrixIter).is();
1265 return bRet;
1268 void ORowSetCache::refreshRow( )
1270 if(isAfterLast())
1271 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_REFESH_AFTERLAST),NULL,SQLSTATE_GENERAL,1000,Any() );
1272 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!");
1273 m_pCacheSet->refreshRow();
1274 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1275 if ( m_bNew )
1277 cancelRowModification();
1281 bool ORowSetCache::rowUpdated( )
1283 return m_pCacheSet->rowUpdated();
1286 bool ORowSetCache::rowInserted( )
1288 return m_pCacheSet->rowInserted();
1291 // XResultSetUpdate
1292 bool ORowSetCache::insertRow(::std::vector< Any >& o_aBookmarks)
1294 if ( !m_bNew || !m_aInsertRow->is() )
1295 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_MOVETOINSERTROW_CALLED),NULL,SQLSTATE_GENERAL,1000,Any() );
1297 m_pCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable);
1299 bool bRet( rowInserted() );
1300 if ( bRet )
1302 ++m_nRowCount;
1303 Any aBookmark = ((*m_aInsertRow)->get())[0].makeAny();
1304 m_bAfterLast = m_bBeforeFirst = false;
1305 if(aBookmark.hasValue())
1307 moveToBookmark(aBookmark);
1308 // update the cached values
1309 ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
1310 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1311 for(;aIter != m_pMatrix->end();++aIter)
1313 if ( m_aMatrixIter != aIter && aIter->is() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
1315 o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
1319 else
1321 OSL_FAIL("There must be a bookmark after the row was inserted!");
1324 return bRet;
1327 void ORowSetCache::resetInsertRow(bool _bClearInsertRow)
1329 if ( _bClearInsertRow )
1330 clearInsertRow();
1331 m_bNew = false;
1332 m_bModified = false;
1335 void ORowSetCache::cancelRowModification()
1337 // clear the insertrow references -> implies that the current row of the rowset changes as well
1338 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1339 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
1340 for(;aCacheIter != aCacheEnd;++aCacheIter)
1342 if ( aCacheIter->second.pRowSet->isInsertRow() && aCacheIter->second.aIterator == m_aInsertRow )
1343 aCacheIter->second.aIterator = m_pMatrix->end();
1345 resetInsertRow(false);
1348 void ORowSetCache::updateRow( ORowSetMatrix::iterator& _rUpdateRow,::std::vector< Any >& o_aBookmarks )
1350 if(isAfterLast() || isBeforeFirst())
1351 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_UPDATEROW),NULL,SQLSTATE_GENERAL,1000,Any() );
1353 Any aBookmark = ((*_rUpdateRow)->get())[0].makeAny();
1354 OSL_ENSURE(aBookmark.hasValue(),"Bookmark must have a value!");
1355 // here we don't have to reposition our CacheSet, when we try to update a row,
1356 // the row was already fetched
1357 moveToBookmark(aBookmark);
1358 m_pCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable);
1359 // refetch the whole row
1360 (*m_aMatrixIter) = NULL;
1362 if ( moveToBookmark(aBookmark) )
1364 // update the cached values
1365 ORowSetValueVector::Vector& rCurrentRow = ((*m_aMatrixIter))->get();
1366 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1367 for(;aIter != m_pMatrix->end();++aIter)
1369 if ( m_aMatrixIter != aIter && aIter->is() && m_pCacheSet->columnValuesUpdated((*aIter)->get(),rCurrentRow) )
1371 o_aBookmarks.push_back(lcl_getBookmark((*aIter)->get()[0],m_pCacheSet));
1376 m_bModified = false;
1379 bool ORowSetCache::deleteRow( )
1381 if(isAfterLast() || isBeforeFirst())
1382 throw SQLException(DBACORE_RESSTRING(RID_STR_NO_DELETEROW),NULL,SQLSTATE_GENERAL,1000,Any() );
1384 m_pCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable);
1385 if ( !m_pCacheSet->rowDeleted() )
1386 return false;
1388 --m_nRowCount;
1389 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < (sal_Int32)m_pMatrix->size(),"Position is behind end()!");
1390 ORowSetMatrix::iterator aPos = calcPosition();
1391 (*aPos) = NULL;
1393 ORowSetMatrix::iterator aEnd = m_pMatrix->end();
1394 for(++aPos;aPos != aEnd && aPos->is();++aPos)
1396 *(aPos-1) = *aPos;
1397 (*aPos) = NULL;
1399 m_aMatrixIter = m_pMatrix->end();
1401 --m_nPosition;
1402 return true;
1405 void ORowSetCache::cancelRowUpdates( )
1407 m_bNew = m_bModified = false;
1408 if(!m_nPosition)
1410 OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0");
1411 ::dbtools::throwFunctionSequenceException(NULL);
1414 if(m_pCacheSet->absolute(m_nPosition))
1415 m_pCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1416 else
1418 OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
1419 ::dbtools::throwFunctionSequenceException(NULL);
1423 void ORowSetCache::moveToInsertRow( )
1425 m_bNew = true;
1426 m_bUpdated = m_bAfterLast = false;
1428 m_aInsertRow = m_pInsertMatrix->begin();
1429 if(!m_aInsertRow->is())
1430 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1432 // we don't unbound the bookmark column
1433 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
1434 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1435 for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i)
1437 aIter->setBound(false);
1438 aIter->setModified(false);
1439 aIter->setNull();
1440 aIter->setTypeKind(m_xMetaData->getColumnType(i));
1444 ORowSetCacheIterator ORowSetCache::createIterator(ORowSetBase* _pRowSet)
1446 ORowSetCacheIterator_Helper aHelper;
1447 aHelper.aIterator = m_pMatrix->end();
1448 aHelper.pRowSet = _pRowSet;
1449 return ORowSetCacheIterator(m_aCacheIterators.insert(m_aCacheIterators.begin(),ORowSetCacheMap::value_type(m_aCacheIterators.size()+1,aHelper)),this,_pRowSet);
1452 void ORowSetCache::deleteIterator(const ORowSetBase* _pRowSet)
1454 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1455 for(;aCacheIter != m_aCacheIterators.end();)
1457 if ( aCacheIter->second.pRowSet == _pRowSet )
1459 m_aCacheIterators.erase(aCacheIter);
1460 aCacheIter = m_aCacheIterators.begin();
1462 else
1463 ++aCacheIter;
1467 void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist)
1469 if(_nDist)
1471 // now correct the iterator in our iterator vector
1472 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
1473 ORowSetCacheMap::iterator aCacheEnd = m_aCacheIterators.end();
1474 for(;aCacheIter != aCacheEnd;++aCacheIter)
1476 if ( !aCacheIter->second.pRowSet->isInsertRow()
1477 && aCacheIter->second.aIterator != m_pMatrix->end() && !m_bModified )
1479 ptrdiff_t nDist = (aCacheIter->second.aIterator - m_pMatrix->begin());
1480 if(nDist < _nDist)
1482 aCacheIter->second.aIterator = m_pMatrix->end();
1484 else
1486 OSL_ENSURE((aCacheIter->second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!");
1487 aCacheIter->second.aIterator -= _nDist;
1488 OSL_ENSURE(aCacheIter->second.aIterator >= m_pMatrix->begin()
1489 && aCacheIter->second.aIterator < m_pMatrix->end(),"Iterator out of area!");
1496 void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow)
1498 m_aInsertRow = m_pInsertMatrix->begin();
1499 if(!m_aInsertRow->is())
1500 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1502 (*(*m_aInsertRow)) = (*(*_rOriginalRow));
1503 // we don't unbound the bookmark column
1504 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin();
1505 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1506 for(;aIter != aEnd;++aIter)
1507 aIter->setModified(false);
1510 void ORowSetCache::checkPositionFlags()
1512 if(m_bRowCountFinal)
1514 m_bAfterLast = m_nPosition > m_nRowCount;
1515 if(m_bAfterLast)
1516 m_nPosition = 0;//m_nRowCount;
1520 void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex)
1522 if(m_bAfterLast || columnIndex >= (sal_Int32)(*m_aInsertRow)->get().size())
1523 throwFunctionSequenceException(m_xSet.get());
1526 bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const OUString& _sUpdateTableName)
1528 bool bOk = false;
1529 if (pNode->count() == 3 && // expression in parentheses
1530 SQL_ISPUNCTUATION(pNode->getChild(0),"(") &&
1531 SQL_ISPUNCTUATION(pNode->getChild(2),")"))
1533 bOk = checkInnerJoin(pNode->getChild(1),_xConnection,_sUpdateTableName);
1535 else if ((SQL_ISRULE(pNode,search_condition) || SQL_ISRULE(pNode,boolean_term)) && // AND/OR link
1536 pNode->count() == 3)
1538 // only allow an AND link
1539 if ( SQL_ISTOKEN(pNode->getChild(1),AND) )
1540 bOk = checkInnerJoin(pNode->getChild(0),_xConnection,_sUpdateTableName)
1541 && checkInnerJoin(pNode->getChild(2),_xConnection,_sUpdateTableName);
1543 else if (SQL_ISRULE(pNode,comparison_predicate))
1545 // only the comparison of columns is allowed
1546 OSL_ENSURE(pNode->count() == 3,"checkInnerJoin: Fehler im Parse Tree");
1547 if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
1548 SQL_ISRULE(pNode->getChild(2),column_ref) &&
1549 pNode->getChild(1)->getNodeType() == SQL_NODE_EQUAL))
1551 bOk = false;
1553 OUString sColumnName,sTableRange;
1554 OSQLParseTreeIterator::getColumnRange( pNode->getChild(0), _xConnection, sColumnName, sTableRange );
1555 bOk = sTableRange == _sUpdateTableName;
1556 if ( !bOk )
1558 OSQLParseTreeIterator::getColumnRange( pNode->getChild(2), _xConnection, sColumnName, sTableRange );
1559 bOk = sTableRange == _sUpdateTableName;
1562 return bOk;
1565 bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection,
1566 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
1567 const OUString& _sUpdateTableName )
1569 bool bOk = false;
1570 OUString sSql = _xAnalyzer->getQuery();
1571 OUString sErrorMsg;
1572 ::connectivity::OSQLParser aSqlParser( m_aContext );
1573 ::std::unique_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql));
1574 if ( pSqlParseNode.get() && SQL_ISRULE(pSqlParseNode, select_statement) )
1576 OSQLParseNode* pTableRefCommalist = pSqlParseNode->getByRule(::connectivity::OSQLParseNode::table_ref_commalist);
1577 OSL_ENSURE(pTableRefCommalist,"NO tables why!?");
1578 if(pTableRefCommalist && pTableRefCommalist->count() == 1)
1580 // we found only one element so it must some kind of join here
1581 OSQLParseNode* pJoin = pTableRefCommalist->getByRule(::connectivity::OSQLParseNode::qualified_join);
1582 if(pJoin)
1583 { // we are only interested in qualified joins like RIGHT or LEFT
1584 OSQLParseNode* pJoinType = pJoin->getChild(1);
1585 OSQLParseNode* pOuterType = NULL;
1586 if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2)
1587 pOuterType = pJoinType->getChild(0);
1588 else if(SQL_ISRULE(pJoinType,outer_join_type))
1589 pOuterType = pJoinType;
1591 bool bCheck = false;
1592 bool bLeftSide = false;
1593 if(pOuterType)
1594 { // found outer join
1595 bLeftSide = SQL_ISTOKEN(pOuterType->getChild(0),LEFT);
1596 bCheck = bLeftSide || SQL_ISTOKEN(pOuterType->getChild(0),RIGHT);
1599 if(bCheck)
1600 { // here we know that we have to check on which side our table resides
1601 const OSQLParseNode* pTableRef;
1602 if(bLeftSide)
1603 pTableRef = pJoin->getChild(0);
1604 else
1605 pTableRef = pJoin->getChild(3);
1606 OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");
1608 OUString sTableRange = OSQLParseNode::getTableRange(pTableRef);
1609 if(sTableRange.isEmpty())
1610 pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, NULL, false, false );
1611 bOk = sTableRange == _sUpdateTableName;
1615 else
1617 OSQLParseNode* pWhereOpt = pSqlParseNode->getChild(3)->getChild(1);
1618 if ( pWhereOpt && !pWhereOpt->isLeaf() )
1619 bOk = checkInnerJoin(pWhereOpt->getChild(1),_xConnection,_sUpdateTableName);
1622 return bOk;
1625 void ORowSetCache::clearInsertRow()
1627 // we don't unbound the bookmark column
1628 if ( m_aInsertRow != m_pInsertMatrix->end() && m_aInsertRow->is() )
1630 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->get().begin()+1;
1631 ORowSetValueVector::Vector::iterator aEnd = (*m_aInsertRow)->get().end();
1632 for(;aIter != aEnd;++aIter)
1634 aIter->setBound(false);
1635 aIter->setModified(false);
1636 aIter->setNull();
1641 ORowSetMatrix::iterator ORowSetCache::calcPosition() const
1643 sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1;
1644 CHECK_MATRIX_POS(nValue);
1645 return ( nValue < 0 || nValue >= static_cast<sal_Int32>(m_pMatrix->size()) ) ? m_pMatrix->end() : (m_pMatrix->begin() + nValue);
1648 TORowSetOldRowHelperRef ORowSetCache::registerOldRow()
1650 TORowSetOldRowHelperRef pRef = new ORowSetOldRowHelper(ORowSetRow());
1651 m_aOldRows.push_back(pRef);
1652 return pRef;
1655 void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef& _rRow)
1657 TOldRowSetRows::iterator aOldRowEnd = m_aOldRows.end();
1658 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1660 if ( aOldRowIter->get() == _rRow.get() )
1662 m_aOldRows.erase(aOldRowIter);
1663 break;
1669 bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos, sal_Int32 _nNewEndPos)
1671 const TOldRowSetRows::const_iterator aOldRowEnd = m_aOldRows.end();
1672 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1674 if ( aOldRowIter->is() && (*aOldRowIter)->getRow().is() )
1675 (*aOldRowIter)->setRow(new ORowSetValueVector( *((*aOldRowIter)->getRow()) ) );
1677 sal_Int32 nNewSt = _nNewStartPos;
1678 bool bRet = fillMatrix(nNewSt,_nNewEndPos);
1679 m_nStartPos = nNewSt;
1680 m_nEndPos = _nNewEndPos;
1681 rotateCacheIterator(static_cast<ORowSetMatrix::difference_type>(m_nFetchSize+1)); // invalidate every iterator
1682 return bRet;
1685 bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter, const ORowSetMatrix::const_iterator& _aEnd, sal_Int32& _nPos, bool _bCheck)
1687 const sal_Int32 nColumnCount = m_xMetaData->getColumnCount();
1688 for (; _bCheck && _aIter != _aEnd; ++_aIter, ++_nPos)
1690 if ( !_aIter->is() )
1691 *_aIter = new ORowSetValueVector(nColumnCount);
1692 else
1694 const TOldRowSetRows::const_iterator aOldRowEnd = m_aOldRows.end();
1695 for (TOldRowSetRows::iterator aOldRowIter = m_aOldRows.begin(); aOldRowIter != aOldRowEnd; ++aOldRowIter)
1697 if ( (*aOldRowIter)->getRow() == *_aIter )
1698 *_aIter = new ORowSetValueVector(nColumnCount);
1701 m_pCacheSet->fillValueRow(*_aIter, _nPos);
1702 _bCheck = m_pCacheSet->next();
1704 return _bCheck;
1707 bool ORowSetCache::isResultSetChanged() const
1709 return m_pCacheSet->isResultSetChanged();
1712 void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet)
1714 m_xSet = _xDriverSet;
1715 m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY)->getMetaData());
1716 m_pCacheSet->reset(_xDriverSet);
1718 m_bRowCountFinal = false;
1719 m_nRowCount = 0;
1720 reFillMatrix(m_nStartPos,m_nEndPos);
1723 void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow
1724 ,::std::vector<sal_Int32>& o_ChangedColumns)
1726 if ( o_ChangedColumns.size() > 1 )
1728 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
1729 for(;aIter != m_pMatrix->end();++aIter)
1731 if ( aIter->is() && m_pCacheSet->updateColumnValues((*aIter)->get(),io_aRow,o_ChangedColumns))
1733 break;
1737 if ( aIter == m_pMatrix->end() )
1739 m_pCacheSet->fillMissingValues(io_aRow);
1744 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */