1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "BookmarkSet.hxx"
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 <comphelper/diagnose_ex.hxx>
49 #include <o3tl/safeint.hxx>
50 #include <osl/diagnose.h>
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 // This class calls m_pCacheSet->FOO_checked(..., sal_False)
68 // (where FOO is absolute, last, previous)
69 // when it does not immediately care about the values in the row's columns.
70 // As a corollary, m_pCacheSet may be left in an inconsistent state,
71 // and all ->fillFOO calls (and ->getFOO) may fail or give wrong results,
72 // until m_pCacheSet is moved (or refreshed) again.
73 // So always make sure m_pCacheSet is moved or refreshed before accessing column values.
76 ORowSetCache::ORowSetCache(const Reference
< XResultSet
>& _xRs
,
77 const Reference
< XSingleSelectQueryAnalyzer
>& _xAnalyzer
,
78 const Reference
<XComponentContext
>& _rContext
,
79 const OUString
& _rUpdateTableName
,
82 const ORowSetValueVector
& _aParameterValueForCache
,
83 const OUString
& i_sRowSetFilter
,
86 ,m_xMetaData(Reference
< XResultSetMetaDataSupplier
>(_xRs
,UNO_QUERY_THROW
)->getMetaData())
87 ,m_aContext( _rContext
)
90 ,m_nPrivileges( Privilege::SELECT
)
94 ,m_bRowCountFinal(false)
96 ,m_bAfterLast( false )
97 ,m_bModified(_bModified
)
101 // first try if the result can be used to do inserts and updates
102 Reference
< XPropertySet
> xProp(_xRs
,UNO_QUERY
);
103 Reference
< XPropertySetInfo
> xPropInfo
= xProp
->getPropertySetInfo();
104 bool bBookmarkable
= false;
107 Reference
< XResultSetUpdate
> xUp(_xRs
,UNO_QUERY_THROW
);
108 bBookmarkable
= xPropInfo
->hasPropertyByName(PROPERTY_ISBOOKMARKABLE
) &&
109 any2bool(xProp
->getPropertyValue(PROPERTY_ISBOOKMARKABLE
)) && Reference
< XRowLocate
>(_xRs
, UNO_QUERY
).is();
112 xUp
->moveToInsertRow();
113 xUp
->cancelRowUpdates();
115 m_nPrivileges
= Privilege::SELECT
|Privilege::DELETE
|Privilege::INSERT
|Privilege::UPDATE
;
116 m_xCacheSet
= new WrappedResultSet(i_nMaxRows
);
117 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
121 catch(const Exception
&)
123 DBG_UNHANDLED_EXCEPTION("dbaccess.core");
127 if ( xPropInfo
->hasPropertyByName(PROPERTY_RESULTSETTYPE
) &&
128 ::comphelper::getINT32(xProp
->getPropertyValue(PROPERTY_RESULTSETTYPE
)) != ResultSetType::FORWARD_ONLY
)
131 catch(const SQLException
&)
133 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
136 // check if all keys of the updateable table are fetched
137 bool bAllKeysFound
= false;
138 sal_Int32 nTablesCount
= 0;
140 bool bNeedKeySet
= !bBookmarkable
|| (xPropInfo
->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY
) &&
141 ::comphelper::getINT32(xProp
->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY
)) == ResultSetConcurrency::READ_ONLY
);
143 OUString aUpdateTableName
= _rUpdateTableName
;
144 Reference
< XConnection
> xConnection
;
145 // first we need a connection
146 Reference
< XStatement
> xStmt(_xRs
->getStatement(),UNO_QUERY
);
148 xConnection
= xStmt
->getConnection();
151 Reference
< XPreparedStatement
> xPrepStmt(_xRs
->getStatement(),UNO_QUERY
);
152 xConnection
= xPrepStmt
->getConnection();
154 OSL_ENSURE(xConnection
.is(),"No connection!");
159 Reference
<XTablesSupplier
> xTabSup(_xAnalyzer
,UNO_QUERY
);
160 OSL_ENSURE(xTabSup
.is(),"ORowSet::execute composer isn't a tablesupplier!");
161 Reference
<XNameAccess
> xTables
= xTabSup
->getTables();
162 Sequence
< OUString
> aTableNames
= xTables
->getElementNames();
163 if ( aTableNames
.getLength() > 1 && _rUpdateTableName
.isEmpty() && bNeedKeySet
)
164 {// here we have a join or union and nobody told us which table to update, so we update them all
165 m_nPrivileges
= Privilege::SELECT
|Privilege::DELETE
|Privilege::INSERT
|Privilege::UPDATE
;
166 rtl::Reference
<OptimisticSet
> pCursor
= new OptimisticSet(m_aContext
,xConnection
,_xAnalyzer
,_aParameterValueForCache
,i_nMaxRows
,m_nRowCount
);
167 m_xCacheSet
= pCursor
;
170 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
171 if ( pCursor
->isReadOnly() )
172 m_nPrivileges
= Privilege::SELECT
;
173 m_aKeyColumns
= pCursor
->getJoinedKeyColumns();
176 catch (const Exception
&)
178 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
184 if(!_rUpdateTableName
.isEmpty() && xTables
->hasByName(_rUpdateTableName
))
185 xTables
->getByName(_rUpdateTableName
) >>= m_aUpdateTable
;
186 else if(xTables
->getElementNames().hasElements())
188 aUpdateTableName
= xTables
->getElementNames()[0];
189 xTables
->getByName(aUpdateTableName
) >>= m_aUpdateTable
;
191 Reference
<XIndexAccess
> xIndexAccess(xTables
,UNO_QUERY
);
192 if(xIndexAccess
.is())
193 nTablesCount
= xIndexAccess
->getCount();
195 nTablesCount
= xTables
->getElementNames().getLength();
197 if(m_aUpdateTable
.is() && nTablesCount
< 3) // for we can't handle more than 2 tables in our keyset
199 Reference
<XPropertySet
> xSet(m_aUpdateTable
,UNO_QUERY
);
200 const Reference
<XNameAccess
> xPrimaryKeyColumns
= dbtools::getPrimaryKeyColumns_throw(xSet
);
201 if ( xPrimaryKeyColumns
.is() )
203 Reference
<XColumnsSupplier
> xColSup(_xAnalyzer
,UNO_QUERY
);
206 Reference
<XNameAccess
> xSelColumns
= xColSup
->getColumns();
207 Reference
<XDatabaseMetaData
> xMeta
= xConnection
->getMetaData();
208 SelectColumnsMetaData
aColumnNames(xMeta
.is() && xMeta
->supportsMixedCaseQuotedIdentifiers());
209 ::dbaccess::getColumnPositions(xSelColumns
,xPrimaryKeyColumns
->getElementNames(),aUpdateTableName
,aColumnNames
);
210 bAllKeysFound
= !aColumnNames
.empty() && aColumnNames
.size() == o3tl::make_unsigned(xPrimaryKeyColumns
->getElementNames().getLength());
216 catch (Exception
const&)
218 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
222 // first check if resultset is bookmarkable
227 m_xCacheSet
= new OBookmarkSet(i_nMaxRows
);
228 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
231 m_nPrivileges
= Privilege::SELECT
;
232 if(Reference
<XResultSetUpdate
>(_xRs
,UNO_QUERY
).is()) // this interface is optional so we have to check it
234 Reference
<XPropertySet
> xTable(m_aUpdateTable
,UNO_QUERY
);
235 if(xTable
.is() && xTable
->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES
))
238 xTable
->getPropertyValue(PROPERTY_PRIVILEGES
) >>= m_nPrivileges
;
240 m_nPrivileges
= Privilege::SELECT
;
244 catch (const SQLException
&)
246 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
253 // need to check if we could handle this select clause
254 bAllKeysFound
= bAllKeysFound
&& (nTablesCount
== 1 || checkJoin(xConnection
,_xAnalyzer
,aUpdateTableName
));
260 // here I know that we have a read only bookmarkable cursor
262 m_nPrivileges
= Privilege::SELECT
;
263 m_xCacheSet
= new WrappedResultSet(i_nMaxRows
);
264 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
267 m_xCacheSet
= new OStaticSet(i_nMaxRows
);
268 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
269 m_nPrivileges
= Privilege::SELECT
;
273 Reference
<XDatabaseMetaData
> xMeta
= xConnection
->getMetaData();
274 SelectColumnsMetaData
aColumnNames(xMeta
.is() && xMeta
->supportsMixedCaseQuotedIdentifiers());
275 Reference
<XColumnsSupplier
> xColSup(_xAnalyzer
,UNO_QUERY
);
276 Reference
<XNameAccess
> xSelColumns
= xColSup
->getColumns();
277 Reference
<XNameAccess
> xColumns
= m_aUpdateTable
->getColumns();
278 ::dbaccess::getColumnPositions(xSelColumns
,xColumns
->getElementNames(),aUpdateTableName
,aColumnNames
);
281 m_nPrivileges
= Privilege::SELECT
;
282 bool bNoInsert
= false;
284 Sequence
< OUString
> aNames(xColumns
->getElementNames());
285 const OUString
* pIter
= aNames
.getConstArray();
286 const OUString
* pEnd
= pIter
+ aNames
.getLength();
287 for(;pIter
!= pEnd
;++pIter
)
289 Reference
<XPropertySet
> xColumn(xColumns
->getByName(*pIter
),UNO_QUERY
);
290 OSL_ENSURE(xColumn
.is(),"Column in table is null!");
293 sal_Int32 nNullable
= 0;
294 xColumn
->getPropertyValue(PROPERTY_ISNULLABLE
) >>= nNullable
;
295 if(nNullable
== ColumnValue::NO_NULLS
&& aColumnNames
.find(*pIter
) == aColumnNames
.end())
296 { // we found a column where null is not allowed so we can't insert new values
298 break; // one column is enough
303 rtl::Reference
<OKeySet
> pKeySet
= new OKeySet(m_aUpdateTable
, aUpdateTableName
,_xAnalyzer
,_aParameterValueForCache
,i_nMaxRows
,m_nRowCount
);
306 m_xCacheSet
= pKeySet
;
307 pKeySet
->construct(_xRs
,i_sRowSetFilter
);
309 if(Reference
<XResultSetUpdate
>(_xRs
,UNO_QUERY
).is()) // this interface is optional so we have to check it
311 Reference
<XPropertySet
> xTable(m_aUpdateTable
,UNO_QUERY
);
312 if(xTable
.is() && xTable
->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES
))
315 xTable
->getPropertyValue(PROPERTY_PRIVILEGES
) >>= m_nPrivileges
;
317 m_nPrivileges
= Privilege::SELECT
;
321 m_nPrivileges
|= ~Privilege::INSERT
; // remove the insert privilege
323 catch (const SQLException
&)
325 TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
326 // we couldn't create a keyset here so we have to create a static cache
327 m_xCacheSet
= new OStaticSet(i_nMaxRows
);
328 m_xCacheSet
->construct(_xRs
,i_sRowSetFilter
);
329 m_nPrivileges
= Privilege::SELECT
;
335 if(!bAllKeysFound
&& xProp
->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY
) &&
336 ::comphelper::getINT32(xProp
->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY
)) == ResultSetConcurrency::READ_ONLY
)
337 m_nPrivileges
= Privilege::SELECT
;
340 ORowSetCache::~ORowSetCache()
351 m_pInsertMatrix
->clear();
352 m_pInsertMatrix
.reset();
354 m_xSet
= WeakReference
< XResultSet
>();
355 m_xMetaData
= nullptr;
356 m_aUpdateTable
= nullptr;
359 void ORowSetCache::setFetchSize(sal_Int32 _nSize
)
361 if(_nSize
== m_nFetchSize
)
364 m_nFetchSize
= _nSize
;
367 m_pMatrix
.reset( new ORowSetMatrix(_nSize
) );
368 m_aMatrixIter
= m_pMatrix
->end();
369 m_aMatrixEnd
= m_pMatrix
->end();
371 m_pInsertMatrix
.reset( new ORowSetMatrix(1) ); // a little bit overkill but ??? :-)
372 m_aInsertRow
= m_pInsertMatrix
->end();
376 // now correct the iterator in our iterator vector
377 std::vector
<sal_Int32
> aPositions
;
378 std::map
<sal_Int32
,bool> aCacheIterToChange
;
379 // first get the positions where they stand now
380 for(const auto& [rIndex
, rHelper
] : m_aCacheIterators
)
382 aCacheIterToChange
[rIndex
] = false;
383 if ( !rHelper
.pRowSet
->isInsertRow()
384 /*&& rHelper.aIterator != m_pMatrix->end()*/ && !m_bModified
)
386 ptrdiff_t nDist
= rHelper
.aIterator
- m_pMatrix
->begin();
387 aPositions
.push_back(nDist
);
388 aCacheIterToChange
[rIndex
] = true;
391 sal_Int32 nKeyPos
= m_aMatrixIter
- m_pMatrix
->begin();
392 m_pMatrix
->resize(_nSize
);
394 if ( nKeyPos
< _nSize
)
395 m_aMatrixIter
= m_pMatrix
->begin() + nKeyPos
;
397 m_aMatrixIter
= m_pMatrix
->end();
398 m_aMatrixEnd
= m_pMatrix
->end();
400 // now adjust their positions because a resize invalidates all iterators
401 std::vector
<sal_Int32
>::const_iterator aIter
= aPositions
.begin();
402 ORowSetCacheMap::iterator aCacheIter
= m_aCacheIterators
.begin();
403 for(const auto& rPosChange
: aCacheIterToChange
)
405 if ( rPosChange
.second
)
407 OSL_ENSURE((*aIter
>= static_cast<ORowSetMatrix::difference_type
>(0)) && (*aIter
< static_cast<sal_Int32
>(m_pMatrix
->size())),"Position is invalid!");
408 if ( *aIter
< _nSize
)
409 aCacheIter
->second
.aIterator
= m_pMatrix
->begin() + *aIter
++;
411 aCacheIter
->second
.aIterator
= m_pMatrix
->end();
418 sal_Int32 nNewSt
= 0;
419 fillMatrix(nNewSt
,_nSize
);
420 OSL_ENSURE(nNewSt
== 0, "fillMatrix set new start to unexpected value");
424 else if (m_nStartPos
< m_nPosition
&& m_nPosition
<= m_nEndPos
)
426 sal_Int32 nNewSt
= -1;
427 _nSize
+= m_nStartPos
;
428 fillMatrix(nNewSt
, _nSize
);
431 m_nStartPos
= nNewSt
;
433 m_aMatrixIter
= calcPosition();
437 m_nEndPos
= m_nStartPos
+ m_nFetchSize
;
442 OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos");
445 m_aMatrixIter
= calcPosition();
449 // XResultSetMetaDataSupplier
451 static Any
lcl_getBookmark(ORowSetValue
& i_aValue
,OCacheSet
* i_pCacheSet
)
453 switch ( i_aValue
.getTypeKind() )
455 case DataType::TINYINT
:
456 case DataType::SMALLINT
:
457 case DataType::INTEGER
:
458 return Any(i_aValue
.getInt32());
460 if ( i_pCacheSet
&& i_aValue
.isNull())
461 i_aValue
= i_pCacheSet
->getBookmark();
462 return i_aValue
.getAny();
466 // css::sdbcx::XRowLocate
467 Any
ORowSetCache::getBookmark( )
470 throwFunctionSequenceException(m_xSet
.get());
472 if ( m_aMatrixIter
>= m_pMatrix
->end() || m_aMatrixIter
< m_pMatrix
->begin() || !(*m_aMatrixIter
).is())
474 return Any(); // this is allowed here because the rowset knows what it is doing
477 return lcl_getBookmark((**m_aMatrixIter
)[0],m_xCacheSet
.get());
480 bool ORowSetCache::moveToBookmark( const Any
& bookmark
)
482 if ( m_xCacheSet
->moveToBookmark(bookmark
) )
484 m_bBeforeFirst
= false;
485 m_nPosition
= m_xCacheSet
->getRow();
487 checkPositionFlags();
492 checkPositionFlags();
495 m_aMatrixIter
= calcPosition();
496 OSL_ENSURE(m_aMatrixIter
->is(),"Iterator after moveToBookmark not valid");
499 m_aMatrixIter
= m_pMatrix
->end();
502 m_aMatrixIter
= m_pMatrix
->end();
507 return m_aMatrixIter
!= m_pMatrix
->end() && (*m_aMatrixIter
).is();
510 bool ORowSetCache::moveRelativeToBookmark( const Any
& bookmark
, sal_Int32 rows
)
512 bool bRet( moveToBookmark( bookmark
) );
515 m_nPosition
= m_xCacheSet
->getRow() + rows
;
516 absolute(m_nPosition
);
518 bRet
= m_aMatrixIter
!= m_pMatrix
->end() && (*m_aMatrixIter
).is();
524 sal_Int32
ORowSetCache::compareBookmarks( const Any
& _first
, const Any
& _second
)
526 return (!_first
.hasValue() || !_second
.hasValue()) ? CompareBookmark::NOT_COMPARABLE
: m_xCacheSet
->compareBookmarks(_first
,_second
);
529 bool ORowSetCache::hasOrderedBookmarks( )
531 return m_xCacheSet
->hasOrderedBookmarks();
534 sal_Int32
ORowSetCache::hashBookmark( const Any
& bookmark
)
536 return m_xCacheSet
->hashBookmark(bookmark
);
540 void ORowSetCache::updateNull(sal_Int32 columnIndex
,ORowSetValueVector::Vector
& io_aRow
541 ,std::vector
<sal_Int32
>& o_ChangedColumns
544 checkUpdateConditions(columnIndex
);
546 ORowSetValueVector::Vector
& rInsert
= **m_aInsertRow
;
547 if ( !rInsert
[columnIndex
].isNull() )
549 rInsert
[columnIndex
].setBound(true);
550 rInsert
[columnIndex
].setNull();
551 rInsert
[columnIndex
].setModified(true);
552 io_aRow
[columnIndex
].setNull();
554 m_xCacheSet
->mergeColumnValues(columnIndex
,rInsert
,io_aRow
,o_ChangedColumns
);
555 impl_updateRowFromCache_throw(io_aRow
,o_ChangedColumns
);
559 void ORowSetCache::updateValue(sal_Int32 columnIndex
,const ORowSetValue
& x
560 ,ORowSetValueVector::Vector
& io_aRow
561 ,std::vector
<sal_Int32
>& o_ChangedColumns
564 checkUpdateConditions(columnIndex
);
566 ORowSetValueVector::Vector
& rInsert
= **m_aInsertRow
;
567 if ( rInsert
[columnIndex
] != x
)
569 rInsert
[columnIndex
].setBound(true);
570 rInsert
[columnIndex
] = x
;
571 rInsert
[columnIndex
].setModified(true);
572 io_aRow
[columnIndex
] = rInsert
[columnIndex
];
574 m_xCacheSet
->mergeColumnValues(columnIndex
,rInsert
,io_aRow
,o_ChangedColumns
);
575 impl_updateRowFromCache_throw(io_aRow
,o_ChangedColumns
);
579 void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex
, const Reference
< css::io::XInputStream
>& x
580 , sal_Int32 length
,ORowSetValueVector::Vector
& io_aRow
581 ,std::vector
<sal_Int32
>& o_ChangedColumns
584 checkUpdateConditions(columnIndex
);
586 Sequence
<sal_Int8
> aSeq
;
588 x
->readBytes(aSeq
,length
);
590 ORowSetValueVector::Vector
& rInsert
= **m_aInsertRow
;
591 rInsert
[columnIndex
].setBound(true);
592 rInsert
[columnIndex
] = aSeq
;
593 rInsert
[columnIndex
].setModified(true);
594 io_aRow
[columnIndex
] = Any(x
);
596 m_xCacheSet
->mergeColumnValues(columnIndex
,rInsert
,io_aRow
,o_ChangedColumns
);
597 impl_updateRowFromCache_throw(io_aRow
,o_ChangedColumns
);
600 void ORowSetCache::updateObject( sal_Int32 columnIndex
, const Any
& x
601 ,ORowSetValueVector::Vector
& io_aRow
602 ,std::vector
<sal_Int32
>& o_ChangedColumns
605 checkUpdateConditions(columnIndex
);
607 ORowSetValueVector::Vector
& rInsert
= **m_aInsertRow
;
610 if ( rInsert
[columnIndex
] != aTemp
)
612 rInsert
[columnIndex
].setBound(true);
613 rInsert
[columnIndex
] = aTemp
;
614 rInsert
[columnIndex
].setModified(true);
615 io_aRow
[columnIndex
] = rInsert
[columnIndex
];
617 m_xCacheSet
->mergeColumnValues(columnIndex
,rInsert
,io_aRow
,o_ChangedColumns
);
618 impl_updateRowFromCache_throw(io_aRow
,o_ChangedColumns
);
622 void ORowSetCache::updateNumericObject( sal_Int32 columnIndex
, const Any
& x
623 ,ORowSetValueVector::Vector
& io_aRow
624 ,std::vector
<sal_Int32
>& o_ChangedColumns
627 checkUpdateConditions(columnIndex
);
629 ORowSetValueVector::Vector
& rInsert
= **m_aInsertRow
;
632 if ( rInsert
[columnIndex
] != aTemp
)
634 rInsert
[columnIndex
].setBound(true);
635 rInsert
[columnIndex
] = aTemp
;
636 rInsert
[columnIndex
].setModified(true);
637 io_aRow
[columnIndex
] = rInsert
[columnIndex
];
639 m_xCacheSet
->mergeColumnValues(columnIndex
,rInsert
,io_aRow
,o_ChangedColumns
);
640 impl_updateRowFromCache_throw(io_aRow
,o_ChangedColumns
);
645 bool ORowSetCache::next( )
649 m_bBeforeFirst
= false;
652 // after we increment the position we have to check if we are already after the last row
653 checkPositionFlags();
658 OSL_ENSURE(((m_nPosition
- m_nStartPos
) - 1) < static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
659 m_aMatrixIter
= calcPosition();
660 checkPositionFlags();
664 return !m_bAfterLast
;
668 bool ORowSetCache::isFirst( ) const
670 return m_nPosition
== 1; // ask resultset for
673 bool ORowSetCache::isLast( ) const
675 return m_nPosition
== m_nRowCount
;
678 void ORowSetCache::beforeFirst( )
682 m_bAfterLast
= false;
684 m_bBeforeFirst
= true;
685 m_xCacheSet
->beforeFirst();
687 m_aMatrixIter
= m_pMatrix
->end();
691 void ORowSetCache::afterLast( )
696 m_bBeforeFirst
= false;
699 if(!m_bRowCountFinal
)
702 m_bRowCountFinal
= true;
703 m_nRowCount
= m_xCacheSet
->getRow();// + 1 removed
705 m_xCacheSet
->afterLast();
708 m_aMatrixIter
= m_pMatrix
->end();
711 bool ORowSetCache::fillMatrix(sal_Int32
& _nNewStartPos
, sal_Int32
&_nNewEndPos
)
713 OSL_ENSURE((_nNewStartPos
!= _nNewEndPos
) || (_nNewStartPos
== 0 && _nNewEndPos
== 0 && m_nRowCount
== 0),
714 "ORowSetCache::fillMatrix: StartPos and EndPos can not be equal (unless the recordset is empty)!");
715 // If _nNewStartPos >= 0, then fill the whole window with new data
716 // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos)
717 // Else, undefined (invalid argument)
718 OSL_ENSURE( _nNewStartPos
>= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );
720 ORowSetMatrix::iterator aIter
;
723 sal_Int32 requestedStartPos
;
724 if ( _nNewStartPos
== -1 )
726 aIter
= m_pMatrix
->begin() + (m_nEndPos
- m_nStartPos
);
728 requestedStartPos
= m_nStartPos
;
732 aIter
= m_pMatrix
->begin();
733 i
= _nNewStartPos
+ 1;
734 requestedStartPos
= _nNewStartPos
;
736 bCheck
= m_xCacheSet
->absolute(i
);
739 for(; i
<= _nNewEndPos
; ++i
,++aIter
)
744 *aIter
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
745 m_xCacheSet
->fillValueRow(*aIter
,i
);
748 { // there are no more rows found so we can fetch some before start
750 if(!m_bRowCountFinal
)
752 if(m_xCacheSet
->previous()) // because we stand after the last row
753 m_nRowCount
= m_xCacheSet
->getRow(); // here we have the row count
755 m_nRowCount
= i
-1; // it can be that getRow return zero
756 m_bRowCountFinal
= true;
758 const ORowSetMatrix::iterator aEnd
= aIter
;
759 ORowSetMatrix::const_iterator aRealEnd
= m_pMatrix
->end();
761 if (m_nRowCount
>= m_nFetchSize
)
763 nPos
= m_nRowCount
- m_nFetchSize
;
769 _nNewStartPos
= nPos
;
770 _nNewEndPos
= m_nRowCount
;
772 bCheck
= m_xCacheSet
->absolute(nPos
);
774 for(;bCheck
&& nPos
<= requestedStartPos
&& aIter
!= aRealEnd
; ++aIter
, ++nPos
)
777 *aIter
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
778 m_xCacheSet
->fillValueRow(*aIter
, nPos
);
779 bCheck
= m_xCacheSet
->next();
782 std::rotate(m_pMatrix
->begin(),aEnd
,aIter
);
785 bCheck
= m_xCacheSet
->next();
787 // we have to read one row forward to ensure that we know when we are on last row
788 // but only when we don't know it already
789 if(!m_bRowCountFinal
)
791 if(!m_xCacheSet
->next())
793 if(m_xCacheSet
->previous()) // because we stand after the last row
794 m_nRowCount
= m_xCacheSet
->getRow(); // here we have the row count
795 m_bRowCountFinal
= true;
798 m_nRowCount
= std::max(i
,m_nRowCount
);
804 // If m_nPosition is out of the current window,
805 // move it and update m_nStartPos and m_nEndPos
806 // Caller is responsible for updating m_aMatrixIter
807 void ORowSetCache::moveWindow()
809 OSL_ENSURE(m_nStartPos
>= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
810 OSL_ENSURE(m_nEndPos
>= m_nStartPos
,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
811 OSL_ENSURE(m_nEndPos
-m_nStartPos
<= m_nFetchSize
,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
813 if ( m_nStartPos
< m_nPosition
&& m_nPosition
<= m_nEndPos
)
815 // just move inside the window
816 OSL_ENSURE((m_nPosition
- m_nStartPos
) <= static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
817 // make double plus sure that we have fetched that row
818 m_aMatrixIter
= calcPosition();
819 OSL_ENSURE(m_aMatrixIter
!= m_pMatrix
->end(), "New m_aMatrixIter is at end(), but should not.");
820 if(!m_aMatrixIter
->is())
822 bool bOk( m_xCacheSet
->absolute( m_nPosition
) );
825 *m_aMatrixIter
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
826 m_xCacheSet
->fillValueRow(*m_aMatrixIter
,m_nPosition
);
827 // we have to read one row forward to ensure that we know when we are on last row
828 // but only when we don't know it already
829 if ( !m_bRowCountFinal
)
831 bOk
= m_xCacheSet
->absolute( m_nPosition
+ 1 );
833 m_nRowCount
= std::max(sal_Int32(m_nPosition
+1),m_nRowCount
);
836 if(!bOk
&& !m_bRowCountFinal
)
838 // because we stand after the last row
839 m_nRowCount
= m_xCacheSet
->previous() ? m_xCacheSet
->getRow() : 0;
840 m_bRowCountFinal
= true;
846 sal_Int32 nDiff
= (m_nFetchSize
- 1) / 2;
847 sal_Int32 nNewStartPos
= (m_nPosition
- nDiff
) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
848 sal_Int32 nNewEndPos
= nNewStartPos
+ m_nFetchSize
;
850 if ( nNewStartPos
< 0 )
852 // The computed new window crashes through the floor (begins before first row);
853 // nNew*Pos has to be shifted by -nNewStartPos
854 nNewEndPos
-= nNewStartPos
;
858 if ( nNewStartPos
< m_nStartPos
)
859 { // need to fill data *before* m_nStartPos
860 if ( nNewEndPos
> m_nStartPos
)
861 { // The two regions are overlapping.
862 // We'll first rotate the contents of m_pMatrix so that the overlap area
863 // is positioned right; in the old window it is at the beginning,
864 // it has to go to the end.
865 // then we fill in the rows between new and old start pos.
868 bCheck
= m_xCacheSet
->absolute(nNewStartPos
+ 1);
870 // m_nEndPos < nNewEndPos when window not filled (e.g. there are fewer rows in total than window size)
871 m_nEndPos
= std::min(nNewEndPos
, m_nEndPos
);
872 const sal_Int32 nOverlapSize
= m_nEndPos
- m_nStartPos
;
873 const sal_Int32 nStartPosOffset
= m_nStartPos
- nNewStartPos
; // by how much m_nStartPos moves
874 m_nStartPos
= nNewStartPos
;
875 OSL_ENSURE( o3tl::make_unsigned(nOverlapSize
) <= m_pMatrix
->size(), "new window end is after end of cache matrix!" );
876 // the first position in m_pMatrix whose data we don't keep;
877 // content will be moved to m_pMatrix.begin()
878 ORowSetMatrix::iterator
aEnd (m_pMatrix
->begin() + nOverlapSize
);
879 // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
880 ORowSetMatrix::iterator
aNewEnd (aEnd
+ nStartPosOffset
);
881 // *m_pMatrix now looks like:
882 // [0; nOverlapSize) i.e. [begin(); aEnd): data kept
883 // [nOverlapSize; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos
884 // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
885 // Note that nOverlapSize + nStartPosOffset == m_nEndPos - m_nStartPos (new values)
886 // When we are finished:
887 // [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos
888 // [nStartPosOffset; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): kept
889 // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
894 ORowSetMatrix::iterator
aIter(aEnd
);
895 sal_Int32 nPos
= m_nStartPos
+ 1;
896 fill(aIter
, aNewEnd
, nPos
, bCheck
);
899 std::rotate(m_pMatrix
->begin(), aEnd
, aNewEnd
);
902 // now correct the iterator in our iterator vector
903 // rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment
904 for(auto& rCacheIter
: m_aCacheIterators
)
906 if ( !rCacheIter
.second
.pRowSet
->isInsertRow()
907 && rCacheIter
.second
.aIterator
!= m_pMatrix
->end() )
909 const ptrdiff_t nDist
= rCacheIter
.second
.aIterator
- m_pMatrix
->begin();
910 if ( nDist
>= nOverlapSize
)
912 // That's from outside the overlap area; invalidate iterator.
913 rCacheIter
.second
.aIterator
= m_pMatrix
->end();
917 // Inside overlap area: move to correct position
918 OSL_ENSURE(((nDist
+ nStartPosOffset
) >= static_cast<ORowSetMatrix::difference_type
>(0)) &&
919 ((nDist
+ nStartPosOffset
) < static_cast<sal_Int32
>(m_pMatrix
->size())),"Position is invalid!");
920 rCacheIter
.second
.aIterator
+= nStartPosOffset
;
921 OSL_ENSURE(rCacheIter
.second
.aIterator
>= m_pMatrix
->begin()
922 && rCacheIter
.second
.aIterator
< m_pMatrix
->end(),"Iterator out of area!");
929 { // normally this should never happen
930 OSL_FAIL("What the hell is happen here!");
935 {// no rows can be reused so fill again
936 reFillMatrix(nNewStartPos
,nNewEndPos
);
940 OSL_ENSURE(nNewStartPos
>= m_nStartPos
, "ORowSetCache::moveWindow internal error: new start pos before current start pos");
941 if ( m_nEndPos
< nNewEndPos
)
942 { // need to fill data *after* m_nEndPos
943 if( nNewStartPos
< m_nEndPos
)
944 { // The two regions are overlapping.
945 const sal_Int32 nRowsInCache
= m_nEndPos
- m_nStartPos
;
946 if ( nRowsInCache
< m_nFetchSize
)
948 // There is some unused space in *m_pMatrix; fill it
949 OSL_ENSURE((nRowsInCache
>= static_cast<ORowSetMatrix::difference_type
>(0)) && (o3tl::make_unsigned(nRowsInCache
) < m_pMatrix
->size()),"Position is invalid!");
950 sal_Int32 nPos
= m_nEndPos
+ 1;
951 bool bCheck
= m_xCacheSet
->absolute(nPos
);
952 ORowSetMatrix::iterator aIter
= m_pMatrix
->begin() + nRowsInCache
;
953 const sal_Int32 nRowsToFetch
= std::min(nNewEndPos
-m_nEndPos
, m_nFetchSize
-nRowsInCache
);
954 const ORowSetMatrix::const_iterator aEnd
= aIter
+ nRowsToFetch
;
955 bCheck
= fill(aIter
, aEnd
, nPos
, bCheck
);
956 m_nEndPos
= nPos
- 1;
957 OSL_ENSURE( (!bCheck
&& m_nEndPos
<= nNewEndPos
) ||
958 ( bCheck
&& m_nEndPos
== nNewEndPos
),
959 "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
962 // A priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive]
963 // have to be refilled with new to-be-fetched rows.
964 // The rows behind this can be reused
965 ORowSetMatrix::iterator aIter
= m_pMatrix
->begin();
966 const sal_Int32 nNewStartPosInMatrix
= nNewStartPos
- m_nStartPos
;
967 OSL_ENSURE((nNewStartPosInMatrix
>= static_cast<ORowSetMatrix::difference_type
>(0)) && (o3tl::make_unsigned(nNewStartPosInMatrix
) < m_pMatrix
->size()),"Position is invalid!");
968 // first position we reuse
969 const ORowSetMatrix::const_iterator aEnd
= m_pMatrix
->begin() + nNewStartPosInMatrix
;
970 // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
971 ORowSetMatrix::iterator aDataEnd
= m_pMatrix
->begin() + (m_nEndPos
- m_nStartPos
);
973 sal_Int32 nPos
= m_nEndPos
+ 1;
974 bool bCheck
= m_xCacheSet
->absolute(nPos
);
975 bCheck
= fill(aIter
, aEnd
, nPos
, bCheck
); // refill the region we don't need anymore
976 //aIter and nPos are now the position *after* last filled in one!
981 OSL_ENSURE(aIter
== aEnd
, "fill() said went till end, but did not.");
982 // rotate the end to the front
983 std::rotate(m_pMatrix
->begin(), aIter
, aDataEnd
);
984 // now correct the iterator in our iterator vector
985 rotateCacheIterator( nNewStartPosInMatrix
);
986 m_nStartPos
= nNewStartPos
;
987 m_nEndPos
= nNewEndPos
;
988 // now I can say how many rows we have
989 // we have to read one row forward to ensure that we know when we are on last row
990 // but only when we don't know it already
992 if(!m_bRowCountFinal
)
993 bOk
= m_xCacheSet
->next();
996 m_xCacheSet
->previous(); // because we stand after the last row
997 m_nRowCount
= nPos
; // here we have the row count
998 OSL_ENSURE(nPos
== m_xCacheSet
->getRow(),"nPos is not valid!");
999 m_bRowCountFinal
= true;
1001 else if(!m_bRowCountFinal
)
1002 m_nRowCount
= std::max(nPos
+1, m_nRowCount
); //+1 because we successfully moved to row after nPos
1004 OSL_ENSURE(m_nRowCount
>= nPos
, "Final m_nRowCount is smaller than row I moved to!");
1007 { // the end was reached before or at end() so we can set the start before or at nNewStartPos
1008 // and possibly keep more of m_pMatrix than planned.
1009 const ORowSetMatrix::const_iterator::difference_type nFetchedRows
= aIter
- m_pMatrix
->begin();
1010 // *m_pMatrix now looks like:
1011 // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows
1012 // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???
1015 m_nStartPos
+= nFetchedRows
;
1017 std::rotate(m_pMatrix
->begin(), aIter
, aDataEnd
);
1018 // now correct the iterator in our iterator vector
1019 rotateCacheIterator( nFetchedRows
);
1021 if ( !m_bRowCountFinal
)
1023 m_xCacheSet
->previous(); // because we stand after the last row
1024 m_nRowCount
= std::max(m_nRowCount
, nPos
); // here we have the row count
1025 OSL_ENSURE(nPos
== m_xCacheSet
->getRow(),"nPos isn't valid!");
1026 m_bRowCountFinal
= true;
1030 // here we need only to check if the beginning row is valid. If not we have to fetch it.
1031 if(!m_pMatrix
->begin()->is())
1033 aIter
= m_pMatrix
->begin();
1035 nPos
= m_nStartPos
+ 1;
1036 bCheck
= m_xCacheSet
->absolute(nPos
);
1037 for(; !aIter
->is() && bCheck
;++aIter
, ++nPos
)
1039 OSL_ENSURE(aIter
!= m_pMatrix
->end(),"Invalid iterator");
1041 *aIter
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
1042 m_xCacheSet
->fillValueRow(*aIter
, nPos
);
1044 bCheck
= m_xCacheSet
->next();
1048 else // no rows can be reused so fill again
1049 reFillMatrix(nNewStartPos
,nNewEndPos
);
1052 if(!m_bRowCountFinal
)
1053 m_nRowCount
= std::max(m_nPosition
,m_nRowCount
);
1054 OSL_ENSURE(m_nStartPos
>= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
1055 OSL_ENSURE(m_nEndPos
> m_nStartPos
,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
1056 OSL_ENSURE(m_nEndPos
-m_nStartPos
<= m_nFetchSize
,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
1059 bool ORowSetCache::first( )
1061 // First move to the first row.
1062 // Then check if the cache window is at the beginning.
1063 // If not, then position the window and fill it with data.
1064 // We move the window smartly, i.e. we clear only the rows that are out of range
1065 bool bRet
= m_xCacheSet
->first();
1068 m_bBeforeFirst
= m_bAfterLast
= false;
1071 m_aMatrixIter
= m_pMatrix
->begin();
1075 m_bRowCountFinal
= m_bBeforeFirst
= m_bAfterLast
= true;
1076 m_nRowCount
= m_nPosition
= 0;
1078 OSL_ENSURE(m_bBeforeFirst
|| m_bNew
,"ORowSetCache::first return false and BeforeFirst isn't true");
1079 m_aMatrixIter
= m_pMatrix
->end();
1084 bool ORowSetCache::last( )
1086 bool bRet
= m_xCacheSet
->last();
1089 m_bBeforeFirst
= m_bAfterLast
= false;
1090 if(!m_bRowCountFinal
)
1092 m_bRowCountFinal
= true;
1093 m_nRowCount
= m_xCacheSet
->getRow(); // not + 1
1095 m_nPosition
= m_xCacheSet
->getRow();
1097 // we have to repositioning because moveWindow can modify the cache
1098 m_xCacheSet
->last();
1099 OSL_ENSURE(((m_nPosition
- m_nStartPos
) - 1) < static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
1100 m_aMatrixIter
= calcPosition();
1104 m_bRowCountFinal
= m_bBeforeFirst
= m_bAfterLast
= true;
1105 m_nRowCount
= m_nPosition
= 0;
1106 OSL_ENSURE(m_bBeforeFirst
,"ORowSetCache::last return false and BeforeFirst isn't true");
1107 m_aMatrixIter
= m_pMatrix
->end();
1109 #if OSL_DEBUG_LEVEL > 0
1112 assert((*m_aMatrixIter
).is() && "ORowSetCache::last: Row not valid!");
1119 sal_Int32
ORowSetCache::getRow( ) const
1121 return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition
;
1124 bool ORowSetCache::absolute( sal_Int32 row
)
1127 throw SQLException(DBA_RES(RID_STR_NO_ABS_ZERO
),nullptr,SQLSTATE_GENERAL
,1000,Any() );
1131 // here we have to scroll from the last row to backward so we have to go to last row and
1132 // and to the previous
1133 if(m_bRowCountFinal
|| last())
1135 m_nPosition
= m_nRowCount
+ row
+ 1; // + row because row is negative and +1 because row==-1 means last row
1138 m_bBeforeFirst
= true;
1139 m_bAfterLast
= false;
1140 m_aMatrixIter
= m_pMatrix
->end();
1144 m_bBeforeFirst
= false;
1145 m_bAfterLast
= m_nPosition
> m_nRowCount
;
1147 OSL_ENSURE(((m_nPosition
- m_nStartPos
) - 1) < static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
1148 m_aMatrixIter
= calcPosition();
1152 m_aMatrixIter
= m_pMatrix
->end();
1157 // the position flags
1158 m_bBeforeFirst
= false;
1159 checkPositionFlags();
1164 checkPositionFlags();
1166 m_aMatrixIter
= calcPosition();
1168 m_aMatrixIter
= m_pMatrix
->end();
1171 m_aMatrixIter
= m_pMatrix
->end();
1174 return !(m_bAfterLast
|| m_bBeforeFirst
);
1177 bool ORowSetCache::relative( sal_Int32 rows
)
1182 sal_Int32 nNewPosition
= m_nPosition
+ rows
;
1184 if ( m_bBeforeFirst
&& rows
> 0 )
1185 nNewPosition
= rows
;
1186 else if ( m_bRowCountFinal
&& m_bAfterLast
&& rows
< 0 )
1187 nNewPosition
= m_nRowCount
+ 1 + rows
;
1189 if ( m_bBeforeFirst
|| ( m_bRowCountFinal
&& m_bAfterLast
) )
1190 throw SQLException( DBA_RES( RID_STR_NO_RELATIVE
), nullptr, SQLSTATE_GENERAL
, 1000, Any() );
1193 bErg
= absolute( nNewPosition
);
1194 bErg
= bErg
&& !isAfterLast() && !isBeforeFirst();
1198 m_bBeforeFirst
= true;
1205 bool ORowSetCache::previous( )
1208 if(!isBeforeFirst())
1210 if(m_bAfterLast
) // we stand after the last row so one before is the last row
1214 m_bAfterLast
= false;
1217 OSL_ENSURE(((m_nPosition
- m_nStartPos
) - 1) < static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
1219 checkPositionFlags();
1223 m_bBeforeFirst
= true;
1224 m_aMatrixIter
= m_pMatrix
->end();
1228 m_aMatrixIter
= calcPosition();
1229 bRet
= (*m_aMatrixIter
).is();
1236 void ORowSetCache::refreshRow( )
1239 throw SQLException(DBA_RES(RID_STR_NO_REFRESH_AFTERLAST
),nullptr,SQLSTATE_GENERAL
,1000,Any() );
1240 OSL_ENSURE(m_aMatrixIter
!= m_pMatrix
->end(),"refreshRow() called for invalid row!");
1241 m_xCacheSet
->refreshRow();
1242 m_xCacheSet
->fillValueRow(*m_aMatrixIter
,m_nPosition
);
1245 cancelRowModification();
1249 bool ORowSetCache::rowUpdated( )
1251 return m_xCacheSet
->rowUpdated();
1254 bool ORowSetCache::rowInserted( )
1256 return m_xCacheSet
->rowInserted();
1260 bool ORowSetCache::insertRow(std::vector
< Any
>& o_aBookmarks
)
1262 if ( !m_bNew
|| !m_aInsertRow
->is() )
1263 throw SQLException(DBA_RES(RID_STR_NO_MOVETOINSERTROW_CALLED
),nullptr,SQLSTATE_GENERAL
,1000,Any() );
1265 m_xCacheSet
->insertRow(*m_aInsertRow
,m_aUpdateTable
);
1267 bool bRet( rowInserted() );
1271 Any aBookmark
= (**m_aInsertRow
)[0].makeAny();
1272 m_bAfterLast
= m_bBeforeFirst
= false;
1273 if(aBookmark
.hasValue())
1275 moveToBookmark(aBookmark
);
1276 // update the cached values
1277 ORowSetValueVector::Vector
& rCurrentRow
= **m_aMatrixIter
;
1278 ORowSetMatrix::const_iterator aIter
= m_pMatrix
->begin();
1279 for(;aIter
!= m_pMatrix
->end();++aIter
)
1281 if ( m_aMatrixIter
!= aIter
&& aIter
->is() && m_xCacheSet
->columnValuesUpdated(**aIter
,rCurrentRow
) )
1283 o_aBookmarks
.push_back(lcl_getBookmark((**aIter
)[0], m_xCacheSet
.get()));
1289 OSL_FAIL("There must be a bookmark after the row was inserted!");
1295 void ORowSetCache::resetInsertRow(bool _bClearInsertRow
)
1297 if ( _bClearInsertRow
)
1300 m_bModified
= false;
1303 void ORowSetCache::cancelRowModification()
1305 // clear the insertrow references -> implies that the current row of the rowset changes as well
1306 for(auto& rCacheIter
: m_aCacheIterators
)
1308 if ( rCacheIter
.second
.pRowSet
->isInsertRow() && rCacheIter
.second
.aIterator
== m_aInsertRow
)
1309 rCacheIter
.second
.aIterator
= m_pMatrix
->end();
1311 resetInsertRow(false);
1314 void ORowSetCache::updateRow( ORowSetMatrix::iterator
const & _rUpdateRow
, std::vector
< Any
>& o_aBookmarks
)
1316 if(isAfterLast() || isBeforeFirst())
1317 throw SQLException(DBA_RES(RID_STR_NO_UPDATEROW
),nullptr,SQLSTATE_GENERAL
,1000,Any() );
1319 Any aBookmark
= (**_rUpdateRow
)[0].makeAny();
1320 OSL_ENSURE(aBookmark
.hasValue(),"Bookmark must have a value!");
1321 // here we don't have to reposition our CacheSet, when we try to update a row,
1322 // the row was already fetched
1323 moveToBookmark(aBookmark
);
1324 m_xCacheSet
->updateRow(*_rUpdateRow
,*m_aMatrixIter
,m_aUpdateTable
);
1325 // refetch the whole row
1326 (*m_aMatrixIter
) = nullptr;
1328 if ( moveToBookmark(aBookmark
) )
1330 // update the cached values
1331 ORowSetValueVector::Vector
& rCurrentRow
= **m_aMatrixIter
;
1332 ORowSetMatrix::const_iterator aIter
= m_pMatrix
->begin();
1333 for(;aIter
!= m_pMatrix
->end();++aIter
)
1335 if ( m_aMatrixIter
!= aIter
&& aIter
->is() && m_xCacheSet
->columnValuesUpdated(**aIter
,rCurrentRow
) )
1337 o_aBookmarks
.push_back(lcl_getBookmark((**aIter
)[0], m_xCacheSet
.get()));
1342 m_bModified
= false;
1345 bool ORowSetCache::deleteRow( )
1347 if(isAfterLast() || isBeforeFirst())
1348 throw SQLException(DBA_RES(RID_STR_NO_DELETEROW
),nullptr,SQLSTATE_GENERAL
,1000,Any() );
1350 m_xCacheSet
->deleteRow(*m_aMatrixIter
,m_aUpdateTable
);
1351 if ( !m_xCacheSet
->rowDeleted() )
1355 OSL_ENSURE(((m_nPosition
- m_nStartPos
) - 1) < static_cast<sal_Int32
>(m_pMatrix
->size()),"Position is behind end()!");
1356 ORowSetMatrix::iterator aPos
= calcPosition();
1359 ORowSetMatrix::const_iterator aEnd
= m_pMatrix
->end();
1360 for(++aPos
;aPos
!= aEnd
&& aPos
->is();++aPos
)
1365 m_aMatrixIter
= m_pMatrix
->end();
1371 void ORowSetCache::cancelRowUpdates( )
1373 m_bNew
= m_bModified
= false;
1376 OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0");
1377 ::dbtools::throwFunctionSequenceException(nullptr);
1380 if(m_xCacheSet
->absolute(m_nPosition
))
1381 m_xCacheSet
->fillValueRow(*m_aMatrixIter
,m_nPosition
);
1384 OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
1385 ::dbtools::throwFunctionSequenceException(nullptr);
1389 void ORowSetCache::moveToInsertRow( )
1392 m_bAfterLast
= false;
1394 m_aInsertRow
= m_pInsertMatrix
->begin();
1395 if(!m_aInsertRow
->is())
1396 *m_aInsertRow
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
1398 // we don't unbound the bookmark column
1399 ORowSetValueVector::Vector::iterator aIter
= (*m_aInsertRow
)->begin()+1;
1400 ORowSetValueVector::Vector::const_iterator aEnd
= (*m_aInsertRow
)->end();
1401 for(sal_Int32 i
= 1;aIter
!= aEnd
;++aIter
,++i
)
1403 aIter
->setBound(false);
1404 aIter
->setModified(false);
1406 aIter
->setTypeKind(m_xMetaData
->getColumnType(i
));
1410 ORowSetCacheIterator
ORowSetCache::createIterator(ORowSetBase
* _pRowSet
)
1412 ORowSetCacheIterator_Helper aHelper
;
1413 aHelper
.aIterator
= m_pMatrix
->end();
1414 aHelper
.pRowSet
= _pRowSet
;
1415 return ORowSetCacheIterator(m_aCacheIterators
.insert(m_aCacheIterators
.begin(),ORowSetCacheMap::value_type(m_aCacheIterators
.size()+1,aHelper
)),this,_pRowSet
);
1418 void ORowSetCache::deleteIterator(const ORowSetBase
* _pRowSet
)
1420 ORowSetCacheMap::const_iterator aCacheIter
= m_aCacheIterators
.begin();
1421 for(;aCacheIter
!= m_aCacheIterators
.end();)
1423 if ( aCacheIter
->second
.pRowSet
== _pRowSet
)
1425 aCacheIter
= m_aCacheIterators
.erase(aCacheIter
);
1432 void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist
)
1440 // now correct the iterator in our iterator vector
1441 for(auto& rCacheIter
: m_aCacheIterators
)
1443 if ( !rCacheIter
.second
.pRowSet
->isInsertRow()
1444 && rCacheIter
.second
.aIterator
!= m_pMatrix
->end())
1446 ptrdiff_t nDist
= rCacheIter
.second
.aIterator
- m_pMatrix
->begin();
1449 rCacheIter
.second
.aIterator
= m_pMatrix
->end();
1453 OSL_ENSURE((rCacheIter
.second
.aIterator
- m_pMatrix
->begin()) >= _nDist
,"Invalid Dist value!");
1454 rCacheIter
.second
.aIterator
-= _nDist
;
1455 OSL_ENSURE(rCacheIter
.second
.aIterator
>= m_pMatrix
->begin()
1456 && rCacheIter
.second
.aIterator
< m_pMatrix
->end(),"Iterator out of area!");
1462 void ORowSetCache::rotateAllCacheIterators()
1467 // now correct the iterator in our iterator vector
1468 for (auto& rCacheIter
: m_aCacheIterators
)
1470 if (!rCacheIter
.second
.pRowSet
->isInsertRow())
1472 rCacheIter
.second
.aIterator
= m_pMatrix
->end();
1477 void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator
& _rOriginalRow
)
1479 m_aInsertRow
= m_pInsertMatrix
->begin();
1480 if(!m_aInsertRow
->is())
1481 *m_aInsertRow
= new ORowSetValueVector(m_xMetaData
->getColumnCount());
1483 (*(*m_aInsertRow
)) = *(*_rOriginalRow
);
1484 // we don't unbound the bookmark column
1485 for(auto& rItem
: **m_aInsertRow
)
1486 rItem
.setModified(false);
1489 void ORowSetCache::checkPositionFlags()
1491 if(m_bRowCountFinal
)
1493 m_bAfterLast
= m_nPosition
> m_nRowCount
;
1495 m_nPosition
= 0;//m_nRowCount;
1499 void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex
)
1501 if(m_bAfterLast
|| columnIndex
>= static_cast<sal_Int32
>((*m_aInsertRow
)->size()))
1502 throwFunctionSequenceException(m_xSet
.get());
1505 bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode
*pNode
,const Reference
< XConnection
>& _xConnection
,const OUString
& _sUpdateTableName
)
1508 if (pNode
->count() == 3 && // expression in parentheses
1509 SQL_ISPUNCTUATION(pNode
->getChild(0),"(") &&
1510 SQL_ISPUNCTUATION(pNode
->getChild(2),")"))
1512 bOk
= checkInnerJoin(pNode
->getChild(1),_xConnection
,_sUpdateTableName
);
1514 else if ((SQL_ISRULE(pNode
,search_condition
) || SQL_ISRULE(pNode
,boolean_term
)) && // AND/OR link
1515 pNode
->count() == 3)
1517 // only allow an AND link
1518 if ( SQL_ISTOKEN(pNode
->getChild(1),AND
) )
1519 bOk
= checkInnerJoin(pNode
->getChild(0),_xConnection
,_sUpdateTableName
)
1520 && checkInnerJoin(pNode
->getChild(2),_xConnection
,_sUpdateTableName
);
1522 else if (SQL_ISRULE(pNode
,comparison_predicate
))
1524 // only the comparison of columns is allowed
1525 OSL_ENSURE(pNode
->count() == 3,"checkInnerJoin: Error in Parse Tree");
1526 if (!(SQL_ISRULE(pNode
->getChild(0),column_ref
) &&
1527 SQL_ISRULE(pNode
->getChild(2),column_ref
) &&
1528 pNode
->getChild(1)->getNodeType() == SQLNodeType::Equal
))
1534 OUString sColumnName
,sTableRange
;
1535 OSQLParseTreeIterator::getColumnRange( pNode
->getChild(0), _xConnection
, sColumnName
, sTableRange
);
1536 bOk
= sTableRange
== _sUpdateTableName
;
1539 OSQLParseTreeIterator::getColumnRange( pNode
->getChild(2), _xConnection
, sColumnName
, sTableRange
);
1540 bOk
= sTableRange
== _sUpdateTableName
;
1547 bool ORowSetCache::checkJoin(const Reference
< XConnection
>& _xConnection
,
1548 const Reference
< XSingleSelectQueryAnalyzer
>& _xAnalyzer
,
1549 const OUString
& _sUpdateTableName
)
1552 OUString sSql
= _xAnalyzer
->getQuery();
1554 ::connectivity::OSQLParser
aSqlParser( m_aContext
);
1555 std::unique_ptr
< ::connectivity::OSQLParseNode
> pSqlParseNode( aSqlParser
.parseTree(sErrorMsg
,sSql
));
1556 if ( pSqlParseNode
&& SQL_ISRULE(pSqlParseNode
, select_statement
) )
1558 OSQLParseNode
* pTableRefCommalist
= pSqlParseNode
->getByRule(::connectivity::OSQLParseNode::table_ref_commalist
);
1559 OSL_ENSURE(pTableRefCommalist
,"NO tables why!?");
1560 if(pTableRefCommalist
&& pTableRefCommalist
->count() == 1)
1562 // we found only one element so it must some kind of join here
1563 OSQLParseNode
* pJoin
= pTableRefCommalist
->getByRule(::connectivity::OSQLParseNode::qualified_join
);
1565 { // we are only interested in qualified joins like RIGHT or LEFT
1566 OSQLParseNode
* pJoinType
= pJoin
->getChild(1);
1567 OSQLParseNode
* pOuterType
= nullptr;
1568 if(SQL_ISRULE(pJoinType
,join_type
) && pJoinType
->count() == 2)
1569 pOuterType
= pJoinType
->getChild(0);
1570 else if(SQL_ISRULE(pJoinType
,outer_join_type
))
1571 pOuterType
= pJoinType
;
1573 bool bCheck
= false;
1574 bool bLeftSide
= false;
1576 { // found outer join
1577 bLeftSide
= SQL_ISTOKEN(pOuterType
->getChild(0),LEFT
);
1578 bCheck
= bLeftSide
|| SQL_ISTOKEN(pOuterType
->getChild(0),RIGHT
);
1582 { // here we know that we have to check on which side our table resides
1583 const OSQLParseNode
* pTableRef
;
1585 pTableRef
= pJoin
->getChild(0);
1587 pTableRef
= pJoin
->getChild(3);
1588 OSL_ENSURE(SQL_ISRULE(pTableRef
,table_ref
),"Must be a tableref here!");
1590 OUString sTableRange
= OSQLParseNode::getTableRange(pTableRef
);
1591 if(sTableRange
.isEmpty())
1592 pTableRef
->getChild(0)->parseNodeToStr( sTableRange
, _xConnection
, nullptr, false, false );
1593 bOk
= sTableRange
== _sUpdateTableName
;
1599 OSQLParseNode
* pWhereOpt
= pSqlParseNode
->getChild(3)->getChild(1);
1600 if ( pWhereOpt
&& !pWhereOpt
->isLeaf() )
1601 bOk
= checkInnerJoin(pWhereOpt
->getChild(1),_xConnection
,_sUpdateTableName
);
1607 void ORowSetCache::clearInsertRow()
1609 // we don't unbound the bookmark column
1610 if ( m_aInsertRow
!= m_pInsertMatrix
->end() && m_aInsertRow
->is() )
1612 ORowSetValueVector::Vector::iterator aIter
= (*m_aInsertRow
)->begin()+1;
1613 ORowSetValueVector::Vector::const_iterator aEnd
= (*m_aInsertRow
)->end();
1614 for(;aIter
!= aEnd
;++aIter
)
1616 aIter
->setBound(false);
1617 aIter
->setModified(false);
1623 ORowSetMatrix::iterator
ORowSetCache::calcPosition() const
1625 sal_Int32 nValue
= (m_nPosition
- m_nStartPos
) - 1;
1626 OSL_ENSURE((nValue
>= static_cast<ORowSetMatrix::difference_type
>(0)) && (o3tl::make_unsigned(nValue
) < m_pMatrix
->size()),"Position is invalid!");
1627 return ( nValue
< 0 || o3tl::make_unsigned(nValue
) >= m_pMatrix
->size() ) ? m_pMatrix
->end() : (m_pMatrix
->begin() + nValue
);
1630 TORowSetOldRowHelperRef
ORowSetCache::registerOldRow()
1632 TORowSetOldRowHelperRef pRef
= new ORowSetOldRowHelper(ORowSetRow());
1633 m_aOldRows
.push_back(pRef
);
1637 void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef
& _rRow
)
1639 TOldRowSetRows::iterator aOldRowIter
= std::find_if(m_aOldRows
.begin(), m_aOldRows
.end(),
1640 [&_rRow
](const TORowSetOldRowHelperRef
& rxOldRow
) { return rxOldRow
.get() == _rRow
.get(); });
1641 if (aOldRowIter
!= m_aOldRows
.end())
1642 m_aOldRows
.erase(aOldRowIter
);
1645 bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos
, sal_Int32 _nNewEndPos
)
1647 for (const auto& rxOldRow
: m_aOldRows
)
1649 if ( rxOldRow
.is() && rxOldRow
->getRow().is() )
1650 rxOldRow
->setRow(new ORowSetValueVector( *(rxOldRow
->getRow()) ) );
1652 sal_Int32 nNewSt
= _nNewStartPos
;
1653 bool bRet
= fillMatrix(nNewSt
,_nNewEndPos
);
1654 m_nStartPos
= nNewSt
;
1655 m_nEndPos
= _nNewEndPos
;
1656 rotateAllCacheIterators(); // invalidate every iterator
1660 bool ORowSetCache::fill(ORowSetMatrix::iterator
& _aIter
, const ORowSetMatrix::const_iterator
& _aEnd
, sal_Int32
& _nPos
, bool _bCheck
)
1662 const sal_Int32 nColumnCount
= m_xMetaData
->getColumnCount();
1663 for (; _bCheck
&& _aIter
!= _aEnd
; ++_aIter
, ++_nPos
)
1665 if ( !_aIter
->is() )
1666 *_aIter
= new ORowSetValueVector(nColumnCount
);
1669 for (const auto& rxOldRow
: m_aOldRows
)
1671 if ( rxOldRow
->getRow() == *_aIter
)
1672 *_aIter
= new ORowSetValueVector(nColumnCount
);
1675 m_xCacheSet
->fillValueRow(*_aIter
, _nPos
);
1676 _bCheck
= m_xCacheSet
->next();
1681 bool ORowSetCache::isResultSetChanged() const
1683 return m_xCacheSet
->isResultSetChanged();
1686 void ORowSetCache::reset(const Reference
< XResultSet
>& _xDriverSet
)
1688 m_xSet
= _xDriverSet
;
1689 m_xMetaData
.set(Reference
< XResultSetMetaDataSupplier
>(_xDriverSet
,UNO_QUERY_THROW
)->getMetaData());
1690 m_xCacheSet
->reset(_xDriverSet
);
1692 m_bRowCountFinal
= false;
1694 reFillMatrix(m_nStartPos
,m_nEndPos
);
1697 void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector
& io_aRow
1698 ,std::vector
<sal_Int32
> const & o_ChangedColumns
)
1700 if ( o_ChangedColumns
.size() > 1 )
1702 for (auto const& elem
: *m_pMatrix
)
1704 if ( elem
.is() && m_xCacheSet
->updateColumnValues(*elem
,io_aRow
,o_ChangedColumns
))
1709 m_xCacheSet
->fillMissingValues(io_aRow
);
1713 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */