/* -*- 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 .
*/
OKeySet::OKeySet(connectivity::OSQLTable _xTable,
OUString _sUpdateTableName, // this can be the alias or the full qualified name const Reference< XSingleSelectQueryAnalyzer >& _xComposer, const ORowSetValueVector& _aParameterValueForCache,
sal_Int32 i_nMaxRows,
sal_Int32& o_nRowCount)
:OCacheSet(i_nMaxRows)
,m_aParameterValueForCache(new ORowSetValueVector(_aParameterValueForCache))
,m_xTable(std::move(_xTable))
,m_xComposer(_xComposer)
,m_sUpdateTableName(std::move(_sUpdateTableName))
,m_rRowCount(o_nRowCount)
,m_bRowCountFinal(false)
{
}
OKeySet::~OKeySet()
{
tryDispose(m_xSet); // m_xStatement is necessarily one of those for (auto & statement : m_vStatements)
{
tryDispose(statement.second);
}
m_xComposer = nullptr;
}
void OKeySet::initColumns()
{
Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); bool bCase = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers();
m_pKeyColumnNames.reset( new SelectColumnsMetaData(comphelper::UStringMixLess(bCase)) );
m_pColumnNames.reset( new SelectColumnsMetaData(comphelper::UStringMixLess(bCase)) );
m_pParameterNames.reset( new SelectColumnsMetaData(comphelper::UStringMixLess(bCase)) );
m_pForeignColumnNames.reset( new SelectColumnsMetaData(comphelper::UStringMixLess(bCase)) );
}
void OKeySet::findTableColumnsMatching_throw( const Any& i_aTable, const OUString& i_rUpdateTableName, const Reference<XDatabaseMetaData>& i_xMeta, const Reference<XNameAccess>& i_xQueryColumns,
std::unique_ptr<SelectColumnsMetaData> const & o_pKeyColumnNames)
{ // first ask the database itself for the best columns which can be used
Sequence< OUString> aBestColumnNames;
Reference<XNameAccess> xKeyColumns = getPrimaryKeyColumns_throw(i_aTable); if ( xKeyColumns.is() )
aBestColumnNames = xKeyColumns->getElementNames();
OUString sUpdateTableName( i_rUpdateTableName ); if ( sUpdateTableName.isEmpty() )
{
SAL_WARN("dbaccess", "OKeySet::findTableColumnsMatching_throw: This is a fallback only - it won't work when the table has an alias name." ); // If i_aTable originates from a query composer, and is a table which appears with an alias in the SELECT statement, // then the below code will not produce correct results. // For instance, imagine a "SELECT alias.col FROM table AS alias". Now i_aTable would be the table named // "table", so our sUpdateTableName would be "table" as well - not the information about the "alias" is // already lost here. // now getColumnPositions would traverse the columns, and check which of them belong to the table denoted // by sUpdateTableName. Since the latter is "table", but the columns only know that they belong to a table // named "alias", there will be no matching - so getColumnPositions wouldn't find anything.
// LEM: there used to be a break here; however, I see no reason to stop // at first non-updateTable, so I removed it. (think of multiple joins...)
}
}
}
// the first row is empty because it's now easier for us to distinguish when we are beforefirst or first // without extra variable to be set
OKeySetValue keySetValue{nullptr,0,nullptr};
m_aKeyMap.emplace(0, keySetValue);
m_aKeyIter = m_aKeyMap.begin();
}
void OKeySet::ensureStatement( )
{ // do we already have a statement for the current combination of NULLness // of key & foreign columns?
std::vector<bool> FilterColumnsNULL;
FilterColumnsNULL.reserve(m_aKeyIter->second.m_aRowSetRow->size()); for (autoconst& elem : *m_aKeyIter->second.m_aRowSetRow)
FilterColumnsNULL.push_back(elem.isNull());
vStatements_t::const_iterator pNewStatement(m_vStatements.find(FilterColumnsNULL)); if(pNewStatement == m_vStatements.end())
{ // no: make a new one
makeNewStatement();
std::pair< vStatements_t::const_iterator, bool > insert_result
(m_vStatements.emplace( FilterColumnsNULL, m_xStatement));
(void) insert_result; // WaE: unused variable
assert(insert_result.second);
} else // yes: use it
m_xStatement = pNewStatement->second;
}
Any OKeySet::getBookmark()
{
OSL_ENSURE(m_aKeyIter != m_aKeyMap.end() && m_aKeyIter != m_aKeyMap.begin(), "getBookmark is only possible when we stand on a valid row!"); return Any(m_aKeyIter->first);
}
OUStringBuffer aSql = "UPDATE " + m_aComposedTableName + " SET "; // list all columns that should be set static constexpr OUStringLiteral aPara(u" = ?,");
OUString aQuote = getIdentifierQuoteString(); static constexpr OUString aAnd(u" AND "_ustr);
OUString sIsNull(u" IS NULL"_ustr);
OUString sParam(u" = ?"_ustr);
// use keys and indexes for exact positioning
Reference<XIndexesSupplier> xIndexSup(_xTable,UNO_QUERY);
Reference<XIndexAccess> xIndexes; if ( xIndexSup.is() )
xIndexes.set(xIndexSup->getIndexes(),UNO_QUERY);
if ( i_sTableName.empty() && !bAutoValuesFetched && m_bInserted )
{ // first check if all key column values were set const OUString sQuote = getIdentifierQuoteString();
OUStringBuffer sMaxStmt; auto aEnd = m_pKeyColumnNames->end(); for (autoconst& autoColumn : m_aAutoColumns)
{ // we will only fetch values which are keycolumns
SelectColumnsMetaData::const_iterator aFind = m_pKeyColumnNames->find(autoColumn); if ( aFind != aEnd )
{
sMaxStmt.append(" MAX(" + ::dbtools::quoteName( sQuote,aFind->second.sRealName) + "),");
}
}
if(!sMaxStmt.isEmpty())
{
sMaxStmt[sMaxStmt.getLength()-1] = ' ';
OUString sStmt = "SELECT " + sMaxStmt + "FROM ";
OUString sCatalog,sSchema,sTable;
::dbtools::qualifiedNameComponents(m_xConnection->getMetaData(),m_sUpdateTableName,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation);
sStmt += ::dbtools::composeTableNameForSelect( m_xConnection, sCatalog, sSchema, sTable ); try
{ // now fetch the autoincrement values
Reference<XStatement> xStatement = m_xConnection->createStatement();
Reference<XResultSet> xRes = xStatement->executeQuery(sStmt);
Reference<XRow> xRow(xRes,UNO_QUERY); if(xRow.is() && xRes->next())
{
sal_Int32 j=1; for (autoconst& autoColumn : m_aAutoColumns)
{ // we will only fetch values which are keycolumns
SelectColumnsMetaData::const_iterator aFind = m_pKeyColumnNames->find(autoColumn); if ( aFind != aEnd )
(*_rInsertRow)[aFind->second.nPosition].fill(j++, aFind->second.nType, xRow);
}
}
::comphelper::disposeComponent(xStatement);
} catch(SQLException&)
{
SAL_WARN("dbaccess", "Could not fetch with MAX() ");
}
}
} if ( m_bInserted )
{
OKeySetMatrix::const_iterator aKeyIter = m_aKeyMap.end();
--aKeyIter;
ORowSetRow aKeyRow = new connectivity::ORowVector< ORowSetValue >(m_pKeyColumnNames->size());
copyRowValue(_rInsertRow,aKeyRow,aKeyIter->first + 1);
m_aKeyIter = m_aKeyMap.emplace( aKeyIter->first + 1, OKeySetValue{aKeyRow,1,nullptr} ).first; // now we set the bookmark for this row
(*_rInsertRow)[0] = Any(static_cast<sal_Int32>(m_aKeyIter->first));
tryRefetch(_rInsertRow,bRefetch);
}
}
// check the if the parameter values have been changed
OSL_ENSURE((m_aParameterValueForCache->size()-1) == m_pParameterNames->size(),"OKeySet::copyRowValue: Parameter values and names differ!");
connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aParaValuesIter = m_aParameterValueForCache->begin() +1;
bool bChanged = false;
sal_Int32 i = 1; for (autoconst& parameterName : *m_pParameterNames)
{
ORowSetValue aValue(*aParaValuesIter);
aValue.setSigned(m_aSignedFlags[parameterName.second.nPosition-1]); if ( (*_rInsertRow)[parameterName.second.nPosition] != aValue )
{
rtl::Reference aCopy( new ORowSetValueVector(*m_aParameterValueForCache));
(*aCopy)[i] = (*_rInsertRow)[parameterName.second.nPosition];
m_aUpdatedParameter[i_nBookmark] = std::move(aCopy);
bChanged = true;
}
++aParaValuesIter;
++i;
} if ( !bChanged )
{
m_aUpdatedParameter.erase(i_nBookmark);
}
// update the key values for (autoconst& keyColumnName : *m_pKeyColumnNames)
{
impl_convertValue_throw(_rInsertRow,keyColumnName.second);
*aIter = (*_rInsertRow)[keyColumnName.second.nPosition];
aIter->setTypeKind(keyColumnName.second.nType);
++aIter;
}
}
OUStringBuffer aSql("DELETE FROM " + m_aComposedTableName + " WHERE ");
// list all columns that should be set
OUString aQuote = getIdentifierQuoteString(); staticconstchar aAnd[] = " AND ";
// use keys and indexes for exact positioning
Reference<XIndexesSupplier> xIndexSup(_xTable,UNO_QUERY);
Reference<XIndexAccess> xIndexes; if ( xIndexSup.is() )
xIndexes.set(xIndexSup->getIndexes(),UNO_QUERY);
// now create end execute the prepared statement
Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(aSql.makeStringAndClear()));
Reference< XParameters > xParameter(xPrep,UNO_QUERY);
sal_Int32 i = 1; for (autoconst& keyColumnName : *m_pKeyColumnNames)
{
setParameter(i++,xParameter,(*_rDeleteRow)[keyColumnName.second.nPosition],keyColumnName.second.nType,keyColumnName.second.nScale);
}
// now we have to set the index values auto aIter = m_pColumnNames->begin(); for (autoconst& indexColumnPosition : aIndexColumnPositions)
{
setParameter(i++,xParameter,(*_rDeleteRow)[indexColumnPosition],(*_rDeleteRow)[indexColumnPosition].getTypeKind(),aIter->second.nScale);
++aIter;
}
if(isAfterLast()) returnfalse;
++m_aKeyIter; if(!m_bRowCountFinal && m_aKeyIter == m_aKeyMap.end())
{ // not yet all records fetched, but we reached the end of those we fetched // try to fetch one more row if (fetchRow())
{
OSL_ENSURE(!isAfterLast(), "fetchRow succeeded, but isAfterLast()"); returntrue;
} else
{ // nope, we arrived at end of data
m_aKeyIter = m_aKeyMap.end();
OSL_ENSURE(isAfterLast(), "fetchRow failed, but not end of data");
}
}
sal_Int32 OKeySet::getRow( )
{
OSL_ENSURE(!isAfterLast(),"getRow is not allowed when afterlast record!");
OSL_ENSURE(!isBeforeFirst(),"getRow is not allowed when beforefirst record!");
row = std::min(std::abs(row), static_cast<sal_Int32>(std::distance(m_aKeyMap.begin(), m_aKeyIter)));
m_aKeyIter = std::prev(m_aKeyIter, row);
} else
{ if(row >= static_cast<sal_Int32>(m_aKeyMap.size()))
{ // we don't have this row if(!m_bRowCountFinal)
{ // but there may still be rows to fetch. bool bNext = true; for(sal_Int32 i=m_aKeyMap.size()-1;i < row && bNext;++i)
bNext = fetchRow(); // it is guaranteed that the above loop has executed at least once, // that is fetchRow called at least once. if ( bNext )
{
bFetchedRow = true;
} else
{ // reached end of data before desired row
m_aKeyIter = m_aKeyMap.end(); returnfalse;
}
} else
{ // no more rows to fetch -> fail
m_aKeyIter = m_aKeyMap.end(); returnfalse;
}
} else
{
m_aKeyIter = std::next(m_aKeyMap.begin(), row);
}
} if ( !bFetchedRow )
{
invalidateRow();
}
bool OKeySet::doTryRefetch_throw()
{
ensureStatement( ); // we just reassign the base members
Reference< XParameters > xParameter(m_xStatement,UNO_QUERY);
OSL_ENSURE(xParameter.is(),"No Parameter interface!");
xParameter->clearParameters();
// now set the primary key column values
connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aIter = m_aKeyIter->second.m_aRowSetRow->begin(); for (autoconst& keyColumnName : *m_pKeyColumnNames)
setOneKeyColumnParameter(nPos,xParameter,*aIter++,keyColumnName.second.nType,keyColumnName.second.nScale); for (autoconst& foreignColumnName : *m_pForeignColumnNames)
setOneKeyColumnParameter(nPos,xParameter,*aIter++,foreignColumnName.second.nType,foreignColumnName.second.nScale);
m_xSet = m_xStatement->executeQuery();
OSL_ENSURE(m_xSet.is(),"No resultset from statement!"); return m_xSet->next();
}
void OKeySet::refreshRow()
{
invalidateRow();
if(isBeforeFirst() || isAfterLast()) return;
if ( m_aKeyIter->second.m_xRow.is() )
{
m_xRow = m_aKeyIter->second.m_xRow; return;
}
bool bOK = doTryRefetch_throw(); if ( !bOK )
{ // This row has disappeared; remove it.
OKeySetMatrix::const_iterator aTemp = m_aKeyIter; // use *next* row
++m_aKeyIter;
m_aKeyMap.erase(aTemp);
// adjust RowCount for the row we have removed if (m_rRowCount > 0)
--m_rRowCount; else
SAL_WARN("dbaccess", "m_rRowCount got out of sync: non-empty m_aKeyMap, but m_rRowCount <= 0");
if (m_aKeyIter == m_aKeyMap.end())
{
::comphelper::disposeComponent(m_xSet); if (!isAfterLast())
{ // it was the last fetched row, // but there may be another one to fetch if (!fetchRow())
{ // nope, that really was the last
m_aKeyIter = m_aKeyMap.end();
OSL_ENSURE(isAfterLast(), "fetchRow() failed but not isAfterLast()!");
}
} // Now, either fetchRow has set m_xRow or isAfterLast()
} else
{
refreshRow();
}
} else
{
m_xRow.set(m_xSet,UNO_QUERY);
OSL_ENSURE(m_xRow.is(),"No row from statement!");
}
}
bool OKeySet::fetchRow()
{ // fetch the next row and append on the keyset bool bRet = false; if ( !m_bRowCountFinal && (!m_nMaxRows || sal_Int32(m_aKeyMap.size()) < m_nMaxRows) )
bRet = m_xDriverSet->next(); if ( bRet )
{
ORowSetRow aKeyRow = new connectivity::ORowVector< ORowSetValue >((*m_pKeyColumnNames).size() + m_pForeignColumnNames->size());
void getColumnPositions(const Reference<XNameAccess>& _rxQueryColumns, const css::uno::Sequence< OUString >& _aColumnNames,
std::u16string_view _rsUpdateTableName,
SelectColumnsMetaData& o_rColumnNames, bool i_bAppendTableName)
{ // get the real name of the columns
::comphelper::UStringMixEqual bCase(o_rColumnNames.key_comp().isCaseSensitive());
sal_Int32 nPos = 0; for (auto& queryColumnName : _rxQueryColumns->getElementNames())
{
++nPos;
Reference<XPropertySet> xQueryColumnProp(_rxQueryColumns->getByName(queryColumnName),UNO_QUERY_THROW);
OUString sRealName,sTableName;
OSL_ENSURE(xQueryColumnProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_REALNAME),"Property REALNAME not available!");
OSL_ENSURE(xQueryColumnProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME),"Property TABLENAME not available!");
xQueryColumnProp->getPropertyValue(PROPERTY_REALNAME) >>= sRealName;
xQueryColumnProp->getPropertyValue(PROPERTY_TABLENAME) >>= sTableName;
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.