diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /dbaccess/source/core/api | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
57 files changed, 22966 insertions, 0 deletions
diff --git a/dbaccess/source/core/api/BookmarkSet.cxx b/dbaccess/source/core/api/BookmarkSet.cxx new file mode 100644 index 000000000..75dfc24f5 --- /dev/null +++ b/dbaccess/source/core/api/BookmarkSet.cxx @@ -0,0 +1,204 @@ +/* -*- 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 . + */ + +#include "BookmarkSet.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <connectivity/dbexception.hxx> + +using namespace dbaccess; +using namespace ::connectivity; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::osl; + +void OBookmarkSet::construct(const Reference< XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) +{ + OCacheSet::construct(_xDriverSet,i_sRowSetFilter); + m_xRowLocate.set(_xDriverSet,UNO_QUERY); +} + +void OBookmarkSet::reset(const Reference< XResultSet>& _xDriverSet) +{ + construct(_xDriverSet, m_sRowSetFilter); +} + +Any OBookmarkSet::getBookmark() +{ + return m_xRowLocate->getBookmark(); +} + +bool OBookmarkSet::moveToBookmark( const Any& bookmark ) +{ + return m_xRowLocate->moveToBookmark(bookmark); +} + +sal_Int32 OBookmarkSet::compareBookmarks( const Any& _first, const Any& _second ) +{ + return m_xRowLocate->compareBookmarks(_first,_second); +} + +bool OBookmarkSet::hasOrderedBookmarks( ) +{ + return m_xRowLocate->hasOrderedBookmarks(); +} + +sal_Int32 OBookmarkSet::hashBookmark( const Any& bookmark ) +{ + return m_xRowLocate->hashBookmark(bookmark); +} + +void OBookmarkSet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + Reference<XRowUpdate> xUpdRow(m_xRowLocate,UNO_QUERY); + if(!xUpdRow.is()) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_XROWUPDATE ), StandardSQLState::GENERAL_ERROR, *this ); + + Reference<XResultSetUpdate> xUpd(m_xRowLocate,UNO_QUERY); + if(xUpd.is()) + { + xUpd->moveToInsertRow(); + sal_Int32 i = 1; + connectivity::ORowVector< ORowSetValue > ::Vector::iterator aEnd = _rInsertRow->end(); + for(connectivity::ORowVector< ORowSetValue > ::Vector::iterator aIter = _rInsertRow->begin()+1;aIter != aEnd;++aIter,++i) + { + aIter->setSigned(m_aSignedFlags[i-1]); + updateColumn(i,xUpdRow,*aIter); + } + xUpd->insertRow(); + (*_rInsertRow->begin()) = m_xRowLocate->getBookmark(); + } + else + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_XRESULTSETUPDATE ), StandardSQLState::GENERAL_ERROR, *this ); +} + +void OBookmarkSet::updateRow(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + Reference<XRowUpdate> xUpdRow(m_xRowLocate,UNO_QUERY); + if(!xUpdRow.is()) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_XROWUPDATE ), StandardSQLState::GENERAL_ERROR, *this ); + + sal_Int32 i = 1; + connectivity::ORowVector< ORowSetValue > ::Vector::const_iterator aOrgIter = _rOriginalRow->begin()+1; + connectivity::ORowVector< ORowSetValue > ::Vector::iterator aEnd = _rInsertRow->end(); + for(connectivity::ORowVector< ORowSetValue > ::Vector::iterator aIter = _rInsertRow->begin()+1;aIter != aEnd;++aIter,++i,++aOrgIter) + { + aIter->setSigned(aOrgIter->isSigned()); + updateColumn(i,xUpdRow,*aIter); + } + + + Reference<XResultSetUpdate> xUpd(m_xRowLocate,UNO_QUERY); + if(xUpd.is()) + xUpd->updateRow(); + else + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_XRESULTSETUPDATE ), StandardSQLState::GENERAL_ERROR, *this ); +} + +void OBookmarkSet::deleteRow(const ORowSetRow& /*_rDeleteRow*/ ,const connectivity::OSQLTable& /*_xTable*/ ) +{ + Reference<XResultSetUpdate> xUpd(m_xRowLocate,UNO_QUERY); + + xUpd->deleteRow(); +} + +void OBookmarkSet::updateColumn(sal_Int32 nPos, const Reference< XRowUpdate >& _xParameter, const ORowSetValue& _rValue) +{ + if(!(_rValue.isBound() && _rValue.isModified())) + return; + + if(_rValue.isNull()) + _xParameter->updateNull(nPos); + else + { + + switch(_rValue.getTypeKind()) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + _xParameter->updateNumericObject(nPos,_rValue.makeAny(),m_xSetMetaData->getScale(nPos)); + break; + case DataType::CHAR: + case DataType::VARCHAR: + _xParameter->updateString(nPos,_rValue.getString()); + break; + case DataType::BIGINT: + if ( _rValue.isSigned() ) + _xParameter->updateLong(nPos,_rValue.getLong()); + else + _xParameter->updateString(nPos,_rValue.getString()); + break; + case DataType::BIT: + case DataType::BOOLEAN: + _xParameter->updateBoolean(nPos,_rValue.getBool()); + break; + case DataType::TINYINT: + if ( _rValue.isSigned() ) + _xParameter->updateByte(nPos,_rValue.getInt8()); + else + _xParameter->updateShort(nPos,_rValue.getInt16()); + break; + case DataType::SMALLINT: + if ( _rValue.isSigned() ) + _xParameter->updateShort(nPos,_rValue.getInt16()); + else + _xParameter->updateInt(nPos,_rValue.getInt32()); + break; + case DataType::INTEGER: + if ( _rValue.isSigned() ) + _xParameter->updateInt(nPos,_rValue.getInt32()); + else + _xParameter->updateLong(nPos,_rValue.getLong()); + break; + case DataType::FLOAT: + _xParameter->updateFloat(nPos,_rValue.getFloat()); + break; + case DataType::DOUBLE: + case DataType::REAL: + _xParameter->updateDouble(nPos,_rValue.getDouble()); + break; + case DataType::DATE: + _xParameter->updateDate(nPos,_rValue.getDate()); + break; + case DataType::TIME: + _xParameter->updateTime(nPos,_rValue.getTime()); + break; + case DataType::TIMESTAMP: + _xParameter->updateTimestamp(nPos,_rValue.getDateTime()); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + _xParameter->updateBytes(nPos,_rValue.getSequence()); + break; + case DataType::BLOB: + case DataType::CLOB: + _xParameter->updateObject(nPos,_rValue.getAny()); + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/BookmarkSet.hxx b/dbaccess/source/core/api/BookmarkSet.hxx new file mode 100644 index 000000000..9863a80ec --- /dev/null +++ b/dbaccess/source/core/api/BookmarkSet.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include "CacheSet.hxx" +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> + +namespace dbaccess +{ + // this set is used when we have a bookmarkable set from the driver + class OBookmarkSet : public OCacheSet + { + css::uno::Reference< css::sdbcx::XRowLocate> m_xRowLocate; + + void updateColumn(sal_Int32 nPos, const css::uno::Reference< css::sdbc::XRowUpdate >& _xParameter, const connectivity::ORowSetValue& _rValue); + public: + explicit OBookmarkSet(sal_Int32 i_nMaxRows) : OCacheSet(i_nMaxRows) + {} + virtual ~OBookmarkSet() override + { + m_xRowLocate = nullptr; + } + + virtual void construct(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) override; + virtual void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet) override; + // css::sdbcx::XRowLocate + virtual css::uno::Any getBookmark() override; + virtual bool moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual bool hasOrderedBookmarks( ) override; + virtual sal_Int32 hashBookmark( const css::uno::Any& bookmark ) override; + // css::sdbc::XResultSetUpdate + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + virtual void updateRow(const ORowSetRow& _rInsertRow,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) override; + virtual void deleteRow(const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CIndexes.cxx b/dbaccess/source/core/api/CIndexes.cxx new file mode 100644 index 000000000..409d87728 --- /dev/null +++ b/dbaccess/source/core/api/CIndexes.cxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#include "CIndexes.hxx" + + +using namespace connectivity; +using namespace connectivity::sdbcx; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace dbaccess; +using namespace cppu; + + +ObjectType OIndexes::createObject(const OUString& _rName) +{ + ObjectType xRet; + if ( m_xIndexes.is() && m_xIndexes->hasByName(_rName) ) + xRet.set(m_xIndexes->getByName(_rName),UNO_QUERY); + else + xRet = OIndexesHelper::createObject(_rName); + + return xRet; +} + +Reference< XPropertySet > OIndexes::createDescriptor() +{ + Reference<XDataDescriptorFactory> xData( m_xIndexes,UNO_QUERY); + if(xData.is()) + return xData->createDataDescriptor(); + else + return OIndexesHelper::createDescriptor(); +} + +// XAppend +ObjectType OIndexes::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + Reference<XAppend> xData( m_xIndexes,UNO_QUERY); + if ( !xData.is() ) + return OIndexesHelper::appendObject( _rForName, descriptor ); + + xData->appendByDescriptor(descriptor); + return createObject( _rForName ); +} + +// XDrop +void OIndexes::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + if ( m_xIndexes.is() ) + { + Reference<XDrop> xData( m_xIndexes,UNO_QUERY); + if ( xData.is() ) + xData->dropByName(_sElementName); + } + else + OIndexesHelper::dropObject(_nPos,_sElementName); +} + +void OIndexes::disposing() +{ + if ( m_xIndexes.is() ) + clear_NoDispose(); + else + OIndexesHelper::disposing(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CIndexes.hxx b/dbaccess/source/core/api/CIndexes.hxx new file mode 100644 index 000000000..f20da6cbe --- /dev/null +++ b/dbaccess/source/core/api/CIndexes.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <connectivity/TIndexes.hxx> + +namespace dbaccess +{ + class OIndexes : public connectivity::OIndexesHelper + { + css::uno::Reference< css::container::XNameAccess > m_xIndexes; + protected: + virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual css::uno::Reference< css::beans::XPropertySet > createDescriptor() override; + virtual connectivity::sdbcx::ObjectType appendObject( const OUString& _rForName, const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + virtual void dropObject(sal_Int32 _nPos, const OUString& _sElementName) override; + public: + OIndexes(connectivity::OTableHelper* _pTable, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector, + const css::uno::Reference< css::container::XNameAccess >& _rxIndexes + ) : connectivity::OIndexesHelper(_pTable,_rMutex,_rVector) + ,m_xIndexes(_rxIndexes) + {} + + virtual void disposing() override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CRowSetColumn.cxx b/dbaccess/source/core/api/CRowSetColumn.cxx new file mode 100644 index 000000000..98435b7c4 --- /dev/null +++ b/dbaccess/source/core/api/CRowSetColumn.cxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + + +#include <stringconstants.hxx> +#include <strings.hxx> +#include "CRowSetColumn.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::beans; + +namespace dbaccess +{ + +ORowSetColumn::ORowSetColumn( const Reference < XResultSetMetaData >& _xMetaData, + const Reference < XRow >& _xRow, sal_Int32 _nPos, + const Reference< XDatabaseMetaData >& _rxDBMeta, + const OUString& _rDescription, + const OUString& i_sLabel, + const std::function<const ::connectivity::ORowSetValue& (sal_Int32)> &_getValue ) + :ORowSetDataColumn( _xMetaData, _xRow, nullptr, _nPos, _rxDBMeta, _rDescription, i_sLabel, _getValue ) +{ +} + +::cppu::IPropertyArrayHelper* ORowSetColumn::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property> aDescriptor + { + { PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_DISPLAYSIZE, PROPERTY_ID_DISPLAYSIZE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISAUTOINCREMENT, PROPERTY_ID_ISAUTOINCREMENT, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCASESENSITIVE, PROPERTY_ID_ISCASESENSITIVE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISDEFINITELYWRITABLE, PROPERTY_ID_ISDEFINITELYWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISROWVERSION, PROPERTY_ID_ISROWVERSION, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSEARCHABLE, PROPERTY_ID_ISSEARCHABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSIGNED, PROPERTY_ID_ISSIGNED, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISWRITABLE, PROPERTY_ID_ISWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_PRECISION, PROPERTY_ID_PRECISION, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCALE, PROPERTY_ID_SCALE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SERVICENAME, PROPERTY_ID_SERVICENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPE, PROPERTY_ID_TYPE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPENAME, PROPERTY_ID_TYPENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_VALUE, PROPERTY_ID_VALUE, cppu::UnoType<Any>::get(), css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::BOUND} + }; + + Sequence< Property > aRegisteredProperties; + describeProperties( aRegisteredProperties ); + + return new ::cppu::OPropertyArrayHelper( ::comphelper::concatSequences( aDescriptor, aRegisteredProperties ), false ); +} + +::cppu::IPropertyArrayHelper& ORowSetColumn::getInfoHelper() +{ + return *static_cast< ::comphelper::OPropertyArrayUsageHelper< ORowSetColumn >* >(this)->getArrayHelper(); +} + +void SAL_CALL ORowSetColumn::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue ) +{ + OSL_ENSURE( nHandle != PROPERTY_ID_VALUE, "ORowSetColumn::setFastPropertyValue_NoBroadcast: hmm? This property is marked as READONLY!" ); + if ( nHandle != PROPERTY_ID_VALUE ) + ORowSetDataColumn::setFastPropertyValue_NoBroadcast( nHandle, rValue ); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CRowSetColumn.hxx b/dbaccess/source/core/api/CRowSetColumn.hxx new file mode 100644 index 000000000..bc0ae7eed --- /dev/null +++ b/dbaccess/source/core/api/CRowSetColumn.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ +#pragma once + +#include "RowSetRow.hxx" +#include <connectivity/CommonTools.hxx> +#include <comphelper/proparrhlp.hxx> +#include "CRowSetDataColumn.hxx" + +namespace dbaccess +{ + class ORowSetColumn; + class ORowSetColumn :public ORowSetDataColumn + ,public ::comphelper::OPropertyArrayUsageHelper< ORowSetColumn > + + { + public: + ORowSetColumn( const css::uno::Reference < css::sdbc::XResultSetMetaData >& _xMetaData, + const css::uno::Reference < css::sdbc::XRow >& _xRow, + sal_Int32 _nPos, + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rxDBMeta, + const OUString& _rDescription, + const OUString& i_sLabel, + const std::function<const ::connectivity::ORowSetValue& (sal_Int32)> &_getValue); + + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,const css::uno::Any& rValue ) override; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CRowSetDataColumn.cxx b/dbaccess/source/core/api/CRowSetDataColumn.cxx new file mode 100644 index 000000000..0dce91848 --- /dev/null +++ b/dbaccess/source/core/api/CRowSetDataColumn.cxx @@ -0,0 +1,236 @@ +/* -*- 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 . + */ + + +#include "CRowSetDataColumn.hxx" +#include <stringconstants.hxx> +#include <strings.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +using namespace dbaccess; +using namespace comphelper; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace cppu; +using namespace osl; + + +ORowSetDataColumn::ORowSetDataColumn( const Reference < XResultSetMetaData >& _xMetaData, + const Reference < XRow >& _xRow, + const Reference < XRowUpdate >& _xRowUpdate, + sal_Int32 _nPos, + const Reference< XDatabaseMetaData >& _rxDBMeta, + const OUString& _rDescription, + const OUString& i_sLabel, + const std::function<const ORowSetValue& (sal_Int32)> &_getValue) + :ODataColumn(_xMetaData,_xRow,_xRowUpdate,_nPos,_rxDBMeta) + ,m_pGetValue(_getValue) + ,m_sLabel(i_sLabel) + ,m_aDescription(_rDescription) +{ + OColumnSettings::registerProperties( *this ); + registerProperty( PROPERTY_DESCRIPTION, PROPERTY_ID_DESCRIPTION, PropertyAttribute::READONLY, &m_aDescription, cppu::UnoType<decltype(m_aDescription)>::get() ); +} + +ORowSetDataColumn::~ORowSetDataColumn() +{ +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ORowSetDataColumn::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property> aDescriptor + { + { PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_DISPLAYSIZE, PROPERTY_ID_DISPLAYSIZE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISAUTOINCREMENT, PROPERTY_ID_ISAUTOINCREMENT, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCASESENSITIVE, PROPERTY_ID_ISCASESENSITIVE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISDEFINITELYWRITABLE, PROPERTY_ID_ISDEFINITELYWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISROWVERSION, PROPERTY_ID_ISROWVERSION, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSEARCHABLE, PROPERTY_ID_ISSEARCHABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSIGNED, PROPERTY_ID_ISSIGNED, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISWRITABLE, PROPERTY_ID_ISWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_PRECISION, PROPERTY_ID_PRECISION, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCALE, PROPERTY_ID_SCALE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SERVICENAME, PROPERTY_ID_SERVICENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPE, PROPERTY_ID_TYPE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPENAME, PROPERTY_ID_TYPENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_VALUE, PROPERTY_ID_VALUE, cppu::UnoType<Any>::get(), css::beans::PropertyAttribute::BOUND } + }; + + Sequence< Property > aRegisteredProperties; + describeProperties( aRegisteredProperties ); + + return new ::cppu::OPropertyArrayHelper( ::comphelper::concatSequences( aDescriptor, aRegisteredProperties ), false ); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& ORowSetDataColumn::getInfoHelper() +{ + return *static_cast< ::comphelper::OPropertyArrayUsageHelper< ORowSetDataColumn >* >(this)->getArrayHelper(); +} + +void SAL_CALL ORowSetDataColumn::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + if ( PROPERTY_ID_VALUE == nHandle ) + { + try + { + rValue = m_pGetValue(m_nPos).makeAny(); + } + catch(const SQLException &e) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw WrappedTargetRuntimeException("Could not retrieve column value: " + e.Message, + *const_cast<ORowSetDataColumn*>(this), + anyEx); + } + } + else if ( PROPERTY_ID_LABEL == nHandle && !m_sLabel.isEmpty() ) + rValue <<= m_sLabel; + else + ODataColumn::getFastPropertyValue( rValue, nHandle ); +} + +void SAL_CALL ORowSetDataColumn::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue ) +{ + switch( nHandle ) + { + case PROPERTY_ID_VALUE: + updateObject(rValue); + break; + case PROPERTY_ID_ISREADONLY: + { + bool bVal = false; + rValue >>= bVal; + m_isReadOnly = bVal; + } + break; + default: + ODataColumn::setFastPropertyValue_NoBroadcast( nHandle,rValue ); + break; + } +} + +sal_Bool SAL_CALL ORowSetDataColumn::convertFastPropertyValue( Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + bool bModified = false; + switch( nHandle ) + { + case PROPERTY_ID_VALUE: + { + rConvertedValue = rValue; + getFastPropertyValue(rOldValue, PROPERTY_ID_VALUE); + bModified = rConvertedValue != rOldValue; + } + break; + case PROPERTY_ID_ISREADONLY: + { + rConvertedValue = rValue; + getFastPropertyValue(rOldValue, PROPERTY_ID_ISREADONLY); + bModified = rConvertedValue != rOldValue; + } + break; + default: + bModified = ODataColumn::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue); + break; + } + + return bModified; +} + +Sequence< sal_Int8 > ORowSetDataColumn::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void ORowSetDataColumn::fireValueChange(const ORowSetValue& _rOldValue) +{ + const ORowSetValue &value(m_pGetValue(m_nPos)); + if ( value != _rOldValue) + { + sal_Int32 nHandle(PROPERTY_ID_VALUE); + m_aOldValue = _rOldValue.makeAny(); + Any aNew = value.makeAny(); + + fire(&nHandle, &aNew, &m_aOldValue, 1, false ); + } +} + +ORowSetDataColumns::ORowSetDataColumns( + bool _bCase, + const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector + ) : connectivity::sdbcx::OCollection(_rParent,_bCase,_rMutex,_rVector) + ,m_aColumns(_rColumns) +{ +} + +ORowSetDataColumns::~ORowSetDataColumns() +{ +} + +sdbcx::ObjectType ORowSetDataColumns::createObject(const OUString& _rName) +{ + connectivity::sdbcx::ObjectType xNamed; + + ::comphelper::UStringMixEqual aCase(isCaseSensitive()); + ::connectivity::OSQLColumns::Vector::const_iterator first = ::connectivity::find(m_aColumns->begin(),m_aColumns->end(),_rName,aCase); + if(first != m_aColumns->end()) + xNamed = *first; + + return xNamed; +} + +void ORowSetDataColumns::disposing() +{ + ORowSetDataColumns_BASE::disposing(); + m_aColumns = nullptr; +} + +void ORowSetDataColumns::assign(const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns,const std::vector< OUString> &_rVector) +{ + m_aColumns = _rColumns; + reFill(_rVector); +} + +void ORowSetDataColumns::impl_refresh() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CRowSetDataColumn.hxx b/dbaccess/source/core/api/CRowSetDataColumn.hxx new file mode 100644 index 000000000..f0efcbf8d --- /dev/null +++ b/dbaccess/source/core/api/CRowSetDataColumn.hxx @@ -0,0 +1,99 @@ +/* -*- 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 . + */ +#pragma once + +#include "datacolumn.hxx" +#include "RowSetRow.hxx" +#include <columnsettings.hxx> + +#include <connectivity/CommonTools.hxx> +#include <comphelper/proparrhlp.hxx> + +#include <functional> + +namespace dbaccess +{ + class ORowSetDataColumn; + typedef ::comphelper::OPropertyArrayUsageHelper<ORowSetDataColumn> ORowSetDataColumn_PROP; + + class ORowSetDataColumn : public ODataColumn, + public OColumnSettings, + public ORowSetDataColumn_PROP + { + protected: + const std::function<const ::connectivity::ORowSetValue& (sal_Int32)> m_pGetValue; + css::uno::Any m_aOldValue; + OUString m_sLabel; + OUString m_aDescription; // description + + virtual ~ORowSetDataColumn() override; + public: + ORowSetDataColumn(const css::uno::Reference < css::sdbc::XResultSetMetaData >& _xMetaData, + const css::uno::Reference < css::sdbc::XRow >& _xRow, + const css::uno::Reference < css::sdbc::XRowUpdate >& _xRowUpdate, + sal_Int32 _nPos, + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rxDBMeta, + const OUString& _rDescription, + const OUString& i_sLabel, + const std::function<const ::connectivity::ORowSetValue& (sal_Int32)> &_getValue); + + + // css::lang::XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const css::uno::Any& rValue ) override; + + void fireValueChange(const ::connectivity::ORowSetValue& _rOldValue); + protected: + using ODataColumn::getFastPropertyValue; + }; + + typedef connectivity::sdbcx::OCollection ORowSetDataColumns_BASE; + class ORowSetDataColumns : public ORowSetDataColumns_BASE + { + ::rtl::Reference< ::connectivity::OSQLColumns> m_aColumns; + protected: + virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override; + public: + ORowSetDataColumns( + bool _bCase, + const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector + ); + virtual ~ORowSetDataColumns() override; + // only the name is identical to ::cppu::OComponentHelper + virtual void disposing() override; + void assign(const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns,const std::vector< OUString> &_rVector); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CacheSet.cxx b/dbaccess/source/core/api/CacheSet.cxx new file mode 100644 index 000000000..9774877e6 --- /dev/null +++ b/dbaccess/source/core/api/CacheSet.cxx @@ -0,0 +1,580 @@ +/* -*- 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 . + */ + +#include "CacheSet.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> + +#include <connectivity/dbtools.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <comphelper/types.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> + +using namespace comphelper; + +using namespace dbaccess; +using namespace dbtools; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; +using namespace ::osl; + + +OCacheSet::OCacheSet(sal_Int32 i_nMaxRows) + :m_nMaxRows(i_nMaxRows) + ,m_bInserted(false) + ,m_bUpdated(false) + ,m_bDeleted(false) +{ + +} + +OUString OCacheSet::getIdentifierQuoteString() const +{ + OUString sQuote; + if ( m_xConnection.is() ) + if (auto xMeta = m_xConnection->getMetaData()) + sQuote = xMeta->getIdentifierQuoteString(); + return sQuote; +} + +void OCacheSet::construct( const Reference< XResultSet>& _xDriverSet,const OUString &i_sRowSetFilter) +{ + OSL_ENSURE(_xDriverSet.is(),"Invalid resultSet"); + + m_sRowSetFilter = i_sRowSetFilter; + + if(!_xDriverSet.is()) + return; + + m_xDriverSet = _xDriverSet; + m_xDriverRow.set(_xDriverSet,UNO_QUERY); + m_xSetMetaData = Reference<XResultSetMetaDataSupplier>(_xDriverSet,UNO_QUERY_THROW)->getMetaData(); + if ( m_xSetMetaData.is() ) + { + const sal_Int32 nCount = m_xSetMetaData->getColumnCount(); + m_aNullable.resize(nCount); + m_aSignedFlags.resize(nCount); + m_aColumnTypes.resize(nCount); + auto pNullableIter = m_aNullable.begin(); + auto pSignedIter = m_aSignedFlags.begin(); + auto pColumnIter = m_aColumnTypes.begin(); + for (sal_Int32 i=1; i <= nCount; ++i,++pSignedIter,++pColumnIter,++pNullableIter) + { + *pNullableIter = m_xSetMetaData->isNullable(i) != ColumnValue::NO_NULLS; + *pSignedIter = m_xSetMetaData->isSigned(i); + *pColumnIter = m_xSetMetaData->getColumnType(i); + } + } + Reference< XStatement> xStmt(m_xDriverSet->getStatement(),UNO_QUERY); + if(xStmt.is()) + m_xConnection = xStmt->getConnection(); + else + { + Reference< XPreparedStatement> xPrepStmt(m_xDriverSet->getStatement(),UNO_QUERY); + if ( xPrepStmt.is() ) + m_xConnection = xPrepStmt->getConnection(); + } +} + +OCacheSet::~OCacheSet() +{ + try + { + m_xDriverSet = nullptr; + m_xDriverRow = nullptr; + m_xSetMetaData = nullptr; + m_xConnection = nullptr; + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", ""); + } + catch(...) + { + SAL_WARN("dbaccess", "Unknown Exception occurred"); + } + +} + +void OCacheSet::fillTableName(const Reference<XPropertySet>& _xTable) +{ + OSL_ENSURE(_xTable.is(),"OCacheSet::fillTableName: PropertySet is empty!"); + if(m_aComposedTableName.isEmpty() && _xTable.is() ) + { + Reference<XDatabaseMetaData> xMeta(m_xConnection->getMetaData()); + m_aComposedTableName = composeTableName(xMeta + ,comphelper::getString(_xTable->getPropertyValue(PROPERTY_CATALOGNAME)) + ,comphelper::getString(_xTable->getPropertyValue(PROPERTY_SCHEMANAME)) + ,comphelper::getString(_xTable->getPropertyValue(PROPERTY_NAME)) + ,true + ,::dbtools::EComposeRule::InDataManipulation); + } +} + +void OCacheSet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql("INSERT INTO " + m_aComposedTableName + " ( "); + + // set values and column names + OUStringBuffer aValues(" VALUES ( "); + OUString aQuote = getIdentifierQuoteString(); + sal_Int32 i = 1; + ORowVector< ORowSetValue >::Vector::const_iterator aIter = _rInsertRow->begin()+1; + connectivity::ORowVector< ORowSetValue > ::Vector::iterator aEnd = _rInsertRow->end(); + for(; aIter != aEnd;++aIter) + { + aSql.append(::dbtools::quoteName( aQuote,m_xSetMetaData->getColumnName(i++)) + ","); + aValues.append("?,"); + } + + aSql[aSql.getLength() - 1] = ')'; + aValues[aValues.getLength() - 1] = ')'; + + aSql.append(aValues); + // now create end execute the prepared statement + { + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(aSql.makeStringAndClear())); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + i = 1; + for(aIter = _rInsertRow->begin()+1; aIter != aEnd;++aIter,++i) + { + if(aIter->isNull()) + xParameter->setNull(i,aIter->getTypeKind()); + else + setParameter(i,xParameter,*aIter,m_xSetMetaData->getColumnType(i),m_xSetMetaData->getScale(i)); + } + + m_bInserted = xPrep->executeUpdate() > 0; + } + + // TODO set the bookmark in the insert row +} + +void OCacheSet::fillParameters( const ORowSetRow& _rRow + ,const connectivity::OSQLTable& _xTable + ,OUStringBuffer& _sCondition + ,OUStringBuffer& _sParameter + ,std::vector< sal_Int32>& _rOrgValues) +{ + // use keys and indexes for exact positioning + // first the keys + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + const Reference<XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(xSet); + // second the indexes + Reference<XIndexesSupplier> xIndexSup(_xTable,UNO_QUERY); + Reference<XIndexAccess> xIndexes; + if(xIndexSup.is()) + xIndexes.set(xIndexSup->getIndexes(),UNO_QUERY); + + Reference<XPropertySet> xIndexColsSup; + std::vector< Reference<XNameAccess> > aAllIndexColumns; + if(xIndexes.is()) + { + for(sal_Int32 j=0;j<xIndexes->getCount();++j) + { + xIndexColsSup.set(xIndexes->getByIndex(j),UNO_QUERY); + if( xIndexColsSup.is() + && comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISUNIQUE)) + && !comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISPRIMARYKEYINDEX)) + ) + aAllIndexColumns.push_back(Reference<XColumnsSupplier>(xIndexColsSup,UNO_QUERY_THROW)->getColumns()); + } + } + + OUString aColumnName; + + static const char aAnd[] = " AND "; + + OUString aQuote = getIdentifierQuoteString(); + + sal_Int32 nCheckCount = 1; // index for the original values + sal_Int32 i = 1; + + OUString sIsNull(" IS NULL"); + OUString sParam(" = ?"); + ORowVector< ORowSetValue >::Vector::const_iterator aIter = _rRow->begin()+1; + ORowVector< ORowSetValue >::Vector::const_iterator aEnd = _rRow->end(); + for(; aIter != aEnd;++aIter,++nCheckCount,++i) + { + aColumnName = m_xSetMetaData->getColumnName(i); + if(xPrimaryKeyColumns.is() && xPrimaryKeyColumns->hasByName(aColumnName)) + { + _sCondition.append(::dbtools::quoteName( aQuote,aColumnName)); + if(aIter->isNull()) + _sCondition.append(sIsNull); + else + _sCondition.append(sParam); + _sCondition.append(aAnd); + _rOrgValues.push_back(nCheckCount); + + } + for (auto const& indexColumn : aAllIndexColumns) + { + if(indexColumn->hasByName(aColumnName)) + { + _sCondition.append(::dbtools::quoteName( aQuote,aColumnName)); + if(aIter->isNull()) + _sCondition.append(sIsNull); + else + _sCondition.append(sParam); + _sCondition.append(aAnd); + _rOrgValues.push_back(nCheckCount); + break; + } + } + if(aIter->isModified()) + { + _sParameter.append(::dbtools::quoteName( aQuote,aColumnName) + "?,"); + } + } +} + +void OCacheSet::updateRow(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql("UPDATE " + m_aComposedTableName + " SET "); + // list all columns that should be set + + OUStringBuffer aCondition; + std::vector< sal_Int32> aOrgValues; + fillParameters(_rInsertRow,_xTable,aCondition,aSql,aOrgValues); + aSql[aSql.getLength() - 1] = ' '; + if ( !aCondition.isEmpty() ) + { + aCondition.setLength(aCondition.getLength()-5); + + aSql.append(" WHERE " + aCondition); + } + else + ::dbtools::throwSQLException( + DBA_RES( RID_STR_NO_UPDATE_MISSING_CONDITION ), StandardSQLState::GENERAL_ERROR, *this ); + + // 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; + connectivity::ORowVector< ORowSetValue > ::Vector::iterator aEnd = _rInsertRow->end(); + for(ORowVector< ORowSetValue >::Vector::const_iterator aIter = _rInsertRow->begin()+1; aIter != aEnd;++aIter) + { + if(aIter->isModified()) + { + setParameter(i,xParameter,*aIter,m_xSetMetaData->getColumnType(i),m_xSetMetaData->getScale(i)); + ++i; + } + } + for (auto const& orgValue : aOrgValues) + { + setParameter(i,xParameter,(*_rOriginalRow)[orgValue],m_xSetMetaData->getColumnType(i),m_xSetMetaData->getScale(i)); + ++i; + } + + m_bUpdated = xPrep->executeUpdate() > 0; +} + +void OCacheSet::deleteRow(const ORowSetRow& _rDeleteRow ,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql("DELETE FROM " + m_aComposedTableName + " WHERE "); + + // 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); + + // Reference<XColumnsSupplier> + Reference<XPropertySet> xIndexColsSup; + std::vector< Reference<XNameAccess> > aAllIndexColumns; + if(xIndexes.is()) + { + for(sal_Int32 j=0;j<xIndexes->getCount();++j) + { + xIndexColsSup.set(xIndexes->getByIndex(j),UNO_QUERY); + if( xIndexColsSup.is() + && comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISUNIQUE)) + && !comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISPRIMARYKEYINDEX)) + ) + aAllIndexColumns.push_back(Reference<XColumnsSupplier>(xIndexColsSup,UNO_QUERY_THROW)->getColumns()); + } + } + + OUStringBuffer aColumnName; + std::vector< sal_Int32> aOrgValues; + fillParameters(_rDeleteRow,_xTable,aSql,aColumnName,aOrgValues); + + aSql.setLength(aSql.getLength()-5); + + // now create and execute the prepared statement + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(aSql.makeStringAndClear())); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + sal_Int32 i = 1; + for (auto const& orgValue : aOrgValues) + { + setParameter(i,xParameter,(*_rDeleteRow)[orgValue],m_xSetMetaData->getColumnType(i),m_xSetMetaData->getScale(i)); + ++i; + } + + m_bDeleted = xPrep->executeUpdate() > 0; +} + +void OCacheSet::setParameter(sal_Int32 nPos + ,const Reference< XParameters >& _xParameter + ,const ORowSetValue& _rValue + ,sal_Int32 _nType + ,sal_Int32 _nScale) +{ + sal_Int32 nType = ( _nType != DataType::OTHER ) ? _nType : _rValue.getTypeKind(); + ::dbtools::setObjectWithInfo(_xParameter,nPos,_rValue,nType,_nScale); +} + +void OCacheSet::fillValueRow(ORowSetRow& _rRow,sal_Int32 _nPosition) +{ + Any aBookmark = getBookmark(); + if(!aBookmark.hasValue()) + aBookmark <<= _nPosition; + + connectivity::ORowVector< ORowSetValue >::Vector::iterator aIter = _rRow->begin(); + connectivity::ORowVector< ORowSetValue >::Vector::iterator aEnd = _rRow->end(); + (*aIter) = aBookmark; + ++aIter; + for(sal_Int32 i=1;aIter != aEnd;++aIter,++i) + { + aIter->setSigned(m_aSignedFlags[i-1]); + aIter->fill(i, m_aColumnTypes[i-1], this); + } +} + +sal_Bool SAL_CALL OCacheSet::wasNull( ) +{ + return m_xDriverRow->wasNull(); +} + +OUString SAL_CALL OCacheSet::getString( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getString(columnIndex); +} + +sal_Bool SAL_CALL OCacheSet::getBoolean( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getBoolean(columnIndex); +} + +sal_Int8 SAL_CALL OCacheSet::getByte( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getByte(columnIndex); +} + +sal_Int16 SAL_CALL OCacheSet::getShort( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getShort(columnIndex); +} + +sal_Int32 SAL_CALL OCacheSet::getInt( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getInt(columnIndex); +} + +sal_Int64 SAL_CALL OCacheSet::getLong( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getLong(columnIndex); +} + +float SAL_CALL OCacheSet::getFloat( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getFloat(columnIndex); +} + +double SAL_CALL OCacheSet::getDouble( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getDouble(columnIndex); +} + +Sequence< sal_Int8 > SAL_CALL OCacheSet::getBytes( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getBytes(columnIndex); +} + +css::util::Date SAL_CALL OCacheSet::getDate( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getDate(columnIndex); +} + +css::util::Time SAL_CALL OCacheSet::getTime( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getTime(columnIndex); +} + +css::util::DateTime SAL_CALL OCacheSet::getTimestamp( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getTimestamp(columnIndex); +} + +Reference< css::io::XInputStream > SAL_CALL OCacheSet::getBinaryStream( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getBinaryStream(columnIndex); +} + +Reference< css::io::XInputStream > SAL_CALL OCacheSet::getCharacterStream( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getCharacterStream(columnIndex); +} + +Any SAL_CALL OCacheSet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& typeMap ) +{ + return m_xDriverRow->getObject(columnIndex,typeMap); +} + +Reference< XRef > SAL_CALL OCacheSet::getRef( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getRef(columnIndex); +} + +Reference< XBlob > SAL_CALL OCacheSet::getBlob( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getBlob(columnIndex); +} + +Reference< XClob > SAL_CALL OCacheSet::getClob( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getClob(columnIndex); +} + +Reference< XArray > SAL_CALL OCacheSet::getArray( sal_Int32 columnIndex ) +{ + return m_xDriverRow->getArray(columnIndex); +} + +// XResultSet +bool OCacheSet::next() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return m_xDriverSet->next(); +} + +void OCacheSet::beforeFirst( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_xDriverSet->beforeFirst(); +} + +void OCacheSet::afterLast( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_xDriverSet->afterLast(); +} + +bool OCacheSet::first() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return m_xDriverSet->first(); +} + +bool OCacheSet::last() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return m_xDriverSet->last(); +} + +sal_Int32 OCacheSet::getRow( ) +{ + return m_xDriverSet->getRow(); +} + +bool OCacheSet::absolute( sal_Int32 row ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return m_xDriverSet->absolute(row); +} + +bool OCacheSet::previous( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return m_xDriverSet->previous(); +} + +void OCacheSet::refreshRow( ) +{ + m_xDriverSet->refreshRow(); +} + +bool OCacheSet::rowUpdated( ) +{ + return m_xDriverSet->rowUpdated(); +} + +bool OCacheSet::rowInserted( ) +{ + return m_xDriverSet->rowInserted(); +} + +bool OCacheSet::rowDeleted( ) +{ + return m_xDriverSet->rowDeleted(); +} + +bool OCacheSet::isResultSetChanged() const +{ + return false; +} + +void OCacheSet::mergeColumnValues(sal_Int32 i_nColumnIndex,ORowSetValueVector::Vector& /*io_aInsertRow*/,ORowSetValueVector::Vector& /*io_aRow*/,std::vector<sal_Int32>& o_aChangedColumns) +{ + o_aChangedColumns.push_back(i_nColumnIndex); +} + +bool OCacheSet::columnValuesUpdated(ORowSetValueVector::Vector& /*io_aCachedRow*/,const ORowSetValueVector::Vector& /*io_aRow*/) +{ + return false; +} + +bool OCacheSet::updateColumnValues(const ORowSetValueVector::Vector& /*io_aCachedRow*/,ORowSetValueVector::Vector& /*io_aRow*/,const std::vector<sal_Int32>& /*i_aChangedColumns*/) +{ + return true; +} + +void OCacheSet::fillMissingValues(ORowSetValueVector::Vector& /*io_aRow*/) const +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/CacheSet.hxx b/dbaccess/source/core/api/CacheSet.hxx new file mode 100644 index 000000000..1006b9662 --- /dev/null +++ b/dbaccess/source/core/api/CacheSet.hxx @@ -0,0 +1,172 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include "RowSetRow.hxx" +#include <cppuhelper/implbase.hxx> + +#include <vector> + +namespace com::sun::star::sdbc{ class XParameters; } + +namespace dbaccess +{ + class OCacheSet : public ::cppu::WeakImplHelper< css::sdbc::XRow> + { + protected: + css::uno::Reference< css::sdbc::XResultSet> m_xDriverSet; + css::uno::Reference< css::sdbc::XRow> m_xDriverRow; + css::uno::Reference< css::sdbc::XResultSetMetaData> m_xSetMetaData; + css::uno::Reference< css::sdbc::XConnection> m_xConnection; + + std::vector<bool> m_aNullable; + std::vector<bool> m_aSignedFlags; + std::vector<sal_Int32> m_aColumnTypes; + OUString m_aComposedTableName; + sal_Int32 m_nMaxRows; + bool m_bInserted; + bool m_bUpdated; + bool m_bDeleted; + OUString m_sRowSetFilter; + + explicit OCacheSet(sal_Int32 i_nMaxRows); + virtual ~OCacheSet() override; + + static void setParameter(sal_Int32 nPos + ,const css::uno::Reference< css::sdbc::XParameters >& _xParameter + ,const connectivity::ORowSetValue& _rValue + ,sal_Int32 _nType + ,sal_Int32 _nScale + ); + void fillParameters( const ORowSetRow& _rRow + ,const connectivity::OSQLTable& _xTable + ,OUStringBuffer& _sCondition + ,OUStringBuffer& _sParameter + ,std::vector< sal_Int32>& _rOrgValues); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void fillTableName(const css::uno::Reference< css::beans::XPropertySet>& _xTable); + + OUString getIdentifierQuoteString() const; + public: + + // late constructor + virtual void construct(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter); + virtual void fillValueRow(ORowSetRow& _rRow,sal_Int32 _nPosition); + + // css::sdbc::XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + // css::sdbc::XResultSet + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool next(); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void beforeFirst( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void afterLast( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool first(); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool last(); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual sal_Int32 getRow( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool absolute( sal_Int32 row ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool previous( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void refreshRow( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool rowUpdated( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool rowInserted( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool rowDeleted( ); + // css::sdbcx::XRowLocate + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual css::uno::Any getBookmark() = 0; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool moveToBookmark( const css::uno::Any& bookmark ) = 0; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) = 0; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual bool hasOrderedBookmarks( ) = 0; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual sal_Int32 hashBookmark( const css::uno::Any& bookmark ) = 0; + // css::sdbc::XResultSetUpdate + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void updateRow( const ORowSetRow& _rInsertRow,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + virtual void deleteRow( const ORowSetRow& _rDeleteRow,const connectivity::OSQLTable& _xTable ) = 0; + + virtual bool isResultSetChanged() const; + virtual void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet) = 0; + virtual void mergeColumnValues(sal_Int32 i_nColumnIndex,ORowSetValueVector::Vector& io_aInsertRow,ORowSetValueVector::Vector& io_aRow,std::vector<sal_Int32>& o_aChangedColumns); + virtual bool columnValuesUpdated(ORowSetValueVector::Vector& o_aCachedRow,const ORowSetValueVector::Vector& i_aRow); + virtual bool updateColumnValues(const ORowSetValueVector::Vector& io_aCachedRow,ORowSetValueVector::Vector& io_aRow,const std::vector<sal_Int32>& i_aChangedColumns); + virtual void fillMissingValues(ORowSetValueVector::Vector& io_aRow) const; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/FilteredContainer.cxx b/dbaccess/source/core/api/FilteredContainer.cxx new file mode 100644 index 000000000..a5cf0b156 --- /dev/null +++ b/dbaccess/source/core/api/FilteredContainer.cxx @@ -0,0 +1,459 @@ +/* -*- 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 . + */ + +#include <strings.hxx> +#include <FilteredContainer.hxx> +#include <RefreshListener.hxx> +#include <sdbcoretools.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <tools/wldcrd.hxx> +#include <tools/diagnose_ex.h> +#include <optional> +#include <sal/log.hxx> + +namespace dbaccess +{ + using namespace dbtools; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::container; + using namespace ::osl; + using namespace ::comphelper; + using namespace ::cppu; + using namespace ::connectivity::sdbcx; + +/** creates a vector of WildCards and reduce the _rTableFilter of the length of WildCards +*/ +static sal_Int32 createWildCardVector(Sequence< OUString >& _rTableFilter, std::vector< WildCard >& _rOut) +{ + // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard + // for them + OUString* pTableFilters = _rTableFilter.getArray(); + OUString* pEnd = pTableFilters + _rTableFilter.getLength(); + sal_Int32 nShiftPos = 0; + for (sal_Int32 i=0; pEnd != pTableFilters; ++pTableFilters,++i) + { + if (pTableFilters->indexOf('%') != -1) + { + _rOut.emplace_back(pTableFilters->replace('%', '*')); + } + else + { + if (nShiftPos != i) + { + _rTableFilter.getArray()[nShiftPos] = _rTableFilter.getArray()[i]; + } + ++nShiftPos; + } + } + // now aTableFilter contains nShiftPos non-wc-strings and aWCSearch all wc-strings + _rTableFilter.realloc(nShiftPos); + return nShiftPos; +} + + static bool lcl_isElementAllowed( const OUString& _rName, + const Sequence< OUString >& _rTableFilter, + const std::vector< WildCard >& _rWCSearch ) + { + sal_Int32 nTableFilterLen = _rTableFilter.getLength(); + + const OUString* tableFilter = _rTableFilter.getConstArray(); + const OUString* tableFilterEnd = _rTableFilter.getConstArray() + nTableFilterLen; + bool bFilterMatch = std::find( tableFilter, tableFilterEnd, _rName ) != tableFilterEnd; + // the table is allowed to "pass" if we had no filters at all or any of the non-wildcard filters matches + if (!bFilterMatch && !_rWCSearch.empty()) + { // or if one of the wildcard expression matches + for (auto const& elem : _rWCSearch) + { + bFilterMatch = elem.Matches( _rName ); + if (bFilterMatch) + break; + } + } + + return bFilterMatch; + } + + typedef ::std::optional< OUString > OptionalString; + + namespace { + + struct TableInfo + { + OptionalString sComposedName; + OptionalString sType; + OptionalString sCatalog; + OptionalString sSchema; + OptionalString sName; + + explicit TableInfo( const OUString& _composedName ) + : sComposedName( _composedName ) + { + } + + TableInfo( const OUString& _catalog, const OUString& _schema, const OUString& _name, + const OUString& _type ) + :sType( _type ) + ,sCatalog( _catalog ) + ,sSchema( _schema ) + ,sName( _name ) + { + } + }; + + } + + typedef std::vector< TableInfo > TableInfos; + + static void lcl_ensureComposedName( TableInfo& _io_tableInfo, const Reference< XDatabaseMetaData >& _metaData ) + { + if ( !_metaData.is() ) + throw RuntimeException("lcl_ensureComposedName: _metaData cannot be null!"); + + if ( !_io_tableInfo.sComposedName ) + { + OSL_ENSURE( !!_io_tableInfo.sCatalog && !!_io_tableInfo.sSchema && !!_io_tableInfo.sName, "lcl_ensureComposedName: How should I composed the name from nothing!?" ); + + _io_tableInfo.sComposedName = OptionalString( + composeTableName( _metaData, *_io_tableInfo.sCatalog, *_io_tableInfo.sSchema, *_io_tableInfo.sName, + false, ::dbtools::EComposeRule::InDataManipulation ) + ); + } + } + + static void lcl_ensureType( TableInfo& _io_tableInfo, const Reference< XDatabaseMetaData >& _metaData, const Reference< XNameAccess >& _masterContainer ) + { + if ( !!_io_tableInfo.sType ) + return; + + lcl_ensureComposedName( _io_tableInfo, _metaData ); + + if ( !_masterContainer.is() ) + throw RuntimeException("lcl_ensureType: _masterContainer cannot be null!"); + + OUString sTypeName; + try + { + Reference< XPropertySet > xTable( _masterContainer->getByName( *_io_tableInfo.sComposedName ), UNO_QUERY_THROW ); + OSL_VERIFY( xTable->getPropertyValue( PROPERTY_TYPE ) >>= sTypeName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + _io_tableInfo.sType = OptionalString( sTypeName ); + } + + static ::std::vector< OUString> lcl_filter( TableInfos&& _unfilteredTables, + const Sequence< OUString >& _tableFilter, const Sequence< OUString >& _tableTypeFilter, + const Reference< XDatabaseMetaData >& _metaData, const Reference< XNameAccess >& _masterContainer ) + { + TableInfos aFilteredTables; + + // first, filter for the table names + sal_Int32 nTableFilterCount = _tableFilter.getLength(); + bool dontFilterTableNames = ( ( nTableFilterCount == 1 ) && _tableFilter[0] == "%" ); + if( dontFilterTableNames ) + { + aFilteredTables = std::move(_unfilteredTables); + } + else + { + // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard + // for them + std::vector< WildCard > aWildCardTableFilter; + Sequence< OUString > aNonWildCardTableFilter = _tableFilter; + nTableFilterCount = createWildCardVector( aNonWildCardTableFilter, aWildCardTableFilter ); + + TableInfos aUnfilteredTables( std::move(_unfilteredTables) ); + aUnfilteredTables.reserve( nTableFilterCount + ( aWildCardTableFilter.size() * 10 ) ); + + for (auto & unfilteredTable : aUnfilteredTables) + { + lcl_ensureComposedName(unfilteredTable, _metaData); + + if ( lcl_isElementAllowed( *unfilteredTable.sComposedName, aNonWildCardTableFilter, aWildCardTableFilter ) ) + aFilteredTables.push_back(unfilteredTable); + } + } + + // second, filter for the table types + sal_Int32 nTableTypeFilterCount = _tableTypeFilter.getLength(); + bool dontFilterTableTypes = ( ( nTableTypeFilterCount == 1 ) && _tableTypeFilter[0] == "%" ); + dontFilterTableTypes = dontFilterTableTypes || ( nTableTypeFilterCount == 0 ); + // (for TableTypeFilter, unlike TableFilter, "empty" means "do not filter at all") + if ( !dontFilterTableTypes ) + { + TableInfos aUnfilteredTables; + aUnfilteredTables.swap( aFilteredTables ); + + const OUString* pTableTypeFilterBegin = _tableTypeFilter.getConstArray(); + const OUString* pTableTypeFilterEnd = pTableTypeFilterBegin + _tableTypeFilter.getLength(); + + for (auto & unfilteredTable : aUnfilteredTables) + { + // ensure that we know the table type + lcl_ensureType( unfilteredTable, _metaData, _masterContainer ); + + if ( std::find( pTableTypeFilterBegin, pTableTypeFilterEnd, *unfilteredTable.sType ) != pTableTypeFilterEnd ) + aFilteredTables.push_back(unfilteredTable); + } + } + + ::std::vector< OUString> aReturn; + for (auto & filteredTable : aFilteredTables) + { + lcl_ensureComposedName(filteredTable, _metaData); + aReturn.push_back(*filteredTable.sComposedName); + } + return aReturn; + } + + // OViewContainer + OFilteredContainer::OFilteredContainer(::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const Reference< XConnection >& _xCon, + bool _bCase, + IRefreshListener* _pRefreshListener, + std::atomic<std::size_t>& _nInAppend) + :OCollection(_rParent,_bCase,_rMutex,std::vector< OUString>()) + ,m_bConstructed(false) + ,m_pRefreshListener(_pRefreshListener) + ,m_nInAppend(_nInAppend) + ,m_xConnection(_xCon) + { + } + + void OFilteredContainer::construct(const Reference< XNameAccess >& _rxMasterContainer, + const Sequence< OUString >& _rTableFilter, + const Sequence< OUString >& _rTableTypeFilter) + { + try + { + Reference<XConnection> xCon = m_xConnection; + if ( xCon.is() ) + m_xMetaData = xCon->getMetaData(); + } + catch(SQLException&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xMasterContainer = _rxMasterContainer; + + if ( m_xMasterContainer.is() ) + { + addMasterContainerListener(); + + TableInfos aUnfilteredTables; + + Sequence< OUString > aNames = m_xMasterContainer->getElementNames(); + const OUString* name = aNames.getConstArray(); + const OUString* nameEnd = name + aNames.getLength(); + for ( ; name != nameEnd; ++name ) + aUnfilteredTables.emplace_back( *name ); + + reFill( lcl_filter( std::move(aUnfilteredTables), + _rTableFilter, _rTableTypeFilter, m_xMetaData, m_xMasterContainer ) ); + + m_bConstructed = true; + } + else + { + construct( _rTableFilter, _rTableTypeFilter ); + } + } + + void OFilteredContainer::construct(const Sequence< OUString >& _rTableFilter, const Sequence< OUString >& _rTableTypeFilter) + { + // build sorted versions of the filter sequences, so the visibility decision is faster + Sequence< OUString > aTableFilter(_rTableFilter); + + // for wildcard search : remove all table filters which are a wildcard expression and build a WildCard + // for them + std::vector< WildCard > aWCSearch; + createWildCardVector(aTableFilter,aWCSearch); + + try + { + Reference< XConnection > xCon( m_xConnection, UNO_SET_THROW ); + m_xMetaData.set( xCon->getMetaData(), UNO_SET_THROW ); + + // create a table filter suitable for the XDatabaseMetaData::getTables call, + // taking into account both the externally-provided table type filter, and any + // table type restriction which is inherent to the container + Sequence< OUString > aTableTypeFilter; + OUString sInherentTableTypeRestriction( getTableTypeRestriction() ); + if ( !sInherentTableTypeRestriction.isEmpty() ) + { + if ( _rTableTypeFilter.hasElements() ) + { + const OUString* tableType = _rTableTypeFilter.getConstArray(); + const OUString* tableTypeEnd = tableType + _rTableTypeFilter.getLength(); + for ( ; tableType != tableTypeEnd; ++tableType ) + { + if ( *tableType == sInherentTableTypeRestriction ) + break; + } + if ( tableType == tableTypeEnd ) + { // the only table type which can be part of this container is not allowed + // by the externally provided table type filter. + m_bConstructed = true; + return; + } + } + aTableTypeFilter = { sInherentTableTypeRestriction }; + } + else + { + // no container-inherent restriction for the table types + if ( !_rTableTypeFilter.hasElements() ) + { // no externally-provided table type filter => use the default filter + getAllTableTypeFilter( aTableTypeFilter ); + } + else + { + aTableTypeFilter = _rTableTypeFilter; + } + } + + static constexpr OUStringLiteral sAll = u"%"; + Reference< XResultSet > xTables = m_xMetaData->getTables( Any(), sAll, sAll, aTableTypeFilter ); + Reference< XRow > xCurrentRow( xTables, UNO_QUERY_THROW ); + + TableInfos aUnfilteredTables; + + OUString sCatalog, sSchema, sName, sType; + while ( xTables->next() ) + { + sCatalog = xCurrentRow->getString(1); + sSchema = xCurrentRow->getString(2); + sName = xCurrentRow->getString(3); + sType = xCurrentRow->getString(4); + + aUnfilteredTables.emplace_back( sCatalog, sSchema, sName, sType ); + } + + reFill( lcl_filter( std::move(aUnfilteredTables), + _rTableFilter, aTableTypeFilter, m_xMetaData, nullptr ) ); + + disposeComponent( xTables ); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + disposing(); + return; + } + + m_bConstructed = true; + } + + void OFilteredContainer::disposing() + { + OCollection::disposing(); + + if ( m_xMasterContainer.is() ) + removeMasterContainerListener(); + + m_xMasterContainer = nullptr; + m_xMetaData = nullptr; + m_pRefreshListener = nullptr; + m_bConstructed = false; + } + + void OFilteredContainer::impl_refresh() + { + if ( m_pRefreshListener ) + { + m_bConstructed = false; + Reference<XRefreshable> xRefresh(m_xMasterContainer,UNO_QUERY); + if ( xRefresh.is() ) + xRefresh->refresh(); + m_pRefreshListener->refresh(this); + } + } + + OUString OFilteredContainer::getNameForObject(const ObjectType& _xObject) + { + OSL_ENSURE( _xObject.is(), "OFilteredContainer::getNameForObject: Object is NULL!" ); + return ::dbtools::composeTableName( m_xMetaData, _xObject, ::dbtools::EComposeRule::InDataManipulation, false ); + } + + // multiple to obtain all tables from XDatabaseMetaData::getTables, via passing a particular + // table type filter: + // adhere to the standard, which requests to pass a NULL table type filter, if + // you want to retrieve all tables + #define FILTER_MODE_STANDARD 0 + // only pass %, which is not allowed by the standard, but understood by some drivers + #define FILTER_MODE_WILDCARD 1 + // only pass TABLE and VIEW + #define FILTER_MODE_FIXED 2 + // do the thing which showed to be the safest way, understood by nearly all + // drivers, even the ones which do not understand the standard + #define FILTER_MODE_MIX_ALL 3 + + void OFilteredContainer::getAllTableTypeFilter( Sequence< OUString >& /* [out] */ _rFilter ) const + { + sal_Int32 nFilterMode = FILTER_MODE_MIX_ALL; + // for compatibility reasons, this is the default: we used this way before we + // introduced the TableTypeFilterMode setting + + // obtain the data source we belong to, and the TableTypeFilterMode setting + Any aFilterModeSetting; + if ( getDataSourceSetting( getDataSource( Reference< XInterface >(m_rParent) ), "TableTypeFilterMode", aFilterModeSetting ) ) + { + OSL_VERIFY( aFilterModeSetting >>= nFilterMode ); + } + + static const OUStringLiteral sAll( u"%" ); + static const OUStringLiteral sView( u"VIEW" ); + static const OUStringLiteral sTable( u"TABLE" ); + + switch ( nFilterMode ) + { + default: + SAL_WARN("dbaccess", "OTableContainer::getAllTableTypeFilter: unknown TableTypeFilterMode!" ); + [[fallthrough]]; + case FILTER_MODE_MIX_ALL: + _rFilter = { sView, sTable, sAll }; + break; + case FILTER_MODE_FIXED: + _rFilter = { sView, sTable }; + break; + case FILTER_MODE_WILDCARD: + _rFilter = { sAll }; + break; + case FILTER_MODE_STANDARD: + _rFilter.realloc( 0 ); + break; + } + } + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/HelperCollections.cxx b/dbaccess/source/core/api/HelperCollections.cxx new file mode 100644 index 000000000..dfc18badc --- /dev/null +++ b/dbaccess/source/core/api/HelperCollections.cxx @@ -0,0 +1,106 @@ +/* -*- 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 . + */ + +#include "HelperCollections.hxx" + +#include <strings.hxx> + +#include <osl/diagnose.h> + +namespace dbaccess +{ + using namespace dbtools; + using namespace comphelper; + using namespace connectivity; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::script; + using namespace ::cppu; + using namespace ::osl; + + OPrivateColumns::OPrivateColumns(const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns, + bool _bCase, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector, + bool _bUseAsIndex + ) : sdbcx::OCollection(_rParent,_bCase,_rMutex,_rVector,_bUseAsIndex) + ,m_aColumns(_rColumns) + { + } + + std::unique_ptr<OPrivateColumns> OPrivateColumns::createWithIntrinsicNames( const ::rtl::Reference< ::connectivity::OSQLColumns >& _rColumns, + bool _bCase, ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex ) + { + std::vector< OUString > aNames; aNames.reserve( _rColumns->size() ); + + OUString sColumName; + for (auto const& column : *_rColumns) + { + Reference< XPropertySet > xColumn(column, UNO_SET_THROW); + xColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumName; + aNames.push_back( sColumName ); + } + return std::unique_ptr<OPrivateColumns>(new OPrivateColumns( _rColumns, _bCase, _rParent, _rMutex, aNames, false )); + } + + void OPrivateColumns::disposing() + { + m_aColumns = nullptr; + clear_NoDispose(); + // we're not owner of the objects we're holding, instead the object we got in our ctor is + // So we're not allowed to dispose our elements. + OPrivateColumns_Base::disposing(); + } + + connectivity::sdbcx::ObjectType OPrivateColumns::createObject(const OUString& _rName) + { + if ( m_aColumns.is() ) + { + ::connectivity::OSQLColumns::Vector::const_iterator aIter = find(m_aColumns->begin(),m_aColumns->end(),_rName,isCaseSensitive()); + if(aIter == m_aColumns->end()) + aIter = findRealName(m_aColumns->begin(),m_aColumns->end(),_rName,isCaseSensitive()); + + if(aIter != m_aColumns->end()) + return connectivity::sdbcx::ObjectType(*aIter,UNO_QUERY); + + OSL_FAIL("Column not found in collection!"); + } + return nullptr; + } + + connectivity::sdbcx::ObjectType OPrivateTables::createObject(const OUString& _rName) + { + if ( !m_aTables.empty() ) + { + OSQLTables::iterator aIter = m_aTables.find(_rName); + OSL_ENSURE(aIter != m_aTables.end(),"Table not found!"); + OSL_ENSURE(aIter->second.is(),"Table is null!"); + return connectivity::sdbcx::ObjectType(m_aTables.find(_rName)->second,UNO_QUERY); + } + return nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/HelperCollections.hxx b/dbaccess/source/core/api/HelperCollections.hxx new file mode 100644 index 000000000..ed89fc643 --- /dev/null +++ b/dbaccess/source/core/api/HelperCollections.hxx @@ -0,0 +1,106 @@ +/* -*- 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 . + */ +#pragma once + +#include <connectivity/sdbcx/VCollection.hxx> +#include <connectivity/dbconversion.hxx> +#include <rtl/ref.hxx> + +namespace dbaccess +{ + using namespace dbtools; + using namespace comphelper; + using namespace connectivity; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::script; + using namespace ::cppu; + using namespace ::osl; + + typedef connectivity::sdbcx::OCollection OPrivateColumns_Base; + class OPrivateColumns : public OPrivateColumns_Base + { + ::rtl::Reference< ::connectivity::OSQLColumns> m_aColumns; + protected: + virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override {} + virtual Reference< XPropertySet > createDescriptor() override + { + return nullptr; + } + public: + OPrivateColumns(const ::rtl::Reference< ::connectivity::OSQLColumns>& _rColumns, + bool _bCase, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const std::vector< OUString> &_rVector, + bool _bUseAsIndex = false + ); + + /** creates a columns instance as above, but taking the names from the columns itself + */ + static std::unique_ptr<OPrivateColumns> createWithIntrinsicNames( + const ::rtl::Reference< ::connectivity::OSQLColumns >& _rColumns, + bool _bCase, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex + ); + + virtual void disposing() override; + }; + typedef connectivity::sdbcx::OCollection OPrivateTables_BASE; + + // OPrivateTables + class OPrivateTables : public OPrivateTables_BASE + { + OSQLTables m_aTables; + protected: + virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override; + virtual void impl_refresh() override {} + virtual Reference< XPropertySet > createDescriptor() override + { + return nullptr; + } + public: + OPrivateTables( const OSQLTables& _rTables, + bool _bCase, + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + std::vector< OUString> && _rVector + ) : sdbcx::OCollection(_rParent,_bCase,_rMutex,_rVector) + ,m_aTables(std::move(_rTables)) + { + } + virtual void disposing() override + { + clear_NoDispose(); + // we're not owner of the objects we're holding, instead the object we got in our ctor is + // So we're not allowed to dispose our elements. + m_aTables.clear(); + OPrivateTables_BASE::disposing(); + } + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/KeySet.cxx b/dbaccess/source/core/api/KeySet.cxx new file mode 100644 index 000000000..338d4d7f8 --- /dev/null +++ b/dbaccess/source/core/api/KeySet.cxx @@ -0,0 +1,1463 @@ +/* -*- 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 . + */ + +#include <memory> +#include <string_view> + +#include "KeySet.hxx" +#include <sal/log.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <algorithm> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <composertools.hxx> +#include "PrivateRow.hxx" + +using namespace dbaccess; +using namespace ::connectivity; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::osl; +using std::vector; + +namespace +{ + void lcl_fillIndexColumns(const Reference<XIndexAccess>& _xIndexes, std::vector< Reference<XNameAccess> >& _rAllIndexColumns) + { + if ( !_xIndexes.is() ) + return; + + Reference<XPropertySet> xIndexColsSup; + sal_Int32 nCount = _xIndexes->getCount(); + for(sal_Int32 j = 0 ; j < nCount ; ++j) + { + xIndexColsSup.set(_xIndexes->getByIndex(j),UNO_QUERY); + if( xIndexColsSup.is() + && comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISUNIQUE)) + && !comphelper::getBOOL(xIndexColsSup->getPropertyValue(PROPERTY_ISPRIMARYKEYINDEX)) + ) + _rAllIndexColumns.push_back(Reference<XColumnsSupplier>(xIndexColsSup,UNO_QUERY_THROW)->getColumns()); + } + } + + template < typename T > void tryDispose( Reference<T> &r ) + { + try + { + ::comphelper::disposeComponent(r); + } + catch(const Exception&) + { + r = nullptr; + } + catch(...) + { + SAL_WARN("dbaccess", "Unknown Exception occurred"); + } + } +} + + +OKeySet::OKeySet(const connectivity::OSQLTable& _xTable, + const OUString& _rUpdateTableName, // 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(_xTable) + ,m_xComposer(_xComposer) + ,m_sUpdateTableName(_rUpdateTableName) + ,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(bCase) ); + m_pColumnNames.reset( new SelectColumnsMetaData(bCase) ); + m_pParameterNames.reset( new SelectColumnsMetaData(bCase) ); + m_pForeignColumnNames.reset( new SelectColumnsMetaData(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(); + + const Reference<XColumnsSupplier> xTblColSup(i_aTable,UNO_QUERY_THROW); + const Reference<XNameAccess> xTblColumns = xTblColSup->getColumns(); + // locate parameter in select columns + Reference<XParametersSupplier> xParaSup(m_xComposer,UNO_QUERY); + Reference<XIndexAccess> xQueryParameters = xParaSup->getParameters(); + const sal_Int32 nParaCount = xQueryParameters->getCount(); + Sequence< OUString> aParameterColumns(nParaCount); + auto aParameterColumnsRange = asNonConstRange(aParameterColumns); + for(sal_Int32 i = 0; i< nParaCount;++i) + { + Reference<XPropertySet> xPara(xQueryParameters->getByIndex(i),UNO_QUERY_THROW); + xPara->getPropertyValue(PROPERTY_REALNAME) >>= aParameterColumnsRange[i]; + } + + 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. + + OUString sCatalog, sSchema, sTable; + Reference<XPropertySet> xTableProp( i_aTable, UNO_QUERY_THROW ); + xTableProp->getPropertyValue( PROPERTY_CATALOGNAME )>>= sCatalog; + xTableProp->getPropertyValue( PROPERTY_SCHEMANAME ) >>= sSchema; + xTableProp->getPropertyValue( PROPERTY_NAME ) >>= sTable; + sUpdateTableName = dbtools::composeTableName( i_xMeta, sCatalog, sSchema, sTable, false, ::dbtools::EComposeRule::InDataManipulation ); + } + + ::dbaccess::getColumnPositions(i_xQueryColumns,aBestColumnNames,sUpdateTableName,(*o_pKeyColumnNames),true); + ::dbaccess::getColumnPositions(i_xQueryColumns,xTblColumns->getElementNames(),sUpdateTableName,(*m_pColumnNames),true); + ::dbaccess::getColumnPositions(i_xQueryColumns,aParameterColumns,sUpdateTableName,(*m_pParameterNames),true); + + if ( o_pKeyColumnNames->empty() ) + { + ::dbtools::throwGenericSQLException("Could not find any key column.", *this ); + } + + for (auto const& keyColumn : *o_pKeyColumnNames) + { + if ( !xTblColumns->hasByName( keyColumn.second.sRealName ) ) + continue; + + Reference<XPropertySet> xProp( xTblColumns->getByName( keyColumn.second.sRealName ), UNO_QUERY ); + bool bAuto = false; + if ( ( xProp->getPropertyValue( PROPERTY_ISAUTOINCREMENT ) >>= bAuto ) && bAuto ) + m_aAutoColumns.push_back( keyColumn.first ); + } +} + +namespace +{ + void appendOneKeyColumnClause( std::u16string_view tblName, const OUString &colName, const connectivity::ORowSetValue &_rValue, OUStringBuffer &o_buf ) + { + OUString fullName; + if (tblName.empty()) + fullName = colName; + else + fullName = OUString::Concat(tblName) + "." + colName; + if ( _rValue.isNull() ) + { + o_buf.append(fullName + " IS NULL "); + } + else + { + o_buf.append(fullName + " = ? "); + } + } +} + +void OKeySet::setOneKeyColumnParameter( sal_Int32 &nPos, const Reference< XParameters > &_xParameter, const connectivity::ORowSetValue &_rValue, sal_Int32 _nType, sal_Int32 _nScale ) +{ + if ( _rValue.isNull() ) + { + // Nothing to do, appendOneKeyColumnClause took care of it, + // the "IS NULL" is hardcoded in the query + } + else + { + setParameter( nPos++, _xParameter, _rValue, _nType, _nScale ); + } +} + +OUStringBuffer OKeySet::createKeyFilter() +{ + connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aIter = m_aKeyIter->second.first->begin(); + + static const char aAnd[] = " AND "; + const OUString aQuote = getIdentifierQuoteString(); + OUStringBuffer aFilter; + // create the where clause + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + if ( ! aFilter.isEmpty() ) + aFilter.append(aAnd); + appendOneKeyColumnClause(::dbtools::quoteTableName(xMeta, keyColumnName.second.sTableName, ::dbtools::EComposeRule::InDataManipulation), + ::dbtools::quoteName(aQuote, keyColumnName.second.sRealName), + *aIter++, + aFilter); + } + for (auto const& foreignColumnName : * m_pForeignColumnNames) + { + if ( ! aFilter.isEmpty() ) + aFilter.append(aAnd); + appendOneKeyColumnClause(::dbtools::quoteTableName(xMeta, foreignColumnName.second.sTableName, ::dbtools::EComposeRule::InDataManipulation), + ::dbtools::quoteName(aQuote, foreignColumnName.second.sRealName), + *aIter++, + aFilter); + } + return aFilter; +} + +void OKeySet::construct(const Reference< XResultSet>& _xDriverSet, const OUString& i_sRowSetFilter) +{ + OCacheSet::construct(_xDriverSet,i_sRowSetFilter); + + initColumns(); + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + Reference<XColumnsSupplier> xQueryColSup(m_xComposer, UNO_QUERY); + const Reference<XNameAccess> xQueryColumns = xQueryColSup->getColumns(); + findTableColumnsMatching_throw( Any(m_xTable), m_sUpdateTableName, xMeta, xQueryColumns, m_pKeyColumnNames ); + + Reference< XSingleSelectQueryComposer> xSourceComposer(m_xComposer,UNO_QUERY); + Reference< XMultiServiceFactory > xFactory(m_xConnection, UNO_QUERY_THROW); + Reference<XSingleSelectQueryComposer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY); + xAnalyzer->setElementaryQuery(xSourceComposer->getElementaryQuery()); + Reference<XTablesSupplier> xTabSup(xAnalyzer,uno::UNO_QUERY); + Reference<XNameAccess> xSelectTables = xTabSup->getTables(); + const Sequence< OUString> aSeq = xSelectTables->getElementNames(); + if ( aSeq.getLength() > 1 ) // special handling for join + { + const OUString* pIter = aSeq.getConstArray(); + const OUString* const pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( *pIter != m_sUpdateTableName ) + { + connectivity::OSQLTable xSelColSup(xSelectTables->getByName(*pIter),uno::UNO_QUERY); + Reference<XPropertySet> xProp(xSelColSup,uno::UNO_QUERY); + OUString sSelectTableName = ::dbtools::composeTableName( xMeta, xProp, ::dbtools::EComposeRule::InDataManipulation, false ); + + ::dbaccess::getColumnPositions(xQueryColumns, xSelColSup->getColumns()->getElementNames(), sSelectTableName, (*m_pForeignColumnNames), true); + + // 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,std::pair<sal_Int32,Reference<XRow> >(0,Reference<XRow>())); + m_aKeyMap.emplace(0, keySetValue); + m_aKeyIter = m_aKeyMap.begin(); +} + +void OKeySet::reset(const Reference< XResultSet>& _xDriverSet) +{ + OCacheSet::construct(_xDriverSet, m_sRowSetFilter); + m_bRowCountFinal = false; + m_aKeyMap.clear(); + OKeySetValue keySetValue(nullptr,std::pair<sal_Int32,Reference<XRow> >(0,Reference<XRow>())); + 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.first->size()); + for (auto const& elem : *m_aKeyIter->second.first) + 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; +} + +void OKeySet::makeNewStatement() +{ + Reference< XSingleSelectQueryComposer> xSourceComposer(m_xComposer,UNO_QUERY); + Reference< XMultiServiceFactory > xFactory(m_xConnection, UNO_QUERY_THROW); + Reference<XSingleSelectQueryComposer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY); + xAnalyzer->setElementaryQuery(xSourceComposer->getElementaryQuery()); + + OUStringBuffer aFilter(createKeyFilter()); + executeStatement(aFilter, xAnalyzer); +} + +void OKeySet::executeStatement(OUStringBuffer& io_aFilter, Reference<XSingleSelectQueryComposer>& io_xAnalyzer) +{ + bool bFilterSet = !m_sRowSetFilter.isEmpty(); + if ( bFilterSet ) + { + FilterCreator aFilterCreator; + aFilterCreator.append( m_sRowSetFilter ); + aFilterCreator.append( io_aFilter.makeStringAndClear() ); + io_aFilter = aFilterCreator.getComposedAndClear(); + } + io_xAnalyzer->setFilter(io_aFilter.makeStringAndClear()); + if ( bFilterSet ) + { + Sequence< Sequence< PropertyValue > > aFilter2 = io_xAnalyzer->getStructuredFilter(); + const Sequence< PropertyValue >* pOr = aFilter2.getConstArray(); + const Sequence< PropertyValue >* pOrEnd = pOr + aFilter2.getLength(); + for(;pOr != pOrEnd;++pOr) + { + const PropertyValue* pAnd = pOr->getConstArray(); + const PropertyValue* pAndEnd = pAnd + pOr->getLength(); + for(;pAnd != pAndEnd;++pAnd) + { + OUString sValue; + if ( !(pAnd->Value >>= sValue) || !( sValue == "?" || sValue.startsWith( ":" ) ) ) + { // we have a criteria which has to be taken into account for updates + m_aFilterColumns.push_back(pAnd->Name); + } + } + } + } + m_xStatement = m_xConnection->prepareStatement(io_xAnalyzer->getQueryWithSubstitution()); + ::comphelper::disposeComponent(io_xAnalyzer); +} + +void OKeySet::invalidateRow() +{ + m_xRow = nullptr; + ::comphelper::disposeComponent(m_xSet); +} + +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); +} + +bool OKeySet::moveToBookmark( const Any& bookmark ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_aKeyIter = m_aKeyMap.find(::comphelper::getINT32(bookmark)); + invalidateRow(); + return m_aKeyIter != m_aKeyMap.end(); +} + +sal_Int32 OKeySet::compareBookmarks( const Any& _first, const Any& _second ) +{ + sal_Int32 nFirst = 0, nSecond = 0; + _first >>= nFirst; + _second >>= nSecond; + + return (nFirst != nSecond) ? CompareBookmark::NOT_EQUAL : CompareBookmark::EQUAL; +} + +bool OKeySet::hasOrderedBookmarks( ) +{ + return true; +} + +sal_Int32 OKeySet::hashBookmark( const Any& bookmark ) +{ + return ::comphelper::getINT32(bookmark); +} + + +void OKeySet::updateRow(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql = "UPDATE " + m_aComposedTableName + " SET "; + // list all columns that should be set + constexpr OUStringLiteral aPara(u" = ?,"); + OUString aQuote = getIdentifierQuoteString(); + constexpr OUStringLiteral aAnd(u" AND "); + OUString sIsNull(" IS NULL"); + OUString sParam(" = ?"); + + // 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); + + std::vector< Reference<XNameAccess> > aAllIndexColumns; + lcl_fillIndexColumns(xIndexes,aAllIndexColumns); + + OUStringBuffer sKeyCondition,sIndexCondition; + std::vector<sal_Int32> aIndexColumnPositions; + + const sal_Int32 nOldLength = aSql.getLength(); + // here we build the condition part for the update statement + for (auto const& columnName : *m_pColumnNames) + { + if ( m_pKeyColumnNames->find(columnName.first) != m_pKeyColumnNames->end() ) + { + sKeyCondition.append(::dbtools::quoteName( aQuote,columnName.second.sRealName)); + if((*_rOriginalRow)[columnName.second.nPosition].isNull()) + sKeyCondition.append(sIsNull); + else + sKeyCondition.append(sParam); + sKeyCondition.append(aAnd); + } + else + { + for (auto const& indexColumn : aAllIndexColumns) + { + if(indexColumn->hasByName(columnName.first)) + { + sIndexCondition.append(::dbtools::quoteName( aQuote,columnName.second.sRealName)); + if((*_rOriginalRow)[columnName.second.nPosition].isNull()) + sIndexCondition.append(sIsNull); + else + { + sIndexCondition.append(sParam); + aIndexColumnPositions.push_back(columnName.second.nPosition); + } + sIndexCondition.append(aAnd); + break; + } + } + } + if((*_rInsertRow)[columnName.second.nPosition].isModified()) + { + aSql.append(::dbtools::quoteName( aQuote,columnName.second.sRealName) + aPara); + } + } + + if( aSql.getLength() != nOldLength ) + { + aSql.setLength(aSql.getLength()-1); + } + else + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_VALUE_CHANGED ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + if(!sKeyCondition.isEmpty() || !sIndexCondition.isEmpty()) + { + aSql.append(" WHERE "); + if(!sKeyCondition.isEmpty() && !sIndexCondition.isEmpty()) + { + aSql.append(sKeyCondition + sIndexCondition); + } + else if(!sKeyCondition.isEmpty()) + { + aSql.append(sKeyCondition); + } + else if(!sIndexCondition.isEmpty()) + { + aSql.append(sIndexCondition); + } + aSql.setLength(aSql.getLength()-5); // remove the last AND + } + else + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_CONDITION_FOR_PK ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + // now create end execute the prepared statement + executeUpdate(_rInsertRow ,_rOriginalRow,aSql.makeStringAndClear(),u"",aIndexColumnPositions); +} + +void OKeySet::executeUpdate(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const OUString& i_sSQL,std::u16string_view i_sTableName,const std::vector<sal_Int32>& _aIndexColumnPositions) +{ + // now create end execute the prepared statement + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(i_sSQL)); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + + bool bRefetch = true; + Reference<XRow> xRow; + sal_Int32 i = 1; + // first the set values + for (auto const& columnName : *m_pColumnNames) + { + if ( i_sTableName.empty() || columnName.second.sTableName == i_sTableName ) + { + sal_Int32 nPos = columnName.second.nPosition; + if((*_rInsertRow)[nPos].isModified()) + { + if ( bRefetch ) + { + bRefetch = std::find(m_aFilterColumns.begin(),m_aFilterColumns.end(),columnName.second.sRealName) == m_aFilterColumns.end(); + } + impl_convertValue_throw(_rInsertRow,columnName.second); + (*_rInsertRow)[nPos].setSigned((*_rOriginalRow)[nPos].isSigned()); + setParameter(i++,xParameter,(*_rInsertRow)[nPos],columnName.second.nType,columnName.second.nScale); + } + } + } + // and then the values of the where condition + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + if ( i_sTableName.empty() || keyColumnName.second.sTableName == i_sTableName ) + { + setParameter(i++,xParameter,(*_rOriginalRow)[keyColumnName.second.nPosition],keyColumnName.second.nType,keyColumnName.second.nScale); + } + } + if ( !_aIndexColumnPositions.empty() ) + { + // now we have to set the index values + auto aIter = m_pColumnNames->begin(); + for (auto const& indexColumnPosition : _aIndexColumnPositions) + { + setParameter(i++,xParameter,(*_rOriginalRow)[indexColumnPosition],(*_rOriginalRow)[indexColumnPosition].getTypeKind(),aIter->second.nScale); + ++aIter; + } + } + const sal_Int32 nRowsUpdated = xPrep->executeUpdate(); + m_bUpdated = nRowsUpdated > 0; + if(m_bUpdated) + { + const sal_Int32 nBookmark = ::comphelper::getINT32((*_rInsertRow)[0].getAny()); + m_aKeyIter = m_aKeyMap.find(nBookmark); + m_aKeyIter->second.second.first = 2; + m_aKeyIter->second.second.second = xRow; + copyRowValue(_rInsertRow,m_aKeyIter->second.first,nBookmark); + tryRefetch(_rInsertRow,bRefetch); + } +} + +void OKeySet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql( "INSERT INTO " + m_aComposedTableName + " ( "); + + // set values and column names + OUStringBuffer aValues(" VALUES ( "); + OUString aQuote = getIdentifierQuoteString(); + + bool bRefetch = true; + bool bModified = false; + for (auto const& columnName : *m_pColumnNames) + { + if((*_rInsertRow)[columnName.second.nPosition].isModified()) + { + if ( bRefetch ) + { + bRefetch = std::find(m_aFilterColumns.begin(),m_aFilterColumns.end(),columnName.second.sRealName) == m_aFilterColumns.end(); + } + aSql.append(::dbtools::quoteName( aQuote,columnName.second.sRealName) + ","); + aValues.append("?,"); + bModified = true; + } + } + if ( !bModified ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_VALUE_CHANGED ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + aSql[aSql.getLength() - 1] = ')'; + aValues[aValues.getLength() - 1] = ')'; + aSql.append(aValues); + // now create,fill and execute the prepared statement + executeInsert(_rInsertRow,aSql.makeStringAndClear(),u"",bRefetch); +} + +void OKeySet::executeInsert( const ORowSetRow& _rInsertRow,const OUString& i_sSQL,std::u16string_view i_sTableName,bool bRefetch ) +{ + // now create,fill and execute the prepared statement + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(i_sSQL)); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + + sal_Int32 i = 1; + for (auto const& columnName : *m_pColumnNames) + { + if ( i_sTableName.empty() || columnName.second.sTableName == i_sTableName ) + { + const sal_Int32 nPos = columnName.second.nPosition; + if((*_rInsertRow)[nPos].isModified()) + { + if((*_rInsertRow)[nPos].isNull()) + xParameter->setNull(i++,(*_rInsertRow)[nPos].getTypeKind()); + else + { + impl_convertValue_throw(_rInsertRow,columnName.second); + (*_rInsertRow)[nPos].setSigned(m_aSignedFlags[nPos-1]); + setParameter(i++,xParameter,(*_rInsertRow)[nPos],columnName.second.nType,columnName.second.nScale); + } + } + } + } + + m_bInserted = xPrep->executeUpdate() > 0; + bool bAutoValuesFetched = false; + if ( m_bInserted ) + { + // first insert the default values into the insertrow + for (auto const& columnName : *m_pColumnNames) + { + if ( !(*_rInsertRow)[columnName.second.nPosition].isModified() ) + { + if(columnName.second.bNullable && columnName.second.sDefaultValue.isEmpty()) + { + (*_rInsertRow)[columnName.second.nPosition].setTypeKind(columnName.second.nType); + (*_rInsertRow)[columnName.second.nPosition].setNull(); + } + else + { + (*_rInsertRow)[columnName.second.nPosition] = columnName.second.sDefaultValue; + (*_rInsertRow)[columnName.second.nPosition].setTypeKind(columnName.second.nType); + } + } + } + try + { + Reference< XGeneratedResultSet > xGRes(xPrep, UNO_QUERY); + if ( xGRes.is() ) + { + Reference< XResultSet > xRes = xGRes->getGeneratedValues(); + Reference< XRow > xRow(xRes,UNO_QUERY); + if ( xRow.is() && xRes->next() ) + { + Reference< XResultSetMetaDataSupplier > xMdSup(xRes,UNO_QUERY); + Reference< XResultSetMetaData > xMd = xMdSup->getMetaData(); + sal_Int32 nColumnCount = xMd->getColumnCount(); + std::vector< OUString >::const_iterator aAutoIter = m_aAutoColumns.begin(); + std::vector< OUString >::const_iterator aAutoEnd = m_aAutoColumns.end(); + for (sal_Int32 j = 1;aAutoIter != aAutoEnd && j <= nColumnCount; ++aAutoIter,++j) + { + SelectColumnsMetaData::const_iterator aFind = m_pKeyColumnNames->find(*aAutoIter); + if ( aFind != m_pKeyColumnNames->end() ) + (*_rInsertRow)[aFind->second.nPosition].fill(j, aFind->second.nType, xRow); + } + bAutoValuesFetched = true; + } + } + } + catch(const Exception&) + { + SAL_WARN("dbaccess", "Could not execute GeneratedKeys() stmt"); + } + } + + ::comphelper::disposeComponent(xPrep); + + 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 (auto const& 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 (auto const& 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,std::pair<sal_Int32,Reference<XRow> >(1,Reference<XRow>())) ).first; + // now we set the bookmark for this row + (*_rInsertRow)[0] = Any(static_cast<sal_Int32>(m_aKeyIter->first)); + tryRefetch(_rInsertRow,bRefetch); + } +} + +void OKeySet::tryRefetch(const ORowSetRow& _rInsertRow,bool bRefetch) +{ + if ( bRefetch ) + { + try + { + bRefetch = doTryRefetch_throw(); + } + catch(const Exception&) + { + bRefetch = false; + } + } + if ( !bRefetch ) + { + m_aKeyIter->second.second.second = new OPrivateRow(std::vector(*_rInsertRow)); + } +} + +void OKeySet::copyRowValue(const ORowSetRow& _rInsertRow, ORowSetRow const & _rKeyRow, sal_Int32 i_nBookmark) +{ + connectivity::ORowVector< ORowSetValue >::Vector::iterator aIter = _rKeyRow->begin(); + + // 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 (auto const& 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] = aCopy; + bChanged = true; + } + ++aParaValuesIter; + ++i; + } + if ( !bChanged ) + { + m_aUpdatedParameter.erase(i_nBookmark); + } + + // update the key values + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + impl_convertValue_throw(_rInsertRow,keyColumnName.second); + *aIter = (*_rInsertRow)[keyColumnName.second.nPosition]; + aIter->setTypeKind(keyColumnName.second.nType); + ++aIter; + } +} + +void OKeySet::deleteRow(const ORowSetRow& _rDeleteRow,const connectivity::OSQLTable& _xTable ) +{ + Reference<XPropertySet> xSet(_xTable,UNO_QUERY); + fillTableName(xSet); + + OUStringBuffer aSql("DELETE FROM " + m_aComposedTableName + " WHERE "); + + // list all columns that should be set + OUString aQuote = getIdentifierQuoteString(); + static const char 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); + + // Reference<XColumnsSupplier> + std::vector< Reference<XNameAccess> > aAllIndexColumns; + lcl_fillIndexColumns(xIndexes,aAllIndexColumns); + + OUStringBuffer sIndexCondition; + std::vector<sal_Int32> aIndexColumnPositions; + + for (auto const& columnName : *m_pColumnNames) + { + if ( m_pKeyColumnNames->find(columnName.first) != m_pKeyColumnNames->end() ) + { + aSql.append(::dbtools::quoteName( aQuote,columnName.second.sRealName)); + if((*_rDeleteRow)[columnName.second.nPosition].isNull()) + { + SAL_WARN("dbaccess", "can a primary key be null"); + aSql.append(" IS NULL"); + } + else + aSql.append(" = ?"); + aSql.append(aAnd); + } + else + { + for (auto const& indexColumn : aAllIndexColumns) + { + if(indexColumn->hasByName(columnName.first)) + { + sIndexCondition.append(::dbtools::quoteName( aQuote,columnName.second.sRealName)); + if((*_rDeleteRow)[columnName.second.nPosition].isNull()) + sIndexCondition.append(" IS NULL"); + else + { + sIndexCondition.append(" = ?"); + aIndexColumnPositions.push_back(columnName.second.nPosition); + } + sIndexCondition.append(aAnd); + + break; + } + } + } + } + aSql.append(sIndexCondition); + aSql.setLength(aSql.getLength()-5); + + // 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 (auto const& 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 (auto const& indexColumnPosition : aIndexColumnPositions) + { + setParameter(i++,xParameter,(*_rDeleteRow)[indexColumnPosition],(*_rDeleteRow)[indexColumnPosition].getTypeKind(),aIter->second.nScale); + ++aIter; + } + + m_bDeleted = xPrep->executeUpdate() > 0; + + if(m_bDeleted) + { + sal_Int32 nBookmark = ::comphelper::getINT32((*_rDeleteRow)[0].getAny()); + if(m_aKeyIter == m_aKeyMap.find(nBookmark) && m_aKeyIter != m_aKeyMap.end()) + ++m_aKeyIter; + m_aKeyMap.erase(nBookmark); + m_bDeleted = true; + } +} + +bool OKeySet::next() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + + if(isAfterLast()) + return false; + ++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()"); + return true; + } + else + { + // nope, we arrived at end of data + m_aKeyIter = m_aKeyMap.end(); + OSL_ENSURE(isAfterLast(), "fetchRow failed, but not end of data"); + } + } + + invalidateRow(); + return !isAfterLast(); +} + +bool OKeySet::isBeforeFirst( ) +{ + return m_aKeyIter == m_aKeyMap.begin(); +} + +bool OKeySet::isAfterLast( ) +{ + return m_bRowCountFinal && m_aKeyIter == m_aKeyMap.end(); +} + +void OKeySet::beforeFirst( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_aKeyIter = m_aKeyMap.begin(); + invalidateRow(); +} + +void OKeySet::afterLast( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + fillAllRows(); + m_aKeyIter = m_aKeyMap.end(); + invalidateRow(); +} + +bool OKeySet::first() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_aKeyIter = m_aKeyMap.begin(); + ++m_aKeyIter; + if(m_aKeyIter == m_aKeyMap.end()) + { + if (!fetchRow()) + { + m_aKeyIter = m_aKeyMap.end(); + return false; + } + } + else + invalidateRow(); + return m_aKeyIter != m_aKeyMap.end() && m_aKeyIter != m_aKeyMap.begin(); +} + +bool OKeySet::last( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + bool bFetchedRow = fillAllRows(); + + m_aKeyIter = m_aKeyMap.end(); + --m_aKeyIter; + if ( !bFetchedRow ) + { + invalidateRow(); + } + return m_aKeyIter != m_aKeyMap.end() && m_aKeyIter != m_aKeyMap.begin(); +} + +sal_Int32 OKeySet::getRow( ) +{ + OSL_ENSURE(!isAfterLast(),"getRow is not allowed when afterlast record!"); + OSL_ENSURE(!isBeforeFirst(),"getRow is not allowed when beforefirst record!"); + + return std::distance(m_aKeyMap.begin(),m_aKeyIter); +} + +bool OKeySet::absolute( sal_Int32 row ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + OSL_ENSURE(row,"absolute(0) isn't allowed!"); + bool bFetchedRow = false; + if(row < 0) + { + if(!m_bRowCountFinal) + bFetchedRow = fillAllRows(); + + 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(); + return false; + } + } + else + { + // no more rows to fetch -> fail + m_aKeyIter = m_aKeyMap.end(); + return false; + } + } + else + { + m_aKeyIter = std::next(m_aKeyMap.begin(), row); + } + } + if ( !bFetchedRow ) + { + invalidateRow(); + } + + return m_aKeyIter != m_aKeyMap.end() && m_aKeyIter != m_aKeyMap.begin(); +} + +bool OKeySet::previous() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + if(m_aKeyIter != m_aKeyMap.begin()) + { + --m_aKeyIter; + invalidateRow(); + } + return m_aKeyIter != m_aKeyMap.begin(); +} + +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(); + + sal_Int32 nPos=1; + connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aParaIter; + connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aParaEnd; + OUpdatedParameter::const_iterator aUpdateFind = m_aUpdatedParameter.find(m_aKeyIter->first); + if ( aUpdateFind == m_aUpdatedParameter.end() ) + { + aParaIter = m_aParameterValueForCache->begin(); + aParaEnd = m_aParameterValueForCache->end(); + } + else + { + aParaIter = aUpdateFind->second->begin(); + aParaEnd = aUpdateFind->second->end(); + } + + for(++aParaIter;aParaIter != aParaEnd;++aParaIter,++nPos) + { + ::dbtools::setObjectWithInfo( xParameter, nPos, aParaIter->makeAny(), aParaIter->getTypeKind() ); + } + + // now set the primary key column values + connectivity::ORowVector< ORowSetValue >::Vector::const_iterator aIter = m_aKeyIter->second.first->begin(); + for (auto const& keyColumnName : *m_pKeyColumnNames) + setOneKeyColumnParameter(nPos,xParameter,*aIter++,keyColumnName.second.nType,keyColumnName.second.nScale); + for (auto const& 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.second.second.is() ) + { + m_xRow = m_aKeyIter->second.second.second; + 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()); + + ::comphelper::disposeComponent(m_xSet); + m_xRow.set(m_xDriverRow, UNO_SET_THROW); + + connectivity::ORowVector< ORowSetValue >::Vector::iterator aIter = aKeyRow->begin(); + // copy key columns + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + const SelectColumnDescription& rColDesc = keyColumnName.second; + aIter->fill(rColDesc.nPosition, rColDesc.nType, m_xRow); + ++aIter; + } + // copy missing columns from other tables + for (auto const& foreignColumnName : *m_pForeignColumnNames) + { + const SelectColumnDescription& rColDesc = foreignColumnName.second; + aIter->fill(rColDesc.nPosition, rColDesc.nType, m_xRow); + ++aIter; + } + m_aKeyIter = m_aKeyMap.emplace( m_aKeyMap.rbegin()->first+1,OKeySetValue(aKeyRow,std::pair<sal_Int32,Reference<XRow> >(0,Reference<XRow>())) ).first; + } + else + m_bRowCountFinal = true; + return bRet; +} + +bool OKeySet::fillAllRows() +{ + if(m_bRowCountFinal) + { + return false; + } + else + { + while(fetchRow()) + ; + return true; + } +} + +// XRow +sal_Bool SAL_CALL OKeySet::wasNull( ) +{ + if ( ! m_xRow.is() ) + throwGenericSQLException("Must call getFOO() for some FOO before wasNull()", *this); + + OSL_ENSURE(m_xRow.is(),"m_xRow is null! I've thrown, but function execution continued?"); + return m_xRow->wasNull(); +} + +inline void OKeySet::ensureRowForData( ) +{ + if (! m_xRow.is() ) + refreshRow(); + if (! m_xRow.is() ) + throwSQLException("Failed to refetch row", "02000", *this, -2); + + OSL_ENSURE(m_xRow.is(),"m_xRow is null! I've called throwSQLException but execution continued?"); +} + +OUString SAL_CALL OKeySet::getString( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getString(columnIndex); +} + +sal_Bool SAL_CALL OKeySet::getBoolean( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getBoolean(columnIndex); +} + +sal_Int8 SAL_CALL OKeySet::getByte( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getByte(columnIndex); +} + +sal_Int16 SAL_CALL OKeySet::getShort( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getShort(columnIndex); +} + +sal_Int32 SAL_CALL OKeySet::getInt( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getInt(columnIndex); +} + +sal_Int64 SAL_CALL OKeySet::getLong( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getLong(columnIndex); +} + +float SAL_CALL OKeySet::getFloat( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getFloat(columnIndex); +} + +double SAL_CALL OKeySet::getDouble( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getDouble(columnIndex); +} + +Sequence< sal_Int8 > SAL_CALL OKeySet::getBytes( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getBytes(columnIndex); +} + +css::util::Date SAL_CALL OKeySet::getDate( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getDate(columnIndex); +} + +css::util::Time SAL_CALL OKeySet::getTime( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getTime(columnIndex); +} + +css::util::DateTime SAL_CALL OKeySet::getTimestamp( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getTimestamp(columnIndex); +} + +Reference< css::io::XInputStream > SAL_CALL OKeySet::getBinaryStream( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getBinaryStream(columnIndex); +} + +Reference< css::io::XInputStream > SAL_CALL OKeySet::getCharacterStream( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getCharacterStream(columnIndex); +} + +Any SAL_CALL OKeySet::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& typeMap ) +{ + ensureRowForData(); + return m_xRow->getObject(columnIndex,typeMap); +} + +Reference< XRef > SAL_CALL OKeySet::getRef( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getRef(columnIndex); +} + +Reference< XBlob > SAL_CALL OKeySet::getBlob( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getBlob(columnIndex); +} + +Reference< XClob > SAL_CALL OKeySet::getClob( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getClob(columnIndex); +} + +Reference< XArray > SAL_CALL OKeySet::getArray( sal_Int32 columnIndex ) +{ + ensureRowForData(); + return m_xRow->getArray(columnIndex); +} + +bool OKeySet::rowUpdated( ) +{ + return m_aKeyIter != m_aKeyMap.begin() && m_aKeyIter != m_aKeyMap.end() && m_aKeyIter->second.second.first == 2; +} + +bool OKeySet::rowInserted( ) +{ + return m_aKeyIter != m_aKeyMap.begin() && m_aKeyIter != m_aKeyMap.end() && m_aKeyIter->second.second.first == 1; +} + +bool OKeySet::rowDeleted( ) +{ + bool bDeleted = m_bDeleted; + m_bDeleted = false; + return bDeleted; +} + +namespace dbaccess +{ + +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 + Sequence< OUString> aSelNames(_rxQueryColumns->getElementNames()); + const OUString* pSelIter = aSelNames.getConstArray(); + const OUString* pSelEnd = pSelIter + aSelNames.getLength(); + + const OUString* pTblColumnIter = _aColumnNames.getConstArray(); + const OUString* pTblColumnEnd = pTblColumnIter + _aColumnNames.getLength(); + + ::comphelper::UStringMixEqual bCase(o_rColumnNames.key_comp().isCaseSensitive()); + + for(sal_Int32 nPos = 1;pSelIter != pSelEnd;++pSelIter,++nPos) + { + Reference<XPropertySet> xQueryColumnProp(_rxQueryColumns->getByName(*pSelIter),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; + + for(;pTblColumnIter != pTblColumnEnd;++pTblColumnIter) + { + if(bCase(sRealName,*pTblColumnIter) && bCase(_rsUpdateTableName,sTableName) && o_rColumnNames.find(*pTblColumnIter) == o_rColumnNames.end()) + { + sal_Int32 nType = 0; + xQueryColumnProp->getPropertyValue(PROPERTY_TYPE) >>= nType; + sal_Int32 nScale = 0; + xQueryColumnProp->getPropertyValue(PROPERTY_SCALE) >>= nScale; + OUString sColumnDefault; + if ( xQueryColumnProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_DEFAULTVALUE) ) + xQueryColumnProp->getPropertyValue(PROPERTY_DEFAULTVALUE) >>= sColumnDefault; + + sal_Int32 nNullable = ColumnValue::NULLABLE_UNKNOWN; + OSL_VERIFY( xQueryColumnProp->getPropertyValue( PROPERTY_ISNULLABLE ) >>= nNullable ); + + SelectColumnDescription aColDesc( nPos, nType, nScale, nNullable != sdbc::ColumnValue::NO_NULLS, sColumnDefault ); + OUString sName; + if ( i_bAppendTableName ) + { + sName = sTableName + "." + sRealName; + aColDesc.sRealName = sRealName; + aColDesc.sTableName = sTableName; + } + else + { + sName = sRealName; + } + o_rColumnNames[sName] = aColDesc; + + break; + } + } + pTblColumnIter = _aColumnNames.getConstArray(); + } + } +} + +void OKeySet::impl_convertValue_throw(const ORowSetRow& _rInsertRow,const SelectColumnDescription& i_aMetaData) +{ + ORowSetValue& aValue((*_rInsertRow)[i_aMetaData.nPosition]); + switch(i_aMetaData.nType) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + { + OUString sValue = aValue.getString(); + sal_Int32 nIndex = sValue.indexOf('.'); + if ( nIndex != -1 ) + { + aValue = sValue.copy(0,std::min(sValue.getLength(),nIndex + (i_aMetaData.nScale > 0 ? i_aMetaData.nScale + 1 : 0))); + } + } + break; + default: + break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/KeySet.hxx b/dbaccess/source/core/api/KeySet.hxx new file mode 100644 index 000000000..57889c774 --- /dev/null +++ b/dbaccess/source/core/api/KeySet.hxx @@ -0,0 +1,216 @@ +/* -*- 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 . + */ + +#pragma once + +#include "CacheSet.hxx" + +#include <memory> +#include <map> +#include <vector> + +#include <com/sun/star/sdb/XSingleSelectQueryAnalyzer.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <comphelper/stl_types.hxx> + +namespace dbaccess +{ + struct SelectColumnDescription + { + OUString sRealName; // may be empty + OUString sTableName; // may be empty + OUString sDefaultValue; + sal_Int32 nPosition; + sal_Int32 nType; + sal_Int32 nScale; + bool bNullable; + + SelectColumnDescription() + :nPosition( 0 ) + ,nType( 0 ) + ,nScale( 0 ) + ,bNullable(false) + { + } + + SelectColumnDescription( sal_Int32 _nPosition, sal_Int32 _nType, sal_Int32 _nScale,bool _bNullable, const OUString& _rDefaultValue ) + :sDefaultValue( _rDefaultValue ) + ,nPosition( _nPosition ) + ,nType( _nType ) + ,nScale( _nScale ) + ,bNullable(_bNullable) + { + } + }; + typedef std::map< OUString, SelectColumnDescription, ::comphelper::UStringMixLess > SelectColumnsMetaData; + + // the elements of _rxQueryColumns must have the properties PROPERTY_REALNAME and PROPERTY_TABLENAME + void getColumnPositions(const css::uno::Reference< css::container::XNameAccess >& _rxQueryColumns, + const css::uno::Sequence< OUString >& _rColumnNames, + std::u16string_view _rsUpdateTableName, + SelectColumnsMetaData& o_rColumnNames /* out */, + bool i_bAppendTableName = false); + + typedef std::pair<ORowSetRow,std::pair<sal_Int32,css::uno::Reference< css::sdbc::XRow> > > OKeySetValue; + typedef std::map<sal_Int32,OKeySetValue > OKeySetMatrix; + typedef std::map<sal_Int32, rtl::Reference<ORowSetValueVector> > OUpdatedParameter; + // is used when the source supports keys + class OKeySet : public OCacheSet + { + protected: + OKeySetMatrix m_aKeyMap; + OKeySetMatrix::iterator m_aKeyIter; + + std::vector< OUString > m_aAutoColumns; // contains all columns which are autoincrement ones + + OUpdatedParameter m_aUpdatedParameter; // contains all parameter which have been updated and are needed for refetching + rtl::Reference<ORowSetValueVector> m_aParameterValueForCache; + std::unique_ptr<SelectColumnsMetaData> m_pKeyColumnNames; // contains all key column names + std::unique_ptr<SelectColumnsMetaData> m_pColumnNames; // contains all column names + std::unique_ptr<SelectColumnsMetaData> m_pParameterNames; // contains all parameter names + std::unique_ptr<SelectColumnsMetaData> m_pForeignColumnNames; // contains all column names of the rest + connectivity::OSQLTable m_xTable; // reference to our table + // we need a different SQL (statement) for each different combination + // of NULLness of key & foreign columns; + // each subclause is either "colName = ?" or "colName IS NULL" + // (we avoid the standard "colName IS NOT DISTINCT FROM ?" because it is not widely supported) + typedef std::map< std::vector<bool>, + css::uno::Reference< css::sdbc::XPreparedStatement > > + vStatements_t; + vStatements_t m_vStatements; + css::uno::Reference< css::sdbc::XPreparedStatement> m_xStatement; + css::uno::Reference< css::sdbc::XResultSet> m_xSet; + css::uno::Reference< css::sdbc::XRow> m_xRow; + css::uno::Reference< css::sdb::XSingleSelectQueryAnalyzer > m_xComposer; + const OUString m_sUpdateTableName; + std::vector< OUString > m_aFilterColumns; + sal_Int32& m_rRowCount; + + bool m_bRowCountFinal; + + /** copies the values from the insert row into the key row + * + * \param _rInsertRow the row which was inserted + * \param _rKeyRow The current key row of the row set. + + \param i_nBookmark The bookmark is used to update the parameter + */ + void copyRowValue(const ORowSetRow& _rInsertRow, ORowSetRow const & _rKeyRow, sal_Int32 i_nBookmark); + + // returns true if it did any work + bool fillAllRows(); + bool fetchRow(); + void invalidateRow(); + + static void impl_convertValue_throw(const ORowSetRow& _rInsertRow,const SelectColumnDescription& i_aMetaData); + void initColumns(); + void findTableColumnsMatching_throw( const css::uno::Any& i_aTable, + const OUString& i_rUpdateTableName, + const css::uno::Reference< css::sdbc::XDatabaseMetaData>& i_xMeta, + const css::uno::Reference< css::container::XNameAccess>& i_xQueryColumns, + std::unique_ptr<SelectColumnsMetaData> const & o_pKeyColumnNames); + void ensureStatement( ); + virtual void makeNewStatement( ); + static void setOneKeyColumnParameter( sal_Int32 &nPos, + const css::uno::Reference< css::sdbc::XParameters > &_xParameter, + const connectivity::ORowSetValue &_rValue, + sal_Int32 _nType, + sal_Int32 _nScale ); + OUStringBuffer createKeyFilter( ); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + bool doTryRefetch_throw(); + void tryRefetch(const ORowSetRow& _rInsertRow,bool bRefetch); + void executeUpdate(const ORowSetRow& _rInsertRow, const ORowSetRow& _rOriginalRow, const OUString& i_sSQL, std::u16string_view i_sTableName,const std::vector<sal_Int32>& _aIndexColumnPositions = std::vector<sal_Int32>()); + void executeInsert( const ORowSetRow& _rInsertRow, const OUString& i_sSQL, std::u16string_view i_sTableName, bool bRefetch = false); + void executeStatement(OUStringBuffer& io_aFilter, css::uno::Reference< css::sdb::XSingleSelectQueryComposer>& io_xAnalyzer); + + virtual ~OKeySet() override; + public: + OKeySet(const connectivity::OSQLTable& _xTable, + const OUString& _rUpdateTableName, + const css::uno::Reference< css::sdb::XSingleSelectQueryAnalyzer >& _xComposer, + const ORowSetValueVector& _aParameterValueForCache, + sal_Int32 i_nMaxRows, + sal_Int32& o_nRowCount); + + // late ctor which can throw exceptions + virtual void construct(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) override; + virtual void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet) override; + + // css::sdbc::XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + + + virtual bool rowUpdated( ) override; + virtual bool rowInserted( ) override; + virtual bool rowDeleted( ) override; + bool isBeforeFirst( ); + bool isAfterLast( ); + + // css::sdbc::XResultSet + virtual bool next() override; + virtual void beforeFirst( ) override; + virtual void afterLast( ) override; + virtual bool first() override; + virtual bool last( ) override; + virtual sal_Int32 getRow( ) override; + virtual bool absolute( sal_Int32 row ) override; + virtual bool previous( ) override; + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void ensureRowForData( ); + virtual void refreshRow( ) override; + // css::sdbcx::XRowLocate + virtual css::uno::Any getBookmark() override; + + virtual bool moveToBookmark( const css::uno::Any& bookmark ) override; + + virtual sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + + virtual bool hasOrderedBookmarks( ) override; + + virtual sal_Int32 hashBookmark( const css::uno::Any& bookmark ) override; + + // css::sdbc::XResultSetUpdate + virtual void updateRow(const ORowSetRow& _rInsertRow,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) override; + virtual void deleteRow(const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/OptimisticSet.cxx b/dbaccess/source/core/api/OptimisticSet.cxx new file mode 100644 index 000000000..d1d70955f --- /dev/null +++ b/dbaccess/source/core/api/OptimisticSet.cxx @@ -0,0 +1,586 @@ +/* -*- 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 . + */ + + +#include <memory> +#include "OptimisticSet.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <map> +#include <algorithm> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <composertools.hxx> + +using namespace dbaccess; +using namespace ::connectivity; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::osl; + +typedef std::map<OUString, OUStringBuffer> TSQLStatements; +namespace +{ + void lcl_fillKeyCondition(const OUString& i_sTableName,std::u16string_view i_sQuotedColumnName,const ORowSetValue& i_aValue,TSQLStatements& io_aKeyConditions) + { + OUStringBuffer& rKeyCondition = io_aKeyConditions[i_sTableName]; + if ( !rKeyCondition.isEmpty() ) + rKeyCondition.append(" AND "); + rKeyCondition.append(i_sQuotedColumnName); + if ( i_aValue.isNull() ) + rKeyCondition.append(" IS NULL"); + else + rKeyCondition.append(" = ?"); + } +} + + +OptimisticSet::OptimisticSet(const Reference<XComponentContext>& _rContext, + const Reference< XConnection>& i_xConnection, + const Reference< XSingleSelectQueryAnalyzer >& _xComposer, + const ORowSetValueVector& _aParameterValueForCache, + sal_Int32 i_nMaxRows, + sal_Int32& o_nRowCount) + :OKeySet(nullptr,OUString(),_xComposer,_aParameterValueForCache,i_nMaxRows,o_nRowCount) + ,m_aSqlParser( _rContext ) + ,m_aSqlIterator( i_xConnection, Reference<XTablesSupplier>(_xComposer,UNO_QUERY_THROW)->getTables(), m_aSqlParser ) + ,m_bResultSetChanged(false) +{ +} + +OptimisticSet::~OptimisticSet() +{ +} + +void OptimisticSet::construct(const Reference< XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) +{ + OCacheSet::construct(_xDriverSet,i_sRowSetFilter); + + initColumns(); + m_sRowSetFilter = i_sRowSetFilter; + + Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData(); + bool bCase = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(); + Reference<XColumnsSupplier> xQueryColSup(m_xComposer,UNO_QUERY); + const Reference<XNameAccess> xQueryColumns = xQueryColSup->getColumns(); + const Reference<XTablesSupplier> xTabSup(m_xComposer,UNO_QUERY); + const Reference<XNameAccess> xTables = xTabSup->getTables(); + const Sequence< OUString> aTableNames = xTables->getElementNames(); + const OUString* pTableNameIter = aTableNames.getConstArray(); + const OUString* pTableNameEnd = pTableNameIter + aTableNames.getLength(); + for( ; pTableNameIter != pTableNameEnd ; ++pTableNameIter) + { + std::unique_ptr<SelectColumnsMetaData> pKeyColumNames(new SelectColumnsMetaData(bCase)); + findTableColumnsMatching_throw(xTables->getByName(*pTableNameIter),*pTableNameIter,xMeta,xQueryColumns,pKeyColumNames); + m_pKeyColumnNames->insert(pKeyColumNames->begin(),pKeyColumNames->end()); + } + + // 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,std::pair<sal_Int32,Reference<XRow> >(0,Reference<XRow>())); + m_aKeyMap.emplace(0,keySetValue); + m_aKeyIter = m_aKeyMap.begin(); + + Reference< XSingleSelectQueryComposer> xSourceComposer(m_xComposer,UNO_QUERY); + Reference< XMultiServiceFactory > xFactory(m_xConnection, UNO_QUERY_THROW); + Reference<XSingleSelectQueryComposer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY); + OUString sQuery = xSourceComposer->getQuery(); + xAnalyzer->setElementaryQuery(xSourceComposer->getElementaryQuery()); + // check for joins + OUString aErrorMsg; + std::unique_ptr<OSQLParseNode> pStatementNode( m_aSqlParser.parseTree( aErrorMsg, sQuery ) ); + m_aSqlIterator.setParseTree( pStatementNode.get() ); + m_aSqlIterator.traverseAll(); + fillJoinedColumns_throw(m_aSqlIterator.getJoinConditions()); + +} + +void OptimisticSet::makeNewStatement( ) +{ + OUStringBuffer aFilter = createKeyFilter(); + + Reference< XSingleSelectQueryComposer> xSourceComposer(m_xComposer,UNO_QUERY); + Reference< XMultiServiceFactory > xFactory(m_xConnection, UNO_QUERY_THROW); + Reference<XSingleSelectQueryComposer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY); + xAnalyzer->setElementaryQuery(xSourceComposer->getElementaryQuery()); + + const OUString sComposerFilter = m_xComposer->getFilter(); + if ( !m_sRowSetFilter.isEmpty() || !sComposerFilter.isEmpty() ) + { + FilterCreator aFilterCreator; + if ( !sComposerFilter.isEmpty() && sComposerFilter != m_sRowSetFilter ) + aFilterCreator.append( sComposerFilter ); + aFilterCreator.append( m_sRowSetFilter ); + aFilterCreator.append( aFilter.makeStringAndClear() ); + aFilter = aFilterCreator.getComposedAndClear(); + } + xAnalyzer->setFilter(aFilter.makeStringAndClear()); + m_xStatement = m_xConnection->prepareStatement(xAnalyzer->getQueryWithSubstitution()); + ::comphelper::disposeComponent(xAnalyzer); +} + +void OptimisticSet::updateRow(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + if ( m_aJoinedKeyColumns.empty() ) + throw SQLException(); + // list all columns that should be set + OUString aQuote = getIdentifierQuoteString(); + + std::map< OUString,bool > aResultSetChanged; + TSQLStatements aKeyConditions; + TSQLStatements aSql; + + // here we build the condition part for the update statement + for (auto const& columnName : *m_pColumnNames) + { + aResultSetChanged.try_emplace(columnName.second.sTableName, false); + const OUString sQuotedColumnName = ::dbtools::quoteName( aQuote,columnName.second.sRealName); + if ( m_pKeyColumnNames->find(columnName.first) != m_pKeyColumnNames->end() ) + { + aResultSetChanged[columnName.second.sTableName] = m_aJoinedKeyColumns.find(columnName.second.nPosition) != m_aJoinedKeyColumns.end(); + lcl_fillKeyCondition(columnName.second.sTableName,sQuotedColumnName,(*_rOriginalRow)[columnName.second.nPosition],aKeyConditions); + } + if((*_rInsertRow)[columnName.second.nPosition].isModified()) + { + if ( m_aJoinedKeyColumns.find(columnName.second.nPosition) != m_aJoinedKeyColumns.end() ) + throw SQLException(); + + std::map<sal_Int32,sal_Int32>::const_iterator aJoinIter = m_aJoinedColumns.find(columnName.second.nPosition); + if ( aJoinIter != m_aJoinedColumns.end() ) + { + (*_rInsertRow)[aJoinIter->second] = (*_rInsertRow)[columnName.second.nPosition]; + } + OUStringBuffer& rPart = aSql[columnName.second.sTableName]; + if ( !rPart.isEmpty() ) + rPart.append(", "); + rPart.append(sQuotedColumnName + " = ?"); + } + } + + if( aSql.empty() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_VALUE_CHANGED ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + if( aKeyConditions.empty() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_CONDITION_FOR_PK ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + Reference<XDatabaseMetaData> xMetaData = m_xConnection->getMetaData(); + + for (auto const& elem : aSql) + { + if ( !elem.second.isEmpty() ) + { + m_bResultSetChanged = m_bResultSetChanged || aResultSetChanged[elem.first]; + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(xMetaData,elem.first,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + OUStringBuffer sSql("UPDATE " + ::dbtools::composeTableNameForSelect( m_xConnection, sCatalog, sSchema, sTable ) + + " SET " + elem.second); + OUStringBuffer& rCondition = aKeyConditions[elem.first]; + if ( !rCondition.isEmpty() ) + sSql.append(" WHERE " + rCondition ); + + executeUpdate(_rInsertRow ,_rOriginalRow,sSql.makeStringAndClear(),elem.first); + } + } +} + +void OptimisticSet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + TSQLStatements aSql; + TSQLStatements aParameter; + TSQLStatements aKeyConditions; + std::map< OUString,bool > aResultSetChanged; + OUString aQuote = getIdentifierQuoteString(); + + // here we build the condition part for the update statement + for (auto const& columnName : *m_pColumnNames) + { + aResultSetChanged.try_emplace(columnName.second.sTableName, false); + + const OUString sQuotedColumnName = ::dbtools::quoteName( aQuote,columnName.second.sRealName); + if ( (*_rInsertRow)[columnName.second.nPosition].isModified() ) + { + if ( m_aJoinedKeyColumns.find(columnName.second.nPosition) != m_aJoinedKeyColumns.end() ) + { + lcl_fillKeyCondition(columnName.second.sTableName,sQuotedColumnName,(*_rInsertRow)[columnName.second.nPosition],aKeyConditions); + aResultSetChanged[columnName.second.sTableName] = true; + } + std::map<sal_Int32,sal_Int32>::const_iterator aJoinIter = m_aJoinedColumns.find(columnName.second.nPosition); + if ( aJoinIter != m_aJoinedColumns.end() ) + { + (*_rInsertRow)[aJoinIter->second] = (*_rInsertRow)[columnName.second.nPosition]; + } + OUStringBuffer& rPart = aSql[columnName.second.sTableName]; + if ( !rPart.isEmpty() ) + rPart.append(", "); + rPart.append(sQuotedColumnName); + OUStringBuffer& rParam = aParameter[columnName.second.sTableName]; + if ( !rParam.isEmpty() ) + rParam.append(", "); + rParam.append("?"); + } + } + if ( aParameter.empty() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_VALUE_CHANGED ), StandardSQLState::GENERAL_ERROR, m_xConnection ); + + Reference<XDatabaseMetaData> xMetaData = m_xConnection->getMetaData(); + for (auto const& elem : aSql) + { + if ( !elem.second.isEmpty() ) + { + m_bResultSetChanged = m_bResultSetChanged || aResultSetChanged[elem.first]; + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(xMetaData,elem.first,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + OUString sComposedTableName = ::dbtools::composeTableNameForSelect( m_xConnection, sCatalog, sSchema, sTable ); + OUString sSql("INSERT INTO " + sComposedTableName + " ( " + elem.second + + ") VALUES ( " + aParameter[elem.first] + " )"); + + OUStringBuffer& rCondition = aKeyConditions[elem.first]; + if ( !rCondition.isEmpty() ) + { + OUString sQuery("SELECT " + elem.second + " FROM " + sComposedTableName + + " WHERE " + rCondition); + + try + { + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(sQuery)); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + // and then the values of the where condition + sal_Int32 i = 1; + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + if ( keyColumnName.second.sTableName == elem.first ) + { + setParameter(i++,xParameter,(*_rInsertRow)[keyColumnName.second.nPosition],keyColumnName.second.nType,keyColumnName.second.nScale); + } + } + Reference<XResultSet> xRes = xPrep->executeQuery(); + Reference<XRow> xRow(xRes,UNO_QUERY); + if ( xRow.is() && xRes->next() ) + { + m_bResultSetChanged = true; + continue; + } + } + catch(const SQLException&) + { + } + } + + executeInsert(_rInsertRow,sSql,elem.first); + } + } +} + +void OptimisticSet::deleteRow(const ORowSetRow& _rDeleteRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + OUString aQuote = getIdentifierQuoteString(); + TSQLStatements aKeyConditions; + + // here we build the condition part for the update statement + for (auto const& columnName : *m_pColumnNames) + { + if ( m_aJoinedKeyColumns.find(columnName.second.nPosition) == m_aJoinedKeyColumns.end() && m_pKeyColumnNames->find(columnName.first) != m_pKeyColumnNames->end() ) + { + // only delete rows which aren't the key in the join + const OUString sQuotedColumnName = ::dbtools::quoteName( aQuote,columnName.second.sRealName); + lcl_fillKeyCondition(columnName.second.sTableName,sQuotedColumnName,(*_rDeleteRow)[columnName.second.nPosition],aKeyConditions); + } + } + Reference<XDatabaseMetaData> xMetaData = m_xConnection->getMetaData(); + for (auto & keyCondition : aKeyConditions) + { + OUStringBuffer& rCondition = keyCondition.second; + if ( !rCondition.isEmpty() ) + { + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(xMetaData,keyCondition.first,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + OUString sSql("DELETE FROM " + ::dbtools::composeTableNameForSelect( m_xConnection, sCatalog, sSchema, sTable ) + + " WHERE " + rCondition ); + executeDelete(_rDeleteRow, sSql, keyCondition.first); + } + } +} + +void OptimisticSet::executeDelete(const ORowSetRow& _rDeleteRow,const OUString& i_sSQL,std::u16string_view i_sTableName) +{ + // now create and execute the prepared statement + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(i_sSQL)); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + + sal_Int32 i = 1; + for (auto const& keyColumnName : *m_pKeyColumnNames) + { + if ( keyColumnName.second.sTableName == i_sTableName ) + setParameter(i++,xParameter,(*_rDeleteRow)[keyColumnName.second.nPosition],keyColumnName.second.nType,keyColumnName.second.nScale); + } + m_bDeleted = xPrep->executeUpdate() > 0; + + if(m_bDeleted) + { + sal_Int32 nBookmark = ::comphelper::getINT32((*_rDeleteRow)[0].getAny()); + if(m_aKeyIter == m_aKeyMap.find(nBookmark) && m_aKeyIter != m_aKeyMap.end()) + ++m_aKeyIter; + m_aKeyMap.erase(nBookmark); + m_bDeleted = true; + } +} + +void OptimisticSet::fillJoinedColumns_throw(const std::vector< TNodePair >& i_aJoinColumns) +{ + for (auto const& joinColumn : i_aJoinColumns) + { + OUString sColumnName,sTableName; + m_aSqlIterator.getColumnRange(joinColumn.first,sColumnName,sTableName); + OUString sLeft(sTableName + "." + sColumnName); + m_aSqlIterator.getColumnRange(joinColumn.second,sColumnName,sTableName); + OUString sRight(sTableName + "." + sColumnName); + fillJoinedColumns_throw(sLeft, sRight); + } +} + +void OptimisticSet::fillJoinedColumns_throw(const OUString& i_sLeftColumn,const OUString& i_sRightColumn) +{ + sal_Int32 nLeft = 0,nRight = 0; + SelectColumnsMetaData::const_iterator aLeftIter = m_pKeyColumnNames->find(i_sLeftColumn); + SelectColumnsMetaData::const_iterator aRightIter = m_pKeyColumnNames->find(i_sRightColumn); + + bool bLeftKey = aLeftIter != m_pKeyColumnNames->end(); + if ( bLeftKey ) + { + nLeft = aLeftIter->second.nPosition; + } + else + { + aLeftIter = m_pColumnNames->find(i_sLeftColumn); + if ( aLeftIter != m_pColumnNames->end() ) + nLeft = aLeftIter->second.nPosition; + } + + bool bRightKey = aRightIter != m_pKeyColumnNames->end(); + if ( bRightKey ) + { + nRight = aRightIter->second.nPosition; + } + else + { + aRightIter = m_pColumnNames->find(i_sRightColumn); + if ( aRightIter != m_pColumnNames->end() ) + nRight = aRightIter->second.nPosition; + } + + if (bLeftKey) + m_aJoinedKeyColumns[nLeft] = nRight; + else + m_aJoinedColumns[nLeft] = nRight; + if (bRightKey) + m_aJoinedKeyColumns[nRight] = nLeft; + else + m_aJoinedColumns[nRight] = nLeft; +} + +bool OptimisticSet::isResultSetChanged() const +{ + bool bOld = m_bResultSetChanged; + m_bResultSetChanged = false; + return bOld; +} + +void OptimisticSet::mergeColumnValues(sal_Int32 i_nColumnIndex,ORowSetValueVector::Vector& io_aInsertRow,ORowSetValueVector::Vector& io_aRow,std::vector<sal_Int32>& o_aChangedColumns) +{ + o_aChangedColumns.push_back(i_nColumnIndex); + std::map<sal_Int32,sal_Int32>::const_iterator aJoinIter = m_aJoinedColumns.find(i_nColumnIndex); + if ( aJoinIter != m_aJoinedColumns.end() ) + { + io_aRow[aJoinIter->second] = io_aRow[i_nColumnIndex]; + io_aInsertRow[aJoinIter->second] = io_aInsertRow[i_nColumnIndex]; + io_aRow[aJoinIter->second].setModified(true); + o_aChangedColumns.push_back(aJoinIter->second); + } +} + +bool OptimisticSet::updateColumnValues(const ORowSetValueVector::Vector& io_aCachedRow,ORowSetValueVector::Vector& io_aRow,const std::vector<sal_Int32>& i_aChangedColumns) +{ + bool bRet = false; + for( const auto& aColIdx : i_aChangedColumns ) + { + SelectColumnsMetaData::const_iterator aFind = std::find_if( + m_pKeyColumnNames->begin(),m_pKeyColumnNames->end(), + [&aColIdx]( const SelectColumnsMetaData::value_type& aType ) + { return aType.second.nPosition == aColIdx; } ); + if ( aFind != m_pKeyColumnNames->end() ) + { + const OUString sTableName = aFind->second.sTableName; + aFind = std::find_if( m_pKeyColumnNames->begin(),m_pKeyColumnNames->end(), + [&sTableName] + ( const SelectColumnsMetaData::value_type& rCurr ) + { return rCurr.second.sTableName == sTableName; } ); + while( aFind != m_pKeyColumnNames->end() ) + { + io_aRow[aFind->second.nPosition].setSigned(io_aCachedRow[aFind->second.nPosition].isSigned()); + if ( io_aCachedRow[aFind->second.nPosition] != io_aRow[aFind->second.nPosition] ) + break; + ++aFind; + } + if ( aFind == m_pKeyColumnNames->end() ) + { + bRet = true; + for( const auto& aCol : *m_pColumnNames ) + { + if ( aCol.second.sTableName == sTableName ) + { + io_aRow[aCol.second.nPosition] = io_aCachedRow[aCol.second.nPosition]; + io_aRow[aCol.second.nPosition].setModified(true); + } + } + } + } + } + return bRet; +} + +bool OptimisticSet::columnValuesUpdated(ORowSetValueVector::Vector& o_aCachedRow,const ORowSetValueVector::Vector& i_aRow) +{ + bool bRet = false; + for( const auto& aCol : *m_pColumnNames ) + { + sal_Int32 nPos = aCol.second.nPosition; + SelectColumnsMetaData::const_iterator aFind = std::find_if( + m_pKeyColumnNames->begin(),m_pKeyColumnNames->end(), + [&nPos] ( const SelectColumnsMetaData::value_type& aType ) + { return aType.second.nPosition == nPos; } ); + if ( aFind != m_pKeyColumnNames->end() ) + { + const OUString sTableName = aFind->second.sTableName; + aFind = std::find_if( m_pKeyColumnNames->begin(),m_pKeyColumnNames->end(), + [&sTableName] + ( const SelectColumnsMetaData::value_type& rCurr ) + { return rCurr.second.sTableName == sTableName; } ); + while( aFind != m_pKeyColumnNames->end() ) + { + o_aCachedRow[aFind->second.nPosition].setSigned(i_aRow[aFind->second.nPosition].isSigned()); + if ( o_aCachedRow[aFind->second.nPosition] != i_aRow[aFind->second.nPosition] ) + break; + ++aFind; + } + if ( aFind == m_pKeyColumnNames->end() ) + { + bRet = true; + for( const auto& aCol2 : *m_pColumnNames ) + { + if ( aCol2.second.sTableName == sTableName ) + { + o_aCachedRow[aCol2.second.nPosition] = i_aRow[aCol2.second.nPosition]; + o_aCachedRow[aCol2.second.nPosition].setModified(true); + } + } + fillMissingValues(o_aCachedRow); + } + } + } + return bRet; +} + +void OptimisticSet::fillMissingValues(ORowSetValueVector::Vector& io_aRow) const +{ + TSQLStatements aSql; + TSQLStatements aKeyConditions; + OUString aQuote = getIdentifierQuoteString(); + // here we build the condition part for the update statement + for (auto const& columnName : *m_pColumnNames) + { + const OUString sQuotedColumnName = ::dbtools::quoteName( aQuote,columnName.second.sRealName); + if ( m_aJoinedKeyColumns.find(columnName.second.nPosition) != m_aJoinedKeyColumns.end() ) + { + lcl_fillKeyCondition(columnName.second.sTableName,sQuotedColumnName,io_aRow[columnName.second.nPosition],aKeyConditions); + } + OUStringBuffer& rPart = aSql[columnName.second.sTableName]; + if ( !rPart.isEmpty() ) + rPart.append(", "); + rPart.append(sQuotedColumnName); + } + Reference<XDatabaseMetaData> xMetaData = m_xConnection->getMetaData(); + for (auto const& elem : aSql) + { + if ( !elem.second.isEmpty() ) + { + OUStringBuffer& rCondition = aKeyConditions[elem.first]; + if ( !rCondition.isEmpty() ) + { + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(xMetaData,elem.first,sCatalog,sSchema,sTable,::dbtools::EComposeRule::InDataManipulation); + OUString sComposedTableName = ::dbtools::composeTableNameForSelect( m_xConnection, sCatalog, sSchema, sTable ); + OUString sQuery("SELECT " + elem.second + " FROM " + sComposedTableName + " WHERE " + rCondition); + rCondition.setLength(0); + + try + { + Reference< XPreparedStatement > xPrep(m_xConnection->prepareStatement(sQuery)); + Reference< XParameters > xParameter(xPrep,UNO_QUERY); + // and then the values of the where condition + sal_Int32 i = 1; + for (auto const& keyColumn : *m_pKeyColumnNames) + { + if ( keyColumn.second.sTableName == elem.first ) + { + setParameter(i++,xParameter,io_aRow[keyColumn.second.nPosition],keyColumn.second.nType,keyColumn.second.nScale); + } + } + Reference<XResultSet> xRes = xPrep->executeQuery(); + Reference<XRow> xRow(xRes,UNO_QUERY); + if ( xRow.is() && xRes->next() ) + { + i = 1; + for (auto const& columnName : *m_pColumnNames) + { + if ( columnName.second.sTableName == elem.first ) + { + io_aRow[columnName.second.nPosition].fill(i++, columnName.second.nType, xRow); + io_aRow[columnName.second.nPosition].setModified(true); + } + } + } + } + catch(const SQLException&) + { + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/OptimisticSet.hxx b/dbaccess/source/core/api/OptimisticSet.hxx new file mode 100644 index 000000000..bf5e3a803 --- /dev/null +++ b/dbaccess/source/core/api/OptimisticSet.hxx @@ -0,0 +1,76 @@ +/* -*- 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 . + */ + +#pragma once + +#include "KeySet.hxx" + +#include <connectivity/sqlparse.hxx> +#include <connectivity/sqliterator.hxx> + +#include <com/sun/star/sdb/XSingleSelectQueryAnalyzer.hpp> + +namespace dbaccess +{ + // is used when the source supports keys + class OptimisticSet : public OKeySet + { + ::connectivity::OSQLParser m_aSqlParser; + ::connectivity::OSQLParseTreeIterator m_aSqlIterator; + + std::map<sal_Int32,sal_Int32> m_aJoinedColumns; + std::map<sal_Int32,sal_Int32> m_aJoinedKeyColumns; + + mutable bool m_bResultSetChanged; + + void executeDelete(const ORowSetRow& _rDeleteRow,const OUString& i_sSQL,std::u16string_view i_sTableName); + void fillJoinedColumns_throw(const std::vector< ::connectivity::TNodePair>& i_aJoinColumns); + void fillJoinedColumns_throw(const OUString& i_sLeftColumn,const OUString& i_sRightColumn); + protected: + virtual void makeNewStatement( ) override; + virtual ~OptimisticSet() override; + public: + OptimisticSet(const css::uno::Reference< css::uno::XComponentContext>& _rContext, + const css::uno::Reference< css::sdbc::XConnection>& i_xConnection, + const css::uno::Reference< css::sdb::XSingleSelectQueryAnalyzer >& _xComposer, + const ORowSetValueVector& _aParameterValueForCache, + sal_Int32 i_nMaxRows, + sal_Int32& o_nRowCount); + + // late ctor which can throw exceptions + virtual void construct(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) override; + + // css::sdbc::XResultSetUpdate + virtual void updateRow(const ORowSetRow& _rInsertRow,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) override; + virtual void deleteRow(const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + + // CacheSet + virtual bool isResultSetChanged() const override; + virtual void mergeColumnValues(sal_Int32 i_nColumnIndex,ORowSetValueVector::Vector& io_aInsertRow,ORowSetValueVector::Vector& io_aRow,std::vector<sal_Int32>& o_aChangedColumns) override; + virtual bool columnValuesUpdated(ORowSetValueVector::Vector& o_aCachedRow,const ORowSetValueVector::Vector& i_aRow) override; + virtual bool updateColumnValues(const ORowSetValueVector::Vector& io_aCachedRow,ORowSetValueVector::Vector& io_aRow,const std::vector<sal_Int32>& i_aChangedColumns) override; + virtual void fillMissingValues(ORowSetValueVector::Vector& io_aRow) const override; + + bool isReadOnly() const { return m_aJoinedKeyColumns.empty(); } + const std::map<sal_Int32,sal_Int32>& getJoinedKeyColumns() const { return m_aJoinedKeyColumns; } + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/PrivateRow.cxx b/dbaccess/source/core/api/PrivateRow.cxx new file mode 100644 index 000000000..9bf270960 --- /dev/null +++ b/dbaccess/source/core/api/PrivateRow.cxx @@ -0,0 +1,133 @@ +/* -*- 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 . + */ + +#include "PrivateRow.hxx" + +using namespace dbaccess; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star; + +sal_Bool SAL_CALL OPrivateRow::wasNull( ) + { + return m_aRow[m_nPos].isNull(); + } + OUString SAL_CALL OPrivateRow::getString( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getString(); + } + sal_Bool SAL_CALL OPrivateRow::getBoolean( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getBool(); + } + ::sal_Int8 SAL_CALL OPrivateRow::getByte( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getInt8(); + } + ::sal_Int16 SAL_CALL OPrivateRow::getShort( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getInt16(); + } + ::sal_Int32 SAL_CALL OPrivateRow::getInt( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getInt32(); + } + ::sal_Int64 SAL_CALL OPrivateRow::getLong( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getLong(); + } + float SAL_CALL OPrivateRow::getFloat( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getFloat(); + } + double SAL_CALL OPrivateRow::getDouble( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getDouble(); + } + Sequence< ::sal_Int8 > SAL_CALL OPrivateRow::getBytes( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getSequence(); + } + css::util::Date SAL_CALL OPrivateRow::getDate( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getDate(); + } + css::util::Time SAL_CALL OPrivateRow::getTime( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getTime(); + } + css::util::DateTime SAL_CALL OPrivateRow::getTimestamp( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].getDateTime(); + } + Reference< css::io::XInputStream > SAL_CALL OPrivateRow::getBinaryStream( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< css::io::XInputStream >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + Reference< css::io::XInputStream > SAL_CALL OPrivateRow::getCharacterStream( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< css::io::XInputStream >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + Any SAL_CALL OPrivateRow::getObject( ::sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& ) + { + m_nPos = columnIndex; + return m_aRow[m_nPos].makeAny(); + } + Reference< XRef > SAL_CALL OPrivateRow::getRef( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< XRef >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + Reference< XBlob > SAL_CALL OPrivateRow::getBlob( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< XBlob >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + Reference< XClob > SAL_CALL OPrivateRow::getClob( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< XClob >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + Reference< XArray > SAL_CALL OPrivateRow::getArray( ::sal_Int32 columnIndex ) + { + m_nPos = columnIndex; + return Reference< XArray >(m_aRow[m_nPos].makeAny(),UNO_QUERY); + } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/PrivateRow.hxx b/dbaccess/source/core/api/PrivateRow.hxx new file mode 100644 index 000000000..c5ce74f15 --- /dev/null +++ b/dbaccess/source/core/api/PrivateRow.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ +#pragma once + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include "RowSetRow.hxx" + +namespace dbaccess +{ + class OPrivateRow : public ::cppu::WeakImplHelper< css::sdbc::XRow> + { + ORowSetValueVector::Vector m_aRow; + sal_Int32 m_nPos; + public: + explicit OPrivateRow(ORowSetValueVector::Vector&& i_aRow) : m_aRow(std::move(i_aRow)),m_nPos(0) + { + } + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( ::sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( ::sal_Int32 columnIndex ) override; + virtual ::sal_Int8 SAL_CALL getByte( ::sal_Int32 columnIndex ) override; + virtual ::sal_Int16 SAL_CALL getShort( ::sal_Int32 columnIndex ) override; + virtual ::sal_Int32 SAL_CALL getInt( ::sal_Int32 columnIndex ) override; + virtual ::sal_Int64 SAL_CALL getLong( ::sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( ::sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( ::sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getBytes( ::sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( ::sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( ::sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( ::sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( ::sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( ::sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( ::sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( ::sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( ::sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( ::sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( ::sal_Int32 columnIndex ) override; + }; +} // dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSet.cxx b/dbaccess/source/core/api/RowSet.cxx new file mode 100644 index 000000000..28c392995 --- /dev/null +++ b/dbaccess/source/core/api/RowSet.cxx @@ -0,0 +1,2965 @@ +/* -*- 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 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <map> +#include <utility> + +#include "RowSet.hxx" +#include <stringconstants.hxx> +#include <sdbcoretools.hxx> +#include <SingleSelectQueryComposer.hxx> +#include "CRowSetColumn.hxx" +#include "CRowSetDataColumn.hxx" +#include "RowSetCache.hxx" +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <tablecontainer.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/sdb/RowChangeAction.hpp> +#include <com/sun/star/sdb/RowSetVetoException.hpp> +#include <com/sun/star/sdb/XCompletedConnection.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> + +#include <comphelper/extract.hxx> +#include <comphelper/seqstream.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <comphelper/uno3.hxx> +#include <connectivity/BlobHelper.hxx> +#include <connectivity/dbconversion.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/interfacecontainer.h> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/safeint.hxx> +#include <unotools/syslocale.hxx> +#include <tools/diagnose_ex.h> + +using namespace utl; +using namespace dbaccess; +using namespace connectivity; +using namespace comphelper; +using namespace dbtools; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::util; +using namespace ::cppu; +using namespace ::osl; + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ORowSet_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ORowSet(context)); +} + +namespace dbaccess +{ +ORowSet::ORowSet( const Reference< css::uno::XComponentContext >& _rxContext ) + :ORowSet_BASE1(m_aMutex) + ,ORowSetBase( _rxContext, ORowSet_BASE1::rBHelper, &m_aMutex ) + ,m_aPrematureParamValues(new ORowSetValueVector) + ,m_aParameterValueForCache(new ORowSetValueVector) + ,m_aRowsetListeners(*m_pMutex) + ,m_aApproveListeners(*m_pMutex) + ,m_aRowsChangeListener(*m_pMutex) + ,m_sErrorString(ResourceManager::loadString(RID_STR_COMMAND_LEADING_TO_ERROR)) + ,m_nFetchDirection(FetchDirection::FORWARD) + ,m_nFetchSize(50) + ,m_nMaxFieldSize(0) + ,m_nMaxRows(0) + ,m_nQueryTimeOut(0) + ,m_nCommandType(CommandType::COMMAND) + ,m_nTransactionIsolation(0) + ,m_nPrivileges(0) + ,m_nLastKnownRowCount(0) + ,m_nInAppend(0) + ,m_bInsertingRow(false) + ,m_bLastKnownRowCountFinal(false) + ,m_bUseEscapeProcessing(true) + ,m_bApplyFilter(false) + ,m_bCommandFacetsDirty( true ) + ,m_bParametersDirty( true ) + ,m_bModified(false) + ,m_bRebuildConnOnExecute(false) + ,m_bIsBookmarkable(true) + ,m_bNew(false) + ,m_bCanUpdateInsertedRows(true) + ,m_bOwnConnection(false) + ,m_bPropChangeNotifyEnabled(true) +{ + m_nResultSetType = ResultSetType::SCROLL_SENSITIVE; + m_nResultSetConcurrency = ResultSetConcurrency::UPDATABLE; + m_pMySelf = this; + m_aActiveConnection <<= m_xActiveConnection; + + sal_Int32 const nRBT = PropertyAttribute::READONLY | PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT; + sal_Int32 const nRT = PropertyAttribute::READONLY | PropertyAttribute::TRANSIENT; + sal_Int32 const nBT = PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT; + + m_aPrematureParamValues->resize( 0 ); + + // sdb.RowSet Properties + registerMayBeVoidProperty(PROPERTY_ACTIVE_CONNECTION,PROPERTY_ID_ACTIVE_CONNECTION, PropertyAttribute::MAYBEVOID|PropertyAttribute::TRANSIENT|PropertyAttribute::BOUND, &m_aActiveConnection, cppu::UnoType<XConnection>::get()); + registerProperty(PROPERTY_DATASOURCENAME, PROPERTY_ID_DATASOURCENAME, PropertyAttribute::BOUND, &m_aDataSourceName, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, &m_aCommand, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_COMMAND_TYPE, PROPERTY_ID_COMMAND_TYPE, PropertyAttribute::BOUND, &m_nCommandType, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_ACTIVECOMMAND, PROPERTY_ID_ACTIVECOMMAND, nRBT, &m_aActiveCommand, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_IGNORERESULT, PROPERTY_ID_IGNORERESULT, PropertyAttribute::BOUND, &m_bIgnoreResult, cppu::UnoType<bool>::get()); + registerProperty(PROPERTY_FILTER, PROPERTY_ID_FILTER, PropertyAttribute::BOUND, &m_aFilter, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_HAVING_CLAUSE, PROPERTY_ID_HAVING_CLAUSE, PropertyAttribute::BOUND, &m_aHavingClause, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_GROUP_BY, PROPERTY_ID_GROUP_BY, PropertyAttribute::BOUND, &m_aGroupBy, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER, PropertyAttribute::BOUND, &m_bApplyFilter, cppu::UnoType<bool>::get()); + registerProperty(PROPERTY_ORDER, PROPERTY_ID_ORDER, PropertyAttribute::BOUND, &m_aOrder, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES, nRT, &m_nPrivileges, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_ISMODIFIED, PROPERTY_ID_ISMODIFIED, nBT, &m_bModified, cppu::UnoType<bool>::get()); + registerProperty(PROPERTY_ISNEW, PROPERTY_ID_ISNEW, nRBT, &m_bNew, cppu::UnoType<bool>::get()); + registerProperty(PROPERTY_SINGLESELECTQUERYCOMPOSER,PROPERTY_ID_SINGLESELECTQUERYCOMPOSER, nRT, &m_xComposer, cppu::UnoType<XSingleSelectQueryComposer>::get()); + + // sdbcx.ResultSet Properties + registerProperty(PROPERTY_ISBOOKMARKABLE, PROPERTY_ID_ISBOOKMARKABLE, nRT, &m_bIsBookmarkable, cppu::UnoType<bool>::get()); + registerProperty(PROPERTY_CANUPDATEINSERTEDROWS,PROPERTY_ID_CANUPDATEINSERTEDROWS, nRT, &m_bCanUpdateInsertedRows, cppu::UnoType<bool>::get()); + // sdbc.ResultSet Properties + registerProperty(PROPERTY_RESULTSETCONCURRENCY, PROPERTY_ID_RESULTSETCONCURRENCY, PropertyAttribute::TRANSIENT, &m_nResultSetConcurrency,::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_RESULTSETTYPE, PROPERTY_ID_RESULTSETTYPE, PropertyAttribute::TRANSIENT, &m_nResultSetType, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_FETCHDIRECTION, PROPERTY_ID_FETCHDIRECTION, PropertyAttribute::TRANSIENT, &m_nFetchDirection, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_FETCHSIZE, PROPERTY_ID_FETCHSIZE, PropertyAttribute::TRANSIENT, &m_nFetchSize, ::cppu::UnoType<sal_Int32>::get()); + + // sdbc.RowSet Properties + registerProperty(PROPERTY_URL, PROPERTY_ID_URL, 0, &m_aURL, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_TRANSACTIONISOLATION, PROPERTY_ID_TRANSACTIONISOLATION, PropertyAttribute::TRANSIENT, &m_nTransactionIsolation,::cppu::UnoType<sal_Int32>::get()); + registerMayBeVoidProperty(PROPERTY_TYPEMAP, PROPERTY_ID_TYPEMAP, PropertyAttribute::MAYBEVOID|PropertyAttribute::TRANSIENT, &m_aTypeMap, cppu::UnoType<XNameAccess>::get()); + registerProperty(PROPERTY_ESCAPE_PROCESSING,PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, &m_bUseEscapeProcessing,cppu::UnoType<bool>::get() ); + registerProperty(PROPERTY_QUERYTIMEOUT, PROPERTY_ID_QUERYTIMEOUT, PropertyAttribute::TRANSIENT, &m_nQueryTimeOut, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_MAXFIELDSIZE, PROPERTY_ID_MAXFIELDSIZE, PropertyAttribute::TRANSIENT, &m_nMaxFieldSize, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_MAXROWS, PROPERTY_ID_MAXROWS, 0, &m_nMaxRows, ::cppu::UnoType<sal_Int32>::get() ); + registerProperty(PROPERTY_USER, PROPERTY_ID_USER, PropertyAttribute::TRANSIENT, &m_aUser, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_PASSWORD, PROPERTY_ID_PASSWORD, PropertyAttribute::TRANSIENT, &m_aPassword, ::cppu::UnoType<OUString>::get()); + + registerProperty(PROPERTY_UPDATE_CATALOGNAME, PROPERTY_ID_UPDATE_CATALOGNAME, PropertyAttribute::BOUND, &m_aUpdateCatalogName, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_UPDATE_SCHEMANAME, PROPERTY_ID_UPDATE_SCHEMANAME, PropertyAttribute::BOUND, &m_aUpdateSchemaName, ::cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_UPDATE_TABLENAME, PROPERTY_ID_UPDATE_TABLENAME, PropertyAttribute::BOUND, &m_aUpdateTableName, ::cppu::UnoType<OUString>::get()); + + // ??? + registerProperty(PROPERTY_CHANGE_NOTIFICATION_ENABLED, PROPERTY_ID_PROPCHANGE_NOTIFY, PropertyAttribute::BOUND, &m_bPropChangeNotifyEnabled, cppu::UnoType<bool>::get()); +} + +ORowSet::~ORowSet() +{ + if ( !m_rBHelper.bDisposed && !m_rBHelper.bInDispose ) + { + SAL_WARN("dbaccess", "Please check who doesn't dispose this component!"); + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +void ORowSet::getPropertyDefaultByHandle( sal_Int32 _nHandle, Any& _rDefault ) const +{ + switch( _nHandle ) + { + case PROPERTY_ID_COMMAND_TYPE: + _rDefault <<= CommandType::COMMAND; + break; + case PROPERTY_ID_IGNORERESULT: + _rDefault <<= false; + break; + case PROPERTY_ID_APPLYFILTER: + _rDefault <<= false; + break; + case PROPERTY_ID_ISMODIFIED: + _rDefault <<= false; + break; + case PROPERTY_ID_ISBOOKMARKABLE: + _rDefault <<= true; + break; + case PROPERTY_ID_CANUPDATEINSERTEDROWS: + _rDefault <<= true; + break; + case PROPERTY_ID_RESULTSETTYPE: + _rDefault <<= ResultSetType::SCROLL_INSENSITIVE; + break; + case PROPERTY_ID_RESULTSETCONCURRENCY: + _rDefault <<= ResultSetConcurrency::UPDATABLE; + break; + case PROPERTY_ID_FETCHDIRECTION: + _rDefault <<= FetchDirection::FORWARD; + break; + case PROPERTY_ID_FETCHSIZE: + _rDefault <<= static_cast<sal_Int32>(1); + break; + case PROPERTY_ID_ESCAPE_PROCESSING: + _rDefault <<= true; + break; + case PROPERTY_ID_MAXROWS: + _rDefault <<= sal_Int32( 0 ); + break; + case PROPERTY_ID_FILTER: + case PROPERTY_ID_HAVING_CLAUSE: + case PROPERTY_ID_GROUP_BY: + case PROPERTY_ID_ORDER: + case PROPERTY_ID_UPDATE_CATALOGNAME: + case PROPERTY_ID_UPDATE_SCHEMANAME: + case PROPERTY_ID_UPDATE_TABLENAME: + _rDefault <<= OUString(); + break; + } +} + +void SAL_CALL ORowSet::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + switch(nHandle) + { + case PROPERTY_ID_ISMODIFIED: + m_bModified = cppu::any2bool(rValue); + break; + case PROPERTY_ID_FETCHDIRECTION: + if( m_nResultSetType == ResultSetType::FORWARD_ONLY) + throw Exception("resultsettype is FORWARD_ONLY", nullptr); + [[fallthrough]]; + default: + OPropertyStateContainer::setFastPropertyValue_NoBroadcast(nHandle,rValue); + } + + if ( ( nHandle == PROPERTY_ID_ACTIVE_CONNECTION ) + || ( nHandle == PROPERTY_ID_DATASOURCENAME ) + || ( nHandle == PROPERTY_ID_COMMAND ) + || ( nHandle == PROPERTY_ID_COMMAND_TYPE ) + || ( nHandle == PROPERTY_ID_IGNORERESULT ) + || ( nHandle == PROPERTY_ID_FILTER ) + || ( nHandle == PROPERTY_ID_HAVING_CLAUSE ) + || ( nHandle == PROPERTY_ID_GROUP_BY ) + || ( nHandle == PROPERTY_ID_APPLYFILTER ) + || ( nHandle == PROPERTY_ID_ORDER ) + || ( nHandle == PROPERTY_ID_URL ) + || ( nHandle == PROPERTY_ID_USER ) + ) + { + m_bCommandFacetsDirty = true; + } + + + switch(nHandle) + { + case PROPERTY_ID_ACTIVE_CONNECTION: + // the new connection + { + assert(m_aActiveConnection == rValue); + Reference< XConnection > xNewConnection(m_aActiveConnection,UNO_QUERY); + setActiveConnection(xNewConnection, false); + } + + m_bOwnConnection = false; + m_bRebuildConnOnExecute = false; + break; + + case PROPERTY_ID_DATASOURCENAME: + if(!m_xStatement.is()) + { + Reference< XConnection > xNewConn; + Any aNewConn; + aNewConn <<= xNewConn; + setFastPropertyValue(PROPERTY_ID_ACTIVE_CONNECTION, aNewConn); + } + else + m_bRebuildConnOnExecute = true; + break; + case PROPERTY_ID_FETCHSIZE: + if(m_pCache) + { + m_pCache->setFetchSize(m_nFetchSize); + fireRowcount(); + } + break; + case PROPERTY_ID_URL: + // is the connection-to-be-built determined by the url (which is the case if m_aDataSourceName is empty) ? + if (m_aDataSourceName.isEmpty()) + { + // are we active at the moment ? + if (m_xStatement.is()) + // yes -> the next execute needs to rebuild our connection because of this new property + m_bRebuildConnOnExecute = true; + else + { // no -> drop our active connection (if we have one) as it doesn't correspond to this new property value anymore + Reference< XConnection > xNewConn; + Any aNewConn; + aNewConn <<= xNewConn; + setFastPropertyValue(PROPERTY_ID_ACTIVE_CONNECTION, aNewConn); + } + } + m_bOwnConnection = true; + break; + case PROPERTY_ID_TYPEMAP: + m_xTypeMap.set(m_aTypeMap, css::uno::UNO_QUERY); + break; + case PROPERTY_ID_PROPCHANGE_NOTIFY: + m_bPropChangeNotifyEnabled = ::cppu::any2bool(rValue); + break; + default: + break; + } +} + +void SAL_CALL ORowSet::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + if(m_pCache) + { + switch(nHandle) + { + case PROPERTY_ID_ISMODIFIED: + rValue <<= m_bModified; + break; + case PROPERTY_ID_ISNEW: + rValue <<= m_bNew; + break; + case PROPERTY_ID_PRIVILEGES: + rValue <<= m_pCache->m_nPrivileges; + break; + case PROPERTY_ID_ACTIVE_CONNECTION: + rValue <<= m_xActiveConnection; + break; + case PROPERTY_ID_TYPEMAP: + rValue <<= m_xTypeMap; + break; + default: + ORowSetBase::getFastPropertyValue(rValue,nHandle); + } + } + else + { + switch(nHandle) + { + case PROPERTY_ID_ACTIVE_CONNECTION: + rValue <<= m_xActiveConnection; + break; + case PROPERTY_ID_TYPEMAP: + rValue <<= m_xTypeMap; + break; + case PROPERTY_ID_PROPCHANGE_NOTIFY: + rValue <<= m_bPropChangeNotifyEnabled; + break; + default: + ORowSetBase::getFastPropertyValue(rValue,nHandle); + } + } +} + +// css::XTypeProvider +Sequence< Type > SAL_CALL ORowSet::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + ::comphelper::concatSequences(ORowSet_BASE1::getTypes(),ORowSetBase::getTypes())); + return aTypes.getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL ORowSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::XInterface +Any SAL_CALL ORowSet::queryInterface( const Type & rType ) +{ + return ORowSet_BASE1::queryInterface( rType); +} + +void SAL_CALL ORowSet::acquire() noexcept +{ + ORowSet_BASE1::acquire(); +} + +void SAL_CALL ORowSet::release() noexcept +{ + ORowSet_BASE1::release(); +} + +// css::XUnoTunnel +sal_Int64 SAL_CALL ORowSet::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence< sal_Int8 > & ORowSet::getUnoTunnelId() +{ + static const comphelper::UnoIdInit s_Id; + return s_Id.getSeq(); +} + +// css::XAggregation +Any SAL_CALL ORowSet::queryAggregation( const Type& rType ) +{ + Any aRet(ORowSetBase::queryInterface(rType)); + if (!aRet.hasValue()) + aRet = ORowSet_BASE1::queryAggregation(rType); + return aRet; +} + +// css::XServiceInfo +OUString SAL_CALL ORowSet::getImplementationName() +{ + return "com.sun.star.comp.dba.ORowSet"; +} + +sal_Bool SAL_CALL ORowSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL ORowSet::getSupportedServiceNames() +{ + return { SERVICE_SDBC_RESULTSET, SERVICE_SDBC_ROWSET, SERVICE_SDBCX_RESULTSET, + SERVICE_SDB_RESULTSET, SERVICE_SDB_ROWSET }; +} + +// OComponentHelper +void SAL_CALL ORowSet::disposing() +{ + OPropertyStateContainer::disposing(); + + MutexGuard aGuard(m_aMutex); + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast< XComponent* >(this); + m_aRowsetListeners.disposeAndClear( aDisposeEvent ); + m_aApproveListeners.disposeAndClear( aDisposeEvent ); + m_aRowsChangeListener.disposeAndClear( aDisposeEvent ); + + freeResources( true ); + + // remove myself as dispose listener + Reference< XComponent > xComponent(m_xActiveConnection, UNO_QUERY); + if (xComponent.is()) + { + Reference<XEventListener> xEvt; + query_aggregation(this,xEvt); + xComponent->removeEventListener(xEvt); + } + + m_aActiveConnection = Any(); // the any contains a reference too + if(m_bOwnConnection) + ::comphelper::disposeComponent(m_xActiveConnection); + m_xActiveConnection = nullptr; + + + ORowSetBase::disposing(); +} + +void ORowSet::freeResources( bool _bComplete ) +{ + MutexGuard aGuard(m_aMutex); + + // free all clones + for (auto const& clone : m_aClones) + { + Reference< XComponent > xComp(clone.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_aClones.clear(); + + doCancelModification(); + + m_aBookmark = Any(); + m_bBeforeFirst = true; + m_bAfterLast = false; + m_bNew = false; + m_bModified = false; + m_bIsInsertRow = false; + m_bLastKnownRowCountFinal = false; + m_nLastKnownRowCount = 0; + + if ( !_bComplete ) + return; + + // the columns must be disposed before the querycomposer is disposed because + // their owner can be the composer + TDataColumns().swap(m_aDataColumns);// clear and resize capacity + std::vector<bool>().swap(m_aReadOnlyDataColumns); + + m_xColumns = nullptr; + if ( m_pColumns ) + m_pColumns->disposing(); + // dispose the composer to avoid that everybody knows that the querycomposer is eol + try { ::comphelper::disposeComponent( m_xComposer ); } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + m_xComposer = nullptr; + } + + // let our warnings container forget the reference to the (possibly disposed) old result set + m_aWarnings.setExternalWarnings( nullptr ); + + m_pCache.reset(); + + impl_resetTables_nothrow(); + + m_xStatement = nullptr; + m_xTypeMap = nullptr; + + if ( m_aOldRow.is() ) + m_aOldRow->clearRow(); + + impl_disposeParametersContainer_nothrow(); + + m_bCommandFacetsDirty = true; +} + +void ORowSet::setActiveConnection( Reference< XConnection > const & _rxNewConn, bool _bFireEvent ) +{ + if (_rxNewConn.get() == m_xActiveConnection.get()) + // nothing to do + return; + + // remove the event listener for the old connection + Reference< XComponent > xComponent(m_xActiveConnection, UNO_QUERY); + if (xComponent.is()) + { + Reference<XEventListener> xListener; + query_aggregation(this, xListener); + xComponent->removeEventListener(xListener); + } + + // if we owned the connection, remember it for later disposing + if(m_bOwnConnection) + m_xOldConnection = m_xActiveConnection; + + // for firing the PropertyChangeEvent + sal_Int32 nHandle = PROPERTY_ID_ACTIVE_CONNECTION; + Any aOldConnection; aOldConnection <<= m_xActiveConnection; + Any aNewConnection; aNewConnection <<= _rxNewConn; + + // set the new connection + m_xActiveConnection = _rxNewConn; + if (m_xActiveConnection.is()) + m_aActiveConnection <<= m_xActiveConnection; + else + m_aActiveConnection.clear(); + + // fire the event + if (_bFireEvent) + fire(&nHandle, &aNewConnection, &aOldConnection, 1, false); + + // register as event listener for the new connection + xComponent.set(m_xActiveConnection,UNO_QUERY); + if (xComponent.is()) + { + Reference<XEventListener> xListener; + query_aggregation(this, xListener); + xComponent->addEventListener(xListener); + } +} + +// css::XEventListener +void SAL_CALL ORowSet::disposing( const css::lang::EventObject& Source ) +{ + // close rowset because the connection is going to be deleted (someone told me :-) + Reference<XConnection> xCon(Source.Source,UNO_QUERY); + if(m_xActiveConnection == xCon) + { + close(); + { + MutexGuard aGuard( m_aMutex ); + Reference< XConnection > xXConnection; + setActiveConnection( xXConnection ); + } + } +} + +// XCloseable +void SAL_CALL ORowSet::close( ) +{ + { + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + } + // additionals things to set + freeResources( true ); +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ORowSet::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& SAL_CALL ORowSet::getInfoHelper() +{ + return *::comphelper::OPropertyArrayUsageHelper<ORowSet>::getArrayHelper(); +} + +void ORowSet::updateValue(sal_Int32 columnIndex,const ORowSetValue& x) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + + ORowSetValueVector::Vector& rRow = **m_aCurrentRow; + ORowSetNotifier aNotify(this, std::vector(rRow)); + m_pCache->updateValue(columnIndex,x,rRow,aNotify.getChangedColumns()); + m_bModified = m_bModified || !aNotify.getChangedColumns().empty(); + aNotify.firePropertyChange(); +} + +// XRowUpdate +void SAL_CALL ORowSet::updateNull( sal_Int32 columnIndex ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + + ORowSetValueVector::Vector& rRow = **m_aCurrentRow; + ORowSetNotifier aNotify(this, std::vector(rRow)); + m_pCache->updateNull(columnIndex,rRow,aNotify.getChangedColumns()); + m_bModified = m_bModified || !aNotify.getChangedColumns().empty(); + aNotify.firePropertyChange(); +} + +void SAL_CALL ORowSet::updateBoolean( sal_Int32 columnIndex, sal_Bool x ) +{ + updateValue(columnIndex, static_cast<bool>(x)); +} + +void SAL_CALL ORowSet::updateByte( sal_Int32 columnIndex, sal_Int8 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateShort( sal_Int32 columnIndex, sal_Int16 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateInt( sal_Int32 columnIndex, sal_Int32 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateLong( sal_Int32 columnIndex, sal_Int64 x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateFloat( sal_Int32 columnIndex, float x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateDouble( sal_Int32 columnIndex, double x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateString( sal_Int32 columnIndex, const OUString& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateBytes( sal_Int32 columnIndex, const Sequence< sal_Int8 >& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateDate( sal_Int32 columnIndex, const css::util::Date& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateTime( sal_Int32 columnIndex, const css::util::Time& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) +{ + updateValue(columnIndex,x); +} + +void SAL_CALL ORowSet::updateBinaryStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + + { + Sequence<sal_Int8> aSeq; + if(x.is()) + x->readBytes(aSeq,length); + updateValue(columnIndex,aSeq); + } +} + +void SAL_CALL ORowSet::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + ORowSetValueVector::Vector& rRow = **m_aCurrentRow; + ORowSetNotifier aNotify(this, std::vector(rRow)); + m_pCache->updateCharacterStream(columnIndex,x,length,rRow,aNotify.getChangedColumns()); + m_bModified = m_bModified || !aNotify.getChangedColumns().empty(); + aNotify.firePropertyChange(); +} + +void SAL_CALL ORowSet::updateObject( sal_Int32 columnIndex, const Any& x ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + + Any aNewValue = x; + + if ( m_pColumns ) + { + Reference<XPropertySet> xColumn(m_pColumns->getByIndex(columnIndex-1),UNO_QUERY); + sal_Int32 nColType = 0; + xColumn->getPropertyValue(PROPERTY_TYPE) >>= nColType; + switch( nColType ) + { + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + { + double nValue = 0; + if ( x >>= nValue ) + { + if ( DataType::TIMESTAMP == nColType ) + aNewValue <<= dbtools::DBTypeConversion::toDateTime( nValue ); + else if ( DataType::DATE == nColType ) + aNewValue <<= dbtools::DBTypeConversion::toDate( nValue ); + else + aNewValue <<= dbtools::DBTypeConversion::toTime( nValue ); + } + break; + } + } + } + + if (!::dbtools::implUpdateObject(this, columnIndex, aNewValue)) + { // there is no other updateXXX call which can handle the value in x + ORowSetValueVector::Vector& rRow = **m_aCurrentRow; + ORowSetNotifier aNotify(this, std::vector(rRow)); + m_pCache->updateObject(columnIndex,aNewValue,rRow,aNotify.getChangedColumns()); + m_bModified = m_bModified || !aNotify.getChangedColumns().empty(); + aNotify.firePropertyChange(); + } +} + +void SAL_CALL ORowSet::updateNumericObject( sal_Int32 columnIndex, const Any& x, sal_Int32 /*scale*/ ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkUpdateConditions(columnIndex); + checkUpdateIterator(); + ORowSetValueVector::Vector& rRow = **m_aCurrentRow; + ORowSetNotifier aNotify(this, std::vector(rRow)); + m_pCache->updateNumericObject(columnIndex,x,rRow,aNotify.getChangedColumns()); + m_bModified = m_bModified || !aNotify.getChangedColumns().empty(); + aNotify.firePropertyChange(); +} + +namespace +{ + class ProtectFlag + { + bool& m_rInsertingRow; + public: + explicit ProtectFlag(bool& rInsertingRow) + : m_rInsertingRow(rInsertingRow) + { + if (m_rInsertingRow) + { + throw std::runtime_error("recursion in insertRow"); + } + m_rInsertingRow = true; + } + ~ProtectFlag() + { + m_rInsertingRow = false; + } + }; +} + +// XResultSetUpdate +void SAL_CALL ORowSet::insertRow() +{ + ProtectFlag aFlagControl(m_bInsertingRow); + + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + // insertRow is not allowed when + // standing not on the insert row nor + // when the row isn't modified + // or the concurrency is read only + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + if(!m_pCache || !m_bNew || !m_bModified || m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY) + throwFunctionSequenceException(*this); + + // remember old value for fire + bool bOld = m_bNew; + + ORowSetRow aOldValues; + if ( !m_aCurrentRow.isNull() ) + aOldValues = new ORowSetValueVector( *(*m_aCurrentRow)); + Sequence<Any> aChangedBookmarks; + RowsChangeEvent aEvt(*this,RowChangeAction::INSERT,1,aChangedBookmarks); + notifyAllListenersRowBeforeChange(aGuard,aEvt); + + std::vector< Any > aBookmarks; + bool bInserted = m_pCache->insertRow(aBookmarks); + + // make sure that our row is set to the new inserted row before clearing the insert flags in the cache + m_pCache->resetInsertRow(bInserted); + + // notification order + // - column values + setCurrentRow( false, true, aOldValues, aGuard ); // we don't move here + + // read-only flag restored + impl_restoreDataColumnsWriteable_throw(); + + // - rowChanged + notifyAllListenersRowChanged(aGuard,aEvt); + + if ( !aBookmarks.empty() ) + { + RowsChangeEvent aUpEvt(*this,RowChangeAction::UPDATE,aBookmarks.size(),comphelper::containerToSequence(aBookmarks)); + notifyAllListenersRowChanged(aGuard,aUpEvt); + } + + // - IsModified + if(!m_bModified) + fireProperty(PROPERTY_ID_ISMODIFIED,false,true); + OSL_ENSURE( !m_bModified, "ORowSet::insertRow: just updated, but _still_ modified?" ); + + // - IsNew + if(m_bNew != bOld) + fireProperty(PROPERTY_ID_ISNEW,m_bNew,bOld); + + // - RowCount/IsRowCountFinal + fireRowcount(); +} + +void SAL_CALL ORowSet::updateRow( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + // not allowed when standing on insert row + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + if ( !m_pCache || m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY || m_bNew || ((m_pCache->m_nPrivileges & Privilege::UPDATE ) != Privilege::UPDATE) ) + throwFunctionSequenceException(*this); + + + if(!m_bModified) + return; + + ORowSetRow aOldValues; + if ( !m_aCurrentRow.isNull() ) + aOldValues = new ORowSetValueVector( *(*m_aCurrentRow) ); + + Sequence<Any> aChangedBookmarks; + RowsChangeEvent aEvt(*this,RowChangeAction::UPDATE,1,aChangedBookmarks); + notifyAllListenersRowBeforeChange(aGuard,aEvt); + + std::vector< Any > aBookmarks; + m_pCache->updateRow(m_aCurrentRow.operator ->(),aBookmarks); + if ( !aBookmarks.empty() ) + aEvt.Bookmarks = comphelper::containerToSequence(aBookmarks); + aEvt.Rows += aBookmarks.size(); + m_aBookmark = m_pCache->getBookmark(); + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + if ( m_pCache->m_aMatrixIter != m_pCache->getEnd() && (*m_pCache->m_aMatrixIter).is() ) + { + if ( m_pCache->isResultSetChanged() ) + { + impl_rebuild_throw(aGuard); + } + else + { + m_aOldRow->setRow(new ORowSetValueVector(*(*m_aCurrentRow))); + + // notification order + // - column values + ORowSetBase::firePropertyChange(aOldValues); + } + // - rowChanged + notifyAllListenersRowChanged(aGuard,aEvt); + + // - IsModified + if(!m_bModified) + fireProperty(PROPERTY_ID_ISMODIFIED,false,true); + OSL_ENSURE( !m_bModified, "ORowSet::updateRow: just updated, but _still_ modified?" ); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + else if ( !m_bAfterLast ) // the update went wrong + { + ::dbtools::throwSQLException( DBA_RES( RID_STR_UPDATE_FAILED ), StandardSQLState::INVALID_CURSOR_POSITION, *this ); + } +} + +void SAL_CALL ORowSet::deleteRow( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( m_bBeforeFirst || m_bAfterLast ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_DELETE_BEFORE_AFTER ), StandardSQLState::INVALID_CURSOR_POSITION, *this ); + if ( m_bNew ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_DELETE_INSERT_ROW ), StandardSQLState::INVALID_CURSOR_POSITION, *this ); + if ( m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_RESULT_IS_READONLY ), StandardSQLState::FUNCTION_SEQUENCE_ERROR, *this ); + if ( ( m_pCache->m_nPrivileges & Privilege::DELETE ) != Privilege::DELETE ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_DELETE_PRIVILEGE ), StandardSQLState::FUNCTION_SEQUENCE_ERROR, *this ); + if ( rowDeleted() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_ROW_ALREADY_DELETED ), StandardSQLState::FUNCTION_SEQUENCE_ERROR, *this ); + + // this call position the cache indirect + Any aBookmarkToDelete( m_aBookmark ); + positionCache( MOVE_NONE ); + sal_Int32 nDeletePosition = m_pCache->getRow(); + + notifyRowSetAndClonesRowDelete( aBookmarkToDelete ); + + ORowSetRow aOldValues; + if ( m_pCache->m_aMatrixIter != m_pCache->getEnd() && m_pCache->m_aMatrixIter->is() ) + aOldValues = new ORowSetValueVector( *(*(m_pCache->m_aMatrixIter)) ); + + Sequence<Any> aChangedBookmarks; + RowsChangeEvent aEvt(*this,RowChangeAction::DELETE,1,aChangedBookmarks); + notifyAllListenersRowBeforeChange(aGuard,aEvt); + + m_pCache->deleteRow(); + notifyRowSetAndClonesRowDeleted( aBookmarkToDelete, nDeletePosition ); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + // notification order + // - rowChanged + notifyAllListenersRowChanged(aGuard,aEvt); + + // - IsModified + // - IsNew + aNotifier.fire( ); + + // - RowCount/IsRowCountFinal + fireRowcount(); +} + +void ORowSet::implCancelRowUpdates( bool _bNotifyModified ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( *m_pMutex ); + if ( m_bBeforeFirst || m_bAfterLast || rowDeleted() ) + return; // nothing to do so return + + checkCache(); + // cancelRowUpdates is not allowed when: + // - standing on the insert row + // - the concurrency is read only + // - the current row is deleted + if ( m_bNew || m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY ) + throwFunctionSequenceException(*this); + + positionCache( MOVE_NONE ); + + ORowSetRow aOldValues; + if ( !m_bModified && _bNotifyModified && !m_aCurrentRow.isNull() ) + aOldValues = new ORowSetValueVector( *(*m_aCurrentRow) ); + + m_pCache->cancelRowUpdates(); + + m_aBookmark = m_pCache->getBookmark(); + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + + // notification order + // IsModified + if( !m_bModified && _bNotifyModified ) + { + // - column values + ORowSetBase::firePropertyChange(aOldValues); + fireProperty(PROPERTY_ID_ISMODIFIED,false,true); + } +} + +void SAL_CALL ORowSet::cancelRowUpdates( ) +{ + implCancelRowUpdates( true ); +} + +void SAL_CALL ORowSet::addRowSetListener( const Reference< XRowSetListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + if(listener.is()) + m_aRowsetListeners.addInterface(listener); +} + +void SAL_CALL ORowSet::removeRowSetListener( const Reference< XRowSetListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + if(listener.is()) + m_aRowsetListeners.removeInterface(listener); +} + +void ORowSet::notifyAllListeners(::osl::ResettableMutexGuard& _rGuard) +{ + EventObject aEvt(*m_pMySelf); + _rGuard.clear(); + m_aRowsetListeners.notifyEach( &XRowSetListener::rowSetChanged, aEvt ); + _rGuard.reset(); +} + +void ORowSet::notifyAllListenersCursorMoved(::osl::ResettableMutexGuard& _rGuard) +{ + EventObject aEvt(*m_pMySelf); + _rGuard.clear(); + m_aRowsetListeners.notifyEach( &XRowSetListener::cursorMoved, aEvt ); + _rGuard.reset(); +} + +void ORowSet::notifyAllListenersRowChanged(::osl::ResettableMutexGuard& _rGuard, const RowsChangeEvent& aEvt) +{ + _rGuard.clear(); + m_aRowsetListeners.notifyEach( &XRowSetListener::rowChanged, static_cast<EventObject>(aEvt) ); + m_aRowsChangeListener.notifyEach( &XRowsChangeListener::rowsChanged, aEvt ); + _rGuard.reset(); +} + +bool ORowSet::notifyAllListenersCursorBeforeMove(::osl::ResettableMutexGuard& _rGuard) +{ + EventObject aEvt(*m_pMySelf); + std::vector< Reference< css::sdb::XRowSetApproveListener > > aListenerSeq = m_aApproveListeners.getElements(); + _rGuard.clear(); + bool bCheck = std::all_of(aListenerSeq.rbegin(), aListenerSeq.rend(), + [&aEvt](Reference<css::sdb::XRowSetApproveListener>& rxItem) { + try + { + return static_cast<bool>(rxItem->approveCursorMove(aEvt)); + } + catch( RuntimeException& ) + { + return true; + } + }); + _rGuard.reset(); + return bCheck; +} + +void ORowSet::notifyAllListenersRowBeforeChange(::osl::ResettableMutexGuard& _rGuard,const RowChangeEvent &aEvt) +{ + std::vector< Reference< css::sdb::XRowSetApproveListener > > aListenerSeq = m_aApproveListeners.getElements(); + _rGuard.clear(); + bool bCheck = std::all_of(aListenerSeq.rbegin(), aListenerSeq.rend(), + [&aEvt](Reference<css::sdb::XRowSetApproveListener>& rxItem) { + try + { + return static_cast<bool>(rxItem->approveRowChange(aEvt)); + } + catch( RuntimeException& ) + { + return true; + } + }); + _rGuard.reset(); + + if ( !bCheck ) + m_aErrors.raiseTypedException( sdb::ErrorCondition::ROW_SET_OPERATION_VETOED, *this, ::cppu::UnoType< RowSetVetoException >::get() ); +} + +void ORowSet::fireRowcount() +{ + sal_Int32 nCurrentRowCount( impl_getRowCount() ); + bool bCurrentRowCountFinal( m_pCache->m_bRowCountFinal ); + + if ( m_nLastKnownRowCount != nCurrentRowCount ) + { + sal_Int32 nHandle = PROPERTY_ID_ROWCOUNT; + Any aNew,aOld; + aNew <<= nCurrentRowCount; aOld <<= m_nLastKnownRowCount; + fire(&nHandle,&aNew,&aOld,1,false); + m_nLastKnownRowCount = nCurrentRowCount; + } + if ( !m_bLastKnownRowCountFinal && ( m_bLastKnownRowCountFinal != bCurrentRowCountFinal ) ) + { + sal_Int32 nHandle = PROPERTY_ID_ISROWCOUNTFINAL; + Any aNew,aOld; + aNew <<= bCurrentRowCountFinal; + aOld <<= m_bLastKnownRowCountFinal; + fire(&nHandle,&aNew,&aOld,1,false); + m_bLastKnownRowCountFinal = bCurrentRowCountFinal; + } +} + +void SAL_CALL ORowSet::moveToInsertRow( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkPositioningAllowed(); + if ( ( m_pCache->m_nPrivileges & Privilege::INSERT ) != Privilege::INSERT ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_INSERT_PRIVILEGE ), StandardSQLState::GENERAL_ERROR, *this ); + + if ( !notifyAllListenersCursorBeforeMove( aGuard ) ) + return; + + // remember old value for fire + ORowSetRow aOldValues; + if ( rowDeleted() ) + { + positionCache( MOVE_FORWARD ); + m_pCache->next(); + setCurrentRow( true, false, aOldValues, aGuard); + } + else + positionCache( MOVE_NONE ); + + // check before because the resultset could be empty + if ( !m_bBeforeFirst + && !m_bAfterLast + && m_pCache->m_aMatrixIter != m_pCache->getEnd() + && m_pCache->m_aMatrixIter->is() + ) + aOldValues = new ORowSetValueVector( *(*(m_pCache->m_aMatrixIter)) ); + + const bool bNewState = m_bNew; + const bool bModState = m_bModified; + + m_pCache->moveToInsertRow(); + m_aCurrentRow = m_pCache->m_aInsertRow; + m_bIsInsertRow = true; + + // set read-only flag to false + impl_setDataColumnsWriteable_throw(); + + // notification order + // - column values + ORowSetBase::firePropertyChange(aOldValues); + + // - cursorMoved + notifyAllListenersCursorMoved(aGuard); + + // - IsModified + if ( bModState != m_bModified ) + fireProperty( PROPERTY_ID_ISMODIFIED, m_bModified, bModState ); + + // - IsNew + if ( bNewState != m_bNew ) + fireProperty( PROPERTY_ID_ISNEW, m_bNew, bNewState ); + + // - RowCount/IsRowCountFinal + fireRowcount(); +} + +void ORowSet::impl_setDataColumnsWriteable_throw() +{ + impl_restoreDataColumnsWriteable_throw(); + m_aReadOnlyDataColumns.resize(m_aDataColumns.size(),false); + std::vector<bool, std::allocator<bool> >::iterator aReadIter = m_aReadOnlyDataColumns.begin(); + for (auto const& dataColumn : m_aDataColumns) + { + bool bReadOnly = false; + dataColumn->getPropertyValue(PROPERTY_ISREADONLY) >>= bReadOnly; + *aReadIter = bReadOnly; + + dataColumn->setPropertyValue(PROPERTY_ISREADONLY,Any(false)); + ++aReadIter; + } +} + +void ORowSet::impl_restoreDataColumnsWriteable_throw() +{ + assert(m_aDataColumns.size() == m_aReadOnlyDataColumns.size() || m_aReadOnlyDataColumns.size() == 0 ); + TDataColumns::const_iterator aIter = m_aDataColumns.begin(); + for (bool readOnlyDataColumn : m_aReadOnlyDataColumns) + { + (*aIter)->setPropertyValue(PROPERTY_ISREADONLY, Any(readOnlyDataColumn) ); + ++aIter; + } + m_aReadOnlyDataColumns.clear(); +} + +void SAL_CALL ORowSet::moveToCurrentRow( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkPositioningAllowed(); + + if ( !m_pCache->m_bNew && !m_bModified ) + // nothing to do if we're not on the insertion row, and not modified otherwise + return; + + if ( rowDeleted() ) + // this would perhaps even justify a RuntimeException... + // if the current row is deleted, then no write access to this row should be possible. So, + // m_bModified should be true. Also, as soon as somebody calls moveToInsertRow, + // our current row should not be deleted anymore. So, we should not have survived the above + // check "if ( !m_pCache->m_bNew && !m_bModified )" + ::dbtools::throwSQLException( DBA_RES( RID_STR_ROW_ALREADY_DELETED ), StandardSQLState::FUNCTION_SEQUENCE_ERROR, *this ); + + if ( !notifyAllListenersCursorBeforeMove( aGuard ) ) + return; + + positionCache( MOVE_NONE_REFRESH ); + + ORowSetNotifier aNotifier( this ); + + // notification order + // - cursorMoved + notifyAllListenersCursorMoved(aGuard); + + // - IsModified + // - IsNew + aNotifier.fire(); +} + +// XRow +sal_Bool SAL_CALL ORowSet::wasNull( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + return ( m_pCache && isInsertRow() ) ? (**m_pCache->m_aInsertRow)[m_nLastColumnIndex].isNull() : ORowSetBase::wasNull(); +} + +const ORowSetValue& ORowSet::getInsertValue(sal_Int32 columnIndex) +{ + checkCache(); + + if ( m_pCache && isInsertRow() ) + { + m_nLastColumnIndex = columnIndex; + return (**m_pCache->m_aInsertRow)[m_nLastColumnIndex]; + } + return getValue(columnIndex); +} + +OUString SAL_CALL ORowSet::getString( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getString(); +} + +sal_Bool SAL_CALL ORowSet::getBoolean( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getBool(); +} + +sal_Int8 SAL_CALL ORowSet::getByte( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getInt8(); +} + +sal_Int16 SAL_CALL ORowSet::getShort( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getInt16(); +} + +sal_Int32 SAL_CALL ORowSet::getInt( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getInt32(); +} + +sal_Int64 SAL_CALL ORowSet::getLong( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getLong(); +} + +float SAL_CALL ORowSet::getFloat( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getFloat(); +} + +double SAL_CALL ORowSet::getDouble( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getDouble(); +} + +Sequence< sal_Int8 > SAL_CALL ORowSet::getBytes( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getSequence(); +} + +css::util::Date SAL_CALL ORowSet::getDate( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getDate(); +} + +css::util::Time SAL_CALL ORowSet::getTime( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getTime(); +} + +css::util::DateTime SAL_CALL ORowSet::getTimestamp( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).getDateTime(); +} + +Reference< css::io::XInputStream > SAL_CALL ORowSet::getBinaryStream( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + if ( m_pCache && isInsertRow() ) + { + checkCache(); + m_nLastColumnIndex = columnIndex; + return new ::comphelper::SequenceInputStream((**m_pCache->m_aInsertRow)[m_nLastColumnIndex].getSequence()); + } + + return ORowSetBase::getBinaryStream(columnIndex); +} + +Reference< css::io::XInputStream > SAL_CALL ORowSet::getCharacterStream( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + if(m_pCache && isInsertRow() ) + { + checkCache(); + m_nLastColumnIndex = columnIndex; + return new ::comphelper::SequenceInputStream((**m_pCache->m_aInsertRow)[m_nLastColumnIndex].getSequence()); + } + + return ORowSetBase::getCharacterStream(columnIndex); +} + +Any SAL_CALL ORowSet::getObject( sal_Int32 columnIndex, const Reference< XNameAccess >& /*typeMap*/ ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getInsertValue(columnIndex).makeAny(); +} + +Reference< XRef > SAL_CALL ORowSet::getRef( sal_Int32 /*columnIndex*/ ) +{ + return Reference< XRef >(); +} + +Reference< XBlob > SAL_CALL ORowSet::getBlob( sal_Int32 columnIndex ) +{ + if ( m_pCache && isInsertRow() ) + { + checkCache(); + m_nLastColumnIndex = columnIndex; + return new ::connectivity::BlobHelper((**m_pCache->m_aInsertRow)[m_nLastColumnIndex].getSequence()); + } + return ORowSetBase::getBlob(columnIndex); +} + +Reference< XClob > SAL_CALL ORowSet::getClob( sal_Int32 columnIndex ) +{ + return Reference< XClob >(getInsertValue(columnIndex).makeAny(),UNO_QUERY); +} + +Reference< XArray > SAL_CALL ORowSet::getArray( sal_Int32 /*columnIndex*/ ) +{ + return Reference< XArray >(); +} + +void SAL_CALL ORowSet::executeWithCompletion( const Reference< XInteractionHandler >& _rxHandler ) +{ + if (!_rxHandler.is()) + execute(); + + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + // tell everybody that we will change the result set + approveExecution(); + + ResettableMutexGuard aGuard( m_aMutex ); + + try + { + freeResources( m_bCommandFacetsDirty ); + + // calc the connection to be used + if (m_xActiveConnection.is() && m_bRebuildConnOnExecute) + { + // there was a setProperty(ActiveConnection), but a setProperty(DataSource) _after_ that, too + Reference< XConnection > xXConnection; + setActiveConnection( xXConnection ); + } + calcConnection( _rxHandler ); + m_bRebuildConnOnExecute = false; + + Reference< XSingleSelectQueryComposer > xComposer = getCurrentSettingsComposer( this, m_aContext, nullptr ); + Reference<XParametersSupplier> xParameters(xComposer, UNO_QUERY); + + Reference<XIndexAccess> xParamsAsIndicies = xParameters.is() ? xParameters->getParameters() : Reference<XIndexAccess>(); + const sal_Int32 nParamCount = xParamsAsIndicies.is() ? xParamsAsIndicies->getCount() : 0; + if ( m_aParametersSet.size() < o3tl::make_unsigned(nParamCount) ) + m_aParametersSet.resize( nParamCount ,false); + + ::dbtools::askForParameters( xComposer, this, m_xActiveConnection, _rxHandler,m_aParametersSet ); + } + // ensure that only the allowed exceptions leave this block + catch(SQLException&) + { + throw; + } + catch(RuntimeException&) + { + throw; + } + catch(Exception const &) + { + TOOLS_WARN_EXCEPTION("dbaccess", "ORowSet::executeWithCompletion: caught an unexpected exception type while filling in the parameters"); + } + + // we're done with the parameters, now for the real execution + + // do the real execute + execute_NoApprove_NoNewConn(aGuard); +} + +Reference< XIndexAccess > SAL_CALL ORowSet::getParameters( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + if ( m_bCommandFacetsDirty ) + // need to rebuild the parameters, since some property which contributes to the + // complete command, and thus the parameters, changed + impl_disposeParametersContainer_nothrow(); + + if ( !m_pParameters && !m_aCommand.isEmpty() ) + { + try + { + OUString sNotInterestedIn; + impl_initComposer_throw( sNotInterestedIn ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + // our caller could change our parameters at any time + m_bParametersDirty = true; + + return m_pParameters; +} + +void ORowSet::approveExecution() +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + EventObject aEvt(*this); + + OInterfaceIteratorHelper3 aApproveIter( m_aApproveListeners ); + while ( aApproveIter.hasMoreElements() ) + { + Reference< XRowSetApproveListener > xListener( aApproveIter.next() ); + try + { + if ( !xListener->approveRowSetChange( aEvt ) ) + throw RowSetVetoException(); + } + catch ( const DisposedException& e ) + { + if ( e.Context == xListener ) + aApproveIter.remove(); + } + catch ( const RuntimeException& ) { throw; } + catch ( const RowSetVetoException& ) { throw; } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +// XRowSet +void SAL_CALL ORowSet::execute( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + // tell everybody that we will change the result set + approveExecution(); + + ResettableMutexGuard aGuard( m_aMutex ); + freeResources( m_bCommandFacetsDirty ); + + // calc the connection to be used + if (m_xActiveConnection.is() && m_bRebuildConnOnExecute) { + // there was a setProperty(ActiveConnection), but a setProperty(DataSource) _after_ that, too + Reference< XConnection> xXConnection; + setActiveConnection( xXConnection ); + } + + calcConnection(nullptr); + m_bRebuildConnOnExecute = false; + + // do the real execute + execute_NoApprove_NoNewConn(aGuard); +} + +void ORowSet::setStatementResultSetType( const Reference< XPropertySet >& _rxStatement, sal_Int32 _nDesiredResultSetType, sal_Int32 _nDesiredResultSetConcurrency ) +{ + OSL_ENSURE( _rxStatement.is(), "ORowSet::setStatementResultSetType: invalid statement - this will crash!" ); + + sal_Int32 nResultSetType( _nDesiredResultSetType ); + sal_Int32 nResultSetConcurrency( _nDesiredResultSetConcurrency ); + + // there *might* be a data source setting which tells use to be more defensive with those settings + // #i15113# + bool bRespectDriverRST = false; + Any aSetting; + if ( getDataSourceSetting( ::dbaccess::getDataSource( m_xActiveConnection ), "RespectDriverResultSetType", aSetting ) ) + { + OSL_VERIFY( aSetting >>= bRespectDriverRST ); + } + + if ( bRespectDriverRST ) + { + // try type/concurrency settings with decreasing usefulness, and rely on what the connection claims + // to support + Reference< XDatabaseMetaData > xMeta( m_xActiveConnection->getMetaData() ); + + sal_Int32 nCharacteristics[5][2] = + { { ResultSetType::SCROLL_SENSITIVE, ResultSetConcurrency::UPDATABLE }, + { ResultSetType::SCROLL_INSENSITIVE, ResultSetConcurrency::UPDATABLE }, + { ResultSetType::SCROLL_SENSITIVE, ResultSetConcurrency::READ_ONLY }, + { ResultSetType::SCROLL_INSENSITIVE, ResultSetConcurrency::READ_ONLY }, + { ResultSetType::FORWARD_ONLY, ResultSetConcurrency::READ_ONLY } + }; + sal_Int32 i=0; + if ( m_xActiveConnection->getMetaData()->isReadOnly() ) + i = 2; // if the database is read-only we only should use read-only concurrency + + for ( ; i<5; ++i ) + { + nResultSetType = nCharacteristics[i][0]; + nResultSetConcurrency = nCharacteristics[i][1]; + + // don't try type/concurrency pairs which are more featured than what our caller requested + if ( nResultSetType > _nDesiredResultSetType ) + continue; + if ( nResultSetConcurrency > _nDesiredResultSetConcurrency ) + continue; + + if ( xMeta.is() && xMeta->supportsResultSetConcurrency( nResultSetType, nResultSetConcurrency ) ) + break; + } + } + + _rxStatement->setPropertyValue( PROPERTY_RESULTSETTYPE, Any( nResultSetType ) ); + _rxStatement->setPropertyValue( PROPERTY_RESULTSETCONCURRENCY, Any( nResultSetConcurrency ) ); +} + +void ORowSet::impl_ensureStatement_throw() +{ + OUString sCommandToExecute; + if(m_bCommandFacetsDirty) + { + impl_initComposer_throw( sCommandToExecute ); + } + else + { + sCommandToExecute = m_bUseEscapeProcessing ? m_xComposer->getQueryWithSubstitution() : m_aActiveCommand; + } + + try + { + m_xStatement = m_xActiveConnection->prepareStatement( sCommandToExecute ); + if ( !m_xStatement.is() ) + { + ::dbtools::throwSQLException( DBA_RES( RID_STR_INTERNAL_ERROR ), StandardSQLState::GENERAL_ERROR, *this ); + } + + Reference< XPropertySet > xStatementProps( m_xStatement, UNO_QUERY_THROW ); + // set the result set type and concurrency + try + { + xStatementProps->setPropertyValue( PROPERTY_USEBOOKMARKS, Any( true ) ); + xStatementProps->setPropertyValue( PROPERTY_MAXROWS, Any( m_nMaxRows ) ); + + setStatementResultSetType( xStatementProps, m_nResultSetType, m_nResultSetConcurrency ); + } + catch ( const Exception& ) + { + // this exception doesn't matter here because when we catch an exception + // then the driver doesn't support this feature + } + } + catch (SQLException& rException) + { + css::sdbc::SQLException* pLastExceptionInChain = SQLExceptionInfo::getLastException(&rException); + assert(pLastExceptionInChain && "will at least be &rException"); + + // append information about what we were actually going to execute + OUString sInfo(m_sErrorString.replaceFirst("$command$", sCommandToExecute)); + css::uno::Any aAppend = SQLExceptionInfo::createException(SQLExceptionInfo::TYPE::SQLContext, sInfo, OUString(), 0); + pLastExceptionInChain->NextException = aAppend; + + // propagate + throw; + } +} + +Reference< XResultSet > ORowSet::impl_prepareAndExecute_throw() +{ + impl_ensureStatement_throw(); + + m_aParameterValueForCache->resize(1); + Reference< XParameters > xParam( m_xStatement, UNO_QUERY_THROW ); + size_t nParamCount( m_pParameters.is() ? m_pParameters->size() : m_aPrematureParamValues->size() ); + for ( size_t i=1; i<=nParamCount; ++i ) + { + ORowSetValue& rParamValue( getParameterStorage( static_cast<sal_Int32>(i) ) ); + ::dbtools::setObjectWithInfo( xParam, i, rParamValue.makeAny(), rParamValue.getTypeKind() ); + m_aParameterValueForCache->push_back(rParamValue); + } + m_bParametersDirty = false; + + Reference< XResultSet > xResultSet(m_xStatement->executeQuery()); + + OUString aComposedUpdateTableName; + if ( !m_aUpdateTableName.isEmpty() ) + aComposedUpdateTableName = composeTableName( m_xActiveConnection->getMetaData(), m_aUpdateCatalogName, m_aUpdateSchemaName, m_aUpdateTableName, false, ::dbtools::EComposeRule::InDataManipulation ); + + SAL_INFO("dbaccess", "ORowSet::impl_prepareAndExecute_throw: creating cache" ); + m_pCache = + std::make_shared<ORowSetCache>(xResultSet, m_xComposer.get(), m_aContext, aComposedUpdateTableName, + m_bModified, m_bNew, *m_aParameterValueForCache, m_aFilter, m_nMaxRows); + if ( m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY ) + { + m_nPrivileges = Privilege::SELECT; + m_pCache->m_nPrivileges = Privilege::SELECT; + } + m_pCache->setFetchSize(m_nFetchSize); + m_aCurrentRow = m_pCache->createIterator(this); + m_bIsInsertRow = false; + m_aOldRow = m_pCache->registerOldRow(); + + return xResultSet; +} + +void ORowSet::impl_initializeColumnSettings_nothrow( const Reference< XPropertySet >& _rxTemplateColumn, const Reference< XPropertySet >& _rxRowSetColumn ) +{ + OSL_ENSURE( _rxTemplateColumn.is() && _rxRowSetColumn.is(), + "ORowSet::impl_initializeColumnSettings_nothrow: this will crash!" ); + + bool bHaveAnyColumnSetting = false; + try + { + Reference< XPropertySetInfo > xInfo( _rxTemplateColumn->getPropertySetInfo(), UNO_SET_THROW ); + + // a number of properties is plain copied + const OUString aPropertyNames[] = { + OUString(PROPERTY_ALIGN), OUString(PROPERTY_RELATIVEPOSITION), OUString(PROPERTY_WIDTH), OUString(PROPERTY_HIDDEN), OUString(PROPERTY_CONTROLMODEL), + OUString(PROPERTY_HELPTEXT), OUString(PROPERTY_CONTROLDEFAULT) + }; + for (const auto & aPropertyName : aPropertyNames) + { + if ( xInfo->hasPropertyByName( aPropertyName ) ) + { + _rxRowSetColumn->setPropertyValue( aPropertyName, _rxTemplateColumn->getPropertyValue( aPropertyName ) ); + bHaveAnyColumnSetting = true; + } + } + + // the format key is slightly more complex + sal_Int32 nFormatKey = 0; + if( xInfo->hasPropertyByName( PROPERTY_NUMBERFORMAT ) ) + { + _rxTemplateColumn->getPropertyValue( PROPERTY_NUMBERFORMAT ) >>= nFormatKey; + bHaveAnyColumnSetting = true; + } + if ( !nFormatKey && m_xNumberFormatTypes.is() ) + nFormatKey = ::dbtools::getDefaultNumberFormat( _rxTemplateColumn, m_xNumberFormatTypes, SvtSysLocale().GetLanguageTag().getLocale() ); + _rxRowSetColumn->setPropertyValue( PROPERTY_NUMBERFORMAT, Any( nFormatKey ) ); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + return; + } + + if ( bHaveAnyColumnSetting ) + return; + + // the template column could not provide *any* setting. Okay, probably it's a parser column, which + // does not offer those. However, perhaps the template column refers to a table column, which we + // can use as new template column + try + { + Reference< XPropertySetInfo > xInfo( _rxTemplateColumn->getPropertySetInfo(), UNO_SET_THROW ); + if ( !xInfo->hasPropertyByName( PROPERTY_TABLENAME ) ) + // no chance + return; + + OUString sTableName; + OSL_VERIFY( _rxTemplateColumn->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName ); + + Reference< XNameAccess > xTables( impl_getTables_throw(), UNO_SET_THROW ); + if ( !xTables->hasByName( sTableName ) ) + // no chance + return; + + Reference< XColumnsSupplier > xTableColSup( xTables->getByName( sTableName ), UNO_QUERY_THROW ); + Reference< XNameAccess > xTableCols( xTableColSup->getColumns(), UNO_SET_THROW ); + + OUString sTableColumnName; + + // get the "Name" or (preferred) "RealName" property of the column + OUString sNamePropertyName( PROPERTY_NAME ); + if ( xInfo->hasPropertyByName( PROPERTY_REALNAME ) ) + sNamePropertyName = PROPERTY_REALNAME; + OSL_VERIFY( _rxTemplateColumn->getPropertyValue( sNamePropertyName ) >>= sTableColumnName ); + + if ( !xTableCols->hasByName( sTableColumnName ) ) + return; + + Reference< XPropertySet > xTableColumn( xTableCols->getByName( sTableColumnName ), UNO_QUERY_THROW ); + impl_initializeColumnSettings_nothrow( xTableColumn, _rxRowSetColumn ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ORowSet::execute_NoApprove_NoNewConn(ResettableMutexGuard& _rClearForNotification) +{ + // now we can dispose our old connection + ::comphelper::disposeComponent(m_xOldConnection); + m_xOldConnection = nullptr; + + // do we need a new statement + if ( m_bCommandFacetsDirty ) + { + m_xStatement = nullptr; + m_xComposer = nullptr; + + Reference< XResultSet > xResultSet( impl_prepareAndExecute_throw() ); + + // let our warnings container forget the reference to the (possibly disposed) old result set + m_aWarnings.setExternalWarnings( nullptr ); + // clear all current warnings + m_aWarnings.clearWarnings(); + // let the warnings container know about the new "external warnings" + m_aWarnings.setExternalWarnings( Reference< XWarningsSupplier >( xResultSet, UNO_QUERY ) ); + + // get the locale + Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); + + // get the numberformatTypes + OSL_ENSURE(m_xActiveConnection.is(),"No ActiveConnection"); + Reference< XNumberFormatsSupplier> xNumberFormat = ::dbtools::getNumberFormats(m_xActiveConnection); + if ( xNumberFormat.is() ) + m_xNumberFormatTypes.set(xNumberFormat->getNumberFormats(),UNO_QUERY); + + ::rtl::Reference< ::connectivity::OSQLColumns> aColumns = new ::connectivity::OSQLColumns(); + std::vector< OUString> aNames; + OUString aDescription; + + const std::map<sal_Int32,sal_Int32>& rKeyColumns = m_pCache->getKeyColumns(); + if(!m_xColumns.is()) + { + SAL_INFO("dbaccess", "ORowSet::execute_NoApprove_NoNewConn::creating columns" ); + // use the meta data + Reference<XResultSetMetaDataSupplier> xMetaSup(m_xStatement,UNO_QUERY); + try + { + Reference<XResultSetMetaData> xMetaData = xMetaSup->getMetaData(); + if ( xMetaData.is() ) + { + sal_Int32 nCount = xMetaData->getColumnCount(); + m_aDataColumns.reserve(nCount+1); + aColumns->reserve(nCount+1); + std::map< OUString, int > aColumnMap; + for (sal_Int32 i = 0 ; i < nCount; ++i) + { + // retrieve the name of the column + OUString sName = xMetaData->getColumnName(i + 1); + // check for duplicate entries + if(aColumnMap.find(sName) != aColumnMap.end()) + { + OUString sAlias(sName); + sal_Int32 searchIndex=1; + while(aColumnMap.find(sAlias) != aColumnMap.end()) + { + sAlias = sName + OUString::number(searchIndex++); + } + sName = sAlias; + } + rtl::Reference<ORowSetDataColumn> pColumn = new ORowSetDataColumn( getMetaData(), + this, + this, + i+1, + m_xActiveConnection->getMetaData(), + aDescription, + OUString(), + [this] (sal_Int32 const column) -> ORowSetValue const& { + return this->getInsertValue(column); + }); + aColumnMap.insert(std::make_pair(sName,0)); + aColumns->emplace_back(pColumn); + pColumn->setName(sName); + aNames.push_back(sName); + m_aDataColumns.push_back(pColumn.get()); + + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_ISREADONLY,Any(rKeyColumns.find(i+1) != rKeyColumns.end())); + + try + { + sal_Int32 nFormatKey = 0; + if(m_xNumberFormatTypes.is()) + nFormatKey = ::dbtools::getDefaultNumberFormat(pColumn,m_xNumberFormatTypes,aLocale); + + + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_NUMBERFORMAT,Any(nFormatKey)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_RELATIVEPOSITION,Any(sal_Int32(i+1))); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_WIDTH,Any(sal_Int32(227))); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_ALIGN,Any(sal_Int32(0))); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_HIDDEN, css::uno::Any(false)); + } + catch(Exception&) + { + } + } + } + } + catch (SQLException&) + { + } + } + else + { + // create the rowset columns + Reference< XResultSetMetaData > xMeta( getMetaData(), UNO_SET_THROW ); + sal_Int32 nCount = xMeta->getColumnCount(); + m_aDataColumns.reserve(nCount+1); + aColumns->reserve(nCount+1); + std::set< Reference< XPropertySet > > aAllColumns; + + for(sal_Int32 i=1; i <= nCount ;++i) + { + OUString sName = xMeta->getColumnName(i); + OUString sColumnLabel = xMeta->getColumnLabel(i); + + // retrieve the column number |i| + Reference<XPropertySet> xColumn; + { + bool bReFetchName = false; + if (m_xColumns->hasByName(sColumnLabel)) + m_xColumns->getByName(sColumnLabel) >>= xColumn; + if (!xColumn.is() && m_xColumns->hasByName(sName)) + m_xColumns->getByName(sName) >>= xColumn; + + // check if column already in the list we need another + if ( aAllColumns.find( xColumn ) != aAllColumns.end() ) + { + xColumn = nullptr; + bReFetchName = true; + sColumnLabel.clear(); + } + if(!xColumn.is()) + { + // no column found so we could look at the position i + Reference<XIndexAccess> xIndexAccess(m_xColumns,UNO_QUERY); + if(xIndexAccess.is() && i <= xIndexAccess->getCount()) + { + xIndexAccess->getByIndex(i-1) >>= xColumn; + } + else + { + Sequence< OUString> aSeq = m_xColumns->getElementNames(); + if( i <= aSeq.getLength()) + { + m_xColumns->getByName(aSeq.getConstArray()[i-1]) >>= xColumn; + } + } + } + if(bReFetchName && xColumn.is()) + xColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + aAllColumns.insert( xColumn ); + } + + // create a RowSetDataColumn + { + Reference<XPropertySetInfo> xInfo = xColumn.is() ? xColumn->getPropertySetInfo() : Reference<XPropertySetInfo>(); + if(xInfo.is() && xInfo->hasPropertyByName(PROPERTY_DESCRIPTION)) + aDescription = comphelper::getString(xColumn->getPropertyValue(PROPERTY_DESCRIPTION)); + + OUString sParseLabel; + if ( xColumn.is() ) + { + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sParseLabel; + } + rtl::Reference<ORowSetDataColumn> pColumn = new ORowSetDataColumn( getMetaData(), + this, + this, + i, + m_xActiveConnection->getMetaData(), + aDescription, + sParseLabel, + [this] (sal_Int32 const column) -> ORowSetValue const& { + return this->getInsertValue(column); + }); + aColumns->emplace_back(pColumn); + + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_ISREADONLY,Any(rKeyColumns.find(i) != rKeyColumns.end())); + + if(sColumnLabel.isEmpty()) + { + if(xColumn.is()) + xColumn->getPropertyValue(PROPERTY_NAME) >>= sColumnLabel; + else + sColumnLabel = DBA_RES( RID_STR_EXPRESSION1 ); + } + pColumn->setName(sColumnLabel); + aNames.push_back(sColumnLabel); + m_aDataColumns.push_back(pColumn.get()); + + if ( xColumn.is() ) + impl_initializeColumnSettings_nothrow( xColumn, pColumn ); + } + } + } + // now create the columns we need + if(m_pColumns) + m_pColumns->assign(aColumns,aNames); + else + { + Reference<XDatabaseMetaData> xMeta = m_xActiveConnection->getMetaData(); + m_pColumns.reset( new ORowSetDataColumns(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(), + aColumns,*this,m_aColumnsMutex,aNames) ); + } + } + else // !m_bCommandFacetsDirty + { + Reference< XResultSet > xResultSet; + if(m_bParametersDirty) + { + xResultSet = impl_prepareAndExecute_throw(); + } + else + { + xResultSet = m_xStatement->executeQuery(); + m_pCache->reset(xResultSet); + } + // let our warnings container forget the reference to the (possibly disposed) old result set + m_aWarnings.setExternalWarnings( nullptr ); + // clear all current warnings + m_aWarnings.clearWarnings(); + // let the warnings container know about the new "external warnings" + m_aWarnings.setExternalWarnings( Reference< XWarningsSupplier >( xResultSet, UNO_QUERY ) ); + } + checkCache(); + // notify the rowset listeners + notifyAllListeners(_rClearForNotification); +} + +// XRowSetApproveBroadcaster +void SAL_CALL ORowSet::addRowSetApproveListener( const Reference< XRowSetApproveListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + m_aApproveListeners.addInterface(listener); +} + +void SAL_CALL ORowSet::removeRowSetApproveListener( const Reference< XRowSetApproveListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + m_aApproveListeners.removeInterface(listener); +} + +// XRowsChangeBroadcaster +void SAL_CALL ORowSet::addRowsChangeListener( const Reference< XRowsChangeListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + m_aRowsChangeListener.addInterface(listener); +} + +void SAL_CALL ORowSet::removeRowsChangeListener( const Reference< XRowsChangeListener >& listener ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + m_aRowsChangeListener.removeInterface(listener); +} + +// XResultSetAccess +Reference< XResultSet > SAL_CALL ORowSet::createResultSet( ) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + if(m_xStatement.is()) + { + rtl::Reference<ORowSetClone> pClone = new ORowSetClone( m_aContext, *this, m_pMutex ); + m_aClones.emplace_back(css::uno::Reference< css::uno::XWeak >(pClone)); + return pClone; + } + return Reference< XResultSet >(); +} + +// css::util::XCancellable +void SAL_CALL ORowSet::cancel( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); +} + +// css::sdbcx::XDeleteRows +Sequence< sal_Int32 > SAL_CALL ORowSet::deleteRows( const Sequence< Any >& rows ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + if(!m_pCache || m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY) + throwFunctionSequenceException(*this); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + Sequence<Any> aChangedBookmarks; + RowsChangeEvent aEvt(*this,RowChangeAction::DELETE,rows.getLength(),aChangedBookmarks); + // notify the rowset listeners + notifyAllListenersRowBeforeChange(aGuard,aEvt); + + Sequence< sal_Int32 > aResults( rows.getLength() ); + const Any* row = rows.getConstArray(); + const Any* rowEnd = rows.getConstArray() + rows.getLength(); + sal_Int32* result = aResults.getArray(); + for ( ; row != rowEnd; ++row, ++result ) + { + *result = 0; + if ( !m_pCache->moveToBookmark( *row ) ) + continue; + sal_Int32 nDeletePosition = m_pCache->getRow(); + + // first notify the clones so that they can save their position + notifyRowSetAndClonesRowDelete( *row ); + + // now delete the row + if ( !m_pCache->deleteRow() ) + continue; + *result = 1; + // now notify that we have deleted + notifyRowSetAndClonesRowDeleted( *row, nDeletePosition ); + } + aEvt.Rows = aResults.getLength(); + + // we have to check if we stand on the insert row and if so we have to reset it + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + // notification order + // - rowChanged + notifyAllListenersRowChanged(aGuard,aEvt); + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + + return aResults; +} + +void ORowSet::notifyRowSetAndClonesRowDelete( const Any& _rBookmark ) +{ + // notify ourself + onDeleteRow( _rBookmark ); + // notify the clones + for (auto const& elem : m_aClones) + { + auto pClone = comphelper::getFromUnoTunnel<ORowSetClone>(elem.get()); + if(pClone) + pClone->onDeleteRow( _rBookmark ); + } +} + +void ORowSet::notifyRowSetAndClonesRowDeleted( const Any& _rBookmark, sal_Int32 _nPos ) +{ + // notify ourself + onDeletedRow( _rBookmark, _nPos ); + // notify the clones + for (auto const& clone : m_aClones) + { + auto pClone = comphelper::getFromUnoTunnel<ORowSetClone>(clone.get()); + if(pClone) + pClone->onDeletedRow( _rBookmark, _nPos ); + } +} + +Reference< XConnection > ORowSet::calcConnection(const Reference< XInteractionHandler >& _rxHandler) +{ + MutexGuard aGuard(m_aMutex); + if (!m_xActiveConnection.is()) + { + Reference< XConnection > xNewConn; + if ( !m_aDataSourceName.isEmpty() ) + { + Reference< XDatabaseContext > xDatabaseContext( DatabaseContext::create(m_aContext) ); + try + { + Reference< XDataSource > xDataSource( xDatabaseContext->getByName( m_aDataSourceName ), UNO_QUERY_THROW ); + + // try connecting with the interaction handler + Reference< XCompletedConnection > xComplConn( xDataSource, UNO_QUERY ); + if ( _rxHandler.is() && xComplConn.is() ) + { + xNewConn = xComplConn->connectWithCompletion( _rxHandler ); + } + else + { + xNewConn = xDataSource->getConnection( m_aUser, m_aPassword ); + } + } + catch ( const SQLException& ) + { + throw; + } + catch ( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + OUString sMessage = ResourceManager::loadString( RID_NO_SUCH_DATA_SOURCE, + "$name$", m_aDataSourceName, "$error$", extractExceptionMessage( m_aContext, aError ) ); + ::dbtools::throwGenericSQLException( sMessage, *this, aError ); + } + } + setActiveConnection(xNewConn); + m_bOwnConnection = true; + } + return m_xActiveConnection; +} + +Reference< XNameAccess > ORowSet::impl_getTables_throw() +{ + Reference< XNameAccess > xTables; + + Reference< XTablesSupplier > xTablesAccess( m_xActiveConnection, UNO_QUERY ); + if ( xTablesAccess.is() ) + { + xTables.set( xTablesAccess->getTables(), UNO_SET_THROW ); + } + else if ( m_xTables ) + { + xTables = m_xTables.get(); + } + else + { + if ( !m_xActiveConnection.is() ) + throw SQLException(DBA_RES(RID_STR_CONNECTION_INVALID),*this,SQLSTATE_GENERAL,1000,Any() ); + + bool bCase = true; + try + { + Reference<XDatabaseMetaData> xMeta = m_xActiveConnection->getMetaData(); + bCase = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(); + } + catch(SQLException&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xTables.reset(new OTableContainer(*this,m_aMutex,m_xActiveConnection,bCase,nullptr,nullptr,m_nInAppend)); + xTables = m_xTables.get(); + Sequence<OUString> aTableFilter { "%" }; + m_xTables->construct(aTableFilter,Sequence< OUString>()); + } + + return xTables; +} + +void ORowSet::impl_resetTables_nothrow() +{ + if ( !m_xTables ) + return; + + try + { + m_xTables->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xTables.reset(); +} + +void ORowSet::impl_initComposer_throw( OUString& _out_rCommandToExecute ) +{ + bool bUseEscapeProcessing = impl_buildActiveCommand_throw( ); + _out_rCommandToExecute = m_aActiveCommand; + if ( !bUseEscapeProcessing ) + return; + + if (m_bCommandFacetsDirty) + m_xComposer = nullptr; + + Reference< XMultiServiceFactory > xFactory( m_xActiveConnection, UNO_QUERY ); + if ( !m_xComposer.is() && xFactory.is() ) + { + try + { + m_xComposer.set( xFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ); + } + catch (const Exception& ) { m_xComposer = nullptr; } + } + if ( !m_xComposer.is() ) + m_xComposer = new OSingleSelectQueryComposer( impl_getTables_throw(), m_xActiveConnection, m_aContext ); + + m_xComposer->setCommand( m_aCommand,m_nCommandType ); + m_aActiveCommand = m_xComposer->getQuery(); + + m_xComposer->setFilter( m_bApplyFilter ? m_aFilter : OUString() ); + m_xComposer->setHavingClause( m_bApplyFilter ? m_aHavingClause : OUString() ); + + if ( m_bIgnoreResult ) + { // append a "0=1" filter + // don't simply overwrite an existent filter, this would lead to problems if this existent + // filter contains parameters (since a keyset may add parameters itself) + m_xComposer->setElementaryQuery( m_xComposer->getQuery( ) ); + m_xComposer->setFilter( "0 = 1" ); + } + + m_xComposer->setOrder( m_aOrder ); + m_xComposer->setGroup( m_aGroupBy ); + + if ( !m_xColumns.is() ) + { + Reference< XColumnsSupplier > xCols( m_xComposer, UNO_QUERY_THROW ); + m_xColumns = xCols->getColumns(); + } + + impl_initParametersContainer_nothrow(); + + _out_rCommandToExecute = m_xComposer->getQueryWithSubstitution(); + + m_bCommandFacetsDirty = false; +} + +bool ORowSet::impl_buildActiveCommand_throw() +{ + // create the sql command + // from a table name or get the command out of a query (not a view) + // the last use the command as it is + bool bDoEscapeProcessing = m_bUseEscapeProcessing; + + m_aActiveCommand.clear(); + OUString sCommand; + + if ( m_aCommand.isEmpty() ) + return bDoEscapeProcessing; + + switch (m_nCommandType) + { + case CommandType::TABLE: + { + impl_resetTables_nothrow(); + if ( bDoEscapeProcessing ) + { + Reference< XNameAccess > xTables( impl_getTables_throw() ); + if ( xTables->hasByName(m_aCommand) ) + { + } + else + { + OUString sMessage( DBA_RES( RID_STR_TABLE_DOES_NOT_EXIST ) ); + throwGenericSQLException(sMessage.replaceAll( "$table$", m_aCommand ),*this); + } + } + else + { + sCommand = "SELECT * FROM "; + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents( m_xActiveConnection->getMetaData(), m_aCommand, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation ); + sCommand += ::dbtools::composeTableNameForSelect( m_xActiveConnection, sCatalog, sSchema, sTable ); + } + } + break; + + case CommandType::QUERY: + { + Reference< XQueriesSupplier > xQueriesAccess(m_xActiveConnection, UNO_QUERY); + if (!xQueriesAccess.is()) + throw SQLException(DBA_RES(RID_STR_NO_XQUERIESSUPPLIER),*this,OUString(),0,Any()); + Reference< css::container::XNameAccess > xQueries(xQueriesAccess->getQueries()); + if (xQueries->hasByName(m_aCommand)) + { + Reference< XPropertySet > xQuery(xQueries->getByName(m_aCommand),UNO_QUERY); + OSL_ENSURE(xQuery.is(),"ORowSet::impl_buildActiveCommand_throw: Query is NULL!"); + if ( xQuery.is() ) + { + xQuery->getPropertyValue(PROPERTY_COMMAND) >>= sCommand; + xQuery->getPropertyValue(PROPERTY_ESCAPE_PROCESSING) >>= bDoEscapeProcessing; + if ( bDoEscapeProcessing != m_bUseEscapeProcessing ) + { + bool bOldValue = m_bUseEscapeProcessing; + m_bUseEscapeProcessing = bDoEscapeProcessing; + fireProperty(PROPERTY_ID_ESCAPE_PROCESSING,bOldValue,bDoEscapeProcessing); + } + + OUString aCatalog,aSchema,aTable; + xQuery->getPropertyValue(PROPERTY_UPDATE_CATALOGNAME) >>= aCatalog; + xQuery->getPropertyValue(PROPERTY_UPDATE_SCHEMANAME) >>= aSchema; + xQuery->getPropertyValue(PROPERTY_UPDATE_TABLENAME) >>= aTable; + if(!aTable.isEmpty()) + m_aUpdateTableName = composeTableName( m_xActiveConnection->getMetaData(), aCatalog, aSchema, aTable, false, ::dbtools::EComposeRule::InDataManipulation ); + } + } + else + { + OUString sMessage( DBA_RES( RID_STR_QUERY_DOES_NOT_EXIST ) ); + throwGenericSQLException(sMessage.replaceAll( "$table$", m_aCommand ),*this); + } + } + break; + + default: + sCommand = m_aCommand; + break; + } + + m_aActiveCommand = sCommand; + + if ( m_aActiveCommand.isEmpty() && !bDoEscapeProcessing ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_SQL_COMMAND ), StandardSQLState::FUNCTION_SEQUENCE_ERROR, *this ); + + return bDoEscapeProcessing; +} + +void ORowSet::impl_initParametersContainer_nothrow() +{ + OSL_PRECOND( !m_pParameters.is(), "ORowSet::impl_initParametersContainer_nothrow: already initialized the parameters!" ); + + m_pParameters = new param::ParameterWrapperContainer( m_xComposer ); + // copy the premature parameters into the final ones + size_t nParamCount( std::min( m_pParameters->size(), m_aPrematureParamValues->size() ) ); + for ( size_t i=0; i<nParamCount; ++i ) + { + (*m_pParameters)[i] = (*m_aPrematureParamValues)[i]; + } +} + +void ORowSet::impl_disposeParametersContainer_nothrow() +{ + if ( !m_pParameters.is() ) + return; + + // copy the actual values to our "premature" ones, to preserve them for later use + size_t nParamCount( m_pParameters->size() ); + m_aPrematureParamValues->resize( nParamCount ); + for ( size_t i=0; i<nParamCount; ++i ) + { + (*m_aPrematureParamValues)[i] = (*m_pParameters)[i]; + } + + m_pParameters->dispose(); + m_pParameters = nullptr; +} + +ORowSetValue& ORowSet::getParameterStorage(sal_Int32 parameterIndex) +{ + ::connectivity::checkDisposed( ORowSet_BASE1::rBHelper.bDisposed ); + if ( parameterIndex < 1 ) + throwInvalidIndexException( *this ); + + if ( m_aParametersSet.size() < o3tl::make_unsigned(parameterIndex) ) + m_aParametersSet.resize( parameterIndex ,false); + m_aParametersSet[parameterIndex - 1] = true; + + if ( m_pParameters.is() ) + { + if ( m_bCommandFacetsDirty ) + // need to rebuild the parameters, since some property which contributes to the + // complete command, and thus the parameters, changed + impl_disposeParametersContainer_nothrow(); + if ( m_pParameters.is() ) + { + if ( o3tl::make_unsigned(parameterIndex) > m_pParameters->size() ) + throwInvalidIndexException( *this ); + return (*m_pParameters)[ parameterIndex - 1 ]; + } + } + + if ( m_aPrematureParamValues->size() < o3tl::make_unsigned(parameterIndex) ) + m_aPrematureParamValues->resize( parameterIndex ); + return (*m_aPrematureParamValues)[ parameterIndex - 1 ]; +} + +// XParameters +void SAL_CALL ORowSet::setNull( sal_Int32 parameterIndex, sal_Int32 /*sqlType*/ ) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + getParameterStorage( parameterIndex ).setNull(); + m_bParametersDirty = true; +} + +void SAL_CALL ORowSet::setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& /*typeName*/ ) +{ + setNull( parameterIndex, sqlType ); +} + +void ORowSet::setParameter(sal_Int32 parameterIndex, const ORowSetValue& x) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + getParameterStorage( parameterIndex ) = x; + m_bParametersDirty = true; +} + +void SAL_CALL ORowSet::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + setParameter(parameterIndex, static_cast<bool>(x)); +} + +void SAL_CALL ORowSet::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setFloat( sal_Int32 parameterIndex, float x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setDouble( sal_Int32 parameterIndex, double x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setDate( sal_Int32 parameterIndex, const css::util::Date& x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) +{ + setParameter(parameterIndex,x); +} + +void SAL_CALL ORowSet::setBinaryStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + ORowSetValue& rParamValue( getParameterStorage( parameterIndex ) ); + + try + { + Sequence <sal_Int8> aData; + x->readBytes(aData, length); + rParamValue = aData; + m_bParametersDirty = true; + x->closeInput(); + } + catch( Exception const & ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw SQLException("ORowSet::setBinaryStream", *this, "S1000", 0,anyEx); + } +} + +void SAL_CALL ORowSet::setCharacterStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + ORowSetValue& rParamValue( getParameterStorage( parameterIndex ) ); + try + { + Sequence <sal_Int8> aData; + OUString aDataStr; + // the data is given as character data and the length defines the character length + sal_Int32 nSize = x->readBytes(aData, length * sizeof(sal_Unicode)); + if (nSize / sizeof(sal_Unicode)) + aDataStr = OUString(reinterpret_cast<sal_Unicode const *>(aData.getConstArray()), nSize / sizeof(sal_Unicode)); + m_bParametersDirty = true; + rParamValue = aDataStr; + rParamValue.setTypeKind( DataType::LONGVARCHAR ); + x->closeInput(); + } + catch( Exception const & ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw SQLException("ORowSet::setCharacterStream", *this, "S1000", 0, anyEx); + } +} + +void SAL_CALL ORowSet::setObject( sal_Int32 parameterIndex, const Any& x ) +{ + if ( !::dbtools::implSetObject( this, parameterIndex, x ) ) + { // there is no other setXXX call which can handle the value in x + throw SQLException(); + } + m_bParametersDirty = true; +} + +void SAL_CALL ORowSet::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 /*scale*/ ) +{ + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + ORowSetValue& rParamValue( getParameterStorage( parameterIndex ) ); + setObject( parameterIndex, x ); + rParamValue.setTypeKind( targetSqlType ); +} + +void SAL_CALL ORowSet::setRef( sal_Int32 /*parameterIndex*/, const Reference< XRef >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setRef", *this ); +} + +void SAL_CALL ORowSet::setBlob( sal_Int32 /*parameterIndex*/, const Reference< XBlob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setBlob", *this ); +} + +void SAL_CALL ORowSet::setClob( sal_Int32 /*parameterIndex*/, const Reference< XClob >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setClob", *this ); +} + +void SAL_CALL ORowSet::setArray( sal_Int32 /*parameterIndex*/, const Reference< XArray >& /*x*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XParameters::setArray", *this ); +} + +void SAL_CALL ORowSet::clearParameters( ) +{ + ::connectivity::checkDisposed(ORowSet_BASE1::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + + size_t nParamCount( m_pParameters.is() ? m_pParameters->size() : m_aPrematureParamValues->size() ); + for ( size_t i=1; i<=nParamCount; ++i ) + getParameterStorage( static_cast<sal_Int32>(i) ).setNull(); + m_aParametersSet.clear(); +} + +Any SAL_CALL ORowSet::getWarnings( ) +{ + return m_aWarnings.getWarnings(); +} + +void SAL_CALL ORowSet::clearWarnings( ) +{ + m_aWarnings.clearWarnings(); +} + +void ORowSet::doCancelModification( ) +{ + if ( isModification() ) + { + // read-only flag restored + impl_restoreDataColumnsWriteable_throw(); + m_pCache->cancelRowModification(); + } + m_bModified = false; + m_bIsInsertRow = false; +} + +bool ORowSet::isModification( ) +{ + return isNew(); +} + +bool ORowSet::isModified( ) +{ + return m_bModified; +} + +bool ORowSet::isNew( ) +{ + return m_bNew; +} + +bool ORowSet::isPropertyChangeNotificationEnabled() const +{ + return m_bPropChangeNotifyEnabled; +} + +void ORowSet::checkUpdateIterator() +{ + if(!m_bIsInsertRow) + { + m_pCache->setUpdateIterator(m_aCurrentRow); + m_aCurrentRow = m_pCache->m_aInsertRow; + m_bIsInsertRow = true; + } +} + +void ORowSet::checkUpdateConditions(sal_Int32 columnIndex) +{ + checkCache(); + if ( m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY) + ::dbtools::throwSQLException( DBA_RES( RID_STR_RESULT_IS_READONLY ), StandardSQLState::GENERAL_ERROR, *this ); + + if ( rowDeleted() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_ROW_ALREADY_DELETED ), StandardSQLState::INVALID_CURSOR_POSITION, *this ); + + if ( m_aCurrentRow.isNull() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_INVALID_CURSOR_STATE ), StandardSQLState::INVALID_CURSOR_STATE, *this ); + + if ( columnIndex <= 0 || (*m_aCurrentRow)->size() <= o3tl::make_unsigned(columnIndex) ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_INVALID_INDEX ), StandardSQLState::INVALID_DESCRIPTOR_INDEX, *this ); +} + +void SAL_CALL ORowSet::refreshRow( ) +{ + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + // notification order: + if ( m_bModified && m_pCache ) + implCancelRowUpdates( false ); // do _not_ notify the IsModify - will do this ourself below + + // - column values + ORowSetBase::refreshRow(); + + // - IsModified + // - IsNew + aNotifier.fire( ); +} + +void ORowSet::impl_rebuild_throw(::osl::ResettableMutexGuard& _rGuard) +{ + Reference< XResultSet > xResultSet(m_xStatement->executeQuery()); + m_pCache->reset(xResultSet); + m_aWarnings.setExternalWarnings( Reference< XWarningsSupplier >( xResultSet, UNO_QUERY ) ); + notifyAllListeners(_rGuard); +} + +// *********************************************************** +// ORowSetClone +// *********************************************************** + +ORowSetClone::ORowSetClone( const Reference<XComponentContext>& _rContext, ORowSet& rParent, ::osl::Mutex* _pMutex ) + :OSubComponent(m_aMutex, rParent) + ,ORowSetBase( _rContext, OComponentHelper::rBHelper, _pMutex ) + ,m_pParent(&rParent) + ,m_nFetchDirection(rParent.m_nFetchDirection) + ,m_nFetchSize(rParent.m_nFetchSize) + ,m_bIsBookmarkable(true) +{ + + m_nResultSetType = rParent.m_nResultSetType; + m_nResultSetConcurrency = ResultSetConcurrency::READ_ONLY; + m_pMySelf = this; + m_bClone = true; + m_bBeforeFirst = rParent.m_bBeforeFirst; + m_bAfterLast = rParent.m_bAfterLast; + m_pCache = rParent.m_pCache; + m_aBookmark = rParent.m_aBookmark; + m_aCurrentRow = m_pCache->createIterator(this); + m_xNumberFormatTypes = rParent.m_xNumberFormatTypes; + + m_aOldRow = m_pCache->registerOldRow(); + + ::rtl::Reference< ::connectivity::OSQLColumns> aColumns = new ::connectivity::OSQLColumns(); + std::vector< OUString> aNames; + + OUString aDescription; + Locale aLocale = SvtSysLocale().GetLanguageTag().getLocale(); + + if ( rParent.m_pColumns ) + { + Sequence< OUString> aSeq = rParent.m_pColumns->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + aColumns->reserve(aSeq.getLength()+1); + for(sal_Int32 i=1;pIter != pEnd ;++pIter,++i) + { + Reference<XPropertySet> xColumn; + rParent.m_pColumns->getByName(*pIter) >>= xColumn; + if(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_DESCRIPTION)) + aDescription = comphelper::getString(xColumn->getPropertyValue(PROPERTY_DESCRIPTION)); + + OUString sParseLabel; + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sParseLabel; + rtl::Reference<ORowSetColumn> pColumn = new ORowSetColumn( rParent.getMetaData(), + this, + i, + rParent.m_xActiveConnection->getMetaData(), + aDescription, + sParseLabel, + [this] (sal_Int32 const column) -> ORowSetValue const& { + return this->getValue(column); + }); + aColumns->emplace_back(pColumn); + pColumn->setName(*pIter); + aNames.push_back(*pIter); + m_aDataColumns.push_back(pColumn.get()); + + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_ALIGN,xColumn->getPropertyValue(PROPERTY_ALIGN)); + sal_Int32 nFormatKey = 0; + xColumn->getPropertyValue(PROPERTY_NUMBERFORMAT) >>= nFormatKey; + if(!nFormatKey && xColumn.is() && m_xNumberFormatTypes.is()) + nFormatKey = ::dbtools::getDefaultNumberFormat(xColumn,m_xNumberFormatTypes,aLocale); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_NUMBERFORMAT,Any(nFormatKey)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_RELATIVEPOSITION,xColumn->getPropertyValue(PROPERTY_RELATIVEPOSITION)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_WIDTH,xColumn->getPropertyValue(PROPERTY_WIDTH)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_HIDDEN,xColumn->getPropertyValue(PROPERTY_HIDDEN)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_CONTROLMODEL,xColumn->getPropertyValue(PROPERTY_CONTROLMODEL)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_HELPTEXT,xColumn->getPropertyValue(PROPERTY_HELPTEXT)); + pColumn->setFastPropertyValue_NoBroadcast(PROPERTY_ID_CONTROLDEFAULT,xColumn->getPropertyValue(PROPERTY_CONTROLDEFAULT)); + + } + } + Reference<XDatabaseMetaData> xMeta = rParent.m_xActiveConnection->getMetaData(); + m_pColumns.reset( new ORowSetDataColumns(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(), + aColumns,*this,m_aMutex,aNames) ); + + sal_Int32 const nRT = PropertyAttribute::READONLY | PropertyAttribute::TRANSIENT; + + // sdb.RowSet Properties + registerMayBeVoidProperty(PROPERTY_ACTIVE_CONNECTION,PROPERTY_ID_ACTIVE_CONNECTION, PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, &rParent.m_aActiveConnection, cppu::UnoType<XConnection>::get()); + registerProperty(PROPERTY_RESULTSETCONCURRENCY, PROPERTY_ID_RESULTSETCONCURRENCY, PropertyAttribute::READONLY, &m_nResultSetConcurrency,::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_RESULTSETTYPE, PROPERTY_ID_RESULTSETTYPE, PropertyAttribute::READONLY, &m_nResultSetType, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_FETCHDIRECTION, PROPERTY_ID_FETCHDIRECTION, PropertyAttribute::TRANSIENT, &m_nFetchDirection, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_FETCHSIZE, PROPERTY_ID_FETCHSIZE, PropertyAttribute::TRANSIENT, &m_nFetchSize, ::cppu::UnoType<sal_Int32>::get()); + registerProperty(PROPERTY_ISBOOKMARKABLE, PROPERTY_ID_ISBOOKMARKABLE, nRT, &m_bIsBookmarkable, cppu::UnoType<bool>::get()); +} + +ORowSetClone::~ORowSetClone() +{ +} + +// css::XTypeProvider +Sequence< Type > ORowSetClone::getTypes() +{ + return ::comphelper::concatSequences(OSubComponent::getTypes(),ORowSetBase::getTypes()); +} + +// css::XInterface +Any ORowSetClone::queryInterface( const Type & rType ) +{ + Any aRet = ORowSetBase::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OSubComponent::queryInterface(rType); + return aRet; +} + +void ORowSetClone::acquire() noexcept +{ + OSubComponent::acquire(); +} + +void ORowSetClone::release() noexcept +{ + OSubComponent::release(); +} + +// XServiceInfo +OUString ORowSetClone::getImplementationName( ) +{ + return "com.sun.star.sdb.ORowSetClone"; +} + +sal_Bool ORowSetClone::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > ORowSetClone::getSupportedServiceNames( ) +{ + return { SERVICE_SDBC_RESULTSET, SERVICE_SDB_RESULTSET }; +} + +// OComponentHelper +void ORowSetClone::disposing() +{ + MutexGuard aGuard( m_aMutex ); + ORowSetBase::disposing(); + + m_pParent = nullptr; + m_pMutex = &m_aMutex; // this must be done here because someone could hold a ref to us and try to do something + OSubComponent::disposing(); +} + +// XCloseable +void ORowSetClone::close() +{ + { + MutexGuard aGuard( m_aMutex ); + if (OComponentHelper::rBHelper.bDisposed) + throw DisposedException(); + } + dispose(); +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ORowSetClone::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& SAL_CALL ORowSetClone::getInfoHelper() +{ + return *::comphelper::OPropertyArrayUsageHelper<ORowSetClone>::getArrayHelper(); +} + +const Sequence< sal_Int8 > & ORowSetClone::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +// css::XUnoTunnel +sal_Int64 SAL_CALL ORowSetClone::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +void SAL_CALL ORowSetClone::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + if ( nHandle == PROPERTY_ID_FETCHSIZE ) + { + if ( m_pParent ) + m_pParent->setFastPropertyValue_NoBroadcast( nHandle, rValue ); + } + + OPropertyStateContainer::setFastPropertyValue_NoBroadcast(nHandle,rValue); +} + +void ORowSetClone::doCancelModification( ) +{ +} + +bool ORowSetClone::isModification( ) +{ + return false; +} + +bool ORowSetClone::isModified( ) +{ + return false; +} + +bool ORowSetClone::isNew( ) +{ + return false; +} + +void SAL_CALL ORowSetClone::execute( ) +{ + throwFunctionNotSupportedSQLException( "RowSetClone::XRowSet::execute", *this ); +} + +void SAL_CALL ORowSetClone::addRowSetListener( const Reference< XRowSetListener >& ) +{ + throwFunctionNotSupportedRuntimeException( "RowSetClone::XRowSet", *this ); +} + +void SAL_CALL ORowSetClone::removeRowSetListener( const Reference< XRowSetListener >& ) +{ + throwFunctionNotSupportedRuntimeException( "RowSetClone::XRowSet", *this ); +} + +} // dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSet.hxx b/dbaccess/source/core/api/RowSet.hxx new file mode 100644 index 000000000..afe49a923 --- /dev/null +++ b/dbaccess/source/core/api/RowSet.hxx @@ -0,0 +1,527 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <atomic> +#include <cstddef> + +#include <apitools.hxx> +#include "RowSetBase.hxx" + +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdb/XResultSetAccess.hpp> +#include <com/sun/star/sdbc/XRowSetListener.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdb/XRowSetApproveBroadcaster.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <com/sun/star/sdb/XCompletedExecution.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp> + +#include <cppuhelper/compbase12.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <connectivity/paramwrapper.hxx> +#include <connectivity/FValue.hxx> +#include <connectivity/warningscontainer.hxx> + +namespace dbaccess +{ + typedef ::cppu::WeakAggComponentImplHelper12 < css::sdb::XResultSetAccess + , css::sdb::XRowSetApproveBroadcaster + , css::sdb::XRowsChangeBroadcaster + , css::sdbcx::XDeleteRows + , css::sdbc::XParameters + , css::lang::XEventListener + , css::sdbc::XResultSetUpdate + , css::sdbc::XRowUpdate + , css::util::XCancellable + , css::sdb::XCompletedExecution + , css::sdb::XParametersSupplier + , css::sdbc::XWarningsSupplier + > ORowSet_BASE1; + + class OTableContainer; + class ORowSet final : public cppu::BaseMutex + , public ORowSet_BASE1 + , public ORowSetBase + , public ::comphelper::OPropertyArrayUsageHelper<ORowSet> + { + friend class ORowSetClone; + + css::uno::Reference< css::sdbc::XConnection > m_xOldConnection; + css::uno::Reference< css::sdbc::XConnection > m_xActiveConnection; + css::uno::Any m_aActiveConnection; + css::uno::Reference< css::container::XNameAccess > m_xTypeMap; + css::uno::Any m_aTypeMap; + css::uno::Reference< css::sdbc::XPreparedStatement > m_xStatement; + css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xComposer; + css::uno::Reference< css::container::XNameAccess > m_xColumns; // the columns from a table or query + + connectivity::OWeakRefArray m_aClones; + /** our parameters as XPropertySet instances and ORowSetValue instances + */ + ::dbtools::param::ParametersContainerRef m_pParameters; + /** our parameters values, used when we do not yet have a parameters container + (since we have not been executed, yet) + */ + rtl::Reference<ORowSetValueVector> m_aPrematureParamValues; + rtl::Reference<ORowSetValueVector> m_aParameterValueForCache; + std::vector<bool> m_aParametersSet; + std::vector<bool> m_aReadOnlyDataColumns; + + ::comphelper::OInterfaceContainerHelper3<css::sdbc::XRowSetListener> m_aRowsetListeners; + ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetApproveListener> m_aApproveListeners; + ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowsChangeListener> m_aRowsChangeListener; + + ::dbtools::WarningsContainer m_aWarnings; + + // no Reference! see OCollection::acquire + std::unique_ptr<OTableContainer> m_xTables; + + OUString m_aCommand; + OUString m_aDataSourceName; + OUString m_aURL; + OUString m_aUser; + OUString m_aPassword; + OUString m_aFilter; + OUString m_aHavingClause; + OUString m_aGroupBy; + OUString m_aOrder; + OUString m_aActiveCommand; + OUString m_aUpdateCatalogName; // is set by a query + OUString m_aUpdateSchemaName; // is set by a query + OUString m_aUpdateTableName; // is set by a query + OUString m_sErrorString; + + sal_Int32 m_nFetchDirection; + sal_Int32 m_nFetchSize; + sal_Int32 m_nMaxFieldSize; + sal_Int32 m_nMaxRows; + sal_Int32 m_nQueryTimeOut; + sal_Int32 m_nCommandType; + sal_Int32 m_nTransactionIsolation; + sal_Int32 m_nPrivileges; + sal_Int32 m_nLastKnownRowCount; + std::atomic<std::size_t> m_nInAppend; + bool m_bInsertingRow; + bool m_bLastKnownRowCountFinal; + bool m_bUseEscapeProcessing ; + bool m_bApplyFilter ; + bool m_bCommandFacetsDirty; // any of the facets which define the active command is dirty + bool m_bParametersDirty; // parameters changed since execute + bool m_bModified ; + bool m_bRebuildConnOnExecute ; + bool m_bIsBookmarkable ; + bool m_bNew ; + bool m_bCanUpdateInsertedRows; + bool m_bOwnConnection; + bool m_bPropChangeNotifyEnabled; + + /** builds m_aActiveCommand from our settings + + @return + whether we should use escape processing before executing the actual command. This is determined + from our own EscapeProcessing property, and possibly overruled by the respective property + of a query we're based on. + */ + bool impl_buildActiveCommand_throw(); + + /** initializes our query composer, and everything which has to do with it + + If we don't use escape processing, then we don't have a composer, and everything + related to it. Nonetheless, _out_rCommandToExecute and the return value are properly + initialized. + + @param _out_rCommandToExecute + The command which is to be executed, according to the current settings - + it is built from our active command plus our current filter/order criterions. + + @precond + m_xActiveConnection points to a valid SDB-level connection + + @throws css::sdb::SQLException + if a database-related error occurred + + @throws css::uno::RuntimeException + if any of the components involved throws a css::uno::RuntimeException + */ + void impl_initComposer_throw( OUString& _out_rCommandToExecute ); + + /** returns the table container of our active connection + + If our connection is able to provide a tables container, this one is returned. + Else, if m_pTables is not <NULL/>, this one will returned. + Else, m_pTables will be constructed and returned. + + @precond m_xActiveConnection is not <NULL/> + @throws css::sdbc::SQLException + if retrieving or constructing the tables container goes wrong + + @see impl_resetTables_nothrow + */ + css::uno::Reference< css::container::XNameAccess > + impl_getTables_throw(); + + /** cleans up m_pTables, and resets it to <NULL/> + */ + void impl_resetTables_nothrow(); + + /** prepares and executes our command + */ + css::uno::Reference< css::sdbc::XResultSet > + impl_prepareAndExecute_throw(); + void impl_ensureStatement_throw(); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XConnection > calcConnection(const css::uno::Reference< css::task::XInteractionHandler >& _rxHandler); + // free clones and ParseTree. Plus, if _bComplete is <TRUE/>, *all* other associated resources + void freeResources( bool _bComplete ); + + /// informs the clones (and ourself) that we are going to delete a record with a given bookmark + void notifyRowSetAndClonesRowDelete( const css::uno::Any& _rBookmark ); + + /// inform the clones (and ourself) that we have deleted a record with a given bookmark + void notifyRowSetAndClonesRowDeleted( const css::uno::Any& _rBookmark, sal_Int32 _nPos ); + + void checkUpdateIterator(); + const connectivity::ORowSetValue& getInsertValue(sal_Int32 columnIndex); + void setParameter(sal_Int32 parameterIndex, const connectivity::ORowSetValue& x); + // resizes the parameter vector if necessary + ::connectivity::ORowSetValue& getParameterStorage( sal_Int32 parameterIndex ); + + void updateValue(sal_Int32 columnIndex,const connectivity::ORowSetValue& x); + void checkUpdateConditions(sal_Int32 columnIndex); + void impl_rebuild_throw(::osl::ResettableMutexGuard& _rGuard); + // set all data columns to writeable + void impl_setDataColumnsWriteable_throw(); + // restore the old state of the data column read-only state + void impl_restoreDataColumnsWriteable_throw(); + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const css::uno::Any& rValue) override; + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue,sal_Int32 nHandle) const override; + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + virtual void fireRowcount() override; + void notifyAllListenersRowBeforeChange(::osl::ResettableMutexGuard& _rGuard,const css::sdb::RowChangeEvent &rEvt); + void notifyAllListenersRowChanged(::osl::ResettableMutexGuard& _rGuard,const css::sdb::RowsChangeEvent& rEvt); + virtual bool notifyAllListenersCursorBeforeMove(::osl::ResettableMutexGuard& _rGuard) override; + virtual void notifyAllListenersCursorMoved(::osl::ResettableMutexGuard& _rGuard) override; + // notify all that rowset changed + void notifyAllListeners(::osl::ResettableMutexGuard& _rGuard); + + virtual void doCancelModification( ) override; + virtual bool isModification( ) override; + virtual bool isModified( ) override; + virtual bool isNew( ) override; + virtual bool isPropertyChangeNotificationEnabled() const override; + + virtual ~ORowSet() override; + + public: + explicit ORowSet(const css::uno::Reference<css::uno::XComponentContext>&); + + // css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // css::lang::XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + // css::uno::XAggregation + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& aType ) override; + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::lang::XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // css::sdbc::XResultSet + virtual void SAL_CALL refreshRow( ) override; + + // XCompletedExecution + virtual void SAL_CALL executeWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override; + + // XParametersSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getParameters( ) override; + + // css::sdbc::XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + + // css::sdbc::XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + + // css::sdbc::XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + + // css::sdbc::XRowSet + virtual void SAL_CALL execute( ) override; + virtual void SAL_CALL addRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override; + virtual void SAL_CALL removeRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override; + + // css::sdb::XRowSetApproveBroadcaster + virtual void SAL_CALL addRowSetApproveListener( const css::uno::Reference< css::sdb::XRowSetApproveListener >& listener ) override; + virtual void SAL_CALL removeRowSetApproveListener( const css::uno::Reference< css::sdb::XRowSetApproveListener >& listener ) override; + + // css::sdb::XRowsChangeBroadcaster + virtual void SAL_CALL addRowsChangeListener( const css::uno::Reference< css::sdb::XRowsChangeListener >& listener ) override; + virtual void SAL_CALL removeRowsChangeListener( const css::uno::Reference< css::sdb::XRowsChangeListener >& listener ) override; + + // css::sdb::XResultSetAccess + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL createResultSet( ) override; + + // css::util::XCancellable + virtual void SAL_CALL cancel( ) override; + + // css::sdbcx::XDeleteRows + virtual css::uno::Sequence< sal_Int32 > SAL_CALL deleteRows( const css::uno::Sequence< css::uno::Any >& rows ) override; + + // XParameters + virtual void SAL_CALL setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) override; + virtual void SAL_CALL setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL setBoolean( sal_Int32 parameterIndex, sal_Bool x ) override; + virtual void SAL_CALL setByte( sal_Int32 parameterIndex, sal_Int8 x ) override; + virtual void SAL_CALL setShort( sal_Int32 parameterIndex, sal_Int16 x ) override; + virtual void SAL_CALL setInt( sal_Int32 parameterIndex, sal_Int32 x ) override; + virtual void SAL_CALL setLong( sal_Int32 parameterIndex, sal_Int64 x ) override; + virtual void SAL_CALL setFloat( sal_Int32 parameterIndex, float x ) override; + virtual void SAL_CALL setDouble( sal_Int32 parameterIndex, double x ) override; + virtual void SAL_CALL setString( sal_Int32 parameterIndex, const OUString& x ) override; + virtual void SAL_CALL setBytes( sal_Int32 parameterIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL setDate( sal_Int32 parameterIndex, const css::util::Date& x ) override; + virtual void SAL_CALL setTime( sal_Int32 parameterIndex, const css::util::Time& x ) override; + virtual void SAL_CALL setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL setBinaryStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setCharacterStream( sal_Int32 parameterIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL setObject( sal_Int32 parameterIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL setObjectWithInfo( sal_Int32 parameterIndex, const css::uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) override; + virtual void SAL_CALL setRef( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XRef >& x ) override; + virtual void SAL_CALL setBlob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XBlob >& x ) override; + virtual void SAL_CALL setClob( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XClob >& x ) override; + virtual void SAL_CALL setArray( sal_Int32 parameterIndex, const css::uno::Reference< css::sdbc::XArray >& x ) override; + virtual void SAL_CALL clearParameters( ) override; + + // XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + /** implement the <method>execute</method>, without calling the approve listeners and without building a new + connection + @param _rClearForNotification mutex to clear before doing the final notifications + */ + void execute_NoApprove_NoNewConn(::osl::ResettableMutexGuard& _rClearForNotification); + + /** call the RowSetApproveListeners<p/> + @throws css::sdb::RowSetVetoException if one of the listeners vetoed + @throws css::uno::RuntimeException + */ + void approveExecution(); + + /// set m_xActiveConnection, fire a PropertyChangeEvent if necessary, do the event listener handling etc + void setActiveConnection( css::uno::Reference< css::sdbc::XConnection > const & _rxNewConn, bool _bFireEvent = true ); + + void implCancelRowUpdates( bool _bNotifyModified ); + + /** sets the given result set type/concurrency at the given statement, while respecting + possibly related data source settings + */ + void setStatementResultSetType( + const css::uno::Reference< css::beans::XPropertySet >& _rxStatement, + sal_Int32 _nDesiredResultSetType, + sal_Int32 _nDesiredResultSetConcurrency + ); + + /** initializes a given RowSet column with the ColumnSettings (width, format, hidden, etc.) from a + template column. + + If the template column supports any of the known column settings, they're plain copied. If not, + the template column is examined for a TableName and Name property, and the table column described + by those is used to find and copy the column settings. + */ + void impl_initializeColumnSettings_nothrow( + const css::uno::Reference< css::beans::XPropertySet >& _rxTemplateColumn, + const css::uno::Reference< css::beans::XPropertySet >& _rxRowSetColumn + ); + + /** initializes our parameters container (m_pParameters) according to the parameter columns as + obtained from our composer + */ + void impl_initParametersContainer_nothrow(); + /** disposes our parameters container + */ + void impl_disposeParametersContainer_nothrow(); + + using ORowSetBase::getFastPropertyValue; + using ORowSetBase::firePropertyChange; + using ORowSetBase::doCancelModification; + using ORowSetBase::isModification; + using ORowSetBase::isModified; + using ORowSetBase::isNew; + }; + + + // ORowSetClone + + class ORowSetClone : public cppu::BaseMutex + ,public OSubComponent + ,public ORowSetBase + ,public ::comphelper::OPropertyArrayUsageHelper < ORowSetClone > + { + ORowSet* m_pParent; + sal_Int32 m_nFetchDirection; + sal_Int32 m_nFetchSize; + bool m_bIsBookmarkable; + + protected: + // the clone can not insert anything + virtual void doCancelModification( ) override; + virtual bool isModification( ) override; + virtual bool isModified( ) override; + virtual bool isNew( ) override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const css::uno::Any& rValue) override; + public: + ORowSetClone( const css::uno::Reference<css::uno::XComponentContext>& _rContext, ORowSet& rParent, ::osl::Mutex* _pMutex ); + virtual ~ORowSetClone() override; + + // css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override + { + return css::uno::Sequence<sal_Int8>(); + } + + // css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // css::lang::XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override + { + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); + } + + // css::sdbc::XRowSet + virtual void SAL_CALL execute( ) override; + virtual void SAL_CALL addRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override; + virtual void SAL_CALL removeRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override; + + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + protected: + using ORowSetBase::doCancelModification; + using ORowSetBase::isModification; + using ORowSetBase::isModified; + using ORowSetBase::isNew; + using ORowSetBase::rowDeleted; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetBase.cxx b/dbaccess/source/core/api/RowSetBase.cxx new file mode 100644 index 000000000..46e612271 --- /dev/null +++ b/dbaccess/source/core/api/RowSetBase.cxx @@ -0,0 +1,1438 @@ +/* -*- 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 . + */ + +#include "RowSetBase.hxx" +#include "CRowSetDataColumn.hxx" +#include <connectivity/sdbcx/VCollection.hxx> +#include "RowSetCache.hxx" +#include <stringconstants.hxx> +#include <sal/log.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/seqstream.hxx> +#include <connectivity/dbexception.hxx> +#include <o3tl/safeint.hxx> +#include <tools/diagnose_ex.h> + +using namespace dbaccess; +using namespace connectivity; +using namespace connectivity::sdbcx; +using namespace comphelper; +using namespace dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::cppu; +using namespace ::osl; + +namespace dbaccess +{ + +// OEmptyCollection +class OEmptyCollection : public sdbcx::OCollection +{ +protected: + virtual void impl_refresh() override; + virtual connectivity::sdbcx::ObjectType createObject(const OUString& _rName) override; +public: + OEmptyCollection(::cppu::OWeakObject& _rParent,::osl::Mutex& _rMutex) : OCollection(_rParent, true, _rMutex, std::vector< OUString>()){} +}; + +void OEmptyCollection::impl_refresh() +{ +} + +connectivity::sdbcx::ObjectType OEmptyCollection::createObject(const OUString& /*_rName*/) +{ + return connectivity::sdbcx::ObjectType(); +} + +// ORowSetBase + +ORowSetBase::ORowSetBase( const Reference<XComponentContext>& _rContext, ::cppu::OBroadcastHelper& _rBHelper, ::osl::Mutex* _pMutex ) + :OPropertyStateContainer(_rBHelper) + ,m_pMutex(_pMutex) + ,m_pMySelf(nullptr) + ,m_rBHelper(_rBHelper) + ,m_aContext( _rContext ) + ,m_nLastColumnIndex(-1) + ,m_nDeletedPosition(-1) + ,m_nResultSetType( ResultSetType::FORWARD_ONLY ) + ,m_nResultSetConcurrency( ResultSetConcurrency::READ_ONLY ) + ,m_bClone(false) + ,m_bIgnoreResult(false) + ,m_bBeforeFirst(true) // changed from sal_False + ,m_bAfterLast(false) + ,m_bIsInsertRow(false) +{ + sal_Int32 nRBT = PropertyAttribute::READONLY | PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT; + + registerPropertyNoMember( PROPERTY_ROWCOUNT, PROPERTY_ID_ROWCOUNT, nRBT, cppu::UnoType<sal_Int32>::get(), css::uno::Any(sal_Int32(0)) ); + registerPropertyNoMember( PROPERTY_ISROWCOUNTFINAL, PROPERTY_ID_ISROWCOUNTFINAL, nRBT, cppu::UnoType<bool>::get(), css::uno::Any(false) ); +} + +ORowSetBase::~ORowSetBase() +{ + if(m_pColumns) + { + TDataColumns().swap(m_aDataColumns); + m_pColumns->acquire(); + m_pColumns->disposing(); + } +} + +// css::lang::XTypeProvider +Sequence< Type > ORowSetBase::getTypes() +{ + return ::comphelper::concatSequences(ORowSetBase_BASE::getTypes(),OPropertyStateContainer::getTypes()); +} + +// css::uno::XInterface +Any ORowSetBase::queryInterface( const Type & rType ) +{ + Any aRet = ORowSetBase_BASE::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OPropertyStateContainer::queryInterface(rType); + return aRet; +} + +void SAL_CALL ORowSetBase::getFastPropertyValue(Any& rValue,sal_Int32 nHandle) const +{ + if(m_pCache) + { + switch(nHandle) + { + case PROPERTY_ID_ROWCOUNT: + rValue <<= impl_getRowCount(); + break; + case PROPERTY_ID_ISROWCOUNTFINAL: + rValue <<= m_pCache->m_bRowCountFinal; + break; + default: + OPropertyStateContainer::getFastPropertyValue(rValue,nHandle); + } + } + else + OPropertyStateContainer::getFastPropertyValue(rValue,nHandle); +} + +// OComponentHelper +void SAL_CALL ORowSetBase::disposing() +{ + MutexGuard aGuard(*m_pMutex); + + if ( m_pColumns ) + { + TDataColumns().swap(m_aDataColumns); + m_pColumns->disposing(); + } + if ( m_pCache ) + { + m_pCache->deregisterOldRow(m_aOldRow); + m_pCache->deleteIterator(this); + } + m_pCache = nullptr; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ORowSetBase::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& SAL_CALL ORowSetBase::getInfoHelper() +{ + return *getArrayHelper(); +} + +// XRow +sal_Bool SAL_CALL ORowSetBase::wasNull( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + return !((m_nLastColumnIndex != -1) && !m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd() && m_aCurrentRow->is()) + || (**m_aCurrentRow)[m_nLastColumnIndex].isNull(); +} + +const ORowSetValue& ORowSetBase::getValue(sal_Int32 columnIndex) +{ + checkCache(); + return impl_getValue(columnIndex); +} + +const ORowSetValue& ORowSetBase::impl_getValue(sal_Int32 columnIndex) +{ + if ( m_bBeforeFirst || m_bAfterLast ) + { + SAL_WARN("dbaccess", "ORowSetBase::getValue: Illegal call here (we're before first or after last)!"); + ::dbtools::throwSQLException( DBA_RES( RID_STR_CURSOR_BEFORE_OR_AFTER ), StandardSQLState::INVALID_CURSOR_POSITION, *m_pMySelf ); + } + + if ( impl_rowDeleted() ) + { + return m_aEmptyValue; + } + + bool bValidCurrentRow = ( !m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd() && m_aCurrentRow->is() ); + if ( !bValidCurrentRow ) + { + // currentrow is null when the clone moves the window + positionCache( MOVE_NONE ); + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + OSL_ENSURE(!m_aCurrentRow.isNull(),"ORowSetBase::getValue: we don't stand on a valid row! Row is null."); + + bValidCurrentRow = ( !m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd() && m_aCurrentRow->is() ); + } + + if ( bValidCurrentRow ) + { +#if OSL_DEBUG_LEVEL > 0 + ORowSetMatrix::const_iterator aCacheEnd; + ORowSetMatrix::iterator aCurrentRow; + aCacheEnd = m_pCache->getEnd(); + aCurrentRow = m_aCurrentRow; + ORowSetCacheMap::const_iterator aCacheIter = m_aCurrentRow.getIter(); + ORowSetCacheIterator_Helper aHelper = aCacheIter->second; + ORowSetMatrix::const_iterator k = aHelper.aIterator; + for (; k != m_pCache->getEnd(); ++k) + { + ORowSetValueVector* pTemp = k->get(); + OSL_ENSURE( pTemp != reinterpret_cast<void*>(0xfeeefeee),"HALT!" ); + } + OSL_ENSURE(!m_aCurrentRow.isNull() && m_aCurrentRow < m_pCache->getEnd() && aCacheIter != m_pCache->m_aCacheIterators.end(),"Invalid iterator set for currentrow!"); +#endif + ORowSetRow rRow = *m_aCurrentRow; + bool bValidPosition = rRow.is() && o3tl::make_unsigned(columnIndex) < rRow->size(); + if (!bValidPosition) + { + SAL_WARN("dbaccess", "ORowSetBase::getValue: Invalid size of vector!"); + ::dbtools::throwSQLException( DBA_RES( RID_STR_CURSOR_BEFORE_OR_AFTER ), StandardSQLState::INVALID_CURSOR_POSITION, *m_pMySelf ); + } + m_nLastColumnIndex = columnIndex; + return (*rRow)[m_nLastColumnIndex]; + } + + // we should normally never reach this + return m_aEmptyValue; +} + +OUString SAL_CALL ORowSetBase::getString( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getString(); +} + +sal_Bool SAL_CALL ORowSetBase::getBoolean( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getBool(); +} + +sal_Int8 SAL_CALL ORowSetBase::getByte( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getInt8(); +} + +sal_Int16 SAL_CALL ORowSetBase::getShort( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getInt16(); +} + +sal_Int32 SAL_CALL ORowSetBase::getInt( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getInt32(); +} + +sal_Int64 SAL_CALL ORowSetBase::getLong( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getLong(); +} + +float SAL_CALL ORowSetBase::getFloat( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getFloat(); +} + +double SAL_CALL ORowSetBase::getDouble( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getDouble(); +} + +Sequence< sal_Int8 > SAL_CALL ORowSetBase::getBytes( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getSequence(); +} + +css::util::Date SAL_CALL ORowSetBase::getDate( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getDate(); +} + +css::util::Time SAL_CALL ORowSetBase::getTime( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getTime(); +} + +css::util::DateTime SAL_CALL ORowSetBase::getTimestamp( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + return getValue(columnIndex).getDateTime(); +} + +Reference< css::io::XInputStream > SAL_CALL ORowSetBase::getBinaryStream( sal_Int32 columnIndex ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( m_bBeforeFirst || m_bAfterLast ) + { + SAL_WARN("dbaccess", "ORowSetBase::getBinaryStream: Illegal call here (we're before first or after last)!"); + ::dbtools::throwSQLException( DBA_RES( RID_STR_CURSOR_BEFORE_OR_AFTER ), StandardSQLState::INVALID_CURSOR_POSITION, *m_pMySelf ); + } + + if ( impl_rowDeleted() ) + { + return nullptr; + } + + bool bValidCurrentRow = ( !m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd() && m_aCurrentRow->is() ); + if ( !bValidCurrentRow ) + { + positionCache( MOVE_NONE ); + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + OSL_ENSURE(!m_aCurrentRow.isNull(),"ORowSetBase::getBinaryStream: we don't stand on a valid row! Row is null."); + + bValidCurrentRow = ( !m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd() && m_aCurrentRow->is() ); + } + + if ( bValidCurrentRow ) + { + m_nLastColumnIndex = columnIndex; + return new ::comphelper::SequenceInputStream((**m_aCurrentRow)[m_nLastColumnIndex].getSequence()); + } + + // we should normally never reach this + return Reference< css::io::XInputStream >(); +} + +Reference< css::io::XInputStream > SAL_CALL ORowSetBase::getCharacterStream( sal_Int32 columnIndex ) +{ + return getBinaryStream(columnIndex); +} + +Any SAL_CALL ORowSetBase::getObject( sal_Int32 columnIndex, const Reference< XNameAccess >& /*typeMap*/ ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + return getValue(columnIndex).makeAny(); +} + +Reference< XRef > SAL_CALL ORowSetBase::getRef( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getRef", *m_pMySelf ); + return nullptr; +} + +Reference< XBlob > SAL_CALL ORowSetBase::getBlob( sal_Int32 columnIndex ) +{ + return Reference< XBlob >(getValue(columnIndex).makeAny(),UNO_QUERY); +} + +Reference< XClob > SAL_CALL ORowSetBase::getClob( sal_Int32 columnIndex ) +{ + return Reference< XClob >(getValue(columnIndex).makeAny(),UNO_QUERY); +} + +Reference< XArray > SAL_CALL ORowSetBase::getArray( sal_Int32 /*columnIndex*/ ) +{ + ::dbtools::throwFeatureNotImplementedSQLException( "XRow::getArray", *m_pMySelf ); + return nullptr; +} + +// css::sdbcx::XRowLocate +Any SAL_CALL ORowSetBase::getBookmark( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::getBookmark() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( m_bBeforeFirst || m_bAfterLast ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_BOOKMARK_BEFORE_OR_AFTER ), StandardSQLState::INVALID_CURSOR_POSITION, *m_pMySelf ); + + if ( impl_rowDeleted() ) + ::dbtools::throwSQLException( DBA_RES( RID_STR_NO_BOOKMARK_DELETED ), StandardSQLState::INVALID_CURSOR_POSITION, *m_pMySelf ); + + OSL_ENSURE( m_aBookmark.hasValue(), "ORowSetBase::getBookmark: bookmark has no value!" ); + return m_aBookmark; +} + +sal_Bool SAL_CALL ORowSetBase::moveToBookmark( const Any& bookmark ) +{ + SAL_INFO("dbaccess", "ORowSetBase::moveToBookmark(Any) Clone = " << m_bClone); + OSL_ENSURE(bookmark.hasValue(),"ORowSetBase::moveToBookmark bookmark has no value!"); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + if(!bookmark.hasValue() || m_nResultSetType == ResultSetType::FORWARD_ONLY) + { + if(bookmark.hasValue()) + SAL_WARN("dbaccess", "MoveToBookmark is not possible when we are only forward"); + else + SAL_WARN("dbaccess", "Bookmark is not valid"); + throwFunctionSequenceException(*m_pMySelf); + } + + checkCache(); + + bool bRet( notifyAllListenersCursorBeforeMove( aGuard ) ); + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || impl_rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + bRet = m_pCache->moveToBookmark(bookmark); + doCancelModification( ); + if(bRet) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + } + else + { + movementFailed(); + } + + // - IsModified + // - IsNew + aNotifier.fire( ); + } + SAL_INFO("dbaccess", "ORowSetBase::moveToBookmark(Any) = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Bool SAL_CALL ORowSetBase::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + SAL_INFO("dbaccess", "ORowSetBase::moveRelativeToBookmark(Any," << rows << ") Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + checkPositioningAllowed(); + + bool bRet( notifyAllListenersCursorBeforeMove( aGuard ) ); + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + bRet = m_pCache->moveRelativeToBookmark(bookmark,rows); + doCancelModification( ); + if(bRet) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + } + else + movementFailed(); + + // - IsModified + // - IsNew + aNotifier.fire( ); + + // RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::moveRelativeToBookmark(Any," << rows << ") = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Int32 SAL_CALL ORowSetBase::compareBookmarks( const Any& _first, const Any& _second ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + return m_pCache->compareBookmarks(_first,_second); +} + +sal_Bool SAL_CALL ORowSetBase::hasOrderedBookmarks( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + return m_pCache->hasOrderedBookmarks(); +} + +sal_Int32 SAL_CALL ORowSetBase::hashBookmark( const Any& bookmark ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + return m_pCache->hashBookmark(bookmark); +} + +// XResultSetMetaDataSupplier +Reference< XResultSetMetaData > SAL_CALL ORowSetBase::getMetaData( ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + Reference< XResultSetMetaData > xMeta; + if(m_pCache) + xMeta = m_pCache->getMetaData(); + + return xMeta; +} + +// XColumnLocate +sal_Int32 SAL_CALL ORowSetBase::findColumn( const OUString& columnName ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + // it is possible to save some time here when we remember the names - position relation in a map + return m_pColumns ? m_pColumns->findColumn(columnName) : sal_Int32(0); +} + +// css::sdbcx::XColumnsSupplier +Reference< XNameAccess > SAL_CALL ORowSetBase::getColumns( ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aColumnsMutex ); + if(!m_pColumns) + { + if (!m_pEmptyCollection) + m_pEmptyCollection.reset( new OEmptyCollection(*m_pMySelf,m_aColumnsMutex) ); + return m_pEmptyCollection.get(); + } + + return m_pColumns.get(); +} + +// XResultSet +sal_Bool SAL_CALL ORowSetBase::next( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::next() Clone = " << m_bClone); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkCache(); + + bool bRet( notifyAllListenersCursorBeforeMove( aGuard ) ); + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || impl_rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + positionCache( MOVE_FORWARD ); + bool bAfterLast = m_pCache->isAfterLast(); + bRet = m_pCache->next(); + doCancelModification( ); + + // if we were afterLast before next() then we still are, + // i.e. bAfterLast implies m_pCache->isAfterLast() + if (bAfterLast) + assert(m_pCache->isAfterLast()); + // so the only way bAfterLast != m_pCache->isAfterLast() + // would be that we just arrived there, + if (bAfterLast != m_pCache->isAfterLast()) + { + assert(!bAfterLast); + assert(m_pCache->isAfterLast()); + } + // in which case we *did* move the cursor + if ( bRet || bAfterLast != m_pCache->isAfterLast() ) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + OSL_ENSURE(!m_bBeforeFirst,"BeforeFirst is true. I don't know why?"); + } + else + { + // moved after the last row + movementFailed(); + OSL_ENSURE(m_bAfterLast,"AfterLast is false. I don't know why?"); + } + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::next() = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Bool SAL_CALL ORowSetBase::isBeforeFirst( ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + SAL_INFO("dbaccess", "ORowSetBase::isBeforeFirst() = " << m_bBeforeFirst << " Clone = " << m_bClone); + + return m_bBeforeFirst; +} + +sal_Bool SAL_CALL ORowSetBase::isAfterLast( ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + SAL_INFO("dbaccess", "ORowSetBase::isAfterLast() = " << m_bAfterLast << " Clone = " << m_bClone); + + return m_bAfterLast; +} + +bool ORowSetBase::isOnFirst() +{ + return isFirst(); +} + +sal_Bool SAL_CALL ORowSetBase::isFirst( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::isFirst() Clone = " << m_bClone); + + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( m_bBeforeFirst || m_bAfterLast ) + return false; + + if ( impl_rowDeleted() ) + return ( m_nDeletedPosition == 1 ); + + positionCache( MOVE_NONE ); + bool bIsFirst = m_pCache->isFirst(); + + SAL_INFO("dbaccess", "ORowSetBase::isFirst() = " << bIsFirst << " Clone = " << m_bClone); + return bIsFirst; +} + +bool ORowSetBase::isOnLast() +{ + return isLast(); +} + +sal_Bool SAL_CALL ORowSetBase::isLast( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::isLast() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( m_bBeforeFirst || m_bAfterLast ) + return false; + + if ( impl_rowDeleted() ) + { + if ( !m_pCache->m_bRowCountFinal ) + return false; + else + return ( m_nDeletedPosition == impl_getRowCount() ); + } + + positionCache( MOVE_NONE ); + bool bIsLast = m_pCache->isLast(); + + SAL_INFO("dbaccess", "ORowSetBase::isLast() = " << bIsLast << " Clone = " << m_bClone); + return bIsLast; +} + +void SAL_CALL ORowSetBase::beforeFirst( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::beforeFirst() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + checkPositioningAllowed(); + + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || impl_rowDeleted(); + + if((bWasNew || !m_bBeforeFirst) && notifyAllListenersCursorBeforeMove(aGuard) ) + { + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + if ( !m_bBeforeFirst ) + { + ORowSetRow aOldValues = getOldRow(bWasNew); + m_pCache->beforeFirst(); + doCancelModification( ); + + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + + // - IsModified + // - Isnew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + + // to be done _after_ the notifications! + m_aOldRow->clearRow(); + } + SAL_INFO("dbaccess", "ORowSetBase::beforeFirst() Clone = " << m_bClone); +} + +void SAL_CALL ORowSetBase::afterLast( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::afterLast() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkPositioningAllowed(); + + bool bWasNew = m_pCache->m_bNew || impl_rowDeleted(); + + if((bWasNew || !m_bAfterLast) && notifyAllListenersCursorBeforeMove(aGuard) ) + { + // check if we are inserting a row + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + if(!m_bAfterLast) + { + ORowSetRow aOldValues = getOldRow(bWasNew); + + m_pCache->afterLast(); + doCancelModification( ); + + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + + // - IsModified + // - Isnew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + } + SAL_INFO("dbaccess", "ORowSetBase::afterLast() Clone = " << m_bClone); +} + +bool SAL_CALL ORowSetBase::move(std::function<bool(ORowSetBase *)> const & _aCheckFunctor, + std::function<bool(ORowSetCache *)> const & _aMovementFunctor) +{ + SAL_INFO("dbaccess", "ORowSetBase::move() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkPositioningAllowed(); + + bool bRet( notifyAllListenersCursorBeforeMove( aGuard ) ); + if( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + bool bMoved = ( bWasNew || !_aCheckFunctor(this) ); + + bRet = _aMovementFunctor(m_pCache.get()); + doCancelModification( ); + + if ( bRet ) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( bMoved, true, aOldValues, aGuard ); + } + else + { // first goes wrong so there is no row + movementFailed(); + } + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::move() = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Bool SAL_CALL ORowSetBase::first( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::first() Clone = " << m_bClone); + auto ioF_tmp = std::mem_fn(&ORowSetBase::isOnFirst); + auto F_tmp = std::mem_fn(&ORowSetCache::first); + return move(ioF_tmp,F_tmp); +} + +sal_Bool SAL_CALL ORowSetBase::last( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::last() Clone = " << m_bClone); + auto ioL_tmp = std::mem_fn(&ORowSetBase::isOnLast); + auto L_tmp = std::mem_fn(&ORowSetCache::last); + return move(ioL_tmp,L_tmp); +} + +sal_Int32 SAL_CALL ORowSetBase::getRow( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::getRow() Clone = " << m_bClone); + ::osl::MutexGuard aGuard( *m_pMutex ); + + checkCache(); + return impl_getRow(); +} + +sal_Int32 ORowSetBase::impl_getRow() +{ + sal_Int32 nPos = 0; + if ( m_bBeforeFirst ) + nPos = 0; + else if ( m_bAfterLast ) + nPos = impl_getRowCount() + 1; + else if ( impl_rowDeleted() ) + nPos = m_nDeletedPosition; + else if ( !m_bClone && m_pCache->m_bNew ) + nPos = 0; + else + { + positionCache( MOVE_NONE ); + nPos = m_pCache->getRow(); + } + SAL_INFO("dbaccess", "ORowSetBase::impl_getRow() = " << nPos << " Clone = " << m_bClone); + return nPos; +} + +sal_Bool SAL_CALL ORowSetBase::absolute( sal_Int32 row ) +{ + SAL_INFO("dbaccess", "ORowSetBase::absolute(" << row << ") Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + checkPositioningAllowed(); + + bool bRet = ( row > 0 ) + && notifyAllListenersCursorBeforeMove( aGuard ); + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + bRet = m_pCache->absolute(row); + doCancelModification( ); + + if(bRet) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + } + else + { // absolute movement goes wrong we stand left or right side of the rows + movementFailed(); + } + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::absolute(" << row << ") = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Bool SAL_CALL ORowSetBase::relative( sal_Int32 rows ) +{ + SAL_INFO("dbaccess", "ORowSetBase::relative(" << rows << ") Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + if(!rows) + return true; // in this case do nothing + + checkPositioningAllowed(); + + bool bRet = + ( ( !m_bAfterLast || rows <= 0 ) + && ( !m_bBeforeFirst || rows >= 0 ) + && notifyAllListenersCursorBeforeMove( aGuard ) + ); + + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + positionCache( rows > 0 ? MOVE_FORWARD : MOVE_BACKWARD ); + bRet = m_pCache->relative(rows); + doCancelModification( ); + + if(bRet) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + } + else + { + movementFailed(); + } + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::relative(" << rows << ") = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +sal_Bool SAL_CALL ORowSetBase::previous( ) +{ + SAL_INFO("dbaccess", "ORowSetBase::previous() Clone = " << m_bClone); + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::ResettableMutexGuard aGuard( *m_pMutex ); + + checkPositioningAllowed(); + + bool bRet = !m_bBeforeFirst + && notifyAllListenersCursorBeforeMove(aGuard); + + if ( bRet ) + { + // check if we are inserting a row + bool bWasNew = m_pCache->m_bNew || rowDeleted(); + + ORowSetNotifier aNotifier( this ); + // this will call cancelRowModification on the cache if necessary + + ORowSetRow aOldValues = getOldRow(bWasNew); + + positionCache( MOVE_BACKWARD ); + bRet = m_pCache->previous(); + doCancelModification( ); + + // if m_bBeforeFirst is false and bRet is false then we stood on the first row + if(!m_bBeforeFirst || bRet) + { + // notification order + // - column values + // - cursorMoved + setCurrentRow( true, true, aOldValues, aGuard ); + } + else + { + SAL_WARN("dbaccess", "ORowSetBase::previous: inconsistency!" ); + // we should never reach this place, as we should not get into this whole branch if m_bBeforeFirst + // was |true| from the beginning + movementFailed(); + } + + // - IsModified + // - IsNew + aNotifier.fire(); + + // - RowCount/IsRowCountFinal + fireRowcount(); + } + SAL_INFO("dbaccess", "ORowSetBase::previous() = " << bRet << " Clone = " << m_bClone); + return bRet; +} + +void ORowSetBase::setCurrentRow( bool _bMoved, bool _bDoNotify, const ORowSetRow& _rOldValues, ::osl::ResettableMutexGuard& _rGuard ) +{ + SAL_INFO("dbaccess", "ORowSetBase::setCurrentRow() Clone = " << m_bClone); + m_bBeforeFirst = m_pCache->isBeforeFirst(); + m_bAfterLast = m_pCache->isAfterLast(); + + if(!(m_bBeforeFirst || m_bAfterLast)) + { + m_aBookmark = m_pCache->getBookmark(); + OSL_ENSURE(m_aBookmark.hasValue(),"Bookmark has no value!"); + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + OSL_ENSURE(!m_aCurrentRow.isNull(),"CurrentRow is null!"); + OSL_ENSURE(!m_aCurrentRow.isNull() && m_aCurrentRow != m_pCache->getEnd(),"Position of matrix iterator isn't valid!"); + OSL_ENSURE(m_aCurrentRow->is(),"Currentrow isn't valid"); + OSL_ENSURE(m_aBookmark.hasValue(),"Bookmark has no value!"); + + m_aCurrentRow = m_pCache->m_aMatrixIter; + m_bIsInsertRow = false; + OSL_ENSURE(!m_aCurrentRow.isNull(),"CurrentRow is nul after positionCache!"); +#if OSL_DEBUG_LEVEL > 0 + ORowSetRow rRow = *m_aCurrentRow; + OSL_ENSURE(rRow.is() ,"Invalid size of vector!"); +#endif + + // notification order + // - column values + if ( _bDoNotify ) + firePropertyChange(_rOldValues); + + } + else + { + m_aOldRow->clearRow(); + m_aCurrentRow = m_pCache->getEnd(); + m_aBookmark = Any(); + } + + // TODO: can this be done before the notifications? + if(!(m_bBeforeFirst || m_bAfterLast) && !m_aCurrentRow.isNull() && m_aCurrentRow->is() && m_aCurrentRow != m_pCache->getEnd()) + m_aOldRow->setRow(new ORowSetValueVector( *(*m_aCurrentRow) )); + + if ( _bMoved && _bDoNotify ) + // - cursorMoved + notifyAllListenersCursorMoved( _rGuard ); + + SAL_INFO("dbaccess", "ORowSetBase::setCurrentRow() Clone = " << m_bClone); +} + +void ORowSetBase::checkPositioningAllowed() +{ + if(!m_pCache || m_nResultSetType == ResultSetType::FORWARD_ONLY) + throwFunctionSequenceException(*m_pMySelf); +} + +Reference< XInterface > ORowSetBase::getStatement() +{ + return nullptr; +} + +void SAL_CALL ORowSetBase::refreshRow( ) +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + if ( impl_rowDeleted() ) + throwSQLException( "The current row is deleted", StandardSQLState::INVALID_CURSOR_STATE, Reference< XRowSet >( this ) ); + + if(!(m_bBeforeFirst || m_bAfterLast)) + { + bool bWasNew = m_pCache->m_bNew || impl_rowDeleted(); + ORowSetRow aOldValues = getOldRow(bWasNew); + positionCache( MOVE_NONE ); + m_pCache->refreshRow(); + firePropertyChange(aOldValues); + } +} + +sal_Bool SAL_CALL ORowSetBase::rowUpdated( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + + if ( impl_rowDeleted() ) + return false; + + return m_pCache->rowUpdated(); +} + +sal_Bool SAL_CALL ORowSetBase::rowInserted( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + + checkCache(); + + if ( impl_rowDeleted() ) + return false; + + return m_pCache->rowInserted(); +} + +sal_Bool SAL_CALL ORowSetBase::rowDeleted( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + checkCache(); + return impl_rowDeleted(); +} + +bool ORowSetBase::impl_rowDeleted( ) +{ + return !m_aBookmark.hasValue() && !m_bBeforeFirst && !m_bAfterLast; +} + +// XWarningsSupplier +Any SAL_CALL ORowSetBase::getWarnings( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + + if ( m_pCache ) + { + Reference< XWarningsSupplier > xWarnings( m_pCache->m_xSet.get(), UNO_QUERY ); + if ( xWarnings.is() ) + return xWarnings->getWarnings(); + } + + return Any(); +} + +void SAL_CALL ORowSetBase::clearWarnings( ) +{ + ::osl::MutexGuard aGuard( *m_pMutex ); + + if ( m_pCache ) + { + Reference< XWarningsSupplier > xWarnings( m_pCache->m_xSet.get(), UNO_QUERY ); + if ( xWarnings.is() ) + xWarnings->clearWarnings(); + } +} + +void ORowSetBase::firePropertyChange(const ORowSetRow& _rOldRow) +{ + if (!isPropertyChangeNotificationEnabled()) + return; + + SAL_INFO("dbaccess", "ORowSetBase::firePropertyChange" ); + SAL_INFO("dbaccess", "ORowSetBase::firePropertyChange() Clone = " << m_bClone); + OSL_ENSURE(m_pColumns,"Columns can not be NULL here!"); + sal_Int32 i=0; + for (auto const& dataColumn : m_aDataColumns) + { + try + { + dataColumn->fireValueChange(_rOldRow.is() ? (*_rOldRow)[i+1] : ::connectivity::ORowSetValue()); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "firePropertyChange: Exception on column " << i); + } + ++i; + } + SAL_INFO("dbaccess", "ORowSetBase::firePropertyChange() Clone = " << m_bClone); +} + +void ORowSetBase::firePropertyChange(sal_Int32 _nPos,const ::connectivity::ORowSetValue& _rOldValue) +{ + OSL_ENSURE(_nPos < static_cast<sal_Int32>(m_aDataColumns.size()),"nPos is invalid!"); + m_aDataColumns[_nPos]->fireValueChange(_rOldValue); +} + +void ORowSetBase::fireRowcount() +{ +} + +bool ORowSetBase::notifyAllListenersCursorBeforeMove(::osl::ResettableMutexGuard& /*_rGuard*/) +{ + return true; +} + +void ORowSetBase::notifyAllListenersCursorMoved(::osl::ResettableMutexGuard& /*_rGuard*/) +{ +} + +bool ORowSetBase::isPropertyChangeNotificationEnabled() const +{ + return true; +} + +void ORowSetBase::fireProperty( sal_Int32 _nProperty, bool _bNew, bool _bOld ) +{ + Any aNew( _bNew ); + Any aOld( _bOld ); + fire( &_nProperty, &aNew, &aOld, 1, false ); +} + +void ORowSetBase::positionCache( CursorMoveDirection _ePrepareForDirection ) +{ + SAL_INFO("dbaccess", "ORowSetBase::positionCache() Clone = " << m_bClone); + + bool bSuccess = false; + if ( m_aBookmark.hasValue() ) + { + if (_ePrepareForDirection == MOVE_NONE_REFRESH || + (m_pCache->isAfterLast() != bool(isAfterLast())) || ( m_pCache->isBeforeFirst() != bool(isBeforeFirst()) ) || + m_pCache->compareBookmarks( m_aBookmark, m_pCache->getBookmark() ) != CompareBookmark::EQUAL ) + bSuccess = m_pCache->moveToBookmark( m_aBookmark ); + else + bSuccess = true; + } + else + { + if ( m_bBeforeFirst ) + { + m_pCache->beforeFirst(); + bSuccess = true; + } + else if ( m_bAfterLast ) + { + m_pCache->afterLast(); + bSuccess = true; + } + else + { + OSL_ENSURE( m_nDeletedPosition >= 1, "ORowSetBase::positionCache: no bookmark, and no valid 'deleted position'!" ); + switch ( _ePrepareForDirection ) + { + case MOVE_FORWARD: + if ( m_nDeletedPosition > 1 ) + bSuccess = m_pCache->absolute( m_nDeletedPosition - 1 ); + else + { + m_pCache->beforeFirst(); + bSuccess = true; + } + break; + + case MOVE_BACKWARD: + if ( m_pCache->m_bRowCountFinal && ( m_nDeletedPosition == impl_getRowCount() ) ) + { + m_pCache->afterLast(); + bSuccess = true; + } + else + bSuccess = m_pCache->absolute( m_nDeletedPosition ); + break; + + case MOVE_NONE: + case MOVE_NONE_REFRESH: + bSuccess = false; // will be asserted below + break; + } + } + } + OSL_ENSURE( bSuccess, "ORowSetBase::positionCache: failed!" ); + + SAL_INFO("dbaccess", "ORowSetBase::positionCache() Clone = " << m_bClone); +} + +void ORowSetBase::checkCache() +{ + ::connectivity::checkDisposed(m_rBHelper.bDisposed); + if(!m_pCache) + throwFunctionSequenceException(*m_pMySelf); +} + +void ORowSetBase::movementFailed() +{ + SAL_INFO("dbaccess", "ORowSetBase::movementFailed() Clone = " << m_bClone); + m_aOldRow->clearRow(); + m_aCurrentRow = m_pCache->getEnd(); + m_bBeforeFirst = m_pCache->isBeforeFirst(); + m_bAfterLast = m_pCache->isAfterLast(); + m_aBookmark = Any(); + OSL_ENSURE(m_bBeforeFirst || m_bAfterLast,"BeforeFirst or AfterLast is wrong!"); + SAL_INFO("dbaccess", "ORowSetBase::movementFailed() Clone = " << m_bClone); +} + +ORowSetRow ORowSetBase::getOldRow(bool _bWasNew) +{ + OSL_ENSURE(m_aOldRow.is(),"RowSetRowHElper isn't valid!"); + ORowSetRow aOldValues; + if ( !_bWasNew && m_aOldRow->getRow().is() ) + aOldValues = new ORowSetValueVector( *(m_aOldRow->getRow())); // remember the old values + return aOldValues; +} + +void ORowSetBase::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const +{ + _rDefault.clear(); +} + +void ORowSetBase::onDeleteRow( const Any& _rBookmark ) +{ + if ( rowDeleted() ) + // not interested in + return; + + ::osl::MutexGuard aGuard( *m_pMutex ); + //OSL_ENSURE( m_aBookmark.hasValue(), "ORowSetBase::onDeleteRow: Bookmark isn't valid!" ); + if ( compareBookmarks( _rBookmark, m_aBookmark ) == CompareBookmark::EQUAL ) + { + positionCache( MOVE_NONE ); + m_nDeletedPosition = m_pCache->getRow(); + } +} + +void ORowSetBase::onDeletedRow( const Any& _rBookmark, sal_Int32 _nPos ) +{ + if ( rowDeleted() ) + { + // if we're a clone, and on a deleted row, and the main RowSet deleted another + // row (only the main RowSet can, clones can't), which is *before* our + // deleted position, then we have to adjust this position + if ( m_bClone && ( _nPos < m_nDeletedPosition ) ) + --m_nDeletedPosition; + return; + } + + ::osl::MutexGuard aGuard( *m_pMutex ); + if ( compareBookmarks( _rBookmark, m_aBookmark ) == CompareBookmark::EQUAL ) + { + m_aOldRow->clearRow(); + m_aCurrentRow = m_pCache->getEnd(); + m_aBookmark = Any(); + } +} + +sal_Int32 ORowSetBase::impl_getRowCount() const +{ + sal_Int32 nRowCount( m_pCache->m_nRowCount ); + if ( const_cast< ORowSetBase* >( this )->rowDeleted() && !m_pCache->m_bNew ) + ++nRowCount; + return nRowCount; +} + +struct ORowSetNotifierImpl +{ + std::vector<sal_Int32> aChangedColumns; + ORowSetValueVector::Vector aRow; +}; + + +ORowSetNotifier::ORowSetNotifier( ORowSetBase* _pRowSet ) + :m_pRowSet( _pRowSet ) + ,m_bWasNew( false ) + ,m_bWasModified( false ) +{ + + OSL_ENSURE( m_pRowSet, "ORowSetNotifier::ORowSetNotifier: invalid row set. This will crash." ); + + // remember the "inserted" and "modified" state for later firing + m_bWasNew = m_pRowSet->isNew( ORowSetBase::GrantNotifierAccess() ); + m_bWasModified = m_pRowSet->isModified( ORowSetBase::GrantNotifierAccess() ); + + // if the row set is on the insert row, then we need to cancel this + if ( m_pRowSet->isModification( ORowSetBase::GrantNotifierAccess() ) ) + m_pRowSet->doCancelModification( ORowSetBase::GrantNotifierAccess() ); +} + +ORowSetNotifier::ORowSetNotifier( ORowSetBase* _pRowSet, ORowSetValueVector::Vector&& i_aRow ) + :m_pImpl(new ORowSetNotifierImpl) + ,m_pRowSet( _pRowSet ) + ,m_bWasNew( false ) + ,m_bWasModified( false ) +{ + + OSL_ENSURE( m_pRowSet, "ORowSetNotifier::ORowSetNotifier: invalid row set. This will crash." ); + m_pImpl->aRow = std::move(i_aRow); // yes, create a copy to store the old values +} + +ORowSetNotifier::~ORowSetNotifier( ) +{ +} + +void ORowSetNotifier::fire() +{ + // we're not interested in firing changes FALSE->TRUE, only TRUE->FALSE. + // (the former would be quite pathological, e.g. after a failed movement) + + if ( m_bWasModified + && ( m_bWasModified != m_pRowSet->isModified( ORowSetBase::GrantNotifierAccess() ) ) + ) + m_pRowSet->fireProperty( PROPERTY_ID_ISMODIFIED, false, true, ORowSetBase::GrantNotifierAccess() ); + + if ( m_bWasNew + && ( m_bWasNew != m_pRowSet->isNew( ORowSetBase::GrantNotifierAccess() ) ) + ) + m_pRowSet->fireProperty( PROPERTY_ID_ISNEW, false, true, ORowSetBase::GrantNotifierAccess() ); +} + +std::vector<sal_Int32>& ORowSetNotifier::getChangedColumns() const +{ + OSL_ENSURE(m_pImpl, "Illegal CTor call, use the other one!"); + return m_pImpl->aChangedColumns; +} + +void ORowSetNotifier::firePropertyChange() +{ + OSL_ENSURE(m_pImpl, "Illegal CTor call, use the other one!"); + if (m_pImpl) + { + for (auto const& changedColumn : m_pImpl->aChangedColumns) + { + m_pRowSet->firePropertyChange(changedColumn-1 ,m_pImpl->aRow[changedColumn-1], ORowSetBase::GrantNotifierAccess()); + } + if ( !m_pImpl->aChangedColumns.empty() ) + m_pRowSet->fireProperty(PROPERTY_ID_ISMODIFIED,true,false, ORowSetBase::GrantNotifierAccess()); + } +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetBase.hxx b/dbaccess/source/core/api/RowSetBase.hxx new file mode 100644 index 000000000..f5443a2af --- /dev/null +++ b/dbaccess/source/core/api/RowSetBase.hxx @@ -0,0 +1,402 @@ +/* -*- 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 . + */ +#pragma once + +#include <memory> +#include <cppuhelper/implbase10.hxx> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <cppuhelper/interfacecontainer.h> +#include <connectivity/sqlerror.hxx> +#include <connectivity/CommonTools.hxx> +#include <comphelper/propertystatecontainer.hxx> +#include <comphelper/proparrhlp.hxx> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include "RowSetRow.hxx" +#include "RowSetCacheIterator.hxx" + +#include <functional> + +namespace com::sun::star { + namespace sdb { struct RowChangeEvent; } + namespace lang { struct Locale; } +} + +namespace dbaccess +{ + class OEmptyCollection; + + typedef ::cppu::ImplHelper10< css::sdbcx::XRowLocate, + css::sdbc::XRow, + css::sdbc::XResultSetMetaDataSupplier, + css::sdbc::XWarningsSupplier, + css::sdbc::XColumnLocate, + css::sdbcx::XColumnsSupplier, + css::lang::XServiceInfo, + css::sdbc::XRowSet, + css::sdbc::XCloseable, + css::lang::XUnoTunnel> ORowSetBase_BASE; + + class ORowSetCache; + class ORowSetDataColumns; + class ORowSetCacheIterator; + class ORowSetDataColumn; + class ORowSetBase : public ORowSetBase_BASE, + public ::comphelper::OPropertyStateContainer, + public ::comphelper::OPropertyArrayUsageHelper<ORowSetBase> // this class hold the static property info + { + protected: + typedef std::vector<ORowSetDataColumn*> TDataColumns; + ::osl::Mutex* m_pMutex; // this is the mutex from the rowset itself + ::osl::Mutex // we need an extra mutex for columns to prevent deadlock when setting new values + // for a row + m_aColumnsMutex; + + css::uno::Any m_aBookmark; + ORowSetCacheIterator m_aCurrentRow; // contains the actual fetched row + TORowSetOldRowHelperRef m_aOldRow; + TDataColumns m_aDataColumns; // holds the columns as m_pColumns but know the implementation class + connectivity::ORowSetValue m_aEmptyValue; // only for error case + + ::cppu::OWeakObject* m_pMySelf; // set by derived classes + std::shared_ptr<ORowSetCache> m_pCache; // the cache is used by the rowset and his clone (shared) + std::unique_ptr<ORowSetDataColumns> m_pColumns; // represent the select columns + ::cppu::OBroadcastHelper& m_rBHelper; // must be set from the derived classes + // is used when the formatkey for database types is set + css::uno::Reference< css::util::XNumberFormatTypes> m_xNumberFormatTypes; + std::unique_ptr<OEmptyCollection> m_pEmptyCollection; + + css::uno::Reference< css::uno::XComponentContext> m_aContext; + ::connectivity::SQLError m_aErrors; + + sal_Int32 m_nLastColumnIndex; // the last column ask for, used for wasNull() + sal_Int32 m_nDeletedPosition; // is set only when a row was deleted + sal_Int32 m_nResultSetType; // fetch property + sal_Int32 m_nResultSetConcurrency; + bool m_bClone; // I'm clone or not + bool m_bIgnoreResult ; + bool m_bBeforeFirst : 1; + bool m_bAfterLast : 1; + bool m_bIsInsertRow : 1; + + protected: + ORowSetBase( + const css::uno::Reference<css::uno::XComponentContext>& _rContext, + ::cppu::OBroadcastHelper& _rBHelper, + ::osl::Mutex* _pMutex + ); + + // fire a notification for all that are listening on column::VALUE property + void firePropertyChange(const ORowSetRow& _rOldRow); + // fire a change for one column + // _nPos starts at zero + void firePropertyChange(sal_Int32 _nPos,const ::connectivity::ORowSetValue& _rNewValue); + + // fire if rowcount changed + virtual void fireRowcount(); + // notify row changed + virtual bool notifyAllListenersCursorBeforeMove(::osl::ResettableMutexGuard& _rGuard); + // notify cursor moved + virtual void notifyAllListenersCursorMoved(::osl::ResettableMutexGuard& _rGuard); + + // cancel the insertion, if necessary (means if we're on the insert row) + virtual void doCancelModification( ) = 0; + // return <TRUE/> if and only if we're using the insert row (means: we're updating _or_ inserting) + virtual bool isModification( ) = 0; + // return <TRUE/> if and only if the current row is modified + // TODO: isn't this the same as isModification? + virtual bool isModified( ) = 0; + // return <TRUE/> if and only if the current row is the insert row + virtual bool isNew( ) = 0; + // return <TRUE/> if the property change notification should be fired + // upon property change. + virtual bool isPropertyChangeNotificationEnabled() const; + // notify the change of a boolean property + void fireProperty( sal_Int32 _nProperty, bool _bNew, bool _bOld ); + + // OPropertyStateContainer + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue,sal_Int32 nHandle) const override; + + enum CursorMoveDirection + { + /// denotes a cursor move forward + MOVE_FORWARD, + /// denotes a cursor move backwards + MOVE_BACKWARD, + /// denotes no cursor move at all, but move cache to current row (if it is not there already) + MOVE_NONE, + /// denotes no cursor move at all, but force the cache to move to current row (and refresh the row) + MOVE_NONE_REFRESH + }; + /** positions the cache in preparation of a cursor move + + Normally, the cache is simply moved to our bookmark (m_aBookmark). If however the current + row is deleted, then the cache is properly positioned for a following cursor movement in the + given direction. + + @param _ePrepareForDirection + the direction into which the cursor should be moved after the call. If we're currently not on + a deleted row, this parameter is ignored, since in this case the cache is simply moved to + m_aBookmark.</br> + If, however, we're currently on a deleted row, this is used to properly position the cache + using <member>m_nDeletedPosition</member>.<br/> + In this case, MOVE_NONE(_REFRESH) is not supported. This is because the deleted row + (to which the RowSet currently points to) is not present in the cache. So, you cannot move the + cache to this row. + */ + void positionCache( CursorMoveDirection _ePrepareForDirection ); + + // returns a value of a column of the current row + const connectivity::ORowSetValue& getValue(sal_Int32 columnIndex); + // the cache has to be checked before calling this method + const connectivity::ORowSetValue& impl_getValue(sal_Int32 columnIndex); + // sets the current and the bookmark + void setCurrentRow( bool _bMoved, bool _bDoNotify, const ORowSetRow& _rOldValues, ::osl::ResettableMutexGuard& _rGuard); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + void checkPositioningAllowed(); + // checks if the cache is null + void checkCache(); + // sets the bookmark to Any() + // m_aCurrentRow to end of matrix + // m_aOldRow to NULL + void movementFailed(); + + ORowSetRow getOldRow(bool _bWasNew); + /** move the cache the position defined by the member functor + @param _aCheckFunctor + Return <TRUE/> when we already stand on the row we want to. + @param _aMovementFunctor + The method used to move. + @return + <TRUE/> if movement was successful. + */ + bool SAL_CALL move( std::function<bool(ORowSetBase *)> const & _aCheckFunctor, + std::function<bool(ORowSetCache *)> const & _aMovementFunctor); + + /** same meaning as isFirst. Only need by mem_fun + @return + <TRUE/> if so. + */ + bool isOnFirst(); + /** same meaning as isLast. Only need by mem_fun + @return + <TRUE/> if so. + */ + bool isOnLast(); + + /** returns the current row count + + This function takes into account that we might actually be positioned on a + deleted row, so that m_pCache->m_nRowCount does not really reflect the actual + count. + + @precond + Our mutex is locked. + */ + sal_Int32 impl_getRowCount() const; + + // the checkCache has to be called before calling this methods + sal_Int32 impl_getRow(); + bool impl_rowDeleted(); + + public: + virtual ~ORowSetBase() override; + + // OComponentHelper + virtual void SAL_CALL disposing(); + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override + { + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); + } + + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + // css::sdbc::XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + // css::sdbc::XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + + // css::sdbc::XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + + // css::sdbcx::XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + + // css::sdbc::XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + + // css::sdbcx::XRowLocate + virtual css::uno::Any SAL_CALL getBookmark( ) override; + virtual sal_Bool SAL_CALL moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Bool SAL_CALL moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ) override; + virtual sal_Int32 SAL_CALL compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual sal_Bool SAL_CALL hasOrderedBookmarks( ) override; + virtual sal_Int32 SAL_CALL hashBookmark( const css::uno::Any& bookmark ) override; + + // css::sdbc::XResultSet + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + + // css::sdbc::XRowSet + virtual void SAL_CALL execute( ) override = 0; + virtual void SAL_CALL addRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override = 0; + virtual void SAL_CALL removeRowSetListener( const css::uno::Reference< css::sdbc::XRowSetListener >& listener ) override = 0; + + // is called when the rowset is going to delete this bookmark _rBookmark + void onDeleteRow( const css::uno::Any& _rBookmark ); + // is called when the rowset has deleted this bookmark _rBookmark + void onDeletedRow( const css::uno::Any& _rBookmark, sal_Int32 _nPos ); + + // granular access control + struct GrantNotifierAccess { friend class ORowSetNotifier; private: GrantNotifierAccess () { } }; + + // cancel the insertion, if necessary (means if we're on the insert row) + void doCancelModification( const GrantNotifierAccess& ) { doCancelModification(); } + bool isModification( const GrantNotifierAccess& ) { return isModification(); } + bool isModified( const GrantNotifierAccess& ) { return isModified(); } + bool isNew( const GrantNotifierAccess& ) { return isNew(); } + bool isInsertRow() const { return m_bIsInsertRow; } // isNew() || isModified(); } + void fireProperty( sal_Int32 _nProperty, bool _bNew, bool _bOld, const GrantNotifierAccess& ) + { + fireProperty( _nProperty, _bNew, _bOld ); + } + void firePropertyChange(sal_Int32 _nPos,const ::connectivity::ORowSetValue& _rNewValue, const GrantNotifierAccess& ) + { + firePropertyChange(_nPos,_rNewValue); + } + using ::comphelper::OPropertyStateContainer::getFastPropertyValue; + }; + + /** eases the handling of the doCancelModification and notifyCancelInsert methods + + <p>The class can only be used on the stack, within a method of ORowSetBase (or derivees)</p> + */ + struct ORowSetNotifierImpl; + class ORowSetNotifier + { + private: + std::unique_ptr<ORowSetNotifierImpl> m_pImpl; + ORowSetBase* m_pRowSet; + // not acquired! This is not necessary because this class here is to be used on the stack within + // a method of ORowSetBase (or derivees) + bool m_bWasNew; + bool m_bWasModified; + + public: + /** constructs the object, and cancels the insertion + + @see ORowSetBase::doCancelModification + */ + explicit ORowSetNotifier( ORowSetBase* m_pRowSet ); + + /** use this one to construct a vector for change value notification + */ + ORowSetNotifier( ORowSetBase* m_pRowSet, ORowSetValueVector::Vector&& i_aRow ); + + // destructs the object. <member>fire</member> has to be called before. + ~ORowSetNotifier( ); + + /** notifies the insertion + + <p>This has <em>not</em> been put into the destructor by intention!<br/> + + The destructor is called during stack unwinding in case of an exception, so if we would do + listener notification there, this would have the potential of another exception during stack + unwinding, which would terminate the application.</p> + + @see ORowSetBase::notifyCancelInsert + */ + void fire(); + + /** notifies value change events and notifies IsModified + @param i_aChangedColumns the index of the changed value columns + @param i_aRow the old values + @see ORowSetBase::notifyCancelInsert + */ + void firePropertyChange(); + + /** use this one to store the inde of the changed column values + */ + std::vector<sal_Int32>& getChangedColumns() const; + + }; + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetCache.cxx b/dbaccess/source/core/api/RowSetCache.cxx new file mode 100644 index 000000000..8a23d6624 --- /dev/null +++ b/dbaccess/source/core/api/RowSetCache.cxx @@ -0,0 +1,1713 @@ +/* -*- 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 . + */ + +#include <memory> +#include "BookmarkSet.hxx" +#include "KeySet.hxx" +#include "OptimisticSet.hxx" +#include "RowSetBase.hxx" +#include "RowSetCache.hxx" +#include "StaticSet.hxx" +#include "WrappedResultSet.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> + +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> + +#include <comphelper/extract.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sqliterator.hxx> +#include <connectivity/sqlnode.hxx> +#include <connectivity/sqlparse.hxx> +#include <sqlbison.hxx> +#include <tools/diagnose_ex.h> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <algorithm> + +using namespace dbaccess; +using namespace dbtools; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::cppu; +using namespace ::osl; + +// 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. + + +ORowSetCache::ORowSetCache(const Reference< XResultSet >& _xRs, + const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer, + const Reference<XComponentContext>& _rContext, + const OUString& _rUpdateTableName, + bool& _bModified, + bool& _bNew, + const ORowSetValueVector& _aParameterValueForCache, + const OUString& i_sRowSetFilter, + sal_Int32 i_nMaxRows) + :m_xSet(_xRs) + ,m_xMetaData(Reference< XResultSetMetaDataSupplier >(_xRs,UNO_QUERY_THROW)->getMetaData()) + ,m_aContext( _rContext ) + ,m_nFetchSize(0) + ,m_nRowCount(0) + ,m_nPrivileges( Privilege::SELECT ) + ,m_nPosition(0) + ,m_nStartPos(0) + ,m_nEndPos(0) + ,m_bRowCountFinal(false) + ,m_bBeforeFirst(true) + ,m_bAfterLast( false ) + ,m_bModified(_bModified) + ,m_bNew(_bNew) +{ + + // first try if the result can be used to do inserts and updates + Reference< XPropertySet> xProp(_xRs,UNO_QUERY); + Reference< XPropertySetInfo > xPropInfo = xProp->getPropertySetInfo(); + bool bBookmarkable = false; + try + { + Reference< XResultSetUpdate> xUp(_xRs,UNO_QUERY_THROW); + bBookmarkable = xPropInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE) && + any2bool(xProp->getPropertyValue(PROPERTY_ISBOOKMARKABLE)) && Reference< XRowLocate >(_xRs, UNO_QUERY).is(); + if ( bBookmarkable ) + { + xUp->moveToInsertRow(); + xUp->cancelRowUpdates(); + _xRs->beforeFirst(); + m_nPrivileges = Privilege::SELECT|Privilege::DELETE|Privilege::INSERT|Privilege::UPDATE; + m_xCacheSet = new WrappedResultSet(i_nMaxRows); + m_xCacheSet->construct(_xRs,i_sRowSetFilter); + return; + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess.core"); + } + try + { + if ( xPropInfo->hasPropertyByName(PROPERTY_RESULTSETTYPE) && + ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETTYPE)) != ResultSetType::FORWARD_ONLY) + _xRs->beforeFirst(); + } + catch(const SQLException&) + { + TOOLS_WARN_EXCEPTION("dbaccess.core", "ORowSetCache"); + } + + // check if all keys of the updateable table are fetched + bool bAllKeysFound = false; + sal_Int32 nTablesCount = 0; + + bool bNeedKeySet = !bBookmarkable || (xPropInfo->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) && + ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY); + + 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; + else if(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(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(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); + + // check privileges + m_nPrivileges = Privilege::SELECT; + bool bNoInsert = false; + + Sequence< OUString> aNames(xColumns->getElementNames()); + const OUString* pIter = aNames.getConstArray(); + const OUString* pEnd = pIter + aNames.getLength(); + for(;pIter != pEnd;++pIter) + { + Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),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(*pIter) == 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 + } + } + } + + rtl::Reference<OKeySet> pKeySet = new OKeySet(m_aUpdateTable, aUpdateTableName ,_xAnalyzer,_aParameterValueForCache,i_nMaxRows,m_nRowCount); + try + { + m_xCacheSet = pKeySet; + pKeySet->construct(_xRs,i_sRowSetFilter); + + 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; + } + } + + } + // last check + if(!bAllKeysFound && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_RESULTSETCONCURRENCY) && + ::comphelper::getINT32(xProp->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY)) == ResultSetConcurrency::READ_ONLY) + m_nPrivileges = Privilege::SELECT; +} + +ORowSetCache::~ORowSetCache() +{ + m_xCacheSet.clear(); + if(m_pMatrix) + { + m_pMatrix->clear(); + m_pMatrix.reset(); + } + + if(m_pInsertMatrix) + { + m_pInsertMatrix->clear(); + m_pInsertMatrix.reset(); + } + m_xSet = WeakReference< XResultSet>(); + m_xMetaData = nullptr; + m_aUpdateTable = nullptr; +} + +void ORowSetCache::setFetchSize(sal_Int32 _nSize) +{ + if(_nSize == m_nFetchSize) + return; + + m_nFetchSize = _nSize; + if(!m_pMatrix) + { + m_pMatrix.reset( new ORowSetMatrix(_nSize) ); + m_aMatrixIter = m_pMatrix->end(); + m_aMatrixEnd = m_pMatrix->end(); + + 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(const auto& [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); + + if ( nKeyPos < _nSize ) + m_aMatrixIter = m_pMatrix->begin() + nKeyPos; + else + m_aMatrixIter = m_pMatrix->end(); + m_aMatrixEnd = m_pMatrix->end(); + + // 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(const auto& 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; + } + else if (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 + } + + return lcl_getBookmark((**m_aMatrixIter)[0],m_xCacheSet.get()); +} + +bool ORowSetCache::moveToBookmark( const Any& bookmark ) +{ + if ( m_xCacheSet->moveToBookmark(bookmark) ) + { + m_bBeforeFirst = false; + m_nPosition = m_xCacheSet->getRow(); + + checkPositionFlags(); + + if(!m_bAfterLast) + { + moveWindow(); + checkPositionFlags(); + if ( !m_bAfterLast ) + { + m_aMatrixIter = calcPosition(); + OSL_ENSURE(m_aMatrixIter->is(),"Iterator after moveToBookmark not valid"); + } + else + m_aMatrixIter = m_pMatrix->end(); + } + else + m_aMatrixIter = m_pMatrix->end(); + } + else + return false; + + return m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is(); +} + +bool ORowSetCache::moveRelativeToBookmark( const Any& bookmark, sal_Int32 rows ) +{ + bool bRet( moveToBookmark( bookmark ) ); + if ( bRet ) + { + m_nPosition = m_xCacheSet->getRow() + rows; + absolute(m_nPosition); + + bRet = m_aMatrixIter != m_pMatrix->end() && (*m_aMatrixIter).is(); + } + + return bRet; +} + +sal_Int32 ORowSetCache::compareBookmarks( const Any& _first, const Any& _second ) +{ + return (!_first.hasValue() || !_second.hasValue()) ? CompareBookmark::NOT_COMPARABLE : m_xCacheSet->compareBookmarks(_first,_second); +} + +bool ORowSetCache::hasOrderedBookmarks( ) +{ + return m_xCacheSet->hasOrderedBookmarks(); +} + +sal_Int32 ORowSetCache::hashBookmark( const Any& bookmark ) +{ + return m_xCacheSet->hashBookmark(bookmark); +} + +// XRowUpdate +void ORowSetCache::updateNull(sal_Int32 columnIndex,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ) +{ + checkUpdateConditions(columnIndex); + + ORowSetValueVector::Vector& rInsert = **m_aInsertRow; + if ( !rInsert[columnIndex].isNull() ) + { + rInsert[columnIndex].setBound(true); + rInsert[columnIndex].setNull(); + rInsert[columnIndex].setModified(true); + io_aRow[columnIndex].setNull(); + + m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns); + impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns); + } +} + +void ORowSetCache::updateValue(sal_Int32 columnIndex,const ORowSetValue& x + ,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ) +{ + checkUpdateConditions(columnIndex); + + ORowSetValueVector::Vector& rInsert = **m_aInsertRow; + if ( rInsert[columnIndex] != x ) + { + rInsert[columnIndex].setBound(true); + rInsert[columnIndex] = x; + rInsert[columnIndex].setModified(true); + io_aRow[columnIndex] = rInsert[columnIndex]; + + m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns); + impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns); + } +} + +void ORowSetCache::updateCharacterStream( sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x + , sal_Int32 length,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ) +{ + checkUpdateConditions(columnIndex); + + Sequence<sal_Int8> aSeq; + if(x.is()) + x->readBytes(aSeq,length); + + ORowSetValueVector::Vector& rInsert = **m_aInsertRow; + rInsert[columnIndex].setBound(true); + rInsert[columnIndex] = aSeq; + rInsert[columnIndex].setModified(true); + io_aRow[columnIndex] = Any(x); + + m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns); + impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns); +} + +void ORowSetCache::updateObject( sal_Int32 columnIndex, const Any& x + ,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ) +{ + checkUpdateConditions(columnIndex); + + ORowSetValueVector::Vector& rInsert = **m_aInsertRow; + ORowSetValue aTemp; + aTemp.fill(x); + if ( rInsert[columnIndex] != aTemp ) + { + rInsert[columnIndex].setBound(true); + rInsert[columnIndex] = aTemp; + rInsert[columnIndex].setModified(true); + io_aRow[columnIndex] = rInsert[columnIndex]; + + m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns); + impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns); + } +} + +void ORowSetCache::updateNumericObject( sal_Int32 columnIndex, const Any& x + ,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ) +{ + checkUpdateConditions(columnIndex); + + ORowSetValueVector::Vector& rInsert = **m_aInsertRow; + ORowSetValue aTemp; + aTemp.fill(x); + if ( rInsert[columnIndex] != aTemp ) + { + rInsert[columnIndex].setBound(true); + rInsert[columnIndex] = aTemp; + rInsert[columnIndex].setModified(true); + io_aRow[columnIndex] = rInsert[columnIndex]; + + m_xCacheSet->mergeColumnValues(columnIndex,rInsert,io_aRow,o_ChangedColumns); + impl_updateRowFromCache_throw(io_aRow,o_ChangedColumns); + } +} + +// XResultSet +bool ORowSetCache::next( ) +{ + if(!isAfterLast()) + { + m_bBeforeFirst = false; + ++m_nPosition; + + // after we increment the position we have to check if we are already after the last row + checkPositionFlags(); + if(!m_bAfterLast) + { + moveWindow(); + + OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!"); + m_aMatrixIter = calcPosition(); + checkPositionFlags(); + } + } + + return !m_bAfterLast; +} + + +bool ORowSetCache::isFirst( ) const +{ + return m_nPosition == 1; // ask resultset for +} + +bool ORowSetCache::isLast( ) const +{ + return m_nPosition == m_nRowCount; +} + +void ORowSetCache::beforeFirst( ) +{ + if(!m_bBeforeFirst) + { + m_bAfterLast = false; + m_nPosition = 0; + m_bBeforeFirst = true; + m_xCacheSet->beforeFirst(); + moveWindow(); + m_aMatrixIter = m_pMatrix->end(); + } +} + +void ORowSetCache::afterLast( ) +{ + if(m_bAfterLast) + return; + + m_bBeforeFirst = false; + m_bAfterLast = true; + + if(!m_bRowCountFinal) + { + m_xCacheSet->last(); + m_bRowCountFinal = true; + m_nRowCount = m_xCacheSet->getRow();// + 1 removed + } + m_xCacheSet->afterLast(); + + m_nPosition = 0; + m_aMatrixIter = m_pMatrix->end(); +} + +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" ); + + ORowSetMatrix::iterator aIter; + sal_Int32 i; + bool bCheck; + sal_Int32 requestedStartPos; + if ( _nNewStartPos == -1 ) + { + aIter = m_pMatrix->begin() + (m_nEndPos - m_nStartPos); + i = m_nEndPos + 1; + requestedStartPos = m_nStartPos; + } + else + { + aIter = m_pMatrix->begin(); + i = _nNewStartPos + 1; + requestedStartPos = _nNewStartPos; + } + bCheck = m_xCacheSet->absolute(i); + + + 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. + + bool bCheck; + bCheck = m_xCacheSet->absolute(nNewStartPos + 1); + + // 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 + + if ( bCheck ) + { + { + ORowSetMatrix::iterator aIter(aEnd); + sal_Int32 nPos = m_nStartPos + 1; + fill(aIter, aNewEnd, nPos, bCheck); + } + + 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; + } + else if(!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(); + + nPos = m_nStartPos + 1; + bCheck = m_xCacheSet->absolute(nPos); + for(; !aIter->is() && bCheck;++aIter, ++nPos) + { + OSL_ENSURE(aIter != m_pMatrix->end(),"Invalid iterator"); + + *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; + + OSL_ENSURE(m_bBeforeFirst || m_bNew,"ORowSetCache::first return false and BeforeFirst isn't true"); + m_aMatrixIter = m_pMatrix->end(); + } + return bRet; +} + +bool ORowSetCache::last( ) +{ + bool bRet = m_xCacheSet->last(); + if(bRet) + { + m_bBeforeFirst = m_bAfterLast = false; + if(!m_bRowCountFinal) + { + m_bRowCountFinal = true; + m_nRowCount = m_xCacheSet->getRow(); // not + 1 + } + m_nPosition = m_xCacheSet->getRow(); + moveWindow(); + // we have to repositioning because moveWindow can modify the cache + m_xCacheSet->last(); + OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!"); + m_aMatrixIter = calcPosition(); + } + else + { + m_bRowCountFinal = m_bBeforeFirst = m_bAfterLast = true; + m_nRowCount = m_nPosition = 0; + OSL_ENSURE(m_bBeforeFirst,"ORowSetCache::last return false and BeforeFirst isn't true"); + m_aMatrixIter = m_pMatrix->end(); + } +#if OSL_DEBUG_LEVEL > 0 + if(bRet) + { + assert((*m_aMatrixIter).is() && "ORowSetCache::last: Row not valid!"); + } +#endif + + return bRet; +} + +sal_Int32 ORowSetCache::getRow( ) const +{ + return (isBeforeFirst() || isAfterLast()) ? 0 : m_nPosition; +} + +bool ORowSetCache::absolute( sal_Int32 row ) +{ + if(!row ) + throw SQLException(DBA_RES(RID_STR_NO_ABS_ZERO),nullptr,SQLSTATE_GENERAL,1000,Any() ); + + 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(); + + if(!m_bAfterLast) + { + moveWindow(); + checkPositionFlags(); + if(!m_bAfterLast) + m_aMatrixIter = calcPosition(); + else + m_aMatrixIter = m_pMatrix->end(); + } + else + m_aMatrixIter = m_pMatrix->end(); + } + + return !(m_bAfterLast || m_bBeforeFirst); +} + +bool ORowSetCache::relative( sal_Int32 rows ) +{ + bool bErg = true; + if(rows) + { + sal_Int32 nNewPosition = m_nPosition + rows; + + if ( m_bBeforeFirst && rows > 0 ) + nNewPosition = rows; + else if ( m_bRowCountFinal && m_bAfterLast && rows < 0 ) + nNewPosition = m_nRowCount + 1 + rows; + else + if ( m_bBeforeFirst || ( m_bRowCountFinal && m_bAfterLast ) ) + throw SQLException( DBA_RES( RID_STR_NO_RELATIVE ), nullptr, SQLSTATE_GENERAL, 1000, Any() ); + if ( nNewPosition ) + { + bErg = absolute( nNewPosition ); + bErg = bErg && !isAfterLast() && !isBeforeFirst(); + } + else + { + m_bBeforeFirst = true; + bErg = false; + } + } + return bErg; +} + +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()!"); + + checkPositionFlags(); + + if(!m_nPosition) + { + m_bBeforeFirst = true; + m_aMatrixIter = m_pMatrix->end(); + } + else + { + m_aMatrixIter = calcPosition(); + bRet = (*m_aMatrixIter).is(); + } + } + } + return bRet; +} + +void ORowSetCache::refreshRow( ) +{ + if(isAfterLast()) + throw SQLException(DBA_RES(RID_STR_NO_REFRESH_AFTERLAST),nullptr,SQLSTATE_GENERAL,1000,Any() ); + OSL_ENSURE(m_aMatrixIter != m_pMatrix->end(),"refreshRow() called for invalid row!"); + m_xCacheSet->refreshRow(); + m_xCacheSet->fillValueRow(*m_aMatrixIter,m_nPosition); + if ( m_bNew ) + { + cancelRowModification(); + } +} + +bool ORowSetCache::rowUpdated( ) +{ + return m_xCacheSet->rowUpdated(); +} + +bool ORowSetCache::rowInserted( ) +{ + return m_xCacheSet->rowInserted(); +} + +// XResultSetUpdate +bool ORowSetCache::insertRow(std::vector< Any >& o_aBookmarks) +{ + if ( !m_bNew || !m_aInsertRow->is() ) + throw SQLException(DBA_RES(RID_STR_NO_MOVETOINSERTROW_CALLED),nullptr,SQLSTATE_GENERAL,1000,Any() ); + + m_xCacheSet->insertRow(*m_aInsertRow,m_aUpdateTable); + + bool bRet( rowInserted() ); + if ( bRet ) + { + ++m_nRowCount; + Any aBookmark = (**m_aInsertRow)[0].makeAny(); + m_bAfterLast = m_bBeforeFirst = false; + if(aBookmark.hasValue()) + { + moveToBookmark(aBookmark); + // update the cached values + ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter; + ORowSetMatrix::const_iterator aIter = m_pMatrix->begin(); + for(;aIter != m_pMatrix->end();++aIter) + { + if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) ) + { + o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get())); + } + } + } + else + { + OSL_FAIL("There must be a bookmark after the row was inserted!"); + } + } + return bRet; +} + +void ORowSetCache::resetInsertRow(bool _bClearInsertRow) +{ + if ( _bClearInsertRow ) + clearInsertRow(); + m_bNew = false; + m_bModified = false; +} + +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); +} + +void ORowSetCache::updateRow( ORowSetMatrix::iterator const & _rUpdateRow, std::vector< Any >& o_aBookmarks ) +{ + if(isAfterLast() || isBeforeFirst()) + throw SQLException(DBA_RES(RID_STR_NO_UPDATEROW),nullptr,SQLSTATE_GENERAL,1000,Any() ); + + 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 ( moveToBookmark(aBookmark) ) + { + // update the cached values + ORowSetValueVector::Vector& rCurrentRow = **m_aMatrixIter; + ORowSetMatrix::const_iterator aIter = m_pMatrix->begin(); + for(;aIter != m_pMatrix->end();++aIter) + { + if ( m_aMatrixIter != aIter && aIter->is() && m_xCacheSet->columnValuesUpdated(**aIter,rCurrentRow) ) + { + o_aBookmarks.push_back(lcl_getBookmark((**aIter)[0], m_xCacheSet.get())); + } + } + } + + m_bModified = false; +} + +bool ORowSetCache::deleteRow( ) +{ + if(isAfterLast() || isBeforeFirst()) + throw SQLException(DBA_RES(RID_STR_NO_DELETEROW),nullptr,SQLSTATE_GENERAL,1000,Any() ); + + m_xCacheSet->deleteRow(*m_aMatrixIter,m_aUpdateTable); + if ( !m_xCacheSet->rowDeleted() ) + return false; + + --m_nRowCount; + OSL_ENSURE(((m_nPosition - m_nStartPos) - 1) < static_cast<sal_Int32>(m_pMatrix->size()),"Position is behind end()!"); + ORowSetMatrix::iterator aPos = calcPosition(); + (*aPos) = nullptr; + + ORowSetMatrix::const_iterator aEnd = m_pMatrix->end(); + for(++aPos;aPos != aEnd && aPos->is();++aPos) + { + *(aPos-1) = *aPos; + (*aPos) = nullptr; + } + m_aMatrixIter = m_pMatrix->end(); + + --m_nPosition; + return true; +} + +void ORowSetCache::cancelRowUpdates( ) +{ + m_bNew = m_bModified = false; + if(!m_nPosition) + { + OSL_FAIL("cancelRowUpdates:Invalid positions pos == 0"); + ::dbtools::throwFunctionSequenceException(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::moveToInsertRow( ) +{ + m_bNew = true; + m_bAfterLast = false; + + m_aInsertRow = m_pInsertMatrix->begin(); + if(!m_aInsertRow->is()) + *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount()); + + // we don't unbound the bookmark column + ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1; + ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end(); + for(sal_Int32 i = 1;aIter != aEnd;++aIter,++i) + { + aIter->setBound(false); + aIter->setModified(false); + aIter->setNull(); + aIter->setTypeKind(m_xMetaData->getColumnType(i)); + } +} + +ORowSetCacheIterator ORowSetCache::createIterator(ORowSetBase* _pRowSet) +{ + ORowSetCacheIterator_Helper aHelper; + aHelper.aIterator = m_pMatrix->end(); + aHelper.pRowSet = _pRowSet; + return ORowSetCacheIterator(m_aCacheIterators.insert(m_aCacheIterators.begin(),ORowSetCacheMap::value_type(m_aCacheIterators.size()+1,aHelper)),this,_pRowSet); +} + +void ORowSetCache::deleteIterator(const ORowSetBase* _pRowSet) +{ + ORowSetCacheMap::const_iterator aCacheIter = m_aCacheIterators.begin(); + for(;aCacheIter != m_aCacheIterators.end();) + { + if ( aCacheIter->second.pRowSet == _pRowSet ) + { + aCacheIter = m_aCacheIterators.erase(aCacheIter); + } + else + ++aCacheIter; + } +} + +void ORowSetCache::rotateCacheIterator(ORowSetMatrix::difference_type _nDist) +{ + if (m_bModified) + return; + + if(!_nDist) + 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()) + { + ptrdiff_t nDist = rCacheIter.second.aIterator - m_pMatrix->begin(); + if(nDist < _nDist) + { + rCacheIter.second.aIterator = m_pMatrix->end(); + } + else + { + OSL_ENSURE((rCacheIter.second.aIterator - m_pMatrix->begin()) >= _nDist,"Invalid Dist value!"); + rCacheIter.second.aIterator -= _nDist; + OSL_ENSURE(rCacheIter.second.aIterator >= m_pMatrix->begin() + && rCacheIter.second.aIterator < m_pMatrix->end(),"Iterator out of area!"); + } + } + } +} + +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(); + } + } +} + +void ORowSetCache::setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow) +{ + m_aInsertRow = m_pInsertMatrix->begin(); + if(!m_aInsertRow->is()) + *m_aInsertRow = new ORowSetValueVector(m_xMetaData->getColumnCount()); + + (*(*m_aInsertRow)) = *(*_rOriginalRow); + // we don't unbound the bookmark column + for(auto& rItem : **m_aInsertRow) + rItem.setModified(false); +} + +void ORowSetCache::checkPositionFlags() +{ + if(m_bRowCountFinal) + { + m_bAfterLast = m_nPosition > m_nRowCount; + if(m_bAfterLast) + m_nPosition = 0;//m_nRowCount; + } +} + +void ORowSetCache::checkUpdateConditions(sal_Int32 columnIndex) +{ + if(m_bAfterLast || columnIndex >= static_cast<sal_Int32>((*m_aInsertRow)->size())) + throwFunctionSequenceException(m_xSet.get()); +} + +bool ORowSetCache::checkInnerJoin(const ::connectivity::OSQLParseNode *pNode,const Reference< XConnection>& _xConnection,const OUString& _sUpdateTableName) +{ + bool bOk = false; + if (pNode->count() == 3 && // expression in parentheses + SQL_ISPUNCTUATION(pNode->getChild(0),"(") && + SQL_ISPUNCTUATION(pNode->getChild(2),")")) + { + bOk = checkInnerJoin(pNode->getChild(1),_xConnection,_sUpdateTableName); + } + else if ((SQL_ISRULE(pNode,search_condition) || SQL_ISRULE(pNode,boolean_term)) && // AND/OR link + pNode->count() == 3) + { + // only allow an AND link + if ( SQL_ISTOKEN(pNode->getChild(1),AND) ) + bOk = checkInnerJoin(pNode->getChild(0),_xConnection,_sUpdateTableName) + && checkInnerJoin(pNode->getChild(2),_xConnection,_sUpdateTableName); + } + else if (SQL_ISRULE(pNode,comparison_predicate)) + { + // only the comparison of columns is allowed + OSL_ENSURE(pNode->count() == 3,"checkInnerJoin: Error in Parse Tree"); + if (!(SQL_ISRULE(pNode->getChild(0),column_ref) && + SQL_ISRULE(pNode->getChild(2),column_ref) && + pNode->getChild(1)->getNodeType() == SQLNodeType::Equal)) + { + bOk = false; + } + else + { + OUString sColumnName,sTableRange; + OSQLParseTreeIterator::getColumnRange( pNode->getChild(0), _xConnection, sColumnName, sTableRange ); + bOk = sTableRange == _sUpdateTableName; + if ( !bOk ) + { + OSQLParseTreeIterator::getColumnRange( pNode->getChild(2), _xConnection, sColumnName, sTableRange ); + bOk = sTableRange == _sUpdateTableName; + } + } + } + return bOk; +} + +bool ORowSetCache::checkJoin(const Reference< XConnection>& _xConnection, + const Reference< XSingleSelectQueryAnalyzer >& _xAnalyzer, + const OUString& _sUpdateTableName ) +{ + bool bOk = false; + OUString sSql = _xAnalyzer->getQuery(); + OUString sErrorMsg; + ::connectivity::OSQLParser aSqlParser( m_aContext ); + std::unique_ptr< ::connectivity::OSQLParseNode> pSqlParseNode( aSqlParser.parseTree(sErrorMsg,sSql)); + if ( pSqlParseNode && SQL_ISRULE(pSqlParseNode, select_statement) ) + { + OSQLParseNode* pTableRefCommalist = pSqlParseNode->getByRule(::connectivity::OSQLParseNode::table_ref_commalist); + OSL_ENSURE(pTableRefCommalist,"NO tables why!?"); + if(pTableRefCommalist && pTableRefCommalist->count() == 1) + { + // we found only one element so it must some kind of join here + OSQLParseNode* pJoin = pTableRefCommalist->getByRule(::connectivity::OSQLParseNode::qualified_join); + if(pJoin) + { // we are only interested in qualified joins like RIGHT or LEFT + OSQLParseNode* pJoinType = pJoin->getChild(1); + OSQLParseNode* pOuterType = nullptr; + if(SQL_ISRULE(pJoinType,join_type) && pJoinType->count() == 2) + pOuterType = pJoinType->getChild(0); + else if(SQL_ISRULE(pJoinType,outer_join_type)) + pOuterType = pJoinType; + + bool bCheck = false; + bool bLeftSide = false; + if(pOuterType) + { // found outer join + bLeftSide = SQL_ISTOKEN(pOuterType->getChild(0),LEFT); + bCheck = bLeftSide || SQL_ISTOKEN(pOuterType->getChild(0),RIGHT); + } + + 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!"); + + OUString sTableRange = OSQLParseNode::getTableRange(pTableRef); + if(sTableRange.isEmpty()) + pTableRef->getChild(0)->parseNodeToStr( sTableRange, _xConnection, nullptr, false, false ); + bOk = sTableRange == _sUpdateTableName; + } + } + } + else + { + OSQLParseNode* pWhereOpt = pSqlParseNode->getChild(3)->getChild(1); + if ( pWhereOpt && !pWhereOpt->isLeaf() ) + bOk = checkInnerJoin(pWhereOpt->getChild(1),_xConnection,_sUpdateTableName); + } + } + return bOk; +} + +void ORowSetCache::clearInsertRow() +{ + // we don't unbound the bookmark column + if ( m_aInsertRow != m_pInsertMatrix->end() && m_aInsertRow->is() ) + { + ORowSetValueVector::Vector::iterator aIter = (*m_aInsertRow)->begin()+1; + ORowSetValueVector::Vector::const_iterator aEnd = (*m_aInsertRow)->end(); + for(;aIter != aEnd;++aIter) + { + aIter->setBound(false); + aIter->setModified(false); + aIter->setNull(); + } + } +} + +ORowSetMatrix::iterator ORowSetCache::calcPosition() const +{ + sal_Int32 nValue = (m_nPosition - m_nStartPos) - 1; + OSL_ENSURE((nValue >= static_cast<ORowSetMatrix::difference_type>(0)) && (o3tl::make_unsigned(nValue) < m_pMatrix->size()),"Position is invalid!"); + return ( nValue < 0 || o3tl::make_unsigned(nValue) >= m_pMatrix->size() ) ? m_pMatrix->end() : (m_pMatrix->begin() + nValue); +} + +TORowSetOldRowHelperRef ORowSetCache::registerOldRow() +{ + TORowSetOldRowHelperRef pRef = new ORowSetOldRowHelper(ORowSetRow()); + m_aOldRows.push_back(pRef); + return pRef; +} + +void ORowSetCache::deregisterOldRow(const TORowSetOldRowHelperRef& _rRow) +{ + TOldRowSetRows::iterator aOldRowIter = std::find_if(m_aOldRows.begin(), m_aOldRows.end(), + [&_rRow](const TORowSetOldRowHelperRef& rxOldRow) { return rxOldRow.get() == _rRow.get(); }); + if (aOldRowIter != m_aOldRows.end()) + m_aOldRows.erase(aOldRowIter); +} + +bool ORowSetCache::reFillMatrix(sal_Int32 _nNewStartPos, sal_Int32 _nNewEndPos) +{ + for (const auto& rxOldRow : m_aOldRows) + { + if ( rxOldRow.is() && rxOldRow->getRow().is() ) + rxOldRow->setRow(new ORowSetValueVector( *(rxOldRow->getRow()) ) ); + } + sal_Int32 nNewSt = _nNewStartPos; + bool bRet = fillMatrix(nNewSt,_nNewEndPos); + m_nStartPos = nNewSt; + m_nEndPos = _nNewEndPos; + rotateAllCacheIterators(); // invalidate every iterator + return bRet; +} + +bool ORowSetCache::fill(ORowSetMatrix::iterator& _aIter, const ORowSetMatrix::const_iterator& _aEnd, sal_Int32& _nPos, bool _bCheck) +{ + const sal_Int32 nColumnCount = m_xMetaData->getColumnCount(); + for (; _bCheck && _aIter != _aEnd; ++_aIter, ++_nPos) + { + if ( !_aIter->is() ) + *_aIter = new ORowSetValueVector(nColumnCount); + else + { + for (const auto& rxOldRow : m_aOldRows) + { + if ( rxOldRow->getRow() == *_aIter ) + *_aIter = new ORowSetValueVector(nColumnCount); + } + } + m_xCacheSet->fillValueRow(*_aIter, _nPos); + _bCheck = m_xCacheSet->next(); + } + return _bCheck; +} + +bool ORowSetCache::isResultSetChanged() const +{ + return m_xCacheSet->isResultSetChanged(); +} + +void ORowSetCache::reset(const Reference< XResultSet>& _xDriverSet) +{ + m_xSet = _xDriverSet; + m_xMetaData.set(Reference< XResultSetMetaDataSupplier >(_xDriverSet,UNO_QUERY_THROW)->getMetaData()); + m_xCacheSet->reset(_xDriverSet); + + m_bRowCountFinal = false; + m_nRowCount = 0; + reFillMatrix(m_nStartPos,m_nEndPos); +} + +void ORowSetCache::impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32> const & o_ChangedColumns) +{ + if ( o_ChangedColumns.size() > 1 ) + { + for (auto const& elem : *m_pMatrix) + { + if ( elem.is() && m_xCacheSet->updateColumnValues(*elem,io_aRow,o_ChangedColumns)) + { + return; + } + } + m_xCacheSet->fillMissingValues(io_aRow); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetCache.hxx b/dbaccess/source/core/api/RowSetCache.hxx new file mode 100644 index 000000000..01ed6394d --- /dev/null +++ b/dbaccess/source/core/api/RowSetCache.hxx @@ -0,0 +1,190 @@ +/* -*- 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 . + */ +#pragma once + +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryAnalyzer.hpp> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include "RowSetRow.hxx" +#include "RowSetCacheIterator.hxx" + +namespace connectivity +{ + class OSQLParseNode; +} +namespace dbaccess +{ + class OCacheSet; + + class ORowSetCache + { + friend class ORowSetBase; + friend class ORowSet; + friend class ORowSetClone; + friend class ORowSetCacheIterator; + + typedef std::vector< TORowSetOldRowHelperRef > TOldRowSetRows; + + std::map<sal_Int32,sal_Int32> m_aKeyColumns; + //the set can be static, bookmarkable or keyset + css::uno::WeakReference< css::sdbc::XResultSet> m_xSet; + css::uno::Reference< css::sdbc::XResultSetMetaData > m_xMetaData; // must be before m_aInsertRow + css::uno::Reference< css::uno::XComponentContext> m_aContext; + + rtl::Reference<OCacheSet> m_xCacheSet; // is a bookmarkable, keyset or static resultset + + std::unique_ptr<ORowSetMatrix> m_pMatrix; // represent the table struct + ORowSetMatrix::iterator m_aMatrixIter; // represent a row of the table + ORowSetMatrix::iterator m_aMatrixEnd; // present the row behind the last row of the table + ORowSetCacheMap m_aCacheIterators; + TOldRowSetRows m_aOldRows; + + std::unique_ptr<ORowSetMatrix> m_pInsertMatrix; // represent the rows which should be inserted normally this is only one + ORowSetMatrix::iterator m_aInsertRow; // represent an insert row + + connectivity::OSQLTable m_aUpdateTable; // used for updates/deletes and inserts + + sal_Int32 m_nFetchSize; + sal_Int32 m_nRowCount; + sal_Int32 m_nPrivileges; + sal_Int32 m_nPosition; // 0 means before first (i.e. 1-based) + + sal_Int32 m_nStartPos; // start pos of the window zero based (inclusive) + sal_Int32 m_nEndPos; // end pos of the window zero based (exclusive) + + bool m_bRowCountFinal ; + bool m_bBeforeFirst ; + bool m_bAfterLast ; + bool& m_bModified ; // points to the rowset member m_bModified + bool& m_bNew ; // points to the rowset member m_bNew + + bool fill(ORowSetMatrix::iterator& _aIter, const ORowSetMatrix::const_iterator& _aEnd, sal_Int32& _nPos, bool _bCheck); + bool reFillMatrix(sal_Int32 _nNewStartPos,sal_Int32 nNewEndPos); + bool fillMatrix(sal_Int32 &_nNewStartPos,sal_Int32 &_nNewEndPos); + void moveWindow(); + + void rotateCacheIterator(ORowSetMatrix::difference_type _nDist); + void rotateAllCacheIterators(); + void updateValue(sal_Int32 columnIndex + ,const connectivity::ORowSetValue& x + ,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ); + + void impl_updateRowFromCache_throw(ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32> const & o_ChangedColumns + ); + // checks and set the flags isAfterLast isLast and position when afterlast is true + void checkPositionFlags(); + void checkUpdateConditions(sal_Int32 columnIndex); + bool checkJoin( const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const css::uno::Reference< css::sdb::XSingleSelectQueryAnalyzer >& _xComposer, + const OUString& _sUpdateTableName); + bool checkInnerJoin(const ::connectivity::OSQLParseNode *pNode + ,const css::uno::Reference< css::sdbc::XConnection>& _xConnection + ,const OUString& _sUpdateTableName); + + // clears the insert row + void clearInsertRow(); + ORowSetMatrix::iterator calcPosition() const; + + protected: + const ORowSetMatrix::iterator& getEnd() const { return m_aMatrixEnd;} + // is called when after a moveToInsertRow a movement (next, etc) was called + void cancelRowModification(); + public: + ORowSetCache(const css::uno::Reference< css::sdbc::XResultSet >& _xRs, + const css::uno::Reference< css::sdb::XSingleSelectQueryAnalyzer >& _xAnalyzer, + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const OUString& _rUpdateTableName, + bool& _bModified, + bool& _bNew, + const ORowSetValueVector& _aParameterValueForCache, + const OUString& i_sRowSetFilter, + sal_Int32 i_nMaxRows); + ~ORowSetCache(); + + + // called from the rowset when an updateXXX was called for the first time + void setUpdateIterator(const ORowSetMatrix::iterator& _rOriginalRow); + ORowSetCacheIterator createIterator(ORowSetBase* _pRowSet); + void deleteIterator(const ORowSetBase* _pRowSet); + // sets the size of the matrix + void setFetchSize(sal_Int32 _nSize); + + TORowSetOldRowHelperRef registerOldRow(); + void deregisterOldRow(const TORowSetOldRowHelperRef& _rRow); + + // css::sdbc::XResultSetMetaDataSupplier + const css::uno::Reference< css::sdbc::XResultSetMetaData >& getMetaData( ) const { return m_xMetaData;} + + // css::sdbcx::XRowLocate + css::uno::Any getBookmark( ); + bool moveToBookmark( const css::uno::Any& bookmark ); + bool moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ); + sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ); + bool hasOrderedBookmarks( ); + sal_Int32 hashBookmark( const css::uno::Any& bookmark ); + + // css::sdbc::XRowUpdate + void updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length,ORowSetValueVector::Vector& io_aRow,std::vector<sal_Int32>& o_ChangedColumns + ); + void updateObject( sal_Int32 columnIndex, const css::uno::Any& x,ORowSetValueVector::Vector& io_aRow ,std::vector<sal_Int32>& o_ChangedColumns); + void updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, ORowSetValueVector::Vector& io_aRow ,std::vector<sal_Int32>& o_ChangedColumns); + void updateNull(sal_Int32 columnIndex + ,ORowSetValueVector::Vector& io_aRow + ,std::vector<sal_Int32>& o_ChangedColumns + ); + + // css::sdbc::XResultSet + bool next( ); + bool isBeforeFirst( ) const { return m_bBeforeFirst;} + bool isAfterLast( ) const { return m_bAfterLast;} + bool isFirst( ) const; + bool isLast( ) const; + void beforeFirst( ); + void afterLast( ); + bool first( ); + bool last( ); + sal_Int32 getRow( ) const; + bool absolute( sal_Int32 row ); + bool relative( sal_Int32 rows ); + bool previous( ); + void refreshRow( ); + bool rowUpdated( ); + bool rowInserted( ); + + // css::sdbc::XResultSetUpdate + bool insertRow(std::vector< css::uno::Any >& o_aBookmarks); + void resetInsertRow(bool _bClearInsertRow); + + void updateRow( ORowSetMatrix::iterator const & _rUpdateRow, std::vector< css::uno::Any >& o_aBookmarks ); + bool deleteRow(); + void cancelRowUpdates( ); + void moveToInsertRow( ); + + const std::map<sal_Int32,sal_Int32>& getKeyColumns() const { return m_aKeyColumns; } + bool isResultSetChanged() const; + void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetCacheIterator.cxx b/dbaccess/source/core/api/RowSetCacheIterator.cxx new file mode 100644 index 000000000..7df7bae6b --- /dev/null +++ b/dbaccess/source/core/api/RowSetCacheIterator.cxx @@ -0,0 +1,91 @@ +/* -*- 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 . + */ + +#include "RowSetCacheIterator.hxx" +#include "RowSetCache.hxx" +#include "RowSetBase.hxx" + +using namespace dbaccess; + +ORowSetCacheIterator::ORowSetCacheIterator(const ORowSetCacheIterator& _rRH) +: m_aIter(_rRH.m_aIter) +, m_pCache(_rRH.m_pCache) +,m_pRowSet(_rRH.m_pRowSet) +{ +} + +ORowSetCacheIterator::operator ORowSetMatrix::iterator const &() +{ + return m_aIter->second.aIterator; +} + +ORowSetCacheIterator& ORowSetCacheIterator::operator =(const ORowSetCacheIterator& _rRH) +{ + if(this == &_rRH) + return *this; + + m_pCache = _rRH.m_pCache; + m_aIter = _rRH.m_aIter; + m_pRowSet = _rRH.m_pRowSet; + + return *this; +} + +ORowSetCacheIterator& ORowSetCacheIterator::operator =(const ORowSetMatrix::iterator& _rIter) +{ + m_aIter->second.aIterator = _rIter; + return *this; +} + +ORowSetRow& ORowSetCacheIterator::operator *() +{ + return *m_aIter->second.aIterator; +} + +ORowSetMatrix::iterator& ORowSetCacheIterator::operator ->() +{ + return m_aIter->second.aIterator; +} + +bool ORowSetCacheIterator::operator <(const ORowSetMatrix::iterator& _rRH) const +{ + return m_aIter->second.aIterator < _rRH; +} + +bool ORowSetCacheIterator::operator !=(const ORowSetMatrix::iterator& _rRH) const +{ + return m_aIter->second.aIterator != _rRH; +} + +bool ORowSetCacheIterator::isNull() const +{ + bool bRet = !m_pCache || !m_pRowSet || m_aIter == m_pCache->m_aCacheIterators.end(); + if ( !bRet ) + { + bRet = ( m_pRowSet->isInsertRow() + ? + m_aIter->second.aIterator == m_pCache->m_pInsertMatrix->end() + : + m_aIter->second.aIterator == m_pCache->m_pMatrix->end() + ); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetCacheIterator.hxx b/dbaccess/source/core/api/RowSetCacheIterator.hxx new file mode 100644 index 000000000..9c7a9afd4 --- /dev/null +++ b/dbaccess/source/core/api/RowSetCacheIterator.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ +#pragma once + +#include <sal/config.h> + +#include <map> + +#include "RowSetRow.hxx" + +namespace dbaccess +{ + class ORowSetBase; + struct ORowSetCacheIterator_Helper + { + ORowSetMatrix::iterator aIterator; + ORowSetBase* pRowSet; + }; + + typedef std::map<sal_Int32, ORowSetCacheIterator_Helper> ORowSetCacheMap; + + class ORowSetCache; + class ORowSetCacheIterator final + { + friend class ORowSetCache; + ORowSetCacheMap::iterator m_aIter; + ORowSetCache* m_pCache; + ORowSetBase* m_pRowSet; + + ORowSetCacheIterator(const ORowSetCacheMap::iterator& _rIter,ORowSetCache* _pCache,ORowSetBase* _pRowSet) + : m_aIter(_rIter) + ,m_pCache(_pCache) + ,m_pRowSet(_pRowSet) + { + } + public: + ORowSetCacheIterator() :m_aIter(),m_pCache(nullptr),m_pRowSet(nullptr){} + ORowSetCacheIterator(const ORowSetCacheIterator& _rRH); + ORowSetCacheIterator& operator =(const ORowSetCacheIterator&); + + bool isNull() const; + ORowSetCacheIterator& operator =(const ORowSetMatrix::iterator&); + operator ORowSetMatrix::iterator const &(); + + ORowSetRow& operator *(); + + ORowSetMatrix::iterator& operator ->(); + + bool operator <(const ORowSetMatrix::iterator& _rRH) const; + bool operator !=(const ORowSetMatrix::iterator& _rRH) const; + + const ORowSetCacheMap::iterator& getIter() const { return m_aIter; } + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/RowSetRow.hxx b/dbaccess/source/core/api/RowSetRow.hxx new file mode 100644 index 000000000..7f24267b7 --- /dev/null +++ b/dbaccess/source/core/api/RowSetRow.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ +#pragma once + +#include <rtl/ref.hxx> +#include <connectivity/CommonTools.hxx> +#include <connectivity/FValue.hxx> +#include <salhelper/simplereferenceobject.hxx> + +namespace dbaccess +{ + typedef connectivity::ORowVector< connectivity::ORowSetValue > ORowSetValueVector; + typedef ::rtl::Reference< ORowSetValueVector > ORowSetRow; + typedef std::vector< ORowSetRow > ORowSetMatrix; + + class ORowSetOldRowHelper : public salhelper::SimpleReferenceObject + { + ORowSetRow m_aRow; + + ORowSetOldRowHelper& operator=(const ORowSetOldRowHelper& _rRH) = delete; + ORowSetOldRowHelper(const ORowSetOldRowHelper& _rRh) = delete; + public: + explicit ORowSetOldRowHelper(const ORowSetRow& _rRow) + : m_aRow(_rRow) + {} + + const ORowSetRow& getRow() const { return m_aRow; } + void clearRow() { m_aRow = nullptr; } + void setRow(const ORowSetRow& _rRow) { m_aRow = _rRow; } + }; + + typedef ::rtl::Reference< ORowSetOldRowHelper > TORowSetOldRowHelperRef; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/SingleSelectQueryComposer.cxx b/dbaccess/source/core/api/SingleSelectQueryComposer.cxx new file mode 100644 index 000000000..3c6906893 --- /dev/null +++ b/dbaccess/source/core/api/SingleSelectQueryComposer.cxx @@ -0,0 +1,1922 @@ +/* -*- 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 . + */ + +#include <string.h> +#include <sal/log.hxx> +#include <composertools.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <stringconstants.hxx> +#include "HelperCollections.hxx" +#include <SingleSelectQueryComposer.hxx> +#include <sqlbison.hxx> +#include <sdbcoretools.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/i18n/LocaleData.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/sdb/BooleanComparisonMode.hpp> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> + +#include <comphelper/types.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/PColumn.hxx> +#include <connectivity/predicateinput.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <unotools/sharedunocomponent.hxx> + +#include <memory> +#include <string_view> + +using namespace ::dbaccess; +using namespace ::dbtools; +using namespace ::comphelper; +using namespace ::connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::util; +using namespace ::cppu; +using namespace ::osl; +using namespace ::utl; + +namespace dbaccess { +namespace BooleanComparisonMode = ::com::sun::star::sdb::BooleanComparisonMode; +} + +constexpr OUStringLiteral STR_SELECT = u"SELECT "; +constexpr OUStringLiteral STR_FROM = u" FROM "; +constexpr OUStringLiteral STR_WHERE = u" WHERE "; +constexpr OUStringLiteral STR_GROUP_BY = u" GROUP BY "; +constexpr OUStringLiteral STR_HAVING = u" HAVING "; +constexpr OUStringLiteral STR_ORDER_BY = u" ORDER BY "; +constexpr OUStringLiteral STR_AND = u" AND "; +constexpr OUStringLiteral STR_OR = u" OR "; +constexpr OUStringLiteral STR_LIKE = u" LIKE "; +constexpr OUStringLiteral L_BRACKET = u"("; +constexpr OUStringLiteral R_BRACKET = u")"; +constexpr OUStringLiteral COMMA = u","; + +namespace +{ + /** parses the given statement, using the given parser, returns a parse node representing + the statement + + If the statement cannot be parsed, an error is thrown. + */ + std::unique_ptr<OSQLParseNode> parseStatement_throwError( OSQLParser& _rParser, const OUString& _rStatement, const Reference< XInterface >& _rxContext ) + { + OUString aErrorMsg; + std::unique_ptr<OSQLParseNode> pNewSqlParseNode = _rParser.parseTree( aErrorMsg, _rStatement ); + if ( !pNewSqlParseNode ) + { + OUString sSQLStateGeneralError( getStandardSQLState( StandardSQLState::GENERAL_ERROR ) ); + SQLException aError2( aErrorMsg, _rxContext, sSQLStateGeneralError, 1000, Any() ); + SQLException aError1( _rStatement, _rxContext, sSQLStateGeneralError, 1000, Any( aError2 ) ); + throw SQLException(_rParser.getContext().getErrorMessage(OParseContext::ErrorCode::General),_rxContext,sSQLStateGeneralError,1000,Any(aError1)); + } + return pNewSqlParseNode; + } + + /** checks whether the given parse node describes a valid single select statement, throws + an error if not + */ + void checkForSingleSelect_throwError( const OSQLParseNode* pStatementNode, OSQLParseTreeIterator& _rIterator, + const Reference< XInterface >& _rxContext, const OUString& _rOriginatingCommand ) + { + const OSQLParseNode* pOldNode = _rIterator.getParseTree(); + + // determine the statement type + _rIterator.setParseTree( pStatementNode ); + _rIterator.traverseAll(); + bool bIsSingleSelect = ( _rIterator.getStatementType() == OSQLStatementType::Select ); + + // throw the error, if necessary + if ( !bIsSingleSelect || SQL_ISRULE( pStatementNode, union_statement ) ) // #i4229# OJ + { + // restore the old node before throwing the exception + _rIterator.setParseTree( pOldNode ); + // and now really ... + SQLException aError1( _rOriginatingCommand, _rxContext, getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, Any() ); + throw SQLException( DBA_RES( RID_STR_ONLY_QUERY ), _rxContext, + getStandardSQLState( StandardSQLState::GENERAL_ERROR ), 1000, Any( aError1 ) ); + } + + delete pOldNode; + } + + /** combines parseStatement_throwError and checkForSingleSelect_throwError + */ + void parseAndCheck_throwError( OSQLParser& _rParser, const OUString& _rStatement, + OSQLParseTreeIterator& _rIterator, const Reference< XInterface >& _rxContext ) + { + std::unique_ptr<OSQLParseNode> pNode = parseStatement_throwError( _rParser, _rStatement, _rxContext ); + checkForSingleSelect_throwError( pNode.release(), _rIterator, _rxContext, _rStatement ); + } + + /** transforms a parse node describing a complete statement into a pure select + statement, without any filter/order/groupby/having clauses + */ + OUString getPureSelectStatement( const OSQLParseNode* _pRootNode, const Reference< XConnection >& _rxConnection ) + { + OUString sSQL = STR_SELECT; + _pRootNode->getChild(1)->parseNodeToStr( sSQL, _rxConnection ); + _pRootNode->getChild(2)->parseNodeToStr( sSQL, _rxConnection ); + sSQL += STR_FROM; + _pRootNode->getChild(3)->getChild(0)->getChild(1)->parseNodeToStr( sSQL, _rxConnection ); + return sSQL; + } + + /** resets an SQL iterator, including deletion of the parse tree, and dispose + */ + void resetIterator( OSQLParseTreeIterator& _rIterator ) + { + const OSQLParseNode* pSqlParseNode = _rIterator.getParseTree(); + _rIterator.setParseTree(nullptr); + delete pSqlParseNode; + _rIterator.dispose(); + } + void lcl_addFilterCriteria_throw(sal_Int32 i_nFilterOperator,std::u16string_view i_sValue,OUStringBuffer& o_sRet) + { + switch( i_nFilterOperator ) + { + case SQLFilterOperator::EQUAL: + o_sRet.append(OUString::Concat(" = ") + i_sValue); + break; + case SQLFilterOperator::NOT_EQUAL: + o_sRet.append(OUString::Concat(" <> ") + i_sValue); + break; + case SQLFilterOperator::LESS: + o_sRet.append(OUString::Concat(" < ") + i_sValue); + break; + case SQLFilterOperator::GREATER: + o_sRet.append(OUString::Concat(" > ") + i_sValue); + break; + case SQLFilterOperator::LESS_EQUAL: + o_sRet.append(OUString::Concat(" <= ") + i_sValue); + break; + case SQLFilterOperator::GREATER_EQUAL: + o_sRet.append(OUString::Concat(" >= ") + i_sValue); + break; + case SQLFilterOperator::LIKE: + o_sRet.append(OUString::Concat(" LIKE ") + i_sValue); + break; + case SQLFilterOperator::NOT_LIKE: + o_sRet.append(OUString::Concat(" NOT LIKE ") + i_sValue); + break; + case SQLFilterOperator::SQLNULL: + o_sRet.append(" IS NULL"); + break; + case SQLFilterOperator::NOT_SQLNULL: + o_sRet.append(" IS NOT NULL"); + break; + default: + throw SQLException(); + } + } + +} + + +OSingleSelectQueryComposer::OSingleSelectQueryComposer(const Reference< XNameAccess>& _rxTables, + const Reference< XConnection>& _xConnection, + const Reference<XComponentContext>& _rContext ) + :OSubComponent(m_aMutex,_xConnection) + ,OPropertyContainer(m_aBHelper) + ,m_aSqlParser( _rContext, &m_aParseContext ) + ,m_aSqlIterator( _xConnection, _rxTables, m_aSqlParser ) + ,m_aAdditiveIterator( _xConnection, _rxTables, m_aSqlParser ) + ,m_aElementaryParts( size_t(SQLPartCount) ) + ,m_xConnection(_xConnection) + ,m_xMetaData(_xConnection->getMetaData()) + ,m_xConnectionTables( _rxTables ) + ,m_aContext( _rContext ) + ,m_nBoolCompareMode( BooleanComparisonMode::EQUAL_INTEGER ) + ,m_nCommandType(CommandType::COMMAND) +{ + if ( !m_aContext.is() || !m_xConnection.is() || !m_xConnectionTables.is() ) + throw IllegalArgumentException(); + + registerProperty(PROPERTY_ORIGINAL,PROPERTY_ID_ORIGINAL,PropertyAttribute::BOUND|PropertyAttribute::READONLY,&m_sOriginal,cppu::UnoType<decltype(m_sOriginal)>::get()); + + m_aCurrentColumns.resize(4); + + m_aLocale = m_aParseContext.getPreferredLocale(); + m_xNumberFormatsSupplier = dbtools::getNumberFormats( m_xConnection, true, m_aContext ); + Reference< XLocaleData4 > xLocaleData( LocaleData::create(m_aContext) ); + LocaleDataItem aData = xLocaleData->getLocaleItem(m_aLocale); + m_sDecimalSep = aData.decimalSeparator; + OSL_ENSURE(m_sDecimalSep.getLength() == 1,"OSingleSelectQueryComposer::OSingleSelectQueryComposer decimal separator is not 1 length"); + try + { + Any aValue; + Reference<XInterface> xDs = dbaccess::getDataSource(_xConnection); + if ( dbtools::getDataSourceSetting(xDs,static_cast <OUString> (PROPERTY_BOOLEANCOMPARISONMODE),aValue) ) + { + OSL_VERIFY( aValue >>= m_nBoolCompareMode ); + } + Reference< XQueriesSupplier > xQueriesAccess(m_xConnection, UNO_QUERY); + if (xQueriesAccess.is()) + m_xConnectionQueries = xQueriesAccess->getQueries(); + } + catch(Exception&) + { + } +} + +OSingleSelectQueryComposer::~OSingleSelectQueryComposer() +{ +} + +// OComponentHelper +void SAL_CALL OSingleSelectQueryComposer::disposing() +{ + OSubComponent::disposing(); + + MutexGuard aGuard(m_aMutex); + + resetIterator( m_aSqlIterator ); + resetIterator( m_aAdditiveIterator ); + + m_xConnectionTables = nullptr; + m_xConnection = nullptr; + + clearCurrentCollections(); +} + +IMPLEMENT_FORWARD_XINTERFACE3(OSingleSelectQueryComposer,OSubComponent,OSingleSelectQueryComposer_BASE,OPropertyContainer) +OUString SAL_CALL OSingleSelectQueryComposer::getImplementationName() + { + return "org.openoffice.comp.dba.OSingleSelectQueryComposer"; + } +sal_Bool SAL_CALL OSingleSelectQueryComposer::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OSingleSelectQueryComposer::getSupportedServiceNames() +{ + return { SERVICE_NAME_SINGLESELECTQUERYCOMPOSER }; +} + +css::uno::Sequence<sal_Int8> OSingleSelectQueryComposer::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > OSingleSelectQueryComposer::getTypes() +{ + return ::comphelper::concatSequences( + OSubComponent::getTypes( ), + OSingleSelectQueryComposer_BASE::getTypes( ), + OPropertyContainer::getTypes( ) + ); +} + +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OSingleSelectQueryComposer::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& OSingleSelectQueryComposer::getInfoHelper() +{ + return *OSingleSelectQueryComposer::getArrayHelper(); +} +::cppu::IPropertyArrayHelper* OSingleSelectQueryComposer::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +// XSingleSelectQueryAnalyzer +OUString SAL_CALL OSingleSelectQueryComposer::getQuery( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + TGetParseNode F_tmp(&OSQLParseTreeIterator::getParseTree); + return getStatementPart(F_tmp,m_aSqlIterator); +} + +void SAL_CALL OSingleSelectQueryComposer::setQuery( const OUString& command ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_nCommandType = CommandType::COMMAND; + // first clear the tables and columns + clearCurrentCollections(); + // now set the new one + setQuery_Impl(command); + m_sOriginal = command; + + // reset the additive iterator to the same statement + parseAndCheck_throwError( m_aSqlParser, m_sOriginal, m_aAdditiveIterator, *this ); + + // we have no "elementary" parts anymore (means filter/groupby/having/order clauses) + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + m_aElementaryParts[ eLoopParts ].clear(); +} + +void SAL_CALL OSingleSelectQueryComposer::setCommand( const OUString& Command,sal_Int32 _nCommandType ) +{ + OUStringBuffer sSQL; + switch(_nCommandType) + { + case CommandType::COMMAND: + setElementaryQuery(Command); + return; + case CommandType::TABLE: + if ( m_xConnectionTables->hasByName(Command) ) + { + sSQL.append("SELECT * FROM "); + Reference< XPropertySet > xTable; + try + { + m_xConnectionTables->getByName( Command ) >>= xTable; + } + catch(const WrappedTargetException& e) + { + SQLException e2; + if ( e.TargetException >>= e2 ) + throw e2; + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + sSQL.append(dbtools::composeTableNameForSelect(m_xConnection,xTable)); + } + else + { + OUString sMessage( DBA_RES( RID_STR_TABLE_DOES_NOT_EXIST ) ); + throwGenericSQLException(sMessage.replaceAll( "$table$", Command ),*this); + } + break; + case CommandType::QUERY: + if ( m_xConnectionQueries->hasByName(Command) ) + { + + Reference<XPropertySet> xQuery(m_xConnectionQueries->getByName(Command),UNO_QUERY); + OUString sCommand; + xQuery->getPropertyValue(PROPERTY_COMMAND) >>= sCommand; + sSQL.append(sCommand); + } + else + { + OUString sMessage( DBA_RES( RID_STR_QUERY_DOES_NOT_EXIST ) ); + throwGenericSQLException(sMessage.replaceAll( "$table$", Command ),*this); + } + + break; + default: + break; + } + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_nCommandType = _nCommandType; + m_sCommand = Command; + // first clear the tables and columns + clearCurrentCollections(); + // now set the new one + OUString sCommand = sSQL.makeStringAndClear(); + setElementaryQuery(sCommand); + m_sOriginal = sCommand; +} + +void OSingleSelectQueryComposer::setQuery_Impl( const OUString& command ) +{ + // parse this + parseAndCheck_throwError( m_aSqlParser, command, m_aSqlIterator, *this ); + + // strip it from all clauses, to have the pure SELECT statement + m_aPureSelectSQL = getPureSelectStatement( m_aSqlIterator.getParseTree(), m_xConnection ); + + // update tables + getTables(); +} + +Sequence< Sequence< PropertyValue > > SAL_CALL OSingleSelectQueryComposer::getStructuredHavingClause( ) +{ + TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleHavingTree); + return getStructuredCondition(F_tmp); +} + +Sequence< Sequence< PropertyValue > > SAL_CALL OSingleSelectQueryComposer::getStructuredFilter( ) +{ + TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleWhereTree); + return getStructuredCondition(F_tmp); +} + +void SAL_CALL OSingleSelectQueryComposer::appendHavingClauseByColumn( const Reference< XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator ) +{ + auto F_tmp = std::mem_fn(&OSingleSelectQueryComposer::implSetHavingClause); + setConditionByColumn(column,andCriteria,F_tmp,filterOperator); +} + +void SAL_CALL OSingleSelectQueryComposer::appendFilterByColumn( const Reference< XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator ) +{ + auto F_tmp = std::mem_fn(&OSingleSelectQueryComposer::implSetFilter); + setConditionByColumn(column,andCriteria,F_tmp,filterOperator); +} + +OUString OSingleSelectQueryComposer::impl_getColumnRealName_throw(const Reference< XPropertySet >& column, bool bGroupBy) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + getColumns(); + if ( !column.is() + || !m_aCurrentColumns[SelectColumns] + || !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME) + ) + { + OUString sError(DBA_RES(RID_STR_COLUMN_UNKNOWN_PROP)); + SQLException aErr(sError.replaceAll("%value", PROPERTY_NAME),*this,SQLSTATE_GENERAL,1000,Any() ); + throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,Any(aErr) ); + } + + OUString aName, aNewName; + column->getPropertyValue(PROPERTY_NAME) >>= aName; + + if ( bGroupBy && + !m_xMetaData->supportsGroupByUnrelated() && + m_aCurrentColumns[SelectColumns] && + !m_aCurrentColumns[SelectColumns]->hasByName(aName) ) + { + OUString sError(DBA_RES(RID_STR_COLUMN_MUST_VISIBLE)); + throw SQLException(sError.replaceAll("%name", aName),*this,SQLSTATE_GENERAL,1000,Any() ); + } + + OUString aQuote = m_xMetaData->getIdentifierQuoteString(); + if ( m_aCurrentColumns[SelectColumns]->hasByName(aName) ) + { + Reference<XPropertySet> xColumn; + m_aCurrentColumns[SelectColumns]->getByName(aName) >>= xColumn; + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_REALNAME),"Property REALNAME not available!"); + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME),"Property TABLENAME not available!"); + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName("Function"),"Property FUNCTION not available!"); + + OUString sRealName, sTableName; + xColumn->getPropertyValue(PROPERTY_REALNAME) >>= sRealName; + xColumn->getPropertyValue(PROPERTY_TABLENAME) >>= sTableName; + bool bFunction = false; + xColumn->getPropertyValue("Function") >>= bFunction; + if ( sRealName == aName ) + { + if ( bFunction ) + aNewName = aName; + else + { + if(sTableName.indexOf('.') != -1) + { + OUString aCatlog,aSchema,aTable; + ::dbtools::qualifiedNameComponents(m_xMetaData,sTableName,aCatlog,aSchema,aTable,::dbtools::EComposeRule::InDataManipulation); + sTableName = ::dbtools::composeTableName( m_xMetaData, aCatlog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation ); + } + else if (!sTableName.isEmpty()) + sTableName = ::dbtools::quoteName(aQuote,sTableName); + + if(sTableName.isEmpty()) + aNewName = ::dbtools::quoteName(aQuote,sRealName); + else + aNewName = sTableName + "." + ::dbtools::quoteName(aQuote,sRealName); + } + } + else + aNewName = ::dbtools::quoteName(aQuote,aName); + } + else + aNewName = getTableAlias(column) + ::dbtools::quoteName(aQuote,aName); + return aNewName; +} + +OUString OSingleSelectQueryComposer::impl_getColumnNameOrderBy_throw(const Reference< XPropertySet >& column) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + getColumns(); + if ( !column.is() + || !m_aCurrentColumns[SelectColumns] + || !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME) + ) + { + OUString sError(DBA_RES(RID_STR_COLUMN_UNKNOWN_PROP)); + SQLException aErr(sError.replaceAll("%value", PROPERTY_NAME),*this,SQLSTATE_GENERAL,1000,Any() ); + throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,Any(aErr) ); + } + + OUString aName; + column->getPropertyValue(PROPERTY_NAME) >>= aName; + + const OUString aQuote = m_xMetaData->getIdentifierQuoteString(); + + if ( m_aCurrentColumns[SelectColumns] && + m_aCurrentColumns[SelectColumns]->hasByName(aName) ) + { + // It is a column from the SELECT list, use it as such. + return ::dbtools::quoteName(aQuote,aName); + } + + // Nope, it is an unrelated column. + // Is that supported? + if ( !m_xMetaData->supportsOrderByUnrelated() ) + { + OUString sError(DBA_RES(RID_STR_COLUMN_MUST_VISIBLE)); + throw SQLException(sError.replaceAll("%name", aName),*this,SQLSTATE_GENERAL,1000,Any() ); + } + + // We need to refer to it by its "real" name, that is by schemaName.tableName.columnNameInTable + return impl_getColumnRealName_throw(column, false); +} + +void SAL_CALL OSingleSelectQueryComposer::appendOrderByColumn( const Reference< XPropertySet >& column, sal_Bool ascending ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + OUString sColumnName( impl_getColumnNameOrderBy_throw(column) ); + OUString sOrder = getOrder(); + if ( !(sOrder.isEmpty() || sColumnName.isEmpty()) ) + sOrder += COMMA; + sOrder += sColumnName; + if ( !(ascending || sColumnName.isEmpty()) ) + sOrder += " DESC "; + + setOrder(sOrder); +} + +void SAL_CALL OSingleSelectQueryComposer::appendGroupByColumn( const Reference< XPropertySet >& column) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + OUString sColumnName( impl_getColumnRealName_throw(column, true) ); + OrderCreator aComposer; + aComposer.append( getGroup() ); + aComposer.append( sColumnName ); + setGroup( aComposer.getComposedAndClear() ); +} + +OUString OSingleSelectQueryComposer::composeStatementFromParts( const std::vector< OUString >& _rParts ) +{ + OSL_ENSURE( _rParts.size() == size_t(SQLPartCount), "OSingleSelectQueryComposer::composeStatementFromParts: invalid parts array!" ); + + OUStringBuffer aSql( m_aPureSelectSQL ); + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + if ( !_rParts[ eLoopParts ].isEmpty() ) + { + aSql.append( getKeyword( eLoopParts ) ); + aSql.append( _rParts[ eLoopParts ] ); + } + + return aSql.makeStringAndClear(); +} + +OUString SAL_CALL OSingleSelectQueryComposer::getElementaryQuery() +{ + return composeStatementFromParts( m_aElementaryParts ); +} + +void SAL_CALL OSingleSelectQueryComposer::setElementaryQuery( const OUString& _rElementary ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + // remember the 4 current "additive" clauses + std::vector< OUString > aAdditiveClauses( SQLPartCount ); + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + aAdditiveClauses[ eLoopParts ] = getSQLPart( eLoopParts, m_aAdditiveIterator, false ); + + // clear the tables and columns + clearCurrentCollections(); + // set and parse the new query + setQuery_Impl( _rElementary ); + + // get the 4 elementary parts of the statement + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + m_aElementaryParts[ eLoopParts ] = getSQLPart( eLoopParts, m_aSqlIterator, false ); + + // reset the AdditiveIterator: m_aPureSelectSQL may have changed + try + { + parseAndCheck_throwError( m_aSqlParser, composeStatementFromParts( aAdditiveClauses ), m_aAdditiveIterator, *this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setElementaryQuery: there should be no error anymore for the additive statement!" ); + // every part of the additive statement should have passed other tests already, and should not + // be able to cause any errors ... me thinks + } +} + +namespace +{ + OUString getComposedClause( const OUString& _rElementaryClause, const OUString& _rAdditionalClause, + TokenComposer& _rComposer, std::u16string_view _rKeyword ) + { + _rComposer.clear(); + _rComposer.append( _rElementaryClause ); + _rComposer.append( _rAdditionalClause ); + OUString sComposed = _rComposer.getComposedAndClear(); + if ( !sComposed.isEmpty() ) + sComposed = _rKeyword + sComposed; + return sComposed; + } +} + +void OSingleSelectQueryComposer::setSingleAdditiveClause( SQLPart _ePart, const OUString& _rClause ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + // if nothing is changed, do nothing + if ( getSQLPart( _ePart, m_aAdditiveIterator, false ) == _rClause ) + return; + + // collect the 4 single parts as they're currently set + std::vector< OUString > aClauses; + aClauses.reserve( size_t(SQLPartCount) ); + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + aClauses.push_back( getSQLPart( eLoopParts, m_aSqlIterator, true ) ); + + // overwrite the one part in question here + std::unique_ptr< TokenComposer > pComposer; + if ( ( _ePart == Where ) || ( _ePart == Having ) ) + pComposer.reset( new FilterCreator ); + else + pComposer.reset( new OrderCreator ); + aClauses[ _ePart ] = getComposedClause( m_aElementaryParts[ _ePart ], _rClause, + *pComposer, getKeyword( _ePart ) ); + + // construct the complete statement + OUStringBuffer aSql(m_aPureSelectSQL); + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + aSql.append(aClauses[ eLoopParts ]); + + // set the query + setQuery_Impl(aSql.makeStringAndClear()); + + // clear column collections which (might) have changed + clearColumns( ParameterColumns ); + if ( _ePart == Order ) + clearColumns( OrderColumns ); + else if ( _ePart == Group ) + clearColumns( GroupByColumns ); + + // also, since the "additive filter" change, we need to rebuild our "additive" statement + aSql = m_aPureSelectSQL; + // again, first get all the old additive parts + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + aClauses[ eLoopParts ] = getSQLPart( eLoopParts, m_aAdditiveIterator, true ); + // then overwrite the one in question + aClauses[ _ePart ] = getComposedClause( OUString(), _rClause, *pComposer, getKeyword( _ePart ) ); + // and parse it, so that m_aAdditiveIterator is up to date + for ( SQLPart eLoopParts = Where; eLoopParts != SQLPartCount; incSQLPart( eLoopParts ) ) + aSql.append(aClauses[ eLoopParts ]); + try + { + parseAndCheck_throwError( m_aSqlParser, aSql.makeStringAndClear(), m_aAdditiveIterator, *this ); + } + catch( const Exception& ) + { + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setSingleAdditiveClause: there should be no error anymore for the additive statement!" ); + // every part of the additive statement should have passed other tests already, and should not + // be able to cause any errors ... me thinks + } +} + +void SAL_CALL OSingleSelectQueryComposer::setFilter( const OUString& filter ) +{ + setSingleAdditiveClause( Where, filter ); +} + +void SAL_CALL OSingleSelectQueryComposer::setOrder( const OUString& order ) +{ + setSingleAdditiveClause( Order, order ); +} + +void SAL_CALL OSingleSelectQueryComposer::setGroup( const OUString& group ) +{ + setSingleAdditiveClause( Group, group ); +} + +void SAL_CALL OSingleSelectQueryComposer::setHavingClause( const OUString& filter ) +{ + setSingleAdditiveClause( Having, filter ); +} + +// XTablesSupplier +Reference< XNameAccess > SAL_CALL OSingleSelectQueryComposer::getTables( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_pTables ) + { + const OSQLTables& aTables = m_aSqlIterator.getTables(); + std::vector< OUString> aNames; + for (auto const& elem : aTables) + aNames.push_back(elem.first); + + m_pTables.reset( new OPrivateTables(aTables,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,std::move(aNames)) ); + } + + return m_pTables.get(); +} + +// XColumnsSupplier +Reference< XNameAccess > SAL_CALL OSingleSelectQueryComposer::getColumns( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !!m_aCurrentColumns[SelectColumns] ) + return m_aCurrentColumns[SelectColumns].get(); + + std::vector< OUString> aNames; + ::rtl::Reference< OSQLColumns> aSelectColumns; + bool bCase = true; + Reference< XNameAccess> xQueryColumns; + if ( m_nCommandType == CommandType::QUERY ) + { + Reference<XColumnsSupplier> xSup(m_xConnectionQueries->getByName(m_sCommand),UNO_QUERY); + if(xSup.is()) + xQueryColumns = xSup->getColumns(); + } + + do { + + try + { + SharedUNOComponent< XStatement, DisposableComponent > xStatement; + SharedUNOComponent< XPreparedStatement, DisposableComponent > xPreparedStatement; + + bCase = m_xMetaData->supportsMixedCaseQuotedIdentifiers(); + aSelectColumns = m_aSqlIterator.getSelectColumns(); + + OUStringBuffer aSQL( m_aPureSelectSQL + STR_WHERE + " ( 0 = 1 )"); + + // preserve the original WHERE clause + // #i102234# + OUString sOriginalWhereClause = getSQLPart( Where, m_aSqlIterator, false ); + if ( !sOriginalWhereClause.isEmpty() ) + { + aSQL.append( " AND ( " + sOriginalWhereClause + " ) " ); + } + + OUString sGroupBy = getSQLPart( Group, m_aSqlIterator, true ); + if ( !sGroupBy.isEmpty() ) + aSQL.append( sGroupBy ); + + OUString sSQL( aSQL.makeStringAndClear() ); + // normalize the statement so that it doesn't contain any application-level features anymore + OUString sError; + const std::unique_ptr< OSQLParseNode > pStatementTree( m_aSqlParser.parseTree( sError, sSQL ) ); + OSL_ENSURE(pStatementTree, "OSingleSelectQueryComposer::getColumns: could not parse the " + "column retrieval statement!"); + if (pStatementTree) + if ( !pStatementTree->parseNodeToExecutableStatement( sSQL, m_xConnection, m_aSqlParser, nullptr ) ) + break; + + Reference< XResultSetMetaData > xResultSetMeta; + Reference< XResultSetMetaDataSupplier > xResMetaDataSup; + try + { + xPreparedStatement.set( m_xConnection->prepareStatement( sSQL ), UNO_QUERY_THROW ); + xResMetaDataSup.set( xPreparedStatement, UNO_QUERY_THROW ); + xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW ); + } + catch( const Exception& ) { } + + if ( !xResultSetMeta.is() && xPreparedStatement.is() ) + { + try + { + //@see issue http://qa.openoffice.org/issues/show_bug.cgi?id=110111 + // access returns a different order of column names when executing select * from + // and asking the columns from the metadata. + Reference< XParameters > xParameters( xPreparedStatement, UNO_QUERY_THROW ); + Reference< XIndexAccess > xPara = getParameters(); + for(sal_Int32 i = 1;i <= xPara->getCount();++i) + xParameters->setNull(i,DataType::VARCHAR); + xResMetaDataSup.set(xPreparedStatement->executeQuery(), UNO_QUERY_THROW ); + xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW ); + } + catch( const Exception& ) { } + } + + if ( !xResultSetMeta.is() ) + { + xStatement.reset( Reference< XStatement >( m_xConnection->createStatement(), UNO_SET_THROW ) ); + Reference< XPropertySet > xStatementProps( xStatement, UNO_QUERY_THROW ); + try { xStatementProps->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, Any( false ) ); } + catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); } + xResMetaDataSup.set( xStatement->executeQuery( sSQL ), UNO_QUERY_THROW ); + xResultSetMeta.set( xResMetaDataSup->getMetaData(), UNO_SET_THROW ); + + if (xResultSetMeta.is()) + { + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::getColumns failed to get xResultSetMeta from executed PreparedStatement, but got it from 'no escape processing' statement. SQL command:\n\t" << sSQL ); + } + } + + if ( aSelectColumns->empty() ) + { + // This is a valid case. If we can syntactically parse the query, but not semantically + // (e.g. because it is based on a table we do not know), then there will be no SelectColumns + aSelectColumns = ::connectivity::parse::OParseColumn::createColumnsForResultSet( xResultSetMeta, m_xMetaData ,xQueryColumns); + break; + } + + const ::comphelper::UStringMixEqual aCaseCompare( bCase ); + std::set< size_t > aUsedSelectColumns; + ::connectivity::parse::OParseColumn::StringMap aColumnNames; + + sal_Int32 nCount = xResultSetMeta->getColumnCount(); + OSL_ENSURE( static_cast<size_t>(nCount) == aSelectColumns->size(), "OSingleSelectQueryComposer::getColumns: inconsistent column counts, this might result in wrong columns!" ); + for(sal_Int32 i=1;i<=nCount;++i) + { + OUString sColumnName = xResultSetMeta->getColumnName(i); + OUString sColumnLabel; + if ( xQueryColumns.is() && xQueryColumns->hasByName(sColumnName) ) + { + Reference<XPropertySet> xQueryColumn(xQueryColumns->getByName(sColumnName),UNO_QUERY_THROW); + xQueryColumn->getPropertyValue(PROPERTY_LABEL) >>= sColumnLabel; + } + else + sColumnLabel = xResultSetMeta->getColumnLabel(i); + bool bFound = false; + OSQLColumns::Vector::const_iterator aFind = ::connectivity::find(aSelectColumns->begin(),aSelectColumns->end(),sColumnLabel,aCaseCompare); + size_t nFoundSelectColumnPos = aFind - aSelectColumns->begin(); + if ( aFind != aSelectColumns->end() ) + { + if ( aUsedSelectColumns.find( nFoundSelectColumnPos ) != aUsedSelectColumns.end() ) + { // we found a column name which exists twice + // so we start after the first found + do + { + aFind = ::connectivity::findRealName(++aFind,aSelectColumns->end(),sColumnName,aCaseCompare); + nFoundSelectColumnPos = aFind - aSelectColumns->begin(); + } + while ( ( aUsedSelectColumns.find( nFoundSelectColumnPos ) != aUsedSelectColumns.end() ) + && ( aFind != aSelectColumns->end() ) + ); + } + if ( aFind != aSelectColumns->end() ) + { + (*aFind)->getPropertyValue(PROPERTY_NAME) >>= sColumnName; + aUsedSelectColumns.insert( nFoundSelectColumnPos ); + aNames.push_back(sColumnName); + bFound = true; + } + } + + if ( bFound ) + continue; + + OSQLColumns::Vector::const_iterator aRealFind = ::connectivity::findRealName( + aSelectColumns->begin(), aSelectColumns->end(), sColumnName, aCaseCompare ); + + if ( i > static_cast< sal_Int32>( aSelectColumns->size() ) ) + { + aSelectColumns->emplace_back(::connectivity::parse::OParseColumn::createColumnForResultSet( xResultSetMeta, m_xMetaData, i ,aColumnNames) + ); + OSL_ENSURE( aSelectColumns->size() == static_cast<size_t>(i), "OSingleSelectQueryComposer::getColumns: inconsistency!" ); + } + else if ( aRealFind == aSelectColumns->end() ) + { + // we can now only look if we found it under the realname property + // here we have to make the assumption that the position is correct + OSQLColumns::Vector::const_iterator aFind2 = aSelectColumns->begin() + i-1; + Reference<XPropertySet> xProp = *aFind2; + if ( !xProp.is() || !xProp->getPropertySetInfo()->hasPropertyByName( PROPERTY_REALNAME ) ) + continue; + + rtl::Reference<::connectivity::parse::OParseColumn> pColumn = new ::connectivity::parse::OParseColumn(xProp,bCase); + pColumn->setFunction(::comphelper::getBOOL(xProp->getPropertyValue("Function"))); + pColumn->setAggregateFunction(::comphelper::getBOOL(xProp->getPropertyValue("AggregateFunction"))); + + OUString sRealName; + xProp->getPropertyValue(PROPERTY_REALNAME) >>= sRealName; + if ( sColumnName.isEmpty() ) + xProp->getPropertyValue(PROPERTY_NAME) >>= sColumnName; + + sal_Int32 j = 0; + while ( std::any_of(aNames.begin(),aNames.end(), + [&aCaseCompare, &sColumnName](const OUString& lhs) + { return aCaseCompare(lhs, sColumnName); } ) ) + { + sColumnName += OUString::number(++j); + } + + pColumn->setName(sColumnName); + pColumn->setRealName(sRealName); + pColumn->setTableName(::comphelper::getString(xProp->getPropertyValue(PROPERTY_TABLENAME))); + + (*aSelectColumns)[i-1] = pColumn; + } + else + continue; + + aUsedSelectColumns.insert( static_cast<size_t>(i - 1) ); + aNames.push_back( sColumnName ); + } + } + catch(const Exception&) + { + } + + } while ( false ); + + bool bMissingSomeColumnLabels = !aNames.empty() && aNames.size() != aSelectColumns->size(); + SAL_WARN_IF(bMissingSomeColumnLabels, "dbaccess", "We have column labels for *some* columns but not all"); + //^^this happens in the evolution address book where we have real column names of e.g. + //first_name, second_name and city. On parsing via + //OSQLParseTreeIterator::appendColumns it creates some labels using those real names + //but the evo address book gives them proper labels of First Name, Second Name and City + //the munge means that here we have e.g. just "City" as a label because it matches + + //This is all a horrible mess + if (bMissingSomeColumnLabels) + aNames.clear(); + + if ( aNames.empty() ) + m_aCurrentColumns[ SelectColumns ] = OPrivateColumns::createWithIntrinsicNames( aSelectColumns, bCase, *this, m_aMutex ); + else + m_aCurrentColumns[ SelectColumns ].reset( new OPrivateColumns( aSelectColumns, bCase, *this, m_aMutex, aNames ) ); + + return m_aCurrentColumns[SelectColumns].get(); +} + +bool OSingleSelectQueryComposer::setORCriteria(OSQLParseNode const * pCondition, OSQLParseTreeIterator& _rIterator, + std::vector< std::vector < PropertyValue > >& rFilters, const Reference< css::util::XNumberFormatter > & xFormatter) const +{ + // Round brackets around the expression + if (pCondition->count() == 3 && + SQL_ISPUNCTUATION(pCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pCondition->getChild(2),")")) + { + return setORCriteria(pCondition->getChild(1), _rIterator, rFilters, xFormatter); + } + // OR logic expression + // a searchcondition can only look like this: search_condition SQL_TOKEN_OR boolean_term + else if (SQL_ISRULE(pCondition,search_condition)) + { + bool bResult = true; + for (int i = 0; bResult && i < 3; i+=2) + { + // Is the first element a OR logic expression again? + // Then descend recursively ... + if (SQL_ISRULE(pCondition->getChild(i),search_condition)) + bResult = setORCriteria(pCondition->getChild(i), _rIterator, rFilters, xFormatter); + else + { + rFilters.emplace_back(); + bResult = setANDCriteria(pCondition->getChild(i), _rIterator, rFilters[rFilters.size() - 1], xFormatter); + } + } + return bResult; + } + else + { + rFilters.emplace_back(); + return setANDCriteria(pCondition, _rIterator, rFilters[rFilters.size() - 1], xFormatter); + } +} + +bool OSingleSelectQueryComposer::setANDCriteria( OSQLParseNode const * pCondition, + OSQLParseTreeIterator& _rIterator, std::vector < PropertyValue >& rFilter, const Reference< XNumberFormatter > & xFormatter) const +{ + // Round brackets + if (SQL_ISRULE(pCondition,boolean_primary)) + { + // this should not occur + SAL_WARN("dbaccess","boolean_primary in And-Criteria"); + return false; + } + // The first element is an AND logical expression again + else if ( SQL_ISRULE(pCondition,boolean_term) && pCondition->count() == 3 ) + { + return setANDCriteria(pCondition->getChild(0), _rIterator, rFilter, xFormatter) && + setANDCriteria(pCondition->getChild(2), _rIterator, rFilter, xFormatter); + } + else if (SQL_ISRULE(pCondition, comparison_predicate)) + { + return setComparisonPredicate(pCondition,_rIterator,rFilter,xFormatter); + } + else if (SQL_ISRULE(pCondition,like_predicate)) + { + return setLikePredicate(pCondition,_rIterator,rFilter,xFormatter); + } + else if (SQL_ISRULE(pCondition,test_for_null) || + SQL_ISRULE(pCondition,in_predicate) || + SQL_ISRULE(pCondition,all_or_any_predicate) || + SQL_ISRULE(pCondition,between_predicate)) + { + if (SQL_ISRULE(pCondition->getChild(0), column_ref)) + { + PropertyValue aItem; + OUString aValue; + OUString aColumnName; + + pCondition->parseNodeToStr( aValue, m_xConnection ); + pCondition->getChild(0)->parseNodeToStr( aColumnName, m_xConnection ); + + // don't display the column name + aValue = aValue.copy(aColumnName.getLength()); + aValue = aValue.trim(); + + aItem.Name = getColumnName(pCondition->getChild(0),_rIterator); + aItem.Value <<= aValue; + aItem.Handle = 0; // just to know that this is not one the known ones + if ( SQL_ISRULE(pCondition,like_predicate) ) + { + if ( SQL_ISTOKEN(pCondition->getChild(1)->getChild(0),NOT) ) + aItem.Handle = SQLFilterOperator::NOT_LIKE; + else + aItem.Handle = SQLFilterOperator::LIKE; + } + else if (SQL_ISRULE(pCondition,test_for_null)) + { + if (SQL_ISTOKEN(pCondition->getChild(1)->getChild(1),NOT) ) + aItem.Handle = SQLFilterOperator::NOT_SQLNULL; + else + aItem.Handle = SQLFilterOperator::SQLNULL; + } + else if (SQL_ISRULE(pCondition,in_predicate)) + { + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: in_predicate not implemented!" ); + } + else if (SQL_ISRULE(pCondition,all_or_any_predicate)) + { + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: all_or_any_predicate not implemented!" ); + } + else if (SQL_ISRULE(pCondition,between_predicate)) + { + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::setANDCriteria: between_predicate not implemented!" ); + } + + rFilter.push_back(aItem); + } + else + return false; + } + else if (SQL_ISRULE(pCondition,existence_test) || + SQL_ISRULE(pCondition,unique_test)) + { + // this couldn't be handled here, too complex + // as we need a field name + return false; + } + else + return false; + + return true; +} + +sal_Int32 OSingleSelectQueryComposer::getPredicateType(OSQLParseNode const * _pPredicate) +{ + sal_Int32 nPredicate = SQLFilterOperator::EQUAL; + switch (_pPredicate->getNodeType()) + { + case SQLNodeType::Equal: + nPredicate = SQLFilterOperator::EQUAL; + break; + case SQLNodeType::NotEqual: + nPredicate = SQLFilterOperator::NOT_EQUAL; + break; + case SQLNodeType::Less: + nPredicate = SQLFilterOperator::LESS; + break; + case SQLNodeType::LessEq: + nPredicate = SQLFilterOperator::LESS_EQUAL; + break; + case SQLNodeType::Great: + nPredicate = SQLFilterOperator::GREATER; + break; + case SQLNodeType::GreatEq: + nPredicate = SQLFilterOperator::GREATER_EQUAL; + break; + default: + SAL_WARN("dbaccess","Wrong NodeType!"); + } + return nPredicate; +} + +bool OSingleSelectQueryComposer::setLikePredicate(OSQLParseNode const * pCondition, OSQLParseTreeIterator const & _rIterator, + std::vector < PropertyValue >& rFilter, const Reference< css::util::XNumberFormatter > & xFormatter) const +{ + OSL_ENSURE(SQL_ISRULE(pCondition, like_predicate),"setLikePredicate: pCondition is not a LikePredicate"); + + assert(pCondition->count() == 2); + OSQLParseNode const *pRowValue = pCondition->getChild(0); + OSQLParseNode const *pPart2 = pCondition->getChild(1); + + PropertyValue aItem; + if ( SQL_ISTOKEN(pPart2->getChild(0),NOT) ) + aItem.Handle = SQLFilterOperator::NOT_LIKE; + else + aItem.Handle = SQLFilterOperator::LIKE; + + if (SQL_ISRULE(pRowValue, column_ref)) + { + OUString aValue; + + // skip (optional "NOT") and "LIKE" + for (size_t i=2; i < pPart2->count(); i++) + { + pPart2->getChild(i)->parseNodeToPredicateStr( + aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + } + + aItem.Name = getColumnName(pRowValue,_rIterator); + aItem.Value <<= aValue; + rFilter.push_back(aItem); + } + else if (SQL_ISRULE(pRowValue, set_fct_spec ) || + SQL_ISRULE(pRowValue, general_set_fct)) + { + OUString aValue; + OUString aColumnName; + + pPart2->getChild(2)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + pPart2->getChild(3)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + pRowValue->parseNodeToPredicateStr( aColumnName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + aItem.Name = getColumnName(pRowValue,_rIterator); + aItem.Value <<= aValue; + rFilter.push_back(aItem); + } + else // Can only be an expression + { + OUString aName, aValue; + + OSQLParseNode const *pValue = pPart2->getChild(2); + + // Field names + for (size_t i=0;i< pRowValue->count();i++) + pRowValue->getChild(i)->parseNodeToPredicateStr( aName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + // Criterion + for(size_t i=0;i< pValue->count();i++) + pValue->getChild(i)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + pPart2->getChild(3)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + aItem.Name = aName; + aItem.Value <<= aValue; + rFilter.push_back(aItem); + } + return true; +} + +bool OSingleSelectQueryComposer::setComparisonPredicate(OSQLParseNode const * pCondition, OSQLParseTreeIterator const & _rIterator, + std::vector < PropertyValue >& rFilter, const Reference< css::util::XNumberFormatter > & xFormatter) const +{ + OSL_ENSURE(SQL_ISRULE(pCondition, comparison_predicate),"setComparisonPredicate: pCondition is not a ComparisonPredicate"); + if (SQL_ISRULE(pCondition->getChild(0), column_ref) || + SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref)) + { + PropertyValue aItem; + OUString aValue; + sal_uInt32 nPos; + if (SQL_ISRULE(pCondition->getChild(0), column_ref)) + { + nPos = 0; + size_t i=1; + + aItem.Handle = getPredicateType(pCondition->getChild(i)); + + // go forward - don't display the operator + for (i++;i < pCondition->count();i++) + pCondition->getChild(i)->parseNodeToPredicateStr( + aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + } + else if (SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref)) + { + nPos = pCondition->count()-1; + + sal_Int32 i = pCondition->count() - 2; + switch (pCondition->getChild(i)->getNodeType()) + { + case SQLNodeType::Equal: + aItem.Handle = SQLFilterOperator::EQUAL; + break; + case SQLNodeType::NotEqual: + aItem.Handle = SQLFilterOperator::NOT_EQUAL; + break; + case SQLNodeType::Less: + // take the opposite as we change the order + aItem.Handle = SQLFilterOperator::GREATER_EQUAL; + break; + case SQLNodeType::LessEq: + // take the opposite as we change the order + aItem.Handle = SQLFilterOperator::GREATER; + break; + case SQLNodeType::Great: + // take the opposite as we change the order + aItem.Handle = SQLFilterOperator::LESS_EQUAL; + break; + case SQLNodeType::GreatEq: + // take the opposite as we change the order + aItem.Handle = SQLFilterOperator::LESS; + break; + default: + break; + } + + // go backward - don't display the operator + for (i--; i >= 0; i--) + pCondition->getChild(i)->parseNodeToPredicateStr( + aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + } + else + return false; + + aItem.Name = getColumnName(pCondition->getChild(nPos),_rIterator); + aItem.Value <<= aValue; + rFilter.push_back(aItem); + } + else if (SQL_ISRULE(pCondition->getChild(0), set_fct_spec ) || + SQL_ISRULE(pCondition->getChild(0), general_set_fct)) + { + PropertyValue aItem; + OUString aValue; + OUString aColumnName; + + pCondition->getChild(2)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + pCondition->getChild(0)->parseNodeToPredicateStr( aColumnName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + aItem.Name = getColumnName(pCondition->getChild(0),_rIterator); + aItem.Value <<= aValue; + aItem.Handle = getPredicateType(pCondition->getChild(1)); + rFilter.push_back(aItem); + } + else // Can only be an expression + { + PropertyValue aItem; + OUString aName, aValue; + + OSQLParseNode *pLhs = pCondition->getChild(0); + OSQLParseNode *pRhs = pCondition->getChild(2); + + // Field names + for (size_t i=0;i< pLhs->count();i++) + pLhs->getChild(i)->parseNodeToPredicateStr( aName, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + // Criterion + aItem.Handle = getPredicateType(pCondition->getChild(1)); + for(size_t i=0;i< pRhs->count();i++) + pRhs->getChild(i)->parseNodeToPredicateStr(aValue, m_xConnection, xFormatter, m_aLocale, m_sDecimalSep ); + + aItem.Name = aName; + aItem.Value <<= aValue; + rFilter.push_back(aItem); + } + return true; +} + +// Functions for analysing SQL +OUString OSingleSelectQueryComposer::getColumnName( ::connectivity::OSQLParseNode const * pColumnRef, OSQLParseTreeIterator const & _rIterator ) +{ + OUString aTableRange, aColumnName; + _rIterator.getColumnRange(pColumnRef,aColumnName,aTableRange); + return aColumnName; +} + +OUString SAL_CALL OSingleSelectQueryComposer::getFilter( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + return getSQLPart(Where,m_aAdditiveIterator,false); +} + +OUString SAL_CALL OSingleSelectQueryComposer::getOrder( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + return getSQLPart(Order,m_aAdditiveIterator,false); +} + +OUString SAL_CALL OSingleSelectQueryComposer::getGroup( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + return getSQLPart(Group,m_aAdditiveIterator,false); +} + +OUString OSingleSelectQueryComposer::getHavingClause() +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + return getSQLPart(Having,m_aAdditiveIterator,false); +} + +OUString OSingleSelectQueryComposer::getTableAlias(const Reference< XPropertySet >& column) const +{ + OUString sReturn; + if(m_pTables && m_pTables->getCount() > 1) + { + OUString aCatalog,aSchema,aTable,aColumnName; + if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_CATALOGNAME)) + column->getPropertyValue(PROPERTY_CATALOGNAME) >>= aCatalog; + if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_SCHEMANAME)) + column->getPropertyValue(PROPERTY_SCHEMANAME) >>= aSchema; + if(column->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME)) + column->getPropertyValue(PROPERTY_TABLENAME) >>= aTable; + column->getPropertyValue(PROPERTY_NAME) >>= aColumnName; + + Sequence< OUString> aNames(m_pTables->getElementNames()); + const OUString* pBegin = aNames.getConstArray(); + const OUString* const pEnd = pBegin + aNames.getLength(); + + if(aTable.isEmpty()) + { // we haven't found a table name, now we must search every table for this column + for(;pBegin != pEnd;++pBegin) + { + Reference<XColumnsSupplier> xColumnsSupp; + m_pTables->getByName(*pBegin) >>= xColumnsSupp; + + if(xColumnsSupp.is() && xColumnsSupp->getColumns()->hasByName(aColumnName)) + { + aTable = *pBegin; + break; + } + } + } + else + { + OUString aComposedName = ::dbtools::composeTableName( m_xMetaData, aCatalog, aSchema, aTable, false, ::dbtools::EComposeRule::InDataManipulation ); + + // Is this the right case for the table name? + // Else, look for it with different case, if applicable. + + if(!m_pTables->hasByName(aComposedName)) + { + ::comphelper::UStringMixLess aTmp(m_aAdditiveIterator.getTables().key_comp()); + ::comphelper::UStringMixEqual aComp(aTmp.isCaseSensitive()); + for(;pBegin != pEnd;++pBegin) + { + Reference<XPropertySet> xTableProp; + m_pTables->getByName(*pBegin) >>= xTableProp; + OSL_ENSURE(xTableProp.is(),"Table isn't a propertyset!"); + if(xTableProp.is()) + { + OUString aCatalog2,aSchema2,aTable2; + xTableProp->getPropertyValue(PROPERTY_CATALOGNAME) >>= aCatalog2; + xTableProp->getPropertyValue(PROPERTY_SCHEMANAME) >>= aSchema2; + xTableProp->getPropertyValue(PROPERTY_NAME) >>= aTable2; + if(aComp(aCatalog,aCatalog2) && aComp(aSchema,aSchema2) && aComp(aTable,aTable2)) + { + aCatalog = aCatalog2; + aSchema = aSchema2; + aTable = aTable2; + break; + } + } + } + } + } + if(pBegin != pEnd) + { + sReturn = ::dbtools::composeTableName( m_xMetaData, aCatalog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation ) + "."; + } + } + return sReturn; +} + +Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getParameters( ) +{ + // now set the Parameters + if ( !m_aCurrentColumns[ParameterColumns] ) + { + ::rtl::Reference< OSQLColumns> aCols = m_aSqlIterator.getParameters(); + std::vector< OUString> aNames; + for (auto const& elem : *aCols) + aNames.push_back(getString(elem->getPropertyValue(PROPERTY_NAME))); + m_aCurrentColumns[ParameterColumns].reset( new OPrivateColumns(aCols,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,aNames,true) ); + } + + return m_aCurrentColumns[ParameterColumns].get(); +} + +void OSingleSelectQueryComposer::clearColumns( const EColumnType _eType ) +{ + OPrivateColumns* pColumns = m_aCurrentColumns[ _eType ].get(); + if ( pColumns != nullptr ) + { + pColumns->disposing(); + m_aColumnsCollection.push_back( std::move(m_aCurrentColumns[ _eType ]) ); + } +} + +void OSingleSelectQueryComposer::clearCurrentCollections() +{ + for (auto & currentColumn : m_aCurrentColumns) + { + if (currentColumn) + { + currentColumn->disposing(); + m_aColumnsCollection.push_back(std::move(currentColumn)); + } + } + + if(m_pTables) + { + m_pTables->disposing(); + m_aTablesCollection.push_back(std::move(m_pTables)); + } +} + +Reference< XIndexAccess > OSingleSelectQueryComposer::setCurrentColumns( EColumnType _eType, + const ::rtl::Reference< OSQLColumns >& _rCols ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + // now set the group columns + if ( !m_aCurrentColumns[_eType] ) + { + std::vector< OUString> aNames; + for (auto const& elem : *_rCols) + aNames.push_back(getString(elem->getPropertyValue(PROPERTY_NAME))); + m_aCurrentColumns[_eType].reset( new OPrivateColumns(_rCols,m_xMetaData->supportsMixedCaseQuotedIdentifiers(),*this,m_aMutex,aNames,true) ); + } + + return m_aCurrentColumns[_eType].get(); +} + +Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getGroupColumns( ) +{ + return setCurrentColumns( GroupByColumns, m_aAdditiveIterator.getGroupColumns() ); +} + +Reference< XIndexAccess > SAL_CALL OSingleSelectQueryComposer::getOrderColumns( ) +{ + return setCurrentColumns( OrderColumns, m_aAdditiveIterator.getOrderColumns() ); +} + +OUString SAL_CALL OSingleSelectQueryComposer::getQueryWithSubstitution( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + OUString sSqlStatement( getQuery() ); + + const OSQLParseNode* pStatementNode = m_aSqlIterator.getParseTree(); + if ( pStatementNode ) + { + SQLException aError; + if ( !pStatementNode->parseNodeToExecutableStatement( sSqlStatement, m_xConnection, m_aSqlParser, &aError ) ) + throw aError; + } + + return sSqlStatement; +} + +OUString OSingleSelectQueryComposer::getStatementPart( TGetParseNode const & _aGetFunctor, OSQLParseTreeIterator& _rIterator ) +{ + OUString sResult; + + const OSQLParseNode* pNode = _aGetFunctor( &_rIterator ); + if ( pNode ) + pNode->parseNodeToStr( sResult, m_xConnection ); + + return sResult; +} + +namespace +{ + OUString lcl_getDecomposedColumnName(const OUString& rComposedName, const OUString& rQuoteString) + { + const sal_Int32 nQuoteLength = rQuoteString.getLength(); + OUString sName = rComposedName.trim(); + OUString sColumnName; + sal_Int32 nPos, nRPos = 0; + + for (;;) + { + nPos = sName.indexOf( rQuoteString, nRPos ); + if ( nPos >= 0 ) + { + nRPos = sName.indexOf( rQuoteString, nPos + nQuoteLength ); + if ( nRPos > nPos ) + { + if ( nRPos + nQuoteLength < sName.getLength() ) + { + nRPos += nQuoteLength; // -1 + 1 skip dot + } + else + { + sColumnName = sName.copy( nPos + nQuoteLength, nRPos - nPos - nQuoteLength ); + break; + } + } + else + break; + } + else + break; + } + return sColumnName.isEmpty() ? rComposedName : sColumnName; + } + + OUString lcl_getCondition(const Sequence< Sequence< PropertyValue > >& filter, + const OPredicateInputController& i_aPredicateInputController, + const Reference< XNameAccess >& i_xSelectColumns, + const OUString& rQuoteString) + { + OUStringBuffer sRet; + const Sequence< PropertyValue >* pOrIter = filter.getConstArray(); + const Sequence< PropertyValue >* pOrEnd = pOrIter + filter.getLength(); + while ( pOrIter != pOrEnd ) + { + if ( pOrIter->hasElements() ) + { + sRet.append(L_BRACKET); + const PropertyValue* pAndIter = pOrIter->getConstArray(); + const PropertyValue* pAndEnd = pAndIter + pOrIter->getLength(); + while ( pAndIter != pAndEnd ) + { + sRet.append(pAndIter->Name); + OUString sValue; + pAndIter->Value >>= sValue; + const OUString sColumnName = lcl_getDecomposedColumnName( pAndIter->Name, rQuoteString ); + if ( i_xSelectColumns.is() && i_xSelectColumns->hasByName(sColumnName) ) + { + Reference<XPropertySet> xColumn(i_xSelectColumns->getByName(sColumnName),UNO_QUERY); + sValue = i_aPredicateInputController.getPredicateValueStr(sValue,xColumn); + } + else + { + sValue = i_aPredicateInputController.getPredicateValueStr(pAndIter->Name,sValue); + } + lcl_addFilterCriteria_throw(pAndIter->Handle,sValue,sRet); + ++pAndIter; + if ( pAndIter != pAndEnd ) + sRet.append(STR_AND); + } + sRet.append(R_BRACKET); + } + ++pOrIter; + if ( pOrIter != pOrEnd && !sRet.isEmpty() ) + sRet.append(STR_OR); + } + return sRet.makeStringAndClear(); + } +} + +void SAL_CALL OSingleSelectQueryComposer::setStructuredFilter( const Sequence< Sequence< PropertyValue > >& filter ) +{ + OPredicateInputController aPredicateInput(m_aContext, m_xConnection, &m_aParseContext); + setFilter(lcl_getCondition(filter, aPredicateInput, getColumns(), m_xMetaData->getIdentifierQuoteString())); +} + +void SAL_CALL OSingleSelectQueryComposer::setStructuredHavingClause( const Sequence< Sequence< PropertyValue > >& filter ) +{ + OPredicateInputController aPredicateInput(m_aContext, m_xConnection); + setHavingClause(lcl_getCondition(filter, aPredicateInput, getColumns(), m_xMetaData->getIdentifierQuoteString())); +} + +void OSingleSelectQueryComposer::setConditionByColumn( const Reference< XPropertySet >& column, bool andCriteria, std::function<bool(OSingleSelectQueryComposer *, const OUString&)> const & _aSetFunctor, sal_Int32 filterOperator) +{ + try + { + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + if ( !column.is() + || !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_VALUE) + || !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_NAME) + || !column->getPropertySetInfo()->hasPropertyByName(PROPERTY_TYPE)) + throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,Any() ); + + sal_Int32 nType = 0; + column->getPropertyValue(PROPERTY_TYPE) >>= nType; + sal_Int32 nSearchable = dbtools::getSearchColumnFlag(m_xConnection,nType); + if(nSearchable == ColumnSearch::NONE) + throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_SEARCHABLE),*this,SQLSTATE_GENERAL,1000,Any() ); + + ::osl::MutexGuard aGuard( m_aMutex ); + + OUString aName; + column->getPropertyValue(PROPERTY_NAME) >>= aName; + + const Any aValue = column->getPropertyValue(PROPERTY_VALUE); + + OUStringBuffer aSQL; + const OUString aQuote = m_xMetaData->getIdentifierQuoteString(); + getColumns(); + + // TODO: if this is called for HAVING, check that the column is a GROUP BY column + // or that it is an aggregate function + + if ( m_aCurrentColumns[SelectColumns] && m_aCurrentColumns[SelectColumns]->hasByName(aName) ) + { + Reference<XPropertySet> xColumn; + m_aCurrentColumns[SelectColumns]->getByName(aName) >>= xColumn; + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_REALNAME),"Property REALNAME not available!"); + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_TABLENAME),"Property TABLENAME not available!"); + OSL_ENSURE(xColumn->getPropertySetInfo()->hasPropertyByName("AggregateFunction"),"Property AggregateFunction not available!"); + + OUString sRealName,sTableName; + xColumn->getPropertyValue(PROPERTY_REALNAME) >>= sRealName; + xColumn->getPropertyValue(PROPERTY_TABLENAME) >>= sTableName; + if(sTableName.indexOf('.') != -1) + { + OUString aCatlog,aSchema,aTable; + ::dbtools::qualifiedNameComponents(m_xMetaData,sTableName,aCatlog,aSchema,aTable,::dbtools::EComposeRule::InDataManipulation); + sTableName = ::dbtools::composeTableName( m_xMetaData, aCatlog, aSchema, aTable, true, ::dbtools::EComposeRule::InDataManipulation ); + } + else + sTableName = ::dbtools::quoteName(aQuote,sTableName); + + if ( !::comphelper::getBOOL(xColumn->getPropertyValue("Function")) ) + { + aSQL = sTableName + "." + ::dbtools::quoteName( aQuote, sRealName ); + } + else + aSQL = sRealName; + } + else + { + aSQL = getTableAlias( column ) + ::dbtools::quoteName( aQuote, aName ); + } + + if ( aValue.hasValue() ) + { + if( !m_xTypeConverter.is() ) + m_xTypeConverter.set( Converter::create(m_aContext) ); + OSL_ENSURE(m_xTypeConverter.is(),"NO typeconverter!"); + + if ( nType != DataType::BOOLEAN && DataType::BIT != nType ) + { + lcl_addFilterCriteria_throw(filterOperator,u"",aSQL); + } + + switch(nType) + { + case DataType::VARCHAR: + case DataType::CHAR: + case DataType::LONGVARCHAR: + aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) ); + break; + case DataType::CLOB: + { + Reference< XClob > xClob(aValue,UNO_QUERY); + if ( xClob.is() ) + { + const ::sal_Int64 nLength = xClob->length(); + if ( sal_Int64(nLength + aSQL.getLength() + STR_LIKE.getLength() ) < sal_Int64(SAL_MAX_INT32) ) + { + aSQL.append("'" + xClob->getSubString(1,static_cast<sal_Int32>(nLength)) + "'"); + } + } + else + { + aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) ); + } + } + break; + case DataType::VARBINARY: + case DataType::BINARY: + case DataType::LONGVARBINARY: + { + Sequence<sal_Int8> aSeq; + if(!(aValue >>= aSeq)) + throw SQLException(DBA_RES(RID_STR_NOT_SEQUENCE_INT8),*this,SQLSTATE_GENERAL,1000,Any() ); + if(nSearchable == ColumnSearch::CHAR) + { + aSQL.append( "\'" ); + } + aSQL.append( "0x" ); + const sal_Int8* pBegin = aSeq.getConstArray(); + const sal_Int8* pEnd = pBegin + aSeq.getLength(); + for(;pBegin != pEnd;++pBegin) + { + aSQL.append( static_cast<sal_Int32>(*pBegin), 16 ); + } + if(nSearchable == ColumnSearch::CHAR) + aSQL.append( "\'" ); + } + break; + case DataType::BIT: + case DataType::BOOLEAN: + { + bool bValue = false; + m_xTypeConverter->convertToSimpleType(aValue, TypeClass_BOOLEAN) >>= bValue; + + OUString sColumnExp = aSQL.makeStringAndClear(); + getBooleanComparisonPredicate( sColumnExp, bValue, m_nBoolCompareMode, aSQL ); + } + break; + default: + aSQL.append( DBTypeConversion::toSQLString( nType, aValue, m_xTypeConverter ) ); + break; + } + } + else + { + sal_Int32 nFilterOp = filterOperator; + if ( filterOperator != SQLFilterOperator::SQLNULL && filterOperator != SQLFilterOperator::NOT_SQLNULL ) + nFilterOp = SQLFilterOperator::SQLNULL; + lcl_addFilterCriteria_throw(nFilterOp,u"",aSQL); + } + + // Attach filter + // Construct SELECT without WHERE and ORDER BY + OUString sFilter = getFilter(); + + if ( !sFilter.isEmpty() && !aSQL.isEmpty() ) + { + sFilter = L_BRACKET + sFilter + R_BRACKET + + (andCriteria ? std::u16string_view(STR_AND) : std::u16string_view(STR_OR)); + } + sFilter += aSQL; + + // add the filter and the sort order + _aSetFunctor(this,sFilter); + } + catch (css::lang::WrappedTargetException & e) + { + if (e.TargetException.isExtractableTo( + cppu::UnoType<css::sdbc::SQLException>::get())) + { + cppu::throwException(e.TargetException); + } + else + { + throw; + } + } +} + +Sequence< Sequence< PropertyValue > > OSingleSelectQueryComposer::getStructuredCondition( TGetParseNode const & _aGetFunctor ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + MutexGuard aGuard(m_aMutex); + + Sequence< Sequence< PropertyValue > > aFilterSeq; + OUString sFilter = getStatementPart( _aGetFunctor, m_aAdditiveIterator ); + + + if ( !sFilter.isEmpty() ) + { + OUString aSql(m_aPureSelectSQL + STR_WHERE + sFilter); + // build a temporary parse node + const OSQLParseNode* pTempNode = m_aAdditiveIterator.getParseTree(); + + OUString aErrorMsg; + std::unique_ptr<OSQLParseNode> pSqlParseNode( m_aSqlParser.parseTree(aErrorMsg,aSql)); + if (pSqlParseNode) + { + m_aAdditiveIterator.setParseTree(pSqlParseNode.get()); + // normalize the filter + OSQLParseNode* pWhereNode = const_cast<OSQLParseNode*>(m_aAdditiveIterator.getWhereTree()); + + OSQLParseNode* pCondition = pWhereNode->getChild(1); + #if OSL_DEBUG_LEVEL > 0 + OUString sCondition; + pCondition->parseNodeToStr( sCondition, m_xConnection ); + #endif + OSQLParseNode::negateSearchCondition(pCondition); + + pCondition = pWhereNode->getChild(1); + #if OSL_DEBUG_LEVEL > 0 + sCondition.clear(); + pCondition->parseNodeToStr( sCondition, m_xConnection ); + #endif + OSQLParseNode::disjunctiveNormalForm(pCondition); + + pCondition = pWhereNode->getChild(1); + #if OSL_DEBUG_LEVEL > 0 + sCondition.clear(); + pCondition->parseNodeToStr( sCondition, m_xConnection ); + #endif + OSQLParseNode::absorptions(pCondition); + + pCondition = pWhereNode->getChild(1); + #if OSL_DEBUG_LEVEL > 0 + sCondition.clear(); + pCondition->parseNodeToStr( sCondition, m_xConnection ); + #endif + if ( pCondition ) + { + std::vector< std::vector < PropertyValue > > aFilters; + Reference< XNumberFormatter > xFormatter( NumberFormatter::create(m_aContext), UNO_QUERY_THROW ); + xFormatter->attachNumberFormatsSupplier( m_xNumberFormatsSupplier ); + + if (setORCriteria(pCondition, m_aAdditiveIterator, aFilters, xFormatter)) + { + aFilterSeq.realloc(aFilters.size()); + Sequence<PropertyValue>* pFilters = aFilterSeq.getArray(); + for (auto const& filter : aFilters) + { + pFilters->realloc(filter.size()); + PropertyValue* pFilter = pFilters->getArray(); + for (auto const& elem : filter) + { + *pFilter = elem; + ++pFilter; + } + ++pFilters; + } + } + } + // restore + m_aAdditiveIterator.setParseTree(pTempNode); + } + } + return aFilterSeq; +} + +OUString OSingleSelectQueryComposer::getKeyword( SQLPart _ePart ) +{ + OUString sKeyword; + switch(_ePart) + { + default: + SAL_WARN("dbaccess", "OSingleSelectQueryComposer::getKeyWord: Invalid enum value!" ); + [[fallthrough]]; // fallback to WHERE + case Where: + sKeyword = STR_WHERE; + break; + case Group: + sKeyword = STR_GROUP_BY; + break; + case Having: + sKeyword = STR_HAVING; + break; + case Order: + sKeyword = STR_ORDER_BY; + break; + } + return sKeyword; +} + +OUString OSingleSelectQueryComposer::getSQLPart( SQLPart _ePart, OSQLParseTreeIterator& _rIterator, bool _bWithKeyword ) +{ + TGetParseNode F_tmp(&OSQLParseTreeIterator::getSimpleWhereTree); + OUString sKeyword( getKeyword( _ePart ) ); + switch(_ePart) + { + case Where: + F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleWhereTree); + break; + case Group: + F_tmp = TGetParseNode (&OSQLParseTreeIterator::getSimpleGroupByTree); + break; + case Having: + F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleHavingTree); + break; + case Order: + F_tmp = TGetParseNode(&OSQLParseTreeIterator::getSimpleOrderTree); + break; + default: + SAL_WARN("dbaccess","Invalid enum value!"); + } + + OUString sRet = getStatementPart( F_tmp, _rIterator ); + if ( _bWithKeyword && !sRet.isEmpty() ) + sRet = sKeyword + sRet; + return sRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/StaticSet.cxx b/dbaccess/source/core/api/StaticSet.cxx new file mode 100644 index 000000000..97226b466 --- /dev/null +++ b/dbaccess/source/core/api/StaticSet.cxx @@ -0,0 +1,280 @@ +/* -*- 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 . + */ + + +#include "StaticSet.hxx" +#include <com/sun/star/sdbcx/CompareBookmark.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <connectivity/CommonTools.hxx> +#include <comphelper/types.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +using namespace dbaccess; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::osl; + +void OStaticSet::fillValueRow(ORowSetRow& _rRow,sal_Int32 /*_nPosition*/) +{ + _rRow = *m_aSetIter; +} + +// css::sdbcx::XRowLocate +Any OStaticSet::getBookmark() +{ + return Any(getRow()); +} + +bool OStaticSet::moveToBookmark( const Any& bookmark ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + return absolute(::comphelper::getINT32(bookmark)); +} + +sal_Int32 OStaticSet::compareBookmarks( const Any& _first, const Any& _second ) +{ + sal_Int32 nFirst = 0, nSecond = 0; + _first >>= nFirst; + _second >>= nSecond; + return (nFirst < nSecond) ? CompareBookmark::LESS : ((nFirst > nSecond) ? CompareBookmark::GREATER : CompareBookmark::EQUAL); +} + +bool OStaticSet::hasOrderedBookmarks( ) +{ + return true; +} + +sal_Int32 OStaticSet::hashBookmark( const Any& bookmark ) +{ + return ::comphelper::getINT32(bookmark); +} + +bool OStaticSet::fetchRow() +{ + bool bRet = false; + if ( !m_bEnd && (!m_nMaxRows || sal_Int32(m_aSet.size()) < m_nMaxRows) ) + bRet = m_xDriverSet->next(); + if ( bRet ) + { + m_aSet.push_back(new connectivity::ORowVector< connectivity::ORowSetValue >(m_xSetMetaData->getColumnCount())); + m_aSetIter = m_aSet.end() - 1; + (**m_aSetIter)[0] = getRow(); + OCacheSet::fillValueRow(*m_aSetIter,(**m_aSetIter)[0].getInt32()); + } + else + m_bEnd = true; + return bRet; +} + +void OStaticSet::fillAllRows() +{ + if(m_bEnd) + return; + + sal_Int32 nColumnCount = m_xSetMetaData->getColumnCount(); + while(m_xDriverSet->next()) + { + ORowSetRow pRow = new connectivity::ORowVector< connectivity::ORowSetValue >(nColumnCount); + m_aSet.push_back(pRow); + m_aSetIter = m_aSet.end() - 1; + (*pRow)[0] = getRow(); + OCacheSet::fillValueRow(pRow,(*pRow)[0].getInt32()); + } + m_bEnd = true; +} + +// XResultSet +bool OStaticSet::next() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + + if(isAfterLast()) + return false; + if(!m_bEnd) // not yet all records fetched + { + ++m_aSetIter; + if(m_aSetIter == m_aSet.end() && !fetchRow()) + m_aSetIter = m_aSet.end(); + } + else if(!isAfterLast()) + ++m_aSetIter; + return !isAfterLast(); +} + +bool OStaticSet::isBeforeFirst( ) +{ + return m_aSetIter == m_aSet.begin(); +} + +bool OStaticSet::isAfterLast( ) +{ + return m_aSetIter == m_aSet.end() && m_bEnd; +} + +void OStaticSet::beforeFirst( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_aSetIter = m_aSet.begin(); +} + +void OStaticSet::afterLast( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + fillAllRows(); + m_aSetIter = m_aSet.end(); +} + +bool OStaticSet::first() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + m_aSetIter = m_aSet.begin()+1; + if(m_aSetIter == m_aSet.end() && !fetchRow()) + m_aSetIter = m_aSet.end(); + + return m_aSetIter != m_aSet.end(); +} + +bool OStaticSet::last() +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + fillAllRows(); + m_aSetIter = m_aSet.end()-1; + + return !isBeforeFirst() && !isAfterLast(); +} + +sal_Int32 OStaticSet::getRow( ) +{ + OSL_ENSURE(!isAfterLast(),"getRow is not allowed when afterlast record!"); + OSL_ENSURE(!isBeforeFirst(),"getRow is not allowed when beforefirst record!"); + + sal_Int32 nPos = m_aSet.size() - (m_aSet.end() - m_aSetIter); + OSL_ENSURE(nPos > 0,"RowPos is < 0"); + return nPos; +} + +bool OStaticSet::absolute( sal_Int32 row ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + OSL_ENSURE(row,"OStaticSet::absolute: INVALID row number!"); + // if row greater 0 than count from end - row means last + if(row < 0) + { + if(!m_bEnd) + fillAllRows(); + + sal_Int32 nRow = getRow(); + nRow += row; + if(nRow <= static_cast<sal_Int32>(m_aSet.size())) + m_aSetIter = m_aSet.begin() + nRow; + else + m_aSetIter = m_aSet.begin(); + } + else if(row > 0) + { + if(o3tl::make_unsigned(row) >= m_aSet.size()) + { + if(!m_bEnd) + { + bool bNext = true; + for(sal_Int32 i=m_aSet.size()-1;i < row && bNext;++i) + bNext = fetchRow(); + } + + if(o3tl::make_unsigned(row) > m_aSet.size()) + m_aSetIter = m_aSet.end(); // check again + else + m_aSetIter = m_aSet.begin() + row; + } + else + m_aSetIter = m_aSet.begin() + row; + } + + return m_aSetIter != m_aSet.end() && m_aSetIter != m_aSet.begin(); +} + +bool OStaticSet::previous( ) +{ + m_bInserted = m_bUpdated = m_bDeleted = false; + + if(m_aSetIter != m_aSet.begin()) + --m_aSetIter; + + return m_aSetIter != m_aSet.begin(); +} + +void OStaticSet::refreshRow( ) +{ +} + +bool OStaticSet::rowUpdated( ) +{ + return m_bUpdated; +} + +bool OStaticSet::rowInserted( ) +{ + return m_bInserted; +} + +bool OStaticSet::rowDeleted( ) +{ + return m_bDeleted; +} + +void OStaticSet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) +{ + OCacheSet::insertRow( _rInsertRow,_xTable); + if(m_bInserted) + { + m_aSet.push_back(new ORowVector< ORowSetValue >(*_rInsertRow)); // we don't know where the new row is so we append it to the current rows + m_aSetIter = m_aSet.end() - 1; + (**m_aSetIter)[0] = (*_rInsertRow)[0] = getBookmark(); + m_bEnd = false; + } +} + +void OStaticSet::deleteRow(const ORowSetRow& _rDeleteRow ,const connectivity::OSQLTable& _xTable ) +{ + OCacheSet::deleteRow(_rDeleteRow,_xTable); + if(m_bDeleted) + { + ORowSetMatrix::iterator aPos = m_aSet.begin()+(*_rDeleteRow)[0].getInt32(); + if(aPos == (m_aSet.end()-1)) + m_aSetIter = m_aSet.end(); + m_aSet.erase(aPos); + } +} + +void OStaticSet::reset(const Reference< XResultSet> &_xDriverSet) +{ + OCacheSet::construct(_xDriverSet, m_sRowSetFilter); + ORowSetMatrix().swap(m_aSet); + m_aSetIter = m_aSet.end(); + m_bEnd = false; + m_aSet.emplace_back(nullptr); // this is the beforefirst record +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/StaticSet.hxx b/dbaccess/source/core/api/StaticSet.hxx new file mode 100644 index 000000000..9f765996b --- /dev/null +++ b/dbaccess/source/core/api/StaticSet.hxx @@ -0,0 +1,75 @@ +/* -*- 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 . + */ + +#pragma once + +#include "CacheSet.hxx" + +namespace dbaccess +{ + // is used when nothing is supported by the driver + // we use a snapshot + class OStaticSet : public OCacheSet + { + ORowSetMatrix m_aSet; + ORowSetMatrix::iterator m_aSetIter; + bool m_bEnd; + bool fetchRow(); + void fillAllRows(); + public: + explicit OStaticSet(sal_Int32 i_nMaxRows) : OCacheSet(i_nMaxRows) + , m_aSetIter(m_aSet.end()) + , m_bEnd(false) + { + m_aSet.push_back(nullptr); // this is the beforefirst record + } + + virtual void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet) override; + + virtual void fillValueRow(ORowSetRow& _rRow,sal_Int32 _nPosition) override; + // css::sdbcx::XRowLocate + virtual css::uno::Any getBookmark() override; + virtual bool moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual bool hasOrderedBookmarks( ) override; + virtual sal_Int32 hashBookmark( const css::uno::Any& bookmark ) override; + + bool isBeforeFirst( ); + bool isAfterLast( ); + + // css::sdbc::XResultSet + virtual bool next() override; + virtual void beforeFirst( ) override; + virtual void afterLast( ) override; + virtual bool first() override; + virtual bool last() override; + virtual sal_Int32 getRow( ) override; + virtual bool absolute( sal_Int32 row ) override; + virtual bool previous( ) override; + virtual void refreshRow( ) override; + virtual bool rowUpdated( ) override; + virtual bool rowInserted( ) override; + virtual bool rowDeleted( ) override; + // css::sdbc::XResultSetUpdate + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + virtual void deleteRow(const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/TableDeco.cxx b/dbaccess/source/core/api/TableDeco.cxx new file mode 100644 index 000000000..92821750f --- /dev/null +++ b/dbaccess/source/core/api/TableDeco.cxx @@ -0,0 +1,634 @@ +/* -*- 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 . + */ + +#include <TableDeco.hxx> +#include <definitioncolumn.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <comphelper/property.hxx> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <ContainerMediator.hxx> + +using namespace dbaccess; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::dbtools; +using namespace ::cppu; + +// ODBTableDecorator + +ODBTableDecorator::ODBTableDecorator( const Reference< XConnection >& _rxConnection, const Reference< XColumnsSupplier >& _rxNewTable, + const Reference< XNumberFormatsSupplier >& _rxNumberFormats, const Reference< XNameAccess >& _xColumnDefinitions ) + :OTableDescriptor_BASE(m_aMutex) + ,ODataSettings(OTableDescriptor_BASE::rBHelper) + ,m_xTable(_rxNewTable) + ,m_xColumnDefinitions(_xColumnDefinitions) + ,m_xConnection( _rxConnection ) + ,m_xMetaData( _rxConnection.is() ? _rxConnection->getMetaData() : Reference< XDatabaseMetaData >() ) + ,m_xNumberFormats( _rxNumberFormats ) + ,m_nPrivileges(-1) +{ + ODataSettings::registerPropertiesFor(this); +} + +ODBTableDecorator::~ODBTableDecorator() +{ +} + +Sequence< sal_Int8 > ODBTableDecorator::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// OComponentHelper +void SAL_CALL ODBTableDecorator::disposing() +{ + OPropertySetHelper::disposing(); + OTableDescriptor_BASE::disposing(); + + MutexGuard aGuard(m_aMutex); + m_xTable = nullptr; + m_xMetaData = nullptr; + m_xColumnDefinitions = nullptr; + m_xNumberFormats = nullptr; + if ( m_pColumns ) + m_pColumns->disposing(); + m_xColumnMediator = nullptr; +} + +sal_Bool SAL_CALL ODBTableDecorator::convertFastPropertyValue( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue ) +{ + bool bRet = true; + switch(nHandle) + { + case PROPERTY_ID_PRIVILEGES: + case PROPERTY_ID_FILTER: + case PROPERTY_ID_ORDER: + case PROPERTY_ID_APPLYFILTER: + case PROPERTY_ID_FONT: + case PROPERTY_ID_ROW_HEIGHT: + case PROPERTY_ID_AUTOGROW: + case PROPERTY_ID_TEXTCOLOR: + case PROPERTY_ID_TEXTLINECOLOR: + case PROPERTY_ID_TEXTEMPHASIS: + case PROPERTY_ID_TEXTRELIEF: + case PROPERTY_ID_FONTCHARWIDTH: + case PROPERTY_ID_FONTCHARSET: + case PROPERTY_ID_FONTFAMILY: + case PROPERTY_ID_FONTHEIGHT: + case PROPERTY_ID_FONTKERNING: + case PROPERTY_ID_FONTNAME: + case PROPERTY_ID_FONTORIENTATION: + case PROPERTY_ID_FONTPITCH: + case PROPERTY_ID_FONTSLANT: + case PROPERTY_ID_FONTSTRIKEOUT: + case PROPERTY_ID_FONTSTYLENAME: + case PROPERTY_ID_FONTUNDERLINE: + case PROPERTY_ID_FONTWEIGHT: + case PROPERTY_ID_FONTWIDTH: + case PROPERTY_ID_FONTWORDLINEMODE: + case PROPERTY_ID_FONTTYPE: + bRet = ODataSettings::convertFastPropertyValue(rConvertedValue, rOldValue,nHandle,rValue); + break; + + default: + { + Any aValue; + getFastPropertyValue(aValue,nHandle); + bRet = ::comphelper::tryPropertyValue(rConvertedValue,rOldValue,rValue,aValue,::cppu::UnoType<OUString>::get()); + } + break; // we assume that it works + } + return bRet; +} + +void ODBTableDecorator::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue) +{ + switch(_nHandle) + { + case PROPERTY_ID_PRIVILEGES: + SAL_WARN("dbaccess", "Property is readonly!"); + [[fallthrough]]; + case PROPERTY_ID_FILTER: + case PROPERTY_ID_ORDER: + case PROPERTY_ID_APPLYFILTER: + case PROPERTY_ID_FONT: + case PROPERTY_ID_ROW_HEIGHT: + case PROPERTY_ID_AUTOGROW: + case PROPERTY_ID_TEXTCOLOR: + case PROPERTY_ID_TEXTLINECOLOR: + case PROPERTY_ID_TEXTEMPHASIS: + case PROPERTY_ID_TEXTRELIEF: + case PROPERTY_ID_FONTCHARWIDTH: + case PROPERTY_ID_FONTCHARSET: + case PROPERTY_ID_FONTFAMILY: + case PROPERTY_ID_FONTHEIGHT: + case PROPERTY_ID_FONTKERNING: + case PROPERTY_ID_FONTNAME: + case PROPERTY_ID_FONTORIENTATION: + case PROPERTY_ID_FONTPITCH: + case PROPERTY_ID_FONTSLANT: + case PROPERTY_ID_FONTSTRIKEOUT: + case PROPERTY_ID_FONTSTYLENAME: + case PROPERTY_ID_FONTUNDERLINE: + case PROPERTY_ID_FONTWEIGHT: + case PROPERTY_ID_FONTWIDTH: + case PROPERTY_ID_FONTWORDLINEMODE: + case PROPERTY_ID_FONTTYPE: + + ODataSettings::setFastPropertyValue_NoBroadcast(_nHandle, _rValue); + break; + case PROPERTY_ID_CATALOGNAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + xProp->setPropertyValue(PROPERTY_CATALOGNAME,_rValue); + } + break; + case PROPERTY_ID_SCHEMANAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + xProp->setPropertyValue(PROPERTY_SCHEMANAME,_rValue); + } + break; + case PROPERTY_ID_NAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + xProp->setPropertyValue(PROPERTY_NAME,_rValue); + } + break; + case PROPERTY_ID_DESCRIPTION: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + xProp->setPropertyValue(PROPERTY_DESCRIPTION,_rValue); + } + break; + case PROPERTY_ID_TYPE: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + xProp->setPropertyValue(PROPERTY_TYPE,_rValue); + } + break; + } +} + +void ODBTableDecorator::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const +{ + + switch(_nHandle) + { + case PROPERTY_ID_PRIVILEGES: + { + if ( -1 == m_nPrivileges ) + fillPrivileges(); + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + Reference<XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + if ( xInfo->hasPropertyByName(PROPERTY_PRIVILEGES) ) + { + _rValue <<= m_nPrivileges; + break; + } + } + [[fallthrough]]; + + case PROPERTY_ID_FILTER: + case PROPERTY_ID_ORDER: + case PROPERTY_ID_APPLYFILTER: + case PROPERTY_ID_FONT: + case PROPERTY_ID_ROW_HEIGHT: + case PROPERTY_ID_AUTOGROW: + case PROPERTY_ID_TEXTCOLOR: + case PROPERTY_ID_TEXTLINECOLOR: + case PROPERTY_ID_TEXTEMPHASIS: + case PROPERTY_ID_TEXTRELIEF: + case PROPERTY_ID_FONTCHARWIDTH: + case PROPERTY_ID_FONTCHARSET: + case PROPERTY_ID_FONTFAMILY: + case PROPERTY_ID_FONTHEIGHT: + case PROPERTY_ID_FONTKERNING: + case PROPERTY_ID_FONTNAME: + case PROPERTY_ID_FONTORIENTATION: + case PROPERTY_ID_FONTPITCH: + case PROPERTY_ID_FONTSLANT: + case PROPERTY_ID_FONTSTRIKEOUT: + case PROPERTY_ID_FONTSTYLENAME: + case PROPERTY_ID_FONTUNDERLINE: + case PROPERTY_ID_FONTWEIGHT: + case PROPERTY_ID_FONTWIDTH: + case PROPERTY_ID_FONTWORDLINEMODE: + case PROPERTY_ID_FONTTYPE: + ODataSettings::getFastPropertyValue(_rValue, _nHandle); + break; + case PROPERTY_ID_CATALOGNAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + _rValue = xProp->getPropertyValue(PROPERTY_CATALOGNAME); + } + break; + case PROPERTY_ID_SCHEMANAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + _rValue = xProp->getPropertyValue(PROPERTY_SCHEMANAME); + } + break; + case PROPERTY_ID_NAME: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + _rValue = xProp->getPropertyValue(PROPERTY_NAME); + } + break; + case PROPERTY_ID_DESCRIPTION: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + _rValue = xProp->getPropertyValue(PROPERTY_DESCRIPTION); + } + break; + case PROPERTY_ID_TYPE: + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + _rValue = xProp->getPropertyValue(PROPERTY_TYPE); + } + break; + default: + SAL_WARN("dbaccess", "Invalid Handle for table"); + } +} + +void ODBTableDecorator::construct() +{ + bool bNotFound = true; + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + if ( xProp.is() ) + { + Reference<XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + bNotFound = !xInfo->hasPropertyByName(PROPERTY_PRIVILEGES); + } + if ( bNotFound ) + registerProperty(PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES, PropertyAttribute::BOUND | PropertyAttribute::READONLY, + &m_nPrivileges, ::cppu::UnoType<sal_Int32>::get()); +} + +::cppu::IPropertyArrayHelper* ODBTableDecorator::createArrayHelper(sal_Int32 /*_nId*/) const +{ + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + Reference<XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + + Sequence< Property > aTableProps = xInfo->getProperties(); + for (Property & prop : asNonConstRange(aTableProps)) + { + if (prop.Name == PROPERTY_CATALOGNAME) + prop.Handle = PROPERTY_ID_CATALOGNAME; + else if (prop.Name == PROPERTY_SCHEMANAME) + prop.Handle = PROPERTY_ID_SCHEMANAME; + else if (prop.Name == PROPERTY_NAME) + prop.Handle = PROPERTY_ID_NAME; + else if (prop.Name == PROPERTY_DESCRIPTION) + prop.Handle = PROPERTY_ID_DESCRIPTION; + else if (prop.Name == PROPERTY_TYPE) + prop.Handle = PROPERTY_ID_TYPE; + else if (prop.Name == PROPERTY_PRIVILEGES) + prop.Handle = PROPERTY_ID_PRIVILEGES; + } + + describeProperties(aTableProps); + + return new ::cppu::OPropertyArrayHelper(aTableProps); +} + +::cppu::IPropertyArrayHelper & SAL_CALL ODBTableDecorator::getInfoHelper() +{ + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + + Reference<XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + bool bIsDescriptor = (xInfo->getPropertyByName(PROPERTY_NAME).Attributes & PropertyAttribute::READONLY) == 0; + + return *ODBTableDecorator_PROP::getArrayHelper( bIsDescriptor ? 0 : 1 ); + + // TODO: this is a HACK, and prone to errors + // The OIdPropertyArrayUsageHelper is intended for classes where there exists a known, limited + // number of different property set infos (distinguished by the ID), all implemented by this very + // same class. + // However, in this case here we have an unknown, potentially unlimited number of different + // property set infos: Depending on the table for which we act as decorator, different property + // sets might exist. +} + +// XServiceInfo +OUString SAL_CALL ODBTableDecorator::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.ODBTableDecorator"; + } +sal_Bool SAL_CALL ODBTableDecorator::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL ODBTableDecorator::getSupportedServiceNames() +{ + return { SERVICE_SDBCX_TABLE }; +} + + +Any SAL_CALL ODBTableDecorator::queryInterface( const Type & rType ) +{ + Any aRet; + if(m_xTable.is()) + { + aRet = m_xTable->queryInterface(rType); + if(aRet.hasValue()) + { // now we know that our table supports this type so we return ourself + aRet = OTableDescriptor_BASE::queryInterface(rType); + if(!aRet.hasValue()) + aRet = ODataSettings::queryInterface(rType); + } + } + + return aRet; +} + +Sequence< Type > SAL_CALL ODBTableDecorator::getTypes( ) +{ + Reference<XTypeProvider> xTypes(m_xTable,UNO_QUERY); + OSL_ENSURE(xTypes.is(),"Table must be a TypeProvider!"); + return xTypes->getTypes(); +} + +// XRename, +void SAL_CALL ODBTableDecorator::rename( const OUString& _rNewName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + Reference<XRename> xRename(m_xTable,UNO_QUERY); + if(!xRename.is()) + throw SQLException(DBA_RES(RID_STR_NO_TABLE_RENAME),*this,SQLSTATE_GENERAL,1000,Any() ); + // not supported + xRename->rename(_rNewName); +} + +// XAlterTable, +void SAL_CALL ODBTableDecorator::alterColumnByName( const OUString& _rName, const Reference< XPropertySet >& _rxDescriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY); + if(!xAlter.is()) + throw SQLException(DBA_RES(RID_STR_COLUMN_ALTER_BY_NAME),*this,SQLSTATE_GENERAL,1000,Any() ); + xAlter->alterColumnByName(_rName,_rxDescriptor); + if(m_pColumns) + m_pColumns->refresh(); +} + +void SAL_CALL ODBTableDecorator::alterColumnByIndex( sal_Int32 _nIndex, const Reference< XPropertySet >& _rxDescriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + Reference<XAlterTable> xAlter(m_xTable,UNO_QUERY); + if(!xAlter.is()) + throw SQLException(DBA_RES(RID_STR_COLUMN_ALTER_BY_INDEX),*this,SQLSTATE_GENERAL,1000,Any() ); + // not supported + xAlter->alterColumnByIndex(_nIndex,_rxDescriptor); + if(m_pColumns) + m_pColumns->refresh(); +} + +Reference< XNameAccess> ODBTableDecorator::getIndexes() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + return Reference< XIndexesSupplier>(m_xTable,UNO_QUERY_THROW)->getIndexes(); +} + +Reference< XIndexAccess> ODBTableDecorator::getKeys() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + return Reference< XKeysSupplier>(m_xTable,UNO_QUERY_THROW)->getKeys(); +} + +Reference< XNameAccess> ODBTableDecorator::getColumns() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + + if(!m_pColumns) + refreshColumns(); + + return m_pColumns.get(); +} + +OUString SAL_CALL ODBTableDecorator::getName() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + Reference<XNamed> xName(m_xTable,UNO_QUERY); + OSL_ENSURE(xName.is(),"Table should support the XNamed interface"); + return xName->getName(); +} + +sal_Int64 SAL_CALL ODBTableDecorator::getSomething( const Sequence< sal_Int8 >& rId ) +{ + if (comphelper::isUnoTunnelId<ODBTableDecorator>(rId)) + return comphelper::getSomething_cast(this); + + sal_Int64 nRet = 0; + Reference<XUnoTunnel> xTunnel(m_xTable,UNO_QUERY); + if(xTunnel.is()) + nRet = xTunnel->getSomething(rId); + return nRet; +} + +const Sequence< sal_Int8 > & ODBTableDecorator::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +void ODBTableDecorator::fillPrivileges() const +{ + // somebody is asking for the privileges and we do not know them, yet + m_nPrivileges = 0; + try + { + Reference<XPropertySet> xProp(m_xTable,UNO_QUERY); + if ( xProp.is() ) + { + if ( xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_PRIVILEGES) ) + { + xProp->getPropertyValue(PROPERTY_PRIVILEGES) >>= m_nPrivileges; + } + if ( m_nPrivileges == 0 ) // second chance + { + OUString sCatalog,sSchema,sName; + xProp->getPropertyValue(PROPERTY_CATALOGNAME) >>= sCatalog; + xProp->getPropertyValue(PROPERTY_SCHEMANAME) >>= sSchema; + xProp->getPropertyValue(PROPERTY_NAME) >>= sName; + m_nPrivileges = ::dbtools::getTablePrivileges(m_xMetaData, sCatalog,sSchema, sName); + } + } + } + catch(const SQLException&) + { + SAL_WARN("dbaccess", "ODBTableDecorator::ODBTableDecorator : could not collect the privileges !"); + } +} + +Reference< XPropertySet > SAL_CALL ODBTableDecorator::createDataDescriptor( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + + Reference< XDataDescriptorFactory > xFactory( m_xTable, UNO_QUERY ); + OSL_ENSURE( xFactory.is(), "ODBTableDecorator::createDataDescriptor: invalid table!" ); + Reference< XColumnsSupplier > xColsSupp; + if ( xFactory.is() ) + xColsSupp.set(xFactory->createDataDescriptor(), css::uno::UNO_QUERY); + + return new ODBTableDecorator( + m_xConnection, + xColsSupp, + m_xNumberFormats, + nullptr + ); +} + +Reference< css::beans::XPropertySetInfo > SAL_CALL ODBTableDecorator::getPropertySetInfo( ) +{ + return ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()); +} + +void ODBTableDecorator::refreshColumns() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OTableDescriptor_BASE::rBHelper.bDisposed); + + std::vector< OUString> aVector; + + Reference<XNameAccess> xNames; + if(m_xTable.is()) + { + xNames = m_xTable->getColumns(); + if(xNames.is()) + { + const Sequence< OUString> aNames = xNames->getElementNames(); + aVector.insert(aVector.end(), aNames.begin(), aNames.end()); + } + } + if(!m_pColumns) + { + OColumns* pCol = new OColumns(*this,m_aMutex,xNames,m_xMetaData.is() && m_xMetaData->supportsMixedCaseQuotedIdentifiers(),aVector, + this,this, + m_xMetaData.is() && m_xMetaData->supportsAlterTableWithAddColumn(), + m_xMetaData.is() && m_xMetaData->supportsAlterTableWithDropColumn()); + + pCol->setParent(*this); + rtl::Reference<OContainerMediator> pMediator = new OContainerMediator( pCol, m_xColumnDefinitions ); + m_xColumnMediator = pMediator; + pCol->setMediator( pMediator.get() ); + m_pColumns.reset(pCol); + } + else + m_pColumns->reFill(aVector); +} + +rtl::Reference<OColumn> ODBTableDecorator::createColumn(const OUString& _rName) const +{ + rtl::Reference<OColumn> pReturn; + + Reference<XNameAccess> xNames; + if ( m_xTable.is() ) + { + xNames = m_xTable->getColumns(); + + if ( xNames.is() && xNames->hasByName(_rName) ) + { + Reference<XPropertySet> xProp(xNames->getByName(_rName),UNO_QUERY); + + Reference<XPropertySet> xColumnDefinition; + if ( m_xColumnDefinitions.is() && m_xColumnDefinitions->hasByName(_rName)) + xColumnDefinition.set(m_xColumnDefinitions->getByName(_rName),UNO_QUERY); + + pReturn = new OTableColumnWrapper( xProp, xColumnDefinition, false ); + } + } + return pReturn; +} + +void ODBTableDecorator::columnAppended( const Reference< XPropertySet >& /*_rxSourceDescriptor*/ ) +{ + // not interested in +} + +void ODBTableDecorator::columnDropped(const OUString& _sName) +{ + Reference<XDrop> xDrop(m_xColumnDefinitions,UNO_QUERY); + if ( xDrop.is() && m_xColumnDefinitions->hasByName(_sName) ) + xDrop->dropByName(_sName); +} + +Reference< XPropertySet > ODBTableDecorator::createColumnDescriptor() +{ + Reference<XDataDescriptorFactory> xNames; + if(m_xTable.is()) + xNames.set(m_xTable->getColumns(),UNO_QUERY); + Reference< XPropertySet > xRet; + if ( xNames.is() ) + xRet = new OTableColumnDescriptorWrapper( xNames->createDataDescriptor(), false, true ); + return xRet; +} + +void SAL_CALL ODBTableDecorator::acquire() noexcept +{ + OTableDescriptor_BASE::acquire(); +} + +void SAL_CALL ODBTableDecorator::release() noexcept +{ + OTableDescriptor_BASE::release(); +} + +void SAL_CALL ODBTableDecorator::setName( const OUString& /*aName*/ ) +{ + throwFunctionNotSupportedRuntimeException( "XNamed::setName", *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/View.cxx b/dbaccess/source/core/api/View.cxx new file mode 100644 index 000000000..c52c4885c --- /dev/null +++ b/dbaccess/source/core/api/View.cxx @@ -0,0 +1,120 @@ +/* -*- 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 . + */ + +#include <View.hxx> +#include <strings.hxx> + +#include <connectivity/dbtools.hxx> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using namespace ::com::sun::star::uno; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::lang::XMultiServiceFactory; + + static OUString lcl_getServiceNameForSetting(const Reference< css::sdbc::XConnection >& _xConnection,const OUString& i_sSetting) + { + OUString sSupportService; + Any aValue; + if ( dbtools::getDataSourceSetting(_xConnection,i_sSetting,aValue) ) + { + aValue >>= sSupportService; + } + return sSupportService; + } + // View + View::View( const Reference< XConnection >& _rxConnection, bool _bCaseSensitive, + const OUString& _rCatalogName,const OUString& _rSchemaName, const OUString& _rName ) + :View_Base( _bCaseSensitive, _rName, _rxConnection->getMetaData(), OUString(), _rSchemaName, _rCatalogName ) + { + m_nCommandHandle = getProperty(PROPERTY_COMMAND).Handle; + try + { + Reference<XMultiServiceFactory> xFac(_rxConnection,UNO_QUERY_THROW); + m_xViewAccess.set(xFac->createInstance(lcl_getServiceNameForSetting(_rxConnection,"ViewAccessServiceName")),UNO_QUERY); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + View::~View() + { + } + + IMPLEMENT_FORWARD_REFCOUNT( View, View_Base ) + IMPLEMENT_GET_IMPLEMENTATION_ID( View ) + + Any SAL_CALL View::queryInterface( const Type & _rType ) + { + if(_rType == cppu::UnoType<XAlterView>::get()&& !m_xViewAccess.is() ) + return Any(); + Any aReturn = View_Base::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + aReturn = View_IBASE::queryInterface( _rType ); + return aReturn; + } + + Sequence< Type > SAL_CALL View::getTypes( ) + { + Type aAlterType = cppu::UnoType<XAlterView>::get(); + + Sequence< Type > aTypes( ::comphelper::concatSequences(View_Base::getTypes(),View_IBASE::getTypes()) ); + std::vector<Type> aOwnTypes; + aOwnTypes.reserve(aTypes.getLength()); + + const Type* pIter = aTypes.getConstArray(); + const Type* pEnd = pIter + aTypes.getLength(); + for(;pIter != pEnd ;++pIter) + { + if( *pIter != aAlterType || m_xViewAccess.is() ) + aOwnTypes.push_back(*pIter); + } + + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); + } + + void SAL_CALL View::alterCommand( const OUString& _rNewCommand ) + { + OSL_ENSURE(m_xViewAccess.is(),"Illegal call to AlterView!"); + m_xViewAccess->alterCommand(this,_rNewCommand); + } + + void SAL_CALL View::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const + { + if ( _nHandle == m_nCommandHandle && m_xViewAccess.is() ) + { + // retrieve the very current command, don't rely on the base classes cached value + // (which we initialized empty, anyway) + _rValue <<= m_xViewAccess->getCommand(const_cast<View*>(this)); + return; + } + + View_Base::getFastPropertyValue( _rValue, _nHandle ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/WrappedResultSet.cxx b/dbaccess/source/core/api/WrappedResultSet.cxx new file mode 100644 index 000000000..09d70826b --- /dev/null +++ b/dbaccess/source/core/api/WrappedResultSet.cxx @@ -0,0 +1,184 @@ +/* -*- 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 . + */ + +#include "WrappedResultSet.hxx" +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> + +using namespace dbaccess; +using namespace ::connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::osl; + +void WrappedResultSet::construct(const Reference< XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) +{ + OCacheSet::construct(_xDriverSet,i_sRowSetFilter); + m_xUpd.set(_xDriverSet,UNO_QUERY_THROW); + m_xRowLocate.set(_xDriverSet,UNO_QUERY_THROW); + m_xUpdRow.set(_xDriverSet,UNO_QUERY_THROW); +} + +void WrappedResultSet::reset(const Reference< XResultSet>& _xDriverSet) +{ + construct(_xDriverSet, m_sRowSetFilter); +} + +Any WrappedResultSet::getBookmark() +{ + if ( m_xRowLocate.is() ) + { + return m_xRowLocate->getBookmark( ); + } + return Any(m_xDriverSet->getRow()); +} + +bool WrappedResultSet::moveToBookmark( const Any& bookmark ) +{ + return m_xRowLocate->moveToBookmark( bookmark ); +} + +sal_Int32 WrappedResultSet::compareBookmarks( const Any& _first, const Any& _second ) +{ + return m_xRowLocate->compareBookmarks( _first,_second ); +} + +bool WrappedResultSet::hasOrderedBookmarks( ) +{ + return m_xRowLocate->hasOrderedBookmarks(); +} + +sal_Int32 WrappedResultSet::hashBookmark( const Any& bookmark ) +{ + return m_xRowLocate->hashBookmark(bookmark); +} + +void WrappedResultSet::insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + m_xUpd->moveToInsertRow(); + sal_Int32 i = 1; + connectivity::ORowVector< ORowSetValue > ::Vector::const_iterator aEnd = _rInsertRow->end(); + for(connectivity::ORowVector< ORowSetValue > ::Vector::iterator aIter = _rInsertRow->begin()+1;aIter != aEnd;++aIter,++i) + { + aIter->setSigned(m_aSignedFlags[i-1]); + updateColumn(i,m_xUpdRow,*aIter); + } + m_xUpd->insertRow(); + (*_rInsertRow->begin()) = getBookmark(); +} + +void WrappedResultSet::updateRow(const ORowSetRow& _rInsertRow ,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& /*_xTable*/ ) +{ + sal_Int32 i = 1; + connectivity::ORowVector< ORowSetValue > ::Vector::const_iterator aOrgIter = _rOriginalRow->begin()+1; + connectivity::ORowVector< ORowSetValue > ::Vector::iterator aEnd = _rInsertRow->end(); + for(connectivity::ORowVector< ORowSetValue > ::Vector::iterator aIter = _rInsertRow->begin()+1;aIter != aEnd;++aIter,++i,++aOrgIter) + { + aIter->setSigned(aOrgIter->isSigned()); + updateColumn(i,m_xUpdRow,*aIter); + } + m_xUpd->updateRow(); +} + +void WrappedResultSet::deleteRow(const ORowSetRow& /*_rDeleteRow*/ ,const connectivity::OSQLTable& /*_xTable*/ ) +{ + m_xUpd->deleteRow(); +} + +void WrappedResultSet::updateColumn(sal_Int32 nPos, const Reference< XRowUpdate >& _xParameter, const ORowSetValue& _rValue) +{ + if(!(_rValue.isBound() && _rValue.isModified())) + return; + + if(_rValue.isNull()) + _xParameter->updateNull(nPos); + else + { + + switch(_rValue.getTypeKind()) + { + case DataType::DECIMAL: + case DataType::NUMERIC: + _xParameter->updateNumericObject(nPos,_rValue.makeAny(),m_xSetMetaData->getScale(nPos)); + break; + case DataType::CHAR: + case DataType::VARCHAR: + _xParameter->updateString(nPos,_rValue.getString()); + break; + case DataType::BIGINT: + if ( _rValue.isSigned() ) + _xParameter->updateLong(nPos,_rValue.getLong()); + else + _xParameter->updateString(nPos,_rValue.getString()); + break; + case DataType::BIT: + case DataType::BOOLEAN: + _xParameter->updateBoolean(nPos,_rValue.getBool()); + break; + case DataType::TINYINT: + if ( _rValue.isSigned() ) + _xParameter->updateByte(nPos,_rValue.getInt8()); + else + _xParameter->updateShort(nPos,_rValue.getInt16()); + break; + case DataType::SMALLINT: + if ( _rValue.isSigned() ) + _xParameter->updateShort(nPos,_rValue.getInt16()); + else + _xParameter->updateInt(nPos,_rValue.getInt32()); + break; + case DataType::INTEGER: + if ( _rValue.isSigned() ) + _xParameter->updateInt(nPos,_rValue.getInt32()); + else + _xParameter->updateLong(nPos,_rValue.getLong()); + break; + case DataType::FLOAT: + _xParameter->updateFloat(nPos,_rValue.getFloat()); + break; + case DataType::DOUBLE: + case DataType::REAL: + _xParameter->updateDouble(nPos,_rValue.getDouble()); + break; + case DataType::DATE: + _xParameter->updateDate(nPos,_rValue.getDate()); + break; + case DataType::TIME: + _xParameter->updateTime(nPos,_rValue.getTime()); + break; + case DataType::TIMESTAMP: + _xParameter->updateTimestamp(nPos,_rValue.getDateTime()); + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + _xParameter->updateBytes(nPos,_rValue.getSequence()); + break; + case DataType::BLOB: + case DataType::CLOB: + _xParameter->updateObject(nPos,_rValue.getAny()); + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/WrappedResultSet.hxx b/dbaccess/source/core/api/WrappedResultSet.hxx new file mode 100644 index 000000000..58b2d7041 --- /dev/null +++ b/dbaccess/source/core/api/WrappedResultSet.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#pragma once + +#include "CacheSet.hxx" +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> + +namespace dbaccess +{ + // this set is used when we have a bookmarkable set from the driver + class WrappedResultSet : public OCacheSet + { + css::uno::Reference< css::sdbcx::XRowLocate> m_xRowLocate; + css::uno::Reference< css::sdbc::XResultSetUpdate> m_xUpd; + css::uno::Reference< css::sdbc::XRowUpdate> m_xUpdRow; + + void updateColumn(sal_Int32 nPos, const css::uno::Reference< css::sdbc::XRowUpdate >& _xParameter, const connectivity::ORowSetValue& _rValue); + public: + explicit WrappedResultSet(sal_Int32 i_nMaxRows) : OCacheSet(i_nMaxRows) + {} + virtual ~WrappedResultSet() override + { + m_xRowLocate = nullptr; + } + + virtual void construct(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet,const OUString& i_sRowSetFilter) override; + virtual void reset(const css::uno::Reference< css::sdbc::XResultSet>& _xDriverSet) override; + // css::sdbcx::XRowLocate + virtual css::uno::Any getBookmark() override; + virtual bool moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Int32 compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual bool hasOrderedBookmarks( ) override; + virtual sal_Int32 hashBookmark( const css::uno::Any& bookmark ) override; + // css::sdbc::XResultSetUpdate + virtual void insertRow( const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + virtual void updateRow(const ORowSetRow& _rInsertRow,const ORowSetRow& _rOriginalRow,const connectivity::OSQLTable& _xTable ) override; + virtual void deleteRow(const ORowSetRow& _rInsertRow,const connectivity::OSQLTable& _xTable ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/callablestatement.cxx b/dbaccess/source/core/api/callablestatement.cxx new file mode 100644 index 000000000..8f864fbe0 --- /dev/null +++ b/dbaccess/source/core/api/callablestatement.cxx @@ -0,0 +1,250 @@ +/* -*- 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 . + */ + +#include <callablestatement.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <strings.hxx> + +using namespace dbaccess; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::cppu; +using namespace ::osl; + +// css::lang::XTypeProvider +Sequence< Type > OCallableStatement::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XRow>::get(), + cppu::UnoType<XOutParameters>::get(), + OPreparedStatement::getTypes() ); + + return aTypes.getTypes(); +} + +Sequence< sal_Int8 > OCallableStatement::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::uno::XInterface +Any OCallableStatement::queryInterface( const Type & rType ) +{ + Any aIface = OPreparedStatement::queryInterface( rType ); + if (!aIface.hasValue()) + aIface = ::cppu::queryInterface( + rType, + static_cast< XRow * >( this ), + static_cast< XOutParameters * >( this )); + return aIface; +} + +void OCallableStatement::acquire() noexcept +{ + OPreparedStatement::acquire(); +} + +void OCallableStatement::release() noexcept +{ + OPreparedStatement::release(); +} + +// XServiceInfo +OUString OCallableStatement::getImplementationName( ) +{ + return "com.sun.star.sdb.OCallableStatement"; +} + +Sequence< OUString > OCallableStatement::getSupportedServiceNames( ) +{ + return { SERVICE_SDBC_CALLABLESTATEMENT, SERVICE_SDB_CALLABLESTATEMENT }; +} + +// XOutParameters +void SAL_CALL OCallableStatement::registerOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) +{ + MutexGuard aGuard(m_aMutex); + + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + Reference< XOutParameters >(m_xAggregateAsSet, UNO_QUERY_THROW)->registerOutParameter( parameterIndex, sqlType, typeName ); +} + +void SAL_CALL OCallableStatement::registerNumericOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, sal_Int32 scale ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + Reference< XOutParameters >(m_xAggregateAsSet, UNO_QUERY_THROW)->registerNumericOutParameter( parameterIndex, sqlType, scale ); +} + +// XRow +sal_Bool SAL_CALL OCallableStatement::wasNull( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->wasNull(); +} + +OUString SAL_CALL OCallableStatement::getString( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getString( columnIndex ); +} + +sal_Bool SAL_CALL OCallableStatement::getBoolean( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getBoolean( columnIndex ); +} + +sal_Int8 SAL_CALL OCallableStatement::getByte( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getByte( columnIndex ); +} + +sal_Int16 SAL_CALL OCallableStatement::getShort( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getShort( columnIndex ); +} + +sal_Int32 SAL_CALL OCallableStatement::getInt( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getInt( columnIndex ); +} + +sal_Int64 SAL_CALL OCallableStatement::getLong( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getLong( columnIndex ); +} + +float SAL_CALL OCallableStatement::getFloat( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getFloat( columnIndex ); +} + +double SAL_CALL OCallableStatement::getDouble( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getDouble( columnIndex ); +} + +Sequence< sal_Int8 > SAL_CALL OCallableStatement::getBytes( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getBytes( columnIndex ); +} + +css::util::Date SAL_CALL OCallableStatement::getDate( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getDate( columnIndex ); +} + +css::util::Time SAL_CALL OCallableStatement::getTime( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getTime( columnIndex ); +} + +css::util::DateTime SAL_CALL OCallableStatement::getTimestamp( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getTimestamp( columnIndex ); +} + +Reference< css::io::XInputStream > SAL_CALL OCallableStatement::getBinaryStream( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getBinaryStream( columnIndex ); +} + +Reference< css::io::XInputStream > SAL_CALL OCallableStatement::getCharacterStream( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getCharacterStream( columnIndex ); +} + +Any SAL_CALL OCallableStatement::getObject( sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& typeMap ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getObject( columnIndex, typeMap ); +} + +Reference< XRef > SAL_CALL OCallableStatement::getRef( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getRef( columnIndex ); +} + +Reference< XBlob > SAL_CALL OCallableStatement::getBlob( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getBlob( columnIndex ); +} + +Reference< XClob > SAL_CALL OCallableStatement::getClob( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getClob( columnIndex ); +} + +Reference< XArray > SAL_CALL OCallableStatement::getArray( sal_Int32 columnIndex ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XRow >(m_xAggregateAsSet, UNO_QUERY_THROW)->getArray( columnIndex ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/column.cxx b/dbaccess/source/core/api/column.cxx new file mode 100644 index 000000000..5bc70affb --- /dev/null +++ b/dbaccess/source/core/api/column.cxx @@ -0,0 +1,411 @@ +/* -*- 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 . + */ + +#include <ContainerMediator.hxx> +#include <column.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <stringconstants.hxx> +#include <sdbcoretools.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdb/tools/XTableAlteration.hpp> + +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <comphelper/uno3.hxx> +#include <connectivity/TTableHelper.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +using namespace dbaccess; +using namespace connectivity; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + + +// OColumn +OColumn::OColumn( const bool _bNameIsReadOnly ) + :OColumnBase( m_aMutex ) + ,::comphelper::OPropertyContainer( OColumnBase::rBHelper ) +{ + + registerProperty( PROPERTY_NAME, PROPERTY_ID_NAME, _bNameIsReadOnly ? PropertyAttribute::READONLY : 0, + &m_sName, cppu::UnoType<decltype(m_sName)>::get() ); +} + +OColumn::~OColumn() +{ +} + +// css::lang::XTypeProvider +Sequence< Type > OColumn::getTypes() +{ + return ::comphelper::concatSequences( + OColumnBase::getTypes(), + getBaseTypes() + ); +} + +// css::uno::XInterface +IMPLEMENT_FORWARD_XINTERFACE2( OColumn, OColumnBase, ::comphelper::OPropertyContainer ) + +// css::lang::XServiceInfo +OUString OColumn::getImplementationName( ) +{ + return "com.sun.star.sdb.OColumn"; +} + +sal_Bool OColumn::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OColumn::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_COLUMN }; +} + +// OComponentHelper +void OColumn::disposing() +{ + OPropertyContainer::disposing(); +} + +// css::beans::XPropertySet +Reference< XPropertySetInfo > OColumn::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +OUString SAL_CALL OColumn::getName( ) +{ + return m_sName; +} + +void SAL_CALL OColumn::setName( const OUString& _rName ) +{ + m_sName = _rName; +} + +void OColumn::registerProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType ) +{ + ::comphelper::OPropertyContainer::registerProperty( _rName, _nHandle, _nAttributes, _pPointerToMember, _rMemberType ); +} + +void OColumn::registerMayBeVoidProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, Any* _pPointerToMember, const Type& _rExpectedType ) +{ + ::comphelper::OPropertyContainer::registerMayBeVoidProperty( _rName, _nHandle, _nAttributes, _pPointerToMember, _rExpectedType ); +} + +// OColumns + +OColumns::OColumns(::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + bool _bCaseSensitive,const std::vector< OUString> &_rVector, + IColumnFactory* _pColFactory, + ::connectivity::sdbcx::IRefreshableColumns* _pRefresh, + bool _bAddColumn, + bool _bDropColumn, + bool _bUseHardRef) + : OColumns_BASE(_rParent,_bCaseSensitive,_rMutex,_rVector,_bUseHardRef) + ,m_pMediator(nullptr) + ,m_pColFactoryImpl(_pColFactory) + ,m_pRefreshColumns(_pRefresh) + ,m_bInitialized(false) + ,m_bAddColumn(_bAddColumn) + ,m_bDropColumn(_bDropColumn) +{ +} + +OColumns::OColumns(::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, + const css::uno::Reference< css::container::XNameAccess >& _rxDrvColumns, + bool _bCaseSensitive,const std::vector< OUString> &_rVector, + IColumnFactory* _pColFactory, + ::connectivity::sdbcx::IRefreshableColumns* _pRefresh, + bool _bAddColumn, + bool _bDropColumn, + bool _bUseHardRef) + : OColumns_BASE(_rParent,_bCaseSensitive,_rMutex,_rVector,_bUseHardRef) + ,m_pMediator(nullptr) + ,m_xDrvColumns(_rxDrvColumns) + ,m_pColFactoryImpl(_pColFactory) + ,m_pRefreshColumns(_pRefresh) + ,m_bInitialized(false) + ,m_bAddColumn(_bAddColumn) + ,m_bDropColumn(_bDropColumn) +{ +} + +OColumns::~OColumns() +{ +} + +// XServiceInfo +OUString OColumns::getImplementationName( ) +{ + return "com.sun.star.sdb.OColumns"; +} + +sal_Bool OColumns::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OColumns::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_CONTAINER }; +} + +void OColumns::append( const OUString& _rName, OColumn* _pColumn ) +{ + MutexGuard aGuard(m_rMutex); + + OSL_ENSURE( _pColumn, "OColumns::append: invalid column!" ); + OSL_ENSURE( !m_pElements->exists( _rName ),"OColumns::append: Column already exists"); + + _pColumn->m_sName = _rName; + + // now really insert the column + insertElement( _rName, _pColumn ); +} + +void OColumns::clearColumns() +{ + MutexGuard aGuard(m_rMutex); + disposing(); +} + +void OColumns::disposing() +{ + MutexGuard aGuard(m_rMutex); + m_xDrvColumns = nullptr; + m_pMediator = nullptr; + m_pColFactoryImpl = nullptr; + OColumns_BASE::disposing(); +} + +void OColumns::impl_refresh() +{ + if (m_pRefreshColumns) + m_pRefreshColumns->refreshColumns(); +} + +connectivity::sdbcx::ObjectType OColumns::createObject(const OUString& _rName) +{ + OSL_ENSURE(m_pColFactoryImpl, "OColumns::createObject: no column factory!"); + + connectivity::sdbcx::ObjectType xRet; + if ( m_pColFactoryImpl ) + { + xRet = m_pColFactoryImpl->createColumn(_rName); + Reference<XChild> xChild(xRet,UNO_QUERY); + if ( xChild.is() ) + xChild->setParent(static_cast<XChild*>(static_cast<TXChild*>(this))); + } + + Reference<XPropertySet> xDest(xRet,UNO_QUERY); + if ( m_pMediator && xDest.is() ) + m_pMediator->notifyElementCreated(_rName,xDest); + + return xRet; +} + +Reference< XPropertySet > OColumns::createDescriptor() +{ + if ( m_pColFactoryImpl ) + { + Reference<XPropertySet> xRet = m_pColFactoryImpl->createColumnDescriptor(); + Reference<XChild> xChild(xRet,UNO_QUERY); + if ( xChild.is() ) + xChild->setParent(static_cast<XChild*>(static_cast<TXChild*>(this))); + return xRet; + } + else + return Reference< XPropertySet >(); +} + +Any SAL_CALL OColumns::queryInterface( const Type & rType ) +{ + Any aRet; + if(m_xDrvColumns.is()) + { + aRet = m_xDrvColumns->queryInterface(rType); + if ( aRet.hasValue() ) + aRet = OColumns_BASE::queryInterface( rType); + if ( !aRet.hasValue() ) + aRet = TXChild::queryInterface( rType); + return aRet; + } + else if(!m_pTable || !m_pTable->isNew()) + { + if(!m_bAddColumn && rType == cppu::UnoType<XAppend>::get()) + return Any(); + if(!m_bDropColumn && rType == cppu::UnoType<XDrop>::get()) + return Any(); + } + + aRet = OColumns_BASE::queryInterface( rType); + if ( !aRet.hasValue() ) + aRet = TXChild::queryInterface( rType); + return aRet; +} + +Sequence< Type > SAL_CALL OColumns::getTypes( ) +{ + bool bAppendFound = false,bDropFound = false; + + sal_Int32 nSize = 0; + Type aAppendType = cppu::UnoType<XAppend>::get(); + Type aDropType = cppu::UnoType<XDrop>::get(); + if(m_xDrvColumns.is()) + { + Reference<XTypeProvider> xTypes(m_xDrvColumns,UNO_QUERY); + Sequence< Type > aTypes(xTypes->getTypes()); + + const Type* pBegin = aTypes.getConstArray(); + const Type* pEnd = pBegin + aTypes.getLength(); + for (;pBegin != pEnd ; ++pBegin) + { + if(aAppendType == *pBegin) + bAppendFound = true; + else if(aDropType == *pBegin) + bDropFound = true; + } + nSize = (bDropFound ? (bAppendFound ? 0 : 1) : (bAppendFound ? 1 : 2)); + } + else + { + if (m_pTable && m_pTable->isNew()) + nSize = 0; + else if (m_bDropColumn) + nSize = m_bAddColumn ? 0 : 1; + else + nSize = m_bAddColumn ? 1 : 2; + bDropFound = (m_pTable && m_pTable->isNew()) || m_bDropColumn; + bAppendFound = (m_pTable && m_pTable->isNew()) || m_bAddColumn; + } + Sequence< Type > aTypes(::comphelper::concatSequences(OColumns_BASE::getTypes(),TXChild::getTypes())); + Sequence< Type > aRet(aTypes.getLength() - nSize); + + const Type* pBegin = aTypes.getConstArray(); + const Type* pEnd = pBegin + aTypes.getLength(); + for(sal_Int32 i=0;pBegin != pEnd ;++pBegin) + { + if(*pBegin != aAppendType && *pBegin != aDropType) + aRet.getArray()[i++] = *pBegin; + else if(bDropFound && *pBegin == aDropType) + aRet.getArray()[i++] = *pBegin; + else if(bAppendFound && *pBegin == aAppendType) + aRet.getArray()[i++] = *pBegin; + } + return aRet; +} + +// XAppend +sdbcx::ObjectType OColumns::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + sdbcx::ObjectType xReturn; + + Reference< XAppend > xAppend( m_xDrvColumns, UNO_QUERY ); + if ( xAppend.is() ) + { + xAppend->appendByDescriptor(descriptor); + xReturn = createObject( _rForName ); + } + else if ( m_pTable && !m_pTable->isNew() ) + { + if ( m_bAddColumn ) + { + Reference< css::sdb::tools::XTableAlteration> xAlterService = m_pTable->getAlterService(); + if ( xAlterService.is() ) + { + xAlterService->addColumn(m_pTable,descriptor); + xReturn = createObject( _rForName ); + } + else + xReturn = OColumns_BASE::appendObject( _rForName, descriptor ); + } + else + ::dbtools::throwGenericSQLException( DBA_RES( RID_STR_NO_COLUMN_ADD ), static_cast<XChild*>(static_cast<TXChild*>(this)) ); + } + else + xReturn = cloneDescriptor( descriptor ); + + if ( m_pColFactoryImpl ) + m_pColFactoryImpl->columnAppended( descriptor ); + + ::dbaccess::notifyDataSourceModified(m_xParent); + + return xReturn; +} + +// XDrop +void OColumns::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + Reference< XDrop > xDrop( m_xDrvColumns, UNO_QUERY ); + if ( xDrop.is() ) + { + xDrop->dropByName( _sElementName ); + } + else if ( m_pTable && !m_pTable->isNew() ) + { + if ( m_bDropColumn ) + { + Reference< css::sdb::tools::XTableAlteration> xAlterService = m_pTable->getAlterService(); + if ( xAlterService.is() ) + xAlterService->dropColumn(m_pTable,_sElementName); + else + OColumns_BASE::dropObject(_nPos,_sElementName); + } + else + ::dbtools::throwGenericSQLException( DBA_RES( RID_STR_NO_COLUMN_DROP ), static_cast<XChild*>(static_cast<TXChild*>(this)) ); + } + + if ( m_pColFactoryImpl ) + m_pColFactoryImpl->columnDropped(_sElementName); + + ::dbaccess::notifyDataSourceModified(m_xParent); +} + +Reference< XInterface > SAL_CALL OColumns::getParent( ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + return m_xParent; +} + +void SAL_CALL OColumns::setParent( const Reference< XInterface >& _xParent ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + m_xParent = _xParent; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/columnsettings.cxx b/dbaccess/source/core/api/columnsettings.cxx new file mode 100644 index 000000000..3f62a5a69 --- /dev/null +++ b/dbaccess/source/core/api/columnsettings.cxx @@ -0,0 +1,150 @@ +/* -*- 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 . + */ + +#include <columnsettings.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <comphelper/property.hxx> +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Type; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::XPropertySetInfo; + + namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; + + // OColumnSettings + + OColumnSettings::OColumnSettings() + :m_bHidden(false) + { + } + + OColumnSettings::~OColumnSettings() + { + } + + void OColumnSettings::registerProperties( IPropertyContainer& _rPropertyContainer ) + { + const sal_Int32 nBoundAttr = PropertyAttribute::BOUND; + const sal_Int32 nMayBeVoidAttr = PropertyAttribute::MAYBEVOID | nBoundAttr; + + const Type& rSalInt32Type = ::cppu::UnoType<sal_Int32>::get(); + const Type& rStringType = ::cppu::UnoType<OUString>::get(); + + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_ALIGN, PROPERTY_ID_ALIGN, nMayBeVoidAttr, &m_aAlignment, rSalInt32Type ); + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_NUMBERFORMAT, PROPERTY_ID_NUMBERFORMAT, nMayBeVoidAttr, &m_aFormatKey, rSalInt32Type ); + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_RELATIVEPOSITION, PROPERTY_ID_RELATIVEPOSITION, nMayBeVoidAttr, &m_aRelativePosition, rSalInt32Type ); + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_WIDTH, PROPERTY_ID_WIDTH, nMayBeVoidAttr, &m_aWidth, rSalInt32Type ); + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_HELPTEXT, PROPERTY_ID_HELPTEXT, nMayBeVoidAttr, &m_aHelpText, rStringType ); + _rPropertyContainer.registerMayBeVoidProperty( PROPERTY_CONTROLDEFAULT, PROPERTY_ID_CONTROLDEFAULT, nMayBeVoidAttr, &m_aControlDefault, rStringType ); + _rPropertyContainer.registerProperty( PROPERTY_CONTROLMODEL, PROPERTY_ID_CONTROLMODEL, nBoundAttr, &m_xControlModel, cppu::UnoType<decltype(m_xControlModel)>::get() ); + _rPropertyContainer.registerProperty( PROPERTY_HIDDEN, PROPERTY_ID_HIDDEN, nBoundAttr, &m_bHidden, cppu::UnoType<decltype(m_bHidden)>::get() ); + } + + bool OColumnSettings::isColumnSettingProperty( const sal_Int32 _nPropertyHandle ) + { + return ( _nPropertyHandle == PROPERTY_ID_ALIGN ) + || ( _nPropertyHandle == PROPERTY_ID_NUMBERFORMAT ) + || ( _nPropertyHandle == PROPERTY_ID_RELATIVEPOSITION ) + || ( _nPropertyHandle == PROPERTY_ID_WIDTH ) + || ( _nPropertyHandle == PROPERTY_ID_HELPTEXT ) + || ( _nPropertyHandle == PROPERTY_ID_CONTROLDEFAULT ) + || ( _nPropertyHandle == PROPERTY_ID_CONTROLMODEL ) + || ( _nPropertyHandle == PROPERTY_ID_HIDDEN ); + } + + bool OColumnSettings::isDefaulted( const sal_Int32 _nPropertyHandle, const Any& _rPropertyValue ) + { + switch ( _nPropertyHandle ) + { + case PROPERTY_ID_ALIGN: + case PROPERTY_ID_NUMBERFORMAT: + case PROPERTY_ID_RELATIVEPOSITION: + case PROPERTY_ID_WIDTH: + case PROPERTY_ID_HELPTEXT: + case PROPERTY_ID_CONTROLDEFAULT: + return !_rPropertyValue.hasValue(); + + case PROPERTY_ID_CONTROLMODEL: + return !Reference< XPropertySet >( _rPropertyValue, UNO_QUERY ).is(); + + case PROPERTY_ID_HIDDEN: + { + bool bHidden = false; + OSL_VERIFY( _rPropertyValue >>= bHidden ); + return !bHidden; + } + } + OSL_FAIL( "OColumnSettings::isDefaulted: illegal property handle!" ); + return false; + } + + bool OColumnSettings::hasDefaultSettings( const Reference< XPropertySet >& _rxColumn ) + { + ENSURE_OR_THROW( _rxColumn.is(), "illegal column" ); + try + { + Reference< XPropertySetInfo > xPSI( _rxColumn->getPropertySetInfo(), UNO_SET_THROW ); + + struct PropertyDescriptor + { + OUString sName; + sal_Int32 nHandle; + }; + const PropertyDescriptor aProps[] = + { + { OUString(PROPERTY_ALIGN), PROPERTY_ID_ALIGN }, + { OUString(PROPERTY_NUMBERFORMAT), PROPERTY_ID_NUMBERFORMAT }, + { OUString(PROPERTY_RELATIVEPOSITION), PROPERTY_ID_RELATIVEPOSITION }, + { OUString(PROPERTY_WIDTH), PROPERTY_ID_WIDTH }, + { OUString(PROPERTY_HELPTEXT), PROPERTY_ID_HELPTEXT }, + { OUString(PROPERTY_CONTROLDEFAULT), PROPERTY_ID_CONTROLDEFAULT }, + { OUString(PROPERTY_CONTROLMODEL), PROPERTY_ID_CONTROLMODEL }, + { OUString(PROPERTY_HIDDEN), PROPERTY_ID_HIDDEN } + }; + + for (const auto & aProp : aProps) + { + if ( xPSI->hasPropertyByName( aProp.sName ) ) + if ( !isDefaulted( aProp.nHandle, _rxColumn->getPropertyValue( aProp.sName ) ) ) + return false; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return true; + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/datacolumn.cxx b/dbaccess/source/core/api/datacolumn.cxx new file mode 100644 index 000000000..c597a4064 --- /dev/null +++ b/dbaccess/source/core/api/datacolumn.cxx @@ -0,0 +1,396 @@ +/* -*- 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 . + */ + +#include "datacolumn.hxx" +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <strings.hxx> + +using namespace dbaccess; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + + +ODataColumn::ODataColumn( + const Reference < XResultSetMetaData >& _xMetaData, + const Reference < XRow >& _xRow, + const Reference < XRowUpdate >& _xRowUpdate, + sal_Int32 _nPos, + const Reference< XDatabaseMetaData >& _rxDBMeta) + :OResultColumn(_xMetaData, _nPos, _rxDBMeta) + ,m_xRow(_xRow) + ,m_xRowUpdate(_xRowUpdate) +{ +} + +ODataColumn::~ODataColumn() +{ +} + +// css::lang::XTypeProvider +Sequence< Type > ODataColumn::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XColumn>::get(), + cppu::UnoType<XColumnUpdate>::get(), + OColumn::getTypes()); + return aTypes.getTypes(); +} + +Sequence< sal_Int8 > ODataColumn::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any SAL_CALL ODataColumn::queryInterface( const Type & _rType ) +{ + Any aReturn = OResultColumn::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = ::cppu::queryInterface(_rType, + static_cast< XColumn* >(this), + static_cast< XColumnUpdate* >(this) + ); + return aReturn; +} + +// XServiceInfo +OUString ODataColumn::getImplementationName( ) +{ + return "com.sun.star.sdb.ODataColumn"; +} + +Sequence< OUString > ODataColumn::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_COLUMN, SERVICE_SDB_RESULTCOLUMN, SERVICE_SDB_DATACOLUMN }; +} + +// OComponentHelper +void ODataColumn::disposing() +{ + OResultColumn::disposing(); + + m_xRow = nullptr; + m_xRowUpdate = nullptr; +} + +// css::sdb::XColumn +sal_Bool ODataColumn::wasNull() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->wasNull(); +} + +OUString ODataColumn::getString() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getString(m_nPos); +} + +sal_Bool ODataColumn::getBoolean() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getBoolean(m_nPos); +} + +sal_Int8 ODataColumn::getByte() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getByte(m_nPos); +} + +sal_Int16 ODataColumn::getShort() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getShort(m_nPos); +} + +sal_Int32 ODataColumn::getInt() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getInt(m_nPos); +} + +sal_Int64 ODataColumn::getLong() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getLong(m_nPos); +} + +float ODataColumn::getFloat() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getFloat(m_nPos); +} + +double ODataColumn::getDouble() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getDouble(m_nPos); +} + +Sequence< sal_Int8 > ODataColumn::getBytes() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getBytes(m_nPos); +} + +css::util::Date ODataColumn::getDate() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getDate(m_nPos); +} + +css::util::Time ODataColumn::getTime() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getTime(m_nPos); +} + +css::util::DateTime ODataColumn::getTimestamp() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getTimestamp(m_nPos); +} + +Reference< css::io::XInputStream > ODataColumn::getBinaryStream() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getBinaryStream(m_nPos); +} + +Reference< css::io::XInputStream > ODataColumn::getCharacterStream() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getCharacterStream(m_nPos); +} + +Any ODataColumn::getObject(const Reference< css::container::XNameAccess > & typeMap) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getObject(m_nPos, typeMap); +} + +Reference< XRef > ODataColumn::getRef() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getRef(m_nPos); +} + +Reference< XBlob > ODataColumn::getBlob() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getBlob(m_nPos); +} + +Reference< XClob > ODataColumn::getClob() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getClob(m_nPos); +} + +Reference< XArray > ODataColumn::getArray() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(!m_xRow.is()); + + return m_xRow->getArray(m_nPos); +} + +// css::sdb::XColumnUpdate +void ODataColumn::updateNull() +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateNull(m_nPos); +} + +void ODataColumn::updateBoolean(sal_Bool x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateBoolean(m_nPos, x); +} + +void ODataColumn::updateByte(sal_Int8 x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateByte(m_nPos, x); +} + +void ODataColumn::updateShort(sal_Int16 x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateShort(m_nPos, x); +} + +void ODataColumn::updateInt(sal_Int32 x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateInt(m_nPos, x); +} + +void ODataColumn::updateLong(sal_Int64 x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateLong(m_nPos, x); +} + +void ODataColumn::updateFloat(float x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateFloat(m_nPos, x); +} + +void ODataColumn::updateDouble(double x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateDouble(m_nPos, x); +} + +void ODataColumn::updateString(const OUString& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateString(m_nPos, x); +} + +void ODataColumn::updateBytes(const Sequence< sal_Int8 >& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateBytes(m_nPos, x); +} + +void ODataColumn::updateDate(const css::util::Date& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateDate(m_nPos, x); +} + +void ODataColumn::updateTime(const css::util::Time& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateTime(m_nPos, x); +} + +void ODataColumn::updateTimestamp(const css::util::DateTime& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateTimestamp(m_nPos, x); +} + +void ODataColumn::updateCharacterStream(const Reference< css::io::XInputStream > & x, sal_Int32 length) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateCharacterStream(m_nPos, x, length); +} + +void ODataColumn::updateBinaryStream(const Reference< css::io::XInputStream > & x, sal_Int32 length) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateBinaryStream(m_nPos, x, length); +} + +void ODataColumn::updateNumericObject(const Any& x, sal_Int32 scale) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateNumericObject(m_nPos, x, scale); +} + +void ODataColumn::updateObject(const Any& x) +{ + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(!m_xRowUpdate.is()); + + m_xRowUpdate->updateObject(m_nPos, x); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/datacolumn.hxx b/dbaccess/source/core/api/datacolumn.hxx new file mode 100644 index 000000000..46512d945 --- /dev/null +++ b/dbaccess/source/core/api/datacolumn.hxx @@ -0,0 +1,107 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdb/XColumnUpdate.hpp> +#include "resultcolumn.hxx" +namespace dbaccess +{ + + // ODataColumn + + class ODataColumn : public OResultColumn, + public css::sdb::XColumn, + public css::sdb::XColumnUpdate + { + css::uno::Reference < css::sdbc::XRow > m_xRow; + css::uno::Reference < css::sdbc::XRowUpdate > m_xRowUpdate; + protected: + virtual ~ODataColumn() override; + public: + ODataColumn (const css::uno::Reference < css::sdbc::XResultSetMetaData >& _xMetaData, + const css::uno::Reference < css::sdbc::XRow >& _xRow, + const css::uno::Reference < css::sdbc::XRowUpdate >& _xRowUpdate, + sal_Int32 _nPos, + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rxDBMeta); + + // css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override { OResultColumn::acquire(); } + virtual void SAL_CALL release() noexcept override { OResultColumn::release(); } + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::sdb::XColumn + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( ) override; + virtual sal_Bool SAL_CALL getBoolean( ) override; + virtual sal_Int8 SAL_CALL getByte( ) override; + virtual sal_Int16 SAL_CALL getShort( ) override; + virtual sal_Int32 SAL_CALL getInt( ) override; + virtual sal_Int64 SAL_CALL getLong( ) override; + virtual float SAL_CALL getFloat( ) override; + virtual double SAL_CALL getDouble( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( ) override; + virtual css::util::Date SAL_CALL getDate( ) override; + virtual css::util::Time SAL_CALL getTime( ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( ) override; + virtual css::uno::Any SAL_CALL getObject( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( ) override; + + // css::sdb::XColumnUpdate + virtual void SAL_CALL updateNull( ) override; + virtual void SAL_CALL updateBoolean( sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( float x ) override; + virtual void SAL_CALL updateDouble( double x ) override; + virtual void SAL_CALL updateString( const OUString& x ) override; + virtual void SAL_CALL updateBytes( const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( const css::uno::Any& x, sal_Int32 scale ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/datasettings.cxx b/dbaccess/source/core/api/datasettings.cxx new file mode 100644 index 000000000..e845f1135 --- /dev/null +++ b/dbaccess/source/core/api/datasettings.cxx @@ -0,0 +1,191 @@ +/* -*- 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 . + */ + +#include <datasettings.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/FontEmphasisMark.hpp> +#include <com/sun/star/awt/FontRelief.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ +// ODataSettings +void ODataSettings::registerPropertiesFor(ODataSettings_Base* _pItem) +{ + if ( m_bQuery ) + { + registerProperty(PROPERTY_HAVING_CLAUSE, PROPERTY_ID_HAVING_CLAUSE, PropertyAttribute::BOUND, + &_pItem->m_sHavingClause, cppu::UnoType<decltype(_pItem->m_sHavingClause)>::get()); + + registerProperty(PROPERTY_GROUP_BY, PROPERTY_ID_GROUP_BY, PropertyAttribute::BOUND, + &_pItem->m_sGroupBy, cppu::UnoType<decltype(_pItem->m_sGroupBy)>::get()); + } + + registerProperty(PROPERTY_FILTER, PROPERTY_ID_FILTER, PropertyAttribute::BOUND, + &_pItem->m_sFilter, cppu::UnoType<decltype(_pItem->m_sFilter)>::get()); + + registerProperty(PROPERTY_ORDER, PROPERTY_ID_ORDER, PropertyAttribute::BOUND, + &_pItem->m_sOrder, cppu::UnoType<decltype(_pItem->m_sOrder)>::get()); + + registerProperty(PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER, PropertyAttribute::BOUND, + &_pItem->m_bApplyFilter, cppu::UnoType<bool>::get()); + + registerProperty(PROPERTY_FONT, PROPERTY_ID_FONT, PropertyAttribute::BOUND, + &_pItem->m_aFont, cppu::UnoType<decltype(_pItem->m_aFont)>::get()); + + registerMayBeVoidProperty(PROPERTY_ROW_HEIGHT, PROPERTY_ID_ROW_HEIGHT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &_pItem->m_aRowHeight, ::cppu::UnoType<sal_Int32>::get()); + + registerProperty(PROPERTY_AUTOGROW, PROPERTY_ID_AUTOGROW, PropertyAttribute::BOUND, + &_pItem->m_bAutoGrow, cppu::UnoType<bool>::get()); + + registerMayBeVoidProperty(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &_pItem->m_aTextColor, ::cppu::UnoType<sal_Int32>::get()); + + registerMayBeVoidProperty(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &_pItem->m_aTextLineColor, ::cppu::UnoType<sal_Int32>::get()); + + registerProperty(PROPERTY_TEXTEMPHASIS, PROPERTY_ID_TEXTEMPHASIS, PropertyAttribute::BOUND, + &_pItem->m_nFontEmphasis, cppu::UnoType<decltype(_pItem->m_nFontEmphasis)>::get()); + + registerProperty(PROPERTY_TEXTRELIEF, PROPERTY_ID_TEXTRELIEF, PropertyAttribute::BOUND,&_pItem->m_nFontRelief, cppu::UnoType<decltype(_pItem->m_nFontRelief)>::get()); + + registerProperty(PROPERTY_FONTNAME, PROPERTY_ID_FONTNAME, PropertyAttribute::BOUND,&_pItem->m_aFont.Name, cppu::UnoType<decltype(_pItem->m_aFont.Name)>::get()); + registerProperty(PROPERTY_FONTHEIGHT, PROPERTY_ID_FONTHEIGHT, PropertyAttribute::BOUND,&_pItem->m_aFont.Height, cppu::UnoType<decltype(_pItem->m_aFont.Height)>::get()); + registerProperty(PROPERTY_FONTWIDTH, PROPERTY_ID_FONTWIDTH, PropertyAttribute::BOUND,&_pItem->m_aFont.Width, cppu::UnoType<decltype(_pItem->m_aFont.Width)>::get()); + registerProperty(PROPERTY_FONTSTYLENAME, PROPERTY_ID_FONTSTYLENAME, PropertyAttribute::BOUND,&_pItem->m_aFont.StyleName, cppu::UnoType<decltype(_pItem->m_aFont.StyleName)>::get()); + registerProperty(PROPERTY_FONTFAMILY, PROPERTY_ID_FONTFAMILY, PropertyAttribute::BOUND,&_pItem->m_aFont.Family, cppu::UnoType<decltype(_pItem->m_aFont.Family)>::get()); + registerProperty(PROPERTY_FONTCHARSET, PROPERTY_ID_FONTCHARSET, PropertyAttribute::BOUND,&_pItem->m_aFont.CharSet, cppu::UnoType<decltype(_pItem->m_aFont.CharSet)>::get()); + registerProperty(PROPERTY_FONTPITCH, PROPERTY_ID_FONTPITCH, PropertyAttribute::BOUND,&_pItem->m_aFont.Pitch, cppu::UnoType<decltype(_pItem->m_aFont.Pitch)>::get()); + registerProperty(PROPERTY_FONTCHARWIDTH, PROPERTY_ID_FONTCHARWIDTH, PropertyAttribute::BOUND,&_pItem->m_aFont.CharacterWidth, cppu::UnoType<decltype(_pItem->m_aFont.CharacterWidth)>::get()); + registerProperty(PROPERTY_FONTWEIGHT, PROPERTY_ID_FONTWEIGHT, PropertyAttribute::BOUND,&_pItem->m_aFont.Weight, cppu::UnoType<decltype(_pItem->m_aFont.Weight)>::get()); + registerProperty(PROPERTY_FONTSLANT, PROPERTY_ID_FONTSLANT, PropertyAttribute::BOUND,&_pItem->m_aFont.Slant, cppu::UnoType<decltype(_pItem->m_aFont.Slant)>::get()); + registerProperty(PROPERTY_FONTUNDERLINE, PROPERTY_ID_FONTUNDERLINE, PropertyAttribute::BOUND,&_pItem->m_aFont.Underline, cppu::UnoType<decltype(_pItem->m_aFont.Underline)>::get()); + registerProperty(PROPERTY_FONTSTRIKEOUT, PROPERTY_ID_FONTSTRIKEOUT, PropertyAttribute::BOUND,&_pItem->m_aFont.Strikeout, cppu::UnoType<decltype(_pItem->m_aFont.Strikeout)>::get()); + registerProperty(PROPERTY_FONTORIENTATION, PROPERTY_ID_FONTORIENTATION, PropertyAttribute::BOUND,&_pItem->m_aFont.Orientation, cppu::UnoType<decltype(_pItem->m_aFont.Orientation)>::get()); + registerProperty(PROPERTY_FONTKERNING, PROPERTY_ID_FONTKERNING, PropertyAttribute::BOUND,&_pItem->m_aFont.Kerning, cppu::UnoType<decltype(_pItem->m_aFont.Kerning)>::get()); + registerProperty(PROPERTY_FONTWORDLINEMODE, PROPERTY_ID_FONTWORDLINEMODE,PropertyAttribute::BOUND,&_pItem->m_aFont.WordLineMode, cppu::UnoType<decltype(_pItem->m_aFont.WordLineMode)>::get()); + registerProperty(PROPERTY_FONTTYPE, PROPERTY_ID_FONTTYPE, PropertyAttribute::BOUND,&_pItem->m_aFont.Type, cppu::UnoType<decltype(_pItem->m_aFont.Type)>::get()); +} + +ODataSettings::ODataSettings(OBroadcastHelper& _rBHelper,bool _bQuery) + :OPropertyStateContainer(_rBHelper) + ,m_bQuery(_bQuery) +{ +} + +ODataSettings_Base::ODataSettings_Base() + :m_bApplyFilter(false) + ,m_bAutoGrow(false) + ,m_aFont(::comphelper::getDefaultFont()) + ,m_nFontEmphasis(css::awt::FontEmphasisMark::NONE) + ,m_nFontRelief(css::awt::FontRelief::NONE) +{ +} + +ODataSettings_Base::~ODataSettings_Base() +{ +} + +void ODataSettings::getPropertyDefaultByHandle( sal_Int32 _nHandle, Any& _rDefault ) const +{ + static css::awt::FontDescriptor aFD = ::comphelper::getDefaultFont(); + switch( _nHandle ) + { + case PROPERTY_ID_HAVING_CLAUSE: + case PROPERTY_ID_GROUP_BY: + case PROPERTY_ID_FILTER: + case PROPERTY_ID_ORDER: + _rDefault <<= OUString(); + break; + case PROPERTY_ID_FONT: + _rDefault <<= ::comphelper::getDefaultFont(); + break; + case PROPERTY_ID_APPLYFILTER: + _rDefault <<= false; + break; + case PROPERTY_ID_TEXTRELIEF: + _rDefault <<= css::awt::FontRelief::NONE; + break; + case PROPERTY_ID_TEXTEMPHASIS: + _rDefault <<= css::awt::FontEmphasisMark::NONE; + break; + case PROPERTY_ID_FONTNAME: + _rDefault <<= aFD.Name; + break; + case PROPERTY_ID_FONTHEIGHT: + _rDefault <<= aFD.Height; + break; + case PROPERTY_ID_FONTWIDTH: + _rDefault <<= aFD.Width; + break; + case PROPERTY_ID_FONTSTYLENAME: + _rDefault <<= aFD.StyleName; + break; + case PROPERTY_ID_FONTFAMILY: + _rDefault <<= aFD.Family; + break; + case PROPERTY_ID_FONTCHARSET: + _rDefault <<= aFD.CharSet; + break; + case PROPERTY_ID_FONTPITCH: + _rDefault <<= aFD.Pitch; + break; + case PROPERTY_ID_FONTCHARWIDTH: + _rDefault <<= aFD.CharacterWidth; + break; + case PROPERTY_ID_FONTWEIGHT: + _rDefault <<= aFD.Weight; + break; + case PROPERTY_ID_FONTSLANT: + _rDefault <<= aFD.Slant; + break; + case PROPERTY_ID_FONTUNDERLINE: + _rDefault <<= aFD.Underline; + break; + case PROPERTY_ID_FONTSTRIKEOUT: + _rDefault <<= aFD.Strikeout; + break; + case PROPERTY_ID_FONTORIENTATION: + _rDefault <<= aFD.Orientation; + break; + case PROPERTY_ID_FONTKERNING: + _rDefault <<= aFD.Kerning; + break; + case PROPERTY_ID_FONTWORDLINEMODE: + _rDefault <<= aFD.WordLineMode; + break; + case PROPERTY_ID_FONTTYPE: + _rDefault <<= aFD.Type; + break; + } +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/definitioncolumn.cxx b/dbaccess/source/core/api/definitioncolumn.cxx new file mode 100644 index 000000000..3f9542180 --- /dev/null +++ b/dbaccess/source/core/api/definitioncolumn.cxx @@ -0,0 +1,610 @@ +/* -*- 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 . + */ + +#include <bitset> + +#include <stringconstants.hxx> +#include <strings.hxx> +#include <definitioncolumn.hxx> +#include <sdbcoretools.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> + +#include <comphelper/property.hxx> +#include <connectivity/dbtools.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::cppu; +using namespace ::comphelper; +using namespace ::osl; +using namespace dbaccess; + +namespace +{ + const sal_Int32 HAS_DESCRIPTION = 0x00000001; + const sal_Int32 HAS_DEFAULTVALUE = 0x00000002; + const sal_Int32 HAS_ROWVERSION = 0x00000004; + const sal_Int32 HAS_AUTOINCREMENT_CREATION = 0x00000008; + const sal_Int32 HAS_CATALOGNAME = 0x00000010; + const sal_Int32 HAS_SCHEMANAME = 0x00000020; + const sal_Int32 HAS_TABLENAME = 0x00000040; +} + +// OTableColumnDescriptor +IMPLEMENT_FORWARD_XINTERFACE2(OTableColumnDescriptor,OColumn,TXChild) + +void OTableColumnDescriptor::impl_registerProperties() +{ + sal_Int32 nDefaultAttr = m_bActAsDescriptor ? 0 : PropertyAttribute::READONLY; + + registerProperty( PROPERTY_TYPENAME, PROPERTY_ID_TYPENAME, nDefaultAttr, &m_aTypeName, cppu::UnoType<decltype(m_aTypeName)>::get() ); + registerProperty( PROPERTY_DESCRIPTION, PROPERTY_ID_DESCRIPTION, nDefaultAttr, &m_aDescription, cppu::UnoType<decltype(m_aDescription)>::get() ); + registerProperty( PROPERTY_DEFAULTVALUE, PROPERTY_ID_DEFAULTVALUE, nDefaultAttr, &m_aDefaultValue, cppu::UnoType<decltype(m_aDefaultValue)>::get() ); + + if ( m_bActAsDescriptor ) + registerProperty( PROPERTY_AUTOINCREMENTCREATION, PROPERTY_ID_AUTOINCREMENTCREATION, nDefaultAttr, &m_aAutoIncrementValue, cppu::UnoType<decltype(m_aAutoIncrementValue)>::get() ); + + registerProperty( PROPERTY_TYPE, PROPERTY_ID_TYPE, nDefaultAttr, &m_nType, cppu::UnoType<decltype(m_nType)>::get() ); + registerProperty( PROPERTY_PRECISION, PROPERTY_ID_PRECISION, nDefaultAttr, &m_nPrecision, cppu::UnoType<decltype(m_nPrecision)>::get() ); + registerProperty( PROPERTY_SCALE, PROPERTY_ID_SCALE, nDefaultAttr, &m_nScale, cppu::UnoType<decltype(m_nScale)>::get() ); + registerProperty( PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE, nDefaultAttr, &m_nIsNullable, cppu::UnoType<decltype(m_nIsNullable)>::get() ); + registerProperty( PROPERTY_ISAUTOINCREMENT, PROPERTY_ID_ISAUTOINCREMENT, nDefaultAttr, &m_bAutoIncrement, cppu::UnoType<decltype(m_bAutoIncrement)>::get() ); + registerProperty( PROPERTY_ISROWVERSION, PROPERTY_ID_ISROWVERSION, nDefaultAttr, &m_bRowVersion, cppu::UnoType<decltype(m_bRowVersion)>::get() ); + registerProperty( PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY, nDefaultAttr, &m_bCurrency, cppu::UnoType<decltype(m_bCurrency)>::get() ); + + OColumnSettings::registerProperties( *this ); +} + +IMPLEMENT_GET_IMPLEMENTATION_ID( OTableColumnDescriptor ) + +// css::lang::XServiceInfo +OUString OTableColumnDescriptor::getImplementationName( ) +{ + return "com.sun.star.sdb.OTableColumnDescriptor"; +} + +Sequence< OUString > OTableColumnDescriptor::getSupportedServiceNames( ) +{ + return { m_bActAsDescriptor? OUString(SERVICE_SDBCX_COLUMNDESCRIPTOR) : OUString(SERVICE_SDBCX_COLUMN), + SERVICE_SDB_COLUMNSETTINGS }; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OTableColumnDescriptor::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& OTableColumnDescriptor::getInfoHelper() +{ + return *static_cast< ::comphelper::OPropertyArrayUsageHelper< OTableColumnDescriptor >* >(this)->getArrayHelper(); +} + +void OTableColumnDescriptor::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + OColumn::setFastPropertyValue_NoBroadcast( nHandle, rValue ); + ::dbaccess::notifyDataSourceModified( m_xParent ); +} + +Reference< XInterface > SAL_CALL OTableColumnDescriptor::getParent( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + return m_xParent; +} + +void SAL_CALL OTableColumnDescriptor::setParent( const Reference< XInterface >& _xParent ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_xParent = _xParent; +} + +// OTableColumn + +OTableColumn::OTableColumn( const OUString& _rName ) + :OTableColumnDescriptor( false /* do not act as descriptor */ ) +{ + m_sName = _rName; +} + +OTableColumn::~OTableColumn() +{ +} + +IMPLEMENT_GET_IMPLEMENTATION_ID( OTableColumn ) + +OUString OTableColumn::getImplementationName( ) +{ + return "com.sun.star.sdb.OTableColumn"; +} + +::cppu::IPropertyArrayHelper& SAL_CALL OTableColumn::getInfoHelper() +{ + return *OTableColumn_PBase::getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OTableColumn::createArrayHelper( ) const +{ + return OTableColumnDescriptor::createArrayHelper(); +} + +// OQueryColumn + +OQueryColumn::OQueryColumn( const Reference< XPropertySet >& _rxParserColumn, const Reference< XConnection >& _rxConnection, const OUString &i_sLabel ) + :OTableColumnDescriptor( false /* do not act as descriptor */ ) + ,m_sLabel(i_sLabel) +{ + const sal_Int32 nPropAttr = PropertyAttribute::READONLY; + registerProperty( PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, nPropAttr, &m_sCatalogName, cppu::UnoType<decltype(m_sCatalogName)>::get() ); + registerProperty( PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, nPropAttr, &m_sSchemaName, cppu::UnoType<decltype(m_sSchemaName)>::get() ); + registerProperty( PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME, nPropAttr, &m_sTableName, cppu::UnoType<decltype(m_sTableName)>::get() ); + registerProperty( PROPERTY_REALNAME, PROPERTY_ID_REALNAME, nPropAttr, &m_sRealName, cppu::UnoType<decltype(m_sRealName)>::get() ); + registerProperty( PROPERTY_LABEL, PROPERTY_ID_LABEL, nPropAttr, &m_sLabel, cppu::UnoType<decltype(m_sLabel)>::get() ); + + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_TYPENAME ) >>= m_aTypeName) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_TYPENAME); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_ISNULLABLE ) >>= m_nIsNullable) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_ISNULLABLE); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_PRECISION ) >>= m_nPrecision) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_PRECISION); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_SCALE ) >>= m_nScale) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_SCALE); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_TYPE ) >>= m_nType) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_TYPE); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_ISAUTOINCREMENT ) >>= m_bAutoIncrement) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_ISAUTOINCREMENT); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_ISCURRENCY ) >>= m_bCurrency) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_ISCURRENCY); + + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_NAME ) >>= m_sName) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_NAME); + + m_bRowVersion = false; + + Reference< XPropertySetInfo > xPSI( _rxParserColumn->getPropertySetInfo(), UNO_SET_THROW ); + if ( xPSI->hasPropertyByName( PROPERTY_DEFAULTVALUE ) ) + if( ! (_rxParserColumn->getPropertyValue( PROPERTY_DEFAULTVALUE ) >>= m_aDefaultValue) ) + SAL_WARN("dbaccess.core", "OQueryColumn: unable to get property " PROPERTY_DEFAULTVALUE); + + // copy some optional properties from the parser column + struct PropertyDescriptor + { + OUString sName; + sal_Int32 nHandle; + }; + const PropertyDescriptor aProps[] = + { + { OUString(PROPERTY_CATALOGNAME), PROPERTY_ID_CATALOGNAME }, + { OUString(PROPERTY_SCHEMANAME), PROPERTY_ID_SCHEMANAME }, + { OUString(PROPERTY_TABLENAME), PROPERTY_ID_TABLENAME }, + { OUString(PROPERTY_REALNAME), PROPERTY_ID_REALNAME } + }; + for (const auto & aProp : aProps) + { + if ( xPSI->hasPropertyByName( aProp.sName ) ) + setFastPropertyValue_NoBroadcast( aProp.nHandle, _rxParserColumn->getPropertyValue( aProp.sName ) ); + } + + // determine the table column we're based on + osl_atomic_increment( &m_refCount ); + { + m_xOriginalTableColumn = impl_determineOriginalTableColumn( _rxConnection ); + } + osl_atomic_decrement( &m_refCount ); +} + +OQueryColumn::~OQueryColumn() +{ +} + +Reference< XPropertySet > OQueryColumn::impl_determineOriginalTableColumn( const Reference< XConnection >& _rxConnection ) +{ + OSL_PRECOND( _rxConnection.is(), "OQueryColumn::impl_determineOriginalTableColumn: illegal connection!" ); + if ( !_rxConnection.is() ) + return nullptr; + + Reference< XPropertySet > xOriginalTableColumn; + try + { + // determine the composed table name, plus the column name, as indicated by the + // respective properties + OUString sCatalog, sSchema, sTable; + if( ! (getPropertyValue( PROPERTY_CATALOGNAME ) >>= sCatalog) ) + SAL_WARN("dbaccess.core", "impl_determineOriginalTableColumn: unable to get property " PROPERTY_CATALOGNAME); + if( ! (getPropertyValue( PROPERTY_SCHEMANAME ) >>= sSchema) ) + SAL_WARN("dbaccess.core", "impl_determineOriginalTableColumn: unable to get property " PROPERTY_SCHEMANAME); + if( ! (getPropertyValue( PROPERTY_TABLENAME ) >>= sTable) ) + SAL_WARN("dbaccess.core", "impl_determineOriginalTableColumn: unable to get property " PROPERTY_TABLENAME); + if ( sCatalog.isEmpty() && sSchema.isEmpty() && sTable.isEmpty() ) + return nullptr; + + OUString sComposedTableName = ::dbtools::composeTableName( + _rxConnection->getMetaData(), sCatalog, sSchema, sTable, false, ::dbtools::EComposeRule::Complete ); + + // retrieve the table in question + Reference< XTablesSupplier > xSuppTables( _rxConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xTables( xSuppTables->getTables(), UNO_SET_THROW ); + if ( !xTables->hasByName( sComposedTableName ) ) + return nullptr; + + Reference< XColumnsSupplier > xSuppCols( xTables->getByName( sComposedTableName ), UNO_QUERY_THROW ); + Reference< XNameAccess > xColumns( xSuppCols->getColumns(), UNO_SET_THROW ); + + OUString sColumn; + if( ! (getPropertyValue( PROPERTY_REALNAME ) >>= sColumn) ) + SAL_WARN("dbaccess.core", "impl_determineOriginalTableColumn: unable to get property " PROPERTY_REALNAME); + if ( !xColumns->hasByName( sColumn ) ) + return nullptr; + + xOriginalTableColumn.set( xColumns->getByName( sColumn ), UNO_QUERY ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return xOriginalTableColumn; +} + +IMPLEMENT_GET_IMPLEMENTATION_ID( OQueryColumn ) + +OUString SAL_CALL OQueryColumn::getImplementationName( ) +{ + return "org.openoffice.comp.dbaccess.OQueryColumn"; +} + +::cppu::IPropertyArrayHelper& SAL_CALL OQueryColumn::getInfoHelper() +{ + return *OQueryColumn_PBase::getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OQueryColumn::createArrayHelper() const +{ + return OTableColumnDescriptor::createArrayHelper(); +} + +void SAL_CALL OQueryColumn::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const +{ + OTableColumnDescriptor::getFastPropertyValue( _rValue, _nHandle ); + + // special treatment for column settings: + if ( !OColumnSettings::isColumnSettingProperty( _nHandle ) ) + return; + + // If the setting has its default value, then try to obtain the value from the table column which + // this query column is based on + if ( !OColumnSettings::isDefaulted( _nHandle, _rValue ) ) + return; + + if ( !m_xOriginalTableColumn.is() ) + return; + + try + { + // determine original property name + OUString sPropName; + sal_Int16 nAttributes( 0 ); + const_cast< OQueryColumn* >( this )->getInfoHelper().fillPropertyMembersByHandle( &sPropName, &nAttributes, _nHandle ); + OSL_ENSURE( !sPropName.isEmpty(), "OColumnWrapper::impl_getPropertyNameFromHandle: property not found!" ); + + _rValue = m_xOriginalTableColumn->getPropertyValue( sPropName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +// OColumnWrapper + +OColumnWrapper::OColumnWrapper( const Reference< XPropertySet > & rCol, const bool _bNameIsReadOnly ) + :OColumn( _bNameIsReadOnly ) + ,m_xAggregate(rCol) + ,m_nColTypeID(-1) +{ + // which type of aggregate property do we have? + // we distinguish the properties by the containment of optional properties + m_nColTypeID = 0; + if ( !m_xAggregate.is() ) + return; + + Reference <XPropertySetInfo > xInfo(m_xAggregate->getPropertySetInfo()); + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_DESCRIPTION) ? HAS_DESCRIPTION : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_DEFAULTVALUE) ? HAS_DEFAULTVALUE : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_ISROWVERSION) ? HAS_ROWVERSION : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_AUTOINCREMENTCREATION) ? HAS_AUTOINCREMENT_CREATION : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_CATALOGNAME) ? HAS_CATALOGNAME : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_SCHEMANAME) ? HAS_SCHEMANAME : 0; + m_nColTypeID |= xInfo->hasPropertyByName(PROPERTY_TABLENAME) ? HAS_TABLENAME : 0; + + m_xAggregate->getPropertyValue(PROPERTY_NAME) >>= m_sName; +} + +OColumnWrapper::~OColumnWrapper() +{ +} + +OUString OColumnWrapper::impl_getPropertyNameFromHandle( const sal_Int32 _nHandle ) const +{ + OUString sPropName; + sal_Int16 nAttributes( 0 ); + const_cast< OColumnWrapper* >( this )->getInfoHelper().fillPropertyMembersByHandle( &sPropName, &nAttributes, _nHandle ); + OSL_ENSURE( !sPropName.isEmpty(), "OColumnWrapper::impl_getPropertyNameFromHandle: property not found!" ); + return sPropName; +} + +void OColumnWrapper::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + // derived classes are free to either use the OPropertyContainer(Helper) mechanisms for properties, + // or to declare additional properties which are to be forwarded to the wrapped object. So we need + // to distinguish those cases. + if ( OColumn::isRegisteredProperty( nHandle ) ) + { + OColumn::getFastPropertyValue( rValue, nHandle ); + } + else + { + rValue = m_xAggregate->getPropertyValue( impl_getPropertyNameFromHandle( nHandle ) ); + } +} + +sal_Bool OColumnWrapper::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, + const Any& rValue ) +{ + bool bModified( false ); + if ( OColumn::isRegisteredProperty( nHandle ) ) + { + bModified = OColumn::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue ); + } + else + { + getFastPropertyValue( rOldValue, nHandle ); + if ( rOldValue != rValue ) + { + rConvertedValue = rValue; + bModified = true; + } + } + return bModified; +} + +void OColumnWrapper::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + if ( OColumn::isRegisteredProperty( nHandle ) ) + { + OColumn::setFastPropertyValue_NoBroadcast( nHandle, rValue ); + } + else + { + m_xAggregate->setPropertyValue( impl_getPropertyNameFromHandle( nHandle ), rValue ); + } +} + +// OTableColumnDescriptorWrapper +OTableColumnDescriptorWrapper::OTableColumnDescriptorWrapper( const Reference< XPropertySet >& _rCol, const bool _bPureWrap, const bool _bIsDescriptor ) + :OColumnWrapper( _rCol, !_bIsDescriptor ) + ,m_bPureWrap( _bPureWrap ) + ,m_bIsDescriptor( _bIsDescriptor ) +{ + // let the ColumnSettings register its properties + OColumnSettings::registerProperties( *this ); +} + +// css::lang::XTypeProvider +IMPLEMENT_GET_IMPLEMENTATION_ID( OTableColumnDescriptorWrapper ) + +// css::lang::XServiceInfo +OUString OTableColumnDescriptorWrapper::getImplementationName( ) +{ + return "com.sun.star.sdb.OTableColumnDescriptorWrapper"; +} + +Sequence< OUString > OTableColumnDescriptorWrapper::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_COLUMNDESCRIPTOR, SERVICE_SDB_COLUMNSETTINGS }; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OTableColumnDescriptorWrapper::createArrayHelper( sal_Int32 nId ) const +{ + const sal_Int32 nHaveAlways = 7; + + // Which optional properties are contained? + const sal_Int32 nHaveOptionally (std::bitset<7>(nId).count()); + + css::uno::Sequence< css::beans::Property> aDescriptor(nHaveAlways + nHaveOptionally); + css::beans::Property* pDesc = aDescriptor.getArray(); + sal_Int32 nPos = 0; + + pDesc[nPos++] = css::beans::Property(PROPERTY_ISAUTOINCREMENT, PROPERTY_ID_ISAUTOINCREMENT, cppu::UnoType<bool>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY, cppu::UnoType<bool>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE, cppu::UnoType<sal_Int32>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_PRECISION, PROPERTY_ID_PRECISION, cppu::UnoType<sal_Int32>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_SCALE, PROPERTY_ID_SCALE, cppu::UnoType<sal_Int32>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_TYPE, PROPERTY_ID_TYPE, cppu::UnoType<sal_Int32>::get(), 0); + pDesc[nPos++] = css::beans::Property(PROPERTY_TYPENAME, PROPERTY_ID_TYPENAME, cppu::UnoType<OUString >::get(), 0); + + if ( nId & HAS_AUTOINCREMENT_CREATION ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_AUTOINCREMENTCREATION, PROPERTY_ID_AUTOINCREMENTCREATION, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID ); + } + if ( nId & HAS_DEFAULTVALUE ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_DEFAULTVALUE, PROPERTY_ID_DEFAULTVALUE, cppu::UnoType<OUString >::get(), 0); + } + if ( nId & HAS_DESCRIPTION ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_DESCRIPTION, PROPERTY_ID_DESCRIPTION, cppu::UnoType<OUString >::get(), 0); + } + if ( nId & HAS_ROWVERSION ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_ISROWVERSION, PROPERTY_ID_ISROWVERSION, cppu::UnoType<bool>::get(), 0); + } + if ( nId & HAS_CATALOGNAME ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, cppu::UnoType<OUString >::get(), 0); + } + if ( nId & HAS_SCHEMANAME ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, cppu::UnoType<OUString >::get(), 0); + } + if ( nId & HAS_TABLENAME ) + { + pDesc[nPos++] = css::beans::Property(PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME, cppu::UnoType<OUString >::get(), 0); + } + + OSL_ENSURE(nPos == aDescriptor.getLength(), "forgot to adjust the count ?"); + + if ( !m_bIsDescriptor ) + { + for ( auto & prop : asNonConstRange(aDescriptor) ) + { + prop.Attributes |= PropertyAttribute::READONLY; + } + } + + // finally also describe the properties which are maintained by our base class, in particular the OPropertyContainerHelper + Sequence< Property > aBaseProperties; + describeProperties( aBaseProperties ); + + Sequence< Property > aAllProperties( ::comphelper::concatSequences( aDescriptor, aBaseProperties ) ); + return new ::cppu::OPropertyArrayHelper( aAllProperties, false ); +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& OTableColumnDescriptorWrapper::getInfoHelper() +{ + return *static_cast< OIdPropertyArrayUsageHelper< OTableColumnDescriptorWrapper >* >(this)->getArrayHelper(m_nColTypeID); +} + +void OTableColumnDescriptorWrapper::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + if ( m_bPureWrap ) + { + rValue = m_xAggregate->getPropertyValue( impl_getPropertyNameFromHandle( nHandle ) ); + } + else + { + OColumnWrapper::getFastPropertyValue( rValue, nHandle ); + } +} + +sal_Bool OTableColumnDescriptorWrapper::convertFastPropertyValue( Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + bool bModified(false); + if ( m_bPureWrap ) + { + // do not delegate to OColumnWrapper: It would, for the properties which were registered with registerProperty, + // ask the OPropertyContainer base class, which is not what we want here. + // TODO: the whole "m_bPureWrap"-thingie is strange. We should have a dedicated class doing this wrapping, + // not a class which normally serves other purposes, and only sometimes does a "pure wrap". It makes the + // code unnecessarily hard to maintain, and error prone. + rOldValue = m_xAggregate->getPropertyValue( impl_getPropertyNameFromHandle( nHandle ) ); + if ( rOldValue != rValue ) + { + rConvertedValue = rValue; + bModified = true; + } + } + else + { + bModified = OColumnWrapper::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue ); + } + return bModified; +} + +void OTableColumnDescriptorWrapper::setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const Any& rValue + ) +{ + if ( m_bPureWrap ) + { + m_xAggregate->setPropertyValue( impl_getPropertyNameFromHandle( nHandle ), rValue ); + } + else + { + OColumnWrapper::setFastPropertyValue_NoBroadcast( nHandle, rValue ); + } +} + +// OTableColumnWrapper +OTableColumnWrapper::OTableColumnWrapper( const Reference< XPropertySet >& rCol, const Reference< XPropertySet >& _xColDefinition, + const bool _bPureWrap ) + :OTableColumnDescriptorWrapper( rCol, _bPureWrap, false ) +{ + osl_atomic_increment( &m_refCount ); + if ( _xColDefinition.is() ) + { + try + { + ::comphelper::copyProperties( _xColDefinition, this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + osl_atomic_decrement( &m_refCount ); +} + +OTableColumnWrapper::~OTableColumnWrapper() +{ +} + +IMPLEMENT_GET_IMPLEMENTATION_ID( OTableColumnWrapper ) + +OUString OTableColumnWrapper::getImplementationName( ) +{ + return "com.sun.star.sdb.OTableColumnWrapper"; +} + +Sequence< OUString > OTableColumnWrapper::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_COLUMN, SERVICE_SDB_COLUMNSETTINGS }; +} + +::cppu::IPropertyArrayHelper& OTableColumnWrapper::getInfoHelper() +{ + return *static_cast< OIdPropertyArrayUsageHelper< OTableColumnWrapper >* >(this)->getArrayHelper(m_nColTypeID); +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OTableColumnWrapper::createArrayHelper( sal_Int32 nId ) const +{ + return OTableColumnDescriptorWrapper::createArrayHelper( nId ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/preparedstatement.cxx b/dbaccess/source/core/api/preparedstatement.cxx new file mode 100644 index 000000000..02dd32379 --- /dev/null +++ b/dbaccess/source/core/api/preparedstatement.cxx @@ -0,0 +1,412 @@ +/* -*- 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 . + */ + + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <connectivity/dbtools.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <preparedstatement.hxx> +#include <strings.hxx> +#include "resultcolumn.hxx" +#include "resultset.hxx" +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::cppu; +using namespace ::osl; +using namespace dbaccess; + + +OPreparedStatement::OPreparedStatement(const Reference< XConnection > & _xConn, + const Reference< XInterface > & _xStatement) + :OStatementBase(_xConn, _xStatement) +{ + m_xAggregateAsParameters.set( m_xAggregateAsSet, UNO_QUERY_THROW ); + + Reference<XDatabaseMetaData> xMeta = _xConn->getMetaData(); + m_pColumns.reset( new OColumns(*this, m_aMutex, xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(),std::vector< OUString>(), nullptr,nullptr) ); +} + +OPreparedStatement::~OPreparedStatement() +{ + m_pColumns->acquire(); + m_pColumns->disposing(); +} + +// css::lang::XTypeProvider +Sequence< Type > OPreparedStatement::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XServiceInfo>::get(), + cppu::UnoType<XPreparedStatement>::get(), + cppu::UnoType<XParameters>::get(), + cppu::UnoType<XResultSetMetaDataSupplier>::get(), + cppu::UnoType<XColumnsSupplier>::get(), + OStatementBase::getTypes() ); + + return aTypes.getTypes(); +} + +Sequence< sal_Int8 > OPreparedStatement::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::uno::XInterface +Any OPreparedStatement::queryInterface( const Type & rType ) +{ + Any aIface = OStatementBase::queryInterface( rType ); + if (!aIface.hasValue()) + aIface = ::cppu::queryInterface( + rType, + static_cast< XServiceInfo * >( this ), + static_cast< XParameters * >( this ), + static_cast< XColumnsSupplier * >( this ), + static_cast< XResultSetMetaDataSupplier * >( this ), + static_cast< XPreparedBatchExecution * >( this ), + static_cast< XMultipleResults * >( this ), + static_cast< XPreparedStatement * >( this )); + return aIface; +} + +void OPreparedStatement::acquire() noexcept +{ + OStatementBase::acquire(); +} + +void OPreparedStatement::release() noexcept +{ + OStatementBase::release(); +} + +// XServiceInfo +OUString OPreparedStatement::getImplementationName( ) +{ + return "com.sun.star.sdb.OPreparedStatement"; +} + +sal_Bool OPreparedStatement::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OPreparedStatement::getSupportedServiceNames( ) +{ + return { SERVICE_SDBC_PREPAREDSTATEMENT, SERVICE_SDB_PREPAREDSTATMENT }; +} + +// OComponentHelper +void OPreparedStatement::disposing() +{ + { + MutexGuard aGuard(m_aMutex); + m_pColumns->disposing(); + m_xAggregateAsParameters = nullptr; + } + OStatementBase::disposing(); +} + +// css::sdbcx::XColumnsSupplier +Reference< css::container::XNameAccess > OPreparedStatement::getColumns() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // do we have to populate the columns + if (!m_pColumns->isInitialized()) + { + try + { + Reference< XResultSetMetaDataSupplier > xSuppMeta( m_xAggregateAsSet, UNO_QUERY_THROW ); + Reference< XResultSetMetaData > xMetaData( xSuppMeta->getMetaData(), UNO_SET_THROW ); + + Reference< XConnection > xConn( getConnection(), UNO_SET_THROW ); + Reference< XDatabaseMetaData > xDBMeta( xConn->getMetaData(), UNO_SET_THROW ); + + for (sal_Int32 i = 0, nCount = xMetaData->getColumnCount(); i < nCount; ++i) + { + // retrieve the name of the column + OUString aName = xMetaData->getColumnName(i + 1); + rtl::Reference<OResultColumn> pColumn = new OResultColumn(xMetaData, i + 1, xDBMeta); + // don't silently assume that the name is unique - preparedStatement implementations + // are allowed to return duplicate names, but we are required to have + // unique column names + if ( m_pColumns->hasByName( aName ) ) + aName = ::dbtools::createUniqueName( m_pColumns.get(), aName ); + + m_pColumns->append(aName, pColumn.get()); + } + } + catch (const SQLException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_pColumns->setInitialized(); + } + return m_pColumns.get(); +} + +// XResultSetMetaDataSupplier +Reference< XResultSetMetaData > OPreparedStatement::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + return Reference< XResultSetMetaDataSupplier >( m_xAggregateAsSet, UNO_QUERY_THROW )->getMetaData(); +} + +// XPreparedStatement +Reference< XResultSet > OPreparedStatement::executeQuery() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + + Reference< XResultSet > xResultSet; + Reference< XResultSet > xDrvResultSet = Reference< XPreparedStatement >( m_xAggregateAsSet, UNO_QUERY_THROW )->executeQuery(); + if (xDrvResultSet.is()) + { + xResultSet = new OResultSet(xDrvResultSet, *this, m_pColumns->isCaseSensitive()); + + // keep the resultset weak + m_aResultSet = xResultSet; + } + return xResultSet; +} + +sal_Int32 OPreparedStatement::executeUpdate() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + + return Reference< XPreparedStatement >( m_xAggregateAsSet, UNO_QUERY_THROW )->executeUpdate(); +} + +sal_Bool OPreparedStatement::execute() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + + return Reference< XPreparedStatement >( m_xAggregateAsSet, UNO_QUERY_THROW )->execute(); +} + +Reference< XConnection > OPreparedStatement::getConnection() +{ + return Reference< XConnection > (m_xParent, UNO_QUERY); +} + +// XParameters +void SAL_CALL OPreparedStatement::setNull( sal_Int32 parameterIndex, sal_Int32 sqlType ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setNull(parameterIndex, sqlType); +} + +void SAL_CALL OPreparedStatement::setObjectNull( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setObjectNull(parameterIndex, sqlType, typeName); +} + +void SAL_CALL OPreparedStatement::setBoolean( sal_Int32 parameterIndex, sal_Bool x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setBoolean(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setByte( sal_Int32 parameterIndex, sal_Int8 x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setByte(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setShort( sal_Int32 parameterIndex, sal_Int16 x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setShort(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setInt( sal_Int32 parameterIndex, sal_Int32 x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setInt(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setLong( sal_Int32 parameterIndex, sal_Int64 x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setLong(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setFloat( sal_Int32 parameterIndex, float x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setFloat(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setDouble( sal_Int32 parameterIndex, double x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setDouble(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setString( sal_Int32 parameterIndex, const OUString& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setString(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setBytes( sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setBytes(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setDate( sal_Int32 parameterIndex, const css::util::Date& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setDate(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setTime( sal_Int32 parameterIndex, const css::util::Time& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setTime(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setTimestamp( sal_Int32 parameterIndex, const css::util::DateTime& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setTimestamp(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setBinaryStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setBinaryStream(parameterIndex, x, length); +} + +void SAL_CALL OPreparedStatement::setCharacterStream( sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setCharacterStream(parameterIndex, x, length); +} + +void SAL_CALL OPreparedStatement::setObject( sal_Int32 parameterIndex, const Any& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setObject(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setObjectWithInfo( sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 scale ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setObjectWithInfo(parameterIndex, x, targetSqlType, scale); +} + +void SAL_CALL OPreparedStatement::setRef( sal_Int32 parameterIndex, const Reference< XRef >& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setRef(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setBlob( sal_Int32 parameterIndex, const Reference< XBlob >& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setBlob(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setClob( sal_Int32 parameterIndex, const Reference< XClob >& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setClob(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::setArray( sal_Int32 parameterIndex, const Reference< XArray >& x ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->setArray(parameterIndex, x); +} + +void SAL_CALL OPreparedStatement::clearParameters( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + m_xAggregateAsParameters->clearParameters(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/query.cxx b/dbaccess/source/core/api/query.cxx new file mode 100644 index 000000000..33ceca9ce --- /dev/null +++ b/dbaccess/source/core/api/query.cxx @@ -0,0 +1,383 @@ +/* -*- 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 . + */ + +#include "query.hxx" +#include <stringconstants.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/PColumn.hxx> +#include <connectivity/warningscontainer.hxx> +#include "HelperCollections.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> + +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> + +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> + +#include <comphelper/property.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <definitioncolumn.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <ContainerMediator.hxx> + +using namespace dbaccess; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::comphelper; +using namespace ::osl; +using namespace ::cppu; +using namespace ::utl; + +namespace dbaccess +{ + +// OQuery + +OQuery::OQuery( const Reference< XPropertySet >& _rxCommandDefinition + ,const Reference< XConnection >& _rxConn + ,const Reference< XComponentContext >& _xORB) + :OContentHelper(_xORB,nullptr,std::make_shared<OContentHelper_Impl>()) + ,OQueryDescriptor_Base(m_aMutex,*this) + ,ODataSettings(OContentHelper::rBHelper,true) + ,m_xCommandDefinition(_rxCommandDefinition) + ,m_xConnection(_rxConn) + ,m_pWarnings( nullptr ) + ,m_eDoingCurrently(AggregateAction::NONE) +{ + registerProperties(); + ODataSettings::registerPropertiesFor(this); + + osl_atomic_increment(&m_refCount); + OSL_ENSURE(m_xCommandDefinition.is(), "OQuery::OQuery : invalid CommandDefinition object !"); + if ( m_xCommandDefinition.is() ) + { + try + { + ::comphelper::copyProperties(_rxCommandDefinition,this); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "OQueryDescriptor_Base::OQueryDescriptor_Base"); + } + + m_xCommandDefinition->addPropertyChangeListener(OUString(), this); + // m_xCommandDefinition->addPropertyChangeListener(PROPERTY_NAME, this); + m_xCommandPropInfo = m_xCommandDefinition->getPropertySetInfo(); + } + OSL_ENSURE(m_xConnection.is(), "OQuery::OQuery : invalid connection !"); + osl_atomic_decrement(&m_refCount); +} + +OQuery::~OQuery() +{ +} + +css::uno::Sequence<sal_Int8> OQuery::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > OQuery::getTypes() +{ + return ::comphelper::concatSequences( + OQueryDescriptor_Base::getTypes( ), + ODataSettings::getTypes( ), + OContentHelper::getTypes( ) + ); +} + +IMPLEMENT_FORWARD_XINTERFACE3( OQuery,OContentHelper,OQueryDescriptor_Base,ODataSettings) + +void OQuery::rebuildColumns() +{ + OSL_PRECOND( getColumnCount() == 0, "OQuery::rebuildColumns: column container should be empty!" ); + // the base class' definition of rebuildColumns promised that clearColumns is called before rebuildColumns + + try + { + m_pColumnMediator = nullptr; + + Reference<XColumnsSupplier> xColSup(m_xCommandDefinition,UNO_QUERY); + Reference< XNameAccess > xColumnDefinitions; + if ( xColSup.is() ) + { + xColumnDefinitions = xColSup->getColumns(); + if ( xColumnDefinitions.is() ) + m_pColumnMediator = new OContainerMediator( m_pColumns.get(), xColumnDefinitions ); + } + + // fill the columns with columns from the statement + Reference< XMultiServiceFactory > xFactory( m_xConnection, UNO_QUERY_THROW ); + SharedUNOComponent< XSingleSelectQueryComposer, DisposableComponent > xComposer( + Reference< XSingleSelectQueryComposer >( xFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ) ); + + Reference< XNameAccess > xColumns; + Reference< XIndexAccess > xColumnsIndexed; + try + { + xComposer->setQuery( m_sCommand ); + Reference< XColumnsSupplier > xCols( xComposer, UNO_QUERY_THROW ); + xColumns.set( xCols->getColumns(), UNO_SET_THROW ); + xColumnsIndexed.set( xColumns, UNO_QUERY_THROW ); + } + catch( const SQLException& ) { } + + SharedUNOComponent< XPreparedStatement, DisposableComponent > xPreparedStatement; + if ( !xColumns.is() || ( xColumnsIndexed->getCount() == 0 ) ) + { // the QueryComposer could not parse it. Try a lean version. + xPreparedStatement.set( m_xConnection->prepareStatement( m_sCommand ), UNO_QUERY_THROW ); + Reference< XResultSetMetaDataSupplier > xResMetaDataSup( xPreparedStatement, UNO_QUERY_THROW ); + Reference< XResultSetMetaData > xResultSetMeta( xResMetaDataSup->getMetaData() ); + if ( !xResultSetMeta.is() ) + { + OUString sError( DBA_RES( RID_STR_STATEMENT_WITHOUT_RESULT_SET ) ); + ::dbtools::throwSQLException( sError, StandardSQLState::GENERAL_ERROR, *this ); + } + + Reference< XDatabaseMetaData > xDBMeta( m_xConnection->getMetaData(), UNO_SET_THROW ); + ::rtl::Reference< OSQLColumns > aParseColumns( + ::connectivity::parse::OParseColumn::createColumnsForResultSet( xResultSetMeta, xDBMeta,xColumnDefinitions ) ); + xColumns = OPrivateColumns::createWithIntrinsicNames( + aParseColumns, xDBMeta->supportsMixedCaseQuotedIdentifiers(), *this, m_aMutex ).release(); + if ( !xColumns.is() ) + throw RuntimeException(); + } + + const Sequence<OUString> aColNames = xColumns->getElementNames(); + for ( const OUString& rName : aColNames ) + { + Reference<XPropertySet> xSource(xColumns->getByName( rName ),UNO_QUERY); + OUString sLabel = rName; + if ( xColumnDefinitions.is() && xColumnDefinitions->hasByName(rName) ) + { + Reference<XPropertySet> xCommandColumn(xColumnDefinitions->getByName( rName ),UNO_QUERY); + xCommandColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel; + } + rtl::Reference<OQueryColumn> pColumn = new OQueryColumn( xSource, m_xConnection, sLabel); + pColumn->setParent( *this ); + + implAppendColumn( rName, pColumn.get() ); + Reference< XPropertySet > xDest( *pColumn, UNO_QUERY_THROW ); + if ( m_pColumnMediator.is() ) + m_pColumnMediator->notifyElementCreated( rName, xDest ); + } + } + catch( const SQLContext& e ) + { + if ( m_pWarnings ) + m_pWarnings->appendWarning( e ); + } + catch( const SQLWarning& e ) + { + if ( m_pWarnings ) + m_pWarnings->appendWarning( e ); + } + catch( const SQLException& e ) + { + if ( m_pWarnings ) + m_pWarnings->appendWarning( e ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +// XServiceInfo +OUString SAL_CALL OQuery::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.OQuery"; + } +sal_Bool SAL_CALL OQuery::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OQuery::getSupportedServiceNames( ) +{ + return { SERVICE_SDB_DATASETTINGS, SERVICE_SDB_QUERY, "com.sun.star.sdb.QueryDefinition" }; +} + +// css::beans::XPropertyChangeListener +void SAL_CALL OQuery::propertyChange( const PropertyChangeEvent& _rSource ) +{ + sal_Int32 nOwnHandle = -1; + { + MutexGuard aGuard(m_aMutex); + + OSL_ENSURE(_rSource.Source.get() == Reference< XInterface >(m_xCommandDefinition, UNO_QUERY).get(), + "OQuery::propertyChange : where did this call come from ?"); + + if (m_eDoingCurrently == AggregateAction::SettingProperties) + // we're setting the property ourself, so we will do the necessary notifications later + return; + + // forward this to our own member holding a copy of the property value + if (getArrayHelper()->hasPropertyByName(_rSource.PropertyName)) + { + Property aOwnProp = getArrayHelper()->getPropertyByName(_rSource.PropertyName); + nOwnHandle = aOwnProp.Handle; + ODataSettings::setFastPropertyValue_NoBroadcast(nOwnHandle, _rSource.NewValue); + // don't use our own setFastPropertyValue_NoBroadcast, this would forward it to the CommandSettings, + // again + // and don't use the "real" setPropertyValue, this is too expensive and not sure to succeed + } + else + { + OSL_FAIL("OQuery::propertyChange : my CommandDefinition has more properties than I do !"); + } + } + + fire(&nOwnHandle, &_rSource.NewValue, &_rSource.OldValue, 1, false); +} + +void SAL_CALL OQuery::disposing( const EventObject& _rSource ) +{ + MutexGuard aGuard(m_aMutex); + + OSL_ENSURE(_rSource.Source.get() == Reference< XInterface >(m_xCommandDefinition, UNO_QUERY).get(), + "OQuery::disposing : where did this call come from ?"); + + m_xCommandDefinition->removePropertyChangeListener(OUString(), this); + m_xCommandDefinition = nullptr; +} + +// XDataDescriptorFactory +Reference< XPropertySet > SAL_CALL OQuery::createDataDescriptor( ) +{ + return new OQueryDescriptor(*this); +} + +// pseudo-XComponent +void SAL_CALL OQuery::disposing() +{ + MutexGuard aGuard(m_aMutex); + if (m_xCommandDefinition.is()) + { + m_xCommandDefinition->removePropertyChangeListener(OUString(), this); + m_xCommandDefinition = nullptr; + } + disposeColumns(); + + m_pWarnings = nullptr; +} + +void OQuery::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) +{ + ODataSettings::setFastPropertyValue_NoBroadcast(_nHandle, _rValue); + OUString sAggPropName; + sal_Int16 nAttr = 0; + if (getInfoHelper().fillPropertyMembersByHandle(&sAggPropName,&nAttr,_nHandle) && + m_xCommandPropInfo.is() && + m_xCommandPropInfo->hasPropertyByName(sAggPropName)) + { // the base class holds the property values itself, but we have to forward this to our CommandDefinition + + m_eDoingCurrently = AggregateAction::SettingProperties; + OAutoActionReset aAutoReset(*this); + m_xCommandDefinition->setPropertyValue(sAggPropName, _rValue); + + if ( PROPERTY_ID_COMMAND == _nHandle ) + // the columns are out of date if we are based on a new statement... + setColumnsOutOfDate(); + } +} + +Reference< XPropertySetInfo > SAL_CALL OQuery::getPropertySetInfo( ) +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +::cppu::IPropertyArrayHelper& OQuery::getInfoHelper() +{ + return *getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OQuery::createArrayHelper( ) const +{ + Sequence< Property > aProps; + // our own props + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + +rtl::Reference<OColumn> OQuery::createColumn(const OUString& /*_rName*/) const +{ + return nullptr; +} + +void SAL_CALL OQuery::rename( const OUString& newName ) +{ + MutexGuard aGuard(m_aMutex); + Reference<XRename> xRename(m_xCommandDefinition,UNO_QUERY); + OSL_ENSURE(xRename.is(),"No XRename interface!"); + if(xRename.is()) + xRename->rename(newName); +} + +void OQuery::registerProperties() +{ + // the properties which OCommandBase supplies (it has no own registration, as it's not derived from + // an OPropertyStateContainer) + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND|PropertyAttribute::CONSTRAINED, + &m_sElementName, cppu::UnoType<decltype(m_sElementName)>::get()); + + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, + &m_sCommand, cppu::UnoType<decltype(m_sCommand)>::get()); + + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, + &m_bEscapeProcessing, cppu::UnoType<bool>::get()); + + registerProperty(PROPERTY_UPDATE_TABLENAME, PROPERTY_ID_UPDATE_TABLENAME, PropertyAttribute::BOUND, + &m_sUpdateTableName, cppu::UnoType<decltype(m_sUpdateTableName)>::get()); + + registerProperty(PROPERTY_UPDATE_SCHEMANAME, PROPERTY_ID_UPDATE_SCHEMANAME, PropertyAttribute::BOUND, + &m_sUpdateSchemaName, cppu::UnoType<decltype(m_sUpdateSchemaName)>::get()); + + registerProperty(PROPERTY_UPDATE_CATALOGNAME, PROPERTY_ID_UPDATE_CATALOGNAME, PropertyAttribute::BOUND, + &m_sUpdateCatalogName, cppu::UnoType<decltype(m_sUpdateCatalogName)>::get()); + + registerProperty(PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, PropertyAttribute::BOUND, + &m_aLayoutInformation, cppu::UnoType<decltype(m_aLayoutInformation)>::get()); +} + +OUString OQuery::determineContentType() const +{ + return "application/vnd.org.openoffice.DatabaseQuery"; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/query.hxx b/dbaccess/source/core/api/query.hxx new file mode 100644 index 000000000..fefcb94b4 --- /dev/null +++ b/dbaccess/source/core/api/query.hxx @@ -0,0 +1,147 @@ +/* -*- 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 . + */ + +#pragma once + +#include "querydescriptor.hxx" +#include <cppuhelper/implbase3.hxx> +#include <rtl/ref.hxx> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <ContentHelper.hxx> + +#include <map> + +namespace dbtools +{ + class WarningsContainer; +} + +namespace dbaccess +{ + +// OQuery - an object implementing the sdb.Query service +typedef ::cppu::ImplHelper3 < css::sdbcx::XDataDescriptorFactory, + css::beans::XPropertyChangeListener, + css::sdbcx::XRename + > OQuery_Base; +class OQuery; +class OColumn; +typedef ::comphelper::OPropertyArrayUsageHelper< OQuery > OQuery_ArrayHelperBase; + +class OQuery :public OContentHelper + ,public OQueryDescriptor_Base + ,public OQuery_Base + ,public OQuery_ArrayHelperBase + ,public ODataSettings +{ + friend struct TRelease; + +protected: + css::uno::Reference< css::beans::XPropertySet > m_xCommandDefinition; + css::uno::Reference< css::sdbc::XConnection > m_xConnection; + css::uno::Reference< css::beans::XPropertySetInfo > m_xCommandPropInfo; + ::rtl::Reference< OContainerMediator > m_pColumnMediator; + ::dbtools::WarningsContainer* m_pWarnings; + + // possible actions on our "aggregate" + enum class AggregateAction { NONE, SettingProperties }; + AggregateAction m_eDoingCurrently; + + /** a class which automatically resets m_eDoingCurrently in its destructor + */ + class OAutoActionReset; // just for the following friend declaration + friend class OAutoActionReset; + class OAutoActionReset + { + OQuery& m_rActor; + public: + explicit OAutoActionReset(OQuery& _rActor) : m_rActor(_rActor) { } + ~OAutoActionReset() { m_rActor.m_eDoingCurrently = AggregateAction::NONE; } + }; + +protected: + virtual ~OQuery() override; + +// OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + using OQuery_ArrayHelperBase::getArrayHelper; + +public: + OQuery( + const css::uno::Reference< css::beans::XPropertySet >& _rxCommandDefinition, + const css::uno::Reference< css::sdbc::XConnection >& _rxConn, + const css::uno::Reference< css::uno::XComponentContext >& _xORB + ); + + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + +// css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + +// OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + +// css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + +// css::sdbcx::XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + +// css::beans::XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + +// css::lang::XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& _rSource ) override; + +// OPropertySetHelper + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + +public: + // the caller is responsible for the lifetime! + void setWarningsContainer( ::dbtools::WarningsContainer* _pWarnings ) { m_pWarnings = _pWarnings; } + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + +protected: + virtual void SAL_CALL disposing() override; + + virtual rtl::Reference<OColumn> createColumn(const OUString& _rName) const override; + + virtual void rebuildColumns( ) override; + + // OContentHelper overridables + virtual OUString determineContentType() const override; + +private: + void registerProperties(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/querycomposer.cxx b/dbaccess/source/core/api/querycomposer.cxx new file mode 100644 index 000000000..8bab87187 --- /dev/null +++ b/dbaccess/source/core/api/querycomposer.cxx @@ -0,0 +1,265 @@ +/* -*- 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 . + */ + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdbc/ColumnSearch.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/configmgr.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <querycomposer.hxx> +#include <strings.hxx> +#include <composertools.hxx> +#include <algorithm> + +using namespace dbaccess; +using namespace comphelper; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::cppu; +using namespace ::osl; +using namespace ::utl; + + +OQueryComposer::OQueryComposer(const Reference< XConnection>& _xConnection) + : OSubComponent(m_aMutex,_xConnection) +{ + OSL_ENSURE(_xConnection.is()," Connection can't be null!"); + + Reference<XMultiServiceFactory> xFac( _xConnection, UNO_QUERY_THROW ); + m_xComposer.set( xFac->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ); + m_xComposerHelper.set( xFac->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ); +} + +OQueryComposer::~OQueryComposer() +{ +} + +void SAL_CALL OQueryComposer::disposing() +{ + ::comphelper::disposeComponent(m_xComposerHelper); + ::comphelper::disposeComponent(m_xComposer); +} + +// css::lang::XTypeProvider +Sequence< Type > SAL_CALL OQueryComposer::getTypes() +{ + return ::comphelper::concatSequences(OSubComponent::getTypes(),OQueryComposer_BASE::getTypes()); +} + +Sequence< sal_Int8 > SAL_CALL OQueryComposer::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any SAL_CALL OQueryComposer::queryInterface( const Type & rType ) +{ + Any aRet = OSubComponent::queryInterface(rType); + if(!aRet.hasValue()) + aRet = OQueryComposer_BASE::queryInterface(rType); + return aRet; +} + +// XServiceInfo +OUString OQueryComposer::getImplementationName( ) +{ + return "com.sun.star.sdb.dbaccess.OQueryComposer"; +} + +sal_Bool OQueryComposer::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OQueryComposer::getSupportedServiceNames( ) +{ + return { SERVICE_SDB_SQLQUERYCOMPOSER }; +} + +// XSQLQueryComposer +OUString SAL_CALL OQueryComposer::getQuery( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + Reference<XPropertySet> xProp(m_xComposer,UNO_QUERY); + OUString sQuery; + if ( xProp.is() ) + xProp->getPropertyValue(PROPERTY_ORIGINAL) >>= sQuery; + return sQuery; +} + +void SAL_CALL OQueryComposer::setQuery( const OUString& command ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_aFilters.clear(); + m_xComposer->setQuery(command); + m_sOrgFilter = m_xComposer->getFilter(); + m_sOrgOrder = m_xComposer->getOrder(); +} + +OUString SAL_CALL OQueryComposer::getComposedQuery( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + MutexGuard aGuard(m_aMutex); + + return m_xComposer->getQuery(); +} + +OUString SAL_CALL OQueryComposer::getFilter( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + MutexGuard aGuard(m_aMutex); + FilterCreator aFilterCreator; + aFilterCreator = std::for_each(m_aFilters.begin(),m_aFilters.end(),aFilterCreator); + return aFilterCreator.getComposedAndClear(); +} + +Sequence< Sequence< PropertyValue > > SAL_CALL OQueryComposer::getStructuredFilter( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + MutexGuard aGuard(m_aMutex); + return m_xComposer->getStructuredFilter(); +} + +OUString SAL_CALL OQueryComposer::getOrder( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + OrderCreator aOrderCreator; + aOrderCreator = std::for_each(m_aOrders.begin(),m_aOrders.end(),aOrderCreator); + return aOrderCreator.getComposedAndClear(); +} + +void SAL_CALL OQueryComposer::appendFilterByColumn( const Reference< XPropertySet >& column ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_xComposerHelper->setQuery(getQuery()); + m_xComposerHelper->setFilter(OUString()); + m_xComposerHelper->appendFilterByColumn(column, true, SQLFilterOperator::EQUAL); + + FilterCreator aFilterCreator; + aFilterCreator.append(getFilter()); + aFilterCreator.append(m_xComposerHelper->getFilter()); + + setFilter( aFilterCreator.getComposedAndClear() ); +} + +void SAL_CALL OQueryComposer::appendOrderByColumn( const Reference< XPropertySet >& column, sal_Bool ascending ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + ::osl::MutexGuard aGuard( m_aMutex ); + + m_xComposerHelper->setQuery(getQuery()); + m_xComposerHelper->setOrder(OUString()); + m_xComposerHelper->appendOrderByColumn(column,ascending); + + OrderCreator aOrderCreator; + aOrderCreator.append(getOrder()); + aOrderCreator.append(m_xComposerHelper->getOrder()); + + setOrder(aOrderCreator.getComposedAndClear()); +} + +void SAL_CALL OQueryComposer::setFilter( const OUString& filter ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + FilterCreator aFilterCreator; + aFilterCreator.append(m_sOrgFilter); + aFilterCreator.append(filter); + + m_aFilters.clear(); + if ( !filter.isEmpty() ) + m_aFilters.push_back(filter); + + m_xComposer->setFilter( aFilterCreator.getComposedAndClear() ); +} + +void SAL_CALL OQueryComposer::setOrder( const OUString& order ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + + OrderCreator aOrderCreator; + aOrderCreator.append(m_sOrgOrder); + aOrderCreator.append(order); + + m_aOrders.clear(); + if ( !order.isEmpty() ) + m_aOrders.push_back(order); + + m_xComposer->setOrder(aOrderCreator.getComposedAndClear()); +} + +// XTablesSupplier +Reference< XNameAccess > SAL_CALL OQueryComposer::getTables( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + return Reference<XTablesSupplier>(m_xComposer,UNO_QUERY_THROW)->getTables(); +} + +// XColumnsSupplier +Reference< XNameAccess > SAL_CALL OQueryComposer::getColumns( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + return Reference<XColumnsSupplier>(m_xComposer,UNO_QUERY_THROW)->getColumns(); +} + +Reference< XIndexAccess > SAL_CALL OQueryComposer::getParameters( ) +{ + ::connectivity::checkDisposed(OSubComponent::rBHelper.bDisposed); + + ::osl::MutexGuard aGuard( m_aMutex ); + return Reference<XParametersSupplier>(m_xComposer,UNO_QUERY_THROW)->getParameters(); +} + +void SAL_CALL OQueryComposer::acquire() noexcept +{ + OSubComponent::acquire(); +} + +void SAL_CALL OQueryComposer::release() noexcept +{ + OSubComponent::release(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/querycontainer.cxx b/dbaccess/source/core/api/querycontainer.cxx new file mode 100644 index 000000000..583cab373 --- /dev/null +++ b/dbaccess/source/core/api/querycontainer.cxx @@ -0,0 +1,430 @@ +/* -*- 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 . + */ + +#include <querycontainer.hxx> +#include "query.hxx" +#include <strings.hxx> +#include <objectnameapproval.hxx> +#include <veto.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XContainerApproveBroadcaster.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/QueryDefinition.hpp> + +#include <osl/diagnose.h> +#include <comphelper/uno3.hxx> +#include <comphelper/property.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/exc_hlp.hxx> + +using namespace dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +// OQueryContainer + +OQueryContainer::OQueryContainer( + const Reference< XNameContainer >& _rxCommandDefinitions + , const Reference< XConnection >& _rxConn + , const Reference< XComponentContext >& _rxORB, + ::dbtools::WarningsContainer* _pWarnings) + :ODefinitionContainer(_rxORB,nullptr,std::make_shared<ODefinitionContainer_Impl>()) + ,m_pWarnings( _pWarnings ) + ,m_xCommandDefinitions(_rxCommandDefinitions) + ,m_xConnection(_rxConn) + ,m_eDoingCurrently(AggregateAction::NONE) +{ +} + +void OQueryContainer::init() +{ + Reference< XContainer > xContainer( m_xCommandDefinitions, UNO_QUERY_THROW ); + xContainer->addContainerListener( this ); + + Reference< XContainerApproveBroadcaster > xContainerApprove( m_xCommandDefinitions, UNO_QUERY_THROW ); + xContainerApprove->addContainerApproveListener( this ); + + // fill my structures + ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + Sequence< OUString > sDefinitionNames = m_xCommandDefinitions->getElementNames(); + const OUString* pDefinitionName = sDefinitionNames.getConstArray(); + const OUString* pEnd = pDefinitionName + sDefinitionNames.getLength(); + for ( ; pDefinitionName != pEnd; ++pDefinitionName ) + { + rDefinitions.insert( *pDefinitionName, TContentPtr() ); + m_aDocuments.push_back(m_aDocumentMap.emplace( *pDefinitionName,Documents::mapped_type()).first); + } + + setElementApproval( std::make_shared<ObjectNameApproval>( m_xConnection, ObjectNameApproval::TypeQuery ) ); +} + +rtl::Reference<OQueryContainer> OQueryContainer::create( + const Reference< XNameContainer >& _rxCommandDefinitions + , const Reference< XConnection >& _rxConn + , const Reference< XComponentContext >& _rxORB, + ::dbtools::WarningsContainer* _pWarnings) +{ + rtl::Reference c( + new OQueryContainer( + _rxCommandDefinitions, _rxConn, _rxORB, _pWarnings)); + c->init(); + return c; +} + +OQueryContainer::~OQueryContainer() +{ + // dispose(); + // maybe we're already disposed, but this should be uncritical +} + +IMPLEMENT_FORWARD_XINTERFACE2( OQueryContainer,ODefinitionContainer,OQueryContainer_Base) + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( OQueryContainer,ODefinitionContainer,OQueryContainer_Base) + +void OQueryContainer::disposing() +{ + ODefinitionContainer::disposing(); + MutexGuard aGuard(m_aMutex); + if ( !m_xCommandDefinitions.is() ) + // already disposed + return; + + Reference< XContainer > xContainer( m_xCommandDefinitions, UNO_QUERY ); + xContainer->removeContainerListener( this ); + Reference< XContainerApproveBroadcaster > xContainerApprove( m_xCommandDefinitions, UNO_QUERY ); + xContainerApprove->removeContainerApproveListener( this ); + + m_xCommandDefinitions = nullptr; + m_xConnection = nullptr; +} + +// XServiceInfo +OUString SAL_CALL OQueryContainer::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.OQueryContainer"; + } +sal_Bool SAL_CALL OQueryContainer::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OQueryContainer::getSupportedServiceNames() +{ + return { SERVICE_SDBCX_CONTAINER, SERVICE_SDB_QUERIES }; +} + +// XDataDescriptorFactory +Reference< XPropertySet > SAL_CALL OQueryContainer::createDataDescriptor( ) +{ + return new OQueryDescriptor(); +} + +// XAppend +void SAL_CALL OQueryContainer::appendByDescriptor( const Reference< XPropertySet >& _rxDesc ) +{ + ResettableMutexGuard aGuard(m_aMutex); + if ( !m_xCommandDefinitions.is() ) + throw DisposedException( OUString(), *this ); + + // first clone this object's CommandDefinition part + Reference< css::sdb::XQueryDefinition > xCommandDefinitionPart = css::sdb::QueryDefinition::create(m_aContext); + + ::comphelper::copyProperties( _rxDesc, Reference<XPropertySet>(xCommandDefinitionPart, UNO_QUERY_THROW) ); + // TODO : the columns part of the descriptor has to be copied + + // create a wrapper for the object (*before* inserting into our command definition container) + Reference< XContent > xNewObject( implCreateWrapper( Reference< XContent>( xCommandDefinitionPart, UNO_QUERY_THROW ) ) ); + + OUString sNewObjectName; + _rxDesc->getPropertyValue(PROPERTY_NAME) >>= sNewObjectName; + + try + { + notifyByName( aGuard, sNewObjectName, xNewObject, nullptr, E_INSERTED, ApproveListeners ); + } + catch (const WrappedTargetException& e) + { + disposeComponent( xNewObject ); + disposeComponent( xCommandDefinitionPart ); + throw WrappedTargetRuntimeException(e.Message, e.Context, e.TargetException); + } + catch (const Exception&) + { + disposeComponent( xNewObject ); + disposeComponent( xCommandDefinitionPart ); + throw; + } + + // insert the basic object into the definition container + { + m_eDoingCurrently = AggregateAction::Inserting; + OAutoActionReset aAutoReset(*this); + m_xCommandDefinitions->insertByName(sNewObjectName, Any(xCommandDefinitionPart)); + } + + implAppend( sNewObjectName, xNewObject ); + try + { + notifyByName( aGuard, sNewObjectName, xNewObject, nullptr, E_INSERTED, ContainerListemers ); + } + catch (const WrappedTargetException& e) + { + throw WrappedTargetRuntimeException(e.Message, e.Context, e.TargetException); + } +} + +// XDrop +void SAL_CALL OQueryContainer::dropByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_aMutex); + if ( !checkExistence(_rName) ) + throw NoSuchElementException(_rName,*this); + + if ( !m_xCommandDefinitions.is() ) + throw DisposedException( OUString(), *this ); + + // now simply forward the remove request to the CommandDefinition container, we're a listener for the removal + // and thus we do everything necessary in ::elementRemoved + m_xCommandDefinitions->removeByName(_rName); +} + +void SAL_CALL OQueryContainer::dropByIndex( sal_Int32 _nIndex ) +{ + MutexGuard aGuard(m_aMutex); + if ((_nIndex<0) || (_nIndex>getCount())) + throw IndexOutOfBoundsException(); + + if ( !m_xCommandDefinitions.is() ) + throw DisposedException( OUString(), *this ); + + OUString sName; + Reference<XPropertySet> xProp(Reference<XIndexAccess>(m_xCommandDefinitions,UNO_QUERY_THROW)->getByIndex(_nIndex),UNO_QUERY); + if ( xProp.is() ) + xProp->getPropertyValue(PROPERTY_NAME) >>= sName; + + dropByName(sName); +} + +void SAL_CALL OQueryContainer::elementInserted( const css::container::ContainerEvent& _rEvent ) +{ + Reference< XContent > xNewElement; + OUString sElementName; + _rEvent.Accessor >>= sElementName; + { + MutexGuard aGuard(m_aMutex); + if (AggregateAction::Inserting == m_eDoingCurrently) + // nothing to do, we're inserting via an "appendByDescriptor" + return; + + OSL_ENSURE(!sElementName.isEmpty(), "OQueryContainer::elementInserted : invalid name !"); + OSL_ENSURE(m_aDocumentMap.find(sElementName) == m_aDocumentMap.end(), "OQueryContainer::elementInserted : oops... we're inconsistent with our master container !"); + if (sElementName.isEmpty() || hasByName(sElementName)) + return; + + // insert an own new element + xNewElement = implCreateWrapper(sElementName); + } + insertByName(sElementName,Any(xNewElement)); +} + +void SAL_CALL OQueryContainer::elementRemoved( const css::container::ContainerEvent& _rEvent ) +{ + OUString sAccessor; + _rEvent.Accessor >>= sAccessor; + { + OSL_ENSURE(!sAccessor.isEmpty(), "OQueryContainer::elementRemoved : invalid name !"); + OSL_ENSURE(m_aDocumentMap.find(sAccessor) != m_aDocumentMap.end(), "OQueryContainer::elementRemoved : oops... we're inconsistent with our master container !"); + if ( sAccessor.isEmpty() || !hasByName(sAccessor) ) + return; + } + removeByName(sAccessor); +} + +void SAL_CALL OQueryContainer::elementReplaced( const css::container::ContainerEvent& _rEvent ) +{ + Reference< XContent > xNewElement; + OUString sAccessor; + _rEvent.Accessor >>= sAccessor; + + { + MutexGuard aGuard(m_aMutex); + OSL_ENSURE(!sAccessor.isEmpty(), "OQueryContainer::elementReplaced : invalid name !"); + OSL_ENSURE(m_aDocumentMap.find(sAccessor) != m_aDocumentMap.end(), "OQueryContainer::elementReplaced : oops... we're inconsistent with our master container !"); + if (sAccessor.isEmpty() || !hasByName(sAccessor)) + return; + + xNewElement = implCreateWrapper(sAccessor); + } + + replaceByName(sAccessor,Any(xNewElement)); +} + +Reference< XVeto > SAL_CALL OQueryContainer::approveInsertElement( const ContainerEvent& Event ) +{ + OUString sName; + OSL_VERIFY( Event.Accessor >>= sName ); + Reference< XContent > xElement( Event.Element, UNO_QUERY_THROW ); + + Reference< XVeto > xReturn; + try + { + getElementApproval()->approveElement( sName ); + } + catch( const Exception& ) + { + xReturn = new Veto( ::cppu::getCaughtException() ); + } + return xReturn; +} + +Reference< XVeto > SAL_CALL OQueryContainer::approveReplaceElement( const ContainerEvent& /*Event*/ ) +{ + return nullptr; +} + +Reference< XVeto > SAL_CALL OQueryContainer::approveRemoveElement( const ContainerEvent& /*Event*/ ) +{ + return nullptr; +} + +void SAL_CALL OQueryContainer::disposing( const css::lang::EventObject& _rSource ) +{ + if (_rSource.Source.get() == Reference< XInterface >(m_xCommandDefinitions, UNO_QUERY).get()) + { // our "master container" (with the command definitions) is being disposed + OSL_FAIL("OQueryContainer::disposing : nobody should dispose the CommandDefinition container before disposing my connection !"); + dispose(); + } + else + { + Reference< XContent > xSource(_rSource.Source, UNO_QUERY); + // it's one of our documents... + for (auto const& document : m_aDocumentMap) + { + if ( xSource == document.second.get() ) + { + m_xCommandDefinitions->removeByName(document.first); + break; + } + } + ODefinitionContainer::disposing(_rSource); + } +} + +OUString OQueryContainer::determineContentType() const +{ + return "application/vnd.org.openoffice.DatabaseQueryContainer"; +} + +Reference< XContent > OQueryContainer::implCreateWrapper(const OUString& _rName) +{ + Reference< XContent > xObject(m_xCommandDefinitions->getByName(_rName),UNO_QUERY); + return implCreateWrapper(xObject); +} + +Reference< XContent > OQueryContainer::implCreateWrapper(const Reference< XContent >& _rxCommandDesc) +{ + Reference<XNameContainer> xContainer(_rxCommandDesc,UNO_QUERY); + Reference< XContent > xReturn; + if ( xContainer .is() ) + { + xReturn = create( xContainer, m_xConnection, m_aContext, m_pWarnings ). + get(); + } + else + { + rtl::Reference<OQuery> pNewObject = new OQuery( Reference< XPropertySet >( _rxCommandDesc, UNO_QUERY ), m_xConnection, m_aContext ); + xReturn = pNewObject; + + pNewObject->setWarningsContainer( m_pWarnings ); +// pNewObject->getColumns(); + // Why? This is expensive. If you comment this in 'cause you really need it, be sure to run the + // QueryInQuery test in dbaccess/qa/complex/dbaccess ... + } + + return xReturn; +} + +Reference< XContent > OQueryContainer::createObject( const OUString& _rName) +{ + return implCreateWrapper(_rName); +} + +bool OQueryContainer::checkExistence(const OUString& _rName) +{ + bool bRet = false; + if ( !m_bInPropertyChange ) + { + bRet = m_xCommandDefinitions->hasByName(_rName); + Documents::const_iterator aFind = m_aDocumentMap.find(_rName); + if ( !bRet && aFind != m_aDocumentMap.end() ) + { + m_aDocuments.erase( std::find(m_aDocuments.begin(),m_aDocuments.end(),aFind)); + m_aDocumentMap.erase(aFind); + } + else if ( bRet && aFind == m_aDocumentMap.end() ) + { + implAppend(_rName,nullptr); + } + } + return bRet; +} + +sal_Bool SAL_CALL OQueryContainer::hasElements( ) +{ + MutexGuard aGuard(m_aMutex); + return m_xCommandDefinitions->hasElements(); +} + +sal_Int32 SAL_CALL OQueryContainer::getCount( ) +{ + MutexGuard aGuard(m_aMutex); + return Reference<XIndexAccess>(m_xCommandDefinitions,UNO_QUERY_THROW)->getCount(); +} + +Sequence< OUString > SAL_CALL OQueryContainer::getElementNames( ) +{ + MutexGuard aGuard(m_aMutex); + + return m_xCommandDefinitions->getElementNames(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/querydescriptor.cxx b/dbaccess/source/core/api/querydescriptor.cxx new file mode 100644 index 000000000..0ac6d76e3 --- /dev/null +++ b/dbaccess/source/core/api/querydescriptor.cxx @@ -0,0 +1,269 @@ +/* -*- 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 . + */ + +#include "querydescriptor.hxx" +#include <stringconstants.hxx> +#include <strings.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::osl; +using namespace ::cppu; + +namespace dbaccess +{ + +// OQueryDescriptor + +OQueryDescriptor::OQueryDescriptor() + :OQueryDescriptor_Base(m_aMutex,*this) + ,ODataSettings(m_aBHelper,true) +{ + registerProperties(); + ODataSettings::registerPropertiesFor(this); +} + +OQueryDescriptor::OQueryDescriptor(const OQueryDescriptor_Base& _rSource) + :OQueryDescriptor_Base(_rSource,*this) + ,ODataSettings(m_aBHelper,true) +{ + registerProperties(); + ODataSettings::registerPropertiesFor(this); +} + +OQueryDescriptor::~OQueryDescriptor() +{ +} + +css::uno::Sequence<sal_Int8> OQueryDescriptor::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > OQueryDescriptor::getTypes() +{ + return ::comphelper::concatSequences( + OQueryDescriptor_Base::getTypes( ), + ODataSettings::getTypes( ) + ); +} + +IMPLEMENT_FORWARD_XINTERFACE3( OQueryDescriptor,OWeakObject,OQueryDescriptor_Base,ODataSettings) + +void OQueryDescriptor::registerProperties() +{ + // the properties which OCommandBase supplies (it has no own registration, as it's not derived from + // an OPropertyStateContainer) + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND|PropertyAttribute::CONSTRAINED, + &m_sElementName, cppu::UnoType<decltype(m_sElementName)>::get()); + + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, + &m_sCommand, cppu::UnoType<decltype(m_sCommand)>::get()); + + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, + &m_bEscapeProcessing, cppu::UnoType<bool>::get()); + + registerProperty(PROPERTY_UPDATE_TABLENAME, PROPERTY_ID_UPDATE_TABLENAME, PropertyAttribute::BOUND, + &m_sUpdateTableName, cppu::UnoType<decltype(m_sUpdateTableName)>::get()); + + registerProperty(PROPERTY_UPDATE_SCHEMANAME, PROPERTY_ID_UPDATE_SCHEMANAME, PropertyAttribute::BOUND, + &m_sUpdateSchemaName, cppu::UnoType<decltype(m_sUpdateSchemaName)>::get()); + + registerProperty(PROPERTY_UPDATE_CATALOGNAME, PROPERTY_ID_UPDATE_CATALOGNAME, PropertyAttribute::BOUND, + &m_sUpdateCatalogName, cppu::UnoType<decltype(m_sUpdateCatalogName)>::get()); + + registerProperty(PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, PropertyAttribute::BOUND, + &m_aLayoutInformation, cppu::UnoType<decltype(m_aLayoutInformation)>::get()); +} + +Reference< XPropertySetInfo > SAL_CALL OQueryDescriptor::getPropertySetInfo( ) +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +::cppu::IPropertyArrayHelper& OQueryDescriptor::getInfoHelper() +{ + return *getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OQueryDescriptor::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +OQueryDescriptor_Base::OQueryDescriptor_Base(::osl::Mutex& _rMutex,::cppu::OWeakObject& _rMySelf) + :m_bColumnsOutOfDate(true) + ,m_rMutex(_rMutex) +{ + m_pColumns.reset( new OColumns(_rMySelf, m_rMutex, true,std::vector< OUString>(), this,this) ); +} + +OQueryDescriptor_Base::OQueryDescriptor_Base(const OQueryDescriptor_Base& _rSource,::cppu::OWeakObject& _rMySelf) + :m_bColumnsOutOfDate(true) + ,m_rMutex(_rSource.m_rMutex) +{ + m_pColumns.reset( new OColumns(_rMySelf, m_rMutex, true,std::vector< OUString>(), this,this) ); + + m_sCommand = _rSource.m_sCommand; + m_bEscapeProcessing = _rSource.m_bEscapeProcessing; + m_sUpdateTableName = _rSource.m_sUpdateTableName; + m_sUpdateSchemaName = _rSource.m_sUpdateSchemaName; + m_sUpdateCatalogName = _rSource.m_sUpdateCatalogName; + m_aLayoutInformation = _rSource.m_aLayoutInformation; +} + +OQueryDescriptor_Base::~OQueryDescriptor_Base() +{ + m_pColumns->acquire(); + m_pColumns->disposing(); +} + +sal_Int64 SAL_CALL OQueryDescriptor_Base::getSomething( const Sequence< sal_Int8 >& _rIdentifier ) +{ + return comphelper::getSomethingImpl(_rIdentifier, this); +} + +const css::uno::Sequence<sal_Int8> & OQueryDescriptor_Base::getUnoTunnelId() +{ + static const comphelper::UnoIdInit aId; + return aId.getSeq(); +} + +css::uno::Sequence<sal_Int8> OQueryDescriptor_Base::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +void OQueryDescriptor_Base::setColumnsOutOfDate( bool _bOutOfDate ) +{ + m_bColumnsOutOfDate = _bOutOfDate; + if ( !m_bColumnsOutOfDate ) + m_pColumns->setInitialized(); +} + +void OQueryDescriptor_Base::implAppendColumn( const OUString& _rName, OColumn* _pColumn ) +{ + m_pColumns->append( _rName, _pColumn ); +} + +void OQueryDescriptor_Base::clearColumns( ) +{ + m_pColumns->clearColumns(); + + setColumnsOutOfDate(); +} + +Reference< XNameAccess > SAL_CALL OQueryDescriptor_Base::getColumns( ) +{ + MutexGuard aGuard(m_rMutex); + + if ( m_bColumnsOutOfDate ) + { + // clear the current columns + clearColumns(); + + // do this before rebuildColumns. This prevents recursion, e.g. in the case where we + // have queries with cyclic references: + // foo := SELECT * FROM bar + // bar := SELECT * FROM foo + setColumnsOutOfDate( false ); + + // rebuild them + try + { + rebuildColumns(); + } + catch ( const Exception& ) + { + setColumnsOutOfDate(); + throw; + } + } + + return m_pColumns.get(); +} + +OUString SAL_CALL OQueryDescriptor_Base::getImplementationName( ) +{ + return "com.sun.star.sdb.OQueryDescriptor"; +} + +sal_Bool SAL_CALL OQueryDescriptor_Base::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL OQueryDescriptor_Base::getSupportedServiceNames( ) +{ + return { SERVICE_SDB_DATASETTINGS, SERVICE_SDB_QUERYDESCRIPTOR }; +} + +void OQueryDescriptor_Base::disposeColumns() +{ + m_pColumns->disposing(); +} + +void OQueryDescriptor_Base::columnAppended( const Reference< XPropertySet >& /*_rxSourceDescriptor*/ ) +{ + // not interested in +} + +void OQueryDescriptor_Base::columnDropped(const OUString& /*_sName*/) +{ + // not interested in +} + +Reference< XPropertySet > OQueryDescriptor_Base::createColumnDescriptor() +{ + OSL_FAIL( "OQueryDescriptor_Base::createColumnDescriptor: called why?" ); + return nullptr; +} + +void OQueryDescriptor_Base::rebuildColumns( ) +{ +} + +// IRefreshableColumns +void OQueryDescriptor_Base::refreshColumns() +{ + MutexGuard aGuard( m_rMutex ); + + clearColumns(); + rebuildColumns(); +} + +rtl::Reference<OColumn> OQueryDescriptor_Base::createColumn( const OUString& /*_rName*/ ) const +{ + // creating a column/descriptor for a query/descriptor does not make sense at all + return nullptr; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/querydescriptor.hxx b/dbaccess/source/core/api/querydescriptor.hxx new file mode 100644 index 000000000..e78b035da --- /dev/null +++ b/dbaccess/source/core/api/querydescriptor.hxx @@ -0,0 +1,143 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/implbase3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <osl/mutex.hxx> + +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> + +#include <apitools.hxx> +#include <column.hxx> +#include <datasettings.hxx> +#include <commandbase.hxx> +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/uno3.hxx> + +namespace dbaccess +{ + +// OQueryDescriptor_Base - a query descriptor (as the name suggests :) +typedef ::cppu::ImplHelper3< + css::sdbcx::XColumnsSupplier, + css::lang::XUnoTunnel, + css::lang::XServiceInfo > OQueryDescriptor_BASE; + +class OQueryDescriptor_Base + :public OQueryDescriptor_BASE + ,public OCommandBase + ,public IColumnFactory + ,public ::connectivity::sdbcx::IRefreshableColumns +{ +private: + bool m_bColumnsOutOfDate : 1; // the columns have to be rebuild on the next getColumns ? + ::osl::Mutex& m_rMutex; + +protected: + std::unique_ptr<OColumns> m_pColumns; // our column descriptions + OUString m_sElementName; + virtual ~OQueryDescriptor_Base(); + + void setColumnsOutOfDate( bool _bOutOfDate = true ); + + sal_Int32 getColumnCount() const { return m_pColumns ? m_pColumns->getCount() : 0; } + void clearColumns( ); + + void implAppendColumn( const OUString& _rName, OColumn* _pColumn ); + +public: + OQueryDescriptor_Base(::osl::Mutex& _rMutex,::cppu::OWeakObject& _rMySelf); + /** constructs the object with a UNO QueryDescriptor. If you use this ctor, the resulting object + won't have any column information (the column container will be empty) + */ + OQueryDescriptor_Base(const OQueryDescriptor_Base& _rSource,::cppu::OWeakObject& _rMySelf); + +// css::sdbcx::XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + +// css::lang::XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override; + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + +// css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +protected: + +// IColumnFactory + virtual rtl::Reference<OColumn> createColumn(const OUString& _rName) const override; + virtual css::uno::Reference< css::beans::XPropertySet > createColumnDescriptor() override; + virtual void columnAppended( const css::uno::Reference< css::beans::XPropertySet >& _rxSourceDescriptor ) override; + virtual void columnDropped(const OUString& _sName) override; + + /** rebuild our columns set + + clearColumns has already been called before, do <em>NOT</em> call it, again + */ + virtual void rebuildColumns( ); + + void disposeColumns(); + + // IRefreshableColumns overridables + virtual void refreshColumns() override; +}; + +class OQueryDescriptor : public comphelper::OMutexAndBroadcastHelper + ,public ::cppu::OWeakObject + ,public OQueryDescriptor_Base + ,public ::comphelper::OPropertyArrayUsageHelper< OQueryDescriptor_Base > + ,public ODataSettings +{ + OQueryDescriptor(const OQueryDescriptor&) = delete; + void operator =(const OQueryDescriptor&) = delete; + // helper + void registerProperties(); +protected: + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual ~OQueryDescriptor() override; +public: + OQueryDescriptor(); + explicit OQueryDescriptor(const OQueryDescriptor_Base& _rSource); + + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + +}; +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/resultcolumn.cxx b/dbaccess/source/core/api/resultcolumn.cxx new file mode 100644 index 000000000..938e7d1d3 --- /dev/null +++ b/dbaccess/source/core/api/resultcolumn.cxx @@ -0,0 +1,296 @@ +/* -*- 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 . + */ + +#include "resultcolumn.hxx" +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <tools/diagnose_ex.h> +#include <stringconstants.hxx> +#include <strings.hxx> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace dbaccess; + + +OResultColumn::OResultColumn( const Reference < XResultSetMetaData >& _xMetaData, sal_Int32 _nPos, + const Reference< XDatabaseMetaData >& _rxDBMeta ) + :OColumn( true ) + ,m_xMetaData( _xMetaData ) + ,m_xDBMetaData( _rxDBMeta ) + ,m_nPos( _nPos ) +{ +} + +void OResultColumn::impl_determineIsRowVersion_nothrow() +{ + if ( m_aIsRowVersion.hasValue() ) + return; + m_aIsRowVersion <<= false; + + OSL_ENSURE( m_xDBMetaData.is(), "OResultColumn::impl_determineIsRowVersion_nothrow: no DBMetaData!" ); + if ( !m_xDBMetaData.is() ) + return; + + try + { + OUString sCatalog, sSchema, sTable, sColumnName; + getPropertyValue( PROPERTY_CATALOGNAME ) >>= sCatalog; + getPropertyValue( PROPERTY_SCHEMANAME ) >>= sSchema; + getPropertyValue( PROPERTY_TABLENAME ) >>= sTable; + getPropertyValue( PROPERTY_NAME ) >>= sColumnName; + + try + { + Reference< XResultSet > xVersionColumns = m_xDBMetaData->getVersionColumns( + Any( sCatalog ), sSchema, sTable ); + if ( xVersionColumns.is() ) // allowed to be NULL + { + Reference< XRow > xResultRow( xVersionColumns, UNO_QUERY_THROW ); + while ( xVersionColumns->next() ) + { + if ( xResultRow->getString( 2 ) == sColumnName ) + { + m_aIsRowVersion <<= true; + break; + } + } + } + } + catch(const SQLException&) + { + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +OResultColumn::~OResultColumn() +{ +} + +// css::lang::XTypeProvider +Sequence< sal_Int8 > OResultColumn::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo +OUString OResultColumn::getImplementationName( ) +{ + return "com.sun.star.sdb.OResultColumn"; +} + +Sequence< OUString > OResultColumn::getSupportedServiceNames( ) +{ + return { SERVICE_SDBCX_COLUMN, SERVICE_SDB_RESULTCOLUMN }; +} + +// OComponentHelper +void OResultColumn::disposing() +{ + OColumn::disposing(); + + MutexGuard aGuard(m_aMutex); + m_xMetaData = nullptr; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OResultColumn::createArrayHelper( ) const +{ + return new ::cppu::OPropertyArrayHelper + { + { + { PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_DISPLAYSIZE, PROPERTY_ID_DISPLAYSIZE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISAUTOINCREMENT, PROPERTY_ID_ISAUTOINCREMENT, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCASESENSITIVE, PROPERTY_ID_ISCASESENSITIVE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISCURRENCY, PROPERTY_ID_ISCURRENCY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISDEFINITELYWRITABLE, PROPERTY_ID_ISDEFINITELYWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISNULLABLE, PROPERTY_ID_ISNULLABLE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISROWVERSION, PROPERTY_ID_ISROWVERSION, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSEARCHABLE, PROPERTY_ID_ISSEARCHABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISSIGNED, PROPERTY_ID_ISSIGNED, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_ISWRITABLE, PROPERTY_ID_ISWRITABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LABEL, PROPERTY_ID_LABEL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_PRECISION, PROPERTY_ID_PRECISION, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCALE, PROPERTY_ID_SCALE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SERVICENAME, PROPERTY_ID_SERVICENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TABLENAME, PROPERTY_ID_TABLENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPE, PROPERTY_ID_TYPE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_TYPENAME, PROPERTY_ID_TYPENAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY } + } + }; +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& OResultColumn::getInfoHelper() +{ + return *static_cast< ::comphelper::OPropertyArrayUsageHelper< OResultColumn >* >(this)->getArrayHelper(); +} + +namespace +{ + template< typename T > + void obtain( Any& _out_rValue, ::std::optional< T > & _rCache, const sal_Int32 _nPos, const Reference < XResultSetMetaData >& _rxResultMeta, T (SAL_CALL XResultSetMetaData::*Getter)( sal_Int32 ) ) + { + if ( !_rCache ) + _rCache = (_rxResultMeta.get()->*Getter)(_nPos); + _out_rValue <<= *_rCache; + } +} + +void OResultColumn::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + try + { + if ( OColumn::isRegisteredProperty( nHandle ) ) + { + OColumn::getFastPropertyValue( rValue, nHandle ); + } + else + { + switch (nHandle) + { + case PROPERTY_ID_ISROWVERSION: + const_cast< OResultColumn* >( this )->impl_determineIsRowVersion_nothrow(); + rValue = m_aIsRowVersion; + break; + case PROPERTY_ID_TABLENAME: + rValue <<= m_xMetaData->getTableName(m_nPos); + break; + case PROPERTY_ID_SCHEMANAME: + rValue <<= m_xMetaData->getSchemaName(m_nPos); + break; + case PROPERTY_ID_CATALOGNAME: + rValue <<= m_xMetaData->getCatalogName(m_nPos); + break; + case PROPERTY_ID_ISSIGNED: + obtain( rValue, m_isSigned, m_nPos, m_xMetaData, &XResultSetMetaData::isSigned ); + break; + case PROPERTY_ID_ISCURRENCY: + obtain( rValue, m_isCurrency, m_nPos, m_xMetaData, &XResultSetMetaData::isCurrency ); + break; + case PROPERTY_ID_ISSEARCHABLE: + obtain( rValue, m_bSearchable, m_nPos, m_xMetaData, &XResultSetMetaData::isSearchable ); + break; + case PROPERTY_ID_ISCASESENSITIVE: + obtain( rValue, m_isCaseSensitive, m_nPos, m_xMetaData, &XResultSetMetaData::isCaseSensitive ); + break; + case PROPERTY_ID_ISREADONLY: + obtain( rValue, m_isReadOnly, m_nPos, m_xMetaData, &XResultSetMetaData::isReadOnly ); + break; + case PROPERTY_ID_ISWRITABLE: + obtain( rValue, m_isWritable, m_nPos, m_xMetaData, &XResultSetMetaData::isWritable ); + break; + case PROPERTY_ID_ISDEFINITELYWRITABLE: + obtain( rValue, m_isDefinitelyWritable, m_nPos, m_xMetaData, &XResultSetMetaData::isDefinitelyWritable ); + break; + case PROPERTY_ID_ISAUTOINCREMENT: + obtain( rValue, m_isAutoIncrement, m_nPos, m_xMetaData, &XResultSetMetaData::isAutoIncrement ); + break; + case PROPERTY_ID_SERVICENAME: + rValue <<= m_xMetaData->getColumnServiceName(m_nPos); + break; + case PROPERTY_ID_LABEL: + obtain( rValue, m_sColumnLabel, m_nPos, m_xMetaData, &XResultSetMetaData::getColumnLabel ); + break; + case PROPERTY_ID_DISPLAYSIZE: + obtain( rValue, m_nColumnDisplaySize, m_nPos, m_xMetaData, &XResultSetMetaData::getColumnDisplaySize ); + break; + case PROPERTY_ID_TYPE: + obtain( rValue, m_nColumnType, m_nPos, m_xMetaData, &XResultSetMetaData::getColumnType ); + break; + case PROPERTY_ID_PRECISION: + obtain( rValue, m_nPrecision, m_nPos, m_xMetaData, &XResultSetMetaData::getPrecision ); + break; + case PROPERTY_ID_SCALE: + obtain( rValue, m_nScale, m_nPos, m_xMetaData, &XResultSetMetaData::getScale ); + break; + case PROPERTY_ID_ISNULLABLE: + obtain( rValue, m_isNullable, m_nPos, m_xMetaData, &XResultSetMetaData::isNullable ); + break; + case PROPERTY_ID_TYPENAME: + rValue <<= m_xMetaData->getColumnTypeName(m_nPos); + break; + default: + OSL_FAIL( "OResultColumn::getFastPropertyValue: unknown property handle!" ); + break; + } + } + } + catch (SQLException& ) + { + // default handling if we caught an exception + switch (nHandle) + { + case PROPERTY_ID_LABEL: + case PROPERTY_ID_TYPENAME: + case PROPERTY_ID_SERVICENAME: + case PROPERTY_ID_TABLENAME: + case PROPERTY_ID_SCHEMANAME: + case PROPERTY_ID_CATALOGNAME: + // empty string'S + rValue <<= OUString(); + break; + case PROPERTY_ID_ISROWVERSION: + case PROPERTY_ID_ISAUTOINCREMENT: + case PROPERTY_ID_ISWRITABLE: + case PROPERTY_ID_ISDEFINITELYWRITABLE: + case PROPERTY_ID_ISCASESENSITIVE: + case PROPERTY_ID_ISSEARCHABLE: + case PROPERTY_ID_ISCURRENCY: + case PROPERTY_ID_ISSIGNED: + { + rValue <<= false; + } break; + case PROPERTY_ID_ISREADONLY: + { + rValue <<= true; + } break; + case PROPERTY_ID_SCALE: + case PROPERTY_ID_PRECISION: + case PROPERTY_ID_DISPLAYSIZE: + rValue <<= sal_Int32(0); + break; + case PROPERTY_ID_TYPE: + rValue <<= sal_Int32(DataType::SQLNULL); + break; + case PROPERTY_ID_ISNULLABLE: + rValue <<= ColumnValue::NULLABLE_UNKNOWN; + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/resultcolumn.hxx b/dbaccess/source/core/api/resultcolumn.hxx new file mode 100644 index 000000000..53803bac8 --- /dev/null +++ b/dbaccess/source/core/api/resultcolumn.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <column.hxx> +#include <optional> +#include <comphelper/proparrhlp.hxx> +namespace dbaccess +{ + + // OResultColumn + + class OResultColumn : public OColumn, + public ::comphelper::OPropertyArrayUsageHelper < OResultColumn > + { + protected: + css::uno::Reference < css::sdbc::XResultSetMetaData > m_xMetaData; + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xDBMetaData; + sal_Int32 m_nPos; + css::uno::Any m_aIsRowVersion; + mutable ::std::optional< sal_Bool > m_isSigned; + mutable ::std::optional< sal_Bool > m_isCurrency; + mutable ::std::optional< sal_Bool > m_bSearchable; + mutable ::std::optional< sal_Bool > m_isCaseSensitive; + mutable ::std::optional< sal_Bool > m_isReadOnly; + mutable ::std::optional< sal_Bool > m_isWritable; + mutable ::std::optional< sal_Bool > m_isDefinitelyWritable; + mutable ::std::optional< sal_Bool > m_isAutoIncrement; + mutable ::std::optional< sal_Int32 > m_isNullable; + mutable ::std::optional< OUString > m_sColumnLabel; + mutable ::std::optional< sal_Int32 > m_nColumnDisplaySize; + mutable ::std::optional< sal_Int32 > m_nColumnType; + mutable ::std::optional< sal_Int32 > m_nPrecision; + mutable ::std::optional< sal_Int32 > m_nScale; + + virtual ~OResultColumn() override; + public: + OResultColumn( + const css::uno::Reference < css::sdbc::XResultSetMetaData >& _xMetaData, + sal_Int32 _nPos, + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _rxDBMeta ); + + // css::lang::XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + + private: + void impl_determineIsRowVersion_nothrow(); + + protected: + using ::cppu::OPropertySetHelper::getFastPropertyValue; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/resultset.cxx b/dbaccess/source/core/api/resultset.cxx new file mode 100644 index 000000000..2fe6cf355 --- /dev/null +++ b/dbaccess/source/core/api/resultset.cxx @@ -0,0 +1,994 @@ +/* -*- 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 . + */ + +#include "resultset.hxx" +#include <sal/log.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/types.hxx> +#include <tools/diagnose_ex.h> +#include "datacolumn.hxx" +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> + + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::cppu; +using namespace ::osl; +using namespace dbaccess; +using namespace dbtools; + + +OResultSet::OResultSet(const css::uno::Reference< css::sdbc::XResultSet >& _xResultSet, + const css::uno::Reference< css::uno::XInterface >& _xStatement, + bool _bCaseSensitive) + :OResultSetBase(m_aMutex) + ,OPropertySetHelper(OResultSetBase::rBHelper) + ,m_xDelegatorResultSet(_xResultSet) + ,m_aWarnings( Reference< XWarningsSupplier >( _xResultSet, UNO_QUERY ) ) + ,m_nResultSetConcurrency(0) + ,m_bIsBookmarkable(false) +{ + m_pColumns.reset( new OColumns(*this, m_aMutex, _bCaseSensitive, std::vector< OUString>(), nullptr,nullptr) ); + + try + { + m_aStatement = _xStatement; + m_xDelegatorResultSetUpdate.set(m_xDelegatorResultSet, css::uno::UNO_QUERY); + m_xDelegatorRow.set(m_xDelegatorResultSet, css::uno::UNO_QUERY); + m_xDelegatorRowUpdate.set(m_xDelegatorResultSet, css::uno::UNO_QUERY); + + Reference< XPropertySet > xSet(m_xDelegatorResultSet, UNO_QUERY); + sal_Int32 nResultSetType(0); + xSet->getPropertyValue(PROPERTY_RESULTSETTYPE) >>= nResultSetType; + xSet->getPropertyValue(PROPERTY_RESULTSETCONCURRENCY) >>= m_nResultSetConcurrency; + + // test for Bookmarks + if (ResultSetType::FORWARD_ONLY != nResultSetType) + { + Reference <XPropertySetInfo > xInfo(xSet->getPropertySetInfo()); + if (xInfo->hasPropertyByName(PROPERTY_ISBOOKMARKABLE)) + { + m_bIsBookmarkable = ::comphelper::getBOOL(xSet->getPropertyValue(PROPERTY_ISBOOKMARKABLE)); + OSL_ENSURE( !m_bIsBookmarkable || Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY).is(), + "OResultSet::OResultSet: aggregate is inconsistent in its bookmarkable attribute!" ); + m_bIsBookmarkable = m_bIsBookmarkable && Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY).is(); + } + } + } + catch (const Exception&) + { + } +} + +OResultSet::~OResultSet() +{ + m_pColumns->acquire(); + m_pColumns->disposing(); +} + +// css::lang::XTypeProvider +Sequence< Type > OResultSet::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XPropertySet>::get(), + OResultSetBase::getTypes()); + + return aTypes.getTypes(); +} + +Sequence< sal_Int8 > OResultSet::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::uno::XInterface +Any OResultSet::queryInterface( const Type & rType ) +{ + Any aIface = OResultSetBase::queryInterface( rType ); + if (!aIface.hasValue()) + aIface = ::cppu::queryInterface( + rType, + static_cast< XPropertySet * >( this )); + + return aIface; +} + +void OResultSet::acquire() noexcept +{ + OResultSetBase::acquire(); +} + +void OResultSet::release() noexcept +{ + OResultSetBase::release(); +} + + +// OResultSetBase +void OResultSet::disposing() +{ + OPropertySetHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + // free the columns + m_pColumns->disposing(); + + // close the pending result set + Reference< XCloseable > (m_xDelegatorResultSet, UNO_QUERY_THROW)->close(); + + m_xDelegatorResultSet = nullptr; + m_xDelegatorRow = nullptr; + m_xDelegatorRowUpdate = nullptr; + + m_aStatement.clear(); +} + +// XCloseable +void OResultSet::close() +{ + { + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + } + dispose(); +} + +// XServiceInfo +OUString OResultSet::getImplementationName( ) +{ + return "com.sun.star.sdb.OResultSet"; +} + +sal_Bool OResultSet::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OResultSet::getSupportedServiceNames( ) +{ + return { SERVICE_SDBC_RESULTSET, SERVICE_SDB_RESULTSET }; +} + +// css::beans::XPropertySet +Reference< XPropertySetInfo > OResultSet::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OResultSet::createArrayHelper( ) const +{ + return new ::cppu::OPropertyArrayHelper + { + { + { PROPERTY_CURSORNAME, PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_FETCHDIRECTION, PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_FETCHSIZE, PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_ISBOOKMARKABLE, PROPERTY_ID_ISBOOKMARKABLE, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_RESULTSETCONCURRENCY, PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_RESULTSETTYPE, PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::READONLY } + } + }; +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& OResultSet::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OResultSet::convertFastPropertyValue(Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + // be lazy ... + rConvertedValue = rValue; + getFastPropertyValue( rOldValue, nHandle ); + return true; +} + +void OResultSet::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + // set it for the driver result set + Reference< XPropertySet > xSet(m_xDelegatorResultSet, UNO_QUERY); + switch (nHandle) + { + case PROPERTY_ID_FETCHDIRECTION: + xSet->setPropertyValue(PROPERTY_FETCHDIRECTION, rValue); + break; + case PROPERTY_ID_FETCHSIZE: + xSet->setPropertyValue(PROPERTY_FETCHSIZE, rValue); + break; + default: + SAL_WARN("dbaccess", "unknown Property"); + } +} + +void OResultSet::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + switch (nHandle) + { + case PROPERTY_ID_ISBOOKMARKABLE: + { + rValue <<= m_bIsBookmarkable; + } break; + default: + { + // get the property name + OUString aPropName; + sal_Int16 nAttributes; + const_cast<OResultSet*>(this)->getInfoHelper(). + fillPropertyMembersByHandle(&aPropName, &nAttributes, nHandle); + OSL_ENSURE(!aPropName.isEmpty(), "property not found?"); + + // now read the value + rValue = Reference< XPropertySet >(m_xDelegatorResultSet, UNO_QUERY_THROW)->getPropertyValue(aPropName); + } + } +} + +// XWarningsSupplier +Any OResultSet::getWarnings() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + return m_aWarnings.getWarnings(); +} + +void OResultSet::clearWarnings() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + m_aWarnings.clearWarnings(); +} + +// css::sdbc::XResultSetMetaDataSupplier +Reference< XResultSetMetaData > OResultSet::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return Reference< XResultSetMetaDataSupplier >(m_xDelegatorResultSet, UNO_QUERY_THROW)->getMetaData(); +} + +// css::sdbc::XColumnLocate +sal_Int32 OResultSet::findColumn(const OUString& columnName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return Reference< XColumnLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->findColumn(columnName); +} + +namespace +{ + Reference< XDatabaseMetaData > lcl_getDBMetaDataFromStatement_nothrow( const Reference< XInterface >& _rxStatement ) + { + Reference< XDatabaseMetaData > xDBMetaData; + try + { + Reference< XStatement > xStatement( _rxStatement, UNO_QUERY ); + Reference< XPreparedStatement > xPreparedStatement( _rxStatement, UNO_QUERY ); + Reference< XConnection > xConn; + if ( xStatement.is() ) + xConn = xStatement->getConnection(); + else if ( xPreparedStatement.is() ) + xConn = xPreparedStatement->getConnection(); + if ( xConn.is() ) + xDBMetaData = xConn->getMetaData(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return xDBMetaData; + } +} + +// css::sdbcx::XColumnsSupplier +Reference< css::container::XNameAccess > OResultSet::getColumns() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + // do we have to populate the columns + if (!m_pColumns->isInitialized()) + { + // get the metadata + Reference< XResultSetMetaData > xMetaData = Reference< XResultSetMetaDataSupplier >(m_xDelegatorResultSet, UNO_QUERY_THROW)->getMetaData(); + + sal_Int32 nColCount = 0; + // do we have columns + try + { + Reference< XDatabaseMetaData > xDBMetaData( lcl_getDBMetaDataFromStatement_nothrow( getStatement() ) ); + nColCount = xMetaData->getColumnCount(); + + for ( sal_Int32 i = 0; i < nColCount; ++i) + { + // retrieve the name of the column + OUString sName = xMetaData->getColumnName(i + 1); + rtl::Reference<ODataColumn> pColumn = new ODataColumn(xMetaData, m_xDelegatorRow, m_xDelegatorRowUpdate, i + 1, xDBMetaData); + + // don't silently assume that the name is unique - result set implementations + // are allowed to return duplicate names, but we are required to have + // unique column names + if ( m_pColumns->hasByName( sName ) ) + sName = ::dbtools::createUniqueName( m_pColumns.get(), sName ); + + m_pColumns->append( sName, pColumn.get() ); + } + } + catch ( const SQLException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_pColumns->setInitialized(); + + #if OSL_DEBUG_LEVEL > 0 + // some sanity checks. Especially in case we auto-adjusted the column names above, + // this might be reasonable + try + { + const Reference< XNameAccess > xColNames( static_cast< XNameAccess* >( m_pColumns.get() ), UNO_SET_THROW ); + const Sequence< OUString > aNames( xColNames->getElementNames() ); + SAL_WARN_IF( aNames.getLength() != nColCount, "dbaccess", + "OResultSet::getColumns: invalid column count!" ); + for ( auto const & name : aNames ) + { + Reference< XPropertySet > xColProps( xColNames->getByName( name ), UNO_QUERY_THROW ); + OUString sName; + OSL_VERIFY( xColProps->getPropertyValue( PROPERTY_NAME ) >>= sName ); + SAL_WARN_IF( sName != name, "dbaccess", "OResultSet::getColumns: invalid column name!" ); + } + + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + #endif + } + return m_pColumns.get(); +} + +// css::sdbc::XRow +sal_Bool OResultSet::wasNull() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->wasNull(); +} + +OUString OResultSet::getString(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getString(columnIndex); +} + +sal_Bool OResultSet::getBoolean(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getBoolean(columnIndex); +} + +sal_Int8 OResultSet::getByte(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getByte(columnIndex); +} + +sal_Int16 OResultSet::getShort(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getShort(columnIndex); +} + +sal_Int32 OResultSet::getInt(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getInt(columnIndex); +} + +sal_Int64 OResultSet::getLong(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getLong(columnIndex); +} + +float OResultSet::getFloat(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getFloat(columnIndex); +} + +double OResultSet::getDouble(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getDouble(columnIndex); +} + +Sequence< sal_Int8 > OResultSet::getBytes(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getBytes(columnIndex); +} + +css::util::Date OResultSet::getDate(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getDate(columnIndex); +} + +css::util::Time OResultSet::getTime(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getTime(columnIndex); +} + +css::util::DateTime OResultSet::getTimestamp(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getTimestamp(columnIndex); +} + +Reference< css::io::XInputStream > OResultSet::getBinaryStream(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getBinaryStream(columnIndex); +} + +Reference< css::io::XInputStream > OResultSet::getCharacterStream(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getCharacterStream(columnIndex); +} + +Any OResultSet::getObject(sal_Int32 columnIndex, const Reference< css::container::XNameAccess > & typeMap) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getObject(columnIndex, typeMap); +} + +Reference< XRef > OResultSet::getRef(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getRef(columnIndex); +} + +Reference< XBlob > OResultSet::getBlob(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getBlob(columnIndex); +} + +Reference< XClob > OResultSet::getClob(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getClob(columnIndex); +} + +Reference< XArray > OResultSet::getArray(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorRow->getArray(columnIndex); +} + +// css::sdbc::XRowUpdate +void OResultSet::updateNull(sal_Int32 columnIndex) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateNull(columnIndex); +} + +void OResultSet::updateBoolean(sal_Int32 columnIndex, sal_Bool x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateBoolean(columnIndex, x); +} + +void OResultSet::updateByte(sal_Int32 columnIndex, sal_Int8 x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateByte(columnIndex, x); +} + +void OResultSet::updateShort(sal_Int32 columnIndex, sal_Int16 x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateShort(columnIndex, x); +} + +void OResultSet::updateInt(sal_Int32 columnIndex, sal_Int32 x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateInt(columnIndex, x); +} + +void OResultSet::updateLong(sal_Int32 columnIndex, sal_Int64 x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateLong(columnIndex, x); +} + +void OResultSet::updateFloat(sal_Int32 columnIndex, float x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateFloat(columnIndex, x); +} + +void OResultSet::updateDouble(sal_Int32 columnIndex, double x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateDouble(columnIndex, x); +} + +void OResultSet::updateString(sal_Int32 columnIndex, const OUString& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateString(columnIndex, x); +} + +void OResultSet::updateBytes(sal_Int32 columnIndex, const Sequence< sal_Int8 >& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateBytes(columnIndex, x); +} + +void OResultSet::updateDate(sal_Int32 columnIndex, const css::util::Date& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateDate(columnIndex, x); +} + +void OResultSet::updateTime(sal_Int32 columnIndex, const css::util::Time& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateTime(columnIndex, x); +} + +void OResultSet::updateTimestamp(sal_Int32 columnIndex, const css::util::DateTime& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateTimestamp(columnIndex, x); +} + +void OResultSet::updateBinaryStream(sal_Int32 columnIndex, const Reference< css::io::XInputStream > & x, sal_Int32 length) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateBinaryStream(columnIndex, x, length); +} + +void OResultSet::updateCharacterStream(sal_Int32 columnIndex, const Reference< css::io::XInputStream > & x, sal_Int32 length) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateCharacterStream(columnIndex, x, length); +} + +void OResultSet::updateNumericObject(sal_Int32 columnIndex, const Any& x, sal_Int32 scale) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateNumericObject(columnIndex, x, scale); +} + +void OResultSet::updateObject(sal_Int32 columnIndex, const Any& x) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorRowUpdate->updateObject(columnIndex, x); +} + +// css::sdbc::XResultSet +sal_Bool OResultSet::next() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->next(); +} + +sal_Bool OResultSet::isBeforeFirst() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->isBeforeFirst(); +} + +sal_Bool OResultSet::isAfterLast() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->isAfterLast(); +} + +sal_Bool OResultSet::isFirst() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->isFirst(); +} + +sal_Bool OResultSet::isLast() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->isLast(); +} + +void OResultSet::beforeFirst() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + m_xDelegatorResultSet->beforeFirst(); +} + +void OResultSet::afterLast() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + m_xDelegatorResultSet->afterLast(); +} + +sal_Bool OResultSet::first() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->first(); +} + +sal_Bool OResultSet::last() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->last(); +} + +sal_Int32 OResultSet::getRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->getRow(); +} + +sal_Bool OResultSet::absolute(sal_Int32 row) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->absolute(row); +} + +sal_Bool OResultSet::relative(sal_Int32 rows) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->relative(rows); +} + +sal_Bool OResultSet::previous() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->previous(); +} + +void OResultSet::refreshRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + m_xDelegatorResultSet->refreshRow(); +} + +sal_Bool OResultSet::rowUpdated() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->rowUpdated(); +} + +sal_Bool OResultSet::rowInserted() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->rowInserted(); +} + +sal_Bool OResultSet::rowDeleted() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_xDelegatorResultSet->rowDeleted(); +} + +Reference< XInterface > OResultSet::getStatement() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + return m_aStatement; +} + +// css::sdbcx::XRowLocate +Any OResultSet::getBookmark() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->getBookmark(); +} + +sal_Bool OResultSet::moveToBookmark(const Any& bookmark) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->moveToBookmark(bookmark); +} + +sal_Bool OResultSet::moveRelativeToBookmark(const Any& bookmark, sal_Int32 rows) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->moveRelativeToBookmark(bookmark, rows); +} + +sal_Int32 OResultSet::compareBookmarks(const Any& _first, const Any& _second) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->compareBookmarks(_first, _second); +} + +sal_Bool OResultSet::hasOrderedBookmarks() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->hasOrderedBookmarks(); +} + +sal_Int32 OResultSet::hashBookmark(const Any& bookmark) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkBookmarkable(); + + return Reference< XRowLocate >(m_xDelegatorResultSet, UNO_QUERY_THROW)->hashBookmark(bookmark); +} + +// css::sdbc::XResultSetUpdate +void OResultSet::insertRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->insertRow(); +} + +void OResultSet::updateRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->updateRow(); +} + +void OResultSet::deleteRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->deleteRow(); +} + +void OResultSet::cancelRowUpdates() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->cancelRowUpdates(); +} + +void OResultSet::moveToInsertRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->moveToInsertRow(); +} + +void OResultSet::moveToCurrentRow() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OResultSetBase::rBHelper.bDisposed); + + checkReadOnly(); + + m_xDelegatorResultSetUpdate->moveToCurrentRow(); +} + +void OResultSet::checkReadOnly() const +{ + if ( ( m_nResultSetConcurrency == ResultSetConcurrency::READ_ONLY ) + || !m_xDelegatorResultSetUpdate.is() + ) + throwSQLException( "The result set is read-only.", StandardSQLState::GENERAL_ERROR, *const_cast< OResultSet* >( this ) ); +} + +void OResultSet::checkBookmarkable() const +{ + if ( !m_bIsBookmarkable ) + throwSQLException( "The result set does not have bookmark support.", StandardSQLState::GENERAL_ERROR, *const_cast< OResultSet* >( this ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/resultset.hxx b/dbaccess/source/core/api/resultset.hxx new file mode 100644 index 000000000..b2da0f248 --- /dev/null +++ b/dbaccess/source/core/api/resultset.hxx @@ -0,0 +1,221 @@ +/* -*- 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 . + */ +#pragma once + +#include <column.hxx> +#include <connectivity/warningscontainer.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XStatement.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdbc/XRowUpdate.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/ResultSetConcurrency.hpp> + +#include <cppuhelper/propshlp.hxx> +#include <comphelper/proparrhlp.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +namespace dbaccess +{ + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XWarningsSupplier, + css::sdbc::XResultSet, + css::sdbc::XResultSetMetaDataSupplier, + css::sdbc::XRow, + css::sdbc::XCloseable, + css::sdbc::XColumnLocate, + css::sdbcx::XRowLocate, + css::sdbcx::XColumnsSupplier, + css::sdbc::XResultSetUpdate, + css::sdbc::XRowUpdate, + css::lang::XServiceInfo > OResultSetBase; + + // OResultSet + + class OResultSet final : public cppu::BaseMutex, + public OResultSetBase, + public ::cppu::OPropertySetHelper, + public ::comphelper::OPropertyArrayUsageHelper < OResultSet > + { + css::uno::Reference< css::uno::XInterface> m_aStatement; + + css::uno::Reference< css::sdbc::XResultSet > m_xDelegatorResultSet; + css::uno::Reference< css::sdbc::XResultSetUpdate > m_xDelegatorResultSetUpdate; + css::uno::Reference< css::sdbc::XRow > m_xDelegatorRow; + css::uno::Reference< css::sdbc::XRowUpdate > m_xDelegatorRowUpdate; + + ::dbtools::WarningsContainer m_aWarnings; + std::unique_ptr<OColumns> m_pColumns; + sal_Int32 m_nResultSetConcurrency; + bool m_bIsBookmarkable : 1; + + public: + OResultSet(const css::uno::Reference< css::sdbc::XResultSet >& _xResultSet, + const css::uno::Reference< css::uno::XInterface >& _xStatement, + bool _bCaseSensitive); + virtual ~OResultSet() override; + + // css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // ::cppu::OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + // cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue + ) override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + + // css::sdbc::XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + + // css::sdbc::XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + + // css::sdbc::XColumnLocate + virtual sal_Int32 SAL_CALL findColumn( const OUString& columnName ) override; + + // css::sdbcx::XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + + // css::sdbc::XRow + virtual sal_Bool SAL_CALL wasNull( ) override; + virtual OUString SAL_CALL getString( sal_Int32 columnIndex ) override; + virtual sal_Bool SAL_CALL getBoolean( sal_Int32 columnIndex ) override; + virtual sal_Int8 SAL_CALL getByte( sal_Int32 columnIndex ) override; + virtual sal_Int16 SAL_CALL getShort( sal_Int32 columnIndex ) override; + virtual sal_Int32 SAL_CALL getInt( sal_Int32 columnIndex ) override; + virtual sal_Int64 SAL_CALL getLong( sal_Int32 columnIndex ) override; + virtual float SAL_CALL getFloat( sal_Int32 columnIndex ) override; + virtual double SAL_CALL getDouble( sal_Int32 columnIndex ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBytes( sal_Int32 columnIndex ) override; + virtual css::util::Date SAL_CALL getDate( sal_Int32 columnIndex ) override; + virtual css::util::Time SAL_CALL getTime( sal_Int32 columnIndex ) override; + virtual css::util::DateTime SAL_CALL getTimestamp( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getBinaryStream( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getCharacterStream( sal_Int32 columnIndex ) override; + virtual css::uno::Any SAL_CALL getObject( sal_Int32 columnIndex, const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL getRef( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL getBlob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL getClob( sal_Int32 columnIndex ) override; + virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL getArray( sal_Int32 columnIndex ) override; + + // css::sdbc::XResultSet + virtual sal_Bool SAL_CALL next( ) override; + virtual sal_Bool SAL_CALL isBeforeFirst( ) override; + virtual sal_Bool SAL_CALL isAfterLast( ) override; + virtual sal_Bool SAL_CALL isFirst( ) override; + virtual sal_Bool SAL_CALL isLast( ) override; + virtual void SAL_CALL beforeFirst( ) override; + virtual void SAL_CALL afterLast( ) override; + virtual sal_Bool SAL_CALL first( ) override; + virtual sal_Bool SAL_CALL last( ) override; + virtual sal_Int32 SAL_CALL getRow( ) override; + virtual sal_Bool SAL_CALL absolute( sal_Int32 row ) override; + virtual sal_Bool SAL_CALL relative( sal_Int32 rows ) override; + virtual sal_Bool SAL_CALL previous( ) override; + virtual void SAL_CALL refreshRow( ) override; + virtual sal_Bool SAL_CALL rowUpdated( ) override; + virtual sal_Bool SAL_CALL rowInserted( ) override; + virtual sal_Bool SAL_CALL rowDeleted( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getStatement( ) override; + + // css::sdbcx::XRowLocate + virtual css::uno::Any SAL_CALL getBookmark( ) override; + virtual sal_Bool SAL_CALL moveToBookmark( const css::uno::Any& bookmark ) override; + virtual sal_Bool SAL_CALL moveRelativeToBookmark( const css::uno::Any& bookmark, sal_Int32 rows ) override; + virtual sal_Int32 SAL_CALL compareBookmarks( const css::uno::Any& first, const css::uno::Any& second ) override; + virtual sal_Bool SAL_CALL hasOrderedBookmarks( ) override; + virtual sal_Int32 SAL_CALL hashBookmark( const css::uno::Any& bookmark ) override; + + // css::sdbc::XResultSetUpdate + virtual void SAL_CALL insertRow( ) override; + virtual void SAL_CALL updateRow( ) override; + virtual void SAL_CALL deleteRow( ) override; + virtual void SAL_CALL cancelRowUpdates( ) override; + virtual void SAL_CALL moveToInsertRow( ) override; + virtual void SAL_CALL moveToCurrentRow( ) override; + + // css::sdbc::XRowUpdate + virtual void SAL_CALL updateNull( sal_Int32 columnIndex ) override; + virtual void SAL_CALL updateBoolean( sal_Int32 columnIndex, sal_Bool x ) override; + virtual void SAL_CALL updateByte( sal_Int32 columnIndex, sal_Int8 x ) override; + virtual void SAL_CALL updateShort( sal_Int32 columnIndex, sal_Int16 x ) override; + virtual void SAL_CALL updateInt( sal_Int32 columnIndex, sal_Int32 x ) override; + virtual void SAL_CALL updateLong( sal_Int32 columnIndex, sal_Int64 x ) override; + virtual void SAL_CALL updateFloat( sal_Int32 columnIndex, float x ) override; + virtual void SAL_CALL updateDouble( sal_Int32 columnIndex, double x ) override; + virtual void SAL_CALL updateString( sal_Int32 columnIndex, const OUString& x ) override; + virtual void SAL_CALL updateBytes( sal_Int32 columnIndex, const css::uno::Sequence< sal_Int8 >& x ) override; + virtual void SAL_CALL updateDate( sal_Int32 columnIndex, const css::util::Date& x ) override; + virtual void SAL_CALL updateTime( sal_Int32 columnIndex, const css::util::Time& x ) override; + virtual void SAL_CALL updateTimestamp( sal_Int32 columnIndex, const css::util::DateTime& x ) override; + virtual void SAL_CALL updateBinaryStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateCharacterStream( sal_Int32 columnIndex, const css::uno::Reference< css::io::XInputStream >& x, sal_Int32 length ) override; + virtual void SAL_CALL updateObject( sal_Int32 columnIndex, const css::uno::Any& x ) override; + virtual void SAL_CALL updateNumericObject( sal_Int32 columnIndex, const css::uno::Any& x, sal_Int32 scale ) override; + + private: + void checkReadOnly() const; + void checkBookmarkable() const; + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/statement.cxx b/dbaccess/source/core/api/statement.cxx new file mode 100644 index 000000000..3a67cbaeb --- /dev/null +++ b/dbaccess/source/core/api/statement.cxx @@ -0,0 +1,591 @@ +/* -*- 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 . + */ + +#include <statement.hxx> +#include "resultset.hxx" +#include <stringconstants.hxx> +#include <strings.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <comphelper/property.hxx> +#include <comphelper/types.hxx> +#include <tools/diagnose_ex.h> +#include <connectivity/dbexception.hxx> + +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::cppu; +using namespace ::osl; +using namespace dbaccess; +using namespace dbtools; + + +OStatementBase::OStatementBase(const Reference< XConnection > & _xConn, + const Reference< XInterface > & _xStatement) + :OSubComponent(m_aMutex, _xConn) + ,OPropertySetHelper(OComponentHelper::rBHelper) + ,m_bUseBookmarks( false ) + ,m_bEscapeProcessing( true ) + +{ + OSL_ENSURE(_xStatement.is() ,"Statement is NULL!"); + m_xAggregateAsSet.set(_xStatement,UNO_QUERY); + m_xAggregateAsCancellable.set(m_xAggregateAsSet, UNO_QUERY); +} + +OStatementBase::~OStatementBase() +{ +} + +// css::lang::XTypeProvider +Sequence< Type > OStatementBase::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XWarningsSupplier>::get(), + cppu::UnoType<XCloseable>::get(), + cppu::UnoType<XMultipleResults>::get(), + cppu::UnoType<css::util::XCancellable>::get(), + OSubComponent::getTypes() ); + Reference< XGeneratedResultSet > xGRes(m_xAggregateAsSet, UNO_QUERY); + if ( xGRes.is() ) + aTypes = OTypeCollection(cppu::UnoType<XGeneratedResultSet>::get(),aTypes.getTypes()); + Reference< XPreparedBatchExecution > xPreparedBatchExecution(m_xAggregateAsSet, UNO_QUERY); + if ( xPreparedBatchExecution.is() ) + aTypes = OTypeCollection(cppu::UnoType<XPreparedBatchExecution>::get(),aTypes.getTypes()); + + return aTypes.getTypes(); +} + +// css::uno::XInterface +Any OStatementBase::queryInterface( const Type & rType ) +{ + Any aIface = OSubComponent::queryInterface( rType ); + if (!aIface.hasValue()) + { + aIface = ::cppu::queryInterface( + rType, + static_cast< XPropertySet * >( this ), + static_cast< XWarningsSupplier * >( this ), + static_cast< XCloseable * >( this ), + static_cast< XMultipleResults * >( this ), + static_cast< css::util::XCancellable * >( this )); + if ( !aIface.hasValue() ) + { + Reference< XGeneratedResultSet > xGRes(m_xAggregateAsSet, UNO_QUERY); + if ( cppu::UnoType<XGeneratedResultSet>::get()== rType && xGRes.is() ) + aIface = ::cppu::queryInterface(rType,static_cast< XGeneratedResultSet * >( this )); + } + if ( !aIface.hasValue() ) + { + Reference< XPreparedBatchExecution > xGRes(m_xAggregateAsSet, UNO_QUERY); + if ( cppu::UnoType<XPreparedBatchExecution>::get()== rType && xGRes.is() ) + aIface = ::cppu::queryInterface(rType,static_cast< XPreparedBatchExecution * >( this )); + } + } + return aIface; +} + +void OStatementBase::acquire() noexcept +{ + OSubComponent::acquire(); +} + +void OStatementBase::release() noexcept +{ + OSubComponent::release(); +} + +void OStatementBase::disposeResultSet() +{ + // free the cursor if alive + Reference< XComponent > xComp(m_aResultSet.get(), UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + m_aResultSet.clear(); +} + +// OComponentHelper +void OStatementBase::disposing() +{ + OPropertySetHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + // free pending results + disposeResultSet(); + + // free the original statement + { + MutexGuard aCancelGuard(m_aCancelMutex); + m_xAggregateAsCancellable = nullptr; + } + + if ( m_xAggregateAsSet.is() ) + { + try + { + Reference< XCloseable > (m_xAggregateAsSet, UNO_QUERY_THROW)->close(); + } + catch(RuntimeException& ) + {// don't care for anymore + } + } + m_xAggregateAsSet = nullptr; + + // free the parent at last + OSubComponent::disposing(); +} + +// XCloseable +void OStatementBase::close() +{ + { + MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + } + dispose(); +} + +// OPropertySetHelper +Reference< XPropertySetInfo > OStatementBase::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* OStatementBase::createArrayHelper( ) const +{ + return new ::cppu::OPropertyArrayHelper + { + { + { PROPERTY_CURSORNAME, PROPERTY_ID_CURSORNAME, cppu::UnoType<OUString>::get(), 0 }, + { PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, cppu::UnoType<bool>::get(), 0 }, + { PROPERTY_FETCHDIRECTION, PROPERTY_ID_FETCHDIRECTION, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_FETCHSIZE, PROPERTY_ID_FETCHSIZE, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_MAXFIELDSIZE, PROPERTY_ID_MAXFIELDSIZE, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_MAXROWS, PROPERTY_ID_MAXROWS, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_QUERYTIMEOUT, PROPERTY_ID_QUERYTIMEOUT, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_RESULTSETCONCURRENCY, PROPERTY_ID_RESULTSETCONCURRENCY, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_RESULTSETTYPE, PROPERTY_ID_RESULTSETTYPE, cppu::UnoType<sal_Int32>::get(), 0 }, + { PROPERTY_USEBOOKMARKS, PROPERTY_ID_USEBOOKMARKS, cppu::UnoType<bool>::get(), 0 } + } + }; +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& OStatementBase::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool OStatementBase::convertFastPropertyValue(Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + bool bModified(false); + switch (nHandle) + { + case PROPERTY_ID_USEBOOKMARKS: + bModified = ::comphelper::tryPropertyValue( rConvertedValue, rOldValue, rValue, m_bUseBookmarks ); + break; + + case PROPERTY_ID_ESCAPE_PROCESSING: + bModified = ::comphelper::tryPropertyValue( rConvertedValue, rOldValue, rValue, m_bEscapeProcessing ); + break; + + default: + if ( m_xAggregateAsSet.is() ) + { + // get the property name + OUString sPropName; + getInfoHelper().fillPropertyMembersByHandle( &sPropName, nullptr, nHandle ); + + // now set the value + Any aCurrentValue = m_xAggregateAsSet->getPropertyValue( sPropName ); + if ( aCurrentValue != rValue ) + { + rOldValue = aCurrentValue; + rConvertedValue = rValue; + bModified = true; + } + } + break; + } + return bModified; +} + +void OStatementBase::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + switch ( nHandle ) + { + case PROPERTY_ID_USEBOOKMARKS: + { + m_bUseBookmarks = ::comphelper::getBOOL( rValue ); + if ( m_xAggregateAsSet.is() && m_xAggregateAsSet->getPropertySetInfo()->hasPropertyByName( PROPERTY_USEBOOKMARKS ) ) + m_xAggregateAsSet->setPropertyValue( PROPERTY_USEBOOKMARKS, rValue ); + } + break; + + case PROPERTY_ID_ESCAPE_PROCESSING: + m_bEscapeProcessing = ::comphelper::getBOOL( rValue ); + if ( m_xAggregateAsSet.is() ) + m_xAggregateAsSet->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, rValue ); + break; + + default: + if ( m_xAggregateAsSet.is() ) + { + OUString sPropName; + getInfoHelper().fillPropertyMembersByHandle( &sPropName, nullptr, nHandle ); + m_xAggregateAsSet->setPropertyValue( sPropName, rValue ); + } + break; + } +} + +void OStatementBase::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + switch (nHandle) + { + case PROPERTY_ID_USEBOOKMARKS: + rValue <<= m_bUseBookmarks; + break; + + case PROPERTY_ID_ESCAPE_PROCESSING: + // don't rely on our aggregate - if it implements this wrong, and always returns + // TRUE here, then we would loop in impl_doEscapeProcessing_nothrow + rValue <<= m_bEscapeProcessing; + break; + + default: + if ( m_xAggregateAsSet.is() ) + { + OUString sPropName; + const_cast< OStatementBase* >( this )->getInfoHelper().fillPropertyMembersByHandle( &sPropName, nullptr, nHandle ); + rValue = m_xAggregateAsSet->getPropertyValue( sPropName ); + } + break; + } +} + +// XWarningsSupplier +Any OStatementBase::getWarnings() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + return Reference< XWarningsSupplier >(m_xAggregateAsSet, UNO_QUERY_THROW)->getWarnings(); +} + +void OStatementBase::clearWarnings() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + Reference< XWarningsSupplier >(m_xAggregateAsSet, UNO_QUERY_THROW)->clearWarnings(); +} + +// css::util::XCancellable +void OStatementBase::cancel() +{ + // no blocking as cancel is typically called from a different thread + MutexGuard aCancelGuard(m_aCancelMutex); + if (m_xAggregateAsCancellable.is()) + m_xAggregateAsCancellable->cancel(); + // else do nothing +} + +// XMultipleResults +Reference< XResultSet > SAL_CALL OStatementBase::getResultSet( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsMultipleResultSets()) + throwFunctionSequenceException(*this); + + return Reference< XMultipleResults >(m_xAggregateAsSet, UNO_QUERY_THROW)->getResultSet(); +} + +sal_Int32 SAL_CALL OStatementBase::getUpdateCount( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsMultipleResultSets()) + throwFunctionSequenceException(*this); + + return Reference< XMultipleResults >(m_xAggregateAsSet, UNO_QUERY_THROW)->getUpdateCount(); +} + +sal_Bool SAL_CALL OStatementBase::getMoreResults( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsMultipleResultSets()) + throwFunctionSequenceException(*this); + + // free the previous results + disposeResultSet(); + + return Reference< XMultipleResults >(m_xAggregateAsSet, UNO_QUERY_THROW)->getMoreResults(); +} + +// XPreparedBatchExecution +void SAL_CALL OStatementBase::addBatch( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + + Reference< XPreparedBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->addBatch(); +} + +void SAL_CALL OStatementBase::clearBatch( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + + Reference< XPreparedBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->clearBatch(); +} + +Sequence< sal_Int32 > SAL_CALL OStatementBase::executeBatch( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + + // free the previous results + disposeResultSet(); + + return Reference< XPreparedBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->executeBatch(); +} + +Reference< XResultSet > SAL_CALL OStatementBase::getGeneratedValues( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + Reference< XGeneratedResultSet > xGRes(m_xAggregateAsSet, UNO_QUERY); + + if ( xGRes.is() ) + return xGRes->getGeneratedValues( ); + return Reference< XResultSet >(); +} + + +// OStatement + +OStatement::OStatement( const Reference< XConnection >& _xConn, const Reference< XInterface > & _xStatement ) + :OStatementBase( _xConn, _xStatement ) + ,m_bAttemptedComposerCreation( false ) +{ + m_xAggregateStatement.set( _xStatement, UNO_QUERY_THROW ); +} + +IMPLEMENT_FORWARD_XINTERFACE2( OStatement, OStatementBase, OStatement_IFACE ); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( OStatement, OStatementBase, OStatement_IFACE ); + +// XServiceInfo +OUString OStatement::getImplementationName( ) +{ + return "com.sun.star.sdb.OStatement"; +} + +sal_Bool OStatement::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OStatement::getSupportedServiceNames( ) +{ + return { SERVICE_SDBC_STATEMENT }; +} + +// XStatement +Reference< XResultSet > OStatement::executeQuery( const OUString& _rSQL ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + Reference< XResultSet > xResultSet; + + OUString sSQL( impl_doEscapeProcessing_nothrow( _rSQL ) ); + + Reference< XResultSet > xInnerResultSet = m_xAggregateStatement->executeQuery( sSQL ); + Reference< XConnection > xConnection( m_xParent, UNO_QUERY_THROW ); + + if ( xInnerResultSet.is() ) + { + Reference< XDatabaseMetaData > xMeta = xConnection->getMetaData(); + bool bCaseSensitive = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(); + xResultSet = new OResultSet( xInnerResultSet, *this, bCaseSensitive ); + + // keep the resultset weak + m_aResultSet = xResultSet; + } + + return xResultSet; +} + +sal_Int32 OStatement::executeUpdate( const OUString& _rSQL ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + + OUString sSQL( impl_doEscapeProcessing_nothrow( _rSQL ) ); + return m_xAggregateStatement->executeUpdate( sSQL ); +} + +sal_Bool OStatement::execute( const OUString& _rSQL ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + disposeResultSet(); + + OUString sSQL( impl_doEscapeProcessing_nothrow( _rSQL ) ); + return m_xAggregateStatement->execute( sSQL ); +} + +void OStatement::addBatch( const OUString& _rSQL ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + + OUString sSQL( impl_doEscapeProcessing_nothrow( _rSQL ) ); + Reference< XBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->addBatch( sSQL ); +} + +void OStatement::clearBatch( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + + Reference< XBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->clearBatch(); +} + +Sequence< sal_Int32 > OStatement::executeBatch( ) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OComponentHelper::rBHelper.bDisposed); + // first check the meta data + Reference<XDatabaseMetaData> xMeta = Reference< XConnection > (m_xParent, UNO_QUERY_THROW)->getMetaData(); + if (!xMeta.is() || !xMeta->supportsBatchUpdates()) + throwFunctionSequenceException(*this); + return Reference< XBatchExecution >(m_xAggregateAsSet, UNO_QUERY_THROW)->executeBatch( ); +} + + +Reference< XConnection > OStatement::getConnection() +{ + return Reference< XConnection >( m_xParent, UNO_QUERY ); +} + +void SAL_CALL OStatement::disposing() +{ + OStatementBase::disposing(); + m_xComposer.clear(); + m_xAggregateStatement.clear(); +} + +OUString OStatement::impl_doEscapeProcessing_nothrow( const OUString& _rSQL ) const +{ + if ( !m_bEscapeProcessing ) + return _rSQL; + try + { + if ( !impl_ensureComposer_nothrow() ) + return _rSQL; + + bool bParseable = false; + try { m_xComposer->setQuery( _rSQL ); bParseable = true; } + catch( const SQLException& ) { } + + if ( !bParseable ) + // if we cannot parse it, silently accept this. The driver is probably able to cope with it then + return _rSQL; + + OUString sLowLevelSQL = m_xComposer->getQueryWithSubstitution(); + return sLowLevelSQL; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return _rSQL; +} + +bool OStatement::impl_ensureComposer_nothrow() const +{ + if ( m_bAttemptedComposerCreation ) + return m_xComposer.is(); + + const_cast< OStatement* >( this )->m_bAttemptedComposerCreation = true; + try + { + Reference< XMultiServiceFactory > xFactory( m_xParent, UNO_QUERY_THROW ); + const_cast< OStatement* >( this )->m_xComposer.set( xFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return m_xComposer.is(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/table.cxx b/dbaccess/source/core/api/table.cxx new file mode 100644 index 000000000..f9a1dffb9 --- /dev/null +++ b/dbaccess/source/core/api/table.cxx @@ -0,0 +1,352 @@ +/* -*- 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 . + */ + +#include <table.hxx> +#include <definitioncolumn.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include "CIndexes.hxx" + +#include <osl/diagnose.h> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdb/tools/XTableRename.hpp> +#include <com/sun/star/sdb/tools/XTableAlteration.hpp> + +#include <connectivity/TKeys.hxx> +#include <connectivity/dbtools.hxx> + +#include <ContainerMediator.hxx> + +using namespace dbaccess; +using namespace connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +// ODBTable + +ODBTable::ODBTable(connectivity::sdbcx::OCollection* _pTables + ,const Reference< XConnection >& _rxConn + ,const OUString& _rCatalog + ,const OUString& _rSchema + ,const OUString& _rName + ,const OUString& _rType + ,const OUString& _rDesc + ,const Reference< XNameAccess >& _xColumnDefinitions) + :OTable_Base(_pTables,_rxConn,_rxConn->getMetaData().is() && _rxConn->getMetaData()->supportsMixedCaseQuotedIdentifiers(), _rName, _rType, _rDesc, _rSchema, _rCatalog ) + ,m_xColumnDefinitions(_xColumnDefinitions) + ,m_nPrivileges(0) +{ + OSL_ENSURE(getMetaData().is(), "ODBTable::ODBTable : invalid conn !"); + OSL_ENSURE(!_rName.isEmpty(), "ODBTable::ODBTable : name !"); + // TODO : think about collecting the privileges here, as we can't ensure that in getFastPropertyValue, where + // we do this at the moment, the statement needed can be supplied by the connection (for example the SQL-Server + // ODBC driver does not allow more than one statement per connection, and in getFastPropertyValue it's more + // likely that it's already used up than it's here.) +} + +ODBTable::ODBTable(connectivity::sdbcx::OCollection* _pTables + ,const Reference< XConnection >& _rxConn) + :OTable_Base(_pTables,_rxConn, _rxConn->getMetaData().is() && _rxConn->getMetaData()->supportsMixedCaseQuotedIdentifiers()) + ,m_nPrivileges(-1) +{ +} + +ODBTable::~ODBTable() +{ +} + +rtl::Reference<OColumn> ODBTable::createColumn(const OUString& _rName) const +{ + Reference<XPropertySet> xProp; + if ( m_xDriverColumns.is() && m_xDriverColumns->hasByName(_rName) ) + { + xProp.set(m_xDriverColumns->getByName(_rName),UNO_QUERY); + } + else + { + OColumns* pColumns = static_cast<OColumns*>(m_xColumns.get()); + xProp.set(pColumns->createBaseObject(_rName),UNO_QUERY); + } + + Reference<XPropertySet> xColumnDefinition; + if ( m_xColumnDefinitions.is() && m_xColumnDefinitions->hasByName(_rName) ) + xColumnDefinition.set(m_xColumnDefinitions->getByName(_rName),UNO_QUERY); + return new OTableColumnWrapper( xProp, xColumnDefinition, false ); +} + +void ODBTable::columnAppended( const Reference< XPropertySet >& /*_rxSourceDescriptor*/ ) +{ + // not interested in +} + +void ODBTable::columnDropped(const OUString& _sName) +{ + Reference<XDrop> xDrop(m_xColumnDefinitions,UNO_QUERY); + if ( xDrop.is() && m_xColumnDefinitions->hasByName(_sName) ) + { + xDrop->dropByName(_sName); + } +} + +Sequence< sal_Int8 > ODBTable::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// OComponentHelper +void SAL_CALL ODBTable::disposing() +{ + OPropertySetHelper::disposing(); + OTable_Base::disposing(); + m_xColumnDefinitions = nullptr; + m_xDriverColumns = nullptr; + m_pColumnMediator = nullptr; +} + +void ODBTable::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const +{ + if ((PROPERTY_ID_PRIVILEGES == _nHandle) && (-1 == m_nPrivileges)) + { // somebody is asking for the privileges and we do not know them, yet + const_cast<ODBTable*>(this)->m_nPrivileges = ::dbtools::getTablePrivileges(getMetaData(),m_CatalogName,m_SchemaName, m_Name); + } + + OTable_Base::getFastPropertyValue(_rValue, _nHandle); +} + +void ODBTable::construct() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // we don't collect the privileges here, this is potentially expensive. Instead we determine them on request. + // (see getFastPropertyValue) + m_nPrivileges = -1; + + OTable_Base::construct(); + + registerProperty(PROPERTY_FILTER, PROPERTY_ID_FILTER, PropertyAttribute::BOUND, + &m_sFilter, cppu::UnoType<OUString>::get()); + + registerProperty(PROPERTY_ORDER, PROPERTY_ID_ORDER, PropertyAttribute::BOUND, + &m_sOrder, cppu::UnoType<OUString>::get()); + + registerProperty(PROPERTY_APPLYFILTER, PROPERTY_ID_APPLYFILTER, PropertyAttribute::BOUND, + &m_bApplyFilter, cppu::UnoType<bool>::get()); + + registerProperty(PROPERTY_FONT, PROPERTY_ID_FONT, PropertyAttribute::BOUND, + &m_aFont, cppu::UnoType<css::awt::FontDescriptor>::get()); + + registerMayBeVoidProperty(PROPERTY_ROW_HEIGHT, PROPERTY_ID_ROW_HEIGHT, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &m_aRowHeight, cppu::UnoType<sal_Int32>::get()); + + registerProperty(PROPERTY_AUTOGROW, PROPERTY_ID_AUTOGROW, PropertyAttribute::BOUND, + &m_bAutoGrow, cppu::UnoType<bool>::get()); + + registerMayBeVoidProperty(PROPERTY_TEXTCOLOR, PROPERTY_ID_TEXTCOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &m_aTextColor, cppu::UnoType<sal_Int32>::get()); + + registerProperty(PROPERTY_PRIVILEGES, PROPERTY_ID_PRIVILEGES, PropertyAttribute::BOUND | PropertyAttribute::READONLY, + &m_nPrivileges, cppu::UnoType<sal_Int32>::get()); + + registerMayBeVoidProperty(PROPERTY_TEXTLINECOLOR, PROPERTY_ID_TEXTLINECOLOR, PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID, + &m_aTextLineColor, cppu::UnoType<sal_Int32>::get()); + + registerProperty(PROPERTY_TEXTEMPHASIS, PROPERTY_ID_TEXTEMPHASIS, PropertyAttribute::BOUND, + &m_nFontEmphasis, cppu::UnoType<sal_Int16>::get()); + + registerProperty(PROPERTY_TEXTRELIEF, PROPERTY_ID_TEXTRELIEF, PropertyAttribute::BOUND, + &m_nFontRelief, cppu::UnoType<sal_Int16>::get()); + + registerProperty(PROPERTY_FONTNAME, PROPERTY_ID_FONTNAME, PropertyAttribute::BOUND,&m_aFont.Name, cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_FONTHEIGHT, PROPERTY_ID_FONTHEIGHT, PropertyAttribute::BOUND,&m_aFont.Height, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTWIDTH, PROPERTY_ID_FONTWIDTH, PropertyAttribute::BOUND,&m_aFont.Width, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTSTYLENAME, PROPERTY_ID_FONTSTYLENAME, PropertyAttribute::BOUND,&m_aFont.StyleName, cppu::UnoType<OUString>::get()); + registerProperty(PROPERTY_FONTFAMILY, PROPERTY_ID_FONTFAMILY, PropertyAttribute::BOUND,&m_aFont.Family, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTCHARSET, PROPERTY_ID_FONTCHARSET, PropertyAttribute::BOUND,&m_aFont.CharSet, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTPITCH, PROPERTY_ID_FONTPITCH, PropertyAttribute::BOUND,&m_aFont.Pitch, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTCHARWIDTH, PROPERTY_ID_FONTCHARWIDTH, PropertyAttribute::BOUND,&m_aFont.CharacterWidth, cppu::UnoType<float>::get()); + registerProperty(PROPERTY_FONTWEIGHT, PROPERTY_ID_FONTWEIGHT, PropertyAttribute::BOUND,&m_aFont.Weight, cppu::UnoType<float>::get()); + registerProperty(PROPERTY_FONTSLANT, PROPERTY_ID_FONTSLANT, PropertyAttribute::BOUND,&m_aFont.Slant, cppu::UnoType<css::awt::FontSlant>::get()); + registerProperty(PROPERTY_FONTUNDERLINE, PROPERTY_ID_FONTUNDERLINE, PropertyAttribute::BOUND,&m_aFont.Underline, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTSTRIKEOUT, PROPERTY_ID_FONTSTRIKEOUT, PropertyAttribute::BOUND,&m_aFont.Strikeout, cppu::UnoType<sal_Int16>::get()); + registerProperty(PROPERTY_FONTORIENTATION, PROPERTY_ID_FONTORIENTATION, PropertyAttribute::BOUND,&m_aFont.Orientation, cppu::UnoType<float>::get()); + registerProperty(PROPERTY_FONTKERNING, PROPERTY_ID_FONTKERNING, PropertyAttribute::BOUND,&m_aFont.Kerning, cppu::UnoType<sal_Bool>::get()); + registerProperty(PROPERTY_FONTWORDLINEMODE, PROPERTY_ID_FONTWORDLINEMODE,PropertyAttribute::BOUND,&m_aFont.WordLineMode, cppu::UnoType<sal_Bool>::get()); + registerProperty(PROPERTY_FONTTYPE, PROPERTY_ID_FONTTYPE, PropertyAttribute::BOUND,&m_aFont.Type, cppu::UnoType<sal_Int16>::get()); + + refreshColumns(); +} + +::cppu::IPropertyArrayHelper* ODBTable::createArrayHelper( sal_Int32 _nId) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + if(!_nId) + { + for(Property & prop : asNonConstRange(aProps)) + { + if (prop.Name == PROPERTY_CATALOGNAME) + prop.Attributes = PropertyAttribute::READONLY; + else if (prop.Name == PROPERTY_SCHEMANAME) + prop.Attributes = PropertyAttribute::READONLY; + else if (prop.Name == PROPERTY_DESCRIPTION) + prop.Attributes = PropertyAttribute::READONLY; + else if (prop.Name == PROPERTY_NAME) + prop.Attributes = PropertyAttribute::READONLY; + } + } + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +::cppu::IPropertyArrayHelper & SAL_CALL ODBTable::getInfoHelper() +{ + return *ODBTable_PROP::getArrayHelper(isNew() ? 1 : 0); +} + +// XServiceInfo +OUString SAL_CALL ODBTable::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.ODBTable"; + } +sal_Bool SAL_CALL ODBTable::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL ODBTable::getSupportedServiceNames() +{ + return { SERVICE_SDBCX_TABLE }; +} + + +Any SAL_CALL ODBTable::queryInterface( const Type & rType ) +{ + if(rType == cppu::UnoType<XRename>::get()&& !getRenameService().is() ) + return Any(); + if(rType == cppu::UnoType<XAlterTable>::get()&& !getAlterService().is() ) + return Any(); + return OTable_Base::queryInterface( rType); +} + +Sequence< Type > SAL_CALL ODBTable::getTypes( ) +{ + Type aRenameType = cppu::UnoType<XRename>::get(); + Type aAlterType = cppu::UnoType<XAlterTable>::get(); + + Sequence< Type > aTypes(OTable_Base::getTypes()); + std::vector<Type> aOwnTypes; + aOwnTypes.reserve(aTypes.getLength()); + + const Type* pIter = aTypes.getConstArray(); + const Type* pEnd = pIter + aTypes.getLength(); + for(;pIter != pEnd ;++pIter) + { + if( (*pIter != aRenameType || getRenameService().is()) && (*pIter != aAlterType || getAlterService().is())) + aOwnTypes.push_back(*pIter); + } + + return Sequence< Type >(aOwnTypes.data(), aOwnTypes.size()); +} + +// XRename, +void SAL_CALL ODBTable::rename( const OUString& _rNewName ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed); + if ( !getRenameService().is() ) + throw SQLException(DBA_RES(RID_STR_NO_TABLE_RENAME),*this,SQLSTATE_GENERAL,1000,Any() ); + + Reference<XPropertySet> xTable(this); + getRenameService()->rename(xTable,_rNewName); + ::connectivity::OTable_TYPEDEF::rename(_rNewName); +} + +// XAlterTable, +void SAL_CALL ODBTable::alterColumnByName( const OUString& _rName, const Reference< XPropertySet >& _rxDescriptor ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(connectivity::sdbcx::OTableDescriptor_BASE::rBHelper.bDisposed); + if ( !getAlterService().is() ) + throw SQLException(DBA_RES(RID_STR_NO_TABLE_RENAME),*this,SQLSTATE_GENERAL,1000,Any() ); + + if ( !m_xColumns->hasByName(_rName) ) + throw SQLException(DBA_RES(RID_STR_COLUMN_NOT_VALID),*this,SQLSTATE_GENERAL,1000,Any() ); + + Reference<XPropertySet> xTable(this); + getAlterService()->alterColumnByName(xTable,_rName,_rxDescriptor); + m_xColumns->refresh(); +} + +sal_Int64 SAL_CALL ODBTable::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf<OTable_Base>{}); +} + +const Sequence< sal_Int8 > & ODBTable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit s_Id; + return s_Id.getSeq(); +} + +Reference< XPropertySet > ODBTable::createColumnDescriptor() +{ + return new OTableColumnDescriptor( true ); +} + +sdbcx::OCollection* ODBTable::createColumns(const ::std::vector< OUString>& _rNames) +{ + Reference<XDatabaseMetaData> xMeta = getMetaData(); + OColumns* pCol = new OColumns(*this, m_aMutex, nullptr, isCaseSensitive(), _rNames, this,this, + getAlterService().is() || (xMeta.is() && xMeta->supportsAlterTableWithAddColumn()), + getAlterService().is() || (xMeta.is() && xMeta->supportsAlterTableWithDropColumn())); + static_cast<OColumnsHelper*>(pCol)->setParent(this); + pCol->setParent(*this); + m_pColumnMediator = new OContainerMediator( pCol, m_xColumnDefinitions ); + pCol->setMediator( m_pColumnMediator.get() ); + return pCol; +} + +sdbcx::OCollection* ODBTable::createKeys(const ::std::vector< OUString>& _rNames) +{ + return new connectivity::OKeysHelper(this,m_aMutex,_rNames); +} + +sdbcx::OCollection* ODBTable::createIndexes(const ::std::vector< OUString>& _rNames) +{ + return new OIndexes(this,m_aMutex,_rNames,nullptr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/tablecontainer.cxx b/dbaccess/source/core/api/tablecontainer.cxx new file mode 100644 index 000000000..3971dd154 --- /dev/null +++ b/dbaccess/source/core/api/tablecontainer.cxx @@ -0,0 +1,456 @@ +/* -*- 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 . + */ + +#include <tablecontainer.hxx> +#include <table.hxx> +#include <comphelper/property.hxx> +#include <comphelper/processfactory.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/sdb/TableDefinition.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <TableDeco.hxx> +#include <sdbcoretools.hxx> +#include <ContainerMediator.hxx> +#include <objectnameapproval.hxx> +#include <tools/diagnose_ex.h> + +using namespace dbaccess; +using namespace dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::connectivity::sdbcx; + +namespace +{ + bool lcl_isPropertySetDefaulted(const Sequence< OUString>& _aNames,const Reference<XPropertySet>& _xProp) + { + Reference<XPropertyState> xState(_xProp,UNO_QUERY); + if ( !xState ) + return false; + const OUString* pIter = _aNames.getConstArray(); + const OUString* pEnd = pIter + _aNames.getLength(); + for(;pIter != pEnd;++pIter) + { + try + { + PropertyState aState = xState->getPropertyState(*pIter); + if ( aState != PropertyState_DEFAULT_VALUE ) + break; + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "" ); + } + } + return ( pIter == pEnd ); + } +} + +// OTableContainer + +OTableContainer::OTableContainer(::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const Reference< XConnection >& _xCon, + bool _bCase, + const Reference< XNameContainer >& _xTableDefinitions, + IRefreshListener* _pRefreshListener, + std::atomic<std::size_t>& _nInAppend) + :OFilteredContainer(_rParent,_rMutex,_xCon,_bCase,_pRefreshListener,_nInAppend) + ,m_xTableDefinitions(_xTableDefinitions) +{ +} + +OTableContainer::~OTableContainer() +{ +} + +void OTableContainer::removeMasterContainerListener() +{ + try + { + Reference<XContainer> xCont( m_xMasterContainer, UNO_QUERY_THROW ); + xCont->removeContainerListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +OUString OTableContainer::getTableTypeRestriction() const +{ + // no restriction at all (other than the ones provided externally) + return OUString(); +} + +// XServiceInfo +OUString SAL_CALL OTableContainer::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.OTableContainer"; + } +sal_Bool SAL_CALL OTableContainer::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OTableContainer::getSupportedServiceNames() +{ + return { SERVICE_SDBCX_CONTAINER, SERVICE_SDBCX_TABLES }; +} + + +namespace +{ +void lcl_createDefinitionObject(const OUString& _rName + ,const Reference< XNameContainer >& _xTableDefinitions + ,Reference<XPropertySet>& _xTableDefinition + ,Reference<XNameAccess>& _xColumnDefinitions) +{ + if ( !_xTableDefinitions.is() ) + return; + + if ( _xTableDefinitions->hasByName(_rName) ) + _xTableDefinition.set(_xTableDefinitions->getByName(_rName),UNO_QUERY); + else + { + // set as folder + _xTableDefinition = TableDefinition::createWithName( ::comphelper::getProcessComponentContext(), _rName ); + _xTableDefinitions->insertByName(_rName,Any(_xTableDefinition)); + } + Reference<XColumnsSupplier> xColumnsSupplier(_xTableDefinition,UNO_QUERY); + if ( xColumnsSupplier.is() ) + _xColumnDefinitions = xColumnsSupplier->getColumns(); +} + +} + +connectivity::sdbcx::ObjectType OTableContainer::createObject(const OUString& _rName) +{ + Reference<XColumnsSupplier > xSup; + if(m_xMasterContainer.is() && m_xMasterContainer->hasByName(_rName)) + xSup.set(m_xMasterContainer->getByName(_rName),UNO_QUERY); + + connectivity::sdbcx::ObjectType xRet; + if ( m_xMetaData.is() ) + { + Reference<XPropertySet> xTableDefinition; + Reference<XNameAccess> xColumnDefinitions; + lcl_createDefinitionObject(_rName,m_xTableDefinitions,xTableDefinition,xColumnDefinitions); + + if ( xSup.is() ) + { + rtl::Reference<ODBTableDecorator> pTable = new ODBTableDecorator( m_xConnection, xSup, ::dbtools::getNumberFormats( m_xConnection ) ,xColumnDefinitions); + xRet = pTable; + pTable->construct(); + } + else + { + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, + _rName, + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + Any aCatalog; + if(!sCatalog.isEmpty()) + aCatalog <<= sCatalog; + OUString sType,sDescription; + Sequence< OUString> aTypeFilter; + getAllTableTypeFilter( aTypeFilter ); + + Reference< XResultSet > xRes; + if ( m_xMetaData.is() ) + xRes = m_xMetaData->getTables(aCatalog,sSchema,sTable,aTypeFilter); + if(xRes.is() && xRes->next()) + { + Reference< XRow > xRow(xRes,UNO_QUERY); + if(xRow.is()) + { + sType = xRow->getString(4); + sDescription = xRow->getString(5); + } + } + ::comphelper::disposeComponent(xRes); + rtl::Reference<ODBTable> pTable = new ODBTable(this + ,m_xConnection + ,sCatalog + ,sSchema + ,sTable + ,sType + ,sDescription + ,xColumnDefinitions); + xRet = pTable; + pTable->construct(); + } + Reference<XPropertySet> xDest(xRet,UNO_QUERY); + if ( xTableDefinition.is() ) + ::comphelper::copyProperties(xTableDefinition,xDest); + + if ( !m_pTableMediator.is() ) + m_pTableMediator = new OContainerMediator( + this, m_xTableDefinitions ); + if ( m_pTableMediator.is() ) + m_pTableMediator->notifyElementCreated(_rName,xDest); + } + + return xRet; +} + +Reference< XPropertySet > OTableContainer::createDescriptor() +{ + Reference< XPropertySet > xRet; + + // first we have to look if the master tables support this + // and if so then create a table object as well with the master tables + Reference<XColumnsSupplier > xMasterColumnsSup; + Reference<XDataDescriptorFactory> xDataFactory(m_xMasterContainer,UNO_QUERY); + if ( xDataFactory.is() && m_xMetaData.is() ) + { + xMasterColumnsSup.set( xDataFactory->createDataDescriptor(), UNO_QUERY ); + rtl::Reference<ODBTableDecorator> pTable = new ODBTableDecorator( m_xConnection, xMasterColumnsSup, ::dbtools::getNumberFormats( m_xConnection ) ,nullptr); + xRet = pTable; + pTable->construct(); + } + else + { + rtl::Reference<ODBTable> pTable = new ODBTable(this, m_xConnection); + xRet = pTable; + pTable->construct(); + } + return xRet; +} + +// XAppend +ObjectType OTableContainer::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + // append the new table with a create stmt + OUString aName = getString(descriptor->getPropertyValue(PROPERTY_NAME)); + if(m_xMasterContainer.is() && m_xMasterContainer->hasByName(aName)) + { + OUString sMessage(DBA_RES(RID_STR_TABLE_IS_FILTERED)); + throw SQLException(sMessage.replaceAll("$name$", aName),static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this)),SQLSTATE_GENERAL,1000,Any()); + } + + Reference< XConnection > xConnection( m_xConnection.get(), UNO_QUERY ); + PContainerApprove pApprove = std::make_shared<ObjectNameApproval>( xConnection, ObjectNameApproval::TypeTable ); + pApprove->approveElement( aName ); + + { + EnsureReset aReset(m_nInAppend); + Reference<XAppend> xAppend(m_xMasterContainer,UNO_QUERY); + if(xAppend.is()) + { + xAppend->appendByDescriptor(descriptor); + } + else + { + OUString aSql = ::dbtools::createSqlCreateTableStatement(descriptor,m_xConnection); + + Reference<XConnection> xCon = m_xConnection; + OSL_ENSURE(xCon.is(),"Connection is null!"); + if ( xCon.is() ) + { + Reference< XStatement > xStmt = xCon->createStatement( ); + if ( xStmt.is() ) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + } + } + + Reference<XPropertySet> xTableDefinition; + Reference<XNameAccess> xColumnDefinitions; + lcl_createDefinitionObject(getNameForObject(descriptor),m_xTableDefinitions,xTableDefinition,xColumnDefinitions); + Reference<XColumnsSupplier> xSup(descriptor,UNO_QUERY); + Reference<XDataDescriptorFactory> xFac(xColumnDefinitions,UNO_QUERY); + Reference<XAppend> xAppend(xColumnDefinitions,UNO_QUERY); + bool bModified = false; + if ( xSup.is() && xColumnDefinitions.is() && xFac.is() && xAppend.is() ) + { + Reference<XNameAccess> xNames = xSup->getColumns(); + if ( xNames.is() ) + { + Reference<XPropertySet> xProp = xFac->createDataDescriptor(); + Sequence< OUString> aSeq = xNames->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( !xColumnDefinitions->hasByName(*pIter) ) + { + Reference<XPropertySet> xColumn(xNames->getByName(*pIter),UNO_QUERY); + if ( !OColumnSettings::hasDefaultSettings( xColumn ) ) + { + ::comphelper::copyProperties( xColumn, xProp ); + xAppend->appendByDescriptor( xProp ); + bModified = true; + } + } + } + } + } + Sequence< OUString> aNames{ + PROPERTY_FILTER, PROPERTY_ORDER, PROPERTY_APPLYFILTER, PROPERTY_FONT, + PROPERTY_ROW_HEIGHT, PROPERTY_TEXTCOLOR, PROPERTY_TEXTLINECOLOR, + PROPERTY_TEXTEMPHASIS, PROPERTY_TEXTRELIEF}; + if ( bModified || !lcl_isPropertySetDefaulted(aNames,xTableDefinition) ) + ::dbaccess::notifyDataSourceModified(m_xTableDefinitions); + + return createObject( _rForName ); +} + +// XDrop +void OTableContainer::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + Reference< XDrop > xDrop(m_xMasterContainer,UNO_QUERY); + if(xDrop.is()) + xDrop->dropByName(_sElementName); + else + { + OUString sComposedName; + + bool bIsView = false; + Reference<XPropertySet> xTable(getObject(_nPos),UNO_QUERY); + if ( xTable.is() && m_xMetaData.is() ) + { + OUString sSchema,sCatalog,sTable; + if (m_xMetaData->supportsCatalogsInTableDefinitions()) + xTable->getPropertyValue(PROPERTY_CATALOGNAME) >>= sCatalog; + if (m_xMetaData->supportsSchemasInTableDefinitions()) + xTable->getPropertyValue(PROPERTY_SCHEMANAME) >>= sSchema; + xTable->getPropertyValue(PROPERTY_NAME) >>= sTable; + + sComposedName = ::dbtools::composeTableName( m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions ); + + OUString sType; + xTable->getPropertyValue(PROPERTY_TYPE) >>= sType; + bIsView = sType.equalsIgnoreAsciiCase("VIEW"); + } + + if(sComposedName.isEmpty()) + ::dbtools::throwFunctionSequenceException(static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this))); + + OUString aSql("DROP "); + + if ( bIsView ) // here we have a view + aSql += "VIEW "; + else + aSql += "TABLE "; + aSql += sComposedName; + Reference<XConnection> xCon = m_xConnection; + OSL_ENSURE(xCon.is(),"Connection is null!"); + if ( xCon.is() ) + { + Reference< XStatement > xStmt = xCon->createStatement( ); + if(xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + } + + if ( m_xTableDefinitions.is() && m_xTableDefinitions->hasByName(_sElementName) ) + { + m_xTableDefinitions->removeByName(_sElementName); + } +} + +void SAL_CALL OTableContainer::elementInserted( const ContainerEvent& Event ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + OUString sName; + Event.Accessor >>= sName; + if ( !m_nInAppend && !hasByName(sName) ) + { + if(!m_xMasterContainer.is() || m_xMasterContainer->hasByName(sName)) + { + ObjectType xName = createObject(sName); + insertElement(sName,xName); + // and notify our listeners + ContainerEvent aEvent(static_cast<XContainer*>(this), Any(sName), Any(xName), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); + } + } +} + +void SAL_CALL OTableContainer::elementRemoved( const ContainerEvent& /*Event*/ ) +{ +} + +void SAL_CALL OTableContainer::elementReplaced( const ContainerEvent& Event ) +{ + // create a new config entry + OUString sOldComposedName,sNewComposedName; + Event.ReplacedElement >>= sOldComposedName; + Event.Accessor >>= sNewComposedName; + + renameObject(sOldComposedName,sNewComposedName); +} + +void OTableContainer::disposing() +{ + OFilteredContainer::disposing(); + // say goodbye to our listeners + m_xTableDefinitions = nullptr; + m_pTableMediator = nullptr; +} + +void SAL_CALL OTableContainer::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} + +void OTableContainer::addMasterContainerListener() +{ + try + { + Reference< XContainer > xCont( m_xMasterContainer, UNO_QUERY_THROW ); + xCont->addContainerListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/api/viewcontainer.cxx b/dbaccess/source/core/api/viewcontainer.cxx new file mode 100644 index 000000000..462a3b1d0 --- /dev/null +++ b/dbaccess/source/core/api/viewcontainer.cxx @@ -0,0 +1,254 @@ +/* -*- 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 . + */ + +#include <strings.hxx> +#include <viewcontainer.hxx> +#include <View.hxx> + +#include <comphelper/types.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> + +using namespace dbaccess; +using namespace dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::connectivity::sdbcx; + +// OViewContainer + +OViewContainer::OViewContainer(::cppu::OWeakObject& _rParent + ,::osl::Mutex& _rMutex + ,const Reference< XConnection >& _xCon + ,bool _bCase + ,IRefreshListener* _pRefreshListener + ,std::atomic<std::size_t>& _nInAppend) + :OFilteredContainer(_rParent,_rMutex,_xCon,_bCase,_pRefreshListener,_nInAppend) + ,m_bInElementRemoved(false) +{ +} + +OViewContainer::~OViewContainer() +{ +} + +// XServiceInfo +OUString SAL_CALL OViewContainer::getImplementationName() + { + return "com.sun.star.sdb.dbaccess.OViewContainer"; + } +sal_Bool SAL_CALL OViewContainer::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OViewContainer::getSupportedServiceNames() +{ + return { SERVICE_SDBCX_CONTAINER, SERVICE_SDBCX_TABLES }; +} + + +ObjectType OViewContainer::createObject(const OUString& _rName) +{ + ObjectType xProp; + if ( m_xMasterContainer.is() && m_xMasterContainer->hasByName(_rName) ) + xProp.set(m_xMasterContainer->getByName(_rName),UNO_QUERY); + + if ( !xProp.is() ) + { + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, + _rName, + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + return new View(m_xConnection, + isCaseSensitive(), + sCatalog, + sSchema, + sTable + ); + } + + return xProp; +} + +Reference< XPropertySet > OViewContainer::createDescriptor() +{ + Reference< XPropertySet > xRet; + // first we have to look if the master tables support this + // and if so then create a table object as well with the master tables + Reference<XDataDescriptorFactory> xDataFactory(m_xMasterContainer,UNO_QUERY); + if(xDataFactory.is()) + xRet = xDataFactory->createDataDescriptor(); + else + xRet = new ::connectivity::sdbcx::OView(isCaseSensitive(),m_xMetaData); + + return xRet; +} + +// XAppend +ObjectType OViewContainer::appendObject( const OUString& _rForName, const Reference< XPropertySet >& descriptor ) +{ + // append the new table with a create stmt + OUString aName = getString(descriptor->getPropertyValue(PROPERTY_NAME)); + + Reference<XAppend> xAppend(m_xMasterContainer,UNO_QUERY); + Reference< XPropertySet > xProp = descriptor; + if(xAppend.is()) + { + EnsureReset aReset(m_nInAppend); + + xAppend->appendByDescriptor(descriptor); + if(m_xMasterContainer->hasByName(aName)) + xProp.set(m_xMasterContainer->getByName(aName),UNO_QUERY); + } + else + { + OUString sComposedName = ::dbtools::composeTableName( m_xMetaData, descriptor, ::dbtools::EComposeRule::InTableDefinitions, true ); + if(sComposedName.isEmpty()) + ::dbtools::throwFunctionSequenceException(static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this))); + + OUString sCommand; + descriptor->getPropertyValue(PROPERTY_COMMAND) >>= sCommand; + + OUString aSQL = "CREATE VIEW " + sComposedName + " AS " + sCommand; + + Reference<XConnection> xCon = m_xConnection; + OSL_ENSURE(xCon.is(),"Connection is null!"); + if ( xCon.is() ) + { + ::utl::SharedUNOComponent< XStatement > xStmt( xCon->createStatement() ); + if ( xStmt.is() ) + xStmt->execute( aSQL ); + } + } + + return createObject( _rForName ); +} + +// XDrop +void OViewContainer::dropObject(sal_Int32 _nPos, const OUString& _sElementName) +{ + if ( m_bInElementRemoved ) + return; + + Reference< XDrop > xDrop(m_xMasterContainer,UNO_QUERY); + if(xDrop.is()) + xDrop->dropByName(_sElementName); + else + { + OUString sComposedName; + + Reference<XPropertySet> xTable(getObject(_nPos),UNO_QUERY); + if ( xTable.is() ) + { + OUString sCatalog,sSchema,sTable; + xTable->getPropertyValue(PROPERTY_CATALOGNAME) >>= sCatalog; + xTable->getPropertyValue(PROPERTY_SCHEMANAME) >>= sSchema; + xTable->getPropertyValue(PROPERTY_NAME) >>= sTable; + + sComposedName = ::dbtools::composeTableName( m_xMetaData, sCatalog, sSchema, sTable, true, ::dbtools::EComposeRule::InTableDefinitions ); + } + + if(sComposedName.isEmpty()) + ::dbtools::throwFunctionSequenceException(static_cast<XTypeProvider*>(static_cast<OFilteredContainer*>(this))); + + OUString aSql = "DROP VIEW " + sComposedName; + Reference<XConnection> xCon = m_xConnection; + OSL_ENSURE(xCon.is(),"Connection is null!"); + if ( xCon.is() ) + { + Reference< XStatement > xStmt = xCon->createStatement( ); + if(xStmt.is()) + xStmt->execute(aSql); + ::comphelper::disposeComponent(xStmt); + } + } +} + +void SAL_CALL OViewContainer::elementInserted( const ContainerEvent& Event ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + OUString sName; + if ( ( Event.Accessor >>= sName ) + && ( !m_nInAppend ) + && ( !hasByName( sName ) ) + ) + { + Reference<XPropertySet> xProp(Event.Element,UNO_QUERY); + OUString sType; + xProp->getPropertyValue(PROPERTY_TYPE) >>= sType; + if ( sType == "VIEW" ) + insertElement(sName,createObject(sName)); + } +} + +void SAL_CALL OViewContainer::elementRemoved( const ContainerEvent& Event ) +{ + ::osl::MutexGuard aGuard(m_rMutex); + OUString sName; + if ( !((Event.Accessor >>= sName) && hasByName(sName)) ) + return; + + m_bInElementRemoved = true; + try + { + dropByName(sName); + } + catch(Exception&) + { + m_bInElementRemoved = false; + throw; + } + m_bInElementRemoved = false; +} + +void SAL_CALL OViewContainer::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} + +void SAL_CALL OViewContainer::elementReplaced( const ContainerEvent& /*Event*/ ) +{ +} + +OUString OViewContainer::getTableTypeRestriction() const +{ + // no restriction at all (other than the ones provided externally) + return "VIEW"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |