/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// This class calls m_pCacheSet->FOO_checked(..., sal_False) // (where FOO is absolute, last, previous) // when it does not immediately care about the values in the row's columns. // As a corollary, m_pCacheSet may be left in an inconsistent state, // and all ->fillFOO calls (and ->getFOO) may fail or give wrong results, // until m_pCacheSet is moved (or refreshed) again. // So always make sure m_pCacheSet is moved or refreshed before accessing column values.
OUString aUpdateTableName = _rUpdateTableName;
Reference< XConnection> xConnection; // first we need a connection
Reference< XStatement> xStmt(_xRs->getStatement(),UNO_QUERY); if(xStmt.is())
xConnection = xStmt->getConnection(); else
{
Reference< XPreparedStatement> xPrepStmt(_xRs->getStatement(),UNO_QUERY);
xConnection = xPrepStmt->getConnection();
}
OSL_ENSURE(xConnection.is(),"No connection!"); if(_xAnalyzer.is())
{ try
{
Reference<XTablesSupplier> xTabSup(_xAnalyzer,UNO_QUERY);
OSL_ENSURE(xTabSup.is(),"ORowSet::execute composer isn't a tablesupplier!");
Reference<XNameAccess> xTables = xTabSup->getTables();
Sequence< OUString> aTableNames = xTables->getElementNames(); if ( aTableNames.getLength() > 1 && _rUpdateTableName.isEmpty() && bNeedKeySet )
{// here we have a join or union and nobody told us which table to update, so we update them all
m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE;
rtl::Reference<OptimisticSet> pCursor = new OptimisticSet(m_aContext,xConnection,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount);
m_xCacheSet = pCursor; try
{
m_xCacheSet->construct(_xRs,i_sRowSetFilter); if ( pCursor->isReadOnly() )
m_nPrivileges = Privilege::SELECT;
m_aKeyColumns = pCursor->getJoinedKeyColumns(); return;
} catch (const Exception&)
{
TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
}
m_xCacheSet.clear();
} else
{ if(!_rUpdateTableName.isEmpty() && xTables->hasByName(_rUpdateTableName))
xTables->getByName(_rUpdateTableName) >>= m_aUpdateTable; elseif(xTables->getElementNames().hasElements())
{
aUpdateTableName = xTables->getElementNames()[0];
xTables->getByName(aUpdateTableName) >>= m_aUpdateTable;
}
Reference<XIndexAccess> xIndexAccess(xTables,UNO_QUERY); if(xIndexAccess.is())
nTablesCount = xIndexAccess->getCount(); else
nTablesCount = xTables->getElementNames().getLength();
if(m_aUpdateTable.is() && nTablesCount < 3) // for we can't handle more than 2 tables in our keyset
{
Reference<XPropertySet> xSet(m_aUpdateTable,UNO_QUERY); const Reference<XNameAccess> xPrimaryKeyColumns = dbtools::getPrimaryKeyColumns_throw(xSet); if ( xPrimaryKeyColumns.is() )
{
Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY); if ( xColSup.is() )
{
Reference<XNameAccess> xSelColumns = xColSup->getColumns();
Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
SelectColumnsMetaData aColumnNames(comphelper::UStringMixLess(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()));
::dbaccess::getColumnPositions(xSelColumns,xPrimaryKeyColumns->getElementNames(),aUpdateTableName,aColumnNames);
bAllKeysFound = !aColumnNames.empty() && aColumnNames.size() == o3tl::make_unsigned(xPrimaryKeyColumns->getElementNames().getLength());
}
}
}
}
} catch (Exception const&)
{
TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
}
}
// first check if resultset is bookmarkable if(!bNeedKeySet)
{ try
{
m_xCacheSet = new OBookmarkSet(i_nMaxRows);
m_xCacheSet->construct(_xRs,i_sRowSetFilter);
// check privileges
m_nPrivileges = Privilege::SELECT; if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
{
Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY); if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
{
m_nPrivileges = 0;
xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges; if(!m_nPrivileges)
m_nPrivileges = Privilege::SELECT;
}
}
} catch (const SQLException&)
{
TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache");
bNeedKeySet = true;
}
} if(bNeedKeySet)
{ // need to check if we could handle this select clause
bAllKeysFound = bAllKeysFound && (nTablesCount == 1 || checkJoin(xConnection,_xAnalyzer,aUpdateTableName));
if(!bAllKeysFound )
{ if ( bBookmarkable )
{ // here I know that we have a read only bookmarkable cursor
_xRs->beforeFirst();
m_nPrivileges = Privilege::SELECT;
m_xCacheSet = new WrappedResultSet(i_nMaxRows);
m_xCacheSet->construct(_xRs,i_sRowSetFilter); return;
}
m_xCacheSet = new OStaticSet(i_nMaxRows);
m_xCacheSet->construct(_xRs,i_sRowSetFilter);
m_nPrivileges = Privilege::SELECT;
} else
{
Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
SelectColumnsMetaData aColumnNames(comphelper::UStringMixLess(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()));
Reference<XColumnsSupplier> xColSup(_xAnalyzer,UNO_QUERY);
Reference<XNameAccess> xSelColumns = xColSup->getColumns();
Reference<XNameAccess> xColumns = m_aUpdateTable->getColumns();
::dbaccess::getColumnPositions(xSelColumns,xColumns->getElementNames(),aUpdateTableName,aColumnNames);
for (auto& columnName : xColumns->getElementNames())
{
Reference<XPropertySet> xColumn(xColumns->getByName(columnName), UNO_QUERY);
OSL_ENSURE(xColumn.is(),"Column in table is null!"); if(xColumn.is())
{
sal_Int32 nNullable = 0;
xColumn->getPropertyValue(PROPERTY_ISNULLABLE) >>= nNullable; if(nNullable == ColumnValue::NO_NULLS && aColumnNames.find(columnName) == aColumnNames.end())
{ // we found a column where null is not allowed so we can't insert new values
bNoInsert = true; break; // one column is enough
}
}
}
if(Reference<XResultSetUpdate>(_xRs,UNO_QUERY).is()) // this interface is optional so we have to check it
{
Reference<XPropertySet> xTable(m_aUpdateTable,UNO_QUERY); if(xTable.is() && xTable->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES))
{
m_nPrivileges = 0;
xTable->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges; if(!m_nPrivileges)
m_nPrivileges = Privilege::SELECT;
}
} if(bNoInsert)
m_nPrivileges |= ~Privilege::INSERT; // remove the insert privilege
} catch (const SQLException&)
{
TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache"); // we couldn't create a keyset here so we have to create a static cache
m_xCacheSet = new OStaticSet(i_nMaxRows);
m_xCacheSet->construct(_xRs,i_sRowSetFilter);
m_nPrivileges = Privilege::SELECT;
}
}
m_pInsertMatrix.reset( new ORowSetMatrix(1) ); // a little bit overkill but ??? :-)
m_aInsertRow = m_pInsertMatrix->end();
} else
{ // now correct the iterator in our iterator vector
std::vector<sal_Int32> aPositions;
std::map<sal_Int32,bool> aCacheIterToChange; // first get the positions where they stand now for(constauto& [rIndex, rHelper] : m_aCacheIterators)
{
aCacheIterToChange[rIndex] = false; if ( !rHelper.pRowSet->isInsertRow() /*&& rHelper.aIterator != m_pMatrix->end()*/ && !m_bModified )
{
ptrdiff_t nDist = rHelper.aIterator - m_pMatrix->begin();
aPositions.push_back(nDist);
aCacheIterToChange[rIndex] = true;
}
}
sal_Int32 nKeyPos = m_aMatrixIter - m_pMatrix->begin();
m_pMatrix->resize(_nSize);
// now adjust their positions because a resize invalidates all iterators
std::vector<sal_Int32>::const_iterator aIter = aPositions.begin();
ORowSetCacheMap::iterator aCacheIter = m_aCacheIterators.begin(); for(constauto& rPosChange : aCacheIterToChange)
{ if ( rPosChange.second )
{
OSL_ENSURE((*aIter >= static_cast<ORowSetMatrix::difference_type>(0)) && (*aIter < static_cast<sal_Int32>(m_pMatrix->size())),"Position is invalid!"); if ( *aIter < _nSize )
aCacheIter->second.aIterator = m_pMatrix->begin() + *aIter++; else
aCacheIter->second.aIterator = m_pMatrix->end();
}
++aCacheIter;
}
} if(!m_nPosition)
{
sal_Int32 nNewSt = 0;
fillMatrix(nNewSt,_nSize);
OSL_ENSURE(nNewSt == 0, "fillMatrix set new start to unexpected value");
m_nStartPos = 0;
m_nEndPos = _nSize;
} elseif (m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos)
{
sal_Int32 nNewSt = -1;
_nSize += m_nStartPos;
fillMatrix(nNewSt, _nSize); if (nNewSt >= 0)
{
m_nStartPos = nNewSt;
m_nEndPos = _nSize;
m_aMatrixIter = calcPosition();
} else
{
m_nEndPos = m_nStartPos + m_nFetchSize;
}
} else
{
OSL_FAIL("m_nPosition not between m_nStartPos and m_nEndpos"); // try to repair
moveWindow();
m_aMatrixIter = calcPosition();
}
}
// XResultSetMetaDataSupplier
static Any lcl_getBookmark(ORowSetValue& i_aValue,OCacheSet* i_pCacheSet)
{ switch ( i_aValue.getTypeKind() )
{ case DataType::TINYINT: case DataType::SMALLINT: case DataType::INTEGER: return Any(i_aValue.getInt32()); default: if ( i_pCacheSet && i_aValue.isNull())
i_aValue = i_pCacheSet->getBookmark(); return i_aValue.getAny();
}
}
// css::sdbcx::XRowLocate
Any ORowSetCache::getBookmark( )
{ if(m_bAfterLast)
throwFunctionSequenceException(m_xSet.get());
if ( m_aMatrixIter >= m_pMatrix->end() || m_aMatrixIter < m_pMatrix->begin() || !(*m_aMatrixIter).is())
{ return Any(); // this is allowed here because the rowset knows what it is doing
}
bool ORowSetCache::fillMatrix(sal_Int32& _nNewStartPos, sal_Int32 &_nNewEndPos)
{
OSL_ENSURE((_nNewStartPos != _nNewEndPos) || (_nNewStartPos == 0 && _nNewEndPos == 0 && m_nRowCount == 0), "ORowSetCache::fillMatrix: StartPos and EndPos can not be equal (unless the recordset is empty)!"); // If _nNewStartPos >= 0, then fill the whole window with new data // Else if _nNewStartPos == -1, then fill only segment [m_nEndPos, _nNewEndPos) // Else, undefined (invalid argument)
OSL_ENSURE( _nNewStartPos >= -1, "ORowSetCache::fillMatrix: invalid _nNewStartPos" );
for(; i <= _nNewEndPos; ++i,++aIter)
{ if(bCheck)
{ if(!aIter->is())
*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
m_xCacheSet->fillValueRow(*aIter,i);
} else
{ // there are no more rows found so we can fetch some before start
if(!m_bRowCountFinal)
{ if(m_xCacheSet->previous()) // because we stand after the last row
m_nRowCount = m_xCacheSet->getRow(); // here we have the row count if(!m_nRowCount)
m_nRowCount = i-1; // it can be that getRow return zero
m_bRowCountFinal = true;
} const ORowSetMatrix::iterator aEnd = aIter;
ORowSetMatrix::const_iterator aRealEnd = m_pMatrix->end();
sal_Int32 nPos; if (m_nRowCount >= m_nFetchSize)
{
nPos = m_nRowCount - m_nFetchSize;
} else
{
nPos = 0;
}
_nNewStartPos = nPos;
_nNewEndPos = m_nRowCount;
++nPos;
bCheck = m_xCacheSet->absolute(nPos);
for(;bCheck && nPos <= requestedStartPos && aIter != aRealEnd; ++aIter, ++nPos)
{ if(!aIter->is())
*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
m_xCacheSet->fillValueRow(*aIter, nPos);
bCheck = m_xCacheSet->next();
} if(aIter != aEnd)
std::rotate(m_pMatrix->begin(),aEnd,aIter); break;
}
bCheck = m_xCacheSet->next();
} // we have to read one row forward to ensure that we know when we are on last row // but only when we don't know it already if(!m_bRowCountFinal)
{ if(!m_xCacheSet->next())
{ if(m_xCacheSet->previous()) // because we stand after the last row
m_nRowCount = m_xCacheSet->getRow(); // here we have the row count
m_bRowCountFinal = true;
} else
m_nRowCount = std::max(i,m_nRowCount);
} return bCheck;
}
// If m_nPosition is out of the current window, // move it and update m_nStartPos and m_nEndPos // Caller is responsible for updating m_aMatrixIter void ORowSetCache::moveWindow()
{
OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
OSL_ENSURE(m_nEndPos >= m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
if ( m_nStartPos < m_nPosition && m_nPosition <= m_nEndPos )
{ // just move inside the window
OSL_ENSURE((m_nPosition - m_nStartPos) <= static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!"); // make double plus sure that we have fetched that row
m_aMatrixIter = calcPosition();
OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(), "New m_aMatrixIter is at end(), but should not."); if(!m_aMatrixIter->is())
{ bool bOk( m_xCacheSet->absolute( m_nPosition ) ); if ( bOk )
{
*m_aMatrixIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition); // we have to read one row forward to ensure that we know when we are on last row // but only when we don't know it already if ( !m_bRowCountFinal )
{
bOk = m_xCacheSet->absolute( m_nPosition + 1 ); if ( bOk )
m_nRowCount = std::max(sal_Int32(m_nPosition+1),m_nRowCount);
}
} if(!bOk && !m_bRowCountFinal)
{ // because we stand after the last row
m_nRowCount = m_xCacheSet->previous() ? m_xCacheSet->getRow() : 0;
m_bRowCountFinal = true;
}
} return;
}
sal_Int32 nDiff = (m_nFetchSize - 1) / 2;
sal_Int32 nNewStartPos = (m_nPosition - nDiff) - 1; //m_nPosition is 1-based, but m_nStartPos is 0-based
sal_Int32 nNewEndPos = nNewStartPos + m_nFetchSize;
if ( nNewStartPos < 0 )
{ // The computed new window crashes through the floor (begins before first row); // nNew*Pos has to be shifted by -nNewStartPos
nNewEndPos -= nNewStartPos;
nNewStartPos = 0;
}
if ( nNewStartPos < m_nStartPos )
{ // need to fill data *before* m_nStartPos if ( nNewEndPos > m_nStartPos )
{ // The two regions are overlapping. // We'll first rotate the contents of m_pMatrix so that the overlap area // is positioned right; in the old window it is at the beginning, // it has to go to the end. // then we fill in the rows between new and old start pos.
// m_nEndPos < nNewEndPos when window not filled (e.g. there are fewer rows in total than window size)
m_nEndPos = std::min(nNewEndPos, m_nEndPos); const sal_Int32 nOverlapSize = m_nEndPos - m_nStartPos; const sal_Int32 nStartPosOffset = m_nStartPos - nNewStartPos; // by how much m_nStartPos moves
m_nStartPos = nNewStartPos;
OSL_ENSURE( o3tl::make_unsigned(nOverlapSize) <= m_pMatrix->size(), "new window end is after end of cache matrix!" ); // the first position in m_pMatrix whose data we don't keep; // content will be moved to m_pMatrix.begin()
ORowSetMatrix::iterator aEnd (m_pMatrix->begin() + nOverlapSize); // the first unused position after we are done; it == m_pMatrix.end() if and only if the window is full
ORowSetMatrix::iterator aNewEnd (aEnd + nStartPosOffset); // *m_pMatrix now looks like: // [0; nOverlapSize) i.e. [begin(); aEnd): data kept // [nOverlapSize; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): new data of positions < old m_nStartPos // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused // Note that nOverlapSize + nStartPosOffset == m_nEndPos - m_nStartPos (new values) // When we are finished: // [0; nStartPosOffset) i.e. [begin(); aEnd): new data of positions < old m_nStartPos // [nStartPosOffset; nOverlapSize + nStartPosOffset) i.e. [aEnd, aNewEnd): kept // [nOverlapSize + nStartPosOffset; size()) i.e. [aNewEnd, end()): unused
std::rotate(m_pMatrix->begin(), aEnd, aNewEnd); if (!m_bModified)
{ // now correct the iterator in our iterator vector // rotateCacheIterator(aEnd-m_pMatrix->begin()); //can't be used because they decrement and here we need to increment for(auto& rCacheIter : m_aCacheIterators)
{ if ( !rCacheIter.second.pRowSet->isInsertRow()
&& rCacheIter.second.aIterator != m_pMatrix->end() )
{ const ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin(); if ( nDist >= nOverlapSize )
{ // That's from outside the overlap area; invalidate iterator.
rCacheIter.second.aIterator = m_pMatrix->end();
} else
{ // Inside overlap area: move to correct position
OSL_ENSURE(((nDist + nStartPosOffset) >= static_cast<ORowSetMatrix::difference_type>(0)) &&
((nDist + nStartPosOffset) < static_cast<sal_Int32>(m_pMatrix->size())),"Position is invalid!");
rCacheIter.second.aIterator += nStartPosOffset;
OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin()
&& rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!");
}
}
}
}
} else
{ // normally this should never happen
OSL_FAIL("What the hell is happen here!"); return;
}
} else
{// no rows can be reused so fill again
reFillMatrix(nNewStartPos,nNewEndPos);
}
}
OSL_ENSURE(nNewStartPos >= m_nStartPos, "ORowSetCache::moveWindow internal error: new start pos before current start pos"); if ( m_nEndPos < nNewEndPos )
{ // need to fill data *after* m_nEndPos if( nNewStartPos < m_nEndPos )
{ // The two regions are overlapping. const sal_Int32 nRowsInCache = m_nEndPos - m_nStartPos; if ( nRowsInCache < m_nFetchSize )
{ // There is some unused space in *m_pMatrix; fill it
OSL_ENSURE((nRowsInCache >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nRowsInCache) < m_pMatrix->size()),"Position is invalid!");
sal_Int32 nPos = m_nEndPos + 1; bool bCheck = m_xCacheSet->absolute(nPos);
ORowSetMatrix::iterator aIter = m_pMatrix->begin() + nRowsInCache; const sal_Int32 nRowsToFetch = std::min(nNewEndPos-m_nEndPos, m_nFetchSize-nRowsInCache); const ORowSetMatrix::const_iterator aEnd = aIter + nRowsToFetch;
bCheck = fill(aIter, aEnd, nPos, bCheck);
m_nEndPos = nPos - 1;
OSL_ENSURE( (!bCheck && m_nEndPos <= nNewEndPos ) ||
( bCheck && m_nEndPos == nNewEndPos ), "ORowSetCache::moveWindow opportunistic fetch-after-current-end went badly");
}
// A priori, the rows from begin() [inclusive] to (begin() + nNewStartPos - m_nStartPos) [exclusive] // have to be refilled with new to-be-fetched rows. // The rows behind this can be reused
ORowSetMatrix::iterator aIter = m_pMatrix->begin(); const sal_Int32 nNewStartPosInMatrix = nNewStartPos - m_nStartPos;
OSL_ENSURE((nNewStartPosInMatrix >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nNewStartPosInMatrix) < m_pMatrix->size()),"Position is invalid!"); // first position we reuse const ORowSetMatrix::const_iterator aEnd = m_pMatrix->begin() + nNewStartPosInMatrix; // End of used portion of the matrix. Is < m_pMatrix->end() if less data than window size
ORowSetMatrix::iterator aDataEnd = m_pMatrix->begin() + (m_nEndPos - m_nStartPos);
sal_Int32 nPos = m_nEndPos + 1; bool bCheck = m_xCacheSet->absolute(nPos);
bCheck = fill(aIter, aEnd, nPos, bCheck); // refill the region we don't need anymore //aIter and nPos are now the position *after* last filled in one!
// bind end to front if(bCheck)
{
OSL_ENSURE(aIter == aEnd, "fill() said went till end, but did not."); // rotate the end to the front
std::rotate(m_pMatrix->begin(), aIter, aDataEnd); // now correct the iterator in our iterator vector
rotateCacheIterator( nNewStartPosInMatrix );
m_nStartPos = nNewStartPos;
m_nEndPos = nNewEndPos; // now I can say how many rows we have // we have to read one row forward to ensure that we know when we are on last row // but only when we don't know it already bool bOk = true; if(!m_bRowCountFinal)
bOk = m_xCacheSet->next(); if(!bOk)
{
m_xCacheSet->previous(); // because we stand after the last row
m_nRowCount = nPos; // here we have the row count
OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos is not valid!");
m_bRowCountFinal = true;
} elseif(!m_bRowCountFinal)
m_nRowCount = std::max(nPos+1, m_nRowCount); //+1 because we successfully moved to row after nPos else
OSL_ENSURE(m_nRowCount >= nPos, "Final m_nRowCount is smaller than row I moved to!");
} else
{ // the end was reached before or at end() so we can set the start before or at nNewStartPos // and possibly keep more of m_pMatrix than planned. const ORowSetMatrix::const_iterator::difference_type nFetchedRows = aIter - m_pMatrix->begin(); // *m_pMatrix now looks like: // [0; nFetchedRows) i.e. [begin(); aIter): newly fetched data for positions m_nEndPos to m_nEndPos+nFetchedRows // [nFetchedRows; ???) i.e. [aIter; aDataEnd]: data to be kept for positions m_nStartPos+nFetchedRows to ???
nPos -= 1;
m_nStartPos += nFetchedRows;
m_nEndPos = nPos;
std::rotate(m_pMatrix->begin(), aIter, aDataEnd); // now correct the iterator in our iterator vector
rotateCacheIterator( nFetchedRows );
if ( !m_bRowCountFinal )
{
m_xCacheSet->previous(); // because we stand after the last row
m_nRowCount = std::max(m_nRowCount, nPos); // here we have the row count
OSL_ENSURE(nPos == m_xCacheSet->getRow(),"nPos isn't valid!");
m_bRowCountFinal = true;
}
} // here we need only to check if the beginning row is valid. If not we have to fetch it. if(!m_pMatrix->begin()->is())
{
aIter = m_pMatrix->begin();
*aIter = new ORowSetValueVector(m_xMetaData->getColumnCount());
m_xCacheSet->fillValueRow(*aIter, nPos);
bCheck = m_xCacheSet->next();
}
}
} else// no rows can be reused so fill again
reFillMatrix(nNewStartPos,nNewEndPos);
}
if(!m_bRowCountFinal)
m_nRowCount = std::max(m_nPosition,m_nRowCount);
OSL_ENSURE(m_nStartPos >= 0,"ORowSetCache::moveWindow: m_nStartPos is less than 0!");
OSL_ENSURE(m_nEndPos > m_nStartPos,"ORowSetCache::moveWindow: m_nStartPos not smaller than m_nEndPos");
OSL_ENSURE(m_nEndPos-m_nStartPos <= m_nFetchSize,"ORowSetCache::moveWindow: m_nStartPos and m_nEndPos too far apart");
}
bool ORowSetCache::first( )
{ // First move to the first row. // Then check if the cache window is at the beginning. // If not, then position the window and fill it with data. // We move the window smartly, i.e. we clear only the rows that are out of range bool bRet = m_xCacheSet->first(); if(bRet)
{
m_bBeforeFirst = m_bAfterLast = false;
m_nPosition = 1;
moveWindow();
m_aMatrixIter = m_pMatrix->begin();
} else
{
m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true;
m_nRowCount = m_nPosition = 0;
if(row < 0)
{ // here we have to scroll from the last row to backward so we have to go to last row and // and to the previous if(m_bRowCountFinal || last())
{
m_nPosition = m_nRowCount + row + 1; // + row because row is negative and +1 because row==-1 means last row if(m_nPosition < 1)
{
m_bBeforeFirst = true;
m_bAfterLast = false;
m_aMatrixIter = m_pMatrix->end();
} else
{
m_bBeforeFirst = false;
m_bAfterLast = m_nPosition > m_nRowCount;
moveWindow();
OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
m_aMatrixIter = calcPosition();
}
} else
m_aMatrixIter = m_pMatrix->end();
} else
{
m_nPosition = row; // the position flags
m_bBeforeFirst = false;
checkPositionFlags();
bool ORowSetCache::previous( )
{ bool bRet = false; if(!isBeforeFirst())
{ if(m_bAfterLast) // we stand after the last row so one before is the last row
bRet = last(); else
{
m_bAfterLast = false;
--m_nPosition;
moveWindow();
OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!");
void ORowSetCache::cancelRowModification()
{ // clear the insertrow references -> implies that the current row of the rowset changes as well for(auto& rCacheIter : m_aCacheIterators)
{ if ( rCacheIter.second.pRowSet->isInsertRow() && rCacheIter.second.aIterator == m_aInsertRow )
rCacheIter.second.aIterator = m_pMatrix->end();
}
resetInsertRow(false);
}
Any aBookmark = (**_rUpdateRow)[0].makeAny();
OSL_ENSURE(aBookmark.hasValue(),"Bookmark must have a value!"); // here we don't have to reposition our CacheSet, when we try to update a row, // the row was already fetched
moveToBookmark(aBookmark);
m_xCacheSet->updateRow(*_rUpdateRow,*m_aMatrixIter,m_aUpdateTable); // refetch the whole row
(*m_aMatrixIter) = nullptr;
if(m_xCacheSet->absolute(m_nPosition))
m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition); else
{
OSL_FAIL("cancelRowUpdates couldn't position right with absolute");
::dbtools::throwFunctionSequenceException(nullptr);
}
}
void ORowSetCache::rotateAllCacheIterators()
{ if (m_bModified) return;
// now correct the iterator in our iterator vector for (auto& rCacheIter : m_aCacheIterators)
{ if (!rCacheIter.second.pRowSet->isInsertRow())
{
rCacheIter.second.aIterator = m_pMatrix->end();
}
}
}
if(bCheck)
{ // here we know that we have to check on which side our table resides const OSQLParseNode* pTableRef; if(bLeftSide)
pTableRef = pJoin->getChild(0); else
pTableRef = pJoin->getChild(3);
OSL_ENSURE(SQL_ISRULE(pTableRef,table_ref),"Must be a tableref here!");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.