nss: upgrade to release 3.73
[LibreOffice.git] / dbaccess / source / core / api / RowSetCache.cxx
blobc80c04594786d88da006e429e87332eb52e4077a
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 <memory>
21 #include "BookmarkSet.hxx"
22 #include "KeySet.hxx"
23 #include "OptimisticSet.hxx"
24 #include "RowSetBase.hxx"
25 #include "RowSetCache.hxx"
26 #include "StaticSet.hxx"
27 #include "WrappedResultSet.hxx"
28 #include <core_resource.hxx>
29 #include <strings.hrc>
30 #include <strings.hxx>
32 #include <com/sun/star/sdbc/ColumnValue.hpp>
33 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
34 #include <com/sun/star/sdbc/ResultSetType.hpp>
35 #include <com/sun/star/sdbcx/CompareBookmark.hpp>
36 #include <com/sun/star/sdbcx/Privilege.hpp>
37 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
38 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
40 #include <comphelper/extract.hxx>
41 #include <comphelper/types.hxx>
42 #include <connectivity/dbexception.hxx>
43 #include <connectivity/dbtools.hxx>
44 #include <connectivity/sqliterator.hxx>
45 #include <connectivity/sqlnode.hxx>
46 #include <connectivity/sqlparse.hxx>
47 #include <sqlbison.hxx>
48 #include <tools/diagnose_ex.h>
49 #include <o3tl/safeint.hxx>
50 #include <osl/diagnose.h>
52 #include <algorithm>
54 using namespace dbaccess;
55 using namespace dbtools;
56 using namespace connectivity;
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::beans;
59 using namespace ::com::sun::star::sdbc;
60 using namespace ::com::sun::star::sdb;
61 using namespace ::com::sun::star::sdbcx;
62 using namespace ::com::sun::star::container;
63 using namespace ::com::sun::star::lang;
64 using namespace ::cppu;
65 using namespace ::osl;
67 #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!")
69 // This class calls m_pCacheSet->FOO_checked(..., sal_False)
70 // (where FOO is absolute, last, previous)
71 // when it does not immediately care about the values in the row's columns.
72 // As a corollary, m_pCacheSet may be left in an inconsistent state,
73 // and all ->fillFOO calls (and ->getFOO) may fail or give wrong results,
74 // until m_pCacheSet is moved (or refreshed) again.
75 // So always make sure m_pCacheSet is moved or refreshed before accessing column values.
78 ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs,
79 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
80 const Reference<XComponentContext>& _rContext,
81 const OUString& _rUpdateTableName,
82 bool& _bModified,
83 bool& _bNew,
84 const ORowSetValueVector& _aParameterValueForCache,
85 const OUString& i_sRowSetFilter,
86 sal_Int32 i_nMaxRows)
87 :m_xSet(_xRs)
88 ,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY_THROW)->getMetaData())
89 ,m_aContext( _rContext )
90 ,m_nFetchSize(0)
91 ,m_nRowCount(0)
92 ,m_nPrivileges( Privilege::SELECT )
93 ,m_nPosition(0)
94 ,m_nStartPos(0)
95 ,m_nEndPos(0)
96 ,m_bRowCountFinal(false)
97 ,m_bBeforeFirst(true)
98 ,m_bAfterLast( false )
99 ,m_bModified(_bModified)
100 ,m_bNew(_bNew)
103 // first try if the result can be used to do inserts and updates
104 Reference< XPropertySet> xProp(_xRs,UNO_QUERY);
105 Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo();
106 bool bBookmarkable = false;
109 Reference< XResultSetUpdate> xUp(_xRs,UNO_QUERY_THROW);
110 bBookmarkable = xPropInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE) &&
111 any2bool(xProp->getPropertyValue(PROPERTY_ISBOOKMARKABLE)) && Reference< XRowLocate >(_xRs, UNO_QUERY).is();
112 if ( bBookmarkable )
114 xUp->moveToInsertRow();
115 xUp->cancelRowUpdates();
116 _xRs->beforeFirst();
117 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
118 m_xCacheSet = new WrappedResultSet(i_nMaxRows);
119 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
120 return;
123 catch(const Exception&)
125 DBG_UNHANDLED_EXCEPTION("dbaccess.core");
129 if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) &&
130 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY)
131 _xRs->beforeFirst();
133 catch(const SQLException&)
135 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
138 // check if all keys of the updateable table are fetched
139 bool bAllKeysFound = false;
140 sal_Int32 nTablesCount = 0;
142 bool bNeedKeySet = !bBookmarkable || (xPropInfo->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
143 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY);
145 OUString aUpdateTableName = _rUpdateTableName;
146 Reference< XConnection> xConnection;
147 // first we need a connection
148 Reference< XStatement> xStmt(_xRs->getStatement(),UNO_QUERY);
149 if(xStmt.is())
150 xConnection = xStmt->getConnection();
151 else
153 Reference< XPreparedStatement> xPrepStmt(_xRs->getStatement(),UNO_QUERY);
154 xConnection = xPrepStmt->getConnection();
156 OSL_ENSURE(xConnection.is(),"No connection!");
157 if(_xAnalyzer.is())
161 Reference<XTablesSupplier> xTabSup(_xAnalyzer,UNO_QUERY);
162 OSL_ENSURE(xTabSup.is(),"ORowSet::execute composer isn't a tablesupplier!");
163 Reference<XNameAccess> xTables = xTabSup->getTables();
164 Sequence< OUString> aTableNames = xTables->getElementNames();
165 if ( aTableNames.getLength() > 1 && _rUpdateTableName.isEmpty() && bNeedKeySet )
166 {// here we have a join or union and nobody told us which table to update, so we update them all
167 m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
168 OptimisticSet* pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
169 m_xCacheSet = pCursor;
172 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
173 if ( pCursor->isReadOnly() )
174 m_nPrivileges = Privilege::SELECT;
175 m_aKeyColumns = pCursor->getJoinedKeyColumns();
176 return;
178 catch (const Exception&)
180 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
182 m_xCacheSet.clear();
184 else
186 if(!_rUpdateTableName.isEmpty() && xTables->hasByName(_rUpdateTableName))
187 xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable;
188 else if(xTables->getElementNames().hasElements())
190 aUpdateTableName = xTables->getElementNames()[0];
191 xTables->getByName(aUpdateTableName) >>= m_aUpdateTable;
193 Reference<XIndexAccess> xIndexAccess(xTables,UNO_QUERY);
194 if(xIndexAccess.is())
195 nTablesCount = xIndexAccess->getCount();
196 else
197 nTablesCount = xTables->getElementNames().getLength();
199 if(m_aUpdateTable.is() && nTablesCount < 3) // for we can't handle more than 2 tables in our keyset
201 Reference<XPropertySet> xSet(m_aUpdateTable,UNO_QUERY);
202 const Reference<XNameAccess> xPrimaryKeyColumns = dbtools::getPrimaryKeyColumns_throw(xSet);
203 if ( xPrimaryKeyColumns.is() )
205 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
206 if ( xColSup.is() )
208 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
209 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
210 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
211 ::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
212 bAllKeysFound = !aColumnNames.empty() && sal_Int32(aColumnNames.size()) == xPrimaryKeyColumns->getElementNames().getLength();
218 catch (Exception const&)
220 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
224 // first check if resultset is bookmarkable
225 if(!bNeedKeySet)
229 m_xCacheSet = new OBookmarkSet(i_nMaxRows);
230 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
232 // check privileges
233 m_nPrivileges = Privilege::SELECT;
234 if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
236 Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
237 if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
239 m_nPrivileges = 0;
240 xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
241 if(!m_nPrivileges)
242 m_nPrivileges = Privilege::SELECT;
246 catch (const SQLException&)
248 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
249 bNeedKeySet = true;
253 if(bNeedKeySet)
255 // need to check if we could handle this select clause
256 bAllKeysFound = bAllKeysFound && (nTablesCount == 1 || checkJoin(xConnection,_xAnalyzer,aUpdateTableName));
258 if(!bAllKeysFound )
260 if ( bBookmarkable )
262 // here I know that we have a read only bookmarkable cursor
263 _xRs->beforeFirst();
264 m_nPrivileges = Privilege::SELECT;
265 m_xCacheSet = new WrappedResultSet(i_nMaxRows);
266 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
267 return;
269 m_xCacheSet = new OStaticSet(i_nMaxRows);
270 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
271 m_nPrivileges = Privilege::SELECT;
273 else
275 Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
276 SelectColumnsMetaData aColumnNames(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
277 Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
278 Reference<XNameAccess> xSelColumns = xColSup->getColumns();
279 Reference<XNameAccess> xColumns = m_aUpdateTable->getColumns();
280 ::dbaccess::getColumnPositions(xSelColumns,xColumns->getElementNames(),aUpdateTableName,aColumnNames);
282 // check privileges
283 m_nPrivileges = Privilege::SELECT;
284 bool bNoInsert = false;
286 Sequence< OUString> aNames(xColumns->getElementNames());
287 const OUString* pIter = aNames.getConstArray();
288 const OUString* pEnd = pIter + aNames.getLength();
289 for(;pIter != pEnd;++pIter)
291 Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),UNO_QUERY);
292 OSL_ENSURE(xColumn.is(),"Column in table is null!");
293 if(xColumn.is())
295 sal_Int32 nNullable = 0;
296 xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable;
297 if(nNullable == ColumnValue::NO_NULLS && aColumnNames.find(*pIter) == aColumnNames.end())
298 { // we found a column where null is not allowed so we can't insert new values
299 bNoInsert = true;
300 break; // one column is enough
305 OKeySet* pKeySet = new OKeySet(m_aUpdateTable, aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
308 m_xCacheSet = pKeySet;
309 pKeySet->construct(_xRs,i_sRowSetFilter);
311 if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
313 Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY);
314 if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
316 m_nPrivileges = 0;
317 xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges;
318 if(!m_nPrivileges)
319 m_nPrivileges = Privilege::SELECT;
322 if(bNoInsert)
323 m_nPrivileges |= ~Privilege::INSERT; // remove the insert privilege
325 catch (const SQLException&)
327 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
328 // we couldn't create a keyset here so we have to create a static cache
329 m_xCacheSet = new OStaticSet(i_nMaxRows);
330 m_xCacheSet->construct(_xRs,i_sRowSetFilter);
331 m_nPrivileges = Privilege::SELECT;
336 // last check
337 if(!bAllKeysFound && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) &&
338 ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY)
339 m_nPrivileges = Privilege::SELECT;
342 ORowSetCache::~ORowSetCache()
344 m_xCacheSet.clear();
345 if(m_pMatrix)
347 m_pMatrix->clear();
348 m_pMatrix.reset();
351 if(m_pInsertMatrix)
353 m_pInsertMatrix->clear();
354 m_pInsertMatrix.reset();
356 m_xSet = WeakReference< XResultSet>();
357 m_xMetaData = nullptr;
358 m_aUpdateTable = nullptr;
361 void ORowSetCache::setFetchSize(sal_Int32 _nSize)
363 if(_nSize == m_nFetchSize)
364 return;
366 m_nFetchSize = _nSize;
367 if(!m_pMatrix)
369 m_pMatrix.reset( new ORowSetMatrix(_nSize) );
370 m_aMatrixIter = m_pMatrix->end();
371 m_aMatrixEnd = m_pMatrix->end();
373 m_pInsertMatrix.reset( new ORowSetMatrix(1) ); // a little bit overkill but ??? :-)
374 m_aInsertRow = m_pInsertMatrix->end();
376 else
378 // now correct the iterator in our iterator vector
379 std::vector<sal_Int32> aPositions;
380 std::map<sal_Int32,bool> aCacheIterToChange;
381 // first get the positions where they stand now
382 for(const auto& [rIndex, rHelper] : m_aCacheIterators)
384 aCacheIterToChange[rIndex] = false;
385 if ( !rHelper.pRowSet->isInsertRow()
386 /*&& rHelper.aIterator != m_pMatrix->end()*/ && !m_bModified )
388 ptrdiff_t nDist = rHelper.aIterator - m_pMatrix->begin();
389 aPositions.push_back(nDist);
390 aCacheIterToChange[rIndex] = true;
393 sal_Int32 nKeyPos = m_aMatrixIter - m_pMatrix->begin();
394 m_pMatrix->resize(_nSize);
396 if ( nKeyPos < _nSize )
397 m_aMatrixIter = m_pMatrix->begin() + nKeyPos;
398 else
399 m_aMatrixIter = m_pMatrix->end();
400 m_aMatrixEnd = m_pMatrix->end();
402 // now adjust their positions because a resize invalidates all iterators
403 std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
404 ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin();
405 for(const auto& rPosChange : aCacheIterToChange)
407 if ( rPosChange.second )
409 CHECK_MATRIX_POS(*aIter);
410 if ( *aIter < _nSize )
411 aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++;
412 else
413 aCacheIter->second.aIterator = m_pMatrix->end();
415 ++aCacheIter;
418 if(!m_nPosition)
420 sal_Int32 nNewSt = 0;
421 fillMatrix(nNewSt,_nSize);
422 OSL_ENSURE(nNewSt == 0, "fillMatrix set new start to unexpected value");
423 m_nStartPos = 0;
424 m_nEndPos = _nSize;
426 else if (m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos)
428 sal_Int32 nNewSt = -1;
429 _nSize += m_nStartPos;
430 fillMatrix(nNewSt, _nSize);
431 if (nNewSt >= 0)
433 m_nStartPos = nNewSt;
434 m_nEndPos = _nSize;
435 m_aMatrixIter = calcPosition();
437 else
439 m_nEndPos = m_nStartPos + m_nFetchSize;
442 else
444 OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos");
445 // try to repair
446 moveWindow();
447 m_aMatrixIter = calcPosition();
451 // XResultSetMetaDataSupplier
453 static Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
455 switch ( i_aValue.getTypeKind() )
457 case DataType::TINYINT:
458 case DataType::SMALLINT:
459 case DataType::INTEGER:
460 return makeAny(static_cast<sal_Int32>(i_aValue));
461 default:
462 if ( i_pCacheSet && i_aValue.isNull())
463 i_aValue = i_pCacheSet->getBookmark();
464 return i_aValue.getAny();
468 // css::sdbcx::XRowLocate
469 Any ORowSetCache::getBookmark( )
471 if(m_bAfterLast)
472 throwFunctionSequenceException(m_xSet.get());
474 if ( m_aMatrixIter >= m_pMatrix->end() || m_aMatrixIter < m_pMatrix->begin() || !(*m_aMatrixIter).is())
476 return Any(); // this is allowed here because the rowset knows what it is doing
479 return lcl_getBookmark((**m_aMatrixIter)[0],m_xCacheSet.get());
482 bool ORowSetCache::moveToBookmark( const Any& bookmark )
484 if ( m_xCacheSet->moveToBookmark(bookmark) )
486 m_bBeforeFirst = false;
487 m_nPosition = m_xCacheSet->getRow();
489 checkPositionFlags();
491 if(!m_bAfterLast)
493 moveWindow();
494 checkPositionFlags();
495 if ( !m_bAfterLast )
497 m_aMatrixIter = calcPosition();
498 OSL_ENSURE(m_aMatrixIter->is(),"Iterator after moveToBookmark not valid");
500 else
501 m_aMatrixIter = m_pMatrix->end();
503 else
504 m_aMatrixIter = m_pMatrix->end();
506 else
507 return false;
509 return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
512 bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows )
514 bool bRet( moveToBookmark( bookmark ) );
515 if ( bRet )
517 m_nPosition = m_xCacheSet->getRow() + rows;
518 absolute(m_nPosition);
520 bRet = m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is();
523 return bRet;
526 sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second )
528 return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_xCacheSet->compareBookmarks(_first,_second);
531 bool ORowSetCache::hasOrderedBookmarks( )
533 return m_xCacheSet->hasOrderedBookmarks();
536 sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark )
538 return m_xCacheSet->hashBookmark(bookmark);
541 // XRowUpdate
542 void ORowSetCache::updateNull(sal_Int32 columnIndex,ORowSetValueVector::Vector& io_aRow
543 ,std::vector<sal_Int32>& o_ChangedColumns
546 checkUpdateConditions(columnIndex);
548 ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
549 if ( !rInsert[columnIndex].isNull() )
551 rInsert[columnIndex].setBound(true);
552 rInsert[columnIndex].setNull();
553 rInsert[columnIndex].setModified(true);
554 io_aRow[columnIndex].setNull();
556 m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
557 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
561 void ORowSetCache::updateValue(sal_Int32 columnIndex,const ORowSetValue& x
562 ,ORowSetValueVector::Vector& io_aRow
563 ,std::vector<sal_Int32>& o_ChangedColumns
566 checkUpdateConditions(columnIndex);
568 ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
569 if ( rInsert[columnIndex] != x )
571 rInsert[columnIndex].setBound(true);
572 rInsert[columnIndex] = x;
573 rInsert[columnIndex].setModified(true);
574 io_aRow[columnIndex] = rInsert[columnIndex];
576 m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
577 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
581 void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x
582 , sal_Int32 length,ORowSetValueVector::Vector& io_aRow
583 ,std::vector<sal_Int32>& o_ChangedColumns
586 checkUpdateConditions(columnIndex);
588 Sequence<sal_Int8> aSeq;
589 if(x.is())
590 x->readBytes(aSeq,length);
592 ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
593 rInsert[columnIndex].setBound(true);
594 rInsert[columnIndex] = aSeq;
595 rInsert[columnIndex].setModified(true);
596 io_aRow[columnIndex] = makeAny(x);
598 m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
599 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
602 void ORowSetCache::updateObject( sal_Int32 columnIndex, const Any& x
603 ,ORowSetValueVector::Vector& io_aRow
604 ,std::vector<sal_Int32>& o_ChangedColumns
607 checkUpdateConditions(columnIndex);
609 ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
610 ORowSetValue aTemp;
611 aTemp.fill(x);
612 if ( rInsert[columnIndex] != aTemp )
614 rInsert[columnIndex].setBound(true);
615 rInsert[columnIndex] = aTemp;
616 rInsert[columnIndex].setModified(true);
617 io_aRow[columnIndex] = rInsert[columnIndex];
619 m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
620 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
624 void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x
625 ,ORowSetValueVector::Vector& io_aRow
626 ,std::vector<sal_Int32>& o_ChangedColumns
629 checkUpdateConditions(columnIndex);
631 ORowSetValueVector::Vector& rInsert = **m_aInsertRow;
632 ORowSetValue aTemp;
633 aTemp.fill(x);
634 if ( rInsert[columnIndex] != aTemp )
636 rInsert[columnIndex].setBound(true);
637 rInsert[columnIndex] = aTemp;
638 rInsert[columnIndex].setModified(true);
639 io_aRow[columnIndex] = rInsert[columnIndex];
641 m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns);
642 impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns);
646 // XResultSet
647 bool ORowSetCache::next( )
649 if(!isAfterLast())
651 m_bBeforeFirst = false;
652 ++m_nPosition;
654 // after we increment the position we have to check if we are already after the last row
655 checkPositionFlags();
656 if(!m_bAfterLast)
658 moveWindow();
660 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
661 m_aMatrixIter = calcPosition();
662 checkPositionFlags();
666 return !m_bAfterLast;
670 bool ORowSetCache::isFirst( ) const
672 return m_nPosition == 1; // ask resultset for
675 bool ORowSetCache::isLast( ) const
677 return m_nPosition == m_nRowCount;
680 void ORowSetCache::beforeFirst( )
682 if(!m_bBeforeFirst)
684 m_bAfterLast = false;
685 m_nPosition = 0;
686 m_bBeforeFirst = true;
687 m_xCacheSet->beforeFirst();
688 moveWindow();
689 m_aMatrixIter = m_pMatrix->end();
693 void ORowSetCache::afterLast( )
695 if(m_bAfterLast)
696 return;
698 m_bBeforeFirst = false;
699 m_bAfterLast = true;
701 if(!m_bRowCountFinal)
703 m_xCacheSet->last();
704 m_bRowCountFinal = true;
705 m_nRowCount = m_xCacheSet->getRow();// + 1 removed
707 m_xCacheSet->afterLast();
709 m_nPosition = 0;
710 m_aMatrixIter = m_pMatrix->end();
713 bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos, sal_Int32 &_nNewEndPos)
715 OSL_ENSURE((_nNewStartPos != _nNewEndPos) || (_nNewStartPos == 0 && _nNewEndPos == 0 && m_nRowCount == 0),
716 "ORowSetCache::fillMatrix: StartPos and EndPos can not be equal (unless the recordset is empty)!");
717 // If _nNewStartPos >= 0, then fill the whole window with new data
718 // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos)
719 // Else, undefined (invalid argument)
720 OSL_ENSURE( _nNewStartPos >= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );
722 ORowSetMatrix::iterator aIter;
723 sal_Int32 i;
724 bool bCheck;
725 sal_Int32 requestedStartPos;
726 if ( _nNewStartPos == -1 )
728 aIter = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
729 i = m_nEndPos + 1;
730 requestedStartPos = m_nStartPos;
732 else
734 aIter = m_pMatrix->begin();
735 i = _nNewStartPos + 1;
736 requestedStartPos = _nNewStartPos;
738 bCheck = m_xCacheSet->absolute(i);
741 for(; i <= _nNewEndPos; ++i,++aIter)
743 if(bCheck)
745 if(!aIter->is())
746 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
747 m_xCacheSet->fillValueRow(*aIter,i);
749 else
750 { // there are no more rows found so we can fetch some before start
752 if(!m_bRowCountFinal)
754 if(m_xCacheSet->previous()) // because we stand after the last row
755 m_nRowCount = m_xCacheSet->getRow(); // here we have the row count
756 if(!m_nRowCount)
757 m_nRowCount = i-1; // it can be that getRow return zero
758 m_bRowCountFinal = true;
760 const ORowSetMatrix::iterator aEnd = aIter;
761 ORowSetMatrix::const_iterator aRealEnd = m_pMatrix->end();
762 sal_Int32 nPos;
763 if (m_nRowCount >= m_nFetchSize)
765 nPos = m_nRowCount - m_nFetchSize;
767 else
769 nPos = 0;
771 _nNewStartPos = nPos;
772 _nNewEndPos = m_nRowCount;
773 ++nPos;
774 bCheck = m_xCacheSet->absolute(nPos);
776 for(;bCheck && nPos <= requestedStartPos && aIter != aRealEnd; ++aIter, ++nPos)
778 if(!aIter->is())
779 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
780 m_xCacheSet->fillValueRow(*aIter, nPos);
781 bCheck = m_xCacheSet->next();
783 if(aIter != aEnd)
784 std::rotate(m_pMatrix->begin(),aEnd,aIter);
785 break;
787 bCheck = m_xCacheSet->next();
789 // we have to read one row forward to ensure that we know when we are on last row
790 // but only when we don't know it already
791 if(!m_bRowCountFinal)
793 if(!m_xCacheSet->next())
795 if(m_xCacheSet->previous()) // because we stand after the last row
796 m_nRowCount = m_xCacheSet->getRow(); // here we have the row count
797 m_bRowCountFinal = true;
799 else
800 m_nRowCount = std::max(i,m_nRowCount);
803 return bCheck;
806 // If m_nPosition is out of the current window,
807 // move it and update m_nStartPos and m_nEndPos
808 // Caller is responsible for updating m_aMatrixIter
809 void ORowSetCache::moveWindow()
811 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
812 OSL_ENSURE(m_nEndPos >= m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
813 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
815 if ( m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos )
817 // just move inside the window
818 OSL_ENSURE((m_nPosition - m_nStartPos) <= static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
819 // make double plus sure that we have fetched that row
820 m_aMatrixIter = calcPosition();
821 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(), "New m_aMatrixIter is at end(), but should not.");
822 if(!m_aMatrixIter->is())
824 bool bOk( m_xCacheSet->absolute( m_nPosition ) );
825 if ( bOk )
827 *m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
828 m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
829 // we have to read one row forward to ensure that we know when we are on last row
830 // but only when we don't know it already
831 if ( !m_bRowCountFinal )
833 bOk = m_xCacheSet->absolute( m_nPosition + 1 );
834 if ( bOk )
835 m_nRowCount = std::max(sal_Int32(m_nPosition+1),m_nRowCount);
838 if(!bOk && !m_bRowCountFinal)
840 // because we stand after the last row
841 m_nRowCount = m_xCacheSet->previous() ? m_xCacheSet->getRow() : 0;
842 m_bRowCountFinal = true;
845 return;
848 sal_Int32 nDiff = (m_nFetchSize - 1) / 2;
849 sal_Int32 nNewStartPos = (m_nPosition - nDiff) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
850 sal_Int32 nNewEndPos = nNewStartPos + m_nFetchSize;
852 if ( nNewStartPos < 0 )
854 // The computed new window crashes through the floor (begins before first row);
855 // nNew*Pos has to be shifted by -nNewStartPos
856 nNewEndPos -= nNewStartPos;
857 nNewStartPos = 0;
860 if ( nNewStartPos < m_nStartPos )
861 { // need to fill data *before* m_nStartPos
862 if ( nNewEndPos > m_nStartPos )
863 { // The two regions are overlapping.
864 // We'll first rotate the contents of m_pMatrix so that the overlap area
865 // is positioned right; in the old window it is at the beginning,
866 // it has to go to the end.
867 // then we fill in the rows between new and old start pos.
869 bool bCheck;
870 bCheck = m_xCacheSet->absolute(nNewStartPos + 1);
872 // m_nEndPos < nNewEndPos when window not filled (e.g. there are fewer rows in total than window size)
873 m_nEndPos = std::min(nNewEndPos, m_nEndPos);
874 const sal_Int32 nOverlapSize = m_nEndPos - m_nStartPos;
875 const sal_Int32 nStartPosOffset = m_nStartPos - nNewStartPos; // by how much m_nStartPos moves
876 m_nStartPos = nNewStartPos;
877 OSL_ENSURE( o3tl::make_unsigned(nOverlapSize) <= m_pMatrix->size(), "new window end is after end of cache matrix!" );
878 // the first position in m_pMatrix whose data we don't keep;
879 // content will be moved to m_pMatrix.begin()
880 ORowSetMatrix::iterator aEnd (m_pMatrix->begin() + nOverlapSize);
881 // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
882 ORowSetMatrix::iterator aNewEnd (aEnd + nStartPosOffset);
883 // *m_pMatrix now looks like:
884 // [0; nOverlapSize) i.e. [begin(); aEnd): data kept
885 // [nOverlapSize; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos
886 // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
887 // Note that nOverlapSize + nStartPosOffset == m_nEndPos - m_nStartPos (new values)
888 // When we are finished:
889 // [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos
890 // [nStartPosOffset; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): kept
891 // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
893 if ( bCheck )
896 ORowSetMatrix::iterator aIter(aEnd);
897 sal_Int32 nPos = m_nStartPos + 1;
898 fill(aIter, aNewEnd, nPos, bCheck);
901 std::rotate(m_pMatrix->begin(), aEnd, aNewEnd);
902 if (!m_bModified)
904 // now correct the iterator in our iterator vector
905 // rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment
906 for(auto& rCacheIter : m_aCacheIterators)
908 if ( !rCacheIter.second.pRowSet->isInsertRow()
909 && rCacheIter.second.aIterator != m_pMatrix->end() )
911 const ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin();
912 if ( nDist >= nOverlapSize )
914 // That's from outside the overlap area; invalidate iterator.
915 rCacheIter.second.aIterator = m_pMatrix->end();
917 else
919 // Inside overlap area: move to correct position
920 CHECK_MATRIX_POS( (nDist + nStartPosOffset) );
921 rCacheIter.second.aIterator += nStartPosOffset;
922 OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin()
923 && rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!");
929 else
930 { // normally this should never happen
931 OSL_FAIL("What the hell is happen here!");
932 return;
935 else
936 {// no rows can be reused so fill again
937 reFillMatrix(nNewStartPos,nNewEndPos);
941 OSL_ENSURE(nNewStartPos >= m_nStartPos, "ORowSetCache::moveWindow internal error: new start pos before current start pos");
942 if ( m_nEndPos < nNewEndPos )
943 { // need to fill data *after* m_nEndPos
944 if( nNewStartPos < m_nEndPos )
945 { // The two regions are overlapping.
946 const sal_Int32 nRowsInCache = m_nEndPos - m_nStartPos;
947 if ( nRowsInCache < m_nFetchSize )
949 // There is some unused space in *m_pMatrix; fill it
950 CHECK_MATRIX_POS(nRowsInCache);
951 sal_Int32 nPos = m_nEndPos + 1;
952 bool bCheck = m_xCacheSet->absolute(nPos);
953 ORowSetMatrix::iterator aIter = m_pMatrix->begin() + nRowsInCache;
954 const sal_Int32 nRowsToFetch = std::min(nNewEndPos-m_nEndPos, m_nFetchSize-nRowsInCache);
955 const ORowSetMatrix::const_iterator aEnd = aIter + nRowsToFetch;
956 bCheck = fill(aIter, aEnd, nPos, bCheck);
957 m_nEndPos = nPos - 1;
958 OSL_ENSURE( (!bCheck && m_nEndPos <= nNewEndPos ) ||
959 ( bCheck && m_nEndPos == nNewEndPos ),
960 "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
963 // A priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive]
964 // have to be refilled with new to-be-fetched rows.
965 // The rows behind this can be reused
966 ORowSetMatrix::iterator aIter = m_pMatrix->begin();
967 const sal_Int32 nNewStartPosInMatrix = nNewStartPos - m_nStartPos;
968 CHECK_MATRIX_POS( nNewStartPosInMatrix );
969 // first position we reuse
970 const ORowSetMatrix::const_iterator aEnd = m_pMatrix->begin() + nNewStartPosInMatrix;
971 // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
972 ORowSetMatrix::iterator aDataEnd = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
974 sal_Int32 nPos = m_nEndPos + 1;
975 bool bCheck = m_xCacheSet->absolute(nPos);
976 bCheck = fill(aIter, aEnd, nPos, bCheck); // refill the region we don't need anymore
977 //aIter and nPos are now the position *after* last filled in one!
979 // bind end to front
980 if(bCheck)
982 OSL_ENSURE(aIter == aEnd, "fill() said went till end, but did not.");
983 // rotate the end to the front
984 std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
985 // now correct the iterator in our iterator vector
986 rotateCacheIterator( nNewStartPosInMatrix );
987 m_nStartPos = nNewStartPos;
988 m_nEndPos = nNewEndPos;
989 // now I can say how many rows we have
990 // we have to read one row forward to ensure that we know when we are on last row
991 // but only when we don't know it already
992 bool bOk = true;
993 if(!m_bRowCountFinal)
994 bOk = m_xCacheSet->next();
995 if(!bOk)
997 m_xCacheSet->previous(); // because we stand after the last row
998 m_nRowCount = nPos; // here we have the row count
999 OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos is not valid!");
1000 m_bRowCountFinal = true;
1002 else if(!m_bRowCountFinal)
1003 m_nRowCount = std::max(nPos+1, m_nRowCount); //+1 because we successfully moved to row after nPos
1004 else
1005 OSL_ENSURE(m_nRowCount >= nPos, "Final m_nRowCount is smaller than row I moved to!");
1007 else
1008 { // the end was reached before or at end() so we can set the start before or at nNewStartPos
1009 // and possibly keep more of m_pMatrix than planned.
1010 const ORowSetMatrix::const_iterator::difference_type nFetchedRows = aIter - m_pMatrix->begin();
1011 // *m_pMatrix now looks like:
1012 // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows
1013 // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???
1015 nPos -= 1;
1016 m_nStartPos += nFetchedRows;
1017 m_nEndPos = nPos;
1018 std::rotate(m_pMatrix->begin(), aIter, aDataEnd);
1019 // now correct the iterator in our iterator vector
1020 rotateCacheIterator( nFetchedRows );
1022 if ( !m_bRowCountFinal )
1024 m_xCacheSet->previous(); // because we stand after the last row
1025 m_nRowCount = std::max(m_nRowCount, nPos); // here we have the row count
1026 OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos isn't valid!");
1027 m_bRowCountFinal = true;
1031 // here we need only to check if the beginning row is valid. If not we have to fetch it.
1032 if(!m_pMatrix->begin()->is())
1034 aIter = m_pMatrix->begin();
1036 nPos = m_nStartPos + 1;
1037 bCheck = m_xCacheSet->absolute(nPos);
1038 for(; !aIter->is() && bCheck;++aIter, ++nPos)
1040 OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator");
1042 *aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
1043 m_xCacheSet->fillValueRow(*aIter, nPos);
1045 bCheck = m_xCacheSet->next();
1049 else // no rows can be reused so fill again
1050 reFillMatrix(nNewStartPos,nNewEndPos);
1053 if(!m_bRowCountFinal)
1054 m_nRowCount = std::max(m_nPosition,m_nRowCount);
1055 OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
1056 OSL_ENSURE(m_nEndPos > m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
1057 OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
1060 bool ORowSetCache::first( )
1062 // First move to the first row.
1063 // Then check if the cache window is at the beginning.
1064 // If not, then position the window and fill it with data.
1065 // We move the window smartly, i.e. we clear only the rows that are out of range
1066 bool bRet = m_xCacheSet->first();
1067 if(bRet)
1069 m_bBeforeFirst = m_bAfterLast = false;
1070 m_nPosition = 1;
1071 moveWindow();
1072 m_aMatrixIter = m_pMatrix->begin();
1074 else
1076 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
1077 m_nRowCount = m_nPosition = 0;
1079 OSL_ENSURE(m_bBeforeFirst || m_bNew,"ORowSetCache::first return false and BeforeFirst isn't true");
1080 m_aMatrixIter = m_pMatrix->end();
1082 return bRet;
1085 bool ORowSetCache::last( )
1087 bool bRet = m_xCacheSet->last();
1088 if(bRet)
1090 m_bBeforeFirst = m_bAfterLast = false;
1091 if(!m_bRowCountFinal)
1093 m_bRowCountFinal = true;
1094 m_nRowCount = m_xCacheSet->getRow(); // not + 1
1096 m_nPosition = m_xCacheSet->getRow();
1097 moveWindow();
1098 // we have to repositioning because moveWindow can modify the cache
1099 m_xCacheSet->last();
1100 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
1101 m_aMatrixIter = calcPosition();
1103 else
1105 m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
1106 m_nRowCount = m_nPosition = 0;
1107 OSL_ENSURE(m_bBeforeFirst,"ORowSetCache::last return false and BeforeFirst isn't true");
1108 m_aMatrixIter = m_pMatrix->end();
1110 #if OSL_DEBUG_LEVEL > 0
1111 if(bRet)
1113 assert((*m_aMatrixIter).is() && "ORowSetCache::last: Row not valid!");
1115 #endif
1117 return bRet;
1120 sal_Int32 ORowSetCache::getRow( ) const
1122 return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition;
1125 bool ORowSetCache::absolute( sal_Int32 row )
1127 if(!row )
1128 throw SQLException(DBA_RES(RID_STR_NO_ABS_ZERO),nullptr,SQLSTATE_GENERAL,1000,Any() );
1130 if(row < 0)
1132 // here we have to scroll from the last row to backward so we have to go to last row and
1133 // and to the previous
1134 if(m_bRowCountFinal || last())
1136 m_nPosition = m_nRowCount + row + 1; // + row because row is negative and +1 because row==-1 means last row
1137 if(m_nPosition < 1)
1139 m_bBeforeFirst = true;
1140 m_bAfterLast = false;
1141 m_aMatrixIter = m_pMatrix->end();
1143 else
1145 m_bBeforeFirst = false;
1146 m_bAfterLast = m_nPosition > m_nRowCount;
1147 moveWindow();
1148 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
1149 m_aMatrixIter = calcPosition();
1152 else
1153 m_aMatrixIter = m_pMatrix->end();
1155 else
1157 m_nPosition = row;
1158 // the position flags
1159 m_bBeforeFirst = false;
1160 checkPositionFlags();
1162 if(!m_bAfterLast)
1164 moveWindow();
1165 checkPositionFlags();
1166 if(!m_bAfterLast)
1167 m_aMatrixIter = calcPosition();
1168 else
1169 m_aMatrixIter = m_pMatrix->end();
1171 else
1172 m_aMatrixIter = m_pMatrix->end();
1175 return !(m_bAfterLast || m_bBeforeFirst);
1178 bool ORowSetCache::relative( sal_Int32 rows )
1180 bool bErg = true;
1181 if(rows)
1183 sal_Int32 nNewPosition = m_nPosition + rows;
1185 if ( m_bBeforeFirst && rows > 0 )
1186 nNewPosition = rows;
1187 else if ( m_bRowCountFinal && m_bAfterLast && rows < 0 )
1188 nNewPosition = m_nRowCount + 1 + rows;
1189 else
1190 if ( m_bBeforeFirst || ( m_bRowCountFinal && m_bAfterLast ) )
1191 throw SQLException( DBA_RES( RID_STR_NO_RELATIVE ), nullptr, SQLSTATE_GENERAL, 1000, Any() );
1192 if ( nNewPosition )
1194 bErg = absolute( nNewPosition );
1195 bErg = bErg && !isAfterLast() && !isBeforeFirst();
1197 else
1199 m_bBeforeFirst = true;
1200 bErg = false;
1203 return bErg;
1206 bool ORowSetCache::previous( )
1208 bool bRet = false;
1209 if(!isBeforeFirst())
1211 if(m_bAfterLast) // we stand after the last row so one before is the last row
1212 bRet = last();
1213 else
1215 m_bAfterLast = false;
1216 --m_nPosition;
1217 moveWindow();
1218 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
1220 checkPositionFlags();
1222 if(!m_nPosition)
1224 m_bBeforeFirst = true;
1225 m_aMatrixIter = m_pMatrix->end();
1227 else
1229 m_aMatrixIter = calcPosition();
1230 bRet = (*m_aMatrixIter).is();
1234 return bRet;
1237 void ORowSetCache::refreshRow( )
1239 if(isAfterLast())
1240 throw SQLException(DBA_RES(RID_STR_NO_REFRESH_AFTERLAST),nullptr,SQLSTATE_GENERAL,1000,Any() );
1241 OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!");
1242 m_xCacheSet->refreshRow();
1243 m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1244 if ( m_bNew )
1246 cancelRowModification();
1250 bool ORowSetCache::rowUpdated( )
1252 return m_xCacheSet->rowUpdated();
1255 bool ORowSetCache::rowInserted( )
1257 return m_xCacheSet->rowInserted();
1260 // XResultSetUpdate
1261 bool ORowSetCache::insertRow(std::vector< Any >& o_aBookmarks)
1263 if ( !m_bNew || !m_aInsertRow->is() )
1264 throw SQLException(DBA_RES(RID_STR_NO_MOVETOINSERTROW_CALLED),nullptr,SQLSTATE_GENERAL,1000,Any() );
1266 m_xCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable);
1268 bool bRet( rowInserted() );
1269 if ( bRet )
1271 ++m_nRowCount;
1272 Any aBookmark = (**m_aInsertRow)[0].makeAny();
1273 m_bAfterLast = m_bBeforeFirst = false;
1274 if(aBookmark.hasValue())
1276 moveToBookmark(aBookmark);
1277 // update the cached values
1278 ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter;
1279 ORowSetMatrix::const_iterator aIter = m_pMatrix->begin();
1280 for(;aIter != m_pMatrix->end();++aIter)
1282 if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) )
1284 o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get()));
1288 else
1290 OSL_FAIL("There must be a bookmark after the row was inserted!");
1293 return bRet;
1296 void ORowSetCache::resetInsertRow(bool _bClearInsertRow)
1298 if ( _bClearInsertRow )
1299 clearInsertRow();
1300 m_bNew = false;
1301 m_bModified = false;
1304 void ORowSetCache::cancelRowModification()
1306 // clear the insertrow references -> implies that the current row of the rowset changes as well
1307 for(auto& rCacheIter : m_aCacheIterators)
1309 if ( rCacheIter.second.pRowSet->isInsertRow() && rCacheIter.second.aIterator == m_aInsertRow )
1310 rCacheIter.second.aIterator = m_pMatrix->end();
1312 resetInsertRow(false);
1315 void ORowSetCache::updateRow( ORowSetMatrix::iterator const & _rUpdateRow, std::vector< Any >& o_aBookmarks )
1317 if(isAfterLast() || isBeforeFirst())
1318 throw SQLException(DBA_RES(RID_STR_NO_UPDATEROW),nullptr,SQLSTATE_GENERAL,1000,Any() );
1320 Any aBookmark = (**_rUpdateRow)[0].makeAny();
1321 OSL_ENSURE(aBookmark.hasValue(),"Bookmark must have a value!");
1322 // here we don't have to reposition our CacheSet, when we try to update a row,
1323 // the row was already fetched
1324 moveToBookmark(aBookmark);
1325 m_xCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable);
1326 // refetch the whole row
1327 (*m_aMatrixIter) = nullptr;
1329 if ( moveToBookmark(aBookmark) )
1331 // update the cached values
1332 ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter;
1333 ORowSetMatrix::const_iterator aIter = m_pMatrix->begin();
1334 for(;aIter != m_pMatrix->end();++aIter)
1336 if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) )
1338 o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get()));
1343 m_bModified = false;
1346 bool ORowSetCache::deleteRow( )
1348 if(isAfterLast() || isBeforeFirst())
1349 throw SQLException(DBA_RES(RID_STR_NO_DELETEROW),nullptr,SQLSTATE_GENERAL,1000,Any() );
1351 m_xCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable);
1352 if ( !m_xCacheSet->rowDeleted() )
1353 return false;
1355 --m_nRowCount;
1356 OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
1357 ORowSetMatrix::iterator aPos = calcPosition();
1358 (*aPos) = nullptr;
1360 ORowSetMatrix::const_iterator aEnd = m_pMatrix->end();
1361 for(++aPos;aPos != aEnd && aPos->is();++aPos)
1363 *(aPos-1) = *aPos;
1364 (*aPos) = nullptr;
1366 m_aMatrixIter = m_pMatrix->end();
1368 --m_nPosition;
1369 return true;
1372 void ORowSetCache::cancelRowUpdates( )
1374 m_bNew = m_bModified = false;
1375 if(!m_nPosition)
1377 OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0");
1378 ::dbtools::throwFunctionSequenceException(nullptr);
1381 if(m_xCacheSet->absolute(m_nPosition))
1382 m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition);
1383 else
1385 OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
1386 ::dbtools::throwFunctionSequenceException(nullptr);
1390 void ORowSetCache::moveToInsertRow( )
1392 m_bNew = true;
1393 m_bAfterLast = false;
1395 m_aInsertRow = m_pInsertMatrix->begin();
1396 if(!m_aInsertRow->is())
1397 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1399 // we don't unbound the bookmark column
1400 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1;
1401 ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end();
1402 for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i)
1404 aIter->setBound(false);
1405 aIter->setModified(false);
1406 aIter->setNull();
1407 aIter->setTypeKind(m_xMetaData->getColumnType(i));
1411 ORowSetCacheIterator ORowSetCache::createIterator(ORowSetBase* _pRowSet)
1413 ORowSetCacheIterator_Helper aHelper;
1414 aHelper.aIterator = m_pMatrix->end();
1415 aHelper.pRowSet = _pRowSet;
1416 return ORowSetCacheIterator(m_aCacheIterators.insert(m_aCacheIterators.begin(),ORowSetCacheMap::value_type(m_aCacheIterators.size()+1,aHelper)),this,_pRowSet);
1419 void ORowSetCache::deleteIterator(const ORowSetBase* _pRowSet)
1421 ORowSetCacheMap::const_iterator aCacheIter = m_aCacheIterators.begin();
1422 for(;aCacheIter != m_aCacheIterators.end();)
1424 if ( aCacheIter->second.pRowSet == _pRowSet )
1426 aCacheIter = m_aCacheIterators.erase(aCacheIter);
1428 else
1429 ++aCacheIter;
1433 void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist)
1435 if (m_bModified)
1436 return;
1438 if(!_nDist)
1439 return;
1441 // now correct the iterator in our iterator vector
1442 for(auto& rCacheIter : m_aCacheIterators)
1444 if ( !rCacheIter.second.pRowSet->isInsertRow()
1445 && rCacheIter.second.aIterator != m_pMatrix->end())
1447 ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin();
1448 if(nDist < _nDist)
1450 rCacheIter.second.aIterator = m_pMatrix->end();
1452 else
1454 OSL_ENSURE((rCacheIter.second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!");
1455 rCacheIter.second.aIterator -= _nDist;
1456 OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin()
1457 && rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!");
1463 void ORowSetCache::rotateAllCacheIterators()
1465 if (m_bModified)
1466 return;
1468 // now correct the iterator in our iterator vector
1469 for (auto& rCacheIter : m_aCacheIterators)
1471 if (!rCacheIter.second.pRowSet->isInsertRow())
1473 rCacheIter.second.aIterator = m_pMatrix->end();
1478 void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow)
1480 m_aInsertRow = m_pInsertMatrix->begin();
1481 if(!m_aInsertRow->is())
1482 *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount());
1484 (*(*m_aInsertRow)) = *(*_rOriginalRow);
1485 // we don't unbound the bookmark column
1486 for(auto& rItem : **m_aInsertRow)
1487 rItem.setModified(false);
1490 void ORowSetCache::checkPositionFlags()
1492 if(m_bRowCountFinal)
1494 m_bAfterLast = m_nPosition > m_nRowCount;
1495 if(m_bAfterLast)
1496 m_nPosition = 0;//m_nRowCount;
1500 void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex)
1502 if(m_bAfterLast || columnIndex >= static_cast<sal_Int32>((*m_aInsertRow)->size()))
1503 throwFunctionSequenceException(m_xSet.get());
1506 bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const OUString& _sUpdateTableName)
1508 bool bOk = false;
1509 if (pNode->count() == 3 && // expression in parentheses
1510 SQL_ISPUNCTUATION(pNode->getChild(0),"(") &&
1511 SQL_ISPUNCTUATION(pNode->getChild(2),")"))
1513 bOk = checkInnerJoin(pNode->getChild(1),_xConnection,_sUpdateTableName);
1515 else if ((SQL_ISRULE(pNode,search_condition) || SQL_ISRULE(pNode,boolean_term)) && // AND/OR link
1516 pNode->count() == 3)
1518 // only allow an AND link
1519 if ( SQL_ISTOKEN(pNode->getChild(1),AND) )
1520 bOk = checkInnerJoin(pNode->getChild(0),_xConnection,_sUpdateTableName)
1521 && checkInnerJoin(pNode->getChild(2),_xConnection,_sUpdateTableName);
1523 else if (SQL_ISRULE(pNode,comparison_predicate))
1525 // only the comparison of columns is allowed
1526 OSL_ENSURE(pNode->count() == 3,"checkInnerJoin: Error in Parse Tree");
1527 if (!(SQL_ISRULE(pNode->getChild(0),column_ref) &&
1528 SQL_ISRULE(pNode->getChild(2),column_ref) &&
1529 pNode->getChild(1)->getNodeType() == SQLNodeType::Equal))
1531 bOk = false;
1533 else
1535 OUString sColumnName,sTableRange;
1536 OSQLParseTreeIterator::getColumnRange( pNode->getChild(0), _xConnection, sColumnName, sTableRange );
1537 bOk = sTableRange == _sUpdateTableName;
1538 if ( !bOk )
1540 OSQLParseTreeIterator::getColumnRange( pNode->getChild(2), _xConnection, sColumnName, sTableRange );
1541 bOk = sTableRange == _sUpdateTableName;
1545 return bOk;
1548 bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection,
1549 const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer,
1550 const OUString& _sUpdateTableName )
1552 bool bOk = false;
1553 OUString sSql = _xAnalyzer->getQuery();
1554 OUString sErrorMsg;
1555 ::connectivity::OSQLParser aSqlParser( m_aContext );
1556 std::unique_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql));
1557 if ( pSqlParseNode && SQL_ISRULE(pSqlParseNode, select_statement) )
1559 OSQLParseNode* pTableRefCommalist = pSqlParseNode->getByRule(::connectivity::OSQLParseNode::table_ref_commalist);
1560 OSL_ENSURE(pTableRefCommalist,"NO tables why!?");
1561 if(pTableRefCommalist && pTableRefCommalist->count() == 1)
1563 // we found only one element so it must some kind of join here
1564 OSQLParseNode* pJoin = pTableRefCommalist->getByRule(::connectivity::OSQLParseNode::qualified_join);
1565 if(pJoin)
1566 { // we are only interested in qualified joins like RIGHT or LEFT
1567 OSQLParseNode* pJoinType = pJoin->getChild(1);
1568 OSQLParseNode* pOuterType = nullptr;
1569 if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2)
1570 pOuterType = pJoinType->getChild(0);
1571 else if(SQL_ISRULE(pJoinType,outer_join_type))
1572 pOuterType = pJoinType;
1574 bool bCheck = false;
1575 bool bLeftSide = false;
1576 if(pOuterType)
1577 { // found outer join
1578 bLeftSide = SQL_ISTOKEN(pOuterType->getChild(0),LEFT);
1579 bCheck = bLeftSide || SQL_ISTOKEN(pOuterType->getChild(0),RIGHT);
1582 if(bCheck)
1583 { // here we know that we have to check on which side our table resides
1584 const OSQLParseNode* pTableRef;
1585 if(bLeftSide)
1586 pTableRef = pJoin->getChild(0);
1587 else
1588 pTableRef = pJoin->getChild(3);
1589 OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");
1591 OUString sTableRange = OSQLParseNode::getTableRange(pTableRef);
1592 if(sTableRange.isEmpty())
1593 pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, nullptr, false, false );
1594 bOk = sTableRange == _sUpdateTableName;
1598 else
1600 OSQLParseNode* pWhereOpt = pSqlParseNode->getChild(3)->getChild(1);
1601 if ( pWhereOpt && !pWhereOpt->isLeaf() )
1602 bOk = checkInnerJoin(pWhereOpt->getChild(1),_xConnection,_sUpdateTableName);
1605 return bOk;
1608 void ORowSetCache::clearInsertRow()
1610 // we don't unbound the bookmark column
1611 if ( m_aInsertRow != m_pInsertMatrix->end() && m_aInsertRow->is() )
1613 ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1;
1614 ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end();
1615 for(;aIter != aEnd;++aIter)
1617 aIter->setBound(false);
1618 aIter->setModified(false);
1619 aIter->setNull();
1624 ORowSetMatrix::iterator ORowSetCache::calcPosition() const
1626 sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1;
1627 CHECK_MATRIX_POS(nValue);
1628 return ( nValue < 0 || nValue >= static_cast<sal_Int32>(m_pMatrix->size()) ) ? m_pMatrix->end() : (m_pMatrix->begin() + nValue);
1631 TORowSetOldRowHelperRef ORowSetCache::registerOldRow()
1633 TORowSetOldRowHelperRef pRef = new ORowSetOldRowHelper(ORowSetRow());
1634 m_aOldRows.push_back(pRef);
1635 return pRef;
1638 void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef& _rRow)
1640 TOldRowSetRows::iterator aOldRowIter = std::find_if(m_aOldRows.begin(), m_aOldRows.end(),
1641 [&_rRow](const TORowSetOldRowHelperRef& rxOldRow) { return rxOldRow.get() == _rRow.get(); });
1642 if (aOldRowIter != m_aOldRows.end())
1643 m_aOldRows.erase(aOldRowIter);
1646 bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos, sal_Int32 _nNewEndPos)
1648 for (const auto& rxOldRow : m_aOldRows)
1650 if ( rxOldRow.is() && rxOldRow->getRow().is() )
1651 rxOldRow->setRow(new ORowSetValueVector( *(rxOldRow->getRow()) ) );
1653 sal_Int32 nNewSt = _nNewStartPos;
1654 bool bRet = fillMatrix(nNewSt,_nNewEndPos);
1655 m_nStartPos = nNewSt;
1656 m_nEndPos = _nNewEndPos;
1657 rotateAllCacheIterators(); // invalidate every iterator
1658 return bRet;
1661 bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter, const ORowSetMatrix::const_iterator& _aEnd, sal_Int32& _nPos, bool _bCheck)
1663 const sal_Int32 nColumnCount = m_xMetaData->getColumnCount();
1664 for (; _bCheck && _aIter != _aEnd; ++_aIter, ++_nPos)
1666 if ( !_aIter->is() )
1667 *_aIter = new ORowSetValueVector(nColumnCount);
1668 else
1670 for (const auto& rxOldRow : m_aOldRows)
1672 if ( rxOldRow->getRow() == *_aIter )
1673 *_aIter = new ORowSetValueVector(nColumnCount);
1676 m_xCacheSet->fillValueRow(*_aIter, _nPos);
1677 _bCheck = m_xCacheSet->next();
1679 return _bCheck;
1682 bool ORowSetCache::isResultSetChanged() const
1684 return m_xCacheSet->isResultSetChanged();
1687 void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet)
1689 m_xSet = _xDriverSet;
1690 m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY_THROW)->getMetaData());
1691 m_xCacheSet->reset(_xDriverSet);
1693 m_bRowCountFinal = false;
1694 m_nRowCount = 0;
1695 reFillMatrix(m_nStartPos,m_nEndPos);
1698 void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow
1699 ,std::vector<sal_Int32> const & o_ChangedColumns)
1701 if ( o_ChangedColumns.size() > 1 )
1703 for (auto const& elem : *m_pMatrix)
1705 if ( elem.is() && m_xCacheSet->updateColumnValues(*elem,io_aRow,o_ChangedColumns))
1707 return;
1710 m_xCacheSet->fillMissingValues(io_aRow);
1714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */