diff options
Diffstat (limited to 'dbaccess/source/core')
153 files changed, 48785 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: */ diff --git a/dbaccess/source/core/dataaccess/ComponentDefinition.cxx b/dbaccess/source/core/dataaccess/ComponentDefinition.cxx new file mode 100644 index 000000000..a3a645982 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ComponentDefinition.cxx @@ -0,0 +1,285 @@ +/* -*- 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 "ComponentDefinition.hxx" +#include <stringconstants.hxx> +#include <strings.hxx> + +#include <osl/diagnose.h> +#include <com/sun/star/beans/PropertyAttribute.hpp> +//#include <cppuhelper/interfacecontainer.hxx> +#include <comphelper/property.hxx> +#include <comphelper/propertysequence.hxx> +#include <definitioncolumn.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::cppu; + +namespace dbaccess +{ + +/// helper class for column property change events which holds the OComponentDefinition weak +class OColumnPropertyListener: + public ::cppu::WeakImplHelper< XPropertyChangeListener > +{ + OComponentDefinition* m_pComponent; +protected: + virtual ~OColumnPropertyListener() override {} +public: + explicit OColumnPropertyListener(OComponentDefinition* _pComponent) : m_pComponent(_pComponent){} + OColumnPropertyListener(const OColumnPropertyListener&) = delete; + const OColumnPropertyListener& operator=(const OColumnPropertyListener&) = delete; + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const PropertyChangeEvent& /*_rEvent*/ ) override + { + if ( m_pComponent ) + m_pComponent->notifyDataSourceModified(); + } + // XEventListener + virtual void SAL_CALL disposing( const EventObject& /*_rSource*/ ) override + { + } + void clear() { m_pComponent = nullptr; } +}; + +OComponentDefinition_Impl::OComponentDefinition_Impl() +{ +} + +OComponentDefinition_Impl::~OComponentDefinition_Impl() +{ +} + +// OComponentDefinition + + +void OComponentDefinition::initialize( const Sequence< Any >& aArguments ) +{ + OUString rName; + if( (aArguments.getLength() == 1) && (aArguments[0] >>= rName) ) + { + Sequence<Any> aNewArgs(comphelper::InitAnyPropertySequence( + { + {PROPERTY_NAME, Any(rName)} + })); + OContentHelper::initialize(aNewArgs); + } + else + OContentHelper::initialize(aArguments); +} + +void OComponentDefinition::registerProperties() +{ + m_xColumnPropertyListener = new OColumnPropertyListener(this); + OComponentDefinition_Impl& rDefinition( getDefinition() ); + ODataSettings::registerPropertiesFor( &rDefinition ); + + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND | PropertyAttribute::READONLY|PropertyAttribute::CONSTRAINED, + &rDefinition.m_aProps.aTitle, cppu::UnoType<decltype(rDefinition.m_aProps.aTitle)>::get()); + + if ( m_bTable ) + { + registerProperty(PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, PropertyAttribute::BOUND, + &rDefinition.m_sSchemaName, cppu::UnoType<decltype(rDefinition.m_sSchemaName)>::get()); + + registerProperty(PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, PropertyAttribute::BOUND, + &rDefinition.m_sCatalogName, cppu::UnoType<decltype(rDefinition.m_sCatalogName)>::get()); + } +} + +OComponentDefinition::OComponentDefinition(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTable) + :OContentHelper(_xORB,_xParentContainer,_pImpl) + ,ODataSettings(OContentHelper::rBHelper,!_bTable) + ,m_bTable(_bTable) +{ + registerProperties(); +} + +OComponentDefinition::~OComponentDefinition() +{ +} + +OComponentDefinition::OComponentDefinition( const Reference< XInterface >& _rxContainer + ,const OUString& _rElementName + ,const Reference< XComponentContext >& _xORB + ,const TContentPtr& _pImpl + ,bool _bTable) + :OContentHelper(_xORB,_rxContainer,_pImpl) + ,ODataSettings(OContentHelper::rBHelper,!_bTable) + ,m_bTable(_bTable) +{ + registerProperties(); + + m_pImpl->m_aProps.aTitle = _rElementName; + OSL_ENSURE(!m_pImpl->m_aProps.aTitle.isEmpty(), "OComponentDefinition::OComponentDefinition : invalid name !"); +} + +css::uno::Sequence<sal_Int8> OComponentDefinition::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > OComponentDefinition::getTypes() +{ + return ::comphelper::concatSequences( + ODataSettings::getTypes( ), + OContentHelper::getTypes( ), + OComponentDefinition_BASE::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE3( OComponentDefinition,OContentHelper,ODataSettings,OComponentDefinition_BASE) + +OUString SAL_CALL OComponentDefinition::getImplementationName() +{ + return "com.sun.star.comp.dba.OComponentDefinition"; +} + +Sequence< OUString > SAL_CALL OComponentDefinition::getSupportedServiceNames() +{ + return { "com.sun.star.sdb.TableDefinition", "com.sun.star.ucb.Content" }; +} + +void SAL_CALL OComponentDefinition::disposing() +{ + OContentHelper::disposing(); + if (m_pColumns) + { + m_pColumns->disposing(); + } + m_xColumnPropertyListener->clear(); + m_xColumnPropertyListener.clear(); +} + +IPropertyArrayHelper& OComponentDefinition::getInfoHelper() +{ + return *getArrayHelper(); +} + +IPropertyArrayHelper* OComponentDefinition::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new OPropertyArrayHelper(aProps); +} + +Reference< XPropertySetInfo > SAL_CALL OComponentDefinition::getPropertySetInfo( ) +{ + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString OComponentDefinition::determineContentType() const +{ + return m_bTable + ? OUString( "application/vnd.org.openoffice.DatabaseTable" ) + : OUString( "application/vnd.org.openoffice.DatabaseCommandDefinition" ); +} + +Reference< XNameAccess> OComponentDefinition::getColumns() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OContentHelper::rBHelper.bDisposed); + + if (!m_pColumns) + { + std::vector< OUString> aNames; + + const OComponentDefinition_Impl& rDefinition( getDefinition() ); + aNames.reserve( rDefinition.size() ); + + for (auto const& definition : rDefinition) + aNames.push_back(definition.first); + + m_pColumns.reset(new OColumns(*this, m_aMutex, true, aNames, this, nullptr, true, false, false)); + m_pColumns->setParent(*this); + } + // see OCollection::acquire + return m_pColumns.get(); +} + +rtl::Reference<OColumn> OComponentDefinition::createColumn(const OUString& _rName) const +{ + const OComponentDefinition_Impl& rDefinition( getDefinition() ); + OComponentDefinition_Impl::const_iterator aFind = rDefinition.find( _rName ); + if ( aFind != rDefinition.end() ) + { + aFind->second->addPropertyChangeListener(OUString(),m_xColumnPropertyListener); + return new OTableColumnWrapper( aFind->second, aFind->second, true ); + } + OSL_FAIL( "OComponentDefinition::createColumn: is this a valid case?" ); + // This here is the last place creating a OTableColumn, and somehow /me thinks it is not needed ... + return new OTableColumn( _rName ); +} + +Reference< XPropertySet > OComponentDefinition::createColumnDescriptor() +{ + return new OTableColumnDescriptor( true ); +} + +void OComponentDefinition::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + ODataSettings::setFastPropertyValue_NoBroadcast(nHandle,rValue); + notifyDataSourceModified(); +} + +void OComponentDefinition::columnDropped(const OUString& _sName) +{ + getDefinition().erase( _sName ); + notifyDataSourceModified(); +} + +void OComponentDefinition::columnAppended( const Reference< XPropertySet >& _rxSourceDescriptor ) +{ + OUString sName; + _rxSourceDescriptor->getPropertyValue( PROPERTY_NAME ) >>= sName; + + Reference<XPropertySet> xColDesc = new OTableColumnDescriptor( true ); + ::comphelper::copyProperties( _rxSourceDescriptor, xColDesc ); + getDefinition().insert( sName, xColDesc ); + + // formerly, here was a setParent at the xColDesc. The parent used was an adapter (ChildHelper_Impl) + // which held another XChild weak, and forwarded all getParent requests to this other XChild. + // m_pColumns was used for this. This was nonsense, since m_pColumns dies when our instance dies, + // but xColDesc will live longer than this. So effectively, the setParent call was pretty useless. + // + // The intention for this parenting was that the column descriptor is able to find the data source, + // by traveling up the parent hierarchy until there's an XDataSource. This didn't work (which + // for instance causes #i65023#). We need another way to properly ensure this. + + notifyDataSourceModified(); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_OComponentDefinition(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new dbaccess::OComponentDefinition( + context, nullptr, std::make_shared<dbaccess::OComponentDefinition_Impl>())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/ComponentDefinition.hxx b/dbaccess/source/core/dataaccess/ComponentDefinition.hxx new file mode 100644 index 000000000..4c10a5354 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ComponentDefinition.hxx @@ -0,0 +1,159 @@ +/* -*- 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 <commandbase.hxx> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/proparrhlp.hxx> +#include <rtl/ref.hxx> +#include <datasettings.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <ContentHelper.hxx> +#include <apitools.hxx> +#include <column.hxx> + +#include <memory> +namespace dbaccess +{ + + typedef ::cppu::ImplHelper1< css::sdbcx::XColumnsSupplier > OComponentDefinition_BASE; + + class OComponentDefinition_Impl : public OContentHelper_Impl + ,public ODataSettings_Base + { + public: + typedef std::map < OUString + , css::uno::Reference< css::beans::XPropertySet > + > Columns; + typedef Columns::iterator iterator; + typedef Columns::const_iterator const_iterator; + + private: + Columns m_aColumns; + + public: + OUString m_sSchemaName; + OUString m_sCatalogName; + + public: + OComponentDefinition_Impl(); + virtual ~OComponentDefinition_Impl() override; + + size_t size() const { return m_aColumns.size(); } + + const_iterator begin() const { return m_aColumns.begin(); } + const_iterator end() const { return m_aColumns.end(); } + + const_iterator find( const OUString& _rName ) const { return m_aColumns.find( _rName ); } + + void erase( const OUString& _rName ) { m_aColumns.erase( _rName ); } + + void insert( const OUString& _rName, const css::uno::Reference< css::beans::XPropertySet >& _rxColumn ) + { + OSL_PRECOND( m_aColumns.find( _rName ) == m_aColumns.end(), "OComponentDefinition_Impl::insert: there's already an element with this name!" ); + m_aColumns.emplace( _rName, _rxColumn ); + } + }; + +class OColumnPropertyListener; +// OComponentDefinition - a database "document" which describes a query +class OComponentDefinition :public OContentHelper + ,public ODataSettings + ,public IColumnFactory + ,public OComponentDefinition_BASE + ,public ::comphelper::OPropertyArrayUsageHelper< OComponentDefinition > +{ + // no Reference! see OCollection::acquire + std::unique_ptr<OColumns> m_pColumns; + rtl::Reference<OColumnPropertyListener> m_xColumnPropertyListener; + bool m_bTable; + +protected: + virtual ~OComponentDefinition() override; + virtual void SAL_CALL disposing() override; + + const OComponentDefinition_Impl& getDefinition() const { return dynamic_cast< const OComponentDefinition_Impl& >( *m_pImpl ); } + OComponentDefinition_Impl& getDefinition() { return dynamic_cast< OComponentDefinition_Impl& >( *m_pImpl ); } +public: + OComponentDefinition( + const css::uno::Reference< css::uno::XComponentContext >&, + const css::uno::Reference< css::uno::XInterface >& _xParentContainer, + const TContentPtr& _pImpl, + bool _bTable = true); + + OComponentDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer + ,const OUString& _rElementName + ,const css::uno::Reference< css::uno::XComponentContext >& + ,const TContentPtr& _pImpl + ,bool _bTable = true + ); + + 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::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( css::uno::Sequence< css::uno::Any > const & rArguments) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // 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; + using OContentHelper::notifyDataSourceModified; + +protected: +// OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) 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/dataaccess/ContentHelper.cxx b/dbaccess/source/core/dataaccess/ContentHelper.cxx new file mode 100644 index 000000000..558dd7450 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ContentHelper.cxx @@ -0,0 +1,615 @@ +/* -*- 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 <ContentHelper.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <ucbhelper/propertyvalueset.hxx> +#include <ucbhelper/contentidentifier.hxx> +#include <comphelper/servicehelper.hxx> +#include <tools/diagnose_ex.h> +#include <sdbcoretools.hxx> +#include <stringconstants.hxx> +#include <strings.hxx> + +#include <map> + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::cppu; + +OContentHelper_Impl::OContentHelper_Impl() + : m_pDataSource(nullptr) +{ +} + +OContentHelper_Impl::~OContentHelper_Impl() +{ +} + +OContentHelper::OContentHelper(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl) + : OContentHelper_COMPBASE(m_aMutex) + ,m_aContentListeners(m_aMutex) + ,m_aPropertyChangeListeners(m_aMutex) + ,m_xParentContainer( _xParentContainer ) + ,m_aContext( _xORB ) + ,m_pImpl(_pImpl) + ,m_nCommandId(0) +{ +} + +void SAL_CALL OContentHelper::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // say goodbye to our listeners + EventObject aEvt(*this); + m_aContentListeners.disposeAndClear(aEvt); + + m_xParentContainer = nullptr; +} + +OUString SAL_CALL OContentHelper::getImplementationName() + { + return "com.sun.star.comp.sdb.Content"; + } +sal_Bool SAL_CALL OContentHelper::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 OContentHelper::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.Content" }; +} + + +const css::uno::Sequence<sal_Int8> & OContentHelper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit aId; + return aId.getSeq(); +} + +css::uno::Sequence<sal_Int8> OContentHelper::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XContent +Reference< XContentIdentifier > SAL_CALL OContentHelper::getIdentifier( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + OUString aIdentifier( "private:" + impl_getHierarchicalName( true ) ); + return new ::ucbhelper::ContentIdentifier( aIdentifier ); +} + +OUString OContentHelper::impl_getHierarchicalName( bool _includingRootContainer ) const +{ + OUStringBuffer aHierarchicalName( m_pImpl->m_aProps.aTitle ); + Reference< XInterface > xParent = m_xParentContainer; + while( xParent.is() ) + { + Reference<XPropertySet> xProp( xParent, UNO_QUERY ); + Reference< XChild > xChild( xParent, UNO_QUERY ); + xParent.set( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY ); + if ( xProp.is() && xParent.is() ) + { + OUString sName; + xProp->getPropertyValue( PROPERTY_NAME ) >>= sName; + + OUString sPrevious = aHierarchicalName.makeStringAndClear(); + aHierarchicalName.append( sName + "/" + sPrevious ); + } + } + OUString sHierarchicalName( aHierarchicalName.makeStringAndClear() ); + if ( !_includingRootContainer ) + sHierarchicalName = sHierarchicalName.copy( sHierarchicalName.indexOf( '/' ) + 1 ); + return sHierarchicalName; +} + +OUString SAL_CALL OContentHelper::getContentType() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if ( !m_pImpl->m_aProps.aContentType ) + { // content type not yet retrieved + m_pImpl->m_aProps.aContentType = determineContentType(); + } + + return *m_pImpl->m_aProps.aContentType; +} + +void SAL_CALL OContentHelper::addContentEventListener( const Reference< XContentEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( _rxListener.is() ) + m_aContentListeners.addInterface(_rxListener); +} + +void SAL_CALL OContentHelper::removeContentEventListener( const Reference< XContentEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (_rxListener.is()) + m_aContentListeners.removeInterface(_rxListener); +} + +// XCommandProcessor +sal_Int32 SAL_CALL OContentHelper::createCommandIdentifier( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + // Just increase counter on every call to generate an identifier. + return ++m_nCommandId; +} + +Any SAL_CALL OContentHelper::execute( const Command& aCommand, sal_Int32 /*CommandId*/, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + if ( aCommand.Name == "getPropertyValues" ) + { + // getPropertyValues + + Sequence< Property > Properties; + if ( !( aCommand.Argument >>= Properties ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + aRet <<= getPropertyValues( Properties); + } + else if ( aCommand.Name == "setPropertyValues" ) + { + // setPropertyValues + + Sequence< PropertyValue > aProperties; + if ( !( aCommand.Argument >>= aProperties ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + if ( !aProperties.hasElements() ) + { + OSL_FAIL( "No properties!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + aRet <<= setPropertyValues( aProperties ); + } + else if ( aCommand.Name == "getPropertySetInfo" ) + { + // getPropertySetInfo + + Reference<XPropertySet> xProp(*this,UNO_QUERY); + if ( xProp.is() ) + aRet <<= xProp->getPropertySetInfo(); + // aRet <<= getPropertySetInfo(); // TODO + } + else + { + // Unsupported command + + OSL_FAIL( "Content::execute - unsupported command!" ); + + ucbhelper::cancelCommandExecution( + Any( UnsupportedCommandException( + OUString(), + static_cast< cppu::OWeakObject * >( this ) ) ), + Environment ); + // Unreachable + } + + return aRet; +} + +void SAL_CALL OContentHelper::abort( sal_Int32 /*CommandId*/ ) +{ +} + +// XPropertiesChangeNotifier +void SAL_CALL OContentHelper::addPropertiesChangeListener( const Sequence< OUString >& PropertyNames, const Reference< XPropertiesChangeListener >& Listener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + sal_Int32 nCount = PropertyNames.getLength(); + if ( !nCount ) + { + // Note: An empty sequence means a listener for "all" properties. + m_aPropertyChangeListeners.addInterface(OUString(), Listener ); + } + else + { + const OUString* pSeq = PropertyNames.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const OUString& rName = pSeq[ n ]; + if ( !rName.isEmpty() ) + m_aPropertyChangeListeners.addInterface(rName, Listener ); + } + } +} + +void SAL_CALL OContentHelper::removePropertiesChangeListener( const Sequence< OUString >& PropertyNames, const Reference< XPropertiesChangeListener >& Listener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + sal_Int32 nCount = PropertyNames.getLength(); + if ( !nCount ) + { + // Note: An empty sequence means a listener for "all" properties. + m_aPropertyChangeListeners.removeInterface( OUString(), Listener ); + } + else + { + const OUString* pSeq = PropertyNames.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const OUString& rName = pSeq[ n ]; + if ( !rName.isEmpty() ) + m_aPropertyChangeListeners.removeInterface( rName, Listener ); + } + } +} + +// XPropertyContainer +void SAL_CALL OContentHelper::addProperty( const OUString& /*Name*/, sal_Int16 /*Attributes*/, const Any& /*DefaultValue*/ ) +{ + OSL_FAIL( "OContentHelper::addProperty: not implemented!" ); +} + +void SAL_CALL OContentHelper::removeProperty( const OUString& /*Name*/ ) +{ + OSL_FAIL( "OContentHelper::removeProperty: not implemented!" ); +} + +// XInitialization +void SAL_CALL OContentHelper::initialize( const Sequence< Any >& _aArguments ) +{ + const Any* pBegin = _aArguments.getConstArray(); + const Any* pEnd = pBegin + _aArguments.getLength(); + PropertyValue aValue; + for(;pBegin != pEnd;++pBegin) + { + *pBegin >>= aValue; + if ( aValue.Name == "Parent" ) + { + m_xParentContainer.set(aValue.Value,UNO_QUERY); + } + else if ( aValue.Name == PROPERTY_NAME ) + { + aValue.Value >>= m_pImpl->m_aProps.aTitle; + } + else if ( aValue.Name == PROPERTY_PERSISTENT_NAME ) + { + aValue.Value >>= m_pImpl->m_aProps.sPersistentName; + } + } +} + +Sequence< Any > OContentHelper::setPropertyValues(const Sequence< PropertyValue >& rValues ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + Sequence< Any > aRet( rValues.getLength() ); + auto aRetRange = asNonConstRange(aRet); + Sequence< PropertyChangeEvent > aChanges( rValues.getLength() ); + sal_Int32 nChanged = 0; + + PropertyChangeEvent aEvent; + aEvent.Source = static_cast< cppu::OWeakObject * >( this ); + aEvent.Further = false; + aEvent.PropertyHandle = -1; + + const PropertyValue* pValues = rValues.getConstArray(); + sal_Int32 nCount = rValues.getLength(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const PropertyValue& rValue = pValues[ n ]; + + if ( rValue.Name == "ContentType" || rValue.Name == "IsDocument" || rValue.Name == "IsFolder" ) + { + // Read-only property! + aRetRange[ n ] <<= IllegalAccessException("Property is read-only!", + static_cast< cppu::OWeakObject * >( this ) ); + } + else if ( rValue.Name == "Title" ) + { + OUString aNewValue; + if ( rValue.Value >>= aNewValue ) + { + if ( aNewValue != m_pImpl->m_aProps.aTitle ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_pImpl->m_aProps.aTitle; + + try + { + impl_rename_throw( aNewValue ,false); + OSL_ENSURE( m_pImpl->m_aProps.aTitle == aNewValue, "OContentHelper::setPropertyValues('Title'): rename did not work!" ); + + aEvent.NewValue <<= aNewValue; + aChanges.getArray()[ nChanged ] = aEvent; + nChanged++; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "OContentHelper::setPropertyValues('Title'): caught an exception while renaming!" ); + } + } + else + { + // Old value equals new value. No error! + } + } + else + { + aRetRange[ n ] <<= IllegalTypeException("Property value has wrong type!", + static_cast< cppu::OWeakObject * >( this ) ); + } + } + + else + { + aRetRange[ n ] <<= Exception("No property set for storing the value!", + static_cast< cppu::OWeakObject * >( this ) ); + } + } + + if ( nChanged > 0 ) + { + notifyDataSourceModified(); + aGuard.clear(); + aChanges.realloc( nChanged ); + notifyPropertiesChange( aChanges ); + } + + return aRet; +} + +// static +Reference< XRow > OContentHelper::getPropertyValues( const Sequence< Property >& rProperties) +{ + // Note: Empty sequence means "get values of all supported properties". + + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_aContext ); + + sal_Int32 nCount = rProperties.getLength(); + if ( nCount ) + { + const Property* pProps = rProperties.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const Property& rProp = pProps[ n ]; + + // Process Core properties. + + if ( rProp.Name == "ContentType" ) + { + xRow->appendString ( rProp, getContentType() ); + } + else if ( rProp.Name == "Title" ) + { + xRow->appendString ( rProp, m_pImpl->m_aProps.aTitle ); + } + else if ( rProp.Name == "IsDocument" ) + { + xRow->appendBoolean( rProp, m_pImpl->m_aProps.bIsDocument ); + } + else if ( rProp.Name == "IsFolder" ) + { + xRow->appendBoolean( rProp, m_pImpl->m_aProps.bIsFolder ); + } + else + xRow->appendVoid(rProp); + } + } + else + { + // Append all Core Properties. + xRow->appendString ( + Property( "ContentType", -1, + cppu::UnoType<OUString>::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + getContentType() ); + xRow->appendString ( + Property( "Title", -1, + cppu::UnoType<OUString>::get(), + PropertyAttribute::BOUND ), + m_pImpl->m_aProps.aTitle ); + xRow->appendBoolean( + Property( "IsDocument", -1, + cppu::UnoType<bool>::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + m_pImpl->m_aProps.bIsDocument ); + xRow->appendBoolean( + Property( "IsFolder", -1, + cppu::UnoType<bool>::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + m_pImpl->m_aProps.bIsFolder ); + + // @@@ Append other properties supported directly. + } + + return xRow; +} + +void OContentHelper::notifyPropertiesChange( const Sequence< PropertyChangeEvent >& evt ) const +{ + + sal_Int32 nCount = evt.getLength(); + if ( !nCount ) + return; + + // First, notify listeners interested in changes of every property. + comphelper::OInterfaceContainerHelper3<XPropertiesChangeListener>* pAllPropsContainer = m_aPropertyChangeListeners.getContainer( OUString() ); + if ( pAllPropsContainer ) + pAllPropsContainer->notifyEach( &XPropertiesChangeListener::propertiesChange, evt ); + + typedef std::map< XPropertiesChangeListener*, Sequence< PropertyChangeEvent > > PropertiesEventListenerMap; + PropertiesEventListenerMap aListeners; + + const PropertyChangeEvent* propertyChangeEvent = evt.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n, ++propertyChangeEvent ) + { + const PropertyChangeEvent& rEvent = *propertyChangeEvent; + const OUString& rName = rEvent.PropertyName; + + comphelper::OInterfaceContainerHelper3<XPropertiesChangeListener>* pPropsContainer = m_aPropertyChangeListeners.getContainer( rName ); + if ( pPropsContainer ) + { + comphelper::OInterfaceIteratorHelper3 aIter( *pPropsContainer ); + while ( aIter.hasMoreElements() ) + { + Sequence< PropertyChangeEvent >* propertyEvents; + + XPropertiesChangeListener* pListener = aIter.next().get(); + PropertiesEventListenerMap::iterator it = aListeners.find( pListener ); + if ( it == aListeners.end() ) + { + // Not in map - create and insert new entry. + auto pair = aListeners.emplace( pListener, Sequence< PropertyChangeEvent >( nCount )); + propertyEvents = &pair.first->second; + } + else + propertyEvents = &(*it).second; + + propertyEvents->getArray()[n] = rEvent; + } + } + } + + // Notify listeners. + for (auto & rPair : aListeners) + { + XPropertiesChangeListener* pListener = rPair.first; + Sequence< PropertyChangeEvent >& rSeq = rPair.second; + + // Propagate event. + pListener->propertiesChange( rSeq ); + } +} + +// css::lang::XUnoTunnel +sal_Int64 OContentHelper::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +Reference< XInterface > SAL_CALL OContentHelper::getParent( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + return m_xParentContainer; +} + +void SAL_CALL OContentHelper::setParent( const Reference< XInterface >& _xParent ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_xParentContainer = _xParent; +} + +void OContentHelper::impl_rename_throw(const OUString& _sNewName,bool _bNotify ) +{ + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + if ( _sNewName == m_pImpl->m_aProps.aTitle ) + return; + try + { + Sequence<PropertyChangeEvent> aChanges{ + { /* Source */ static_cast<cppu::OWeakObject*>(this), + /* PropertyName */ PROPERTY_NAME, + /* Further */ false, + /* PropertyHandle */ PROPERTY_ID_NAME, + /* OldValue */ Any(m_pImpl->m_aProps.aTitle), + /* NewValue */ Any(_sNewName) } + }; + + aGuard.clear(); + + m_pImpl->m_aProps.aTitle = _sNewName; + if ( _bNotify ) + notifyPropertiesChange( aChanges ); + notifyDataSourceModified(); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(_sNewName,*this); + } +} + +void SAL_CALL OContentHelper::rename( const OUString& newName ) +{ + + impl_rename_throw(newName); + +} + +void OContentHelper::notifyDataSourceModified() +{ + ::dbaccess::notifyDataSourceModified(m_xParentContainer); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/ModelImpl.cxx b/dbaccess/source/core/dataaccess/ModelImpl.cxx new file mode 100644 index 000000000..5a4078fcc --- /dev/null +++ b/dbaccess/source/core/dataaccess/ModelImpl.cxx @@ -0,0 +1,1432 @@ +/* -*- 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 <databasecontext.hxx> +#include "databasedocument.hxx" +#include "datasource.hxx" +#include <definitioncontainer.hxx> +#include <ModelImpl.hxx> +#include <sdbcoretools.hxx> + +#include <com/sun/star/beans/PropertyBag.hpp> +#include <com/sun/star/container/XSet.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/embed/StorageFactory.hpp> +#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/sdb/BooleanComparisonMode.hpp> +#include <com/sun/star/script/DocumentScriptLibraryContainer.hpp> +#include <com/sun/star/script/DocumentDialogLibraryContainer.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <com/sun/star/security/DocumentDigitalSignatures.hpp> +#include <com/sun/star/security/XDocumentDigitalSignatures.hpp> +#include <com/sun/star/task/DocumentMacroConfirmationRequest.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/types.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/signaturestate.hxx> +#include <tools/diagnose_ex.h> +#include <osl/file.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <tools/urlobj.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/tempfile.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <algorithm> + +using namespace css; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +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::embed; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::script; +using namespace ::cppu; +using namespace ::osl; +using namespace ::comphelper; + +namespace dbaccess +{ + +// DocumentStorageAccess +class DocumentStorageAccess : public ::cppu::WeakImplHelper< XDocumentSubStorageSupplier + , XTransactionListener > +{ + typedef std::map< OUString, Reference< XStorage > > NamedStorages; + + ::osl::Mutex m_aMutex; + /// all sub storages which we ever gave to the outer world + NamedStorages m_aExposedStorages; + ODatabaseModelImpl* m_pModelImplementation; + bool m_bPropagateCommitToRoot; + bool m_bDisposingSubStorages; + +public: + explicit DocumentStorageAccess( ODatabaseModelImpl& _rModelImplementation ) + :m_pModelImplementation( &_rModelImplementation ) + ,m_bPropagateCommitToRoot( true ) + ,m_bDisposingSubStorages( false ) + { + } + +protected: + virtual ~DocumentStorageAccess() override + { + } + +public: + void dispose(); + + // XDocumentSubStorageSupplier + virtual Reference< XStorage > SAL_CALL getDocumentSubStorage( const OUString& aStorageName, ::sal_Int32 _nMode ) override; + virtual Sequence< OUString > SAL_CALL getDocumentSubStoragesNames( ) override; + + // XTransactionListener + virtual void SAL_CALL preCommit( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL commited( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL preRevert( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL reverted( const css::lang::EventObject& aEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + /// disposes all storages managed by this instance + void disposeStorages(); + + /// disposes all known sub storages + void commitStorages(); + + /// commits the dedicated "database" storage + bool commitEmbeddedStorage( bool _bPreventRootCommits ); + +private: + /** opens the sub storage with the given name, in the given mode + */ + Reference< XStorage > impl_openSubStorage_nothrow( const OUString& _rStorageName, sal_Int32 _nMode ); + + void impl_suspendCommitPropagation() + { + OSL_ENSURE( m_bPropagateCommitToRoot, "DocumentStorageAccess::impl_suspendCommitPropagation: already suspended" ); + m_bPropagateCommitToRoot = false; + } + void impl_resumeCommitPropagation() + { + OSL_ENSURE( !m_bPropagateCommitToRoot, "DocumentStorageAccess::impl_resumeCommitPropagation: not suspended" ); + m_bPropagateCommitToRoot = true; + } + +}; + +void DocumentStorageAccess::dispose() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + for (auto const& exposedStorage : m_aExposedStorages) + { + try + { + Reference< XTransactionBroadcaster > xBroadcaster(exposedStorage.second, UNO_QUERY); + if ( xBroadcaster.is() ) + xBroadcaster->removeTransactionListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + m_aExposedStorages.clear(); + + m_pModelImplementation = nullptr; +} + +Reference< XStorage > DocumentStorageAccess::impl_openSubStorage_nothrow( const OUString& _rStorageName, sal_Int32 _nDesiredMode ) +{ + OSL_ENSURE( !_rStorageName.isEmpty(),"ODatabaseModelImpl::impl_openSubStorage_nothrow: Invalid storage name!" ); + + Reference< XStorage > xStorage; + try + { + Reference< XStorage > xRootStorage( m_pModelImplementation->getOrCreateRootStorage() ); + if ( xRootStorage.is() ) + { + sal_Int32 nRealMode = m_pModelImplementation->m_bDocumentReadOnly ? ElementModes::READ : _nDesiredMode; + if ( nRealMode == ElementModes::READ ) + { + if ( xRootStorage.is() && !xRootStorage->hasByName( _rStorageName ) ) + return xStorage; + } + + xStorage = xRootStorage->openStorageElement( _rStorageName, nRealMode ); + + Reference< XTransactionBroadcaster > xBroad( xStorage, UNO_QUERY ); + if ( xBroad.is() ) + xBroad->addTransactionListener( this ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return xStorage; +} + +void DocumentStorageAccess::disposeStorages() +{ + m_bDisposingSubStorages = true; + + for (auto & exposedStorage : m_aExposedStorages) + { + try + { + ::comphelper::disposeComponent( exposedStorage.second ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + m_aExposedStorages.clear(); + + m_bDisposingSubStorages = false; +} + +void DocumentStorageAccess::commitStorages() +{ + try + { + for (auto const& exposedStorage : m_aExposedStorages) + { + tools::stor::commitStorageIfWriteable( exposedStorage.second ); + } + } + catch(const WrappedTargetException&) + { + // WrappedTargetException not allowed to leave + throw IOException(); + } +} + +bool DocumentStorageAccess::commitEmbeddedStorage( bool _bPreventRootCommits ) +{ + if ( _bPreventRootCommits ) + impl_suspendCommitPropagation(); + + bool bSuccess = false; + try + { + NamedStorages::const_iterator pos = m_aExposedStorages.find( "database" ); + if ( pos != m_aExposedStorages.end() ) + bSuccess = tools::stor::commitStorageIfWriteable( pos->second ); + } + catch( Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( _bPreventRootCommits ) + impl_resumeCommitPropagation(); + + return bSuccess; + +} + +Reference< XStorage > SAL_CALL DocumentStorageAccess::getDocumentSubStorage( const OUString& aStorageName, ::sal_Int32 _nDesiredMode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + NamedStorages::const_iterator pos = m_aExposedStorages.find( aStorageName ); + if ( pos == m_aExposedStorages.end() ) + { + Reference< XStorage > xResult = impl_openSubStorage_nothrow( aStorageName, _nDesiredMode ); + pos = m_aExposedStorages.emplace( aStorageName, xResult ).first; + } + + return pos->second; +} + +Sequence< OUString > SAL_CALL DocumentStorageAccess::getDocumentSubStoragesNames( ) +{ + Reference< XStorage > xRootStor( m_pModelImplementation->getRootStorage() ); + if ( !xRootStor.is() ) + return Sequence< OUString >(); + + std::vector< OUString > aNames; + + const Sequence< OUString > aElementNames( xRootStor->getElementNames() ); + for ( OUString const & name : aElementNames ) + { + if ( xRootStor->isStorageElement( name ) ) + aNames.push_back( name ); + } + return aNames.empty() + ? Sequence< OUString >() + : Sequence< OUString >( aNames.data(), aNames.size() ); +} + +void SAL_CALL DocumentStorageAccess::preCommit( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::commited( const css::lang::EventObject& aEvent ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pModelImplementation ) + m_pModelImplementation->setModified( true ); + + if ( !(m_pModelImplementation && m_bPropagateCommitToRoot) ) + return; + + Reference< XStorage > xStorage( aEvent.Source, UNO_QUERY ); + + // check if this is the dedicated "database" sub storage + NamedStorages::const_iterator pos = m_aExposedStorages.find( "database" ); + if ( ( pos != m_aExposedStorages.end() ) + && ( pos->second == xStorage ) + ) + { + // if so, also commit the root storage + m_pModelImplementation->commitRootStorage(); + } +} + +void SAL_CALL DocumentStorageAccess::preRevert( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::reverted( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::disposing( const css::lang::EventObject& Source ) +{ + OSL_ENSURE( Reference< XStorage >( Source.Source, UNO_QUERY ).is(), "DocumentStorageAccess::disposing: No storage? What's this?" ); + + if ( m_bDisposingSubStorages ) + return; + + auto find = std::find_if(m_aExposedStorages.begin(), m_aExposedStorages.end(), + [&Source](const NamedStorages::value_type& rEntry) { return rEntry.second == Source.Source; }); + if (find != m_aExposedStorages.end()) + m_aExposedStorages.erase( find ); +} + +// ODatabaseModelImpl + +ODatabaseModelImpl::ODatabaseModelImpl( const Reference< XComponentContext >& _rxContext, ODatabaseContext& _rDBContext ) + :m_aContainer(4) + ,m_aMacroMode( *this ) + ,m_nImposedMacroExecMode( MacroExecMode::NEVER_EXECUTE ) + ,m_rDBContext( _rDBContext ) + ,m_refCount(0) + ,m_bModificationLock( false ) + ,m_bDocumentInitialized( false ) + ,m_nScriptingSignatureState(SignatureState::UNKNOWN) + ,m_aContext( _rxContext ) + ,m_nLoginTimeout(0) + ,m_bReadOnly(false) + ,m_bPasswordRequired(false) + ,m_bSuppressVersionColumns(true) + ,m_bModified(false) + ,m_bDocumentReadOnly(false) + ,m_bMacroCallsSeenWhileLoading(false) + ,m_pSharedConnectionManager(nullptr) + ,m_nControllerLockCount(0) +{ + // some kind of default + m_sConnectURL = "jdbc:"; + m_aTableFilter = { "%" }; + impl_construct_nothrow(); +} + +ODatabaseModelImpl::ODatabaseModelImpl( + const OUString& _rRegistrationName, + const Reference< XComponentContext >& _rxContext, + ODatabaseContext& _rDBContext + ) + :m_aContainer(4) + ,m_aMacroMode( *this ) + ,m_nImposedMacroExecMode( MacroExecMode::NEVER_EXECUTE ) + ,m_rDBContext( _rDBContext ) + ,m_refCount(0) + ,m_bModificationLock( false ) + ,m_bDocumentInitialized( false ) + ,m_nScriptingSignatureState(SignatureState::UNKNOWN) + ,m_aContext( _rxContext ) + ,m_sName(_rRegistrationName) + ,m_nLoginTimeout(0) + ,m_bReadOnly(false) + ,m_bPasswordRequired(false) + ,m_bSuppressVersionColumns(true) + ,m_bModified(false) + ,m_bDocumentReadOnly(false) + ,m_bMacroCallsSeenWhileLoading(false) + ,m_pSharedConnectionManager(nullptr) + ,m_nControllerLockCount(0) +{ + impl_construct_nothrow(); +} + +ODatabaseModelImpl::~ODatabaseModelImpl() +{ +} + +void ODatabaseModelImpl::impl_construct_nothrow() +{ + // create the property bag to hold the settings (also known as "Info" property) + try + { + // the set of property value types in the bag is limited: + Sequence< Type > aAllowedTypes({ + cppu::UnoType<sal_Bool>::get(), + cppu::UnoType<double>::get(), + cppu::UnoType<OUString>::get(), + cppu::UnoType<sal_Int32>::get(), + cppu::UnoType<sal_Int16>::get(), + cppu::UnoType<Sequence< Any >>::get(), + }); + + m_xSettings = PropertyBag::createWithTypes( m_aContext, aAllowedTypes, false/*AllowEmptyPropertyName*/, true/*AutomaticAddition*/ ); + + // insert the default settings + Reference< XPropertyContainer > xContainer( m_xSettings, UNO_QUERY_THROW ); + Reference< XSet > xSettingsSet( m_xSettings, UNO_QUERY_THROW ); + const AsciiPropertyValue* pSettings = getDefaultDataSourceSettings(); + for ( ; pSettings->AsciiName; ++pSettings ) + { + if ( !pSettings->DefaultValue.hasValue() ) + { + Property aProperty( + OUString::createFromAscii( pSettings->AsciiName ), + -1, + pSettings->ValueType, + PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID + ); + xSettingsSet->insert( Any( aProperty ) ); + } + else + { + xContainer->addProperty( + OUString::createFromAscii( pSettings->AsciiName ), + PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + pSettings->DefaultValue + ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_rDBContext.appendAtTerminateListener(*this); +} + +namespace +{ + OUString lcl_getContainerStorageName_throw( ODatabaseModelImpl::ObjectType _eType ) + { + const char* pAsciiName( nullptr ); + switch ( _eType ) + { + case ODatabaseModelImpl::E_FORM: pAsciiName = "forms"; break; + case ODatabaseModelImpl::E_REPORT: pAsciiName = "reports"; break; + case ODatabaseModelImpl::E_QUERY: pAsciiName = "queries"; break; + case ODatabaseModelImpl::E_TABLE: pAsciiName = "tables"; break; + default: + throw RuntimeException(); + } + return OUString::createFromAscii( pAsciiName ); + } + + bool lcl_hasObjectWithMacros_throw( const ODefinitionContainer_Impl& _rObjectDefinitions, const Reference< XStorage >& _rxContainerStorage ) + { + bool bSomeDocHasMacros = false; + + for (auto const& objectDefinition : _rObjectDefinitions) + { + const TContentPtr& rDefinition( objectDefinition.second ); + const OUString& rPersistentName( rDefinition->m_aProps.sPersistentName ); + + if ( rPersistentName.isEmpty() ) + { // it's a logical sub folder used to organize the real objects + const ODefinitionContainer_Impl& rSubFoldersObjectDefinitions( dynamic_cast< const ODefinitionContainer_Impl& >( *rDefinition ) ); + bSomeDocHasMacros = lcl_hasObjectWithMacros_throw( rSubFoldersObjectDefinitions, _rxContainerStorage ); + if (bSomeDocHasMacros) + break; + continue; + } + + bSomeDocHasMacros = ODatabaseModelImpl::objectHasMacros( _rxContainerStorage, rPersistentName ); + if (bSomeDocHasMacros) + break; + } + return bSomeDocHasMacros; + } + + bool lcl_hasObjectsWithMacros_nothrow( ODatabaseModelImpl& _rModel, const ODatabaseModelImpl::ObjectType _eType ) + { + bool bSomeDocHasMacros = false; + + const OContentHelper_Impl& rContainerData( *_rModel.getObjectContainer( _eType ) ); + const ODefinitionContainer_Impl& rObjectDefinitions = dynamic_cast< const ODefinitionContainer_Impl& >( rContainerData ); + + try + { + Reference< XStorage > xContainerStorage( _rModel.getStorage( _eType ) ); + // note the READWRITE here: If the storage already existed before, then the OpenMode will + // be ignored, anyway. + // If the storage did not yet exist, then it will be created. If the database document + // is read-only, the OpenMode will be automatically downgraded to READ. Otherwise, + // the storage will in fact be created as READWRITE. While this is not strictly necessary + // for this particular use case here, it is required since the storage is *cached*, and + // later use cases will need the READWRITE mode. + + if ( xContainerStorage.is() ) + bSomeDocHasMacros = lcl_hasObjectWithMacros_throw( rObjectDefinitions, xContainerStorage ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + // be on the safe side: If we can't reliably determine whether there are macros, + // assume there actually are. Better this way, than the other way round. + bSomeDocHasMacros = true; + } + + return bSomeDocHasMacros; + } +} + +bool ODatabaseModelImpl::objectHasMacros( const Reference< XStorage >& _rxContainerStorage, const OUString& _rPersistentName ) +{ + OSL_PRECOND( _rxContainerStorage.is(), "ODatabaseModelImpl::objectHasMacros: this will crash!" ); + + bool bHasMacros = true; + try + { + if ( !_rxContainerStorage->hasByName( _rPersistentName ) ) + return false; + + Reference< XStorage > xObjectStor( _rxContainerStorage->openStorageElement( + _rPersistentName, ElementModes::READ ) ); + + bHasMacros = ::sfx2::DocumentMacroMode::storageHasMacros( xObjectStor ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bHasMacros; +} + +void ODatabaseModelImpl::reset() +{ + m_bReadOnly = false; + std::vector<TContentPtr>(4).swap(m_aContainer); + + if ( m_pStorageAccess.is() ) + { + m_pStorageAccess->dispose(); + m_pStorageAccess.clear(); + } +} + +void ODatabaseModelImpl::disposing( const css::lang::EventObject& Source ) +{ + Reference<XConnection> xCon(Source.Source,UNO_QUERY); + if ( xCon.is() ) + { + bool bStore = false; + for (OWeakConnectionArray::iterator i = m_aConnections.begin(); i != m_aConnections.end(); ) + { + css::uno::Reference< css::sdbc::XConnection > xIterConn ( *i ); + if ( !xIterConn.is()) + { + i = m_aConnections.erase(i); + } + else if ( xCon == xIterConn ) + { + *i = css::uno::WeakReference< css::sdbc::XConnection >(); + bStore = true; + break; + } else + ++i; + } + + if ( bStore ) + commitRootStorage(); + } + else + { + OSL_FAIL( "ODatabaseModelImpl::disposing: where does this come from?" ); + } +} + +void ODatabaseModelImpl::clearConnections() +{ + OWeakConnectionArray aConnections; + aConnections.swap( m_aConnections ); + + Reference< XConnection > xConn; + for (auto const& connection : aConnections) + { + xConn = connection; + if ( xConn.is() ) + { + try + { + xConn->close(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + m_pSharedConnectionManager = nullptr; + m_xSharedConnectionManager = nullptr; +} + +void ODatabaseModelImpl::dispose() +{ + // dispose the data source and the model + try + { + Reference< XDataSource > xDS( m_xDataSource ); + ::comphelper::disposeComponent( xDS ); + + Reference< XModel > xModel( m_xModel ); + ::comphelper::disposeComponent( xModel ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_xDataSource = WeakReference<XDataSource>(); + m_xModel = WeakReference< XModel >(); + + for (auto const& elem : m_aContainer) + { + if ( elem ) + elem->m_pDataSource = nullptr; + } + m_aContainer.clear(); + + clearConnections(); + + m_xNumberFormatsSupplier = nullptr; + + try + { + bool bCouldStore = commitEmbeddedStorage( true ); + // "true" means that committing the embedded storage should not trigger committing the root + // storage. This is because we are going to commit the root storage ourself, anyway + disposeStorages(); + if ( bCouldStore ) + commitRootStorage(); + + impl_switchToStorage_throw( nullptr ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( m_pStorageAccess.is() ) + { + m_pStorageAccess->dispose(); + m_pStorageAccess.clear(); + } +} + +const Reference< XNumberFormatsSupplier > & ODatabaseModelImpl::getNumberFormatsSupplier() +{ + if (!m_xNumberFormatsSupplier.is()) + { + // the arguments : the work locale of the current user + Locale aLocale( LanguageTag::convertToLocale( utl::ConfigManager::getWorkLocale(), false)); + + m_xNumberFormatsSupplier.set( NumberFormatsSupplier::createWithLocale( m_aContext, aLocale ) ); + } + return m_xNumberFormatsSupplier; +} + +void ODatabaseModelImpl::setDocFileLocation( const OUString& i_rLoadedFrom ) +{ + ENSURE_OR_THROW( !i_rLoadedFrom.isEmpty(), "invalid URL" ); + m_sDocFileLocation = i_rLoadedFrom; +} + +void ODatabaseModelImpl::setResource( const OUString& i_rDocumentURL, const Sequence< PropertyValue >& _rArgs ) +{ + ENSURE_OR_THROW( !i_rDocumentURL.isEmpty(), "invalid URL" ); + + ::comphelper::NamedValueCollection aMediaDescriptor( _rArgs ); +#if OSL_DEBUG_LEVEL > 0 + if ( aMediaDescriptor.has( "SalvagedFile" ) ) + { + OUString sSalvagedFile( aMediaDescriptor.getOrDefault( "SalvagedFile", OUString() ) ); + // If SalvagedFile is an empty string, this indicates "the document is being recovered, but i_rDocumentURL already + // is the real document URL, not the temporary document location" + if ( sSalvagedFile.isEmpty() ) + sSalvagedFile = i_rDocumentURL; + + OSL_ENSURE( sSalvagedFile == i_rDocumentURL, "ODatabaseModelImpl::setResource: inconsistency!" ); + // nowadays, setResource should only be called with the logical URL of the document + } +#endif + + m_aMediaDescriptor = stripLoadArguments( aMediaDescriptor ); + + impl_switchToLogicalURL( i_rDocumentURL ); +} + +::comphelper::NamedValueCollection ODatabaseModelImpl::stripLoadArguments( const ::comphelper::NamedValueCollection& _rArguments ) +{ + OSL_ENSURE( !_rArguments.has( "Model" ), "ODatabaseModelImpl::stripLoadArguments: this is suspicious (1)!" ); + OSL_ENSURE( !_rArguments.has( "ViewName" ), "ODatabaseModelImpl::stripLoadArguments: this is suspicious (2)!" ); + + ::comphelper::NamedValueCollection aMutableArgs( _rArguments ); + aMutableArgs.remove( "Model" ); + aMutableArgs.remove( "ViewName" ); + return aMutableArgs; +} + +void ODatabaseModelImpl::disposeStorages() +{ + getDocumentStorageAccess()->disposeStorages(); +} + +Reference< XSingleServiceFactory > ODatabaseModelImpl::createStorageFactory() const +{ + return StorageFactory::create( m_aContext ); +} + +void ODatabaseModelImpl::commitRootStorage() +{ + Reference< XStorage > xStorage( getOrCreateRootStorage() ); + bool bSuccess = commitStorageIfWriteable_ignoreErrors( xStorage ); + SAL_WARN_IF(!bSuccess && xStorage.is(), "dbaccess", + "ODatabaseModelImpl::commitRootStorage: could not commit the storage!"); +} + +Reference< XStorage > const & ODatabaseModelImpl::getOrCreateRootStorage() +{ + if ( !m_xDocumentStorage.is() ) + { + Reference< XSingleServiceFactory> xStorageFactory = StorageFactory::create( m_aContext ); + Any aSource = m_aMediaDescriptor.get( "Stream" ); + if ( !aSource.hasValue() ) + aSource = m_aMediaDescriptor.get( "InputStream" ); + if ( !aSource.hasValue() && !m_sDocFileLocation.isEmpty() ) + aSource <<= m_sDocFileLocation; + // TODO: shouldn't we also check URL? + + OSL_ENSURE( aSource.hasValue(), "ODatabaseModelImpl::getOrCreateRootStorage: no source to create the storage from!" ); + + if ( aSource.hasValue() ) + { + Sequence< Any > aStorageCreationArgs{ aSource, Any(ElementModes::READWRITE) }; + + Reference< XStorage > xDocumentStorage; + OUString sURL; + aSource >>= sURL; + // Don't try to load a meta-URL as-is. + if (!sURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:")) + { + try + { + xDocumentStorage.set( xStorageFactory->createInstanceWithArguments( aStorageCreationArgs ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + m_bDocumentReadOnly = true; + aStorageCreationArgs.getArray()[1] <<= ElementModes::READ; + try + { + xDocumentStorage.set( xStorageFactory->createInstanceWithArguments( aStorageCreationArgs ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + impl_switchToStorage_throw( xDocumentStorage ); + } + } + return m_xDocumentStorage.getTyped(); +} + +DocumentStorageAccess* ODatabaseModelImpl::getDocumentStorageAccess() +{ + if ( !m_pStorageAccess.is() ) + { + m_pStorageAccess = new DocumentStorageAccess( *this ); + } + return m_pStorageAccess.get(); +} + +void ODatabaseModelImpl::modelIsDisposing( const bool _wasInitialized, ResetModelAccess ) +{ + m_xModel.clear(); + + // Basic libraries and Dialog libraries are a model facet, though held at this impl class. + // They automatically dispose themself when the model they belong to is being disposed. + // So, to not be tempted to do anything with them, again, we reset them. + m_xBasicLibraries.clear(); + m_xDialogLibraries.clear(); + + m_bDocumentInitialized = _wasInitialized; +} + +Reference< XDocumentSubStorageSupplier > ODatabaseModelImpl::getDocumentSubStorageSupplier() +{ + return getDocumentStorageAccess(); +} + +bool ODatabaseModelImpl::commitEmbeddedStorage( bool _bPreventRootCommits ) +{ + return getDocumentStorageAccess()->commitEmbeddedStorage( _bPreventRootCommits ); +} + +bool ODatabaseModelImpl::commitStorageIfWriteable_ignoreErrors( const Reference< XStorage >& _rxStorage ) +{ + bool bTryToPreserveScriptSignature = false; + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + OUString sTmpFileUrl = aTempFile.GetURL(); + SignatureState aSignatureState = getScriptingSignatureState(); + OUString sLocation = getDocFileLocation(); + bool bIsEmbedded = sLocation.startsWith("vnd.sun.star.pkg:") && sLocation.endsWith("/EmbeddedDatabase"); + if (!bIsEmbedded && !sLocation.isEmpty() + && (aSignatureState == SignatureState::OK || aSignatureState == SignatureState::NOTVALIDATED + || aSignatureState == SignatureState::INVALID + || aSignatureState == SignatureState::UNKNOWN)) + { + bTryToPreserveScriptSignature = true; + // We need to first save the file (which removes the macro signature), then add the macro signature again. + // For that, we need a temporary copy of the original file. + osl::File::RC rc = osl::File::copy(sLocation, sTmpFileUrl); + if (rc != osl::FileBase::E_None) + throw uno::RuntimeException("Could not create temp file"); + } + + bool bSuccess = false; + try + { + bSuccess = tools::stor::commitStorageIfWriteable( _rxStorage ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // Preserve script signature if the script has not changed + if (bTryToPreserveScriptSignature) + { + OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(_rxStorage)); + uno::Reference<security::XDocumentDigitalSignatures> xDDSigns; + try + { + xDDSigns = security::DocumentDigitalSignatures::createWithVersion( + comphelper::getProcessComponentContext(), aODFVersion); + + const OUString aScriptSignName + = xDDSigns->getScriptingContentSignatureDefaultStreamName(); + + if (!aScriptSignName.isEmpty()) + { + Reference<XStorage> xReadOrig + = comphelper::OStorageHelper::GetStorageOfFormatFromURL( + ZIP_STORAGE_FORMAT_STRING, sTmpFileUrl, ElementModes::READ); + if (!xReadOrig.is()) + throw uno::RuntimeException("Could not read " + sTmpFileUrl); + uno::Reference<embed::XStorage> xMetaInf + = xReadOrig->openStorageElement("META-INF", embed::ElementModes::READ); + + uno::Reference<embed::XStorage> xTargetMetaInf + = _rxStorage->openStorageElement("META-INF", embed::ElementModes::READWRITE); + if (xMetaInf.is() && xTargetMetaInf.is() && xMetaInf->hasByName(aScriptSignName)) + { + xMetaInf->copyElementTo(aScriptSignName, xTargetMetaInf, aScriptSignName); + + uno::Reference<embed::XTransactedObject> xTransact(xTargetMetaInf, + uno::UNO_QUERY); + if (xTransact.is()) + xTransact->commit(); + + xTargetMetaInf->dispose(); + + // now check the copied signature + uno::Sequence<security::DocumentSignatureInformation> aInfos + = xDDSigns->verifyScriptingContentSignatures( + _rxStorage, uno::Reference<io::XInputStream>()); + SignatureState nState = DocumentSignatures::getSignatureState(aInfos); + if (nState == SignatureState::OK || nState == SignatureState::NOTVALIDATED + || nState == SignatureState::PARTIAL_OK) + { + // commit the ZipStorage from target medium + xTransact.set(_rxStorage, uno::UNO_QUERY); + if (xTransact.is()) + xTransact->commit(); + } + else + { + SAL_WARN("dbaccess", "An invalid signature was copied!"); + } + } + } + } + catch (uno::Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", ""); + } + } + + return bSuccess; +} + +void ODatabaseModelImpl::setModified( bool _bModified ) +{ + if ( isModifyLocked() ) + return; + + try + { + Reference< XModifiable > xModi( m_xModel.get(), UNO_QUERY ); + if ( xModi.is() ) + xModi->setModified( _bModified ); + else + m_bModified = _bModified; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference<XDataSource> ODatabaseModelImpl::getOrCreateDataSource() +{ + Reference<XDataSource> xDs = m_xDataSource; + if ( !xDs.is() ) + { + xDs = new ODatabaseSource(this); + m_xDataSource = xDs; + } + return xDs; +} + +Reference< XModel> ODatabaseModelImpl::getModel_noCreate() const +{ + return m_xModel; +} + +Reference< XModel > ODatabaseModelImpl::createNewModel_deliverOwnership() +{ + Reference< XModel > xModel( m_xModel ); + OSL_PRECOND( !xModel.is(), "ODatabaseModelImpl::createNewModel_deliverOwnership: not to be called if there already is a model!" ); + if ( !xModel.is() ) + { + bool bHadModelBefore = m_bDocumentInitialized; + + xModel = ODatabaseDocument::createDatabaseDocument( this, ODatabaseDocument::FactoryAccess() ); + m_xModel = xModel; + + try + { + Reference< XGlobalEventBroadcaster > xModelCollection = theGlobalEventBroadcaster::get( m_aContext ); + xModelCollection->insert( Any( xModel ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( bHadModelBefore ) + { + // do an attachResources + // In case the document is loaded regularly, this is not necessary, as our loader will do it. + // However, in case that the document is implicitly created by asking the data source for the document, + // then nobody would call the doc's attachResource. So, we do it here, to ensure it's in a proper + // state, fires all events, and so on. + // #i105505# + xModel->attachResource( xModel->getURL(), m_aMediaDescriptor.getPropertyValues() ); + } + } + return xModel; +} + +void ODatabaseModelImpl::acquire() +{ + osl_atomic_increment(&m_refCount); +} + +void ODatabaseModelImpl::release() +{ + if ( osl_atomic_decrement(&m_refCount) == 0 ) + { + acquire(); // prevent multiple releases + m_rDBContext.removeFromTerminateListener(*this); + dispose(); + m_rDBContext.storeTransientProperties(*this); + if (!m_sDocumentURL.isEmpty()) + m_rDBContext.revokeDatabaseDocument(*this); + delete this; + } +} + +void ODatabaseModelImpl::commitStorages() +{ + getDocumentStorageAccess()->commitStorages(); +} + +Reference< XStorage > ODatabaseModelImpl::getStorage( const ObjectType _eType ) +{ + return getDocumentStorageAccess()->getDocumentSubStorage( getObjectContainerStorageName( _eType ), + css::embed::ElementModes::READWRITE ); +} + +const AsciiPropertyValue* ODatabaseModelImpl::getDefaultDataSourceSettings() +{ + static const AsciiPropertyValue aKnownSettings[] = + { + // known JDBC settings + AsciiPropertyValue( "JavaDriverClass", Any( OUString() ) ), + AsciiPropertyValue( "JavaDriverClassPath", Any( OUString() ) ), + AsciiPropertyValue( "IgnoreCurrency", Any( false ) ), + // known settings for file-based drivers + AsciiPropertyValue( "Extension", Any( OUString() ) ), + AsciiPropertyValue( "CharSet", Any( OUString() ) ), + AsciiPropertyValue( "HeaderLine", Any( true ) ), + AsciiPropertyValue( "FieldDelimiter", Any( OUString( "," ) ) ), + AsciiPropertyValue( "StringDelimiter", Any( OUString( "\"" ) ) ), + AsciiPropertyValue( "DecimalDelimiter", Any( OUString( "." ) ) ), + AsciiPropertyValue( "ThousandDelimiter", Any( OUString() ) ), + AsciiPropertyValue( "ShowDeleted", Any( false ) ), + // known ODBC settings + AsciiPropertyValue( "SystemDriverSettings", Any( OUString() ) ), + AsciiPropertyValue( "UseCatalog", Any( false ) ), + AsciiPropertyValue( "TypeInfoSettings", Any( Sequence< Any >()) ), + // settings related to auto increment handling + AsciiPropertyValue( "AutoIncrementCreation", Any( OUString() ) ), + AsciiPropertyValue( "AutoRetrievingStatement", Any( OUString() ) ), + AsciiPropertyValue( "IsAutoRetrievingEnabled", Any( false ) ), + // known LDAP driver settings + AsciiPropertyValue( "HostName", Any( OUString() ) ), + AsciiPropertyValue( "PortNumber", Any( sal_Int32(389) ) ), + AsciiPropertyValue( "BaseDN", Any( OUString() ) ), + AsciiPropertyValue( "MaxRowCount", Any( sal_Int32(100) ) ), + // known MySQLNative driver settings + AsciiPropertyValue( "LocalSocket", Any( OUString() ) ), + AsciiPropertyValue( "NamedPipe", Any( OUString() ) ), + // misc known driver settings + AsciiPropertyValue( "ParameterNameSubstitution", Any( false ) ), + AsciiPropertyValue( "AddIndexAppendix", Any( true ) ), + AsciiPropertyValue( "IgnoreDriverPrivileges", Any( true ) ), + AsciiPropertyValue( "ImplicitCatalogRestriction", ::cppu::UnoType< OUString >::get() ), + AsciiPropertyValue( "ImplicitSchemaRestriction", ::cppu::UnoType< OUString >::get() ), + AsciiPropertyValue( "PrimaryKeySupport", ::cppu::UnoType< sal_Bool >::get() ), + AsciiPropertyValue( "ShowColumnDescription", Any( false ) ), + // known SDB level settings + AsciiPropertyValue( "NoNameLengthLimit", Any( false ) ), + AsciiPropertyValue( "AppendTableAliasName", Any( false ) ), + AsciiPropertyValue( "GenerateASBeforeCorrelationName", Any( false ) ), + AsciiPropertyValue( "ColumnAliasInOrderBy", Any( true ) ), + AsciiPropertyValue( "EnableSQL92Check", Any( false ) ), + AsciiPropertyValue( "BooleanComparisonMode", Any( BooleanComparisonMode::EQUAL_INTEGER ) ), + AsciiPropertyValue( "TableTypeFilterMode", Any( sal_Int32(3) ) ), + AsciiPropertyValue( "RespectDriverResultSetType", Any( false ) ), + AsciiPropertyValue( "UseSchemaInSelect", Any( true ) ), + AsciiPropertyValue( "UseCatalogInSelect", Any( true ) ), + AsciiPropertyValue( "EnableOuterJoinEscape", Any( true ) ), + AsciiPropertyValue( "PreferDosLikeLineEnds", Any( false ) ), + AsciiPropertyValue( "FormsCheckRequiredFields", Any( true ) ), + AsciiPropertyValue( "EscapeDateTime", Any( true ) ), + + // known services to handle database tasks + AsciiPropertyValue( "TableAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "TableRenameServiceName", Any( OUString() ) ), + AsciiPropertyValue( "ViewAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "ViewAccessServiceName", Any( OUString() ) ), + AsciiPropertyValue( "CommandDefinitions", Any( OUString() ) ), + AsciiPropertyValue( "Forms", Any( OUString() ) ), + AsciiPropertyValue( "Reports", Any( OUString() ) ), + AsciiPropertyValue( "KeyAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "IndexAlterationServiceName", Any( OUString() ) ), + + AsciiPropertyValue() + }; + return aKnownSettings; +} + +TContentPtr& ODatabaseModelImpl::getObjectContainer( ObjectType _eType ) +{ + OSL_PRECOND( _eType >= E_FORM && _eType <= E_TABLE, "ODatabaseModelImpl::getObjectContainer: illegal index!" ); + TContentPtr& rContentPtr = m_aContainer[ _eType ]; + + if ( !rContentPtr ) + { + rContentPtr = std::make_shared<ODefinitionContainer_Impl>(); + rContentPtr->m_pDataSource = this; + rContentPtr->m_aProps.aTitle = lcl_getContainerStorageName_throw( _eType ); + } + return rContentPtr; +} + +bool ODatabaseModelImpl::adjustMacroMode_AutoReject() +{ + return m_aMacroMode.adjustMacroMode( nullptr ); +} + +bool ODatabaseModelImpl::checkMacrosOnLoading() +{ + Reference< XInteractionHandler > xInteraction; + xInteraction = m_aMediaDescriptor.getOrDefault( "InteractionHandler", xInteraction ); + return m_aMacroMode.checkMacrosOnLoading( xInteraction ); +} + +void ODatabaseModelImpl::resetMacroExecutionMode() +{ + m_aMacroMode = ::sfx2::DocumentMacroMode( *this ); +} + +Reference< XStorageBasedLibraryContainer > ODatabaseModelImpl::getLibraryContainer( bool _bScript ) +{ + Reference< XStorageBasedLibraryContainer >& rxContainer( _bScript ? m_xBasicLibraries : m_xDialogLibraries ); + if ( rxContainer.is() ) + return rxContainer; + + Reference< XStorageBasedDocument > xDocument( getModel_noCreate(), UNO_QUERY_THROW ); + // this is only to be called if there already exists a document model - in fact, it is + // to be called by the document model only + + try + { + Reference< XStorageBasedLibraryContainer > (*Factory)( const Reference< XComponentContext >&, const Reference< XStorageBasedDocument >&) + = _bScript ? &DocumentScriptLibraryContainer::create : &DocumentDialogLibraryContainer::create; + + rxContainer.set( + (*Factory)( m_aContext, xDocument ), + UNO_SET_THROW + ); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + throw WrappedTargetRuntimeException( + OUString(), + xDocument, + ::cppu::getCaughtException() + ); + } + return rxContainer; +} + +void ODatabaseModelImpl::storeLibraryContainersTo( const Reference< XStorage >& _rxToRootStorage ) +{ + if ( m_xBasicLibraries.is() ) + m_xBasicLibraries->storeLibrariesToStorage( _rxToRootStorage ); + + if ( m_xDialogLibraries.is() ) + m_xDialogLibraries->storeLibrariesToStorage( _rxToRootStorage ); +} + +Reference< XStorage > ODatabaseModelImpl::switchToStorage( const Reference< XStorage >& _rxNewRootStorage ) +{ + if ( !_rxNewRootStorage.is() ) + throw IllegalArgumentException(); + + return impl_switchToStorage_throw( _rxNewRootStorage ); +} + +namespace +{ + void lcl_modifyListening( ::sfx2::IModifiableDocument& _rDocument, + const Reference< XStorage >& _rxStorage, ::rtl::Reference< ::sfx2::DocumentStorageModifyListener >& _inout_rListener, + comphelper::SolarMutex& _rMutex, bool _bListen ) + { + Reference< XModifiable > xModify( _rxStorage, UNO_QUERY ); + OSL_ENSURE( xModify.is() || !_rxStorage.is(), "lcl_modifyListening: storage can't notify us!" ); + + if ( xModify.is() && !_bListen && _inout_rListener.is() ) + { + xModify->removeModifyListener( _inout_rListener ); + } + + if ( _inout_rListener.is() ) + { + _inout_rListener->dispose(); + _inout_rListener = nullptr; + } + + if ( xModify.is() && _bListen ) + { + _inout_rListener = new ::sfx2::DocumentStorageModifyListener( _rDocument, _rMutex ); + xModify->addModifyListener( _inout_rListener ); + } + } +} + +namespace +{ + void lcl_rebaseScriptStorage_throw( const Reference< XStorageBasedLibraryContainer >& _rxContainer, + const Reference< XStorage >& _rxNewRootStorage ) + { + if ( _rxContainer.is() ) + { + if ( _rxNewRootStorage.is() ) + _rxContainer->setRootStorage( _rxNewRootStorage ); +// else + // TODO: what to do here? dispose the container? + } + } +} + +Reference< XStorage > const & ODatabaseModelImpl::impl_switchToStorage_throw( const Reference< XStorage >& _rxNewRootStorage ) +{ + // stop listening for modifications at the old storage + lcl_modifyListening( *this, m_xDocumentStorage.getTyped(), m_pStorageModifyListener, Application::GetSolarMutex(), false ); + + // set new storage + m_xDocumentStorage.reset( _rxNewRootStorage, SharedStorage::TakeOwnership ); + + // start listening for modifications + lcl_modifyListening( *this, m_xDocumentStorage.getTyped(), m_pStorageModifyListener, Application::GetSolarMutex(), true ); + + // forward new storage to Basic and Dialog library containers + lcl_rebaseScriptStorage_throw( m_xBasicLibraries, m_xDocumentStorage.getTyped() ); + lcl_rebaseScriptStorage_throw( m_xDialogLibraries, m_xDocumentStorage.getTyped() ); + + m_bReadOnly = !tools::stor::storageIsWritable_nothrow( m_xDocumentStorage.getTyped() ); + // TODO: our data source, if it exists, must broadcast the change of its ReadOnly property + + return m_xDocumentStorage.getTyped(); +} + +void ODatabaseModelImpl::impl_switchToLogicalURL( const OUString& i_rDocumentURL ) +{ + if ( i_rDocumentURL == m_sDocumentURL ) + return; + + const OUString sOldURL( m_sDocumentURL ); + // update our name, if necessary + if ( ( m_sName == m_sDocumentURL ) // our name is our old URL + || ( m_sName.isEmpty() ) // we do not have a name, yet (i.e. are not registered at the database context) + ) + { + INetURLObject aURL( i_rDocumentURL ); + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + { + m_sName = i_rDocumentURL; + // TODO: our data source must broadcast the change of the Name property + } + } + + // remember URL + m_sDocumentURL = i_rDocumentURL; + + // update our location, if necessary + if ( m_sDocFileLocation.isEmpty() ) + m_sDocFileLocation = m_sDocumentURL; + + // register at the database context, or change registration + if (!sOldURL.isEmpty()) + m_rDBContext.databaseDocumentURLChange( sOldURL, m_sDocumentURL ); + else + m_rDBContext.registerDatabaseDocument( *this ); +} + +OUString ODatabaseModelImpl::getObjectContainerStorageName( const ObjectType _eType ) +{ + return lcl_getContainerStorageName_throw( _eType ); +} + +sal_Int16 ODatabaseModelImpl::getCurrentMacroExecMode() const +{ + sal_Int16 nCurrentMode = MacroExecMode::NEVER_EXECUTE; + try + { + nCurrentMode = m_aMediaDescriptor.getOrDefault( "MacroExecutionMode", nCurrentMode ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return nCurrentMode; +} + +void ODatabaseModelImpl::setCurrentMacroExecMode( sal_uInt16 nMacroMode ) +{ + m_aMediaDescriptor.put( "MacroExecutionMode", nMacroMode ); +} + +OUString ODatabaseModelImpl::getDocumentLocation() const +{ + return getURL(); + // formerly, we returned getDocFileLocation here, which is the location of the file from which we + // recovered the "real" document. + // However, during CWS autorecovery evolving, we clarified (with MAV/MT) the role of XModel::getURL and + // XStorable::getLocation. In this course, we agreed that for a macro security check, the *document URL* + // (not the recovery file URL) is to be used: The recovery file lies in the backup folder, and by definition, + // this folder is considered to be secure. So, the document URL needs to be used to decide about the security. +} + +ODatabaseModelImpl::EmbeddedMacros ODatabaseModelImpl::determineEmbeddedMacros() +{ + if ( !m_aEmbeddedMacros ) + { + if ( ::sfx2::DocumentMacroMode::storageHasMacros( getOrCreateRootStorage() ) ) + { + m_aEmbeddedMacros = eDocumentWideMacros; + } + else if ( lcl_hasObjectsWithMacros_nothrow( *this, E_FORM ) + || lcl_hasObjectsWithMacros_nothrow( *this, E_REPORT ) + ) + { + m_aEmbeddedMacros = eSubDocumentMacros; + } + else + { + m_aEmbeddedMacros = eNoMacros; + } + } + return *m_aEmbeddedMacros; +} + +bool ODatabaseModelImpl::documentStorageHasMacros() const +{ + const_cast< ODatabaseModelImpl* >( this )->determineEmbeddedMacros(); + return ( *m_aEmbeddedMacros != eNoMacros ); +} + +bool ODatabaseModelImpl::macroCallsSeenWhileLoading() const +{ + return m_bMacroCallsSeenWhileLoading; +} + +Reference< XEmbeddedScripts > ODatabaseModelImpl::getEmbeddedDocumentScripts() const +{ + return Reference< XEmbeddedScripts >( getModel_noCreate(), UNO_QUERY ); +} + +SignatureState ODatabaseModelImpl::getScriptingSignatureState() +{ + return m_nScriptingSignatureState; +} + +bool ODatabaseModelImpl::hasTrustedScriptingSignature(bool bAllowUIToAddAuthor) +{ + bool bResult = false; + + try + { + // Don't use m_xDocumentStorage, that somehow has an incomplete storage representation + // which leads to signatures not being found + Reference<XStorage> xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL( + ZIP_STORAGE_FORMAT_STRING, m_sDocFileLocation, ElementModes::READ); + + OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(getOrCreateRootStorage())); + uno::Reference<security::XDocumentDigitalSignatures> xSigner( + security::DocumentDigitalSignatures::createWithVersion( + comphelper::getProcessComponentContext(), aODFVersion)); + const uno::Sequence<security::DocumentSignatureInformation> aInfo + = xSigner->verifyScriptingContentSignatures(xStorage, + uno::Reference<io::XInputStream>()); + + if (!aInfo.hasElements()) + return false; + + m_nScriptingSignatureState = DocumentSignatures::getSignatureState(aInfo); + if (m_nScriptingSignatureState == SignatureState::OK + || m_nScriptingSignatureState == SignatureState::NOTVALIDATED) + { + bResult = std::any_of(aInfo.begin(), aInfo.end(), + [&xSigner](const security::DocumentSignatureInformation& rInfo) { + return xSigner->isAuthorTrusted(rInfo.Signer); + }); + } + + if (!bResult && bAllowUIToAddAuthor) + { + Reference<XInteractionHandler> xInteraction; + xInteraction = m_aMediaDescriptor.getOrDefault("InteractionHandler", xInteraction); + if (xInteraction.is()) + { + task::DocumentMacroConfirmationRequest aRequest; + aRequest.DocumentURL = m_sDocFileLocation; + aRequest.DocumentStorage = xStorage; + aRequest.DocumentSignatureInformation = aInfo; + aRequest.DocumentVersion = aODFVersion; + aRequest.Classification = task::InteractionClassification_QUERY; + bResult = SfxMedium::CallApproveHandler(xInteraction, uno::Any(aRequest), true); + } + } + } + catch (uno::Exception&) + { + } + + return bResult; +} + +void ODatabaseModelImpl::storageIsModified() +{ + setModified( true ); +} + +ModelDependentComponent::ModelDependentComponent( const ::rtl::Reference< ODatabaseModelImpl >& _model ) + :m_pImpl( _model ) +{ +} + +ModelDependentComponent::~ModelDependentComponent() +{ +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/SharedConnection.cxx b/dbaccess/source/core/dataaccess/SharedConnection.cxx new file mode 100644 index 000000000..31a8e68ba --- /dev/null +++ b/dbaccess/source/core/dataaccess/SharedConnection.cxx @@ -0,0 +1,154 @@ +/* -*- 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 "SharedConnection.hxx" +#include <comphelper/uno3.hxx> + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace connectivity; + +OSharedConnection::OSharedConnection(Reference<XAggregation>& _rxProxyConnection) + : OSharedConnection_BASE(m_aMutex) +{ + setDelegation(_rxProxyConnection, m_refCount); +} + +OSharedConnection::~OSharedConnection() {} + +void SAL_CALL OSharedConnection::disposing() +{ + OSharedConnection_BASE::disposing(); + OConnectionWrapper::disposing(); +} + +Reference<XStatement> SAL_CALL OSharedConnection::createStatement() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->createStatement(); +} + +Reference<XPreparedStatement> SAL_CALL OSharedConnection::prepareStatement(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->prepareStatement(sql); +} + +Reference<XPreparedStatement> SAL_CALL OSharedConnection::prepareCall(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->prepareCall(sql); +} + +OUString SAL_CALL OSharedConnection::nativeSQL(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->nativeSQL(sql); +} + +sal_Bool SAL_CALL OSharedConnection::getAutoCommit() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getAutoCommit(); +} + +void SAL_CALL OSharedConnection::commit() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + m_xConnection->commit(); +} + +void SAL_CALL OSharedConnection::rollback() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + m_xConnection->rollback(); +} + +sal_Bool SAL_CALL OSharedConnection::isClosed() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (!m_xConnection.is()) + return true; + + return m_xConnection->isClosed(); +} + +Reference<XDatabaseMetaData> SAL_CALL OSharedConnection::getMetaData() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getMetaData(); +} + +sal_Bool SAL_CALL OSharedConnection::isReadOnly() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->isReadOnly(); +} + +OUString SAL_CALL OSharedConnection::getCatalog() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getCatalog(); +} + +sal_Int32 SAL_CALL OSharedConnection::getTransactionIsolation() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getTransactionIsolation(); +} + +Reference<css::container::XNameAccess> SAL_CALL OSharedConnection::getTypeMap() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getTypeMap(); +} + +IMPLEMENT_GET_IMPLEMENTATION_ID(OSharedConnection) + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/SharedConnection.hxx b/dbaccess/source/core/dataaccess/SharedConnection.hxx new file mode 100644 index 000000000..ea6e961cf --- /dev/null +++ b/dbaccess/source/core/dataaccess/SharedConnection.hxx @@ -0,0 +1,118 @@ +/* -*- 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/ConnectionWrapper.hxx> +#include <connectivity/CommonTools.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <comphelper/sequence.hxx> + +namespace dbaccess +{ + // OSharedConnection: This class implements a simple forwarding of connection calls. + // All methods will be forwarded with exception of the set methods, which are not allowed + // to be called on shared connections. Instances of this class will be created when the + // datasource is asked for not isolated connection. + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XConnection + > OSharedConnection_BASE; + typedef ::connectivity::OConnectionWrapper OSharedConnection_BASE2; + + class OSharedConnection : public ::cppu::BaseMutex + , public OSharedConnection_BASE + , public OSharedConnection_BASE2 + { + protected: + virtual void SAL_CALL disposing() override; + virtual ~OSharedConnection() override; + public: + explicit OSharedConnection(css::uno::Reference< css::uno::XAggregation >& _rxProxyConnection); + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + virtual void SAL_CALL acquire() noexcept override { OSharedConnection_BASE::acquire(); } + virtual void SAL_CALL release() noexcept override { OSharedConnection_BASE::release(); } + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override + { + return ::comphelper::concatSequences( + OSharedConnection_BASE::getTypes(), + OSharedConnection_BASE2::getTypes() + ); + } + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& _rType ) override + { + css::uno::Any aReturn = OSharedConnection_BASE::queryInterface(_rType); + if ( !aReturn.hasValue() ) + aReturn = OSharedConnection_BASE2::queryInterface(_rType); + return aReturn; + } + + // XCloseable + virtual void SAL_CALL close( ) override + { + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(rBHelper.bDisposed); + } + dispose(); + } + + // XConnection + virtual void SAL_CALL setAutoCommit( sal_Bool /*autoCommit*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setReadOnly( sal_Bool /*readOnly*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setCatalog( const OUString& /*catalog*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setTransactionIsolation( sal_Int32 /*level*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& /*typeMap*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + // XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx b/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx new file mode 100644 index 000000000..a03caea57 --- /dev/null +++ b/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx @@ -0,0 +1,301 @@ +/* -*- 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 <bookmarkcontainer.hxx> + +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <comphelper/enumhelper.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <cppuhelper/supportsservice.hxx> + +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; + +namespace dbaccess +{ + +// OBookmarkContainer + +OBookmarkContainer::OBookmarkContainer(OWeakObject& _rParent, Mutex& _rMutex) + :m_rParent(_rParent) + ,m_aContainerListeners(_rMutex) + ,m_rMutex(_rMutex) +{ +} + + +void SAL_CALL OBookmarkContainer::acquire( ) noexcept +{ + m_rParent.acquire(); +} + +void SAL_CALL OBookmarkContainer::release( ) noexcept +{ + m_rParent.release(); +} + +OBookmarkContainer::~OBookmarkContainer() +{ +} + +// XServiceInfo +OUString SAL_CALL OBookmarkContainer::getImplementationName( ) +{ + return "com.sun.star.comp.dba.OBookmarkContainer"; +} + +sal_Bool SAL_CALL OBookmarkContainer::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL OBookmarkContainer::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DefinitionContainer" }; +} + +// XNameContainer +void SAL_CALL OBookmarkContainer::insertByName( const OUString& _rName, const Any& aElement ) +{ + MutexGuard aGuard(m_rMutex); + + if (checkExistence(_rName)) + throw ElementExistException(); + + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + // approve the new object + OUString sNewLink; + if (!(aElement >>= sNewLink)) + throw IllegalArgumentException(); + + implAppend(_rName, sNewLink); + + // notify the listeners + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sNewLink), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); + } +} + +void SAL_CALL OBookmarkContainer::removeByName( const OUString& _rName ) +{ + OUString sOldBookmark; + { + MutexGuard aGuard(m_rMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + // the old element (for the notifications) + sOldBookmark = m_aBookmarks[_rName]; + + // do the removal + implRemove(_rName); + } + + // notify the listeners + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sOldBookmark), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvent ); + } +} + +// XNameReplace +void SAL_CALL OBookmarkContainer::replaceByName( const OUString& _rName, const Any& aElement ) +{ + ClearableMutexGuard aGuard(m_rMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + // do we have such an element? + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + // approve the new object + OUString sNewLink; + if (!(aElement >>= sNewLink)) + throw IllegalArgumentException(); + + // the old element (for the notifications) + OUString sOldLink = m_aBookmarks[_rName]; + + // do the replace + implReplace(_rName, sNewLink); + + // notify the listeners + aGuard.clear(); + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sNewLink), Any(sOldLink)); + m_aContainerListeners.notifyEach( &XContainerListener::elementReplaced, aEvent ); + } +} + +void SAL_CALL OBookmarkContainer::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + MutexGuard aGuard(m_rMutex); + if (_rxListener.is()) + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL OBookmarkContainer::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + MutexGuard aGuard(m_rMutex); + if (_rxListener.is()) + m_aContainerListeners.removeInterface(_rxListener); +} + +// XElementAccess +Type SAL_CALL OBookmarkContainer::getElementType( ) +{ + return ::cppu::UnoType<OUString>::get(); +} + +sal_Bool SAL_CALL OBookmarkContainer::hasElements( ) +{ + MutexGuard aGuard(m_rMutex); + return !m_aBookmarks.empty(); +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL OBookmarkContainer::createEnumeration( ) +{ + MutexGuard aGuard(m_rMutex); + return new ::comphelper::OEnumerationByIndex(static_cast<XIndexAccess*>(this)); +} + +// XIndexAccess +sal_Int32 SAL_CALL OBookmarkContainer::getCount( ) +{ + MutexGuard aGuard(m_rMutex); + return m_aBookmarks.size(); +} + +Any SAL_CALL OBookmarkContainer::getByIndex( sal_Int32 _nIndex ) +{ + MutexGuard aGuard(m_rMutex); + + if ((_nIndex < 0) || (o3tl::make_unsigned(_nIndex) >= m_aBookmarksIndexed.size())) + throw IndexOutOfBoundsException(); + + return Any(m_aBookmarksIndexed[_nIndex]->second); +} + +Any SAL_CALL OBookmarkContainer::getByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_rMutex); + + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + return Any(m_aBookmarks[_rName]); +} + +Sequence< OUString > SAL_CALL OBookmarkContainer::getElementNames( ) +{ + MutexGuard aGuard(m_rMutex); + + Sequence< OUString > aNames(m_aBookmarks.size()); + OUString* pNames = aNames.getArray(); + + for (auto const& bookmarkIndexed : m_aBookmarksIndexed) + { + *pNames = bookmarkIndexed->first; + ++pNames; + } + + return aNames; +} + +sal_Bool SAL_CALL OBookmarkContainer::hasByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_rMutex); + + return checkExistence(_rName); +} + +void OBookmarkContainer::implRemove(const OUString& _rName) +{ + MutexGuard aGuard(m_rMutex); + + // look for the name in the index access vector + MapString2String::const_iterator aMapPos = m_aBookmarks.end(); + auto aSearch = std::find_if(m_aBookmarksIndexed.begin(), m_aBookmarksIndexed.end(), + [&_rName](const MapString2String::iterator& rIter) { return rIter->first == _rName; }); + if (aSearch != m_aBookmarksIndexed.end()) + { + aMapPos = *aSearch; + m_aBookmarksIndexed.erase(aSearch); + } + + if (m_aBookmarks.end() == aMapPos) + { + OSL_FAIL("OBookmarkContainer::implRemove: inconsistence!"); + return; + } + + // remove the map entries + m_aBookmarks.erase(aMapPos); +} + +void OBookmarkContainer::implAppend(const OUString& _rName, const OUString& _rDocumentLocation) +{ + MutexGuard aGuard(m_rMutex); + + OSL_ENSURE(m_aBookmarks.find(_rName) == m_aBookmarks.end(),"Bookmark already known!"); + m_aBookmarksIndexed.push_back(m_aBookmarks.emplace( _rName,_rDocumentLocation).first); +} + +void OBookmarkContainer::implReplace(const OUString& _rName, const OUString& _rNewLink) +{ + MutexGuard aGuard(m_rMutex); + OSL_ENSURE(checkExistence(_rName), "OBookmarkContainer::implReplace : invalid name !"); + + m_aBookmarks[_rName] = _rNewLink; +} + +Reference< XInterface > SAL_CALL OBookmarkContainer::getParent( ) +{ + return m_rParent; +} + +void SAL_CALL OBookmarkContainer::setParent( const Reference< XInterface >& /*Parent*/ ) +{ + throw NoSupportException(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commandcontainer.cxx b/dbaccess/source/core/dataaccess/commandcontainer.cxx new file mode 100644 index 000000000..8afb81520 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commandcontainer.cxx @@ -0,0 +1,100 @@ +/* -*- 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 "commandcontainer.hxx" +#include "commanddefinition.hxx" + +#include <com/sun/star/sdb/TableDefinition.hpp> +#include <com/sun/star/sdb/CommandDefinition.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ucb; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +// OCommandContainer + +OCommandContainer::OCommandContainer( const Reference< css::uno::XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTables + ) + :ODefinitionContainer(_xORB,_xParentContainer,_pImpl,!_bTables) + ,m_bTables(_bTables) +{ +} + +OCommandContainer::~OCommandContainer() +{ +} + +IMPLEMENT_FORWARD_XINTERFACE2( OCommandContainer,ODefinitionContainer,OCommandContainer_BASE) +css::uno::Sequence< css::uno::Type > OCommandContainer::getTypes() +{ + return ::comphelper::concatSequences( + ODefinitionContainer::getTypes( ), + OCommandContainer_BASE::getTypes( ) + ); +} + +css::uno::Sequence<sal_Int8> OCommandContainer::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +Reference< XContent > OCommandContainer::createObject( const OUString& _rName) +{ + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + OSL_ENSURE( rDefinitions.find(_rName) != rDefinitions.end(), "OCommandContainer::createObject: Invalid entry in map!" ); + + const TContentPtr& pElementContent( rDefinitions.find( _rName )->second ); + if ( m_bTables ) + return new OComponentDefinition( *this, _rName, m_aContext, pElementContent, m_bTables ); + else + return static_cast< css::sdb::XQueryDefinition * > ( new OCommandDefinition( *this, _rName, m_aContext, pElementContent ) ); +} + +Reference< XInterface > SAL_CALL OCommandContainer::createInstanceWithArguments(const Sequence< Any >& /*aArguments*/ ) +{ + return createInstance( ); +} + +Reference< XInterface > SAL_CALL OCommandContainer::createInstance( ) +{ + if(m_bTables) + return css::sdb::TableDefinition::createDefault( m_aContext ); + else + return css::sdb::CommandDefinition::create( m_aContext ); +} + +OUString OCommandContainer::determineContentType() const +{ + return "application/vnd.org.openoffice.DatabaseCommandDefinitionContainer"; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commandcontainer.hxx b/dbaccess/source/core/dataaccess/commandcontainer.hxx new file mode 100644 index 000000000..3ea36f648 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commandcontainer.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 <definitioncontainer.hxx> + +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <cppuhelper/implbase1.hxx> + +namespace dbaccess +{ +// OCommandContainer + +typedef ::cppu::ImplHelper1 < css::lang::XSingleServiceFactory + > OCommandContainer_BASE; + +class OCommandContainer : public ODefinitionContainer + ,public OCommandContainer_BASE +{ + bool m_bTables; + +public: + /** constructs the container.<BR> + */ + OCommandContainer( + const css::uno::Reference< css::uno::XComponentContext >& _xORB + ,const css::uno::Reference< css::uno::XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTables + ); + + DECLARE_XINTERFACE( ) + + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +protected: + virtual ~OCommandContainer() override; + + // ODefinitionContainer + virtual css::uno::Reference< css::ucb::XContent > createObject(const OUString& _rName) override; + +protected: + // OContentHelper overridables + virtual OUString determineContentType() const override; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commanddefinition.cxx b/dbaccess/source/core/dataaccess/commanddefinition.cxx new file mode 100644 index 000000000..f04c47228 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commanddefinition.cxx @@ -0,0 +1,153 @@ +/* -*- 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 "commanddefinition.hxx" +#include <stringconstants.hxx> +#include <strings.hxx> + +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +namespace dbaccess +{ + +void OCommandDefinition::registerProperties() +{ + OCommandDefinition_Impl& rCommandDefinition = dynamic_cast< OCommandDefinition_Impl& >( *m_pImpl ); + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, + &rCommandDefinition.m_sCommand, cppu::UnoType<decltype(rCommandDefinition.m_sCommand)>::get()); + + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, + &rCommandDefinition.m_bEscapeProcessing, cppu::UnoType<bool>::get()); + + registerProperty(PROPERTY_UPDATE_TABLENAME, PROPERTY_ID_UPDATE_TABLENAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateTableName, cppu::UnoType<decltype(rCommandDefinition.m_sUpdateTableName)>::get()); + + registerProperty(PROPERTY_UPDATE_SCHEMANAME, PROPERTY_ID_UPDATE_SCHEMANAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateSchemaName, cppu::UnoType<decltype(rCommandDefinition.m_sUpdateSchemaName)>::get()); + + registerProperty(PROPERTY_UPDATE_CATALOGNAME, PROPERTY_ID_UPDATE_CATALOGNAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateCatalogName, cppu::UnoType<decltype(rCommandDefinition.m_sUpdateCatalogName)>::get()); + registerProperty(PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, PropertyAttribute::BOUND, + &rCommandDefinition.m_aLayoutInformation, cppu::UnoType<decltype(rCommandDefinition.m_aLayoutInformation)>::get()); +} + +OCommandDefinition::OCommandDefinition(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _rxContainer + ,const TContentPtr& _pImpl) + :OComponentDefinition(_xORB,_rxContainer,_pImpl,false) +{ + registerProperties(); +} + +OCommandDefinition::~OCommandDefinition() +{ +} + +OCommandDefinition::OCommandDefinition( const Reference< XInterface >& _rxContainer + ,const OUString& _rElementName + ,const Reference< XComponentContext >& _xORB + ,const TContentPtr& _pImpl) + :OComponentDefinition(_rxContainer,_rElementName,_xORB,_pImpl,false) +{ + registerProperties(); +} + +css::uno::Sequence<sal_Int8> OCommandDefinition::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > OCommandDefinition::getTypes() +{ + return ::comphelper::concatSequences( + OCommandDefinition_Base::getTypes( ), + OComponentDefinition::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE2( OCommandDefinition,OComponentDefinition,OCommandDefinition_Base) +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OCommandDefinition::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& OCommandDefinition::getInfoHelper() +{ + return *OCommandDefinition_PROP::getArrayHelper(); +} +::cppu::IPropertyArrayHelper* OCommandDefinition::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +OUString SAL_CALL OCommandDefinition::getImplementationName() +{ + return "com.sun.star.comp.dba.OCommandDefinition"; +} + +css::uno::Sequence<OUString> SAL_CALL OCommandDefinition::getSupportedServiceNames() +{ + return { + "com.sun.star.sdb.QueryDefinition", + "com.sun.star.sdb.CommandDefinition", + "com.sun.star.ucb.Content" + }; +} + +void SAL_CALL OCommandDefinition::rename( const OUString& newName ) +{ + try + { + sal_Int32 nHandle = PROPERTY_ID_NAME; + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + Any aOld(m_pImpl->m_aProps.aTitle); + aGuard.clear(); + Any aNew(newName); + fire(&nHandle, &aNew, &aOld, 1, true ); + + m_pImpl->m_aProps.aTitle = newName; + fire(&nHandle, &aNew, &aOld, 1, false ); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(newName,*this); + } +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_OCommandDefinition(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new dbaccess::OCommandDefinition( + context, nullptr, std::make_shared<dbaccess::OCommandDefinition_Impl>() )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commanddefinition.hxx b/dbaccess/source/core/dataaccess/commanddefinition.hxx new file mode 100644 index 000000000..ed348a889 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commanddefinition.hxx @@ -0,0 +1,125 @@ +/* -*- 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 <commandbase.hxx> +#include <apitools.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/sdb/XQueryDefinition.hpp> +#include <datasettings.hxx> +#include <ContentHelper.hxx> +#include "ComponentDefinition.hxx" + +#include <comphelper/proparrhlp.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase2.hxx> + +namespace dbaccess +{ + +// OCommandDefinition - a database "document" which describes a query + class OCommandDefinition_Impl : public OComponentDefinition_Impl + ,public OCommandBase + { + public: + }; + +typedef ::cppu::ImplHelper2 < css::sdbcx::XRename, + css::sdb::XQueryDefinition + > OCommandDefinition_Base; +class OCommandDefinition; +typedef ::comphelper::OPropertyArrayUsageHelper< OCommandDefinition > + OCommandDefinition_PROP; + +class OCommandDefinition : public OComponentDefinition + ,public OCommandDefinition_Base + ,public OCommandDefinition_PROP +{ +protected: + virtual ~OCommandDefinition() override; + +public: + OCommandDefinition(const css::uno::Reference< css::uno::XComponentContext >& , + const css::uno::Reference< css::uno::XInterface >& _xParentContainer, + const TContentPtr& _pImpl); + + OCommandDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer + ,const OUString& _rElementName + ,const css::uno::Reference< css::uno::XComponentContext >& + ,const TContentPtr& _pImpl + ); + + 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::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // overrides to resolve ambiguity + virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override + { OComponentDefinition::setPropertyValue(p1, p2); } + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override + { return OComponentDefinition::getPropertyValue(p1); } + virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override + { OComponentDefinition::addPropertyChangeListener(p1, p2); } + virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override + { OComponentDefinition::removePropertyChangeListener(p1, p2); } + virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override + { OComponentDefinition::addVetoableChangeListener(p1, p2); } + virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override + { OComponentDefinition::removeVetoableChangeListener(p1, p2); } + virtual css::uno::Reference<css::ucb::XContentIdentifier> SAL_CALL getIdentifier() override + { return OComponentDefinition::getIdentifier(); } + virtual OUString SAL_CALL getContentType() override + { return OComponentDefinition::getContentType(); } + virtual void SAL_CALL addContentEventListener(const css::uno::Reference<css::ucb::XContentEventListener>& p1) override + { OComponentDefinition::addContentEventListener(p1); } + virtual void SAL_CALL removeContentEventListener(const css::uno::Reference<css::ucb::XContentEventListener>& p1) override + { OComponentDefinition::removeContentEventListener(p1); } + virtual void SAL_CALL dispose() override + { OComponentDefinition::dispose(); } + virtual void SAL_CALL addEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override + { OComponentDefinition::addEventListener(p1); } + virtual void SAL_CALL removeEventListener(const css::uno::Reference<css::lang::XEventListener>& p1) override + { OComponentDefinition::removeEventListener(p1); } + + // OPropertySetHelper + virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual cppu::IPropertyArrayHelper* createArrayHelper() const override; + + +private: + // helper + void registerProperties(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/connection.cxx b/dbaccess/source/core/dataaccess/connection.cxx new file mode 100644 index 000000000..37cf9e669 --- /dev/null +++ b/dbaccess/source/core/dataaccess/connection.cxx @@ -0,0 +1,808 @@ +/* -*- 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 <iterator> + +#include "connection.hxx" +#include "datasource.hxx" +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <statement.hxx> +#include <preparedstatement.hxx> +#include <callablestatement.hxx> +#include <SingleSelectQueryComposer.hxx> +#include <querycomposer.hxx> + +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/tools/ConnectionTools.hpp> +#include <com/sun/star/reflection/ProxyFactory.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbmetadata.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdb::application; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::reflection; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::graphic; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::dbtools; + +using ::com::sun::star::sdb::tools::XTableName; +using ::com::sun::star::sdb::tools::XObjectNames; +using ::com::sun::star::sdb::tools::XDataSourceMetaData; + +namespace dbaccess +{ + +// XServiceInfo +OUString OConnection::getImplementationName( ) +{ + return "com.sun.star.comp.dbaccess.Connection"; +} + +sal_Bool OConnection::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OConnection::getSupportedServiceNames( ) +{ + Sequence< OUString > aSupported = OConnectionWrapper::getSupportedServiceNames(); + + if ( comphelper::findValue( aSupported, SERVICE_SDB_CONNECTION ) == -1 ) + { + sal_Int32 nLen = aSupported.getLength(); + aSupported.realloc( nLen + 1 ); + aSupported.getArray()[ nLen ] = SERVICE_SDB_CONNECTION; + } + + return aSupported; +} + +// XCloseable +void OConnection::close() +{ + // being closed is the same as being disposed + dispose(); +} + +sal_Bool OConnection::isClosed() +{ + MutexGuard aGuard(m_aMutex); + return !m_xMasterConnection.is(); +} + +// XConnection +Reference< XStatement > OConnection::createStatement() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference< XStatement > xStatement; + Reference< XStatement > xMasterStatement = m_xMasterConnection->createStatement(); + if ( xMasterStatement.is() ) + { + xStatement = new OStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +Reference< XPreparedStatement > OConnection::prepareStatement(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + // TODO convert the SQL to SQL the driver understands + Reference< XPreparedStatement > xStatement; + Reference< XPreparedStatement > xMasterStatement = m_xMasterConnection->prepareStatement(sql); + if ( xMasterStatement.is() ) + { + xStatement = new OPreparedStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +Reference< XPreparedStatement > OConnection::prepareCall(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference< XPreparedStatement > xStatement; + Reference< XPreparedStatement > xMasterStatement = m_xMasterConnection->prepareCall(sql); + if ( xMasterStatement.is() ) + { + xStatement = new OCallableStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +OUString OConnection::nativeSQL(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->nativeSQL(sql); +} + +void OConnection::setAutoCommit(sal_Bool autoCommit) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setAutoCommit(autoCommit); +} + +sal_Bool OConnection::getAutoCommit() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getAutoCommit(); +} + +void OConnection::commit() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->commit(); +} + +void OConnection::rollback() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->rollback(); +} + +Reference< XDatabaseMetaData > OConnection::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getMetaData(); +} + +void OConnection::setReadOnly(sal_Bool readOnly) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setReadOnly(readOnly); +} + +sal_Bool OConnection::isReadOnly() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->isReadOnly(); +} + +void OConnection::setCatalog(const OUString& catalog) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setCatalog(catalog); +} + +OUString OConnection::getCatalog() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getCatalog(); +} + +void OConnection::setTransactionIsolation(sal_Int32 level) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setTransactionIsolation(level); +} + +sal_Int32 OConnection::getTransactionIsolation() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getTransactionIsolation(); +} + +Reference< XNameAccess > OConnection::getTypeMap() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getTypeMap(); +} + +void OConnection::setTypeMap(const Reference< XNameAccess > & typeMap) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setTypeMap(typeMap); +} + +// OConnection + +OConnection::OConnection(ODatabaseSource& _rDB + , Reference< XConnection > const & _rxMaster + , const Reference< XComponentContext >& _rxORB) + :OSubComponent(m_aMutex, static_cast< OWeakObject* >(&_rDB)) + // as the queries reroute their refcounting to us, this m_aMutex is okey. If the queries + // container would do its own refcounting, it would have to acquire m_pMutex + // same for tables + ,m_aTableFilter(_rDB.m_pImpl->m_aTableFilter) + ,m_aTableTypeFilter(_rDB.m_pImpl->m_aTableTypeFilter) + ,m_aContext( _rxORB ) + ,m_xMasterConnection(_rxMaster) + ,m_aWarnings( Reference< XWarningsSupplier >( _rxMaster, UNO_QUERY ) ) + ,m_nInAppend(0) + ,m_bSupportsViews(false) + ,m_bSupportsUsers(false) + ,m_bSupportsGroups(false) +{ + osl_atomic_increment(&m_refCount); + + try + { + Reference< XProxyFactory > xProxyFactory = ProxyFactory::create( m_aContext ); + Reference<XAggregation> xAgg = xProxyFactory->createProxy(_rxMaster); + setDelegation(xAgg,m_refCount); + OSL_ENSURE(m_xConnection.is(), "OConnection::OConnection : invalid master connection !"); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xTableUIProvider.set(m_xMasterConnection, css::uno::UNO_QUERY); + + try + { + m_xQueries = OQueryContainer::create(Reference< XNameContainer >(_rDB.getQueryDefinitions(), UNO_QUERY), this, _rxORB, &m_aWarnings).get(); + + bool bCase = true; + Reference<XDatabaseMetaData> xMeta; + try + { + xMeta = getMetaData(); + bCase = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(); + } + catch(const SQLException&) + { + } + Reference< XNameContainer > xTableDefinitions(_rDB.getTables(),UNO_QUERY); + m_pTables.reset( new OTableContainer( *this, m_aMutex, this, bCase, xTableDefinitions, this, m_nInAppend ) ); + + // check if we support types + if ( xMeta.is() ) + { + Reference<XResultSet> xRes = xMeta->getTableTypes(); + if(xRes.is()) + { + Reference<XRow> xRow(xRes,UNO_QUERY); + while(xRes->next()) + { + OUString sValue = xRow->getString(1); + if( !xRow->wasNull() && sValue == "VIEW") + { + m_bSupportsViews = true; + break; + } + } + } + // some dbs don't support this type so we should ask if a XViewsSupplier is supported + if(!m_bSupportsViews) + { + Reference< XViewsSupplier > xMaster(getMasterTables(),UNO_QUERY); + + if (xMaster.is() && xMaster->getViews().is()) + m_bSupportsViews = true; + } + if(m_bSupportsViews) + { + m_pViews.reset( new OViewContainer(*this, m_aMutex, this, bCase, this, m_nInAppend) ); + m_pViews->addContainerListener(m_pTables.get()); + m_pTables->addContainerListener(m_pViews.get()); + } + m_bSupportsUsers = Reference< XUsersSupplier> (getMasterTables(),UNO_QUERY).is(); + m_bSupportsGroups = Reference< XGroupsSupplier> (getMasterTables(),UNO_QUERY).is(); + + impl_checkTableQueryNames_nothrow(); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + osl_atomic_decrement( &m_refCount ); +} + +OConnection::~OConnection() +{ +} + +// XWarningsSupplier +Any SAL_CALL OConnection::getWarnings() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_aWarnings.getWarnings(); +} + +void SAL_CALL OConnection::clearWarnings( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_aWarnings.clearWarnings(); +} + +namespace +{ + struct CompareTypeByName + { + bool operator() ( const Type& _rLHS, const Type& _rRHS ) const + { + return _rLHS.getTypeName() < _rRHS.getTypeName(); + } + }; + typedef std::set< Type, CompareTypeByName > TypeBag; + + void lcl_copyTypes( TypeBag& _out_rTypes, const Sequence< Type >& _rTypes ) + { + std::copy( _rTypes.begin(), _rTypes.end(), + std::insert_iterator< TypeBag >( _out_rTypes, _out_rTypes.begin() ) ); + } +} + +// css::lang::XTypeProvider +Sequence< Type > OConnection::getTypes() +{ + TypeBag aNormalizedTypes; + + lcl_copyTypes( aNormalizedTypes, OSubComponent::getTypes() ); + lcl_copyTypes( aNormalizedTypes, OConnection_Base::getTypes() ); + lcl_copyTypes( aNormalizedTypes, ::connectivity::OConnectionWrapper::getTypes() ); + + if ( !m_bSupportsViews ) + aNormalizedTypes.erase( cppu::UnoType<XViewsSupplier>::get() ); + if ( !m_bSupportsUsers ) + aNormalizedTypes.erase( cppu::UnoType<XUsersSupplier>::get() ); + if ( !m_bSupportsGroups ) + aNormalizedTypes.erase( cppu::UnoType<XGroupsSupplier>::get() ); + + return comphelper::containerToSequence(aNormalizedTypes); +} + +Sequence< sal_Int8 > OConnection::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::uno::XInterface +Any OConnection::queryInterface( const Type & rType ) +{ + if ( !m_bSupportsViews && rType.equals( cppu::UnoType<XViewsSupplier>::get() ) ) + return Any(); + else if ( !m_bSupportsUsers && rType.equals( cppu::UnoType<XUsersSupplier>::get() ) ) + return Any(); + else if ( !m_bSupportsGroups && rType.equals( cppu::UnoType<XGroupsSupplier>::get() ) ) + return Any(); + Any aReturn = OSubComponent::queryInterface( rType ); + if (!aReturn.hasValue()) + { + aReturn = OConnection_Base::queryInterface( rType ); + if (!aReturn.hasValue()) + aReturn = OConnectionWrapper::queryInterface( rType ); + } + return aReturn; +} + +void OConnection::acquire() noexcept +{ + // include this one when you want to see who calls it (call graph) + OSubComponent::acquire(); +} + +void OConnection::release() noexcept +{ + // include this one when you want to see who calls it (call graph) + OSubComponent::release(); +} + +// OSubComponent +void OConnection::disposing() +{ + MutexGuard aGuard(m_aMutex); + + OSubComponent::disposing(); + OConnectionWrapper::disposing(); + + for (auto const& statement : m_aStatements) + { + Reference<XComponent> xComp(statement.get(),UNO_QUERY); + ::comphelper::disposeComponent(xComp); + } + m_aStatements.clear(); + m_xMasterTables = nullptr; + + if(m_pTables) + m_pTables->dispose(); + if(m_pViews) + m_pViews->dispose(); + + ::comphelper::disposeComponent(m_xQueries); + + for (auto const& composer : m_aComposers) + { + Reference<XComponent> xComp(composer.get(),UNO_QUERY); + ::comphelper::disposeComponent(xComp); + } + + m_aComposers.clear(); + + try + { + if (m_xMasterConnection.is()) + m_xMasterConnection->close(); + } + catch(const Exception&) + { + } + m_xMasterConnection = nullptr; +} + +// XChild +Reference< XInterface > OConnection::getParent() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xParent; +} + +void OConnection::setParent(const Reference< XInterface > & /*Parent*/) +{ + throw NoSupportException(); +} + +// XSQLQueryComposerFactory +Reference< XSQLQueryComposer > OConnection::createQueryComposer() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + // Reference< XNumberFormatsSupplier > xSupplier = pParent->getNumberFormatsSupplier(); + Reference< XSQLQueryComposer > xComposer( new OQueryComposer( this ) ); + m_aComposers.emplace_back(xComposer); + return xComposer; +} + +void OConnection::impl_fillTableFilter() +{ + Reference<XPropertySet> xProp(getParent(),UNO_QUERY); + if ( xProp.is() ) + { + xProp->getPropertyValue(PROPERTY_TABLEFILTER) >>= m_aTableFilter; + xProp->getPropertyValue(PROPERTY_TABLETYPEFILTER) >>= m_aTableTypeFilter; + } +} + +void OConnection::refresh(const Reference< XNameAccess >& _rToBeRefreshed) +{ + if ( _rToBeRefreshed == Reference< XNameAccess >(m_pTables.get()) ) + { + if (m_pTables && !m_pTables->isInitialized()) + { + impl_fillTableFilter(); + // check if our "master connection" can supply tables + getMasterTables(); + + if (m_xMasterTables.is() && m_xMasterTables->getTables().is()) + { // yes -> wrap them + m_pTables->construct(m_xMasterTables->getTables(),m_aTableFilter, m_aTableTypeFilter); + } + else + { // no -> use an own container + m_pTables->construct(m_aTableFilter, m_aTableTypeFilter); + } + } + } + else if ( _rToBeRefreshed == Reference< XNameAccess >(m_pViews.get()) ) + { + if (m_pViews && !m_pViews->isInitialized()) + { + impl_fillTableFilter(); + // check if our "master connection" can supply tables + Reference< XViewsSupplier > xMaster(getMasterTables(),UNO_QUERY); + + if (xMaster.is() && xMaster->getViews().is()) + m_pViews->construct(xMaster->getViews(),m_aTableFilter, m_aTableTypeFilter); + else + m_pViews->construct(m_aTableFilter, m_aTableTypeFilter); + } + } +} + +// XTablesSupplier +Reference< XNameAccess > OConnection::getTables() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + refresh(m_pTables.get()); + + return m_pTables.get(); +} + +Reference< XNameAccess > SAL_CALL OConnection::getViews( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + refresh(m_pViews.get()); + + return m_pViews.get(); +} + +// XQueriesSupplier +Reference< XNameAccess > OConnection::getQueries() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + return m_xQueries; +} + +// css::sdb::XCommandPreparation +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCommand( const OUString& command, sal_Int32 commandType ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + OUString aStatement; + switch (commandType) + { + case CommandType::TABLE: + { + aStatement = "SELECT * FROM "; + + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents( getMetaData(), command, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation ); + aStatement += ::dbtools::composeTableNameForSelect( this, sCatalog, sSchema, sTable ); + } + break; + case CommandType::QUERY: + if ( m_xQueries->hasByName(command) ) + { + Reference< XPropertySet > xQuery(m_xQueries->getByName(command),UNO_QUERY); + xQuery->getPropertyValue(PROPERTY_COMMAND) >>= aStatement; + } + break; + default: + aStatement = command; + } + // TODO EscapeProcessing + return prepareStatement(aStatement); +} + +Reference< XInterface > SAL_CALL OConnection::createInstance( const OUString& _sServiceSpecifier ) +{ + Reference< XServiceInfo > xRet; + if ( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER == _sServiceSpecifier || _sServiceSpecifier == "com.sun.star.sdb.SingleSelectQueryAnalyzer" ) + { + xRet = new OSingleSelectQueryComposer( getTables(),this, m_aContext ); + m_aComposers.emplace_back(xRet); + } + else + { + if ( !_sServiceSpecifier.isEmpty() ) + { + TSupportServices::const_iterator aFind = m_aSupportServices.find(_sServiceSpecifier); + if ( aFind == m_aSupportServices.end() ) + { + Reference<XConnection> xMy(this); + Sequence<Any> aArgs{ Any(NamedValue("ActiveConnection",Any(xMy))) }; + aFind = m_aSupportServices.emplace( + _sServiceSpecifier, + m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(_sServiceSpecifier, aArgs, m_aContext) + ).first; + } + return aFind->second; + } + } + return Reference<XInterface>(xRet, UNO_QUERY); +} + +Reference< XInterface > SAL_CALL OConnection::createInstanceWithArguments( const OUString& _sServiceSpecifier, const Sequence< Any >& /*Arguments*/ ) +{ + return createInstance(_sServiceSpecifier); +} + +Sequence< OUString > SAL_CALL OConnection::getAvailableServiceNames( ) +{ + Sequence< OUString > aRet { SERVICE_NAME_SINGLESELECTQUERYCOMPOSER }; + return aRet; +} + +Reference< XTablesSupplier > const & OConnection::getMasterTables() +{ +// check if out "master connection" can supply tables + if(!m_xMasterTables.is()) + { + try + { + Reference<XDatabaseMetaData> xMeta = getMetaData(); + if ( xMeta.is() ) + m_xMasterTables = ::dbtools::getDataDefinitionByURLAndConnection( xMeta->getURL(), m_xMasterConnection, m_aContext ); + } + catch(const SQLException&) + { + } + } + return m_xMasterTables; +} + +// XUsersSupplier +Reference< XNameAccess > SAL_CALL OConnection::getUsers( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference<XUsersSupplier> xUsr(getMasterTables(),UNO_QUERY); + return xUsr.is() ? xUsr->getUsers() : Reference< XNameAccess >(); +} + +// XGroupsSupplier +Reference< XNameAccess > SAL_CALL OConnection::getGroups( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + Reference<XGroupsSupplier> xGrp(getMasterTables(),UNO_QUERY); + return xGrp.is() ? xGrp->getGroups() : Reference< XNameAccess >(); +} + +void OConnection::impl_loadConnectionTools_throw() +{ + m_xConnectionTools = css::sdb::tools::ConnectionTools::createWithConnection( m_aContext, this ); +} + +Reference< XTableName > SAL_CALL OConnection::createTableName( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->createTableName(); +} + +Reference< XObjectNames > SAL_CALL OConnection::getObjectNames( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getObjectNames(); +} + +Reference< XDataSourceMetaData > SAL_CALL OConnection::getDataSourceMetaData( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getDataSourceMetaData(); +} + +Reference< css::container::XNameAccess > SAL_CALL OConnection::getFieldsByCommandDescriptor( ::sal_Int32 commandType, const OUString& command, css::uno::Reference< css::lang::XComponent >& keepFieldsAlive ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getFieldsByCommandDescriptor(commandType,command,keepFieldsAlive); +} + +Reference< XSingleSelectQueryComposer > SAL_CALL OConnection::getComposer( ::sal_Int32 commandType, const OUString& command ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getComposer(commandType,command); +} + +void OConnection::impl_checkTableQueryNames_nothrow() +{ + DatabaseMetaData aMeta( static_cast< XConnection* >( this ) ); + if ( !aMeta.supportsSubqueriesInFrom() ) + // nothing to do + return; + + try + { + Reference< XNameAccess > xTables( getTables() ); + const Sequence< OUString > aTableNames( xTables->getElementNames() ); + std::set< OUString > aSortedTableNames( aTableNames.begin(), aTableNames.end() ); + + Reference< XNameAccess > xQueries( getQueries() ); + const Sequence< OUString > aQueryNames( xQueries->getElementNames() ); + + for ( auto const & queryName : aQueryNames ) + { + if ( aSortedTableNames.find( queryName ) != aSortedTableNames.end() ) + { + OUString sConflictWarning( DBA_RES( RID_STR_CONFLICTING_NAMES ) ); + m_aWarnings.appendWarning( sConflictWarning, "01SB0", *this ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< XGraphic > SAL_CALL OConnection::getTableIcon( const OUString& TableName, ::sal_Int32 ColorMode ) +{ + Reference< XGraphic > xReturn; + + // ask our aggregate + if ( m_xTableUIProvider.is() ) + xReturn = m_xTableUIProvider->getTableIcon( TableName, ColorMode ); + + // ask ourself + // well, we don't have own functionality here ... + // In the future, we might decide to delegate the complete handling to this interface. + // In this case, we would need to load the icon here. + + return xReturn; +} + +Reference< XInterface > SAL_CALL OConnection::getTableEditor( const Reference< XDatabaseDocumentUI >& DocumentUI, const OUString& TableName ) +{ + Reference< XInterface > xReturn; + + // ask our aggregate + if ( m_xTableUIProvider.is() ) + xReturn = m_xTableUIProvider->getTableEditor( DocumentUI, TableName ); + + // ask ourself + // well, we don't have own functionality here ... + // In the future, we might decide to delegate the complete handling to this interface. + // In this case, we would need to instantiate a css.sdb.TableDesign here. + + return xReturn; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/connection.hxx b/dbaccess/source/core/dataaccess/connection.hxx new file mode 100644 index 000000000..4ecfa7d70 --- /dev/null +++ b/dbaccess/source/core/dataaccess/connection.hxx @@ -0,0 +1,229 @@ +/* -*- 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 <map> + +#include <apitools.hxx> +#include <querycontainer.hxx> +#include <tablecontainer.hxx> +#include <viewcontainer.hxx> +#include <RefreshListener.hxx> + +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp> +#include <com/sun/star/sdb/XCommandPreparation.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/sdbcx/XUsersSupplier.hpp> +#include <com/sun/star/sdbcx/XGroupsSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/tools/XConnectionTools.hpp> +#include <com/sun/star/sdb/application/XTableUIProvider.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> + +#include <cppuhelper/implbase13.hxx> +#include <connectivity/ConnectionWrapper.hxx> +#include <connectivity/CommonTools.hxx> +#include <connectivity/warningscontainer.hxx> + +namespace dbaccess +{ + +typedef cppu::ImplHelper13 < css::container::XChild + , css::sdbcx::XTablesSupplier + , css::sdbcx::XViewsSupplier + , css::sdbc::XConnection + , css::sdbc::XWarningsSupplier + , css::sdb::XQueriesSupplier + , css::sdb::XSQLQueryComposerFactory + , css::sdb::XCommandPreparation + , css::lang::XMultiServiceFactory + , css::sdbcx::XUsersSupplier + , css::sdbcx::XGroupsSupplier + , css::sdb::tools::XConnectionTools + , css::sdb::application::XTableUIProvider + > OConnection_Base; + +class ODatabaseSource; +// OConnection +class OConnection final :public ::cppu::BaseMutex + ,public OSubComponent + ,public ::connectivity::OConnectionWrapper + ,public OConnection_Base + ,public IRefreshListener +{ + css::uno::Reference< css::sdbcx::XTablesSupplier > + m_xMasterTables; // just to avoid the recreation of the catalog + connectivity::OWeakRefArray m_aStatements; + css::uno::Reference< css::container::XNameAccess > + m_xQueries; + connectivity::OWeakRefArray m_aComposers; + + // the filter as set on the parent data link at construction of the connection + css::uno::Sequence< OUString > m_aTableFilter; + css::uno::Sequence< OUString > m_aTableTypeFilter; + css::uno::Reference< css::uno::XComponentContext > m_aContext; + css::uno::Reference< css::sdbc::XConnection > m_xMasterConnection; + css::uno::Reference< css::sdb::tools::XConnectionTools > m_xConnectionTools; + css::uno::Reference< css::sdb::application::XTableUIProvider > m_xTableUIProvider; + + // defines the helper services for example to query the command of a view + // @ see com.sun.star.sdb.tools.XViewAccess + typedef std::map< OUString, css::uno::Reference< css::uno::XInterface> > TSupportServices; + TSupportServices m_aSupportServices; + + std::unique_ptr<OTableContainer> m_pTables; + std::unique_ptr<OViewContainer> m_pViews; + ::dbtools::WarningsContainer m_aWarnings; + std::atomic<std::size_t> m_nInAppend; + bool m_bSupportsViews; // true when the getTableTypes return "VIEW" as type + bool m_bSupportsUsers; + bool m_bSupportsGroups; + + virtual ~OConnection() override; +public: + OConnection(ODatabaseSource& _rDB + ,css::uno::Reference< css::sdbc::XConnection > const & _rxMaster + ,const css::uno::Reference< css::uno::XComponentContext >& _rxORB); + +// 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; + +// OComponentHelper + virtual void SAL_CALL disposing() override; + +// css::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + +// css::sdbcx::XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; +// css::sdbcx::XViewsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getViews( ) override; + +// css::sdb::XQueriesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getQueries( ) override; + +// css::sdb::XSQLQueryComposerFactory + virtual css::uno::Reference< css::sdb::XSQLQueryComposer > SAL_CALL createQueryComposer( ) override; + +// css::sdb::XCommandPreparation + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCommand( const OUString& command, sal_Int32 commandType ) override; + +// css::sdbc::XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) 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; + +// XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + +// css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + + // XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XUsersSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getUsers( ) override; + // XGroupsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getGroups( ) override; + + // XConnectionTools + virtual css::uno::Reference< css::sdb::tools::XTableName > SAL_CALL createTableName( ) override; + virtual css::uno::Reference< css::sdb::tools::XObjectNames > SAL_CALL getObjectNames( ) override; + virtual css::uno::Reference< css::sdb::tools::XDataSourceMetaData > SAL_CALL getDataSourceMetaData( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getFieldsByCommandDescriptor( ::sal_Int32 commandType, const OUString& command, css::uno::Reference< css::lang::XComponent >& keepFieldsAlive ) override; + virtual css::uno::Reference< css::sdb::XSingleSelectQueryComposer > SAL_CALL getComposer( ::sal_Int32 commandType, const OUString& command ) override; + + // XTableUIProvider + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL getTableIcon( const OUString& TableName, ::sal_Int32 ColorMode ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getTableEditor( const css::uno::Reference< css::sdb::application::XDatabaseDocumentUI >& DocumentUI, const OUString& TableName ) override; + + // IRefreshListener + virtual void refresh(const css::uno::Reference< css::container::XNameAccess >& _rToBeRefreshed) override; + +private: + /// @throws css::lang::DisposedException + void checkDisposed() + { + if ( rBHelper.bDisposed || !m_xConnection.is() ) + throw css::lang::DisposedException(); + } + + css::uno::Reference< css::sdbcx::XTablesSupplier > const & getMasterTables(); + + /** checks whether or not there are naming conflicts between tables and queries + */ + void impl_checkTableQueryNames_nothrow(); + + /** loads the XConnectionTools implementation which we forward the respective functionality to + + @throws css::uno::RuntimeException + if the implementation cannot be loaded + + @postcond + m_xConnectionTools is nol <NULL/> + */ + void impl_loadConnectionTools_throw(); + + /** reads the table filter and table type filter from the datasource + */ + void impl_fillTableFilter(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx b/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx new file mode 100644 index 000000000..ef5239a67 --- /dev/null +++ b/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx @@ -0,0 +1,229 @@ +/* -*- 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 <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdb/XDataAccessDescriptorFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +namespace +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::lang::XServiceInfo; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::beans::Property; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::sdbc::XResultSet; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::PropertyValue; + + namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; + namespace CommandType = ::com::sun::star::sdb::CommandType; + + // DataAccessDescriptor + typedef ::comphelper::OMutexAndBroadcastHelper DataAccessDescriptor_MutexBase; + + typedef ::cppu::WeakImplHelper< XServiceInfo + > DataAccessDescriptor_TypeBase; + + typedef ::comphelper::OPropertyContainer DataAccessDescriptor_PropertyBase; + + class DataAccessDescriptor :public DataAccessDescriptor_MutexBase + ,public DataAccessDescriptor_TypeBase + ,public DataAccessDescriptor_PropertyBase + ,public ::comphelper::OPropertyArrayUsageHelper< DataAccessDescriptor > + { + public: + DataAccessDescriptor(); + + // UNO + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + protected: + virtual ~DataAccessDescriptor() override; + + protected: + // XPropertySet + virtual Reference< XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + private: + // </properties> + OUString m_sDataSourceName; + OUString m_sDatabaseLocation; + OUString m_sConnectionResource; + Sequence< PropertyValue > m_aConnectionInfo; + Reference< XConnection > m_xActiveConnection; + OUString m_sCommand; + sal_Int32 m_nCommandType; + OUString m_sFilter; + OUString m_sOrder; + OUString m_sHavingClause; + OUString m_sGroupBy; + bool m_bEscapeProcessing; + Reference< XResultSet > m_xResultSet; + Sequence< Any > m_aSelection; + bool m_bBookmarkSelection; + OUString m_sColumnName; + Reference< XPropertySet > m_xColumn; + // </properties> + }; + + DataAccessDescriptor::DataAccessDescriptor() + :DataAccessDescriptor_PropertyBase( m_aBHelper ) + ,m_nCommandType( CommandType::COMMAND ) + ,m_bEscapeProcessing( true ) + ,m_bBookmarkSelection( true ) + { + registerProperty(PROPERTY_DATASOURCENAME, PROPERTY_ID_DATASOURCENAME, PropertyAttribute::BOUND, &m_sDataSourceName , cppu::UnoType<decltype(m_sDataSourceName )>::get()); + registerProperty(PROPERTY_DATABASE_LOCATION, PROPERTY_ID_DATABASE_LOCATION, PropertyAttribute::BOUND, &m_sDatabaseLocation , cppu::UnoType<decltype(m_sDatabaseLocation )>::get()); + registerProperty(PROPERTY_CONNECTION_RESOURCE, PROPERTY_ID_CONNECTION_RESOURCE, PropertyAttribute::BOUND, &m_sConnectionResource , cppu::UnoType<decltype(m_sConnectionResource )>::get()); + registerProperty(PROPERTY_CONNECTION_INFO, PROPERTY_ID_CONNECTION_INFO, PropertyAttribute::BOUND, &m_aConnectionInfo , cppu::UnoType<decltype(m_aConnectionInfo )>::get()); + registerProperty(PROPERTY_ACTIVE_CONNECTION, PROPERTY_ID_ACTIVE_CONNECTION, PropertyAttribute::BOUND, &m_xActiveConnection , cppu::UnoType<decltype(m_xActiveConnection )>::get()); + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, &m_sCommand , cppu::UnoType<decltype(m_sCommand )>::get()); + registerProperty(PROPERTY_COMMAND_TYPE, PROPERTY_ID_COMMAND_TYPE, PropertyAttribute::BOUND, &m_nCommandType , cppu::UnoType<decltype(m_nCommandType )>::get()); + registerProperty(PROPERTY_FILTER, PROPERTY_ID_FILTER, PropertyAttribute::BOUND, &m_sFilter , cppu::UnoType<decltype(m_sFilter )>::get()); + registerProperty(PROPERTY_ORDER, PROPERTY_ID_ORDER, PropertyAttribute::BOUND, &m_sOrder , cppu::UnoType<decltype(m_sOrder )>::get()); + registerProperty(PROPERTY_HAVING_CLAUSE, PROPERTY_ID_HAVING_CLAUSE, PropertyAttribute::BOUND, &m_sHavingClause , cppu::UnoType<decltype(m_sHavingClause )>::get()); + registerProperty(PROPERTY_GROUP_BY, PROPERTY_ID_GROUP_BY, PropertyAttribute::BOUND, &m_sGroupBy , cppu::UnoType<decltype(m_sGroupBy )>::get()); + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, &m_bEscapeProcessing , cppu::UnoType<decltype(m_bEscapeProcessing )>::get()); + registerProperty(PROPERTY_RESULT_SET, PROPERTY_ID_RESULT_SET, PropertyAttribute::BOUND, &m_xResultSet , cppu::UnoType<decltype(m_xResultSet )>::get()); + registerProperty(PROPERTY_SELECTION, PROPERTY_ID_SELECTION, PropertyAttribute::BOUND, &m_aSelection , cppu::UnoType<decltype(m_aSelection )>::get()); + registerProperty(PROPERTY_BOOKMARK_SELECTION, PROPERTY_ID_BOOKMARK_SELECTION, PropertyAttribute::BOUND, &m_bBookmarkSelection , cppu::UnoType<decltype(m_bBookmarkSelection )>::get()); + registerProperty(PROPERTY_COLUMN_NAME, PROPERTY_ID_COLUMN_NAME, PropertyAttribute::BOUND, &m_sColumnName , cppu::UnoType<decltype(m_sColumnName )>::get()); + registerProperty(PROPERTY_COLUMN, PROPERTY_ID_COLUMN, PropertyAttribute::BOUND, &m_xColumn , cppu::UnoType<decltype(m_xColumn )>::get()); + } + + DataAccessDescriptor::~DataAccessDescriptor() + { + } + + IMPLEMENT_FORWARD_XINTERFACE2( DataAccessDescriptor, DataAccessDescriptor_TypeBase, DataAccessDescriptor_PropertyBase ); + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( DataAccessDescriptor, DataAccessDescriptor_TypeBase, DataAccessDescriptor_PropertyBase ); + + OUString SAL_CALL DataAccessDescriptor::getImplementationName() + { + return "com.sun.star.comp.dba.DataAccessDescriptor"; + } + + sal_Bool SAL_CALL DataAccessDescriptor::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > SAL_CALL DataAccessDescriptor::getSupportedServiceNames( ) + { + return { "com.sun.star.sdb.DataAccessDescriptor" }; + } + + Reference< XPropertySetInfo > SAL_CALL DataAccessDescriptor::getPropertySetInfo() + { + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + ::cppu::IPropertyArrayHelper& DataAccessDescriptor::getInfoHelper() + { + return *getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* DataAccessDescriptor::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + // DataAccessDescriptorFactory + class DataAccessDescriptorFactory: public ::cppu::WeakImplHelper<XServiceInfo, css::sdb::XDataAccessDescriptorFactory> + { + public: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XDataAccessDescriptorFactory + virtual Reference< XPropertySet > SAL_CALL createDataAccessDescriptor( ) override; + + DataAccessDescriptorFactory(); + }; + + DataAccessDescriptorFactory::DataAccessDescriptorFactory() + { + } + + OUString SAL_CALL DataAccessDescriptorFactory::getImplementationName() + { + return "com.sun.star.comp.dba.DataAccessDescriptorFactory"; + } + + sal_Bool SAL_CALL DataAccessDescriptorFactory::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > SAL_CALL DataAccessDescriptorFactory::getSupportedServiceNames() + { + return { "com.sun.star.sdb.DataAccessDescriptorFactory" }; + } + + Reference< XPropertySet > SAL_CALL DataAccessDescriptorFactory::createDataAccessDescriptor( ) + { + return new DataAccessDescriptor(); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_dba_DataAccessDescriptorFactory( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DataAccessDescriptorFactory()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasecontext.cxx b/dbaccess/source/core/dataaccess/databasecontext.cxx new file mode 100644 index 000000000..5afdadb67 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasecontext.cxx @@ -0,0 +1,753 @@ +/* -*- 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 <config_features.h> + +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <databasecontext.hxx> +#include "databaseregistrations.hxx" +#include "datasource.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/task/InteractionClassification.hpp> +#include <com/sun/star/ucb/InteractiveIOException.hpp> +#include <com/sun/star/ucb/IOErrorCode.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <basic/basmgr.hxx> +#include <comphelper/enumhelper.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> +#include <svl/filenotation.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <rtl/ref.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <vector> + +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::document; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::osl; +using namespace ::utl; + +using ::com::sun::star::task::InteractionClassification_ERROR; +using ::com::sun::star::ucb::IOErrorCode_NO_FILE; +using ::com::sun::star::ucb::InteractiveIOException; +using ::com::sun::star::ucb::IOErrorCode_NOT_EXISTING; +using ::com::sun::star::ucb::IOErrorCode_NOT_EXISTING_PATH; + +namespace dbaccess +{ + + typedef ::cppu::WeakImplHelper< XTerminateListener + > DatabaseDocumentLoader_Base; + class DatabaseDocumentLoader : public DatabaseDocumentLoader_Base + { + private: + Reference< XDesktop2 > m_xDesktop; + std::vector< const ODatabaseModelImpl* > m_aDatabaseDocuments; + + public: + explicit DatabaseDocumentLoader( const Reference<XComponentContext> & rxContext); + + void append(const ODatabaseModelImpl& _rModelImpl ) + { + m_aDatabaseDocuments.emplace_back(&_rModelImpl); + } + + void remove(const ODatabaseModelImpl& _rModelImpl) + { + m_aDatabaseDocuments.erase(std::find(m_aDatabaseDocuments.begin(), m_aDatabaseDocuments.end(), &_rModelImpl)); + } + + + private: + // XTerminateListener + virtual void SAL_CALL queryTermination( const lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const lang::EventObject& Event ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + }; + + DatabaseDocumentLoader::DatabaseDocumentLoader( const Reference<XComponentContext> & rxContext ) + { + try + { + m_xDesktop.set( Desktop::create(rxContext) ); + m_xDesktop->addTerminateListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void SAL_CALL DatabaseDocumentLoader::queryTermination( const lang::EventObject& /*Event*/ ) + { + std::vector< const ODatabaseModelImpl* > aCpy(m_aDatabaseDocuments); + for( const auto& pCopy : aCpy ) + { + try + { + const Reference< XModel2 > xMod( pCopy->getModel_noCreate(), + UNO_QUERY_THROW ); + if( !xMod->getControllers()->hasMoreElements() ) + { + Reference< util::XCloseable > xClose( xMod, + UNO_QUERY_THROW ); + xClose->close( false ); + } + } + catch( const CloseVetoException& ) + { + throw TerminationVetoException(); + } + } + } + + void SAL_CALL DatabaseDocumentLoader::notifyTermination( const lang::EventObject& /*Event*/ ) + { + } + + void SAL_CALL DatabaseDocumentLoader::disposing( const lang::EventObject& /*Source*/ ) + { + } + +// ODatabaseContext + +ODatabaseContext::ODatabaseContext( const Reference< XComponentContext >& _rxContext ) + :DatabaseAccessContext_Base(m_aMutex) + ,m_aContext( _rxContext ) + ,m_aContainerListeners(m_aMutex) +{ + m_xDatabaseDocumentLoader = new DatabaseDocumentLoader( _rxContext ); + +#if HAVE_FEATURE_SCRIPTING + ::basic::BasicManagerRepository::registerCreationListener( *this ); +#endif + + osl_atomic_increment( &m_refCount ); + { + m_xDBRegistrationAggregate.set( createDataSourceRegistrations( m_aContext ), UNO_SET_THROW ); + m_xDatabaseRegistrations.set( m_xDBRegistrationAggregate, UNO_QUERY_THROW ); + + m_xDBRegistrationAggregate->setDelegator( *this ); + } + osl_atomic_decrement( &m_refCount ); +} + +ODatabaseContext::~ODatabaseContext() +{ +#if HAVE_FEATURE_SCRIPTING + ::basic::BasicManagerRepository::revokeCreationListener( *this ); +#endif + + m_xDatabaseDocumentLoader.clear(); + m_xDBRegistrationAggregate->setDelegator( nullptr ); + m_xDBRegistrationAggregate.clear(); + m_xDatabaseRegistrations.clear(); +} + +// XServiceInfo +OUString ODatabaseContext::getImplementationName( ) +{ + return "com.sun.star.comp.dba.ODatabaseContext"; +} + +sal_Bool ODatabaseContext::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > ODatabaseContext::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DatabaseContext" }; +} + +Reference< XInterface > ODatabaseContext::impl_createNewDataSource() +{ + ::rtl::Reference pImpl( new ODatabaseModelImpl( m_aContext, *this ) ); + Reference< XDataSource > xDataSource( pImpl->getOrCreateDataSource() ); + + return xDataSource; +} + +Reference< XInterface > SAL_CALL ODatabaseContext::createInstance( ) +{ + // for convenience of the API user, we ensure the document is fully initialized (effectively: XLoadable::initNew + // has been called at the DatabaseDocument). + return impl_createNewDataSource(); +} + +Reference< XInterface > SAL_CALL ODatabaseContext::createInstanceWithArguments( const Sequence< Any >& _rArguments ) +{ + ::comphelper::NamedValueCollection aArgs( _rArguments ); + OUString sURL = aArgs.getOrDefault( INFO_POOLURL, OUString() ); + + Reference< XInterface > xDataSource; + if ( !sURL.isEmpty() ) + xDataSource = getObject( sURL ); + + if ( !xDataSource.is() ) + xDataSource = impl_createNewDataSource(); + + return xDataSource; +} + +// DatabaseAccessContext_Base +void ODatabaseContext::disposing() +{ + // notify our listener + css::lang::EventObject aDisposeEvent(static_cast< XContainer* >(this)); + m_aContainerListeners.disposeAndClear(aDisposeEvent); + + // dispose the data sources + // disposing seems to remove elements, so work on copy for valid iterators + ObjectCache objCopy; + objCopy.swap(m_aDatabaseObjects); + for (auto const& elem : objCopy) + { + rtl::Reference< ODatabaseModelImpl > obj(elem.second); + // make sure obj is acquired and does not delete itself from within + // dispose() + obj->dispose(); + } +} + +// XNamingService +Reference< XInterface > ODatabaseContext::getRegisteredObject(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + OUString sURL( getDatabaseLocation( _rName ) ); + + if ( sURL.isEmpty() ) + // there is a registration for this name, but no URL + throw IllegalArgumentException(); + + // check if URL is already loaded + Reference< XInterface > xExistent = getObject( sURL ); + if ( xExistent.is() ) + return xExistent; + + return loadObjectFromURL( _rName, sURL ); +} + +Reference< XInterface > ODatabaseContext::loadObjectFromURL(const OUString& _rName,const OUString& _sURL) +{ + INetURLObject aURL( _sURL ); + + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + throw NoSuchElementException( _rName, *this ); + + bool bEmbeddedDataSource = aURL.isSchemeEqualTo(INetProtocol::VndSunStarPkg); + try + { + if (!bEmbeddedDataSource) + { + ::ucbhelper::Content aContent( _sURL, nullptr, comphelper::getProcessComponentContext() ); + if ( !aContent.isDocument() ) + throw InteractiveIOException( + _sURL, *this, InteractionClassification_ERROR, IOErrorCode_NO_FILE + ); + } + } + catch ( const InteractiveIOException& e ) + { + if ( ( e.Code == IOErrorCode_NO_FILE ) + || ( e.Code == IOErrorCode_NOT_EXISTING ) + || ( e.Code == IOErrorCode_NOT_EXISTING_PATH ) + ) + { + // #i40463# #i39187# + OUString sErrorMessage( DBA_RES( RID_STR_FILE_DOES_NOT_EXIST ) ); + ::svt::OFileNotation aTransformer( _sURL ); + + SQLException aError; + aError.Message = sErrorMessage.replaceAll( "$file$", aTransformer.get( ::svt::OFileNotation::N_SYSTEM ) ); + + throw WrappedTargetException( _sURL, *this, Any( aError ) ); + } + throw WrappedTargetException( _sURL, *this, ::cppu::getCaughtException() ); + } + catch( const Exception& ) + { + throw WrappedTargetException( _sURL, *this, ::cppu::getCaughtException() ); + } + + OSL_ENSURE( m_aDatabaseObjects.find( _sURL ) == m_aDatabaseObjects.end(), + "ODatabaseContext::loadObjectFromURL: not intended for already-cached objects!" ); + + ::rtl::Reference< ODatabaseModelImpl > pModelImpl; + { + pModelImpl.set( new ODatabaseModelImpl( _rName, m_aContext, *this ) ); + + Reference< XModel > xModel( pModelImpl->createNewModel_deliverOwnership(), UNO_SET_THROW ); + Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aArgs; + aArgs.put( "URL", _sURL ); + aArgs.put( "MacroExecutionMode", MacroExecMode::USE_CONFIG ); + aArgs.put( "InteractionHandler", task::InteractionHandler::createWithParent(m_aContext, nullptr) ); + if (bEmbeddedDataSource) + { + // In this case the host contains the real path, and the path is the embedded stream name. + auto const uri = css::uri::UriReferenceFactory::create(m_aContext)->parse(_sURL); + if (uri.is() && uri->isAbsolute() + && uri->hasAuthority() && !uri->hasQuery() && !uri->hasFragment()) + { + auto const auth = uri->getAuthority(); + auto const decAuth = rtl::Uri::decode( + auth, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8); + if (auth.isEmpty() == decAuth.isEmpty()) { + // Decoding of auth to UTF-8 succeeded: + OUString sBaseURI = decAuth + uri->getPath(); + aArgs.put("BaseURI", sBaseURI); + } else { + SAL_WARN( + "dbaccess.core", + "<" << _sURL << "> cannot be parse as vnd.sun.star.pkg URL"); + } + } else { + SAL_WARN( + "dbaccess.core", "<" << _sURL << "> cannot be parse as vnd.sun.star.pkg URL"); + } + } + + Sequence< PropertyValue > aResource( aArgs.getPropertyValues() ); + xLoad->load( aResource ); + xModel->attachResource( _sURL, aResource ); + + ::utl::CloseableComponent aEnsureClose( xModel ); + } + + setTransientProperties( _sURL, *pModelImpl ); + + return pModelImpl->getOrCreateDataSource(); +} + +void ODatabaseContext::appendAtTerminateListener(const ODatabaseModelImpl& _rDataSourceModel) +{ + m_xDatabaseDocumentLoader->append(_rDataSourceModel); +} + +void ODatabaseContext::removeFromTerminateListener(const ODatabaseModelImpl& _rDataSourceModel) +{ + m_xDatabaseDocumentLoader->remove(_rDataSourceModel); +} + +void ODatabaseContext::setTransientProperties(const OUString& _sURL, ODatabaseModelImpl& _rDataSourceModel ) +{ + if ( m_aDatasourceProperties.end() == m_aDatasourceProperties.find(_sURL) ) + return; + try + { + OUString sAuthFailedPassword; + Reference< XPropertySet > xDSProps( _rDataSourceModel.getOrCreateDataSource(), UNO_QUERY_THROW ); + const Sequence< PropertyValue >& rSessionPersistentProps = m_aDatasourceProperties[_sURL]; + for ( auto const & prop : rSessionPersistentProps ) + { + if ( prop.Name == "AuthFailedPassword" ) + { + OSL_VERIFY( prop.Value >>= sAuthFailedPassword ); + } + else + { + xDSProps->setPropertyValue( prop.Name, prop.Value ); + } + } + + _rDataSourceModel.m_sFailedPassword = sAuthFailedPassword; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODatabaseContext::registerObject(const OUString& _rName, const Reference< XInterface > & _rxObject) +{ + if ( _rName.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + Reference< XDocumentDataSource > xDocDataSource( _rxObject, UNO_QUERY ); + Reference< XModel > xModel( xDocDataSource.is() ? xDocDataSource->getDatabaseDocument() : Reference< XOfficeDatabaseDocument >(), UNO_QUERY ); + if ( !xModel.is() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + OUString sURL = xModel->getURL(); + if ( sURL.isEmpty() ) + throw IllegalArgumentException( DBA_RES( RID_STR_DATASOURCE_NOT_STORED ), *this, 2 ); + + { // avoid deadlocks: lock m_aMutex after checking arguments + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + registerDatabaseLocation( _rName, sURL ); + + ODatabaseSource::setName( xDocDataSource, _rName, ODatabaseSource::DBContextAccess() ); + } + + // notify our container listeners + ContainerEvent aEvent(static_cast<XContainer*>(this), Any(_rName), Any(_rxObject), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); +} + +void ODatabaseContext::storeTransientProperties( ODatabaseModelImpl& _rModelImpl) +{ + Reference< XPropertySet > xSource( _rModelImpl.getOrCreateDataSource(), UNO_QUERY ); + ::comphelper::NamedValueCollection aRememberProps; + + try + { + // get the info about the properties, check which ones are transient and not readonly + Reference< XPropertySetInfo > xSetInfo; + if (xSource.is()) + xSetInfo = xSource->getPropertySetInfo(); + Sequence< Property > aProperties; + if (xSetInfo.is()) + aProperties = xSetInfo->getProperties(); + + for ( const Property& rProperty : std::as_const(aProperties) ) + { + if ( ( ( rProperty.Attributes & PropertyAttribute::TRANSIENT) != 0 ) + && ( ( rProperty.Attributes & PropertyAttribute::READONLY) == 0 ) + ) + { + // found such a property + aRememberProps.put( rProperty.Name, xSource->getPropertyValue( rProperty.Name ) ); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // additionally, remember the "failed password", which is not available as property + // #i86178# + aRememberProps.put( "AuthFailedPassword", _rModelImpl.m_sFailedPassword ); + + OUString sDocumentURL( _rModelImpl.getURL() ); + if ( m_aDatabaseObjects.find( sDocumentURL ) != m_aDatabaseObjects.end() ) + { + m_aDatasourceProperties[ sDocumentURL ] = aRememberProps.getPropertyValues(); + } + else if ( m_aDatabaseObjects.find( _rModelImpl.m_sName ) != m_aDatabaseObjects.end() ) + { + OSL_FAIL( "ODatabaseContext::storeTransientProperties: a database document register by name? This shouldn't happen anymore!" ); + // all the code should have been changed so that registration is by URL only + m_aDatasourceProperties[ _rModelImpl.m_sName ] = aRememberProps.getPropertyValues(); + } + else + { + OSL_ENSURE( sDocumentURL.isEmpty() && _rModelImpl.m_sName.isEmpty() , + "ODatabaseContext::storeTransientProperties: a non-empty data source which I do not know?!" ); + } +} + +void SAL_CALL ODatabaseContext::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL ODatabaseContext::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + m_aContainerListeners.removeInterface(_rxListener); +} + +void ODatabaseContext::revokeObject(const OUString& _rName) +{ + ClearableMutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + OUString sURL = getDatabaseLocation( _rName ); + + revokeDatabaseLocation( _rName ); + // will throw if something goes wrong + + if ( m_aDatabaseObjects.find( _rName ) != m_aDatabaseObjects.end() ) + { + m_aDatasourceProperties[ sURL ] = m_aDatasourceProperties[ _rName ]; + } + + // check if URL is already loaded + ObjectCache::const_iterator aExistent = m_aDatabaseObjects.find( sURL ); + if ( aExistent != m_aDatabaseObjects.end() ) + m_aDatabaseObjects.erase( aExistent ); + + // notify our container listeners + ContainerEvent aEvent( *this, Any( _rName ), Any(), Any() ); + aGuard.clear(); + m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvent ); +} + +sal_Bool SAL_CALL ODatabaseContext::hasRegisteredDatabase( const OUString& Name ) +{ + return m_xDatabaseRegistrations->hasRegisteredDatabase( Name ); +} + +Sequence< OUString > SAL_CALL ODatabaseContext::getRegistrationNames() +{ + return m_xDatabaseRegistrations->getRegistrationNames(); +} + +OUString SAL_CALL ODatabaseContext::getDatabaseLocation( const OUString& Name ) +{ + return m_xDatabaseRegistrations->getDatabaseLocation( Name ); +} + +void SAL_CALL ODatabaseContext::registerDatabaseLocation( const OUString& Name, const OUString& Location ) +{ + m_xDatabaseRegistrations->registerDatabaseLocation( Name, Location ); +} + +void SAL_CALL ODatabaseContext::revokeDatabaseLocation( const OUString& Name ) +{ + m_xDatabaseRegistrations->revokeDatabaseLocation( Name ); +} + +void SAL_CALL ODatabaseContext::changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) +{ + m_xDatabaseRegistrations->changeDatabaseLocation( Name, NewLocation ); +} + +sal_Bool SAL_CALL ODatabaseContext::isDatabaseRegistrationReadOnly( const OUString& Name ) +{ + return m_xDatabaseRegistrations->isDatabaseRegistrationReadOnly( Name ); +} + +void SAL_CALL ODatabaseContext::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) +{ + m_xDatabaseRegistrations->addDatabaseRegistrationsListener( Listener ); +} + +void SAL_CALL ODatabaseContext::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) +{ + m_xDatabaseRegistrations->removeDatabaseRegistrationsListener( Listener ); +} + +// css::container::XElementAccess +Type ODatabaseContext::getElementType( ) +{ + return cppu::UnoType<XDataSource>::get(); +} + +sal_Bool ODatabaseContext::hasElements() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return getElementNames().hasElements(); +} + +// css::container::XEnumerationAccess +Reference< css::container::XEnumeration > ODatabaseContext::createEnumeration() +{ + MutexGuard aGuard(m_aMutex); + return new ::comphelper::OEnumerationByName(static_cast<XNameAccess*>(this)); +} + +// css::container::XNameAccess +Any ODatabaseContext::getByName(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + if ( _rName.isEmpty() ) + throw NoSuchElementException(_rName, *this); + + try + { + Reference< XInterface > xExistent = getObject( _rName ); + if ( xExistent.is() ) + return Any( xExistent ); + + // see whether this is a registered name + OUString sURL; + if ( hasRegisteredDatabase( _rName ) ) + { + sURL = getDatabaseLocation( _rName ); + // is the object cached under its URL? + xExistent = getObject( sURL ); + } + else + // interpret the name as URL + sURL = _rName; + + if ( !xExistent.is() ) + // try to load this as URL + xExistent = loadObjectFromURL( _rName, sURL ); + return Any( xExistent ); + } + catch (const NoSuchElementException&) + { // let these exceptions through + throw; + } + catch (const WrappedTargetException&) + { // let these exceptions through + throw; + } + catch (const RuntimeException&) + { // let these exceptions through + throw; + } + catch (const Exception&) + { // exceptions other than the specified ones -> wrap + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException(_rName, *this, aError ); + } +} + +Sequence< OUString > ODatabaseContext::getElementNames() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return getRegistrationNames(); +} + +sal_Bool ODatabaseContext::hasByName(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return hasRegisteredDatabase( _rName ); +} + +Reference< XInterface > ODatabaseContext::getObject( const OUString& _rURL ) +{ + ObjectCache::const_iterator aFind = m_aDatabaseObjects.find( _rURL ); + Reference< XInterface > xExistent; + if ( aFind != m_aDatabaseObjects.end() ) + xExistent = aFind->second->getOrCreateDataSource(); + return xExistent; +} + +void ODatabaseContext::registerDatabaseDocument( ODatabaseModelImpl& _rModelImpl ) +{ + OUString sURL( _rModelImpl.getURL() ); + SAL_INFO("dbaccess.core", "DatabaseContext: registering " << sURL); + if ( m_aDatabaseObjects.find( sURL ) == m_aDatabaseObjects.end() ) + { + m_aDatabaseObjects[ sURL ] = &_rModelImpl; + setTransientProperties( sURL, _rModelImpl ); + } + else + OSL_FAIL( "ODatabaseContext::registerDatabaseDocument: already have an object registered for this URL!" ); +} + +void ODatabaseContext::revokeDatabaseDocument( const ODatabaseModelImpl& _rModelImpl ) +{ + const OUString& sURL( _rModelImpl.getURL() ); + SAL_INFO("dbaccess.core", "DatabaseContext: deregistering " << sURL); + m_aDatabaseObjects.erase( sURL ); +} + +void ODatabaseContext::databaseDocumentURLChange( const OUString& _rOldURL, const OUString& _rNewURL ) +{ + SAL_INFO("dbaccess.core", "DatabaseContext: changing registrations from " << _rOldURL << + " to " << _rNewURL); + ObjectCache::const_iterator oldPos = m_aDatabaseObjects.find( _rOldURL ); + ENSURE_OR_THROW( oldPos != m_aDatabaseObjects.end(), "illegal old database document URL" ); + ObjectCache::const_iterator newPos = m_aDatabaseObjects.find( _rNewURL ); + ENSURE_OR_THROW( newPos == m_aDatabaseObjects.end(), "illegal new database document URL" ); + + m_aDatabaseObjects[ _rNewURL ] = oldPos->second; + m_aDatabaseObjects.erase( oldPos ); +} + +sal_Int64 SAL_CALL ODatabaseContext::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence< sal_Int8 > & ODatabaseContext::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +void ODatabaseContext::onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) _rxForDocument; + (void) _rBasicManager; +#else + // if it's a database document ... + Reference< XOfficeDatabaseDocument > xDatabaseDocument( _rxForDocument, UNO_QUERY ); + // ... or a sub document of a database document ... + if ( !xDatabaseDocument.is() ) + { + Reference< XChild > xDocAsChild( _rxForDocument, UNO_QUERY ); + if ( xDocAsChild.is() ) + xDatabaseDocument.set( xDocAsChild->getParent(), UNO_QUERY ); + } + + // ... whose BasicManager has just been created, then add the global DatabaseDocument variable to its scope. + if ( xDatabaseDocument.is() ) + _rBasicManager.SetGlobalUNOConstant( "ThisDatabaseDocument", Any( xDatabaseDocument ) ); +#endif +} + +} // namespace dbaccess + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseContext_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new dbaccess::ODatabaseContext(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasedocument.cxx b/dbaccess/source/core/dataaccess/databasedocument.cxx new file mode 100644 index 000000000..419d02d2d --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasedocument.cxx @@ -0,0 +1,2211 @@ +/* -*- 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 <core_resource.hxx> +#include <strings.hrc> +#include "databasedocument.hxx" +#include "documenteventexecutor.hxx" +#include <databasecontext.hxx> +#include "documentcontainer.hxx" +#include <sdbcoretools.hxx> +#include <strings.hxx> +#include <recovery/dbdocrecovery.hxx> + +#include <officecfg/Office/Common.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include <com/sun/star/document/XGraphicStorageHandler.hpp> +#include <com/sun/star/document/GraphicStorageHandler.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/ui/UIConfigurationManager.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> + +#include <com/sun/star/script/XStorageBasedLibraryContainer.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/DialogProvider.hpp> + +#include <comphelper/documentconstants.hxx> +#include <comphelper/enumhelper.hxx> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/numberedcollection.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <comphelper/types.hxx> + +#include <connectivity/dbtools.hxx> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <framework/titlehelper.hxx> +#include <unotools/saveopt.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> + +#include <vcl/GraphicObject.hxx> +#include <tools/urlobj.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::script::provider; +using namespace ::com::sun::star::ui; +using namespace ::cppu; + +namespace dbaccess +{ + +// ViewMonitor + +bool ViewMonitor::onControllerConnected( const Reference< XController >& _rxController ) +{ + bool bFirstControllerEver = !m_bEverHadController; + m_bEverHadController = true; + + m_xLastConnectedController = _rxController; + m_bLastIsFirstEverController = bFirstControllerEver; + + return bFirstControllerEver; +} + +bool ViewMonitor::onSetCurrentController( const Reference< XController >& _rxController ) +{ + // we interpret this as "loading the document (including UI) is finished", + // if and only if this is the controller which was last connected, and it was the + // first controller ever connected + bool bLoadFinished = ( _rxController == m_xLastConnectedController ) && m_bLastIsFirstEverController; + + // notify the respective events + if ( bLoadFinished ) + m_rEventNotifier.notifyDocumentEventAsync( m_bIsNewDocument ? "OnNew" : "OnLoad" ); + + return bLoadFinished; +} + + +ODatabaseDocument::ODatabaseDocument(const ::rtl::Reference<ODatabaseModelImpl>& _pImpl ) + :ModelDependentComponent( _pImpl ) + ,ODatabaseDocument_OfficeDocument( getMutex() ) + ,m_aModifyListeners( getMutex() ) + ,m_aCloseListener( getMutex() ) + ,m_aStorageListeners( getMutex() ) + ,m_pEventContainer( new DocumentEvents( *this, getMutex(), _pImpl->getDocumentEvents() ) ) + ,m_aEventNotifier( *this, getMutex() ) + ,m_aViewMonitor( m_aEventNotifier ) + ,m_eInitState( NotInitialized ) + ,m_bClosing( false ) + ,m_bAllowDocumentScripting( false ) + ,m_bHasBeenRecovered( false ) + ,m_bEmbedded(false) +{ + osl_atomic_increment( &m_refCount ); + { + impl_reparent_nothrow( m_xForms ); + impl_reparent_nothrow( m_xReports ); + impl_reparent_nothrow( m_pImpl->m_xTableDefinitions ); + impl_reparent_nothrow( m_pImpl->m_xCommandDefinitions ); + + m_pEventExecutor = new DocumentEventExecutor( m_pImpl->m_aContext, this ); + } + osl_atomic_decrement( &m_refCount ); + + // if there previously was a document instance for the same Impl which was already initialized, + // then consider ourself initialized, too. + // #i94840# + if ( !m_pImpl->hadInitializedDocument() ) + return; + + // Note we set our init-state to "Initializing", not "Initialized". We're created from inside the ModelImpl, + // which is expected to call attachResource in case there was a previous incarnation of the document, + // so we can properly finish our initialization then. + impl_setInitializing(); + + if ( !m_pImpl->getURL().isEmpty() ) + { + // if the previous incarnation of the DatabaseDocument already had a URL, then creating this incarnation + // here is effectively loading the document. + // #i105505# + m_aViewMonitor.onLoadedDocument(); + } +} + +ODatabaseDocument::~ODatabaseDocument() +{ + if ( !ODatabaseDocument_OfficeDocument::rBHelper.bInDispose && !ODatabaseDocument_OfficeDocument::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +Any SAL_CALL ODatabaseDocument::queryInterface( const Type& _rType ) +{ + // strip XEmbeddedScripts and XScriptInvocationContext if we have any form/report + // which already contains macros. In this case, the database document itself is not + // allowed to contain macros, too. + if ( !m_bAllowDocumentScripting + && ( _rType.equals( cppu::UnoType<XEmbeddedScripts>::get() ) + || _rType.equals( cppu::UnoType<XScriptInvocationContext>::get() ) + ) + ) + return Any(); + + Any aReturn = ODatabaseDocument_OfficeDocument::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = ODatabaseDocument_Title::queryInterface(_rType); + return aReturn; +} + +void SAL_CALL ODatabaseDocument::acquire( ) noexcept +{ + ODatabaseDocument_OfficeDocument::acquire(); +} + +void SAL_CALL ODatabaseDocument::release( ) noexcept +{ + ODatabaseDocument_OfficeDocument::release(); +} + +Sequence< Type > SAL_CALL ODatabaseDocument::getTypes( ) +{ + Sequence< Type > aTypes = ::comphelper::concatSequences( + ODatabaseDocument_OfficeDocument::getTypes(), + ODatabaseDocument_Title::getTypes() + ); + + // strip XEmbeddedScripts and XScriptInvocationContext if we have any form/report + // which already contains macros. In this case, the database document itself is not + // allowed to contain macros, too. + if ( !m_bAllowDocumentScripting ) + { + auto [begin, end] = asNonConstRange(aTypes); + auto newEnd = std::remove_if( begin, end, + [](const Type& t) + { return t == cppu::UnoType<XEmbeddedScripts>::get() || + t == cppu::UnoType<XScriptInvocationContext>::get();} ); + aTypes.realloc( std::distance(begin, newEnd) ); + } + + return aTypes; +} + +Sequence< sal_Int8 > SAL_CALL ODatabaseDocument::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +// local functions +namespace +{ + Reference< XStatusIndicator > lcl_extractStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments ) + { + Reference< XStatusIndicator > xStatusIndicator; + return _rArguments.getOrDefault( "StatusIndicator", xStatusIndicator ); + } + + void lcl_triggerStatusIndicator_throw( const ::comphelper::NamedValueCollection& _rArguments, DocumentGuard& _rGuard, const bool _bStart ) + { + Reference< XStatusIndicator > xStatusIndicator( lcl_extractStatusIndicator( _rArguments ) ); + if ( !xStatusIndicator.is() ) + return; + + _rGuard.clear(); + try + { + if ( _bStart ) + xStatusIndicator->start( OUString(), sal_Int32(1000000) ); + else + xStatusIndicator->end(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + _rGuard.reset(); + // note that |reset| can throw a DisposedException + } + + void lcl_extractStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments, Sequence< Any >& _rCallArgs ) + { + Reference< XStatusIndicator > xStatusIndicator( lcl_extractStatusIndicator( _rArguments ) ); + if ( !xStatusIndicator.is() ) + return; + + sal_Int32 nLength = _rCallArgs.getLength(); + _rCallArgs.realloc( nLength + 1 ); + _rCallArgs.getArray()[ nLength ] <<= xStatusIndicator; + } + + void lcl_extractAndStartStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments, Reference< XStatusIndicator >& _rxStatusIndicator, + Sequence< Any >& _rCallArgs ) + { + _rxStatusIndicator = lcl_extractStatusIndicator( _rArguments ); + if ( !_rxStatusIndicator.is() ) + return; + + try + { + _rxStatusIndicator->start( OUString(), sal_Int32(1000000) ); + + sal_Int32 nLength = _rCallArgs.getLength(); + _rCallArgs.realloc( nLength + 1 ); + _rCallArgs.getArray()[ nLength ] <<= _rxStatusIndicator; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + Sequence< PropertyValue > lcl_appendFileNameToDescriptor( const ::comphelper::NamedValueCollection& _rDescriptor, const OUString& _rURL ) + { + ::comphelper::NamedValueCollection aMutableDescriptor( _rDescriptor ); + if ( !_rURL.isEmpty() ) + { + aMutableDescriptor.put( "FileName", _rURL ); + aMutableDescriptor.put( "URL", _rURL ); + } + return aMutableDescriptor.getPropertyValues(); + } +} + +constexpr OUStringLiteral sPictures = u"Pictures"; + +// base documents seem to have a different behaviour to other documents, the +// root storage contents at least seem to be re-used over different saves, thus if there is a +// top level Picture directory it is never cleared. +// If we delete the 'Pictures' directory then the dialog library storage which does store +// any embed images will not work properly. ( this is due to the fact it will +// try to load the dialog which will try and access the embed images, if those images are not cached in +// memory it will try to read them from the Picture directory which is now gone, so... we have to use this +// inglorious hack below which basically will +// +// a) create a temp storage +// +// b) introspect any dialogs for any embed graphics and grab the associate URL(s) +// +// c) populate the temp storage with the associated embed images ( will be stored in a 'Pictures' folder ) +// +// d) delete the 'Picture' element from the root storage +// +// e) copy the Pictures element of the temp storage to the root storage +// +// this assumes that we don't use the Pictures folder in the root of the base +// document for anything, I believe this is a valid assumption ( as much as +// I could check anyway ) + +/// @throws RuntimeException +static void lcl_uglyHackToStoreDialogeEmbedImages( const Reference< XStorageBasedLibraryContainer >& xDlgCont, const Reference< XStorage >& xStorage, const Reference< XModel >& rxModel, const Reference<XComponentContext >& rxContext ) +{ + const Sequence< OUString > sLibraries = xDlgCont->getElementNames(); + Reference< XStorage > xTmpPic = xStorage->openStorageElement( "tempPictures", ElementModes::READWRITE ); + + std::vector<uno::Reference<graphic::XGraphic>> vxGraphicList; + for ( OUString const & sLibrary : sLibraries ) + { + xDlgCont->loadLibrary( sLibrary ); + Reference< XNameContainer > xLib; + xDlgCont->getByName( sLibrary ) >>= xLib; + if ( xLib.is() ) + { + Sequence< OUString > sDialogs = xLib->getElementNames(); + sal_Int32 nDialogs( sDialogs.getLength() ); + for ( sal_Int32 j=0; j < nDialogs; ++j ) + { + Reference < awt::XDialogProvider > xDlgPrv = awt::DialogProvider::createWithModel(rxContext, rxModel); + OUString sDialogUrl = + "vnd.sun.star.script:" + sLibrary + "." + sDialogs[j] + "?location=document"; + + Reference< css::awt::XControl > xDialog( xDlgPrv->createDialog( sDialogUrl ), UNO_QUERY ); + Reference< XInterface > xModel( xDialog->getModel() ); + vcl::graphic::SearchForGraphics(xModel, vxGraphicList); + } + } + } + // if we have any image urls, make sure we copy the associated images into tempPictures + if (!vxGraphicList.empty()) + { + // Export the images to the storage + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + xGraphicStorageHandler.set(GraphicStorageHandler::createWithStorage(rxContext, xTmpPic)); + if (xGraphicStorageHandler.is()) + { + for (uno::Reference<graphic::XGraphic> const & rxGraphic : vxGraphicList) + { + xGraphicStorageHandler->saveGraphic(rxGraphic); + } + } + // delete old 'Pictures' storage and copy the contents of tempPictures into xStorage + xStorage->removeElement( sPictures ); + xTmpPic->copyElementTo( sPictures, xStorage, sPictures ); + } + else + { + // clean up an existing Pictures dir + if ( xStorage->isStorageElement( sPictures ) ) + xStorage->removeElement( sPictures ); + } +} + +void ODatabaseDocument::impl_setInitialized() +{ + m_eInitState = Initialized; + + // start event notifications + m_aEventNotifier.onDocumentInitialized(); +} + +void ODatabaseDocument::impl_reset_nothrow() +{ + try + { + m_pImpl->clearConnections(); + m_pImpl->disposeStorages(); + m_pImpl->resetRootStorage(); + + clearObjectContainer( m_xForms ); + clearObjectContainer( m_xReports ); + clearObjectContainer( m_pImpl->m_xTableDefinitions ); + clearObjectContainer( m_pImpl->m_xCommandDefinitions ); + + m_eInitState = NotInitialized; + + m_pImpl->reset(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_pImpl->m_bDocumentReadOnly = false; +} + +namespace +{ + /** property map for import/export info set */ + comphelper::PropertyMapEntry const aExportInfoMap[] = + { + { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("UsePrettyPrinting"), 0, ::cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("TargetStorage"), 0, cppu::UnoType<embed::XStorage>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("StreamRelPath"), 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + }; +} + +void ODatabaseDocument::impl_import_nolck_throw( const Reference< XComponentContext >& _rContext, const Reference< XInterface >& _rxTargetComponent, + const ::comphelper::NamedValueCollection& _rResource ) +{ + Sequence< Any > aFilterCreationArgs; + Reference< XStatusIndicator > xStatusIndicator; + lcl_extractAndStartStatusIndicator( _rResource, xStatusIndicator, aFilterCreationArgs ); + + uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) ); + OUString sBaseURI = _rResource.getOrDefault("BaseURI", OUString()); + if (sBaseURI.isEmpty()) + sBaseURI = _rResource.getOrDefault("URL",OUString()); + assert(!sBaseURI.isEmpty()); // needed for relative URLs + xInfoSet->setPropertyValue("BaseURI", uno::Any(sBaseURI)); + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + + const sal_Int32 nCount = aFilterCreationArgs.getLength(); + aFilterCreationArgs.realloc(nCount + 1); + aFilterCreationArgs.getArray()[nCount] <<= xInfoSet; + + Reference< XImporter > xImporter( + _rContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.comp.sdb.DBFilter", aFilterCreationArgs, _rContext), + UNO_QUERY_THROW ); + + Reference< XComponent > xComponent( _rxTargetComponent, UNO_QUERY_THROW ); + xImporter->setTargetDocument( xComponent ); + + Reference< XFilter > xFilter( xImporter, UNO_QUERY_THROW ); + Sequence< PropertyValue > aFilterArgs( ODatabaseModelImpl::stripLoadArguments( _rResource ).getPropertyValues() ); + xFilter->filter( aFilterArgs ); + + if ( xStatusIndicator.is() ) + xStatusIndicator->end(); +} + +void SAL_CALL ODatabaseDocument::initNew( ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + impl_reset_nothrow(); + + impl_setInitializing(); + + // create a temporary storage + Reference< XStorage > xTempStor( ::comphelper::OStorageHelper::GetTemporaryStorage( m_pImpl->m_aContext ) ); + + // store therein + impl_storeToStorage_throw( xTempStor, Sequence< PropertyValue >(), aGuard ); + + // let the impl know we're now based on this storage + m_pImpl->switchToStorage( xTempStor ); + + // for the newly created document, allow document-wide scripting + m_bAllowDocumentScripting = true; + + impl_setInitialized(); + + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + + impl_setModified_nothrow( false, aGuard ); + // <- SYNCHRONIZED + + m_aEventNotifier.notifyDocumentEvent( "OnCreate" ); + + impl_notifyStorageChange_nolck_nothrow( xTempStor ); +} + +void SAL_CALL ODatabaseDocument::load( const Sequence< PropertyValue >& Arguments ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + impl_reset_nothrow(); + + ::comphelper::NamedValueCollection aResource( Arguments ); + if ( aResource.has( "FileName" ) && !aResource.has( "URL" ) ) + // FileName is the compatibility name for URL, so we might have clients passing + // a FileName only. However, some of our code works with the URL only, so ensure + // we have one. + aResource.put( "URL", aResource.get( "FileName" ) ); + if ( aResource.has( "URL" ) && !aResource.has( "FileName" ) ) + // similar ... just in case there is legacy code which expects a FileName only + aResource.put( "FileName", aResource.get( "URL" ) ); + + // now that somebody (perhaps) told us a macro execution mode, remember it as + // ImposedMacroExecMode + m_pImpl->setImposedMacroExecMode( + aResource.getOrDefault( "MacroExecutionMode", m_pImpl->getImposedMacroExecMode() ) ); + + impl_setInitializing(); + try + { + aGuard.clear(); + impl_import_nolck_throw( m_pImpl->m_aContext, *this, aResource ); + aGuard.reset(); + } + catch( const Exception& ) + { + impl_reset_nothrow(); + throw; + } + // tell our view monitor that the document has been loaded - this way it will fire the proper + // event (OnLoad instead of OnCreate) later on + m_aViewMonitor.onLoadedDocument(); + + // note that we do *not* call impl_setInitialized() here: The initialization is only complete + // when the XModel::attachResource has been called, not sooner. + // however, in case of embedding, XModel::attachResource is already called. + if (m_bEmbedded) + impl_setInitialized(); + + impl_setModified_nothrow( false, aGuard ); + // <- SYNCHRONIZED +} + +namespace +{ + bool lcl_hasAnyModifiedSubComponent_throw( const Reference< XController >& i_rController ) + { + Reference< css::sdb::application::XDatabaseDocumentUI > xDatabaseUI( i_rController, UNO_QUERY_THROW ); + + const Sequence< Reference< XComponent > > aComponents( xDatabaseUI->getSubComponents() ); + + bool isAnyModified = false; + for ( auto const & xComponent : aComponents ) + { + Reference< XModifiable > xModify( xComponent, UNO_QUERY ); + if ( xModify.is() ) + { + isAnyModified = xModify->isModified(); + continue; + } + + // TODO: clarify: anything else to care for? Both the sub components with and without model + // should support the XModifiable interface, so I think nothing more is needed here. + OSL_FAIL( "lcl_hasAnyModifiedSubComponent_throw: anything left to do here?" ); + } + + return isAnyModified; + } +} + +sal_Bool SAL_CALL ODatabaseDocument::wasModifiedSinceLastSave() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + // The implementation here is somewhat sloppy, in that it returns whether *any* part of the whole + // database document, including opened sub components, is modified. This is more than what is requested: + // We need to return <TRUE/> if the doc itself, or any of the opened sub components, has been modified + // since the last call to any of the save* methods, or since the document has been loaded/created. + // However, the API definition explicitly allows to be that sloppy ... + + if ( isModified() ) + return true; + + // auto recovery is an "UI feature", it is to restore the UI the user knows. Thus, + // we ask our connected controllers, not simply our existing form/report definitions. + // (There is some information which even cannot be obtained without asking the controller. + // For instance, newly created, but not yet saved, forms/reports are accessible via the + // controller only, but not via the model.) + + try + { + for (auto const& controller : m_aControllers) + { + if ( lcl_hasAnyModifiedSubComponent_throw(controller) ) + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return false; +} + +void SAL_CALL ODatabaseDocument::storeToRecoveryFile( const OUString& i_TargetLocation, const Sequence< PropertyValue >& i_MediaDescriptor ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + ModifyLock aLock( *this ); + + try + { + // create a storage for the target location + Reference< XStorage > xTargetStorage( impl_createStorageFor_throw( i_TargetLocation ) ); + + // first store the document as a whole into this storage + impl_storeToStorage_throw( xTargetStorage, i_MediaDescriptor, aGuard ); + + // save the sub components which need saving + DatabaseDocumentRecovery aDocRecovery( m_pImpl->m_aContext); + aDocRecovery.saveModifiedSubComponents( xTargetStorage, m_aControllers ); + + // commit the root storage + tools::stor::commitStorageIfWriteable( xTargetStorage ); + } + catch( const IOException& ) + { + throw; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const WrappedTargetException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException( OUString(), *this, aError ); + } +} + +void SAL_CALL ODatabaseDocument::recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const Sequence< PropertyValue >& i_MediaDescriptor ) +{ + try + { + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + if ( i_SourceLocation.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + + // load the document itself, by simply delegating to our "load" method + + // our load implementation expects the SalvagedFile and URL to be in the media descriptor + ::comphelper::NamedValueCollection aMediaDescriptor( i_MediaDescriptor ); + aMediaDescriptor.put( "SalvagedFile", i_SalvagedFile ); + aMediaDescriptor.put( "URL", i_SourceLocation ); + + aGuard.clear(); // (load has an own guarding scheme) + load( aMediaDescriptor.getPropertyValues() ); + aGuard.reset(); + + // Without a controller, we are unable to recover the sub components, as they're always tied to a controller. + // So, everything else is done when the first controller is connected. + m_bHasBeenRecovered = true; + + // tell the impl that we've been loaded from the given location + m_pImpl->setDocFileLocation( i_SourceLocation ); + + // by definition (of XDocumentRecovery), we're responsible for delivering a fully-initialized document, + // which includes an attachResource call. + const OUString sLogicalDocumentURL( i_SalvagedFile.isEmpty() ? i_SourceLocation : i_SalvagedFile ); + impl_attachResource( sLogicalDocumentURL, aMediaDescriptor.getPropertyValues(), aGuard ); + // <- SYNCHRONIZED + } + catch( const IOException& ) + { + throw; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const WrappedTargetException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException( OUString(), *this, aError ); + } +} + +// XModel +sal_Bool SAL_CALL ODatabaseDocument::attachResource( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + if (_rURL.isEmpty() && _rArguments.getLength() == 1 && _rArguments[0].Name == "SetEmbedded") + { + m_bEmbedded = true; + return true; + } + + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + bool bRet = false; + try + { + bRet = impl_attachResource( _rURL, _rArguments, aGuard ); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetRuntimeException( OUString(), *this, aError ); + } + return bRet; +} + +bool ODatabaseDocument::impl_attachResource( const OUString& i_rLogicalDocumentURL, + const Sequence< PropertyValue >& i_rMediaDescriptor, DocumentGuard& _rDocGuard ) +{ + if (i_rLogicalDocumentURL == getURL()) + { + ::comphelper::NamedValueCollection aArgs(i_rMediaDescriptor); + + // this misuse of attachresource is a hack of the Basic importer code + // repurposing existing interfaces for uses it probably wasn't intended + // for + + // we do not support macro signatures, so we can ignore that request + aArgs.remove("BreakMacroSignature"); + + bool bMacroEventRead = false; + if ((aArgs.get( "MacroEventRead" ) >>= bMacroEventRead) && bMacroEventRead) + m_pImpl->m_bMacroCallsSeenWhileLoading = true; + aArgs.remove( "MacroEventRead" ); + + if (aArgs.empty()) + return false; + } + + // if no URL has been provided, the caller was lazy enough to not call our getURL - which is not allowed anymore, + // now since getURL and getLocation both return the same, so calling one of those should be simple. + OUString sDocumentURL( i_rLogicalDocumentURL ); + OSL_ENSURE( !sDocumentURL.isEmpty(), "ODatabaseDocument::impl_attachResource: invalid URL!" ); + if ( sDocumentURL.isEmpty() ) + sDocumentURL = getURL(); + + m_pImpl->setResource( sDocumentURL, i_rMediaDescriptor ); + + if ( impl_isInitializing() ) + { // this means we've just been loaded, and this is the attachResource call which follows + // the load call. + impl_setInitialized(); + + // determine whether the document as a whole, or sub documents, have macros. Especially the latter + // controls the availability of our XEmbeddedScripts and XScriptInvocationContext interfaces, and we + // should know this before anybody actually uses the object. + m_bAllowDocumentScripting = ( m_pImpl->determineEmbeddedMacros() != ODatabaseModelImpl::eSubDocumentMacros ); + + _rDocGuard.clear(); + // <- SYNCHRONIZED + m_aEventNotifier.notifyDocumentEvent( "OnLoadFinished" ); + } + + return true; +} + +OUString SAL_CALL ODatabaseDocument::getURL( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getURL(); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getArgs( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getMediaDescriptor().getPropertyValues(); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getArgs2( const ::css::uno::Sequence< ::rtl::OUString >& requestedArgs ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + std::vector<PropertyValue> aRet; + for (const auto & rArgName : requestedArgs) + aRet.push_back(PropertyValue(rArgName, 0, m_pImpl->getMediaDescriptor().get(rArgName), PropertyState_DIRECT_VALUE)); + return comphelper::containerToSequence(aRet); +} + +void SAL_CALL ODatabaseDocument::setArgs(const Sequence<beans::PropertyValue>& /* aArgs */) +{ + throw NoSupportException(); +} + +void SAL_CALL ODatabaseDocument::connectController( const Reference< XController >& _xController ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + +#if OSL_DEBUG_LEVEL > 0 + for (auto const& controller : m_aControllers) + { + OSL_ENSURE( controller != _xController, "ODatabaseDocument::connectController: this controller is already connected!" ); + } +#endif + + m_aControllers.push_back( _xController ); + + m_aEventNotifier.notifyDocumentEventAsync( "OnViewCreated", Reference< XController2 >( _xController, UNO_QUERY ) ); + + bool bFirstControllerEver = m_aViewMonitor.onControllerConnected( _xController ); + if ( !bFirstControllerEver ) + return; + + // check/adjust our macro mode. + m_pImpl->checkMacrosOnLoading(); +} + +void SAL_CALL ODatabaseDocument::disconnectController( const Reference< XController >& _xController ) +{ + bool bNotifyViewClosed = false; + bool bLastControllerGone = false; + bool bIsClosing = false; + + // SYNCHRONIZED -> + { + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Controllers::iterator pos = std::find( m_aControllers.begin(), m_aControllers.end(), _xController ); + OSL_ENSURE( pos != m_aControllers.end(), "ODatabaseDocument::disconnectController: don't know this controller!" ); + if ( pos != m_aControllers.end() ) + { + m_aControllers.erase( pos ); + bNotifyViewClosed = true; + } + + if ( m_xCurrentController == _xController ) + m_xCurrentController = nullptr; + + bLastControllerGone = m_aControllers.empty(); + bIsClosing = m_bClosing; + } + // <- SYNCHRONIZED + + if ( bNotifyViewClosed ) + m_aEventNotifier.notifyDocumentEvent( "OnViewClosed", Reference< XController2 >( _xController, UNO_QUERY ) ); + + if ( !bLastControllerGone || bIsClosing ) + return; + + // if this was the last view, close the document as a whole + // #i51157# + try + { + close( true ); + } + catch( const CloseVetoException& ) + { + // okay, somebody vetoed and took ownership + } +} + +void SAL_CALL ODatabaseDocument::lockControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + ++m_pImpl->m_nControllerLockCount; +} + +void SAL_CALL ODatabaseDocument::unlockControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + --m_pImpl->m_nControllerLockCount; +} + +sal_Bool SAL_CALL ODatabaseDocument::hasControllersLocked( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_pImpl->m_nControllerLockCount != 0; +} + +Reference< XController > SAL_CALL ODatabaseDocument::getCurrentController() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_xCurrentController.is() ? m_xCurrentController : ( m_aControllers.empty() ? Reference< XController >() : *m_aControllers.begin() ); +} + +void SAL_CALL ODatabaseDocument::setCurrentController( const Reference< XController >& _xController ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + m_xCurrentController = _xController; + + if ( !m_aViewMonitor.onSetCurrentController( _xController ) ) + return; + + // check if there are sub components to recover from our document storage + bool bAttemptRecovery = m_bHasBeenRecovered; + if ( !bAttemptRecovery && m_pImpl->getMediaDescriptor().has( "ForceRecovery" ) ) + // do not use getOrDefault, it will throw for invalid types, which is not desired here + m_pImpl->getMediaDescriptor().get( "ForceRecovery" ) >>= bAttemptRecovery; + + if ( !bAttemptRecovery ) + return; + + try + { + DatabaseDocumentRecovery aDocRecovery( m_pImpl->m_aContext ); + aDocRecovery.recoverSubDocuments( m_pImpl->getRootStorage(), _xController ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< XInterface > SAL_CALL ODatabaseDocument::getCurrentSelection( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XInterface > xRet; + Reference< XSelectionSupplier > xDocView( getCurrentController(), UNO_QUERY ); + if ( xDocView.is() ) + xRet.set(xDocView->getSelection(),UNO_QUERY); + + return xRet; +} + +// XStorable +sal_Bool SAL_CALL ODatabaseDocument::hasLocation( ) +{ + return !getLocation().isEmpty(); +} + +OUString SAL_CALL ODatabaseDocument::getLocation( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getURL(); + // both XStorable::getLocation and XModel::getURL have to return the URL of the document, *not* + // the location of the file which the document was possibly recovered from (which would be getDocFileLocation) +} + +sal_Bool SAL_CALL ODatabaseDocument::isReadonly( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->m_bDocumentReadOnly; +} + +void SAL_CALL ODatabaseDocument::store( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + OUString sDocumentURL( m_pImpl->getURL() ); + if ( !sDocumentURL.isEmpty() ) + { + if ( m_pImpl->getDocFileLocation() == m_pImpl->getURL() ) + if ( m_pImpl->m_bDocumentReadOnly ) + throw IOException(); + + impl_storeAs_throw( m_pImpl->getURL(), m_pImpl->getMediaDescriptor(), SAVE, aGuard ); + return; + } + + // if we have no URL, but did survive the DocumentGuard above, then we've been inited via XLoadable::initNew, + // i.e. we're based on a temporary storage + OSL_ENSURE( m_pImpl->getDocFileLocation().isEmpty(), "ODatabaseDocument::store: unexpected URL inconsistency!" ); + + try + { + impl_storeToStorage_throw( m_pImpl->getRootStorage(), m_pImpl->getMediaDescriptor().getPropertyValues(), aGuard ); + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + if ( aError.isExtractableTo( ::cppu::UnoType< IOException >::get() ) + || aError.isExtractableTo( ::cppu::UnoType< RuntimeException >::get() ) + ) + { + // allowed to leave + throw; + } + impl_throwIOExceptionCausedBySave_throw( aError, {} ); + } +} + +void ODatabaseDocument::impl_throwIOExceptionCausedBySave_throw( const Any& i_rError, std::u16string_view i_rTargetURL ) const +{ + OUString sErrorMessage = extractExceptionMessage( m_pImpl->m_aContext, i_rError ); + sErrorMessage = ResourceManager::loadString( + RID_STR_ERROR_WHILE_SAVING, + "$location$", i_rTargetURL, + "$message$", sErrorMessage + ); + throw IOException( sErrorMessage, *const_cast< ODatabaseDocument* >( this ) ); +} + +void ODatabaseDocument::impl_storeAs_throw( const OUString& _rURL, const ::comphelper::NamedValueCollection& _rArguments, + const StoreType _eType, DocumentGuard& _rGuard ) +{ + OSL_PRECOND( ( _eType == SAVE ) || ( _eType == SAVE_AS ), + "ODatabaseDocument::impl_storeAs_throw: you introduced a new type which cannot be handled here!" ); + + // if we're in the process of initializing the document (which effectively means it is an implicit + // initialization triggered in storeAsURL), the we do not notify events, since to an observer, the SaveAs + // should not be noticeable + bool bIsInitializationProcess = impl_isInitializing(); + + if ( !bIsInitializationProcess ) + { + _rGuard.clear(); + m_aEventNotifier.notifyDocumentEvent( _eType == SAVE ? "OnSave" : "OnSaveAs", nullptr, Any( _rURL ) ); + _rGuard.reset(); + } + + Reference< XStorage > xNewRootStorage; + // will be non-NULL if our storage changed + + try + { + ModifyLock aLock( *this ); + // ignore all changes of our "modified" state during storing + + bool bLocationChanged = ( _rURL != m_pImpl->getDocFileLocation() ); + if ( bLocationChanged ) + { + // create storage for target URL + uno::Reference<embed::XStorage> xTargetStorage( + impl_GetStorageOrCreateFor_throw(_rArguments, _rURL)); + + if ( m_pImpl->isEmbeddedDatabase() ) + m_pImpl->clearConnections(); + + // commit everything + m_pImpl->commitEmbeddedStorage(); + m_pImpl->commitStorages(); + + // copy own storage to target storage + Reference< XStorage > xCurrentStorage( m_pImpl->getRootStorage() ); + if ( xCurrentStorage.is() ) + xCurrentStorage->copyToStorage( xTargetStorage ); + + m_pImpl->disposeStorages(); + + // each and every document definition obtained via m_xForms and m_xReports depends + // on the sub storages which we just disposed. So, dispose the forms/reports collections, too. + // This ensures that they're re-created when needed. + clearObjectContainer( m_xForms ); + clearObjectContainer( m_xReports ); + + xNewRootStorage = m_pImpl->switchToStorage( xTargetStorage ); + + m_pImpl->m_bDocumentReadOnly = false; + } + + // store to current storage + Reference< XStorage > xCurrentStorage( m_pImpl->getOrCreateRootStorage(), UNO_SET_THROW ); + Sequence< PropertyValue > aMediaDescriptor( lcl_appendFileNameToDescriptor( _rArguments, _rURL ) ); + impl_storeToStorage_throw( xCurrentStorage, aMediaDescriptor, _rGuard ); + + // success - tell our impl + m_pImpl->setDocFileLocation( _rURL ); + m_pImpl->setResource( _rURL, aMediaDescriptor ); + + // if we are in an initialization process, then this is finished, now that we stored the document + if ( bIsInitializationProcess ) + impl_setInitialized(); + } + catch( const IOException& ) + { + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + throw; + } + catch( const RuntimeException& ) + { + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + + // notify the failure + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + + impl_throwIOExceptionCausedBySave_throw( aError, _rURL ); + } + + // notify the document event + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveDone" : "OnSaveAsDone", nullptr, Any( _rURL ) ); + + // reset our "modified" flag, and clear the guard + impl_setModified_nothrow( false, _rGuard ); + // <- SYNCHRONIZED + + // notify storage listeners + if ( xNewRootStorage.is() ) + impl_notifyStorageChange_nolck_nothrow( xNewRootStorage ); +} + +Reference< XStorage > ODatabaseDocument::impl_createStorageFor_throw( const OUString& _rURL ) const +{ + Reference< ucb::XSimpleFileAccess3 > xTempAccess(ucb::SimpleFileAccess::create(m_pImpl->m_aContext)); + Reference< io::XStream > xStream = xTempAccess->openFileReadWrite( _rURL ); + Reference< io::XTruncate > xTruncate(xStream,UNO_QUERY); + if ( xTruncate.is() ) + { + xTruncate->truncate(); + } + Sequence<Any> aParam{ Any(xStream), Any(ElementModes::READWRITE | ElementModes::TRUNCATE) }; + + Reference< XSingleServiceFactory > xStorageFactory( m_pImpl->createStorageFactory(), UNO_SET_THROW ); + return Reference< XStorage >( xStorageFactory->createInstanceWithArguments( aParam ), UNO_QUERY_THROW ); +} + +css::uno::Reference<css::embed::XStorage> ODatabaseDocument::impl_GetStorageOrCreateFor_throw( + const ::comphelper::NamedValueCollection& _rArguments, const OUString& _rURL) const +{ + // Try to get the storage from arguments, then create storage for target URL + uno::Reference<embed::XStorage> xTargetStorage; + _rArguments.get("TargetStorage") >>= xTargetStorage; + if (!xTargetStorage.is()) + xTargetStorage = impl_createStorageFor_throw(_rURL); + + // In case we got a StreamRelPath, then xTargetStorage should reference that sub-storage. + OUString sStreamRelPath = _rArguments.getOrDefault("StreamRelPath", OUString()); + if (!sStreamRelPath.isEmpty()) + xTargetStorage + = xTargetStorage->openStorageElement(sStreamRelPath, embed::ElementModes::READWRITE); + + return xTargetStorage; +} + +void SAL_CALL ODatabaseDocument::storeAsURL( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + + // Normally, a document initialization is done via XLoadable::load or XLoadable::initNew. For convenience + // reasons, and to not break existing API clients, it's allowed to call storeAsURL without having initialized + // the document, in which case the initialization will be done implicitly. + bool bImplicitInitialization = !impl_isInitialized(); + // implicit initialization while another initialization is just running is not possible + if ( bImplicitInitialization && impl_isInitializing() ) + throw RuntimeException(); + + if ( bImplicitInitialization ) + impl_setInitializing(); + + try + { + impl_storeAs_throw( _rURL, _rArguments, SAVE_AS, aGuard ); + // <- SYNCHRONIZED + + // impl_storeAs_throw cleared the lock on our mutex, but the below lines need this lock + // SYNCHRONIZED -> + aGuard.reset(); + + // our title might have changed, potentially at least + // Sadly, we cannot check this: Calling getTitle here and now would not deliver + // an up-to-date result, as the call is delegated to our TitleHelper instance, which itself + // updates its title only if it gets the OnSaveAsDone event (which was sent asynchronously + // by impl_storeAs_throw). So, we simply notify always, and also asynchronously + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + } + catch( const Exception& ) + { + impl_reset_nothrow(); + throw; + } + + if ( bImplicitInitialization ) + m_bAllowDocumentScripting = true; + + aGuard.clear(); + // <- SYNCHRONIZED + + if ( bImplicitInitialization ) + m_aEventNotifier.notifyDocumentEvent( "OnCreate" ); +} + +void ODatabaseDocument::impl_storeToStorage_throw( const Reference< XStorage >& _rxTargetStorage, const Sequence< PropertyValue >& _rMediaDescriptor, + DocumentGuard& _rDocGuard ) const +{ + if ( !_rxTargetStorage.is() ) + throw IllegalArgumentException( OUString(), *const_cast< ODatabaseDocument* >( this ), 1 ); + + if ( !m_pImpl.is() ) + throw DisposedException( OUString(), *const_cast< ODatabaseDocument* >( this ) ); + + try + { + // commit everything + m_pImpl->commitEmbeddedStorage(); + m_pImpl->commitStorages(); + + // copy own storage to target storage + if ( impl_isInitialized() ) + { + Reference< XStorage > xCurrentStorage = m_pImpl->getOrCreateRootStorage(); + // Root storage may be empty in case of embedding. + if ( xCurrentStorage.is() && xCurrentStorage != _rxTargetStorage ) + xCurrentStorage->copyToStorage( _rxTargetStorage ); + } + + // write into target storage + ::comphelper::NamedValueCollection aWriteArgs( _rMediaDescriptor ); + lcl_triggerStatusIndicator_throw( aWriteArgs, _rDocGuard, true ); + impl_writeStorage_throw( _rxTargetStorage, aWriteArgs ); + lcl_triggerStatusIndicator_throw( aWriteArgs, _rDocGuard, false ); + + // commit target storage + m_pImpl->commitStorageIfWriteable_ignoreErrors(_rxTargetStorage); + } + catch( const IOException& ) { throw; } + catch( const RuntimeException& ) { throw; } + catch ( const Exception& e ) + { + throw IOException( e.Message, *const_cast< ODatabaseDocument* >( this ) ); + } +} + +void SAL_CALL ODatabaseDocument::storeToURL( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + ModifyLock aLock( *this ); + + { + aGuard.clear(); + m_aEventNotifier.notifyDocumentEvent( "OnSaveTo", nullptr, Any( _rURL ) ); + aGuard.reset(); + } + + try + { + const ::comphelper::NamedValueCollection aArguments(_rArguments); + // create storage for target URL + Reference<XStorage> xTargetStorage(impl_GetStorageOrCreateFor_throw(aArguments, _rURL)); + + // extend media descriptor with URL + Sequence<PropertyValue> aMediaDescriptor(lcl_appendFileNameToDescriptor(aArguments, _rURL)); + + // store to this storage + impl_storeToStorage_throw( xTargetStorage, aMediaDescriptor, aGuard ); + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + m_aEventNotifier.notifyDocumentEventAsync( "OnSaveToFailed", nullptr, aError ); + + if ( aError.isExtractableTo( ::cppu::UnoType< IOException >::get() ) + || aError.isExtractableTo( ::cppu::UnoType< RuntimeException >::get() ) + ) + { + // allowed to leave + throw; + } + + impl_throwIOExceptionCausedBySave_throw( aError, _rURL ); + } + + m_aEventNotifier.notifyDocumentEventAsync( "OnSaveToDone", nullptr, Any( _rURL ) ); +} + +// XModifyBroadcaster +void SAL_CALL ODatabaseDocument::addModifyListener( const Reference< XModifyListener >& _xListener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aModifyListeners.addInterface(_xListener); +} + +void SAL_CALL ODatabaseDocument::removeModifyListener( const Reference< XModifyListener >& _xListener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aModifyListeners.removeInterface(_xListener); +} + +// XModifiable +sal_Bool SAL_CALL ODatabaseDocument::isModified( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_pImpl->m_bModified; +} + +void SAL_CALL ODatabaseDocument::setModified( sal_Bool _bModified ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + if ( impl_isInitialized() ) + impl_setModified_nothrow( _bModified, aGuard ); + // it's allowed to call setModified without the document being initialized already. In this case, + // we simply ignore the call - when the initialization is finished, the respective code will set + // a proper "modified" flag +} + +void ODatabaseDocument::impl_setModified_nothrow( bool _bModified, DocumentGuard& _rGuard ) +{ + // SYNCHRONIZED -> + bool bModifiedChanged = ( m_pImpl->m_bModified != _bModified ) && ( !m_pImpl->isModifyLocked() ); + + if ( bModifiedChanged ) + { + m_pImpl->m_bModified = _bModified; + m_aEventNotifier.notifyDocumentEventAsync( "OnModifyChanged" ); + } + _rGuard.clear(); + // <- SYNCHRONIZED + + if ( bModifiedChanged ) + { + lang::EventObject aEvent( *this ); + m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent ); + } +} + +// css::document::XEventBroadcaster +void SAL_CALL ODatabaseDocument::addEventListener(const uno::Reference< document::XEventListener >& Listener ) +{ + m_aEventNotifier.addLegacyEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeEventListener( const uno::Reference< document::XEventListener >& Listener ) +{ + m_aEventNotifier.removeLegacyEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) +{ + m_aEventNotifier.addDocumentEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) +{ + m_aEventNotifier.removeDocumentEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::notifyDocumentEvent( const OUString& EventName, const Reference< XController2 >& ViewController, const Any& Supplement ) +{ + if ( EventName.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + if ( !DocumentEvents::needsSynchronousNotification( EventName ) ) + { + m_aEventNotifier.notifyDocumentEventAsync( EventName, ViewController, Supplement ); + return; + } + aGuard.clear(); + // <- SYNCHRONIZED + + m_aEventNotifier.notifyDocumentEvent( EventName, ViewController, Supplement ); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getPrinter( ) +{ + OSL_FAIL( "ODatabaseDocument::getPrinter: not supported!" ); + return Sequence< PropertyValue >(); +} + +void SAL_CALL ODatabaseDocument::setPrinter( const Sequence< PropertyValue >& /*aPrinter*/ ) +{ + OSL_FAIL( "ODatabaseDocument::setPrinter: not supported!" ); +} + +void SAL_CALL ODatabaseDocument::print( const Sequence< PropertyValue >& /*xOptions*/ ) +{ + OSL_FAIL( "ODatabaseDocument::print: not supported!" ); +} + +void ODatabaseDocument::impl_reparent_nothrow( const WeakReference< XNameAccess >& _rxContainer ) +{ + Reference< XChild > xChild( _rxContainer.get(), UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( *this ); +} + +void ODatabaseDocument::clearObjectContainer( WeakReference< XNameAccess >& _rxContainer) +{ + Reference< XNameAccess > xContainer = _rxContainer; + ::comphelper::disposeComponent( xContainer ); + + Reference< XChild > xChild( _rxContainer.get(),UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( nullptr ); + _rxContainer.clear(); +} + +Reference< XNameAccess > ODatabaseDocument::impl_getDocumentContainer_throw( ODatabaseModelImpl::ObjectType _eType ) +{ + if ( ( _eType != ODatabaseModelImpl::E_FORM ) && ( _eType != ODatabaseModelImpl::E_REPORT ) ) + throw IllegalArgumentException(); + + bool bFormsContainer = _eType == ODatabaseModelImpl::E_FORM; + + WeakReference< XNameAccess >& rContainerRef( bFormsContainer ? m_xForms : m_xReports ); + Reference< XNameAccess > xContainer = rContainerRef; + if ( !xContainer.is() ) + { + Any aValue; + css::uno::Reference< css::uno::XInterface > xMy(*this); + if ( dbtools::getDataSourceSetting(xMy,bFormsContainer ? "Forms" : "Reports",aValue) ) + { + OUString sSupportService; + aValue >>= sSupportService; + if ( !sSupportService.isEmpty() ) + { + Sequence<Any> aArgs{ Any(NamedValue("DatabaseDocument",Any(xMy))) }; + xContainer.set( + m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(sSupportService, aArgs, m_pImpl->m_aContext), + UNO_QUERY); + rContainerRef = xContainer; + } + } + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( _eType ) ); + rContainerRef = xContainer = new ODocumentContainer( m_pImpl->m_aContext, *this, rContainerData, bFormsContainer ); + } + impl_reparent_nothrow( xContainer ); + } + return xContainer; +} + +void ODatabaseDocument::impl_closeControllerFrames_nolck_throw( bool _bDeliverOwnership ) +{ + Controllers aCopy = m_aControllers; + + for (auto const& elem : aCopy) + { + if ( !elem.is() ) + continue; + + try + { + Reference< XCloseable> xFrame( elem->getFrame(), UNO_QUERY ); + if ( xFrame.is() ) + xFrame->close( _bDeliverOwnership ); + } + catch( const CloseVetoException& ) { throw; } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void ODatabaseDocument::impl_disposeControllerFrames_nothrow() +{ + Controllers aCopy; + aCopy.swap( m_aControllers ); // ensure m_aControllers is empty afterwards + for( const auto& rController : aCopy ) + { + try + { + if( rController.is() ) + { + Reference< XFrame > xFrame( rController->getFrame() ); + ::comphelper::disposeComponent( xFrame ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void SAL_CALL ODatabaseDocument::close(sal_Bool bDeliverOwnership) +{ + // nearly everything below can/must be done without our mutex locked, the below is just for + // the checks for being disposed and the like + // SYNCHRONIZED -> + { + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + assert (!m_bClosing); + m_bClosing = true; + } + // <- SYNCHRONIZED + + try + { + // allow listeners to veto + lang::EventObject aEvent( *this ); + m_aCloseListener.forEach( + [&aEvent, &bDeliverOwnership] (uno::Reference<XCloseListener> const& xListener) { + return xListener->queryClosing(aEvent, bDeliverOwnership); + }); + + // notify that we're going to unload + m_aEventNotifier.notifyDocumentEvent( "OnPrepareUnload" ); + + impl_closeControllerFrames_nolck_throw( bDeliverOwnership ); + + m_aCloseListener.notifyEach( &XCloseListener::notifyClosing, const_cast<const lang::EventObject&>(aEvent) ); + + dispose(); + } + catch ( const Exception& ) + { + SolarMutexGuard g; + m_bClosing = false; + throw; + } + + // SYNCHRONIZED -> + SolarMutexGuard g; + m_bClosing = false; + // <- SYNCHRONIZED +} + +void SAL_CALL ODatabaseDocument::addCloseListener( const Reference< css::util::XCloseListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aCloseListener.addInterface(Listener); +} + +void SAL_CALL ODatabaseDocument::removeCloseListener( const Reference< css::util::XCloseListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aCloseListener.removeInterface(Listener); +} + +Reference< XNameAccess > SAL_CALL ODatabaseDocument::getFormDocuments( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getDocumentContainer_throw( ODatabaseModelImpl::E_FORM ); +} + +Reference< XNameAccess > SAL_CALL ODatabaseDocument::getReportDocuments( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getDocumentContainer_throw( ODatabaseModelImpl::E_REPORT ); +} + +void ODatabaseDocument::WriteThroughComponent( const Reference< XComponent >& xComponent, const char* pStreamName, + const char* pServiceName, const Sequence< Any >& _rArguments, const Sequence< PropertyValue >& rMediaDesc, + const Reference<XStorage>& _xStorageToSaveTo ) const +{ + OSL_ENSURE( pStreamName, "Need stream name!" ); + OSL_ENSURE( pServiceName, "Need service name!" ); + + // open stream + OUString sStreamName = OUString::createFromAscii( pStreamName ); + Reference< XStream > xStream = _xStorageToSaveTo->openStreamElement( sStreamName, ElementModes::READWRITE | ElementModes::TRUNCATE ); + if ( !xStream.is() ) + return; + + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + OSL_ENSURE( xOutputStream.is(), "Can't create output stream in package!" ); + if ( !xOutputStream.is() ) + return; + + Reference< XSeekable > xSeek( xOutputStream, UNO_QUERY ); + if ( xSeek.is() ) + xSeek->seek(0); + + Reference< XPropertySet > xStreamProp( xOutputStream, UNO_QUERY_THROW ); + xStreamProp->setPropertyValue( INFO_MEDIATYPE, Any( OUString( "text/xml" ) ) ); + xStreamProp->setPropertyValue( "Compressed", Any( true ) ); + + // write the stuff + WriteThroughComponent( xOutputStream, xComponent, pServiceName, _rArguments, rMediaDesc ); +} + +void ODatabaseDocument::WriteThroughComponent( const Reference< XOutputStream >& xOutputStream, + const Reference< XComponent >& xComponent, const char* pServiceName, const Sequence< Any >& _rArguments, + const Sequence< PropertyValue >& rMediaDesc ) const +{ + OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" ); + OSL_ENSURE( xComponent.is(), "Need component!" ); + OSL_ENSURE( nullptr != pServiceName, "Need component name!" ); + + // get component + Reference< XWriter > xSaxWriter = xml::sax::Writer::create( m_pImpl->m_aContext ); + + // connect XML writer to output stream + xSaxWriter->setOutputStream( xOutputStream ); + + // prepare arguments (prepend doc handler to given arguments) + Sequence<Any> aArgs( 1 + _rArguments.getLength() ); + auto pArgs = aArgs.getArray(); + pArgs[0] <<= xSaxWriter; + for ( sal_Int32 i = 0; i < _rArguments.getLength(); ++i ) + pArgs[ i+1 ] = _rArguments[i]; + + // get filter component + Reference< XExporter > xExporter( m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pServiceName), aArgs, m_pImpl->m_aContext), UNO_QUERY_THROW ); + + // connect model and filter + xExporter->setSourceDocument( xComponent ); + + // filter + Reference< XFilter > xFilter( xExporter, UNO_QUERY_THROW ); + xFilter->filter( rMediaDesc ); +} + +void ODatabaseDocument::impl_writeStorage_throw( const Reference< XStorage >& _rxTargetStorage, const ::comphelper::NamedValueCollection& _rMediaDescriptor ) const +{ + // extract status indicator + Sequence< Any > aDelegatorArguments; + lcl_extractStatusIndicator( _rMediaDescriptor, aDelegatorArguments ); + + uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) ); + + xInfoSet->setPropertyValue("UsePrettyPrinting", uno::Any(officecfg::Office::Common::Save::Document::PrettyPrinting::get())); + if ( officecfg::Office::Common::Save::URL::FileSystem::get() ) + { + OUString sBaseURI = _rMediaDescriptor.getOrDefault("BaseURI", OUString()); + if (sBaseURI.isEmpty()) + sBaseURI = _rMediaDescriptor.getOrDefault("URL",OUString()); + xInfoSet->setPropertyValue("BaseURI", uno::Any(sBaseURI)); + } + + // Set TargetStorage, so it doesn't have to be re-constructed based on possibly empty URL. + xInfoSet->setPropertyValue("TargetStorage", uno::Any(m_pImpl->getRootStorage())); + + // Set StreamRelPath, in case this document is an embedded one. + OUString sStreamRelPath; + OUString sURL = _rMediaDescriptor.getOrDefault("URL", OUString()); + if (sURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:")) + { + // In this case the host contains the real path, and the path is the embedded stream name. + INetURLObject aURL(sURL); + sStreamRelPath = aURL.GetURLPath(INetURLObject::DecodeMechanism::WithCharset); + if (sStreamRelPath.startsWith("/")) + sStreamRelPath = sStreamRelPath.copy(1); + } + if (!sStreamRelPath.isEmpty()) + xInfoSet->setPropertyValue("StreamRelPath", uno::Any(sStreamRelPath)); + + sal_Int32 nArgsLen = aDelegatorArguments.getLength(); + aDelegatorArguments.realloc(nArgsLen+1); + aDelegatorArguments.getArray()[nArgsLen++] <<= xInfoSet; + + Reference< XPropertySet > xProp( _rxTargetStorage, UNO_QUERY_THROW ); + xProp->setPropertyValue( INFO_MEDIATYPE, Any( OUString(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) ) ); + + OUString aVersion; + SvtSaveOptions::ODFSaneDefaultVersion const nDefVersion = + GetODFSaneDefaultVersion(); + // older versions can not have this property set, + // it exists only starting from ODF1.2 + if (nDefVersion >= SvtSaveOptions::ODFSVER_013) + { + aVersion = ODFVER_013_TEXT; + } + else if (nDefVersion >= SvtSaveOptions::ODFSVER_012) + { + aVersion = ODFVER_012_TEXT; + } + + if (!aVersion.isEmpty()) + { + try + { + xProp->setPropertyValue("Version" , uno::Any(aVersion)); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "exception setting Version"); + } + } + + Reference< XComponent > xComponent( *const_cast< ODatabaseDocument* >( this ), UNO_QUERY_THROW ); + + Sequence< PropertyValue > aMediaDescriptor; + _rMediaDescriptor >>= aMediaDescriptor; + + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("settings.xml"))); + WriteThroughComponent( xComponent, "settings.xml", "com.sun.star.comp.sdb.XMLSettingsExporter", + aDelegatorArguments, aMediaDescriptor, _rxTargetStorage ); + + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + WriteThroughComponent( xComponent, "content.xml", "com.sun.star.comp.sdb.DBExportFilter", + aDelegatorArguments, aMediaDescriptor, _rxTargetStorage ); + + if ( _rxTargetStorage->hasByName ( sPictures ) ) + { + try + { + // Delete any previously existing Pictures folder and regenerate + // any needed content if needed + Reference< XStorageBasedLibraryContainer > xDlgs = m_pImpl->getLibraryContainer( false ); + if ( xDlgs.is() ) + { + Reference< XModel > xModel(const_cast< ODatabaseDocument*>(this)); + lcl_uglyHackToStoreDialogeEmbedImages( m_pImpl->getLibraryContainer(false), _rxTargetStorage, xModel, m_pImpl->m_aContext ); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + m_pImpl->storeLibraryContainersTo( _rxTargetStorage ); +} + +Reference< XUIConfigurationManager > SAL_CALL ODatabaseDocument::getUIConfigurationManager( ) +{ + return Reference< XUIConfigurationManager >( getUIConfigurationManager2(), UNO_QUERY_THROW ); +} + +Reference< XUIConfigurationManager2 > const & ODatabaseDocument::getUIConfigurationManager2( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + if ( !m_xUIConfigurationManager.is() ) + { + m_xUIConfigurationManager = UIConfigurationManager::create( m_pImpl->m_aContext ); + + OUString aUIConfigFolderName( "Configurations2" ); + + // First try to open with READWRITE and then READ + Reference< XStorage > xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, ElementModes::READWRITE ); + if ( xConfigStorage.is() ) + { + OUString aMediaType; + Reference< XPropertySet > xPropSet( xConfigStorage, UNO_QUERY ); + Any a = xPropSet->getPropertyValue( INFO_MEDIATYPE ); + if ( !( a >>= aMediaType ) || aMediaType.isEmpty() ) + { + a <<= OUString("application/vnd.sun.xml.ui.configuration"); + xPropSet->setPropertyValue( INFO_MEDIATYPE, a ); + } + } + else + xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, ElementModes::READ ); + + // initialize ui configuration manager with document substorage + m_xUIConfigurationManager->setStorage( xConfigStorage ); + } + + return m_xUIConfigurationManager; +} + +Reference< XStorage > SAL_CALL ODatabaseDocument::getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XDocumentSubStorageSupplier > xStorageAccess( m_pImpl->getDocumentSubStorageSupplier() ); + return xStorageAccess->getDocumentSubStorage( aStorageName, nMode ); +} + +Sequence< OUString > SAL_CALL ODatabaseDocument::getDocumentSubStoragesNames( ) +{ + Reference< XDocumentSubStorageSupplier > xStorageAccess( m_pImpl->getDocumentSubStorageSupplier() ); + return xStorageAccess->getDocumentSubStoragesNames(); +} + +void ODatabaseDocument::impl_notifyStorageChange_nolck_nothrow( const Reference< XStorage >& xNewRootStorage ) +{ + Reference< XInterface > xMe( *this ); + + m_aStorageListeners.forEach( + [&xMe, &xNewRootStorage] (uno::Reference<XStorageChangeListener> const& xListener) { + return xListener->notifyStorageChange(xMe, xNewRootStorage); + }); +} + +void ODatabaseDocument::disposing() +{ + if ( !m_pImpl.is() ) + { + // this means that we're already disposed + OSL_ENSURE( ODatabaseDocument_OfficeDocument::rBHelper.bDisposed, "ODatabaseDocument::disposing: no impl anymore, but not yet disposed!" ); + return; + } + + if ( impl_isInitialized() ) + m_aEventNotifier.notifyDocumentEvent( "OnUnload" ); + + Reference< XModel > xHoldAlive( this ); + + m_aEventNotifier.disposing(); + + lang::EventObject aDisposeEvent(static_cast<XWeak*>(this)); + m_aModifyListeners.disposeAndClear( aDisposeEvent ); + m_aCloseListener.disposeAndClear( aDisposeEvent ); + m_aStorageListeners.disposeAndClear( aDisposeEvent ); + + // this is the list of objects which we currently hold as member. Upon resetting + // those members, we can (potentially) release the last reference to them, in which + // case they will be deleted - if they're C++ implementations, that is :). + // Some of those implementations are offending enough to require the SolarMutex, which + // means we should not release the last reference while our own mutex is locked ... + std::vector< Reference< XInterface > > aKeepAlive; + + // SYNCHRONIZED -> + { + SolarMutexGuard aGuard; + + OSL_ENSURE(m_aControllers.empty(), + "ODatabaseDocument::disposing: there still are controllers!"); + // normally, nobody should explicitly dispose, but only XCloseable::close + // the document. And upon closing, our controllers are closed, too + + { + uno::Reference<uno::XInterface> xUIInterface(m_xUIConfigurationManager); + aKeepAlive.push_back(xUIInterface); + } + m_xUIConfigurationManager = nullptr; + + clearObjectContainer(m_xForms); + clearObjectContainer(m_xReports); + + // reset the macro mode: in case the our impl struct stays alive (e.g. because our DataSource + // object still exists), and somebody subsequently re-opens the document, we want to have + // the security warning, again. + m_pImpl->resetMacroExecutionMode(); + + // similar arguing for our ViewMonitor + m_aViewMonitor.reset(); + + // tell our Impl to forget us + m_pImpl->modelIsDisposing(impl_isInitialized(), ODatabaseModelImpl::ResetModelAccess()); + + // now, at the latest, the controller array should be empty. Controllers are + // expected to listen for our disposal, and disconnect then + OSL_ENSURE(m_aControllers.empty(), + "ODatabaseDocument::disposing: there still are controllers!"); + impl_disposeControllerFrames_nothrow(); + + { + uno::Reference<uno::XInterface> xModuleInterface(m_xModuleManager); + aKeepAlive.push_back(xModuleInterface); + } + m_xModuleManager.clear(); + + { + uno::Reference<uno::XInterface> xTitleInterface(m_xTitleHelper); + aKeepAlive.push_back(xTitleInterface); + } + m_xTitleHelper.clear(); + + m_pImpl.clear(); + } + // <- SYNCHRONIZED + + aKeepAlive.clear(); +} + +// XComponent +void SAL_CALL ODatabaseDocument::dispose( ) +{ + ::cppu::WeakComponentImplHelperBase::dispose(); +} + +void SAL_CALL ODatabaseDocument::addEventListener( const Reference< lang::XEventListener >& _xListener ) +{ + ::cppu::WeakComponentImplHelperBase::addEventListener( _xListener ); +} + +void SAL_CALL ODatabaseDocument::removeEventListener( const Reference< lang::XEventListener >& _xListener ) +{ + ::cppu::WeakComponentImplHelperBase::removeEventListener( _xListener ); +} + +// XServiceInfo +OUString ODatabaseDocument::getImplementationName() +{ + return "com.sun.star.comp.dba.ODatabaseDocument"; +} + +Sequence< OUString > ODatabaseDocument::getSupportedServiceNames() +{ + return { "com.sun.star.sdb.OfficeDatabaseDocument", "com.sun.star.document.OfficeDocument" }; +} + +sal_Bool ODatabaseDocument::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Reference< XDataSource > SAL_CALL ODatabaseDocument::getDataSource() +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getOrCreateDataSource(); +} + +namespace +{ +/// Property map for embedded import info set. +comphelper::PropertyMapEntry const aEmbeddedImportInfoMap[] = +{ + {OUString("StreamRelPath"), 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + {OUString("StreamName"), 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + {OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, +}; +} + +void SAL_CALL ODatabaseDocument::loadFromStorage(const Reference<XStorage>& xStorage, const Sequence<PropertyValue>& rMediaDescriptor) +{ + DocumentGuard aGuard(*this, DocumentGuard::InitMethod); + + uno::Reference<beans::XPropertySet> xInfoSet(comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aEmbeddedImportInfoMap))); + xInfoSet->setPropertyValue("StreamRelPath", uno::Any(comphelper::NamedValueCollection::getOrDefault(rMediaDescriptor, u"HierarchicalDocumentName", OUString()))); + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + xInfoSet->setPropertyValue("SourceStorage", uno::Any(xStorage)); + + uno::Sequence<uno::Any> aFilterCreationArgs{ Any(xInfoSet) }; + + uno::Reference<document::XImporter> xImporter(m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.comp.sdb.DBFilter", aFilterCreationArgs, m_pImpl->m_aContext), uno::UNO_QUERY_THROW); + + uno::Reference<lang::XComponent> xComponent(*this, uno::UNO_QUERY_THROW); + xImporter->setTargetDocument(xComponent); + + uno::Reference<document::XFilter> xFilter(xImporter, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aFilterArgs; + xFilter->filter(aFilterArgs); + + // In case of embedding, XModel::attachResource is already called. + if (m_bEmbedded) + impl_setInitialized(); + + impl_setModified_nothrow(false, aGuard); +} + +void SAL_CALL ODatabaseDocument::storeToStorage( const Reference< XStorage >& _rxStorage, const Sequence< PropertyValue >& _rMediaDescriptor ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_storeToStorage_throw( _rxStorage, _rMediaDescriptor, aGuard ); +} + +void SAL_CALL ODatabaseDocument::switchToStorage( const Reference< XStorage >& _rxNewRootStorage ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XStorage > xNewRootStorage( m_pImpl->switchToStorage( _rxNewRootStorage ) ); + + aGuard.clear(); + impl_notifyStorageChange_nolck_nothrow( xNewRootStorage ); +} + +Reference< XStorage > SAL_CALL ODatabaseDocument::getDocumentStorage( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::MethodUsedDuringInit); + return m_pImpl->getOrCreateRootStorage(); +} + +void SAL_CALL ODatabaseDocument::addStorageChangeListener( const Reference< XStorageChangeListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aStorageListeners.addInterface( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeStorageChangeListener( const Reference< XStorageChangeListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aStorageListeners.addInterface( Listener ); +} + +Reference< XStorageBasedLibraryContainer > SAL_CALL ODatabaseDocument::getBasicLibraries() +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return m_pImpl->getLibraryContainer( true ); +} + +Reference< XStorageBasedLibraryContainer > SAL_CALL ODatabaseDocument::getDialogLibraries() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return m_pImpl->getLibraryContainer( false ); +} + +sal_Bool SAL_CALL ODatabaseDocument::getAllowMacroExecution() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return m_pImpl->adjustMacroMode_AutoReject(); +} + +Reference< XEmbeddedScripts > SAL_CALL ODatabaseDocument::getScriptContainer() +{ + return this; +} + +Reference< provider::XScriptProvider > SAL_CALL ODatabaseDocument::getScriptProvider( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XScriptProvider > xScriptProvider( m_xScriptProvider ); + if ( !xScriptProvider.is() ) + { + Reference < XScriptProviderFactory > xFactory = + theMasterScriptProviderFactory::get( m_pImpl->m_aContext ); + + Any aScriptProviderContext; + if ( m_bAllowDocumentScripting ) + aScriptProviderContext <<= Reference< XModel >( this ); + + xScriptProvider.set( xFactory->createScriptProvider( aScriptProviderContext ), UNO_SET_THROW ); + m_xScriptProvider = xScriptProvider; + } + + return xScriptProvider; +} + +Reference< XNameReplace > SAL_CALL ODatabaseDocument::getEvents( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return m_pEventContainer.get(); +} + +Reference< XInterface > ODatabaseDocument::getThis() const +{ + return *const_cast< ODatabaseDocument* >( this ); +} + +namespace { + +struct CreateAny +{ + Any operator() (const Reference<XController>& lhs) const + { + return Any(lhs); + } +}; + +} + +// XModel2 +Reference< XEnumeration > SAL_CALL ODatabaseDocument::getControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + uno::Sequence< Any> aController( m_aControllers.size() ); + std::transform( m_aControllers.begin(), m_aControllers.end(), aController.getArray(), CreateAny() ); + return new ::comphelper::OAnyEnumeration(aController); +} + +Sequence< OUString > SAL_CALL ODatabaseDocument::getAvailableViewControllerNames( ) +{ + Sequence< OUString > aNames { SERVICE_SDB_APPLICATIONCONTROLLER }; + return aNames; +} + +Reference< XController2 > SAL_CALL ODatabaseDocument::createDefaultViewController( const Reference< XFrame >& Frame ) +{ + return createViewController( "Default", Sequence< PropertyValue >(), Frame); +} + +Reference< XController2 > SAL_CALL ODatabaseDocument::createViewController( const OUString& ViewName, const Sequence< PropertyValue >& Arguments, const Reference< XFrame >& Frame ) +{ + if ( ViewName != "Default" && ViewName != "Preview" ) + throw IllegalArgumentException( OUString(), *this, 1 ); + if ( !Frame.is() ) + throw IllegalArgumentException( OUString(), *this, 3 ); + + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + aGuard.clear(); + + Reference< XController2 > xController( + m_pImpl->m_aContext->getServiceManager()->createInstanceWithContext("org.openoffice.comp.dbu.OApplicationController", m_pImpl->m_aContext), + UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aInitArgs( Arguments ); + aInitArgs.put( "Frame", Frame ); + if ( ViewName == "Preview" ) + aInitArgs.put( "Preview", true ); + Reference< XInitialization > xInitController( xController, UNO_QUERY_THROW ); + xInitController->initialize( aInitArgs.getWrappedPropertyValues() ); + + return xController; +} + +Reference< XTitle > const & ODatabaseDocument::impl_getTitleHelper_throw() +{ + if ( ! m_xTitleHelper.is ()) + { + Reference< XUntitledNumbers > xDesktop(Desktop::create(m_pImpl->m_aContext), uno::UNO_QUERY_THROW); + Reference< frame::XModel > xThis (getThis(), uno::UNO_QUERY_THROW); + + m_xTitleHelper = new ::framework::TitleHelper(m_pImpl->m_aContext, xThis, xDesktop); + } + + return m_xTitleHelper; +} + +uno::Reference< frame::XUntitledNumbers > ODatabaseDocument::impl_getUntitledHelper_throw(const uno::Reference< uno::XInterface >& _xComponent) +{ + if ( !m_xModuleManager.is() ) + m_xModuleManager.set( ModuleManager::create(m_pImpl->m_aContext) ); + + OUString sModuleId; + if (_xComponent.is()) + sModuleId = m_xModuleManager->identify(_xComponent); + + uno::Reference< frame::XUntitledNumbers > xNumberedControllers; + + TNumberedController::const_iterator aFind = m_aNumberedControllers.find(sModuleId); + if ( aFind == m_aNumberedControllers.end() ) + { + rtl::Reference<::comphelper::NumberedCollection> pHelper = new ::comphelper::NumberedCollection(); + xNumberedControllers = pHelper; + pHelper->setOwner(uno::Reference< frame::XModel >(this)); + + m_aNumberedControllers.emplace( sModuleId,xNumberedControllers ); + } + else + xNumberedControllers = aFind->second; + + return xNumberedControllers; +} + +// css.frame.XTitle +OUString SAL_CALL ODatabaseDocument::getTitle() +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getTitleHelper_throw()->getTitle(); +} + +// css.frame.XTitle +void SAL_CALL ODatabaseDocument::setTitle( const OUString& sTitle ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getTitleHelper_throw()->setTitle( sTitle ); + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + // <- SYNCHRONIZED +} + +// css.frame.XTitleChangeBroadcaster +void SAL_CALL ODatabaseDocument::addTitleChangeListener( const uno::Reference< frame::XTitleChangeListener >& xListener ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + uno::Reference< frame::XTitleChangeBroadcaster > xBroadcaster( impl_getTitleHelper_throw(), uno::UNO_QUERY_THROW ); + xBroadcaster->addTitleChangeListener( xListener ); +} + +// css.frame.XTitleChangeBroadcaster +void SAL_CALL ODatabaseDocument::removeTitleChangeListener( const uno::Reference< frame::XTitleChangeListener >& xListener ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + uno::Reference< frame::XTitleChangeBroadcaster > xBroadcaster( impl_getTitleHelper_throw(), uno::UNO_QUERY_THROW ); + xBroadcaster->removeTitleChangeListener( xListener ); +} + +// css.frame.XUntitledNumbers +::sal_Int32 SAL_CALL ODatabaseDocument::leaseNumber( const uno::Reference< uno::XInterface >& xComponent ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return impl_getUntitledHelper_throw(xComponent)->leaseNumber (xComponent); +} + +// css.frame.XUntitledNumbers +void SAL_CALL ODatabaseDocument::releaseNumber( ::sal_Int32 nNumber ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getUntitledHelper_throw()->releaseNumber (nNumber); +} + +// css.frame.XUntitledNumbers +void SAL_CALL ODatabaseDocument::releaseNumberForComponent( const uno::Reference< uno::XInterface >& xComponent ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getUntitledHelper_throw(xComponent)->releaseNumberForComponent (xComponent); +} + +// css.frame.XUntitledNumbers +OUString SAL_CALL ODatabaseDocument::getUntitledPrefix() +{ + return OUString(); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseDocument(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + Reference<XUnoTunnel> xDBContextTunnel(DatabaseContext::create(context), UNO_QUERY_THROW); + dbaccess::ODatabaseContext* pContext + = comphelper::getFromUnoTunnel<dbaccess::ODatabaseContext>(xDBContextTunnel); + assert(pContext); + + rtl::Reference pImpl( + new dbaccess::ODatabaseModelImpl(context, *pContext)); + css::uno::Reference<XInterface> inst(pImpl->createNewModel_deliverOwnership()); + inst->acquire(); + return inst.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasedocument.hxx b/dbaccess/source/core/dataaccess/databasedocument.hxx new file mode 100644 index 000000000..bd458d1b9 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasedocument.hxx @@ -0,0 +1,753 @@ +/* -*- 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 <memory> + +#include <ModelImpl.hxx> +#include "documenteventnotifier.hxx" + +#include <com/sun/star/document/XDocumentSubStorageSupplier.hpp> +#include <com/sun/star/frame/DoubleInitializationException.hpp> +#include <com/sun/star/frame/XModel3.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp> +#include <com/sun/star/frame/XUntitledNumbers.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/sdb/XReportDocumentsSupplier.hpp> +#include <com/sun/star/sdb/XFormDocumentsSupplier.hpp> +#include <com/sun/star/view/XPrintable.hpp> +#include <com/sun/star/frame/XModuleManager2.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/NotInitializedException.hpp> +#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp> +#include <com/sun/star/embed/XTransactionListener.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> +#include <com/sun/star/script/XStorageBasedLibraryContainer.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/document/XDocumentRecovery.hpp> +#include <com/sun/star/ui/XUIConfigurationManager2.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase3.hxx> +#include <rtl/ref.hxx> + +namespace comphelper { + class NamedValueCollection; +} + +namespace dbaccess +{ + +class DocumentEvents; +class DocumentEventExecutor; +class DocumentGuard; + +typedef std::vector< css::uno::Reference< css::frame::XController > > Controllers; + +// ViewMonitor +/** helper class monitoring the views of a document, and firing appropriate events + when views are attached / detached +*/ +class ViewMonitor +{ +public: + explicit ViewMonitor( DocumentEventNotifier& _rEventNotifier ) + :m_rEventNotifier( _rEventNotifier ) + ,m_bIsNewDocument( true ) + ,m_bEverHadController( false ) + ,m_bLastIsFirstEverController( false ) + ,m_xLastConnectedController() + { + } + + ViewMonitor(const ViewMonitor&) = delete; + const ViewMonitor& operator=(const ViewMonitor&) = delete; + + void reset() + { + m_bEverHadController = false; + m_bLastIsFirstEverController = false; + m_xLastConnectedController.clear(); + } + + /** to be called when a view (aka controller) has been connected to the document + @return + <TRUE/> if and only if this was the first-ever controller connected to the document + */ + bool onControllerConnected( + const css::uno::Reference< css::frame::XController >& _rxController + ); + + /** to be called when a controller is set as current controller + @return <TRUE/> + if and only if the controller connection indicates that loading the document is finished. This + is the case if the given controller has previously been connected, and it was the first controller + ever for which this happened. + */ + bool onSetCurrentController( + const css::uno::Reference< css::frame::XController >& _rxController + ); + + void onLoadedDocument() { m_bIsNewDocument = false; } + +private: + DocumentEventNotifier& m_rEventNotifier; + bool m_bIsNewDocument; + bool m_bEverHadController; + bool m_bLastIsFirstEverController; + css::uno::Reference< css::frame::XController > + m_xLastConnectedController; +}; + +// ODatabaseDocument +typedef cppu::PartialWeakComponentImplHelper< css::frame::XModel3 + , css::util::XModifiable + , css::frame::XStorable + , css::document::XEventBroadcaster + , css::document::XDocumentEventBroadcaster + , css::view::XPrintable + , css::util::XCloseable + , css::lang::XServiceInfo + , css::sdb::XOfficeDatabaseDocument + , css::ui::XUIConfigurationManagerSupplier + , css::document::XStorageBasedDocument + , css::document::XEmbeddedScripts + , css::document::XScriptInvocationContext + , css::script::provider::XScriptProviderSupplier + , css::document::XEventsSupplier + , css::frame::XLoadable + , css::document::XDocumentRecovery + > ODatabaseDocument_OfficeDocument; + +typedef ::cppu::ImplHelper3< css::frame::XTitle + , css::frame::XTitleChangeBroadcaster + , css::frame::XUntitledNumbers + > ODatabaseDocument_Title; + +class ODatabaseDocument :public ModelDependentComponent // ModelDependentComponent must be first! + ,public ODatabaseDocument_OfficeDocument + ,public ODatabaseDocument_Title +{ + enum InitState + { + NotInitialized, + Initializing, + Initialized + }; + + typedef std::map< OUString, css::uno::Reference< css::frame::XUntitledNumbers > > TNumberedController; + css::uno::Reference< css::ui::XUIConfigurationManager2> m_xUIConfigurationManager; + + ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_aModifyListeners; + ::comphelper::OInterfaceContainerHelper3<css::util::XCloseListener> m_aCloseListener; + ::comphelper::OInterfaceContainerHelper3<css::document::XStorageChangeListener> m_aStorageListeners; + + std::unique_ptr<DocumentEvents> m_pEventContainer; + ::rtl::Reference< DocumentEventExecutor > m_pEventExecutor; + DocumentEventNotifier m_aEventNotifier; + + css::uno::Reference< css::frame::XController > m_xCurrentController; + Controllers m_aControllers; + ViewMonitor m_aViewMonitor; + + css::uno::WeakReference< css::container::XNameAccess > m_xForms; + css::uno::WeakReference< css::container::XNameAccess > m_xReports; + css::uno::WeakReference< css::script::provider::XScriptProvider > m_xScriptProvider; + + /** @short such module manager is used to classify new opened documents. */ + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; + css::uno::Reference< css::frame::XTitle > m_xTitleHelper; + TNumberedController m_aNumberedControllers; + + /** true if and only if the DatabaseDocument's "initNew" or "load" have been called (or, well, + the document has be initialized implicitly - see storeAsURL + */ + InitState m_eInitState; + bool m_bClosing; + bool m_bAllowDocumentScripting; + bool m_bHasBeenRecovered; + /// If XModel::attachResource() was called to inform us that the document is embedded into another one. + bool m_bEmbedded; + + enum StoreType { SAVE, SAVE_AS }; + /** stores the document to the given URL, rebases it to the respective new storage, if necessary, resets + the modified flag, and notifies any listeners as required + + @param _rURL + the URL to store the document to + @param _rArguments + arguments for storing the document (MediaDescriptor) + @param _eType + the type of the store process (Save or SaveAs). The method will automatically + notify the proper events for this type. + @param _rGuard + the instance lock to be released before doing synchronous notifications + @throws css::io::IOException + @throws css::uno::RuntimeException + */ + void impl_storeAs_throw( + const OUString& _rURL, + const ::comphelper::NamedValueCollection& _rArguments, + const StoreType _eType, + DocumentGuard& _rGuard + ); + + /** notifies our storage change listeners that our underlying storage changed + + @param _rxNewRootStorage + the new root storage to be notified. If <NULL/>, it is assumed that no storage change actually + happened, and the listeners are not notified. + */ + void impl_notifyStorageChange_nolck_nothrow( + const css::uno::Reference< css::embed::XStorage >& _rxNewRootStorage + ); + + /// write a single XML stream into the package + void WriteThroughComponent( + const css::uno::Reference< css::lang::XComponent > & xComponent, /// the component we export + const char* pStreamName, /// the stream name + const char* pServiceName, /// service name of the component + const css::uno::Sequence< css::uno::Any> & rArguments, /// the argument (XInitialization) + const css::uno::Sequence< css::beans::PropertyValue> & rMediaDesc,/// output descriptor + const css::uno::Reference< css::embed::XStorage >& _xStorageToSaveTo + ) const; + + /// write a single output stream + /// (to be called either directly or by WriteThroughComponent(...)) + void WriteThroughComponent( + const css::uno::Reference< css::io::XOutputStream >& xOutputStream, + const css::uno::Reference< css::lang::XComponent >& xComponent, + const char* pServiceName, + const css::uno::Sequence< css::uno::Any >& rArguments, + const css::uno::Sequence< css::beans::PropertyValue> & rMediaDesc + ) const; + + /** writes the content and settings + @param sURL + The URL + @param lArguments + The media descriptor + @param _xStorageToSaveTo + The storage which should be used for saving + */ + void impl_writeStorage_throw( + const css::uno::Reference< css::embed::XStorage >& _rxTargetStorage, + const ::comphelper::NamedValueCollection& _rMediaDescriptor + ) const; + + // ModelDependentComponent overridables + virtual css::uno::Reference< css::uno::XInterface > getThis() const override; + + css::uno::Reference< css::frame::XTitle > const & impl_getTitleHelper_throw(); + css::uno::Reference< css::frame::XUntitledNumbers > impl_getUntitledHelper_throw( + const css::uno::Reference< css::uno::XInterface >& _xComponent = css::uno::Reference< css::uno::XInterface >()); + +private: + explicit ODatabaseDocument(const ::rtl::Reference<ODatabaseModelImpl>& _pImpl); + // Do NOT create those documents directly, always use ODatabaseModelImpl::getModel. Reason is that + // ODatabaseDocument requires clear ownership, and in turn lifetime synchronisation with the ModelImpl. + // If you create a ODatabaseDocument directly, you might easily create a leak. + // #i50905# + +protected: + virtual void SAL_CALL disposing() override; + + virtual ~ODatabaseDocument() override; + +public: + struct FactoryAccess { friend class ODatabaseModelImpl; private: FactoryAccess() { } }; + static rtl::Reference<ODatabaseDocument> createDatabaseDocument( const ::rtl::Reference<ODatabaseModelImpl>& _pImpl, FactoryAccess /*accessControl*/ ) + { + return new ODatabaseDocument( _pImpl ); + } + + // 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; + + // 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XModel + virtual sal_Bool SAL_CALL attachResource( const OUString& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override ; + virtual OUString SAL_CALL getURL( ) override ; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getArgs( ) override ; + virtual void SAL_CALL connectController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual void SAL_CALL disconnectController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual void SAL_CALL lockControllers( ) override ; + virtual void SAL_CALL unlockControllers( ) override ; + virtual sal_Bool SAL_CALL hasControllersLocked( ) override ; + virtual css::uno::Reference< css::frame::XController > SAL_CALL getCurrentController( ) override ; + virtual void SAL_CALL setCurrentController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getCurrentSelection( ) override ; + + // XModel2 + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL getControllers( ) override ; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableViewControllerNames( ) override ; + virtual css::uno::Reference< css::frame::XController2 > SAL_CALL createDefaultViewController( const css::uno::Reference< css::frame::XFrame >& Frame ) override ; + virtual css::uno::Reference< css::frame::XController2 > SAL_CALL createViewController( const OUString& ViewName, const css::uno::Sequence< css::beans::PropertyValue >& Arguments, const css::uno::Reference< css::frame::XFrame >& Frame ) override ; + virtual void SAL_CALL setArgs(const css::uno::Sequence<css::beans::PropertyValue>& aArgs) override; + + // XModel3 + virtual ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL getArgs2( const ::css::uno::Sequence< ::rtl::OUString >& requestedArgs ) override; + + // XStorable + virtual sal_Bool SAL_CALL hasLocation( ) override ; + virtual OUString SAL_CALL getLocation( ) override ; + virtual sal_Bool SAL_CALL isReadonly( ) override ; + virtual void SAL_CALL store( ) override ; + virtual void SAL_CALL storeAsURL( const OUString& sURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override ; + virtual void SAL_CALL storeToURL( const OUString& sURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override ; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // css::util::XModifiable + virtual sal_Bool SAL_CALL isModified( ) override ; + virtual void SAL_CALL setModified( sal_Bool bModified ) override ; + + // XEventBroadcaster + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::document::XEventListener >& aListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& aListener ) override; + + // XDocumentEventBroadcaster + virtual void SAL_CALL addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override; + virtual void SAL_CALL removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override; + virtual void SAL_CALL notifyDocumentEvent( const OUString& EventName, const css::uno::Reference< css::frame::XController2 >& ViewController, const css::uno::Any& Supplement ) override; + + // XPrintable + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPrinter( ) override ; + virtual void SAL_CALL setPrinter( const css::uno::Sequence< css::beans::PropertyValue >& aPrinter ) override ; + virtual void SAL_CALL print( const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override ; + + // XFormDocumentsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getFormDocuments( ) override; + + // XReportDocumentsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getReportDocuments( ) override; + + // XCloseable + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + virtual void SAL_CALL addCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + virtual void SAL_CALL removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + // XUIConfigurationManagerSupplier + virtual css::uno::Reference< css::ui::XUIConfigurationManager > SAL_CALL getUIConfigurationManager( ) override; + + // XDocumentSubStorageSupplier + virtual css::uno::Reference< css::embed::XStorage > SAL_CALL getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getDocumentSubStoragesNames( ) override; + + // XOfficeDatabaseDocument + virtual css::uno::Reference< css::sdbc::XDataSource > SAL_CALL getDataSource() override; + + // XStorageBasedDocument + virtual void SAL_CALL loadFromStorage( const css::uno::Reference< css::embed::XStorage >& xStorage, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescriptor ) override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& xStorage, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescriptor ) override; + virtual void SAL_CALL switchToStorage( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + virtual css::uno::Reference< css::embed::XStorage > SAL_CALL getDocumentStorage( ) override; + virtual void SAL_CALL addStorageChangeListener( const css::uno::Reference< css::document::XStorageChangeListener >& xListener ) override; + virtual void SAL_CALL removeStorageChangeListener( const css::uno::Reference< css::document::XStorageChangeListener >& xListener ) override; + + // XEmbeddedScripts + virtual css::uno::Reference< css::script::XStorageBasedLibraryContainer > SAL_CALL getBasicLibraries() override; + virtual css::uno::Reference< css::script::XStorageBasedLibraryContainer > SAL_CALL getDialogLibraries() override; + virtual sal_Bool SAL_CALL getAllowMacroExecution() override; + + // XScriptInvocationContext + virtual css::uno::Reference< css::document::XEmbeddedScripts > SAL_CALL getScriptContainer() override; + + // XScriptProviderSupplier + virtual css::uno::Reference< css::script::provider::XScriptProvider > SAL_CALL getScriptProvider( ) override; + + // XEventsSupplier + virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; + + // XLoadable + virtual void SAL_CALL initNew( ) override; + virtual void SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + + // css.document.XDocumentRecovery + virtual sal_Bool SAL_CALL wasModifiedSinceLastSave() override; + virtual void SAL_CALL storeToRecoveryFile( const OUString& i_TargetLocation, const css::uno::Sequence< css::beans::PropertyValue >& i_MediaDescriptor ) override; + virtual void SAL_CALL recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const css::uno::Sequence< css::beans::PropertyValue >& i_MediaDescriptor ) override; + + // XTitle + virtual OUString SAL_CALL getTitle( ) override; + virtual void SAL_CALL setTitle( const OUString& sTitle ) override; + + // XTitleChangeBroadcaster + virtual void SAL_CALL addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) override; + virtual void SAL_CALL removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) override; + + // XUntitledNumbers + virtual ::sal_Int32 SAL_CALL leaseNumber( const css::uno::Reference< css::uno::XInterface >& xComponent ) override; + virtual void SAL_CALL releaseNumber( ::sal_Int32 nNumber ) override; + virtual void SAL_CALL releaseNumberForComponent( const css::uno::Reference< css::uno::XInterface >& xComponent ) override; + virtual OUString SAL_CALL getUntitledPrefix( ) override; + + /** clears the given object container + + Clearing is done via disposal - the method calls XComponent::dispose at the given object, + which must be one of our impl's or our object containers (m_xForms, m_xReports, + m_xTableDefinitions, m_xCommandDefinitions) + + @param _rxContainer + the container to clear + */ + static void clearObjectContainer( + css::uno::WeakReference< css::container::XNameAccess >& _rxContainer); + + /** checks whether the component is already initialized, throws a NotInitializedException if not + */ + void checkInitialized() const + { + if ( !impl_isInitialized() ) + throw css::lang::NotInitializedException( OUString(), getThis() ); + } + + /** checks the document is currently in the initialization phase, or already initialized. + Throws NotInitializedException if not so. + */ + void checkNotUninitialized() const + { + if ( impl_isInitialized() || impl_isInitializing() ) + // fine + return; + + throw css::lang::NotInitializedException( OUString(), getThis() ); + } + + /** checks whether the document is currently being initialized, or already initialized, + throws a DoubleInitializationException if so + */ + void checkNotInitialized() const + { + if ( impl_isInitializing() || impl_isInitialized() ) + throw css::frame::DoubleInitializationException( OUString(), getThis() ); + } + +private: + /// @throws css::uno::RuntimeException + css::uno::Reference< css::ui::XUIConfigurationManager2 > const & getUIConfigurationManager2(); + + /** returns whether the model is currently being initialized + */ + bool impl_isInitializing() const { return m_eInitState == Initializing; } + + /** returns whether the model is already initialized, i.e. the XModel's "initNew" or "load" methods have been called + */ + bool impl_isInitialized() const { return m_eInitState == Initialized; } + + /// tells the model it is being initialized now + void impl_setInitializing() { m_eInitState = Initializing; } + + /// tells the model its initialization is done + void impl_setInitialized(); + + /** closes the frames of all connected controllers + + @param _bDeliverOwnership + determines if the ownership should be transferred to the component which + possibly vetos the closing + + @throws css::util::CloseVetoException + if the closing was vetoed by any instance + */ + void impl_closeControllerFrames_nolck_throw( bool _bDeliverOwnership ); + + /** disposes the frames of all controllers which are still left in m_aControllers. + */ + void impl_disposeControllerFrames_nothrow(); + + /** does a reparenting at the given object container to ourself + + Calls XChild::setParent at the given object, which must be one of our impl's or our + object containers (m_xForms, m_xReports, m_xTableDefinitions, m_xCommandDefinitions) + */ + void impl_reparent_nothrow( const css::uno::WeakReference< css::container::XNameAccess >& _rxContainer ); + + /** retrieves the forms or reports contained, creates and initializes it, if necessary + + @throws DisposedException + if the instance is already disposed + @throws IllegalArgumentException + if <arg>_eType</arg> is not ODatabaseModelImpl::E_FORM and not ODatabaseModelImpl::E_REPORT + */ + css::uno::Reference< css::container::XNameAccess > + impl_getDocumentContainer_throw( ODatabaseModelImpl::ObjectType _eType ); + + /** resets everything + + @precond + m_pImpl is not <NULL/> + */ + void + impl_reset_nothrow(); + + /** imports the document from the given resource. + */ + static void + impl_import_nolck_throw( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::uno::XInterface >& _rxTargetComponent, + const ::comphelper::NamedValueCollection& _rResource + ); + + /** creates a storage for the given URL, truncating it if a file with this name already exists + + @throws Exception + if creating the storage failed + + @return + the newly created storage for the file at the given URL + */ + css::uno::Reference< css::embed::XStorage > + impl_createStorageFor_throw( + const OUString& _rURL + ) const; + + /** Extracts storage from arguments, or creates for the given URL, truncating it if a file with + this name already exists + + @throws Exception + if creating the storage failed + + @return + the storage that is either extracted from arguments, or newly created for the file at + the given URL + */ + css::uno::Reference<css::embed::XStorage> impl_GetStorageOrCreateFor_throw( + const ::comphelper::NamedValueCollection& _rArguments, const OUString& _rURL) const; + + /** sets our "modified" flag + + will notify all our respective listeners, if the "modified" state actually changed + + @param _bModified + the (new) flag indicating whether the document is currently modified or not + @param _rGuard + the guard for our instance. At method entry, the guard must hold the lock. At the moment + of method leave, the lock will be released. + @precond + our mutex is locked + @postcond + our mutex is not locked + */ + void impl_setModified_nothrow( bool _bModified, DocumentGuard& _rGuard ); + + /** stores the document to the given storage + + Note that the document is actually not rebased to this storage, it just stores a copy of itself + to the given target storage. + + @param _rxTargetStorage + denotes the storage to store the document into + @param _rMediaDescriptor + contains additional parameters for storing the document + @param _rDocGuard + a guard which holds the (only) lock to the document, and which will be temporarily + released where necessary (e.g. for notifications, or calling into other components) + + @throws css::uno::IllegalArgumentException + if the given storage is <NULL/>. + + @throws css::uno::RuntimeException + when any of the used operations throws it + + @throws css::io::IOException + when any of the used operations throws it, or any other exception occurs which is no + RuntimeException and no IOException + */ + void impl_storeToStorage_throw( + const css::uno::Reference< css::embed::XStorage >& _rxTargetStorage, + const css::uno::Sequence< css::beans::PropertyValue >& _rMediaDescriptor, + DocumentGuard& _rDocGuard + ) const; + + /** impl-version of attachResource + + @param i_rLogicalDocumentURL + denotes the logical URL of the document, to be reported by getURL/getLocation + @param i_rMediaDescriptor + denotes additional document parameters + @param _rDocGuard + is the guard which currently protects the document instance + */ + bool impl_attachResource( + const OUString& i_rLogicalDocumentURL, + const css::uno::Sequence< css::beans::PropertyValue >& i_rMediaDescriptor, + DocumentGuard& _rDocGuard + ); + + /** throws an IOException with the message as defined in the RID_STR_ERROR_WHILE_SAVING resource, wrapping + the given caught non-IOException error + */ + void impl_throwIOExceptionCausedBySave_throw( + const css::uno::Any& i_rError, + std::u16string_view i_rTargetURL + ) const; +}; + +/** an extended version of the ModelMethodGuard, which also cares for the initialization state + of the document +*/ +class DocumentGuard : private ModelMethodGuard +{ +public: + enum InitMethod_ + { + // a method which is to initialize the document + InitMethod, + }; + + enum DefaultMethod_ + { + // a default method + DefaultMethod + }; + + enum MethodUsedDuringInit_ + { + // a method which is used (externally) during the initialization phase + MethodUsedDuringInit + }; + + enum MethodWithoutInit_ + { + // a method which does not need initialization - use with care! + MethodWithoutInit + }; + + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::lang::NotInitializedException + if the given component is not yet initialized + */ + DocumentGuard(const ODatabaseDocument& _document, DefaultMethod_) + : ModelMethodGuard(_document) + , m_document(_document ) + { + m_document.checkInitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::frame::DoubleInitializationException + if the given component is already initialized, or currently being initialized. + */ + DocumentGuard(const ODatabaseDocument& _document, InitMethod_) + : ModelMethodGuard(_document) + , m_document(_document) + { + m_document.checkNotInitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::lang::NotInitializedException + if the component is still uninitialized, and not in the initialization + phase currently. + */ + DocumentGuard(const ODatabaseDocument& _document, MethodUsedDuringInit_) + : ModelMethodGuard(_document) + , m_document(_document) + { + m_document.checkNotUninitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + */ + DocumentGuard(const ODatabaseDocument& _document, MethodWithoutInit_) + : ModelMethodGuard( _document ) + , m_document( _document ) + { + } + + void clear() + { + ModelMethodGuard::clear(); + } + void reset() + { + ModelMethodGuard::reset(); + m_document.checkDisposed(); + } + +private: + + const ODatabaseDocument& m_document; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databaseregistrations.cxx b/dbaccess/source/core/dataaccess/databaseregistrations.cxx new file mode 100644 index 000000000..40fa526d1 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databaseregistrations.cxx @@ -0,0 +1,369 @@ +/* -*- 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 <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/sdb/XDatabaseRegistrations.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase1.hxx> +#include <osl/diagnose.h> +#include <unotools/pathoptions.hxx> +#include <tools/urlobj.hxx> +#include <unotools/confignode.hxx> + +#include "databaseregistrations.hxx" + +namespace dbaccess +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::lang::IllegalAccessException; + using ::com::sun::star::container::ElementExistException; + using ::com::sun::star::sdb::XDatabaseRegistrations; + using ::com::sun::star::sdb::XDatabaseRegistrationsListener; + using ::com::sun::star::sdb::DatabaseRegistrationEvent; + using ::com::sun::star::uno::XAggregation; + + static OUString getConfigurationRootPath() + { + return "org.openoffice.Office.DataAccess/RegisteredNames"; + } + + static OUString getLocationNodeName() + { + return "Location"; + } + + static OUString getNameNodeName() + { + return "Name"; + } + + // DatabaseRegistrations - declaration + typedef ::cppu::WeakAggImplHelper1 < XDatabaseRegistrations + > DatabaseRegistrations_Base; + + namespace { + + class DatabaseRegistrations :public ::cppu::BaseMutex + ,public DatabaseRegistrations_Base + { + public: + explicit DatabaseRegistrations( const Reference<XComponentContext>& _rxContext ); + + protected: + virtual ~DatabaseRegistrations() override; + + public: + virtual sal_Bool SAL_CALL hasRegisteredDatabase( const OUString& Name ) override; + virtual Sequence< OUString > SAL_CALL getRegistrationNames() override; + virtual OUString SAL_CALL getDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL registerDatabaseLocation( const OUString& Name, const OUString& Location ) override; + virtual void SAL_CALL revokeDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) override; + virtual sal_Bool SAL_CALL isDatabaseRegistrationReadOnly( const OUString& Name ) override; + virtual void SAL_CALL addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override; + virtual void SAL_CALL removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override; + + private: + void + impl_checkValidName_common(std::u16string_view _rName); + ::utl::OConfigurationNode + impl_checkValidName_throw_must_exist(const OUString& _rName); + ::utl::OConfigurationNode + impl_checkValidName_throw_must_not_exist(const OUString& _rName); + + void impl_checkValidLocation_throw( std::u16string_view _rLocation ); + + /** retrieves the configuration node whose "Name" sub node has the given value + + Since we separated the name of the registration node from the "Name" value of the registration, we cannot + simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name. + Instead, we must search all nodes. + + If a node with the given display name does not exist, then a NoSuchElementException is thrown. + + If no exception is thrown, then a valid node is returned: If the node existed it is returned. + */ + ::utl::OConfigurationNode + impl_getNodeForName_throw_must_exist(const OUString& _rName); + + /** retrieves the configuration node whose "Name" sub node has the given value + + Since we separated the name of the registration node from the "Name" value of the registration, we cannot + simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name. + Instead, we must search all nodes. + + If a node with the given name already exists, then an ElementExistException is thrown. + + If no exception is thrown, then a valid node is returned: If the node did not yet exist a new node is created, + in this case the root node is not yet committed. + */ + ::utl::OConfigurationNode + impl_getNodeForName_throw_must_not_exist(const OUString& _rName); + + + ::utl::OConfigurationNode + impl_getNodeForName_nothrow(std::u16string_view _rName); + + private: + Reference<XComponentContext> m_aContext; + ::utl::OConfigurationTreeRoot m_aConfigurationRoot; + ::comphelper::OInterfaceContainerHelper3<XDatabaseRegistrationsListener> m_aRegistrationListeners; + }; + + } + + // DatabaseRegistrations - implementation + DatabaseRegistrations::DatabaseRegistrations( const Reference<XComponentContext> & _rxContext ) + :m_aContext( _rxContext ) + ,m_aRegistrationListeners( m_aMutex ) + { + m_aConfigurationRoot = ::utl::OConfigurationTreeRoot::createWithComponentContext( + m_aContext, getConfigurationRootPath() ); + } + + DatabaseRegistrations::~DatabaseRegistrations() + { + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_nothrow( std::u16string_view _rName ) + { + const Sequence< OUString > aNames( m_aConfigurationRoot.getNodeNames() ); + for ( auto const & nodeName : aNames ) + { + ::utl::OConfigurationNode aNodeForName = m_aConfigurationRoot.openNode( nodeName ); + + OUString sTestName; + OSL_VERIFY( aNodeForName.getNodeValue( getNameNodeName() ) >>= sTestName ); + if ( sTestName == _rName ) + return aNodeForName; + } + return ::utl::OConfigurationNode(); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_exist(const OUString& _rName) + { + ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) ); + + if (!aNodeForName.isValid()) + { + throw NoSuchElementException( _rName, *this ); + } + + return aNodeForName; + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_not_exist(const OUString& _rName) + { + ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) ); + + if (aNodeForName.isValid()) + throw ElementExistException( _rName, *this ); + + // make unique + OUString sNewNodeName = "org.openoffice." + _rName; + while ( m_aConfigurationRoot.hasByName( sNewNodeName ) ) + { + sNewNodeName = "org.openoffice." + _rName + " 2"; + } + + ::utl::OConfigurationNode aNewNode( m_aConfigurationRoot.createNode( sNewNodeName ) ); + aNewNode.setNodeValue( getNameNodeName(), Any( _rName ) ); + return aNewNode; + } + + void DatabaseRegistrations::impl_checkValidName_common(std::u16string_view _rName) + { + if ( !m_aConfigurationRoot.isValid() ) + throw RuntimeException( OUString(), *this ); + + if ( _rName.empty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_exist(const OUString& _rName) + { + impl_checkValidName_common(_rName); + return impl_getNodeForName_throw_must_exist(_rName); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_not_exist(const OUString& _rName) + { + impl_checkValidName_common(_rName); + return impl_getNodeForName_throw_must_not_exist(_rName); + } + + void DatabaseRegistrations::impl_checkValidLocation_throw( std::u16string_view _rLocation ) + { + if ( _rLocation.empty() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + INetURLObject aURL( _rLocation ); + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + throw IllegalArgumentException( OUString(), *this, 2 ); + } + + sal_Bool SAL_CALL DatabaseRegistrations::hasRegisteredDatabase( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::utl::OConfigurationNode aNodeForName = impl_getNodeForName_nothrow( Name ); + return aNodeForName.isValid(); + } + + Sequence< OUString > SAL_CALL DatabaseRegistrations::getRegistrationNames() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_aConfigurationRoot.isValid() ) + throw RuntimeException( OUString(), *this ); + + const Sequence< OUString > aProgrammaticNames( m_aConfigurationRoot.getNodeNames() ); + Sequence< OUString > aDisplayNames( aProgrammaticNames.getLength() ); + OUString* pDisplayName = aDisplayNames.getArray(); + + for ( auto const & name : aProgrammaticNames ) + { + ::utl::OConfigurationNode aRegistrationNode = m_aConfigurationRoot.openNode( name ); + OSL_VERIFY( aRegistrationNode.getNodeValue( getNameNodeName() ) >>= *pDisplayName ); + ++pDisplayName; + } + + return aDisplayNames; + } + + OUString SAL_CALL DatabaseRegistrations::getDatabaseLocation( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name); + + OUString sLocation; + OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation ); + sLocation = SvtPathOptions().SubstituteVariable( sLocation ); + + return sLocation; + } + + void SAL_CALL DatabaseRegistrations::registerDatabaseLocation( const OUString& Name, const OUString& Location ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + impl_checkValidLocation_throw( Location ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_not_exist(Name); + + // register + aDataSourceRegistration.setNodeValue( getLocationNodeName(), Any( Location ) ); + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, OUString(), Location ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::registeredDatabaseLocation, aEvent ); + } + + void SAL_CALL DatabaseRegistrations::revokeDatabaseLocation( const OUString& Name ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name); + + // obtain properties for notification + OUString sLocation; + OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation ); + + // revoke + if ( aNodeForName.isReadonly() + || !m_aConfigurationRoot.removeNode( aNodeForName.getLocalName() ) + ) + throw IllegalAccessException( OUString(), *this ); + + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, sLocation, OUString() ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::revokedDatabaseLocation, aEvent ); + } + + void SAL_CALL DatabaseRegistrations::changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + impl_checkValidLocation_throw( NewLocation ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name); + + if ( aDataSourceRegistration.isReadonly() ) + throw IllegalAccessException( OUString(), *this ); + + // obtain properties for notification + OUString sOldLocation; + OSL_VERIFY( aDataSourceRegistration.getNodeValue( getLocationNodeName() ) >>= sOldLocation ); + + // change + aDataSourceRegistration.setNodeValue( getLocationNodeName(), Any( NewLocation ) ); + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, sOldLocation, NewLocation ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::changedDatabaseLocation, aEvent ); + } + + sal_Bool SAL_CALL DatabaseRegistrations::isDatabaseRegistrationReadOnly( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name); + return aDataSourceRegistration.isReadonly(); + } + + void SAL_CALL DatabaseRegistrations::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) + { + if ( Listener.is() ) + m_aRegistrationListeners.addInterface( Listener ); + } + + void SAL_CALL DatabaseRegistrations::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) + { + if ( Listener.is() ) + m_aRegistrationListeners.removeInterface( Listener ); + } + + // DatabaseRegistrations - factory + Reference< XAggregation > createDataSourceRegistrations( const Reference<XComponentContext> & _rxContext ) + { + return new DatabaseRegistrations( _rxContext ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databaseregistrations.hxx b/dbaccess/source/core/dataaccess/databaseregistrations.hxx new file mode 100644 index 000000000..709bc9f98 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databaseregistrations.hxx @@ -0,0 +1,32 @@ +/* -*- 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/uno/XAggregation.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace dbaccess +{ +css::uno::Reference<css::uno::XAggregation> +createDataSourceRegistrations(const css::uno::Reference<css::uno::XComponentContext>& _rxContext); + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/datasource.cxx b/dbaccess/source/core/dataaccess/datasource.cxx new file mode 100644 index 000000000..d88b661c9 --- /dev/null +++ b/dbaccess/source/core/dataaccess/datasource.cxx @@ -0,0 +1,1437 @@ +/* -*- 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 "datasource.hxx" +#include "commandcontainer.hxx" +#include <stringconstants.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include "connection.hxx" +#include "SharedConnection.hxx" +#include "databasedocument.hxx" +#include <OAuthenticationContinuation.hxx> + +#include <hsqlimport.hxx> +#include <migrwarndlg.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/document/XDocumentSubStorageSupplier.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/reflection/ProxyFactory.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbc/ConnectionPool.hpp> +#include <com/sun/star/sdbc/XDriverAccess.hpp> +#include <com/sun/star/sdbc/XDriverManager.hpp> +#include <com/sun/star/sdbc/DriverManager.hpp> +#include <com/sun/star/ucb/AuthenticationRequest.hpp> +#include <com/sun/star/ucb/XInteractionSupplyAuthentication.hpp> + +#include <cppuhelper/implbase.hxx> +#include <comphelper/interaction.hxx> +#include <comphelper/property.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <officecfg/Office/Common.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <osl/process.h> +#include <sal/log.hxx> +#include <tools/urlobj.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <rtl/digest.h> + +#include <algorithm> +#include <iterator> +#include <set> + +#include <config_firebird.h> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +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::embed; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::reflection; +using namespace ::cppu; +using namespace ::osl; +using namespace ::dbtools; +using namespace ::comphelper; + +namespace dbaccess +{ + +namespace { + +/** helper class which implements a XFlushListener, and forwards all + notification events to another XFlushListener + + The speciality is that the foreign XFlushListener instance, to which + the notifications are forwarded, is held weak. + + Thus, the class can be used with XFlushable instance which hold + their listeners with a hard reference, if you simply do not *want* + to be held hard-ref-wise. +*/ +class FlushNotificationAdapter : public ::cppu::WeakImplHelper< XFlushListener > +{ +private: + WeakReference< XFlushable > m_aBroadcaster; + WeakReference< XFlushListener > m_aListener; + +public: + static void installAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ) + { + new FlushNotificationAdapter( _rxBroadcaster, _rxListener ); + } + +protected: + FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ); + virtual ~FlushNotificationAdapter() override; + + void impl_dispose(); + +protected: + // XFlushListener + virtual void SAL_CALL flushed( const css::lang::EventObject& rEvent ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +} + +FlushNotificationAdapter::FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ) + :m_aBroadcaster( _rxBroadcaster ) + ,m_aListener( _rxListener ) +{ + OSL_ENSURE( _rxBroadcaster.is(), "FlushNotificationAdapter::FlushNotificationAdapter: invalid flushable!" ); + + osl_atomic_increment( &m_refCount ); + { + if ( _rxBroadcaster.is() ) + _rxBroadcaster->addFlushListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount == 1, "FlushNotificationAdapter::FlushNotificationAdapter: broadcaster isn't holding by hard ref!?" ); +} + +FlushNotificationAdapter::~FlushNotificationAdapter() +{ +} + +void FlushNotificationAdapter::impl_dispose() +{ + Reference< XFlushListener > xKeepAlive( this ); + + Reference< XFlushable > xFlushable( m_aBroadcaster ); + if ( xFlushable.is() ) + xFlushable->removeFlushListener( this ); + + m_aListener.clear(); + m_aBroadcaster.clear(); +} + +void SAL_CALL FlushNotificationAdapter::flushed( const EventObject& rEvent ) +{ + Reference< XFlushListener > xListener( m_aListener ); + if ( xListener.is() ) + xListener->flushed( rEvent ); + else + impl_dispose(); +} + +void SAL_CALL FlushNotificationAdapter::disposing( const EventObject& Source ) +{ + Reference< XFlushListener > xListener( m_aListener ); + if ( xListener.is() ) + xListener->disposing( Source ); + + impl_dispose(); +} + +OAuthenticationContinuation::OAuthenticationContinuation() + :m_bRememberPassword(true), // TODO: a meaningful default + m_bCanSetUserName(true) +{ +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetRealm( ) +{ + return false; +} + +void SAL_CALL OAuthenticationContinuation::setRealm( const OUString& /*Realm*/ ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setRealm: not supported!"); +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetUserName( ) +{ + // we always allow this, even if the database document is read-only. In this case, + // it's simply that the user cannot store the new user name. + return m_bCanSetUserName; +} + +void SAL_CALL OAuthenticationContinuation::setUserName( const OUString& _rUser ) +{ + m_sUser = _rUser; +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetPassword( ) +{ + return true; +} + +void SAL_CALL OAuthenticationContinuation::setPassword( const OUString& _rPassword ) +{ + m_sPassword = _rPassword; +} + +Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberPasswordModes( RememberAuthentication& _reDefault ) +{ + _reDefault = RememberAuthentication_SESSION; + return { _reDefault }; +} + +void SAL_CALL OAuthenticationContinuation::setRememberPassword( RememberAuthentication _eRemember ) +{ + m_bRememberPassword = (RememberAuthentication_NO != _eRemember); +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetAccount( ) +{ + return false; +} + +void SAL_CALL OAuthenticationContinuation::setAccount( const OUString& ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setAccount: not supported!"); +} + +Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberAccountModes( RememberAuthentication& _reDefault ) +{ + _reDefault = RememberAuthentication_NO; + return { RememberAuthentication_NO }; +} + +void SAL_CALL OAuthenticationContinuation::setRememberAccount( RememberAuthentication /*Remember*/ ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setRememberAccount: not supported!"); +} + +namespace { + +/** The class OSharedConnectionManager implements a structure to share connections. + It owns the master connections which will be disposed when the last connection proxy is gone. +*/ +// need to hold the digest +struct TDigestHolder +{ + sal_uInt8 m_pBuffer[RTL_DIGEST_LENGTH_SHA1]; + TDigestHolder() + { + m_pBuffer[0] = 0; + } + +}; + +} + +class OSharedConnectionManager : public ::cppu::WeakImplHelper< XEventListener > +{ + + // contains the currently used master connections + struct TConnectionHolder + { + Reference< XConnection > xMasterConnection; + oslInterlockedCount nALiveCount; + }; + + // the less-compare functor, used for the stl::map + struct TDigestLess + { + bool operator() (const TDigestHolder& x, const TDigestHolder& y) const + { + sal_uInt32 i; + for(i=0;i < RTL_DIGEST_LENGTH_SHA1 && (x.m_pBuffer[i] >= y.m_pBuffer[i]); ++i) + ; + return i < RTL_DIGEST_LENGTH_SHA1; + } + }; + + typedef std::map< TDigestHolder,TConnectionHolder,TDigestLess> TConnectionMap; // holds the master connections + typedef std::map< Reference< XConnection >,TConnectionMap::iterator> TSharedConnectionMap;// holds the shared connections + + ::osl::Mutex m_aMutex; + TConnectionMap m_aConnections; // remember the master connection in conjunction with the digest + TSharedConnectionMap m_aSharedConnection; // the shared connections with conjunction with an iterator into the connections map + Reference< XProxyFactory > m_xProxyFactory; + +protected: + virtual ~OSharedConnectionManager() override; + +public: + explicit OSharedConnectionManager(const Reference< XComponentContext >& _rxContext); + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + Reference<XConnection> getConnection( const OUString& url, + const OUString& user, + const OUString& password, + const Sequence< PropertyValue >& _aInfo, + ODatabaseSource* _pDataSource); + void addEventListener(const Reference<XConnection>& _rxConnection, TConnectionMap::iterator const & _rIter); +}; + +OSharedConnectionManager::OSharedConnectionManager(const Reference< XComponentContext >& _rxContext) +{ + m_xProxyFactory.set( ProxyFactory::create( _rxContext ) ); +} + +OSharedConnectionManager::~OSharedConnectionManager() +{ +} + +void SAL_CALL OSharedConnectionManager::disposing( const css::lang::EventObject& Source ) +{ + MutexGuard aGuard(m_aMutex); + Reference<XConnection> xConnection(Source.Source,UNO_QUERY); + TSharedConnectionMap::const_iterator aFind = m_aSharedConnection.find(xConnection); + if ( m_aSharedConnection.end() != aFind ) + { + osl_atomic_decrement(&aFind->second->second.nALiveCount); + if ( !aFind->second->second.nALiveCount ) + { + ::comphelper::disposeComponent(aFind->second->second.xMasterConnection); + m_aConnections.erase(aFind->second); + } + m_aSharedConnection.erase(aFind); + } +} + +Reference<XConnection> OSharedConnectionManager::getConnection( const OUString& url, + const OUString& user, + const OUString& password, + const Sequence< PropertyValue >& _aInfo, + ODatabaseSource* _pDataSource) +{ + MutexGuard aGuard(m_aMutex); + TConnectionMap::key_type nId; + Sequence< PropertyValue > aInfoCopy(_aInfo); + sal_Int32 nPos = aInfoCopy.getLength(); + aInfoCopy.realloc( nPos + 2 ); + auto pInfoCopy = aInfoCopy.getArray(); + pInfoCopy[nPos].Name = "TableFilter"; + pInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableFilter; + pInfoCopy[nPos].Name = "TableTypeFilter"; + pInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableTypeFilter; + + OUString sUser = user; + OUString sPassword = password; + if ((sUser.isEmpty()) && (sPassword.isEmpty()) && (!_pDataSource->m_pImpl->m_sUser.isEmpty())) + { // ease the usage of this method. data source which are intended to have a user automatically + // fill in the user/password combination if the caller of this method does not specify otherwise + sUser = _pDataSource->m_pImpl->m_sUser; + if (!_pDataSource->m_pImpl->m_aPassword.isEmpty()) + sPassword = _pDataSource->m_pImpl->m_aPassword; + } + + ::connectivity::OConnectionWrapper::createUniqueId(url,aInfoCopy,nId.m_pBuffer,sUser,sPassword); + TConnectionMap::iterator aIter = m_aConnections.find(nId); + + if ( m_aConnections.end() == aIter ) + { + TConnectionHolder aHolder; + aHolder.nALiveCount = 0; // will be incremented by addListener + aHolder.xMasterConnection = _pDataSource->buildIsolatedConnection(user,password); + aIter = m_aConnections.emplace(nId,aHolder).first; + } + + Reference<XConnection> xRet; + if ( aIter->second.xMasterConnection.is() ) + { + Reference< XAggregation > xConProxy = m_xProxyFactory->createProxy(aIter->second.xMasterConnection); + xRet = new OSharedConnection(xConProxy); + m_aSharedConnection.emplace(xRet,aIter); + addEventListener(xRet,aIter); + } + + return xRet; +} + +void OSharedConnectionManager::addEventListener(const Reference<XConnection>& _rxConnection, TConnectionMap::iterator const & _rIter) +{ + Reference<XComponent> xComp(_rxConnection,UNO_QUERY); + xComp->addEventListener(this); + OSL_ENSURE( m_aConnections.end() != _rIter , "Iterator is end!"); + osl_atomic_increment(&_rIter->second.nALiveCount); +} + +namespace +{ + Sequence< PropertyValue > lcl_filterDriverProperties( const Reference< XDriver >& _xDriver, const OUString& _sUrl, + const Sequence< PropertyValue >& _rDataSourceSettings, const AsciiPropertyValue* _pKnownSettings ) + { + if ( _xDriver.is() ) + { + Sequence< DriverPropertyInfo > aDriverInfo(_xDriver->getPropertyInfo(_sUrl,_rDataSourceSettings)); + + const PropertyValue* pDataSourceSetting = _rDataSourceSettings.getConstArray(); + const PropertyValue* pEnd = pDataSourceSetting + _rDataSourceSettings.getLength(); + + std::vector< PropertyValue > aRet; + + for ( ; pDataSourceSetting != pEnd ; ++pDataSourceSetting ) + { + bool bAllowSetting = false; + const AsciiPropertyValue* pSetting = _pKnownSettings; + for ( ; pSetting->AsciiName; ++pSetting ) + { + if ( pDataSourceSetting->Name.equalsAscii( pSetting->AsciiName ) ) + { // the particular data source setting is known + + const DriverPropertyInfo* pAllowedDriverSetting = aDriverInfo.getConstArray(); + const DriverPropertyInfo* pDriverSettingsEnd = pAllowedDriverSetting + aDriverInfo.getLength(); + for ( ; pAllowedDriverSetting != pDriverSettingsEnd; ++pAllowedDriverSetting ) + { + if ( pAllowedDriverSetting->Name.equalsAscii( pSetting->AsciiName ) ) + { // the driver also allows this setting + bAllowSetting = true; + break; + } + } + break; + } + } + if ( bAllowSetting || !pSetting->AsciiName ) + { // if the driver allows this particular setting, or if the setting is completely unknown, + // we pass it to the driver + aRet.push_back( *pDataSourceSetting ); + } + } + if ( !aRet.empty() ) + return comphelper::containerToSequence(aRet); + } + return Sequence< PropertyValue >(); + } + + typedef std::map< OUString, sal_Int32 > PropertyAttributeCache; + + struct IsDefaultAndNotRemoveable + { + private: + const PropertyAttributeCache& m_rAttribs; + + public: + explicit IsDefaultAndNotRemoveable( const PropertyAttributeCache& _rAttribs ) : m_rAttribs( _rAttribs ) { } + + bool operator()( const PropertyValue& _rProp ) + { + if ( _rProp.State != PropertyState_DEFAULT_VALUE ) + return false; + + bool bRemoveable = true; + + PropertyAttributeCache::const_iterator pos = m_rAttribs.find( _rProp.Name ); + OSL_ENSURE( pos != m_rAttribs.end(), "IsDefaultAndNotRemoveable: illegal property name!" ); + if ( pos != m_rAttribs.end() ) + bRemoveable = ( ( pos->second & PropertyAttribute::REMOVABLE ) != 0 ); + + return !bRemoveable; + } + }; +} + + +ODatabaseSource::ODatabaseSource(const ::rtl::Reference<ODatabaseModelImpl>& _pImpl) + :ModelDependentComponent( _pImpl ) + ,ODatabaseSource_Base( getMutex() ) + ,OPropertySetHelper( ODatabaseSource_Base::rBHelper ) + , m_Bookmarks(*this, getMutex()) + ,m_aFlushListeners( getMutex() ) +{ + // some kind of default + SAL_INFO("dbaccess", "DS: ctor: " << std::hex << this << ": " << std::hex << m_pImpl.get() ); +} + +ODatabaseSource::~ODatabaseSource() +{ + SAL_INFO("dbaccess", "DS: dtor: " << std::hex << this << ": " << std::hex << m_pImpl.get() ); + if ( !ODatabaseSource_Base::rBHelper.bInDispose && !ODatabaseSource_Base::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +void ODatabaseSource::setName( const Reference< XDocumentDataSource >& _rxDocument, const OUString& _rNewName, DBContextAccess ) +{ + ODatabaseSource& rModelImpl = dynamic_cast< ODatabaseSource& >( *_rxDocument ); + + SolarMutexGuard g; + if ( rModelImpl.m_pImpl.is() ) + rModelImpl.m_pImpl->m_sName = _rNewName; +} + +// css::lang::XTypeProvider +Sequence< Type > ODatabaseSource::getTypes() +{ + OTypeCollection aPropertyHelperTypes( cppu::UnoType<XFastPropertySet>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XMultiPropertySet>::get()); + + return ::comphelper::concatSequences( + ODatabaseSource_Base::getTypes(), + aPropertyHelperTypes.getTypes() + ); +} + +Sequence< sal_Int8 > ODatabaseSource::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// css::uno::XInterface +Any ODatabaseSource::queryInterface( const Type & rType ) +{ + Any aIface = ODatabaseSource_Base::queryInterface( rType ); + if ( !aIface.hasValue() ) + aIface = ::cppu::OPropertySetHelper::queryInterface( rType ); + return aIface; +} + +void ODatabaseSource::acquire() noexcept +{ + ODatabaseSource_Base::acquire(); +} + +void ODatabaseSource::release() noexcept +{ + ODatabaseSource_Base::release(); +} + +void SAL_CALL ODatabaseSource::disposing( const css::lang::EventObject& Source ) +{ + if ( m_pImpl.is() ) + m_pImpl->disposing(Source); +} + +// XServiceInfo +OUString ODatabaseSource::getImplementationName( ) +{ + return "com.sun.star.comp.dba.ODatabaseSource"; +} + +Sequence< OUString > ODatabaseSource::getSupportedServiceNames( ) +{ + return { SERVICE_SDB_DATASOURCE, "com.sun.star.sdb.DocumentDataSource" }; +} + +sal_Bool ODatabaseSource::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +// OComponentHelper +void ODatabaseSource::disposing() +{ + SAL_INFO("dbaccess", "DS: disp: " << std::hex << this << ", " << std::hex << m_pImpl.get() ); + ODatabaseSource_Base::WeakComponentImplHelperBase::disposing(); + OPropertySetHelper::disposing(); + + EventObject aDisposeEvent(static_cast<XWeak*>(this)); + m_aFlushListeners.disposeAndClear( aDisposeEvent ); + + ODatabaseDocument::clearObjectContainer(m_pImpl->m_xCommandDefinitions); + ODatabaseDocument::clearObjectContainer(m_pImpl->m_xTableDefinitions); + m_pImpl.clear(); +} + +weld::Window* ODatabaseModelImpl::GetFrameWeld() +{ + if (m_xDialogParent.is()) + return Application::GetFrameWeld(m_xDialogParent); + + Reference<XModel> xModel = getModel_noCreate(); + if (!xModel.is()) + return nullptr; + Reference<XController> xController(xModel->getCurrentController()); + if (!xController.is()) + return nullptr; + Reference<XFrame> xFrame(xController->getFrame()); + if (!xFrame.is()) + return nullptr; + Reference<css::awt::XWindow> xWindow(xFrame->getContainerWindow()); + return Application::GetFrameWeld(xWindow); +} + +Reference< XConnection > ODatabaseSource::buildLowLevelConnection(const OUString& _rUid, const OUString& _rPwd) +{ + Reference< XConnection > xReturn; + + Reference< XDriverManager > xManager; + +#if ENABLE_FIREBIRD_SDBC + bool bIgnoreMigration = false; + bool bNeedMigration = false; + Reference< XModel > xModel = m_pImpl->getModel_noCreate(); + if ( xModel) + { + //See ODbTypeWizDialogSetup::SaveDatabaseDocument + ::comphelper::NamedValueCollection::get(xModel->getArgs(), u"IgnoreFirebirdMigration") >>= bIgnoreMigration; + } + else + { + //ignore when we don't have a model. E.g. Mailmerge, data sources, fields... + bIgnoreMigration = true; + } + + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + bIgnoreMigration = true; + + if(!bIgnoreMigration && m_pImpl->m_sConnectURL == "sdbc:embedded:hsqldb") + { + Reference<XStorage> const xRootStorage = m_pImpl->getOrCreateRootStorage(); + OUString sMigrEnvVal; + osl_getEnvironment(OUString("DBACCESS_HSQL_MIGRATION").pData, + &sMigrEnvVal.pData); + if(!sMigrEnvVal.isEmpty()) + bNeedMigration = true; + else + { + Reference<XPropertySet> const xPropSet(xRootStorage, UNO_QUERY_THROW); + sal_Int32 nOpenMode(0); + if ((xPropSet->getPropertyValue("OpenMode") >>= nOpenMode) + && (nOpenMode & css::embed::ElementModes::WRITE) + && (!Application::IsHeadlessModeEnabled())) + { + MigrationWarnDialog aWarnDlg(m_pImpl->GetFrameWeld()); + bNeedMigration = aWarnDlg.run() == RET_OK; + } + } + if (bNeedMigration) + { + // back up content xml file if migration was successful + static constexpr OUStringLiteral BACKUP_XML_NAME = u"content_before_migration.xml"; + try + { + if(xRootStorage->isStreamElement(BACKUP_XML_NAME)) + xRootStorage->removeElement(BACKUP_XML_NAME); + } + catch (NoSuchElementException&) + { + SAL_INFO("dbaccess", "No file content_before_migration.xml found" ); + } + xRootStorage->copyElementTo("content.xml", xRootStorage, + BACKUP_XML_NAME); + + m_pImpl->m_sConnectURL = "sdbc:embedded:firebird"; + } + } +#endif + + try { + xManager.set( ConnectionPool::create( m_pImpl->m_aContext ), UNO_QUERY_THROW ); + } catch( const Exception& ) { } + if ( !xManager.is() ) + // no connection pool installed, fall back to driver manager + xManager.set( DriverManager::create(m_pImpl->m_aContext ), UNO_QUERY_THROW ); + + OUString sUser(_rUid); + OUString sPwd(_rPwd); + if ((sUser.isEmpty()) && (sPwd.isEmpty()) && (!m_pImpl->m_sUser.isEmpty())) + { // ease the usage of this method. data source which are intended to have a user automatically + // fill in the user/password combination if the caller of this method does not specify otherwise + sUser = m_pImpl->m_sUser; + if (!m_pImpl->m_aPassword.isEmpty()) + sPwd = m_pImpl->m_aPassword; + } + + TranslateId pExceptionMessageId = RID_STR_COULDNOTCONNECT_UNSPECIFIED; + if (xManager.is()) + { + sal_Int32 nAdditionalArgs(0); + if (!sUser.isEmpty()) ++nAdditionalArgs; + if (!sPwd.isEmpty()) ++nAdditionalArgs; + + Sequence< PropertyValue > aUserPwd(nAdditionalArgs); + auto aUserPwdRange = asNonConstRange(aUserPwd); + sal_Int32 nArgPos = 0; + if (!sUser.isEmpty()) + { + aUserPwdRange[ nArgPos ].Name = "user"; + aUserPwdRange[ nArgPos ].Value <<= sUser; + ++nArgPos; + } + if (!sPwd.isEmpty()) + { + aUserPwdRange[ nArgPos ].Name = "password"; + aUserPwdRange[ nArgPos ].Value <<= sPwd; + } + Reference< XDriver > xDriver; + try + { + + // choose driver + Reference< XDriverAccess > xAccessDrivers( xManager, UNO_QUERY ); + if ( xAccessDrivers.is() ) + xDriver = xAccessDrivers->getDriverByURL( m_pImpl->m_sConnectURL ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("dbaccess", "ODatabaseSource::buildLowLevelConnection: got a strange exception while analyzing the error" ); + } + if ( !xDriver.is() || !xDriver->acceptsURL( m_pImpl->m_sConnectURL ) ) + { + // Nowadays, it's allowed for a driver to be registered for a given URL, but actually not to accept it. + // This is because registration nowadays happens at compile time (by adding respective configuration data), + // but acceptance is decided at runtime. + pExceptionMessageId = RID_STR_COULDNOTCONNECT_NODRIVER; + } + else + { + Sequence< PropertyValue > aDriverInfo = lcl_filterDriverProperties( + xDriver, + m_pImpl->m_sConnectURL, + m_pImpl->m_xSettings->getPropertyValues(), + dbaccess::ODatabaseModelImpl::getDefaultDataSourceSettings() + ); + + if ( m_pImpl->isEmbeddedDatabase() ) + { + sal_Int32 nCount = aDriverInfo.getLength(); + aDriverInfo.realloc(nCount + 3 ); + auto pDriverInfo = aDriverInfo.getArray(); + + pDriverInfo[nCount].Name = "URL"; + pDriverInfo[nCount++].Value <<= m_pImpl->getURL(); + + pDriverInfo[nCount].Name = "Storage"; + Reference< css::document::XDocumentSubStorageSupplier> xDocSup( m_pImpl->getDocumentSubStorageSupplier() ); + pDriverInfo[nCount++].Value <<= xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE); + + pDriverInfo[nCount].Name = "Document"; + pDriverInfo[nCount++].Value <<= getDatabaseDocument(); + } + if (nAdditionalArgs) + xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL, ::comphelper::concatSequences(aUserPwd,aDriverInfo)); + else + xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL,aDriverInfo); + + if ( m_pImpl->isEmbeddedDatabase() ) + { + // see ODatabaseSource::flushed for comment on why we register as FlushListener + // at the connection + Reference< XFlushable > xFlushable( xReturn, UNO_QUERY ); + if ( xFlushable.is() ) + FlushNotificationAdapter::installAdapter( xFlushable, this ); + } + } + } + else + pExceptionMessageId = RID_STR_COULDNOTLOAD_MANAGER; + + if ( !xReturn.is() ) + { + OUString sMessage = DBA_RES(pExceptionMessageId) + .replaceAll("$name$", m_pImpl->m_sConnectURL); + + SQLContext aContext; + aContext.Message = DBA_RES(RID_STR_CONNECTION_REQUEST). + replaceFirst("$name$", m_pImpl->m_sConnectURL); + + throwGenericSQLException( sMessage, static_cast< XDataSource* >( this ), Any( aContext ) ); + } + +#if ENABLE_FIREBIRD_SDBC + if( bNeedMigration ) + { + Reference< css::document::XDocumentSubStorageSupplier> xDocSup( + m_pImpl->getDocumentSubStorageSupplier() ); + dbahsql::HsqlImporter importer(xReturn, + xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE) ); + importer.importHsqlDatabase(m_pImpl->GetFrameWeld()); + } +#endif + + return xReturn; +} + +// OPropertySetHelper +Reference< XPropertySetInfo > ODatabaseSource::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ODatabaseSource::createArrayHelper( ) const +{ + return new ::cppu::OPropertyArrayHelper + { + { + { PROPERTY_INFO, PROPERTY_ID_INFO, cppu::UnoType<Sequence< PropertyValue >>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISPASSWORDREQUIRED, PROPERTY_ID_ISPASSWORDREQUIRED, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, cppu::UnoType<Sequence< PropertyValue >>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_NUMBERFORMATSSUPPLIER, PROPERTY_ID_NUMBERFORMATSSUPPLIER, cppu::UnoType<XNumberFormatsSupplier>::get(), + css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_PASSWORD, PROPERTY_ID_PASSWORD, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_SETTINGS, PROPERTY_ID_SETTINGS, cppu::UnoType<XPropertySet>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SUPPRESSVERSIONCL, PROPERTY_ID_SUPPRESSVERSIONCL, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLEFILTER, PROPERTY_ID_TABLEFILTER, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLETYPEFILTER, PROPERTY_ID_TABLETYPEFILTER, cppu::UnoType<Sequence< OUString >>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_URL, PROPERTY_ID_URL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_USER, PROPERTY_ID_USER, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND } + } + }; +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& ODatabaseSource::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool ODatabaseSource::convertFastPropertyValue(Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + bool bModified(false); + if ( m_pImpl.is() ) + { + switch (nHandle) + { + case PROPERTY_ID_TABLEFILTER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableFilter); + break; + case PROPERTY_ID_TABLETYPEFILTER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableTypeFilter); + break; + case PROPERTY_ID_USER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sUser); + break; + case PROPERTY_ID_PASSWORD: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aPassword); + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bPasswordRequired); + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bSuppressVersionColumns); + break; + case PROPERTY_ID_LAYOUTINFORMATION: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aLayoutInformation); + break; + case PROPERTY_ID_URL: + { + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sConnectURL); + } break; + case PROPERTY_ID_INFO: + { + Sequence<PropertyValue> aValues; + if (!(rValue >>= aValues)) + throw IllegalArgumentException(); + + for ( auto const & checkName : std::as_const(aValues) ) + { + if ( checkName.Name.isEmpty() ) + throw IllegalArgumentException(); + } + + Sequence< PropertyValue > aSettings = m_pImpl->m_xSettings->getPropertyValues(); + bModified = aSettings.getLength() != aValues.getLength(); + if ( !bModified ) + { + const PropertyValue* pInfoIter = aSettings.getConstArray(); + const PropertyValue* checkValue = aValues.getConstArray(); + for ( ;!bModified && checkValue != std::cend(aValues) ; ++checkValue,++pInfoIter) + { + bModified = checkValue->Name != pInfoIter->Name; + if ( !bModified ) + { + bModified = checkValue->Value != pInfoIter->Value; + } + } + } + + rConvertedValue = rValue; + rOldValue <<= aSettings; + } + break; + default: + SAL_WARN("dbaccess", "ODatabaseSource::convertFastPropertyValue: unknown or readonly Property!" ); + } + } + return bModified; +} + +namespace +{ + struct SelectPropertyName + { + public: + const OUString& operator()( const PropertyValue& _lhs ) + { + return _lhs.Name; + } + }; + + /** sets a new set of property values for a given property bag instance + + The method takes a property bag, and a sequence of property values to set for this bag. + Upon return, every property which is not part of the given sequence is + <ul><li>removed from the bag, if it's a removable property</li> + <li><em>or</em>reset to its default value, if it's not a removable property</li> + </ul>. + + @param _rxPropertyBag + the property bag to operate on + @param _rAllNewPropertyValues + the new property values to set for the bag + */ + void lcl_setPropertyValues_resetOrRemoveOther( const Reference< XPropertyBag >& _rxPropertyBag, const Sequence< PropertyValue >& _rAllNewPropertyValues ) + { + // sequences are ugly to operate on + std::set<OUString> aToBeSetPropertyNames; + std::transform( + _rAllNewPropertyValues.begin(), + _rAllNewPropertyValues.end(), + std::inserter( aToBeSetPropertyNames, aToBeSetPropertyNames.end() ), + SelectPropertyName() + ); + + try + { + // obtain all properties currently known at the bag + Reference< XPropertySetInfo > xPSI( _rxPropertyBag->getPropertySetInfo(), UNO_SET_THROW ); + const Sequence< Property > aAllExistentProperties( xPSI->getProperties() ); + + Reference< XPropertyState > xPropertyState( _rxPropertyBag, UNO_QUERY_THROW ); + + // loop through them, and reset resp. default properties which are not to be set + for ( auto const & existentProperty : aAllExistentProperties ) + { + if ( aToBeSetPropertyNames.find( existentProperty.Name ) != aToBeSetPropertyNames.end() ) + continue; + + // this property is not to be set, but currently exists in the bag. + // -> Remove it, or reset it to the default. + if ( ( existentProperty.Attributes & PropertyAttribute::REMOVABLE ) != 0 ) + _rxPropertyBag->removeProperty( existentProperty.Name ); + else + xPropertyState->setPropertyToDefault( existentProperty.Name ); + } + + // finally, set the new property values + _rxPropertyBag->setPropertyValues( _rAllNewPropertyValues ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void ODatabaseSource::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + if ( !m_pImpl.is() ) + return; + + switch(nHandle) + { + case PROPERTY_ID_TABLEFILTER: + rValue >>= m_pImpl->m_aTableFilter; + break; + case PROPERTY_ID_TABLETYPEFILTER: + rValue >>= m_pImpl->m_aTableTypeFilter; + break; + case PROPERTY_ID_USER: + rValue >>= m_pImpl->m_sUser; + // if the user name has changed, reset the password + m_pImpl->m_aPassword.clear(); + break; + case PROPERTY_ID_PASSWORD: + rValue >>= m_pImpl->m_aPassword; + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + m_pImpl->m_bPasswordRequired = any2bool(rValue); + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + m_pImpl->m_bSuppressVersionColumns = any2bool(rValue); + break; + case PROPERTY_ID_URL: + rValue >>= m_pImpl->m_sConnectURL; + break; + case PROPERTY_ID_INFO: + { + Sequence< PropertyValue > aInfo; + OSL_VERIFY( rValue >>= aInfo ); + lcl_setPropertyValues_resetOrRemoveOther( m_pImpl->m_xSettings, aInfo ); + } + break; + case PROPERTY_ID_LAYOUTINFORMATION: + rValue >>= m_pImpl->m_aLayoutInformation; + break; + } + m_pImpl->setModified(true); +} + +void ODatabaseSource::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + if ( !m_pImpl.is() ) + return; + + switch (nHandle) + { + case PROPERTY_ID_TABLEFILTER: + rValue <<= m_pImpl->m_aTableFilter; + break; + case PROPERTY_ID_TABLETYPEFILTER: + rValue <<= m_pImpl->m_aTableTypeFilter; + break; + case PROPERTY_ID_USER: + rValue <<= m_pImpl->m_sUser; + break; + case PROPERTY_ID_PASSWORD: + rValue <<= m_pImpl->m_aPassword; + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + rValue <<= m_pImpl->m_bPasswordRequired; + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + rValue <<= m_pImpl->m_bSuppressVersionColumns; + break; + case PROPERTY_ID_ISREADONLY: + rValue <<= m_pImpl->m_bReadOnly; + break; + case PROPERTY_ID_INFO: + { + try + { + // collect the property attributes of all current settings + Reference< XPropertySet > xSettingsAsProps( m_pImpl->m_xSettings, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPST( xSettingsAsProps->getPropertySetInfo(), UNO_SET_THROW ); + const Sequence< Property > aSettings( xPST->getProperties() ); + std::map< OUString, sal_Int32 > aPropertyAttributes; + for ( auto const & setting : aSettings ) + { + aPropertyAttributes[ setting.Name ] = setting.Attributes; + } + + // get all current settings with their values + Sequence< PropertyValue > aValues( m_pImpl->m_xSettings->getPropertyValues() ); + + // transform them so that only property values which fulfill certain + // criteria survive + Sequence< PropertyValue > aNonDefaultOrUserDefined( aValues.getLength() ); + auto [begin, end] = asNonConstRange(aValues); + auto pCopyStart = aNonDefaultOrUserDefined.getArray(); + const PropertyValue* pCopyEnd = std::remove_copy_if( + begin, + end, + pCopyStart, + IsDefaultAndNotRemoveable( aPropertyAttributes ) + ); + aNonDefaultOrUserDefined.realloc( pCopyEnd - pCopyStart ); + rValue <<= aNonDefaultOrUserDefined; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + break; + case PROPERTY_ID_SETTINGS: + rValue <<= m_pImpl->m_xSettings; + break; + case PROPERTY_ID_URL: + rValue <<= m_pImpl->m_sConnectURL; + break; + case PROPERTY_ID_NUMBERFORMATSSUPPLIER: + rValue <<= m_pImpl->getNumberFormatsSupplier(); + break; + case PROPERTY_ID_NAME: + rValue <<= m_pImpl->m_sName; + break; + case PROPERTY_ID_LAYOUTINFORMATION: + rValue <<= m_pImpl->m_aLayoutInformation; + break; + default: + SAL_WARN("dbaccess","unknown Property"); + } +} + +// XDataSource +void ODatabaseSource::setLoginTimeout(sal_Int32 seconds) +{ + ModelMethodGuard aGuard( *this ); + m_pImpl->m_nLoginTimeout = seconds; +} + +sal_Int32 ODatabaseSource::getLoginTimeout() +{ + ModelMethodGuard aGuard( *this ); + return m_pImpl->m_nLoginTimeout; +} + +// XCompletedConnection +Reference< XConnection > SAL_CALL ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler ) +{ + return connectWithCompletion(_rxHandler,false); +} + +Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password) +{ + return getConnection(user,password,false); +} + +Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnection( const OUString& user, const OUString& password ) +{ + return getConnection(user,password,true); +} + +Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnectionWithCompletion( const Reference< XInteractionHandler >& _rxHandler ) +{ + return connectWithCompletion(_rxHandler,true); +} + +Reference< XConnection > ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler,bool _bIsolated ) +{ + ModelMethodGuard aGuard( *this ); + + if (!_rxHandler.is()) + { + SAL_WARN("dbaccess","ODatabaseSource::connectWithCompletion: invalid interaction handler!"); + return getConnection(m_pImpl->m_sUser, m_pImpl->m_aPassword,_bIsolated); + } + + OUString sUser(m_pImpl->m_sUser), sPassword(m_pImpl->m_aPassword); + bool bNewPasswordGiven = false; + + if (m_pImpl->m_bPasswordRequired && sPassword.isEmpty()) + { // we need a password, but don't have one yet. + // -> ask the user + + // build an interaction request + // two continuations (Ok and Cancel) + rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort; + rtl::Reference<OAuthenticationContinuation> pAuthenticate = new OAuthenticationContinuation; + + // the name which should be referred in the login dialog + OUString sServerName( m_pImpl->m_sName ); + INetURLObject aURLCheck( sServerName ); + if ( aURLCheck.GetProtocol() != INetProtocol::NotValid ) + sServerName = aURLCheck.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous ); + + // the request + AuthenticationRequest aRequest; + aRequest.ServerName = sServerName; + aRequest.HasRealm = aRequest.HasAccount = false; + aRequest.HasUserName = aRequest.HasPassword = true; + aRequest.UserName = m_pImpl->m_sUser; + aRequest.Password = m_pImpl->m_sFailedPassword.isEmpty() ? m_pImpl->m_aPassword : m_pImpl->m_sFailedPassword; + rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + pRequest->addContinuation(pAbort); + pRequest->addContinuation(pAuthenticate); + + // handle the request + try + { + _rxHandler->handle(pRequest); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if (!pAuthenticate->wasSelected()) + return Reference< XConnection >(); + + // get the result + sUser = m_pImpl->m_sUser = pAuthenticate->getUser(); + sPassword = pAuthenticate->getPassword(); + + if (pAuthenticate->getRememberPassword()) + { + m_pImpl->m_aPassword = pAuthenticate->getPassword(); + bNewPasswordGiven = true; + } + m_pImpl->m_sFailedPassword.clear(); + } + + try + { + return getConnection(sUser, sPassword,_bIsolated); + } + catch(Exception&) + { + if (bNewPasswordGiven) + { + m_pImpl->m_sFailedPassword = m_pImpl->m_aPassword; + // assume that we had an authentication problem. Without this we may, after an unsuccessful connect, while + // the user gave us a password and the order to remember it, never allow a password input again (at least + // not without restarting the session) + m_pImpl->m_aPassword.clear(); + } + throw; + } +} + +Reference< XConnection > ODatabaseSource::buildIsolatedConnection(const OUString& user, const OUString& password) +{ + Reference< XConnection > xConn; + Reference< XConnection > xSdbcConn = buildLowLevelConnection(user, password); + OSL_ENSURE( xSdbcConn.is(), "ODatabaseSource::buildIsolatedConnection: invalid return value of buildLowLevelConnection!" ); + // buildLowLevelConnection is expected to always succeed + if ( xSdbcConn.is() ) + { + // build a connection server and return it (no stubs) + xConn = new OConnection(*this, xSdbcConn, m_pImpl->m_aContext); + } + return xConn; +} + +Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password,bool _bIsolated) +{ + ModelMethodGuard aGuard( *this ); + + Reference< XConnection > xConn; + if ( _bIsolated ) + { + xConn = buildIsolatedConnection(user,password); + } + else + { // create a new proxy for the connection + if ( !m_pImpl->m_xSharedConnectionManager.is() ) + { + // TODO ideally we could just have one field, but to make that work + // we'd need to move OSharedConnectionManager into its own file and header + rtl::Reference<OSharedConnectionManager> manager = + new OSharedConnectionManager( m_pImpl->m_aContext ); + m_pImpl->m_pSharedConnectionManager = manager.get(); + m_pImpl->m_xSharedConnectionManager = m_pImpl->m_pSharedConnectionManager; + } + xConn = m_pImpl->m_pSharedConnectionManager->getConnection( + m_pImpl->m_sConnectURL, user, password, m_pImpl->m_xSettings->getPropertyValues(), this ); + } + + if ( xConn.is() ) + { + Reference< XComponent> xComp(xConn,UNO_QUERY); + if ( xComp.is() ) + xComp->addEventListener( static_cast< XContainerListener* >( this ) ); + m_pImpl->m_aConnections.emplace_back(xConn); + } + + return xConn; +} + +Reference< XNameAccess > SAL_CALL ODatabaseSource::getBookmarks( ) +{ + ModelMethodGuard aGuard( *this ); + // tdf#114596 this may look nutty but see OBookmarkContainer::acquire() + return static_cast<XNameContainer*>(&m_Bookmarks); +} + +Reference< XNameAccess > SAL_CALL ODatabaseSource::getQueryDefinitions( ) +{ + ModelMethodGuard aGuard( *this ); + + Reference< XNameAccess > xContainer = m_pImpl->m_xCommandDefinitions; + if ( !xContainer.is() ) + { + Any aValue; + css::uno::Reference< css::uno::XInterface > xMy(*this); + if ( dbtools::getDataSourceSetting(xMy,"CommandDefinitions",aValue) ) + { + OUString sSupportService; + aValue >>= sSupportService; + if ( !sSupportService.isEmpty() ) + { + Sequence<Any> aArgs{ Any(NamedValue("DataSource",Any(xMy))) }; + xContainer.set( m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(sSupportService, aArgs, m_pImpl->m_aContext), UNO_QUERY); + } + } + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_QUERY ) ); + xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, false ); + } + m_pImpl->m_xCommandDefinitions = xContainer; + } + return xContainer; +} + +// XTablesSupplier +Reference< XNameAccess > ODatabaseSource::getTables() +{ + ModelMethodGuard aGuard( *this ); + + Reference< XNameAccess > xContainer = m_pImpl->m_xTableDefinitions; + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_TABLE ) ); + xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, true ); + m_pImpl->m_xTableDefinitions = xContainer; + } + return xContainer; +} + +void SAL_CALL ODatabaseSource::flush( ) +{ + try + { + // SYNCHRONIZED -> + { + ModelMethodGuard aGuard( *this ); + + typedef ::utl::SharedUNOComponent< XModel, ::utl::CloseableComponent > SharedModel; + SharedModel xModel( m_pImpl->getModel_noCreate(), SharedModel::NoTakeOwnership ); + + if ( !xModel.is() ) + xModel.reset( m_pImpl->createNewModel_deliverOwnership(), SharedModel::TakeOwnership ); + + Reference< css::frame::XStorable> xStorable( xModel, UNO_QUERY_THROW ); + xStorable->store(); + } + // <- SYNCHRONIZED + + css::lang::EventObject aFlushedEvent(*this); + m_aFlushListeners.notifyEach( &XFlushListener::flushed, aFlushedEvent ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL ODatabaseSource::flushed( const EventObject& /*rEvent*/ ) +{ + ModelMethodGuard aGuard( *this ); + + // Okay, this is some hack. + // + // In general, we have the problem that embedded databases write into their underlying storage, which + // logically is one of our sub storage, and practically is a temporary file maintained by the + // package implementation. As long as we did not commit this storage and our main storage, + // the changes made by the embedded database engine are not really reflected in the database document + // file. This is Bad (TM) for a "real" database application - imagine somebody entering some + // data, and then crashing: For a database application, you would expect that the data still is present + // when you connect to the database next time. + // + // Since this is a conceptual problem as long as we do use those ZIP packages (in fact, we *cannot* + // provide the desired functionality as long as we do not have a package format which allows O(1) writes), + // we cannot completely fix this. However, we can relax the problem by committing more often - often + // enough so that data loss is more seldom, and seldom enough so that there's no noticeable performance + // decrease. + // + // For this, we introduced a few places which XFlushable::flush their connections, and register as + // XFlushListener at the embedded connection (which needs to provide the XFlushable functionality). + // Then, when the connection is flushed, we commit both the database storage and our main storage. + // + // #i55274# + + OSL_ENSURE( m_pImpl->isEmbeddedDatabase(), "ODatabaseSource::flushed: no embedded database?!" ); + bool bWasModified = m_pImpl->m_bModified; + m_pImpl->commitEmbeddedStorage(); + m_pImpl->setModified( bWasModified ); +} + +void SAL_CALL ODatabaseSource::addFlushListener( const Reference< css::util::XFlushListener >& _xListener ) +{ + m_aFlushListeners.addInterface(_xListener); +} + +void SAL_CALL ODatabaseSource::removeFlushListener( const Reference< css::util::XFlushListener >& _xListener ) +{ + m_aFlushListeners.removeInterface(_xListener); +} + +void SAL_CALL ODatabaseSource::elementInserted( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +void SAL_CALL ODatabaseSource::elementRemoved( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +void SAL_CALL ODatabaseSource::elementReplaced( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +// XDocumentDataSource +Reference< XOfficeDatabaseDocument > SAL_CALL ODatabaseSource::getDatabaseDocument() +{ + ModelMethodGuard aGuard( *this ); + + Reference< XModel > xModel( m_pImpl->getModel_noCreate() ); + if ( !xModel.is() ) + xModel = m_pImpl->createNewModel_deliverOwnership(); + + return Reference< XOfficeDatabaseDocument >( xModel, UNO_QUERY_THROW ); +} + +void SAL_CALL ODatabaseSource::initialize( css::uno::Sequence< css::uno::Any > const & rArguments) +{ + ::comphelper::NamedValueCollection aProperties( rArguments ); + if (aProperties.has("ParentWindow")) + aProperties.get("ParentWindow") >>= m_pImpl->m_xDialogParent; +} + +Reference< XInterface > ODatabaseSource::getThis() const +{ + return *const_cast< ODatabaseSource* >( this ); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseSource(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + css::uno::Reference<XInterface> inst( + DatabaseContext::create(context)->createInstance()); + inst->acquire(); + return inst.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/datasource.hxx b/dbaccess/source/core/dataaccess/datasource.hxx new file mode 100644 index 000000000..5b5985eac --- /dev/null +++ b/dbaccess/source/core/dataaccess/datasource.hxx @@ -0,0 +1,220 @@ +/* -*- 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/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/sdb/XBookmarksSupplier.hpp> +#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp> +#include <com/sun/star/sdbc/XIsolatedConnection.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <cppuhelper/propshlp.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <cppuhelper/weakref.hxx> +#include <cppuhelper/compbase.hxx> +#include <com/sun/star/embed/XTransactionListener.hpp> +#include <apitools.hxx> +#include <bookmarkcontainer.hxx> +#include <rtl/ref.hxx> +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/sdb/XCompletedConnection.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <ContentHelper.hxx> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <ModelImpl.hxx> + +namespace dbaccess +{ + +class OSharedConnectionManager; + +// ODatabaseSource +typedef ::cppu::WeakComponentImplHelper< css::lang::XServiceInfo + , css::sdbc::XDataSource + , css::sdb::XBookmarksSupplier + , css::sdb::XQueryDefinitionsSupplier + , css::sdb::XCompletedConnection + , css::container::XContainerListener + , css::sdbc::XIsolatedConnection + , css::sdbcx::XTablesSupplier + , css::util::XFlushable + , css::util::XFlushListener + , css::sdb::XDocumentDataSource + , css::lang::XInitialization + > ODatabaseSource_Base; + +class ODatabaseSource :public ModelDependentComponent // must be first + ,public ODatabaseSource_Base + ,public ::cppu::OPropertySetHelper + ,public ::comphelper::OPropertyArrayUsageHelper < ODatabaseSource > +{ + friend class ODatabaseContext; + friend class OConnection; + friend class OSharedConnectionManager; + +private: + using ODatabaseSource_Base::rBHelper; + // note: this thing uses the ref-count of "this", see OBookmarkContainer::acquire! + OBookmarkContainer m_Bookmarks; + ::comphelper::OInterfaceContainerHelper3<css::util::XFlushListener> m_aFlushListeners; + +private: + virtual ~ODatabaseSource() override; + +public: + explicit ODatabaseSource( const ::rtl::Reference< ODatabaseModelImpl >& _pImpl ); + + struct DBContextAccess { friend class ODatabaseContext; private: DBContextAccess() { } }; + + /** sets a new name for the data source + + The name of a data source (our m_sName member) is the registration name, *if* the + data source actually *is* registered at the database context. + + Normally, this name is passed at time of creation of the ODatabaseModelImpl instance, + but if a newly created data source is registered, then it must be possible to propagate + the new registration name. + */ + static void setName( + const css::uno::Reference< css::sdb::XDocumentDataSource >& _rxDocument, + const OUString& _rNewName, + DBContextAccess + ); + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + // css::sdbcx::XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) 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; + +// OComponentHelper + virtual void SAL_CALL disposing() override; + +// css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) 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::sdb::XCompletedConnection + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connectWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override; + +// css::sdbc::XDataSource + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( const OUString& user, const OUString& password ) override; + virtual void SAL_CALL setLoginTimeout( sal_Int32 seconds ) override; + virtual sal_Int32 SAL_CALL getLoginTimeout( ) override; + +//::css::sdb::XBookmarksSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getBookmarks( ) override; + +//::css::sdb::XQueryDefinitionsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getQueryDefinitions( ) override; + +// css::sdbc::XIsolatedConnection + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getIsolatedConnection( const OUString& user, const OUString& password ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getIsolatedConnectionWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override; + +// XFlushable + virtual void SAL_CALL flush( ) override; + virtual void SAL_CALL addFlushListener( const css::uno::Reference< css::util::XFlushListener >& l ) override; + virtual void SAL_CALL removeFlushListener( const css::uno::Reference< css::util::XFlushListener >& l ) override; + + // XFlushListener + virtual void SAL_CALL flushed( const css::lang::EventObject& rEvent ) override; + + // XDocumentDataSource + virtual css::uno::Reference< css::sdb::XOfficeDatabaseDocument > SAL_CALL getDatabaseDocument() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +protected: + // ModelDependentComponent overridables + virtual css::uno::Reference< css::uno::XInterface > getThis() const override; + +private: +// helper + /** open a connection for the current settings. this is the simple connection we get from the driver + manager, so it can be used as a master for a "high level" sdb connection. + */ + css::uno::Reference< css::sdbc::XConnection > buildLowLevelConnection( + const OUString& _rUid, const OUString& _rPwd + ); + + css::uno::Reference< css::sdbc::XConnection > buildIsolatedConnection( + const OUString& user, const OUString& password + ); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XConnection > getConnection( const OUString& user, const OUString& password , bool _bIsolated); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XConnection > connectWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler , bool _bIsolated); + +protected: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/definitioncontainer.cxx b/dbaccess/source/core/dataaccess/definitioncontainer.cxx new file mode 100644 index 000000000..81d8b0302 --- /dev/null +++ b/dbaccess/source/core/dataaccess/definitioncontainer.cxx @@ -0,0 +1,679 @@ +/* -*- 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 <definitioncontainer.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> + +#include <tools/diagnose_ex.h> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <comphelper/enumhelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <comphelper/servicehelper.hxx> +#include <comphelper/types.hxx> +#include <rtl/ref.hxx> + +using namespace ::com::sun::star::uno; +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 ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::com::sun::star::ucb; + +namespace dbaccess +{ + +// ODefinitionContainer_Impl +void ODefinitionContainer_Impl::erase( const TContentPtr& _pDefinition ) +{ + NamedDefinitions::const_iterator aPos = find( _pDefinition ); + if ( aPos != end() ) + m_aDefinitions.erase( aPos ); +} + +ODefinitionContainer_Impl::const_iterator ODefinitionContainer_Impl::find( const TContentPtr& _pDefinition ) const +{ + return std::find_if( + m_aDefinitions.begin(), + m_aDefinitions.end(), + [&_pDefinition] (const NamedDefinitions::value_type& namedDef) { + return namedDef.second == _pDefinition; + }); +} + +ODefinitionContainer_Impl::iterator ODefinitionContainer_Impl::find( const TContentPtr& _pDefinition ) +{ + return std::find_if( + m_aDefinitions.begin(), + m_aDefinitions.end(), + [&_pDefinition] (const NamedDefinitions::value_type& namedDef) { + return namedDef.second == _pDefinition; + }); +} + +// ODefinitionContainer + +ODefinitionContainer::ODefinitionContainer( const Reference< XComponentContext >& _xORB + , const Reference< XInterface >& _xParentContainer + , const TContentPtr& _pImpl + , bool _bCheckSlash + ) + :OContentHelper(_xORB,_xParentContainer,_pImpl) + ,m_aApproveListeners(m_aMutex) + ,m_aContainerListeners(m_aMutex) + ,m_bInPropertyChange(false) + ,m_bCheckSlash(_bCheckSlash) +{ + assert(m_pImpl); + m_pImpl->m_aProps.bIsDocument = false; + m_pImpl->m_aProps.bIsFolder = true; + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + for (auto const& definition : rDefinitions) + m_aDocuments.push_back( + m_aDocumentMap.emplace(definition.first, Documents::mapped_type() ).first ); + +} + +void SAL_CALL ODefinitionContainer::disposing() +{ + OContentHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + // say goodbye to our listeners + EventObject aEvt(*this); + m_aApproveListeners.disposeAndClear(aEvt); + m_aContainerListeners.disposeAndClear(aEvt); + + // dispose our elements + for (auto const& elem : m_aDocumentMap) + { + Reference<XContent> xProp = elem.second; + if ( xProp.is() ) + { + removeObjectListener(xProp); + ::comphelper::disposeComponent(xProp); + } + } + + // remove our elements + m_aDocuments.clear(); + // !!! do this before clearing the map which the vector elements refer to !!! + m_aDocumentMap.clear(); +} + +ODefinitionContainer::~ODefinitionContainer() +{ +} + +IMPLEMENT_FORWARD_XINTERFACE2( ODefinitionContainer,OContentHelper,ODefinitionContainer_Base) +css::uno::Sequence< css::uno::Type > ODefinitionContainer::getTypes() +{ + return ::comphelper::concatSequences( + OContentHelper::getTypes( ), + ODefinitionContainer_Base::getTypes( ) + ); +} + + +css::uno::Sequence<sal_Int8> ODefinitionContainer::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo +OUString SAL_CALL ODefinitionContainer::getImplementationName( ) +{ + return "com.sun.star.sdb.ODefinitionContainer"; +} + +Sequence< OUString > SAL_CALL ODefinitionContainer::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DefinitionContainer", "com.sun.star.ucb.Content" }; +} + +// XNameContainer +void SAL_CALL ODefinitionContainer::insertByName( const OUString& _rName, const Any& aElement ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // approve the new object + Reference< XContent > xNewElement(aElement,UNO_QUERY); + approveNewObject( _rName, xNewElement ); // will throw if necessary + + notifyByName( aGuard, _rName, xNewElement, nullptr, E_INSERTED, ApproveListeners ); + implAppend( _rName, xNewElement ); + notifyByName( aGuard, _rName, xNewElement, nullptr, E_INSERTED, ContainerListemers ); +} + +void SAL_CALL ODefinitionContainer::removeByName( const OUString& _rName ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(_rName,*this); + + // the old element (for the notifications) + Reference< XContent > xOldElement = implGetByName( _rName, impl_haveAnyListeners_nothrow() ); + + // do the removal + notifyByName( aGuard, _rName, nullptr, xOldElement, E_REMOVED, ApproveListeners ); + implRemove( _rName ); + notifyByName( aGuard, _rName, nullptr, xOldElement, E_REMOVED, ContainerListemers ); + + removeObjectListener( xOldElement ); + disposeComponent(xOldElement); +} + +// XNameReplace +void SAL_CALL ODefinitionContainer::replaceByName( const OUString& _rName, const Any& aElement ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + try + { + // let derived classes approve the new object + Reference< XContent > xNewElement(aElement,UNO_QUERY); + approveNewObject( _rName, xNewElement ); // will throw if necessary + + // the old element (for the notifications) + Reference< XContent > xOldElement = implGetByName( _rName, impl_haveAnyListeners_nothrow() ); + + notifyByName( aGuard, _rName, xNewElement, xOldElement, E_REPLACED, ApproveListeners ); + implReplace( _rName, xNewElement ); + notifyByName( aGuard, _rName, xNewElement, xOldElement, E_REPLACED, ContainerListemers ); + + // and dispose it + disposeComponent(xOldElement); + } + catch (const RuntimeException&) + { + throw; + } + catch (const NoSuchElementException&) + { + throw; + } + catch (const WrappedTargetException&) + { + throw; + } + catch (const Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetException( + "wrapped Exception " + e.Message, + css::uno::Reference<css::uno::XInterface>(), a); + } +} + +namespace +{ + typedef Reference< XVeto > ( SAL_CALL XContainerApproveListener::*ContainerApprovalMethod )( const ContainerEvent& ); + + struct RaiseExceptionFromVeto + { + private: + ContainerApprovalMethod m_pMethod; + const ContainerEvent& m_rEvent; + + public: + explicit RaiseExceptionFromVeto( ContainerApprovalMethod _pMethod, const ContainerEvent& _rEvent ) + :m_pMethod( _pMethod ) + ,m_rEvent( _rEvent ) + { + } + + void operator()( const Reference< XContainerApproveListener >& Listener ) const + { + Reference< XVeto > xVeto = (Listener.get()->*m_pMethod)( m_rEvent ); + if ( !xVeto.is() ) + return; + + Any eVetoDetails = xVeto->getDetails(); + + IllegalArgumentException aIllegalArgumentError; + if ( eVetoDetails >>= aIllegalArgumentError ) + throw aIllegalArgumentError; + + WrappedTargetException aWrappedError; + if ( eVetoDetails >>= aWrappedError ) + throw aWrappedError; + + throw WrappedTargetException( xVeto->getReason(), Listener, eVetoDetails ); + } + }; +} + +void ODefinitionContainer::notifyByName( ResettableMutexGuard& _rGuard, const OUString& _rName, + const Reference< XContent >& _xNewElement, const Reference< XContent >& _xOldElement, + ContainerOperation _eOperation, ListenerType _eType ) +{ + bool bApprove = ( _eType == ApproveListeners ); + + ::comphelper::OInterfaceContainerHelper2& rContainer( bApprove ? m_aApproveListeners : m_aContainerListeners ); + if ( !rContainer.getLength() ) + return; + + ContainerEvent aEvent( *this, Any( _rName ), Any( _xNewElement ), Any( _xOldElement ) ); + + _rGuard.clear(); + switch ( _eOperation ) + { + case E_INSERTED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveInsertElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementInserted, aEvent ); + break; + case E_REPLACED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveReplaceElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementReplaced, aEvent ); + break; + case E_REMOVED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveRemoveElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementRemoved, aEvent ); + break; + } + + if ( bApprove ) + _rGuard.reset(); +} + +void SAL_CALL ODefinitionContainer::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + if (_rxListener.is()) + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL ODefinitionContainer::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + if (_rxListener.is()) + m_aContainerListeners.removeInterface(_rxListener); +} + +void SAL_CALL ODefinitionContainer::addContainerApproveListener( const Reference< XContainerApproveListener >& Listener ) +{ + if ( Listener.is() ) + m_aApproveListeners.addInterface( Listener ); +} + +void SAL_CALL ODefinitionContainer::removeContainerApproveListener( const Reference< XContainerApproveListener >& Listener ) +{ + if ( Listener.is() ) + m_aApproveListeners.removeInterface( Listener ); +} + +// XElementAccess +Type SAL_CALL ODefinitionContainer::getElementType( ) +{ + return cppu::UnoType<XContent>::get(); +} + +sal_Bool SAL_CALL ODefinitionContainer::hasElements( ) +{ + MutexGuard aGuard(m_aMutex); + return !m_aDocuments.empty(); +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL ODefinitionContainer::createEnumeration( ) +{ + MutexGuard aGuard(m_aMutex); + return new ::comphelper::OEnumerationByIndex(static_cast<XIndexAccess*>(this)); +} + +// XIndexAccess +sal_Int32 SAL_CALL ODefinitionContainer::getCount( ) +{ + MutexGuard aGuard(m_aMutex); + return m_aDocuments.size(); +} + +Any SAL_CALL ODefinitionContainer::getByIndex( sal_Int32 _nIndex ) +{ + MutexGuard aGuard(m_aMutex); + + if ((_nIndex < 0) || (o3tl::make_unsigned(_nIndex) >= m_aDocuments.size())) + throw IndexOutOfBoundsException(); + + Documents::iterator aPos = m_aDocuments[_nIndex]; + Reference<XContent> xProp = aPos->second; + if (!xProp.is()) + { // that's the first access to the object + // -> create it + xProp = createObject(aPos->first); + aPos->second = Documents::mapped_type(); + // and update the name-access map + } + + return Any(xProp); +} + +Any SAL_CALL ODefinitionContainer::getByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_aMutex); + + return Any( implGetByName( _rName, true ) ); +} + +Reference< XContent > ODefinitionContainer::implGetByName(const OUString& _rName, bool _bReadIfNecessary) +{ + Documents::iterator aMapPos = m_aDocumentMap.find(_rName); + if (aMapPos == m_aDocumentMap.end()) + throw NoSuchElementException(_rName,*this); + + Reference< XContent > xProp = aMapPos->second; + + if (_bReadIfNecessary && !xProp.is()) + { // the object has never been accessed before, so we have to read it now + // (that's the expensive part) + + // create the object and insert it into the map + xProp = createObject(_rName); + aMapPos->second = xProp; + addObjectListener(xProp); + } + + return xProp; +} + +Sequence< OUString > SAL_CALL ODefinitionContainer::getElementNames( ) +{ + MutexGuard aGuard(m_aMutex); + + Sequence< OUString > aNames(m_aDocumentMap.size()); + OUString* pNames = aNames.getArray(); + for (auto const& elem : m_aDocumentMap) + { + *pNames = elem.first; + ++pNames; + } + + return aNames; +} + +sal_Bool SAL_CALL ODefinitionContainer::hasByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_aMutex); + + return checkExistence(_rName); +} + +void SAL_CALL ODefinitionContainer::disposing( const EventObject& _rSource ) +{ + MutexGuard aGuard(m_aMutex); + Reference< XContent > xSource(_rSource.Source, UNO_QUERY); + // it's one of our documents... + for (auto & elem : m_aDocumentMap) + { + if ( xSource == elem.second.get() ) + { + removeObjectListener(xSource); + // and clear our document map/vector, so the object will be recreated on next access + elem.second = Documents::mapped_type(); + } + } +} + +void ODefinitionContainer::implRemove(const OUString& _rName) +{ + // from the object maps + Documents::const_iterator aFind = m_aDocumentMap.find(_rName); + if ( aFind != m_aDocumentMap.end() ) + { + m_aDocuments.erase( std::find(m_aDocuments.begin(),m_aDocuments.end(),aFind)); + m_aDocumentMap.erase(aFind); + + getDefinitions().erase( _rName ); + + notifyDataSourceModified(); + } +} + +namespace +{ + bool lcl_ensureName( const Reference< XContent >& _rxContent, const OUString& _rName ) + { + if ( !_rxContent.is() ) + return true; + + // obtain the current name. If it's the same as the new one, + // don't do anything + try + { + Reference< XPropertySet > xProps( _rxContent, UNO_QUERY ); + if ( xProps.is() ) + { + OUString sCurrentName; + OSL_VERIFY( xProps->getPropertyValue( PROPERTY_NAME ) >>= sCurrentName ); + if ( sCurrentName == _rName ) + return true; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_ensureName: caught an exception while obtaining the current name!" ); + } + + // set the new name + Reference< XRename > xRename( _rxContent, UNO_QUERY ); + OSL_ENSURE( xRename.is(), "lcl_ensureName: invalid content (not renameable)!" ); + if ( !xRename.is() ) + return false; + try + { + xRename->rename( _rName ); + return true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_ensureName" ); + } + return false; + } +} + +void ODefinitionContainer::implAppend(const OUString& _rName, const Reference< XContent >& _rxNewObject) +{ + MutexGuard aGuard(m_aMutex); + try + { + Reference<XChild> xChild(_rxNewObject,UNO_QUERY); + if ( xChild.is() ) + xChild->setParent(static_cast<OWeakObject*>(this)); + + ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( _rName ); + if ( aFind == rDefinitions.end() ) + { + // ensure that the new object has the proper name. + // Somebody could create an object with name "foo", and insert it as "bar" + // into a container. In this case, we need to ensure that the object name + // is also "bar" + // #i44786# + lcl_ensureName( _rxNewObject, _rName ); + + ::rtl::Reference< OContentHelper > pContent = comphelper::getFromUnoTunnel<OContentHelper>( _rxNewObject ); + if ( pContent.is() ) + { + TContentPtr pImpl = pContent->getImpl(); + rDefinitions.erase( pImpl ); + pImpl->m_aProps.aTitle = _rName; + rDefinitions.insert( _rName, pImpl ); + } + } + + m_aDocuments.push_back(m_aDocumentMap.emplace(_rName,_rxNewObject).first); + notifyDataSourceModified(); + // now update our structures + if ( _rxNewObject.is() ) + addObjectListener(_rxNewObject); + } + catch(Exception&) + { + OSL_FAIL("ODefinitionContainer::implAppend: caught something !"); + } +} + +void ODefinitionContainer::implReplace(const OUString& _rName, const Reference< XContent >& _rxNewObject) +{ + OSL_ENSURE(checkExistence(_rName), "ODefinitionContainer::implReplace : invalid name !"); + + Documents::iterator aFind = m_aDocumentMap.find(_rName); + removeObjectListener(aFind->second); + aFind->second = _rxNewObject; + addObjectListener(aFind->second); +} + +void ODefinitionContainer::approveNewObject(const OUString& _sName,const Reference< XContent >& _rxObject) const +{ + // check the arguments + if ( _sName.isEmpty() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_NAME_MUST_NOT_BE_EMPTY ), + *this, + 0 ); + + if ( m_bCheckSlash && _sName.indexOf( '/' ) != -1 ) + throw IllegalArgumentException( + m_aErrorHelper.getErrorMessage( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES ), + *this, + 0 ); + + if ( !_rxObject.is() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_NO_NULL_OBJECTS_IN_CONTAINER ), + *this, + 0 ); + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + if ( rDefinitions.find( _sName ) != rDefinitions.end() ) + throw ElementExistException( + DBA_RES( RID_STR_NAME_ALREADY_USED ), + *this ); + + ::rtl::Reference< OContentHelper > pContent( comphelper::getFromUnoTunnel<OContentHelper>( _rxObject ) ); + if ( !pContent.is() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_OBJECT_CONTAINER_MISMATCH ), + *this, + 1 ); + + if ( rDefinitions.find( pContent->getImpl() ) != rDefinitions.end() ) + throw ElementExistException( + DBA_RES( RID_STR_OBJECT_ALREADY_CONTAINED ), + *this ); +} + +// XPropertyChangeListener +void SAL_CALL ODefinitionContainer::propertyChange( const PropertyChangeEvent& evt ) +{ + if( evt.PropertyName != PROPERTY_NAME && evt.PropertyName != "Title" ) + return; + + MutexGuard aGuard(m_aMutex); + + m_bInPropertyChange = true; + try + { + OUString sNewName,sOldName; + evt.OldValue >>= sOldName; + evt.NewValue >>= sNewName; + Reference<XContent> xContent( evt.Source, UNO_QUERY ); + removeObjectListener( xContent ); + implRemove( sOldName ); + implAppend( sNewName, xContent ); + } + catch(const Exception& ex) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); + } + m_bInPropertyChange = false; +} + +// XVetoableChangeListener +void SAL_CALL ODefinitionContainer::vetoableChange( const PropertyChangeEvent& aEvent ) +{ + MutexGuard aGuard(m_aMutex); + + if( aEvent.PropertyName == PROPERTY_NAME || aEvent.PropertyName == "Title" ) + { + OUString sNewName; + aEvent.NewValue >>= sNewName; + if(hasByName(sNewName)) + throw PropertyVetoException(); + } +} + +void ODefinitionContainer::addObjectListener(const Reference< XContent >& _xNewObject) +{ + OSL_ENSURE(_xNewObject.is(),"ODefinitionContainer::addObjectListener: Object is null!"); + Reference<XPropertySet> xProp(_xNewObject,UNO_QUERY); + if ( xProp.is() ) + { + xProp->addPropertyChangeListener(PROPERTY_NAME, this); + xProp->addVetoableChangeListener(PROPERTY_NAME, this); + } +} + +void ODefinitionContainer::removeObjectListener(const Reference< XContent >& _xNewObject) +{ + Reference<XPropertySet> xProp(_xNewObject,UNO_QUERY); + if ( xProp.is() ) + { + xProp->removePropertyChangeListener(PROPERTY_NAME, this); + xProp->removeVetoableChangeListener(PROPERTY_NAME, this); + } +} + +bool ODefinitionContainer::checkExistence(const OUString& _rName) +{ + return m_aDocumentMap.find(_rName) != m_aDocumentMap.end(); +} + +} + +// namespace dbaccess +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentcontainer.cxx b/dbaccess/source/core/dataaccess/documentcontainer.cxx new file mode 100644 index 000000000..6a9f17adb --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentcontainer.cxx @@ -0,0 +1,770 @@ +/* -*- 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 "documentcontainer.hxx" +#include <stringconstants.hxx> +#include "documentdefinition.hxx" +#include <ModelImpl.hxx> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <connectivity/dbtools.hxx> +#include "myucp_resultset.hxx" +#include <ucbhelper/cancelcommandexecution.hxx> +#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp> +#include <com/sun/star/ucb/InsertCommandArgument.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <comphelper/mimeconfighelper.hxx> +#include <connectivity/sqlerror.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> + +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <o3tl/string_view.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::io; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +namespace { + +// LocalNameApproval +class LocalNameApproval : public IContainerApprove +{ + ::connectivity::SQLError m_aErrors; + +public: + void approveElement( const OUString& _rName ) override; +}; + +} + +void LocalNameApproval::approveElement( const OUString& _rName ) +{ + if ( _rName.indexOf( '/' ) != -1 ) + throw IllegalArgumentException( + m_aErrors.getErrorMessage( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES ), + nullptr, + 0 + ); +} + +// ODocumentContainer + +ODocumentContainer::ODocumentContainer(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + , bool _bFormsContainer + ) + :ODefinitionContainer(_xORB,_xParentContainer,_pImpl) + ,OPropertyStateContainer(OContentHelper::rBHelper) + ,m_bFormsContainer(_bFormsContainer) +{ + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND | PropertyAttribute::READONLY | PropertyAttribute::CONSTRAINED, + &m_pImpl->m_aProps.aTitle, cppu::UnoType<decltype(m_pImpl->m_aProps.aTitle)>::get()); + + setElementApproval( std::make_shared<LocalNameApproval>() ); +} + +ODocumentContainer::~ODocumentContainer() +{ + + if ( !OContentHelper::rBHelper.bInDispose && !OContentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +IMPLEMENT_FORWARD_XINTERFACE3( ODocumentContainer,ODefinitionContainer,ODocumentContainer_Base,OPropertyStateContainer) + +css::uno::Sequence<sal_Int8> ODocumentContainer::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > ODocumentContainer::getTypes() +{ + return ::comphelper::concatSequences( + ODefinitionContainer::getTypes( ), + OPropertyStateContainer::getTypes( ), + ODocumentContainer_Base::getTypes( ) + ); +} +OUString SAL_CALL ODocumentContainer::getImplementationName() + { + return "com.sun.star.comp.dba.ODocumentContainer"; + }; +sal_Bool SAL_CALL ODocumentContainer::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::Reference< css::beans::XPropertySetInfo > SAL_CALL ODocumentContainer::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& ODocumentContainer::getInfoHelper() +{ + return *ODocumentContainer::getArrayHelper(); +} +::cppu::IPropertyArrayHelper* ODocumentContainer::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +Sequence< OUString > SAL_CALL ODocumentContainer::getSupportedServiceNames( ) +{ + return { m_bFormsContainer ? OUString(SERVICE_NAME_FORM_COLLECTION) : OUString(SERVICE_NAME_REPORT_COLLECTION) }; +} + +OUString ODocumentContainer::determineContentType() const +{ + return OUString(); +} + +Reference< XContent > ODocumentContainer::createObject( const OUString& _rName) +{ + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( _rName ); + OSL_ENSURE( aFind != rDefinitions.end(), "ODocumentContainer::createObject:Invalid entry in map!" ); + if ( aFind->second->m_aProps.bIsFolder ) + return new ODocumentContainer( m_aContext, *this, aFind->second, m_bFormsContainer ); + return new ODocumentDefinition( *this, m_aContext, aFind->second, m_bFormsContainer ); +} + +Reference< XInterface > SAL_CALL ODocumentContainer::createInstance( const OUString& aServiceSpecifier ) +{ + return createInstanceWithArguments( aServiceSpecifier, Sequence< Any >() ); +} + +namespace +{ + template< class TYPE > + void lcl_extractAndRemove( ::comphelper::NamedValueCollection& io_rArguments, const OUString& i_rName, TYPE& o_rValue ) + { + if ( io_rArguments.has( i_rName ) ) + { + io_rArguments.get_ensureType( i_rName, o_rValue ); + io_rArguments.remove( i_rName ); + } + } +} + +Reference< XInterface > SAL_CALL ODocumentContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& _aArguments ) +{ + Reference< XInterface > xRet; + Reference< XContent > xContent; + if ( ServiceSpecifier == SERVICE_SDB_DOCUMENTDEFINITION ) + { + MutexGuard aGuard(m_aMutex); + + // extract known arguments + OUString sName, sPersistentName, sURL, sMediaType, sDocServiceName; + Reference< XCommandProcessor > xCopyFrom; + Reference< XConnection > xConnection; + bool bAsTemplate( false ); + Sequence< sal_Int8 > aClassID; + + ::comphelper::NamedValueCollection aArgs( _aArguments ); + lcl_extractAndRemove( aArgs, PROPERTY_NAME, sName ); + lcl_extractAndRemove( aArgs, PROPERTY_PERSISTENT_NAME, sPersistentName ); + lcl_extractAndRemove( aArgs, PROPERTY_URL, sURL ); + lcl_extractAndRemove( aArgs, PROPERTY_EMBEDDEDOBJECT, xCopyFrom ); + lcl_extractAndRemove( aArgs, PROPERTY_ACTIVE_CONNECTION, xConnection ); + lcl_extractAndRemove( aArgs, PROPERTY_AS_TEMPLATE, bAsTemplate ); + lcl_extractAndRemove( aArgs, INFO_MEDIATYPE, sMediaType ); + lcl_extractAndRemove( aArgs, "DocumentServiceName" , sDocServiceName ); + + // ClassID has two allowed types, so a special treatment here + Any aClassIDArg = aArgs.get( "ClassID" ); + if ( aClassIDArg.hasValue() ) + { + if ( !( aClassIDArg >>= aClassID ) ) + { + // Extended for usage also with a string + OUString sClassIDString; + if ( !( aClassIDArg >>= sClassIDString ) ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + aClassID = ::comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation( sClassIDString ); + } + +#if OSL_DEBUG_LEVEL > 0 + OUString sClassIDString = ::comphelper::MimeConfigurationHelper::GetStringClassIDRepresentation( aClassID ); + (void)sClassIDString; +#endif + aArgs.remove( "ClassID" ); + } + // Everything which now is still present in the arguments is passed to the embedded object + const Sequence< PropertyValue > aCreationArgs( aArgs.getPropertyValues() ); + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + bool bNew = sPersistentName.isEmpty(); + if ( bNew ) + { + sPersistentName = "Obj" + OUString::number(rDefinitions.size() + 1); + Reference<XNameAccess> xElements = getContainerStorage(); + if ( xElements.is() ) + sPersistentName = ::dbtools::createUniqueName(xElements,sPersistentName); + + const bool bNeedClassID = !aClassID.hasElements() && sURL.isEmpty() ; + if ( xCopyFrom.is() ) + { + Sequence<Any> aIni{ Any(getContainerStorage()), Any(sPersistentName) }; + Command aCommand; + aCommand.Name = "copyTo"; + aCommand.Argument <<= aIni; + + xCopyFrom->execute(aCommand,-1,Reference< XCommandEnvironment >()); + Reference<XPropertySet> xProp(xCopyFrom,UNO_QUERY); + if ( xProp.is() && xProp->getPropertySetInfo().is() && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_AS_TEMPLATE) ) + xProp->getPropertyValue(PROPERTY_AS_TEMPLATE) >>= bAsTemplate; + + // if we do not have an own class ID, see if we can determine one from the copy we just created + if ( bNeedClassID ) + ODocumentDefinition::GetDocumentServiceFromMediaType( getContainerStorage(), sPersistentName, m_aContext, aClassID ); + } + else + { + if ( bNeedClassID ) + { + if ( !sMediaType.isEmpty() ) + ODocumentDefinition::GetDocumentServiceFromMediaType( sMediaType, m_aContext, aClassID ); + else if ( !sDocServiceName.isEmpty() ) + { + ::comphelper::MimeConfigurationHelper aConfigHelper( m_aContext ); + const Sequence< NamedValue > aProps( aConfigHelper.GetObjectPropsByDocumentName( sDocServiceName ) ); + const ::comphelper::NamedValueCollection aMediaTypeProps( aProps ); + aClassID = aMediaTypeProps.getOrDefault( "ClassID", Sequence< sal_Int8 >() ); + } + } + } + } + + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( sName ); + TContentPtr pElementImpl; + if ( bNew || ( aFind == rDefinitions.end() ) ) + { + pElementImpl = std::make_shared<OContentHelper_Impl>(); + if ( !bNew ) + pElementImpl->m_aProps.aTitle = sName; + + pElementImpl->m_aProps.sPersistentName = sPersistentName; + pElementImpl->m_aProps.bAsTemplate = bAsTemplate; + pElementImpl->m_pDataSource = m_pImpl->m_pDataSource; + } + else + pElementImpl = aFind->second; + + ::rtl::Reference< ODocumentDefinition > pDocDef = new ODocumentDefinition( *this, m_aContext, pElementImpl, m_bFormsContainer ); + if ( aClassID.hasElements() ) + { + pDocDef->initialLoad( aClassID, aCreationArgs, xConnection ); + } + else + { + OSL_ENSURE( !aCreationArgs.hasElements(), "ODocumentContainer::createInstance: additional creation args are lost, if you do not provide a class ID." ); + } + xContent = pDocDef.get(); + + if ( !sURL.isEmpty() ) + { + Sequence<Any> aIni{ Any(sURL) }; + Command aCommand; + aCommand.Name = "insert"; + aCommand.Argument <<= aIni; + Reference< XCommandProcessor > xCommandProcessor(xContent,UNO_QUERY); + if ( xContent.is() ) + { + xCommandProcessor->execute(aCommand,-1,Reference< XCommandEnvironment >()); + } + } + } + else if ( ServiceSpecifier == SERVICE_NAME_FORM_COLLECTION || SERVICE_NAME_REPORT_COLLECTION == ServiceSpecifier ) + { + const Any* pBegin = _aArguments.getConstArray(); + const Any* pEnd = pBegin + _aArguments.getLength(); + PropertyValue aValue; + OUString sName; + Reference<XNameAccess> xCopyFrom; + for(;pBegin != pEnd;++pBegin) + { + *pBegin >>= aValue; + if ( aValue.Name == PROPERTY_NAME) + { + aValue.Value >>= sName; + } + else if ( aValue.Name == PROPERTY_EMBEDDEDOBJECT) + { + xCopyFrom.set(aValue.Value,UNO_QUERY); + } + } + OSL_ENSURE(!sName.isEmpty(),"Invalid name for a document container!"); + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( sName ); + TContentPtr pElementImpl; + if ( aFind == rDefinitions.end() ) + { + pElementImpl = std::make_shared<ODefinitionContainer_Impl>(); + pElementImpl->m_aProps.aTitle = sName; + pElementImpl->m_pDataSource = m_pImpl->m_pDataSource; + } + else + pElementImpl = aFind->second; + OSL_ENSURE( pElementImpl ," Invalid entry in map!"); + xContent = new ODocumentContainer( m_aContext, *this, pElementImpl, ServiceSpecifier == SERVICE_NAME_FORM_COLLECTION ); + + // copy children + if ( xCopyFrom.is() ) + { + Sequence< OUString> aSeq = xCopyFrom->getElementNames(); + const OUString* elements = aSeq.getConstArray(); + const OUString* elementsEnd = elements + aSeq.getLength(); + Reference<XContent> xObjectToCopy; + + Reference<XMultiServiceFactory> xORB(xContent,UNO_QUERY); + OSL_ENSURE(xORB.is(),"No service factory given"); + if ( xORB.is() ) + { + for(;elements != elementsEnd;++elements) + { + xCopyFrom->getByName(*elements) >>= xObjectToCopy; + Sequence<Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"Name", Any(*elements)}, // set as folder + {"Parent", Any(xContent)}, + {PROPERTY_EMBEDDEDOBJECT, Any(xObjectToCopy)}, + })); + + OUString sServiceName; + if ( Reference< XNameAccess >( xObjectToCopy, UNO_QUERY ).is() ) + { + if ( m_bFormsContainer ) + sServiceName = SERVICE_NAME_FORM_COLLECTION; + else + sServiceName = SERVICE_NAME_REPORT_COLLECTION; + } + else + sServiceName = SERVICE_SDB_DOCUMENTDEFINITION; + + Reference<XContent > xNew(xORB->createInstanceWithArguments(sServiceName,aArguments),UNO_QUERY); + Reference<XNameContainer> xNameContainer(xContent,UNO_QUERY); + if ( xNameContainer.is() ) + xNameContainer->insertByName(*elements,Any(xNew)); + } + } + } + } + xRet = xContent; + return xRet; +} + +Sequence< OUString > SAL_CALL ODocumentContainer::getAvailableServiceNames( ) +{ + return + { + SERVICE_SDB_DOCUMENTDEFINITION, + SERVICE_NAME_FORM_COLLECTION, + SERVICE_NAME_REPORT_COLLECTION + }; +} + +Any SAL_CALL ODocumentContainer::execute( const Command& aCommand, sal_Int32 CommandId, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + if ( aCommand.Name == "open" ) + { + // open command for a folder content + OpenCommandArgument2 aOpenCommand; + if ( !( aCommand.Argument >>= aOpenCommand ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + bool bOpenFolder = + ( ( aOpenCommand.Mode == OpenMode::ALL ) || + ( aOpenCommand.Mode == OpenMode::FOLDERS ) || + ( aOpenCommand.Mode == OpenMode::DOCUMENTS ) ); + + if ( bOpenFolder ) + { + // open as folder - return result set + + Reference< XDynamicResultSet > xSet + = new DynamicResultSet( m_aContext, + this, + aOpenCommand, + Environment ); + aRet <<= xSet; + } + else + { + // Unsupported. + ucbhelper::cancelCommandExecution( + Any( UnsupportedOpenModeException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + sal_Int16( aOpenCommand.Mode ) ) ), + Environment ); + // Unreachable + } + } + else if ( aCommand.Name == "insert" ) + { + // insert + + InsertCommandArgument arg; + if ( !( aCommand.Argument >>= arg ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + } + else if ( aCommand.Name == "delete" ) + { + // delete + Sequence< OUString> aSeq = getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + removeByName(*pIter); + + dispose(); + } + else + aRet = OContentHelper::execute(aCommand,CommandId,Environment); + return aRet; +} + +namespace +{ + bool lcl_queryContent(std::u16string_view _sName,Reference< XNameContainer >& _xNameContainer,Any& _rRet,OUString& _sSimpleName) + { + sal_Int32 nIndex = 0; + OUString sName( o3tl::getToken(_sName,0,'/',nIndex) ); + bool bRet = _xNameContainer->hasByName(sName); + if ( bRet ) + { + _sSimpleName = sName; + _rRet = _xNameContainer->getByName(_sSimpleName); + while ( nIndex != -1 && bRet ) + { + sName = o3tl::getToken(_sName,0,'/',nIndex); + _xNameContainer.set(_rRet,UNO_QUERY); + bRet = _xNameContainer.is(); + if ( bRet ) + { + bRet = _xNameContainer->hasByName(sName); + _sSimpleName = sName; + if ( bRet ) + _rRet = _xNameContainer->getByName(sName); + } + } + } + if ( nIndex == -1 ) + _sSimpleName = sName; // a content + else + _xNameContainer.clear(); // a sub folder doesn't exist + return bRet; + } +} + +Reference< XComponent > SAL_CALL ODocumentContainer::loadComponentFromURL( const OUString& _sURL + , const OUString& /*TargetFrameName*/ + , sal_Int32 /*SearchFlags*/ + , const Sequence< PropertyValue >& Arguments ) +{ + ::SolarMutexGuard aSolarGuard; + + MutexGuard aGuard(m_aMutex); + Reference< XComponent > xComp; + try + { + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( !lcl_queryContent(_sURL,xNameContainer,aContent,sName) ) + { + OUString sMessage( + DBA_RES(RID_STR_NAME_NOT_FOUND).replaceFirst("$name$", _sURL)); + throw IllegalArgumentException( sMessage, *this, 1 ); + } + + Reference< XCommandProcessor > xContent(aContent,UNO_QUERY); + if ( xContent.is() ) + { + Command aCommand; + + ::comphelper::NamedValueCollection aArgs( Arguments ); + aCommand.Name = aArgs.getOrDefault( "OpenMode", OUString("open") ); + aArgs.remove( "OpenMode" ); + + OpenCommandArgument2 aOpenCommand; + aOpenCommand.Mode = OpenMode::DOCUMENT; + aArgs.put( "OpenCommandArgument", aOpenCommand ); + + aCommand.Argument <<= aArgs.getPropertyValues(); + xComp.set(xContent->execute(aCommand,xContent->createCommandIdentifier(),Reference< XCommandEnvironment >()),UNO_QUERY); + } + } + catch(const NoSuchElementException&) + { + throw IllegalArgumentException(); + } + catch(const WrappedTargetException &e) + { + throw WrappedTargetRuntimeException(e.Message, e.Context, e.TargetException); + } + return xComp; +} + +Any SAL_CALL ODocumentContainer::getByHierarchicalName( const OUString& _sName ) +{ + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + return aContent; + throw NoSuchElementException(_sName,*this); +} + +sal_Bool SAL_CALL ODocumentContainer::hasByHierarchicalName( const OUString& _sName ) +{ + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + return lcl_queryContent(_sName,xNameContainer,aContent,sName); +} + +// XHierarchicalNameContainer +void SAL_CALL ODocumentContainer::insertByHierarchicalName( const OUString& _sName, const Any& _aElement ) +{ + Reference< XContent > xContent(_aElement,UNO_QUERY); + if ( !xContent.is() ) + throw IllegalArgumentException(); + + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw ElementExistException(_sName,*this); + + if ( !xNameContainer.is() ) + { + sal_Int32 index = sName.getLength(); + OUString sMessage( + DBA_RES(RID_STR_NO_SUB_FOLDER).replaceFirst("$folder$", + o3tl::getToken(_sName, 0,'/',index))); + throw IllegalArgumentException( sMessage, *this, 1 ); + } + + xNameContainer->insertByName(sName,_aElement); +} + +void SAL_CALL ODocumentContainer::removeByHierarchicalName( const OUString& _sName ) +{ + if ( _sName.isEmpty() ) + throw NoSuchElementException(_sName,*this); + + MutexGuard aGuard(m_aMutex); + Any aContent; + OUString sName; + Reference< XNameContainer > xNameContainer(this); + if ( !lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw NoSuchElementException(_sName,*this); + + xNameContainer->removeByName(sName); +} + +// XHierarchicalNameReplace +void SAL_CALL ODocumentContainer::replaceByHierarchicalName( const OUString& _sName, const Any& _aElement ) +{ + Reference< XContent > xContent(_aElement,UNO_QUERY); + if ( !xContent.is() ) + throw IllegalArgumentException(); + + MutexGuard aGuard(m_aMutex); + Any aContent; + OUString sName; + Reference< XNameContainer > xNameContainer(this); + if ( !lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw NoSuchElementException(_sName,*this); + + xNameContainer->replaceByName(sName,_aElement); +} + +OUString SAL_CALL ODocumentContainer::getHierarchicalName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getHierarchicalName( false ); +} + +OUString SAL_CALL ODocumentContainer::composeHierarchicalName( const OUString& i_rRelativeName ) +{ + OUString aBuffer = getHierarchicalName() + "/" + i_rRelativeName; + return aBuffer; +} + +::rtl::Reference<OContentHelper> ODocumentContainer::getContent(const OUString& _sName) const +{ + ::rtl::Reference<OContentHelper> pContent; + try + { + pContent = comphelper::getFromUnoTunnel<OContentHelper>(const_cast<ODocumentContainer*>(this)->implGetByName( _sName, true )); + } + catch(const Exception&) + { + } + return pContent; +} + +void ODocumentContainer::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const +{ + _rDefault.clear(); +} + +void SAL_CALL ODocumentContainer::commit( ) +{ + MutexGuard aGuard(m_aMutex); + for (auto const& elem : m_aDocumentMap) + { + Reference<XTransactedObject> xTrans(elem.second.get(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->commit(); + } + Reference<XTransactedObject> xTrans(getContainerStorage(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->commit(); +} + +void SAL_CALL ODocumentContainer::revert( ) +{ + MutexGuard aGuard(m_aMutex); + for (auto const& elem : m_aDocumentMap) + { + Reference<XTransactedObject> xTrans(elem.second.get(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->revert(); + } + Reference<XTransactedObject> xTrans(getContainerStorage(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->revert(); +} + +Reference< XStorage> ODocumentContainer::getContainerStorage() const +{ + return m_pImpl->m_pDataSource + ? m_pImpl->m_pDataSource->getStorage( m_bFormsContainer ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + : Reference< XStorage>(); +} + +void SAL_CALL ODocumentContainer::removeByName( const OUString& _rName ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(_rName,*this); + + Reference< XCommandProcessor > xContent( implGetByName( _rName, true ), UNO_QUERY ); + if ( xContent.is() ) + { + Command aCommand; + + aCommand.Name = "delete"; + xContent->execute(aCommand,xContent->createCommandIdentifier(),Reference< XCommandEnvironment >()); + } + + // do the removal + implRemove(_rName); + + notifyByName( aGuard, _rName, nullptr, nullptr, E_REMOVED, ContainerListemers ); +} + +void SAL_CALL ODocumentContainer::rename( const OUString& newName ) +{ + try + { + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + if ( newName == m_pImpl->m_aProps.aTitle ) + return; + + sal_Int32 nHandle = PROPERTY_ID_NAME; + Any aOld(m_pImpl->m_aProps.aTitle); + Any aNew(newName); + + aGuard.clear(); + fire(&nHandle, &aNew, &aOld, 1, true ); + m_pImpl->m_aProps.aTitle = newName; + fire(&nHandle, &aNew, &aOld, 1, false ); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(newName,*this); + } +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentcontainer.hxx b/dbaccess/source/core/dataaccess/documentcontainer.hxx new file mode 100644 index 000000000..d47709e80 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentcontainer.hxx @@ -0,0 +1,137 @@ +/* -*- 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 <definitioncontainer.hxx> +#include <cppuhelper/implbase5.hxx> +#include <connectivity/CommonTools.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/container/XHierarchicalNameContainer.hpp> +#include <com/sun/star/container/XHierarchicalName.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <comphelper/propertystatecontainer.hxx> +#include <comphelper/proparrhlp.hxx> +#include <rtl/ref.hxx> +#include <apitools.hxx> + +namespace dbaccess +{ +typedef ::cppu::ImplHelper5 < css::frame::XComponentLoader + , css::lang::XMultiServiceFactory + , css::container::XHierarchicalNameContainer + , css::container::XHierarchicalName + , css::embed::XTransactedObject + > ODocumentContainer_Base; +// ODocumentContainer - collections of database documents (reports/forms) +class ODocumentContainer : public ODefinitionContainer + , public ODocumentContainer_Base + , public ::comphelper::OPropertyStateContainer + , public ::comphelper::OPropertyArrayUsageHelper< ODocumentContainer > +{ + bool m_bFormsContainer; + +public: + /** constructs the container.<BR> + */ + ODocumentContainer( + const css::uno::Reference< css::uno::XComponentContext >& _xORB + , const css::uno::Reference< css::uno::XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + , bool _bFormsContainer + ); + + // css::uno::XInterface + DECLARE_XINTERFACE( ) + + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; + + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + + // XComponentLoader + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentFromURL( const OUString& URL, const OUString& TargetFrameName, sal_Int32 SearchFlags, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // css::lang::XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override ; + + // XHierarchicalNameAccess + virtual css::uno::Any SAL_CALL getByHierarchicalName( const OUString& _sName ) override; + virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString& _sName ) override; + + // XHierarchicalNameContainer + virtual void SAL_CALL insertByHierarchicalName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByHierarchicalName( const OUString& Name ) override; + + // XHierarchicalName + virtual OUString SAL_CALL getHierarchicalName( ) override; + virtual OUString SAL_CALL composeHierarchicalName( const OUString& aRelativeName ) override; + + // XNameContainer + virtual void SAL_CALL removeByName( const OUString& _rName ) override; + + // XHierarchicalNameReplace + virtual void SAL_CALL replaceByHierarchicalName( const OUString& aName, const css::uno::Any& aElement ) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XTransactedObject + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL revert( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // helper + ::rtl::Reference<OContentHelper> getContent(const OUString& _sName) const; + css::uno::Reference< css::embed::XStorage > getContainerStorage() const; + +protected: + virtual ~ODocumentContainer() override; + + /** OContentHelper + */ + virtual OUString determineContentType() const override; + + // ODefinitionContainer + virtual css::uno::Reference< css::ucb::XContent > createObject( + const OUString& _rName + ) override; + + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentdefinition.cxx b/dbaccess/source/core/dataaccess/documentdefinition.cxx new file mode 100644 index 000000000..98e84d6d4 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentdefinition.cxx @@ -0,0 +1,2102 @@ +/* -*- 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 "documentdefinition.hxx" +#include <ModelImpl.hxx> +#include <stringconstants.hxx> +#include <sdbcoretools.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <comphelper/sequence.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/classids.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/frame/XUntitledNumbers.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/task/XJobExecutor.hpp> +#include <com/sun/star/report/XReportDefinition.hpp> +#include <com/sun/star/report/XReportEngine.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/ucb/MissingPropertiesException.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/frame/XModule.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/embed/XCommonEmbedPersist.hpp> +#include "intercept.hxx" +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/sdb/XInteractionDocumentSave.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/sdb/DocumentSaveRequest.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <comphelper/interaction.hxx> +#include <connectivity/dbtools.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <com/sun/star/task/XInteractionApprove.hpp> +#include <com/sun/star/task/XInteractionDisapprove.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/io/WrongFormatException.hpp> +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/sdb/application/DatabaseObject.hpp> +#include <com/sun/star/util/XModifiable2.hpp> + +using namespace ::com::sun::star; +using namespace view; +using namespace uno; +using namespace util; +using namespace ucb; +using namespace beans; +using namespace lang; +using namespace awt; +using namespace embed; +using namespace frame; +using namespace document; +using namespace sdbc; +using namespace sdb; +using namespace io; +using namespace container; +using namespace datatransfer; +using namespace task; +using namespace form; +using namespace drawing; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +using sdb::application::XDatabaseDocumentUI; +namespace DatabaseObject = sdb::application::DatabaseObject; + +#define DEFAULT_WIDTH 10000 +#define DEFAULT_HEIGHT 7500 + +namespace dbaccess +{ + + typedef ::std::optional< bool > optional_bool; + + // helper + namespace + { + OUString lcl_determineContentType_nothrow( const Reference< XStorage >& _rxContainerStorage, + const OUString& _rEntityName ) + { + OUString sContentType; + try + { + ::utl::SharedUNOComponent< XPropertySet > xStorageProps( + _rxContainerStorage->openStorageElement( _rEntityName, ElementModes::READ ), UNO_QUERY_THROW ); + OSL_VERIFY( xStorageProps->getPropertyValue( INFO_MEDIATYPE ) >>= sContentType ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sContentType; + } + } + + // OEmbedObjectHolder + typedef ::cppu::WeakComponentImplHelper< embed::XStateChangeListener > TEmbedObjectHolder; + + namespace { + + class OEmbedObjectHolder : public ::cppu::BaseMutex + ,public TEmbedObjectHolder + { + Reference< XEmbeddedObject > m_xBroadCaster; + ODocumentDefinition* m_pDefinition; + bool m_bInStateChange; + protected: + virtual void SAL_CALL disposing() override; + public: + OEmbedObjectHolder(const Reference< XEmbeddedObject >& _xBroadCaster,ODocumentDefinition* _pDefinition) + : TEmbedObjectHolder(m_aMutex) + ,m_xBroadCaster(_xBroadCaster) + ,m_pDefinition(_pDefinition) + ,m_bInStateChange(false) + { + osl_atomic_increment( &m_refCount ); + { + if ( m_xBroadCaster.is() ) + m_xBroadCaster->addStateChangeListener(this); + } + osl_atomic_decrement( &m_refCount ); + } + + virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + }; + + } + + void SAL_CALL OEmbedObjectHolder::disposing() + { + if ( m_xBroadCaster.is() ) + m_xBroadCaster->removeStateChangeListener(this); + m_xBroadCaster = nullptr; + m_pDefinition = nullptr; + } + + void SAL_CALL OEmbedObjectHolder::changingState( const lang::EventObject& /*aEvent*/, ::sal_Int32 /*nOldState*/, ::sal_Int32 /*nNewState*/ ) + { + } + + void SAL_CALL OEmbedObjectHolder::stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) + { + if ( !m_bInStateChange && nNewState == EmbedStates::RUNNING && nOldState == EmbedStates::ACTIVE && m_pDefinition ) + { + m_bInStateChange = true; + Reference<XInterface> xHoldAlive(static_cast< ::cppu::OWeakObject* >(m_pDefinition),UNO_QUERY); + { + Reference<XEmbeddedObject> xEmbeddedObject(aEvent.Source,UNO_QUERY); + if ( xEmbeddedObject.is() ) + xEmbeddedObject->changeState(EmbedStates::LOADED); + } + m_bInStateChange = false; + } + } + + void SAL_CALL OEmbedObjectHolder::disposing( const lang::EventObject& /*Source*/ ) + { + m_xBroadCaster = nullptr; + } + + // OEmbeddedClientHelper + class OEmbeddedClientHelper : public ::cppu::WeakImplHelper<XEmbeddedClient> + { + public: + virtual void SAL_CALL saveObject( ) override + { + } + // XComponentSupplier + virtual Reference< util::XCloseable > SAL_CALL getComponent( ) override + { + return Reference< css::util::XCloseable >(); + } + + // XEmbeddedClient + virtual void SAL_CALL visibilityChanged( sal_Bool /*bVisible*/ ) override + { + } + }; + + namespace { + + // LockModifiable + class LockModifiable + { + public: + explicit LockModifiable( const Reference< XInterface >& i_rModifiable ) + :m_xModifiable( i_rModifiable, UNO_QUERY ) + { + OSL_ENSURE( m_xModifiable.is(), "LockModifiable::LockModifiable: invalid component!" ); + if ( m_xModifiable.is() ) + { + if ( !m_xModifiable->isSetModifiedEnabled() ) + { + // somebody already locked that, no need to lock it, again, and no need to unlock it later + m_xModifiable.clear(); + } + else + { + m_xModifiable->disableSetModified(); + } + } + } + + ~LockModifiable() + { + if ( m_xModifiable.is() ) + m_xModifiable->enableSetModified(); + } + + private: + Reference< XModifiable2 > m_xModifiable; + }; + + } + + // LifetimeCoupler + typedef ::cppu::WeakImplHelper< css::lang::XEventListener + > LifetimeCoupler_Base; + + namespace { + + /** helper class which couples the lifetime of a component to the lifetime + of another component + + Instances of this class are constructed with two components. The first is + simply held by reference, and thus kept alive. The second one is observed + for <code>disposing</code> calls - if they occur, i.e. if the component dies, + the reference to the first component is cleared. + + This way, you can ensure that a certain component is kept alive as long + as a second component is not disposed. + */ + class LifetimeCoupler : public LifetimeCoupler_Base + { + private: + Reference< XInterface > m_xClient; + + public: + static void couple( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor ) + { + new LifetimeCoupler( _rxClient, _rxActor ); + } + + private: + LifetimeCoupler( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor ) + :m_xClient( _rxClient ) + { + OSL_ENSURE( _rxActor.is(), "LifetimeCoupler::LifetimeCoupler: this will crash!" ); + osl_atomic_increment( &m_refCount ); + { + _rxActor->addEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount, "LifetimeCoupler::LifetimeCoupler: the actor is not holding us by hard ref - this won't work!" ); + } + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + protected: + }; + + } + + void SAL_CALL LifetimeCoupler::disposing( const css::lang::EventObject& /*Source*/ ) + { + m_xClient.clear(); + } + + namespace { + + // ODocumentSaveContinuation + class ODocumentSaveContinuation : public OInteraction< XInteractionDocumentSave > + { + OUString m_sName; + Reference<XContent> m_xParentContainer; + + public: + ODocumentSaveContinuation() { } + + const Reference<XContent>& getContent() const { return m_xParentContainer; } + const OUString& getName() const { return m_sName; } + + // XInteractionDocumentSave + virtual void SAL_CALL setName( const OUString& _sName,const Reference<XContent>& _xParent) override; + }; + + } + + void SAL_CALL ODocumentSaveContinuation::setName( const OUString& _sName,const Reference<XContent>& _xParent) + { + m_sName = _sName; + m_xParentContainer = _xParent; + } + +OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const Reference< XStorage >& _rxContainerStorage, + const OUString& _rEntityName, const Reference< XComponentContext >& _rContext, + Sequence< sal_Int8 >& _rClassId ) +{ + return GetDocumentServiceFromMediaType( + lcl_determineContentType_nothrow( _rxContainerStorage, _rEntityName ), + _rContext, _rClassId ); +} + +OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const OUString& _rMediaType, + const Reference< XComponentContext >& _rContext, Sequence< sal_Int8 >& _rClassId ) +{ + OUString sResult; + try + { + ::comphelper::MimeConfigurationHelper aConfigHelper( _rContext ); + sResult = aConfigHelper.GetDocServiceNameFromMediaType( _rMediaType ); + _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(aConfigHelper.GetExplicitlyRegisteredObjClassID( _rMediaType )); + if ( !_rClassId.hasElements() && !sResult.isEmpty() ) + { + Reference< XNameAccess > xObjConfig = aConfigHelper.GetObjConfiguration(); + if ( xObjConfig.is() ) + { + const Sequence< OUString > aClassIDs = xObjConfig->getElementNames(); + for ( OUString const & classId : aClassIDs ) + { + Reference< XNameAccess > xObjectProps; + OUString aEntryDocName; + + if ( ( xObjConfig->getByName( classId ) >>= xObjectProps ) && xObjectProps.is() + && ( xObjectProps->getByName("ObjectDocumentServiceName") >>= aEntryDocName ) + && aEntryDocName == sResult ) + { + _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(classId); + break; + } + } + } + } +#if OSL_DEBUG_LEVEL > 0 + // alternative, shorter approach + const Sequence< NamedValue > aProps( aConfigHelper.GetObjectPropsByMediaType( _rMediaType ) ); + const ::comphelper::NamedValueCollection aMediaTypeProps( aProps ); + const OUString sAlternativeResult = aMediaTypeProps.getOrDefault( "ObjectDocumentServiceName", OUString() ); + OSL_ENSURE( sAlternativeResult == sResult, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (1)!" ); + const Sequence< sal_Int8 > aAlternativeClassID = aMediaTypeProps.getOrDefault( "ClassID", Sequence< sal_Int8 >() ); + OSL_ENSURE( aAlternativeClassID == _rClassId, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (2)!" ); +#endif + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sResult; +} + +// ODocumentDefinition + +ODocumentDefinition::ODocumentDefinition( const Reference< XInterface >& _rxContainer, const Reference< XComponentContext >& _xORB, + const TContentPtr& _pImpl, bool _bForm ) + :OContentHelper(_xORB,_rxContainer,_pImpl) + ,OPropertyStateContainer(OContentHelper::rBHelper) + ,m_bForm(_bForm) + ,m_bOpenInDesign(false) + ,m_bInExecute(false) + ,m_bRemoveListener(false) +{ + registerProperties(); +} + +void ODocumentDefinition::initialLoad( const Sequence< sal_Int8 >& i_rClassID, const Sequence< PropertyValue >& i_rCreationArgs, + const Reference< XConnection >& i_rConnection ) +{ + OSL_ENSURE( i_rClassID.hasElements(), "ODocumentDefinition::initialLoad: illegal class ID!" ); + if ( !i_rClassID.hasElements() ) + return; + + loadEmbeddedObject( i_rConnection, i_rClassID, i_rCreationArgs, false, false ); +} + +ODocumentDefinition::~ODocumentDefinition() +{ + if ( !OContentHelper::rBHelper.bInDispose && !OContentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + + if ( m_pInterceptor.is() ) + { + m_pInterceptor->dispose(); + m_pInterceptor.clear(); + } +} + +void ODocumentDefinition::closeObject() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( m_xEmbeddedObject.is() ) + { + try + { + m_xEmbeddedObject->close(true); + } + catch(const Exception&) + { + } + m_xEmbeddedObject = nullptr; + m_pClientHelper.clear(); + } +} + +void SAL_CALL ODocumentDefinition::disposing() +{ + OContentHelper::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + closeObject(); + ::comphelper::disposeComponent(m_xListener); + if ( m_bRemoveListener ) + { + Reference<util::XCloseable> xCloseable(m_pImpl->m_pDataSource->getModel_noCreate(),UNO_QUERY); + if ( xCloseable.is() ) + xCloseable->removeCloseListener(this); + } +} + +css::uno::Sequence<sal_Int8> ODocumentDefinition::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > ODocumentDefinition::getTypes() +{ + return ::comphelper::concatSequences( + OContentHelper::getTypes( ), + OPropertyStateContainer::getTypes( ), + ODocumentDefinition_Base::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE3( ODocumentDefinition,OContentHelper,OPropertyStateContainer,ODocumentDefinition_Base) + +void ODocumentDefinition::registerProperties() +{ + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::CONSTRAINED | PropertyAttribute::BOUND | PropertyAttribute::READONLY, + &m_pImpl->m_aProps.aTitle, cppu::UnoType<decltype(m_pImpl->m_aProps.aTitle)>::get()); + + registerProperty(PROPERTY_AS_TEMPLATE, PROPERTY_ID_AS_TEMPLATE, PropertyAttribute::READONLY, &m_pImpl->m_aProps.bAsTemplate, + cppu::UnoType<decltype(m_pImpl->m_aProps.bAsTemplate)>::get()); + + registerProperty(PROPERTY_PERSISTENT_NAME, PROPERTY_ID_PERSISTENT_NAME, PropertyAttribute::READONLY, &m_pImpl->m_aProps.sPersistentName, + cppu::UnoType<decltype(m_pImpl->m_aProps.sPersistentName)>::get()); + + registerProperty(PROPERTY_IS_FORM, PROPERTY_ID_IS_FORM, PropertyAttribute::READONLY, &m_bForm, cppu::UnoType<decltype(m_bForm)>::get()); +} + +void SAL_CALL ODocumentDefinition::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const +{ + if ( i_nHandle == PROPERTY_ID_PERSISTENT_PATH ) + { + OUString sPersistentPath; + if ( !m_pImpl->m_aProps.sPersistentName.isEmpty() ) + { + sPersistentPath = ODatabaseModelImpl::getObjectContainerStorageName( m_bForm ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + + "/" + m_pImpl->m_aProps.sPersistentName; + } + o_rValue <<= sPersistentPath; + return; + } + + OPropertyStateContainer::getFastPropertyValue( o_rValue, i_nHandle ); +} + +Reference< XPropertySetInfo > SAL_CALL ODocumentDefinition::getPropertySetInfo( ) +{ + Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +IPropertyArrayHelper& ODocumentDefinition::getInfoHelper() +{ + return *getArrayHelper(); +} + +IPropertyArrayHelper* ODocumentDefinition::createArrayHelper( ) const +{ + // properties maintained by our base class (see registerProperties) + Sequence< Property > aProps; + describeProperties( aProps ); + + // properties not maintained by our base class + Sequence< Property > aManualProps{ { /* Name */ PROPERTY_PERSISTENT_PATH, + /* Handle */ PROPERTY_ID_PERSISTENT_PATH, + /* Type */ ::cppu::UnoType<OUString>::get(), + /* Attributes */ PropertyAttribute::READONLY } }; + + return new OPropertyArrayHelper( ::comphelper::concatSequences( aProps, aManualProps ) ); +} + +namespace { + +class OExecuteImpl +{ + bool& m_rbSet; +public: + explicit OExecuteImpl(bool& _rbSet) : m_rbSet(_rbSet){ m_rbSet=true; } + ~OExecuteImpl(){ m_rbSet = false; } +}; + + bool lcl_extractOpenMode( const Any& _rValue, sal_Int32& _out_rMode ) + { + OpenCommandArgument aOpenCommand; + if ( _rValue >>= aOpenCommand ) + _out_rMode = aOpenCommand.Mode; + else + { + OpenCommandArgument2 aOpenCommand2; + if ( _rValue >>= aOpenCommand2 ) + _out_rMode = aOpenCommand2.Mode; + else + return false; + } + return true; + } +} + +void ODocumentDefinition::impl_removeFrameFromDesktop_throw( const Reference<XComponentContext> & _rxContext, const Reference< XFrame >& _rxFrame ) +{ + Reference< XDesktop2 > xDesktop = Desktop::create( _rxContext ); + Reference< XFrames > xFrames( xDesktop->getFrames(), UNO_SET_THROW ); + xFrames->remove( _rxFrame ); +} + +void ODocumentDefinition::impl_onActivateEmbeddedObject_nothrow( const bool i_bReactivated ) +{ + try + { + Reference< XModel > xModel( getComponent(), UNO_QUERY ); + Reference< XController > xController( xModel.is() ? xModel->getCurrentController() : Reference< XController >() ); + if ( !xController.is() ) + return; + + if ( !m_xListener.is() ) + // it's the first time the embedded object has been activated + // create an OEmbedObjectHolder + m_xListener = new OEmbedObjectHolder( m_xEmbeddedObject, this ); + + // raise the window to top (especially necessary if this is not the first activation) + Reference< XFrame > xFrame( xController->getFrame(), UNO_SET_THROW ); + Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + xTopWindow->toFront(); + + // remove the frame from the desktop's frame collection because we need full control of it. + impl_removeFrameFromDesktop_throw( m_aContext, xFrame ); + + // ensure that we ourself are kept alive as long as the embedded object's frame is + // opened + LifetimeCoupler::couple( *this, xFrame ); + + // init the edit view + if ( m_bForm && m_bOpenInDesign && !i_bReactivated ) + impl_initFormEditView( xController ); + } + catch( const RuntimeException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +namespace +{ + // PreserveVisualAreaSize + /** stack-guard for preserving the size of the VisArea of an XModel + */ + class PreserveVisualAreaSize + { + private: + Reference< XVisualObject > m_xVisObject; + awt::Size m_aOriginalSize; + + public: + explicit PreserveVisualAreaSize( const Reference< XModel >& _rxModel ) + :m_xVisObject( _rxModel, UNO_QUERY ) + { + if ( m_xVisObject.is() ) + { + try + { + m_aOriginalSize = m_xVisObject->getVisualAreaSize( Aspects::MSOLE_CONTENT ); + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::PreserveVisualAreaSize" ); + } + } + } + + ~PreserveVisualAreaSize() + { + if ( m_xVisObject.is() && m_aOriginalSize.Width && m_aOriginalSize.Height ) + { + try + { + m_xVisObject->setVisualAreaSize( Aspects::MSOLE_CONTENT, m_aOriginalSize ); + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::~PreserveVisualAreaSize" ); + } + } + } + }; + + // LayoutManagerLock + /** helper class for stack-usage which during its lifetime locks a layout manager + */ + class LayoutManagerLock + { + private: + Reference< XLayoutManager > m_xLayoutManager; + + public: + explicit LayoutManagerLock( const Reference< XController >& _rxController ) + { + OSL_ENSURE( _rxController.is(), "LayoutManagerLock::LayoutManagerLock: this will crash!" ); + Reference< XFrame > xFrame( _rxController->getFrame() ); + try + { + Reference< XPropertySet > xPropSet( xFrame, UNO_QUERY_THROW ); + m_xLayoutManager.set( + xPropSet->getPropertyValue( "LayoutManager" ), + UNO_QUERY_THROW ); + m_xLayoutManager->lock(); + + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::LayoutManagerLock" ); + } + } + + ~LayoutManagerLock() + { + try + { + // unlock the layout manager + if ( m_xLayoutManager.is() ) + m_xLayoutManager->unlock(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::~LayoutManagerLock" ); + } + } + }; +} + +void ODocumentDefinition::impl_initFormEditView( const Reference< XController >& _rxController ) +{ + try + { + Reference< XViewSettingsSupplier > xSettingsSupplier( _rxController, UNO_QUERY_THROW ); + Reference< XPropertySet > xViewSettings( xSettingsSupplier->getViewSettings(), UNO_SET_THROW ); + + // the below code could indirectly tamper with the "modified" flag of the model, temporarily disable this + LockModifiable aLockModify( _rxController->getModel() ); + + // The visual area size can be changed by the setting of the following properties + // so it should be restored later + PreserveVisualAreaSize aPreserveVisAreaSize( _rxController->getModel() ); + + // Layout manager should not layout while the size is still not restored + // so it will stay locked for this time + LayoutManagerLock aLockLayout( _rxController ); + + // setting of the visual properties + xViewSettings->setPropertyValue("ShowRulers",Any(true)); + xViewSettings->setPropertyValue("ShowVertRuler",Any(true)); + xViewSettings->setPropertyValue("ShowHoriRuler",Any(true)); + xViewSettings->setPropertyValue("IsRasterVisible",Any(true)); + xViewSettings->setPropertyValue("IsSnapToRaster",Any(true)); + xViewSettings->setPropertyValue("ShowOnlineLayout",Any(true)); + xViewSettings->setPropertyValue("RasterSubdivisionX",Any(sal_Int32(5))); + xViewSettings->setPropertyValue("RasterSubdivisionY",Any(sal_Int32(5))); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODocumentDefinition::impl_showOrHideComponent_throw( const bool i_bShow ) +{ + const sal_Int32 nCurrentState = m_xEmbeddedObject.is() ? m_xEmbeddedObject->getCurrentState() : EmbedStates::LOADED; + switch ( nCurrentState ) + { + default: + case EmbedStates::LOADED: + throw embed::WrongStateException( OUString(), *this ); + + case EmbedStates::RUNNING: + if ( !i_bShow ) + // fine, a running (and not yet active) object is never visible + return; + { + LockModifiable aLockModify( impl_getComponent_throw() ); + m_xEmbeddedObject->changeState( EmbedStates::ACTIVE ); + impl_onActivateEmbeddedObject_nothrow( false ); + } + break; + + case EmbedStates::ACTIVE: + { + Reference< XModel > xEmbeddedDoc( impl_getComponent_throw(), UNO_QUERY_THROW ); + Reference< XController > xEmbeddedController( xEmbeddedDoc->getCurrentController(), UNO_SET_THROW ); + Reference< XFrame > xEmbeddedFrame( xEmbeddedController->getFrame(), UNO_SET_THROW ); + Reference< XWindow > xEmbeddedWindow( xEmbeddedFrame->getContainerWindow(), UNO_SET_THROW ); + xEmbeddedWindow->setVisible( i_bShow ); + } + break; + } +} + +Any ODocumentDefinition::onCommandOpenSomething( const Any& _rOpenArgument, const bool _bActivate, + const Reference< XCommandEnvironment >& _rxEnvironment ) +{ + OExecuteImpl aExecuteGuard( m_bInExecute ); + + Reference< XConnection > xConnection; + sal_Int32 nOpenMode = OpenMode::DOCUMENT; + + ::comphelper::NamedValueCollection aDocumentArgs; + + // for the document, default to the interaction handler as used for loading the DB doc + // This might be overwritten below, when examining _rOpenArgument. + const ::comphelper::NamedValueCollection& aDBDocArgs( m_pImpl->m_pDataSource->getMediaDescriptor() ); + Reference< XInteractionHandler > xHandler( aDBDocArgs.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() ) ); + if ( xHandler.is() ) + aDocumentArgs.put( "InteractionHandler", xHandler ); + + ::std::optional< sal_Int16 > aDocumentMacroMode; + + if ( !lcl_extractOpenMode( _rOpenArgument, nOpenMode ) ) + { + Sequence< PropertyValue > aArguments; + if ( _rOpenArgument >>= aArguments ) + { + const PropertyValue* pIter = aArguments.getConstArray(); + const PropertyValue* pEnd = pIter + aArguments.getLength(); + for ( ;pIter != pEnd; ++pIter ) + { + if ( pIter->Name == PROPERTY_ACTIVE_CONNECTION ) + { + xConnection.set( pIter->Value, UNO_QUERY ); + continue; + } + + if ( lcl_extractOpenMode( pIter->Value, nOpenMode ) ) + continue; + + if ( pIter->Name == "MacroExecutionMode" ) + { + sal_Int16 nMacroExecMode( !aDocumentMacroMode ? MacroExecMode::USE_CONFIG : *aDocumentMacroMode ); + OSL_VERIFY( pIter->Value >>= nMacroExecMode ); + aDocumentMacroMode = nMacroExecMode; + continue; + } + + // unknown argument -> pass to the loaded document + aDocumentArgs.put( pIter->Name, pIter->Value ); + } + } + } + + bool bExecuteDBDocMacros = m_pImpl->m_pDataSource->checkMacrosOnLoading(); + // Note that this call implies the user might be asked for the macro execution mode. + // Normally, this would happen when the database document is loaded, and subsequent calls + // will simply use the user's decision from this point in time. + // However, it is possible to programmatically load forms/reports, without actually + // loading the database document into a frame. In this case, the user will be asked + // here and now. + // #i87741# + + // allow the command arguments to downgrade the macro execution mode, but not to upgrade + // it + if ( ( m_pImpl->m_pDataSource->getImposedMacroExecMode() == MacroExecMode::USE_CONFIG ) + && bExecuteDBDocMacros + ) + { + // while loading the whole database document, USE_CONFIG, was passed. + // Additionally, *by now* executing macros from the DB doc is allowed (this is what bExecuteDBDocMacros + // indicates). This means either one of: + // 1. The DB doc or one of the sub docs contained macros and + // 1a. the user explicitly allowed executing them + // 1b. the configuration allows executing them without asking the user + // 2. Neither the DB doc nor the sub docs contained macros, thus macro + // execution was silently enabled, assuming that any macro will be a + // user-created macro + // + // The problem with this: If the to-be-opened sub document has macros embedded in + // the content.xml (which is valid ODF, but normally not produced by OOo itself), + // then this has not been detected while loading the database document - it would + // be too expensive, as it effectively would require loading all forms/reports. + // + // So, in such a case, and with 2. above, we would silently execute those macros, + // regardless of the global security settings - which would be a security issue, of + // course. + if ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::eNoMacros ) + { + // this is case 2. from above + // So, pass a USE_CONFIG to the to-be-loaded document. This means that + // the user will be prompted with a security message upon opening this + // sub document, in case the settings require this, *and* the document + // contains scripts in the content.xml. But this is better than the security + // issue we had before ... + aDocumentMacroMode = MacroExecMode::USE_CONFIG; + } + } + + if ( !aDocumentMacroMode ) + { + // nobody so far felt responsible for setting it + // => use the DBDoc-wide macro exec mode for the document, too + aDocumentMacroMode = bExecuteDBDocMacros ? MacroExecMode::ALWAYS_EXECUTE_NO_WARN + : MacroExecMode::NEVER_EXECUTE; + } + aDocumentArgs.put( "MacroExecutionMode", *aDocumentMacroMode ); + + if ( ( nOpenMode == OpenMode::ALL ) + || ( nOpenMode == OpenMode::FOLDERS ) + || ( nOpenMode == OpenMode::DOCUMENTS ) + || ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_NONE ) + || ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_WRITE ) + ) + { + // not supported + ucbhelper::cancelCommandExecution( + Any( UnsupportedOpenModeException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + sal_Int16( nOpenMode ) ) ), + _rxEnvironment ); + // Unreachable + OSL_FAIL( "unreachable" ); + } + + OSL_ENSURE( !m_pImpl->m_aProps.sPersistentName.isEmpty(), + "ODocumentDefinition::onCommandOpenSomething: no persistent name - cannot load!" ); + if ( m_pImpl->m_aProps.sPersistentName.isEmpty() ) + return Any(); + + // embedded objects themself do not support the hidden flag. We implement support for + // it by changing the STATE to RUNNING only, instead of ACTIVE. + bool bOpenHidden = aDocumentArgs.getOrDefault( "Hidden", false ); + aDocumentArgs.remove( "Hidden" ); + + loadEmbeddedObject( xConnection, Sequence< sal_Int8 >(), aDocumentArgs.getPropertyValues(), false, !m_bOpenInDesign ); + OSL_ENSURE( m_xEmbeddedObject.is(), "ODocumentDefinition::onCommandOpenSomething: what's this?" ); + if ( !m_xEmbeddedObject.is() ) + return Any(); + + Reference< XModel > xModel( getComponent(), UNO_QUERY ); + Reference< report::XReportDefinition > xReportDefinition(xModel,UNO_QUERY); + + Reference< XModule > xModule( xModel, UNO_QUERY ); + if ( xModule.is() ) + { + if ( m_bForm ) + xModule->setIdentifier( "com.sun.star.sdb.FormDesign" ); + else if ( !xReportDefinition.is() ) + xModule->setIdentifier( "com.sun.star.text.TextDocument" ); + + updateDocumentTitle(); + } + + bool bIsAliveNewStyleReport = ( !m_bOpenInDesign && xReportDefinition.is() ); + if ( bIsAliveNewStyleReport ) + { + // we are in ReadOnly mode + // we would like to open the Writer or Calc with the report direct, without design it. + Reference< report::XReportEngine > xReportEngine( m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.report.OReportEngineJFree", m_aContext), UNO_QUERY_THROW ); + + xReportEngine->setReportDefinition(xReportDefinition); + xReportEngine->setActiveConnection(m_xLastKnownConnection); + if ( bOpenHidden ) + return Any( xReportEngine->createDocumentModel() ); + return Any( xReportEngine->createDocumentAlive( nullptr ) ); + } + + if ( _bActivate && !bOpenHidden ) + { + LockModifiable aLockModify( impl_getComponent_throw() ); + m_xEmbeddedObject->changeState( EmbedStates::ACTIVE ); + impl_onActivateEmbeddedObject_nothrow( false ); + } + else + { + // ensure that we ourself are kept alive as long as the document is open + LifetimeCoupler::couple( *this, xModel ); + } + + if ( !m_bForm && m_pImpl->m_aProps.bAsTemplate && !m_bOpenInDesign ) + ODocumentDefinition::fillReportData( m_aContext, getComponent(), xConnection ); + + return Any( xModel ); +} + +Any SAL_CALL ODocumentDefinition::execute( const Command& aCommand, sal_Int32 CommandId, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + + bool bOpen = aCommand.Name == "open"; + bool bOpenInDesign = aCommand.Name == "openDesign"; + bool bOpenForMail = aCommand.Name == "openForMail"; + if ( bOpen || bOpenInDesign || bOpenForMail ) + { + // opening the document involves a lot of VCL code, which is not thread-safe, but needs the SolarMutex locked. + // Unfortunately, the DocumentDefinition, as well as the EmbeddedObject implementation, calls into VCL-dependent + // components *without* releasing the own mutex, which is a guaranteed recipe for deadlocks. + // We have control over this implementation here, and in modifying it to release the own mutex before calling into + // the VCL-dependent components is not too difficult (was there, seen it). + // However, we do /not/ have control over the EmbeddedObject implementation, and from a first look, it seems as + // making it release the own mutex before calling SolarMutex-code is ... difficult, at least. + // So, to be on the same side, we lock the SolarMutex here. Yes, it sucks. + ::SolarMutexGuard aSolarGuard; + osl::MutexGuard aGuard(m_aMutex); + if ( m_bInExecute ) + return aRet; + + bool bActivateObject = true; + if ( bOpenForMail ) + { + OSL_FAIL( "ODocumentDefinition::execute: 'openForMail' should not be used anymore - use the 'Hidden' parameter instead!" ); + bActivateObject = false; + } + + // if the object is already opened, do nothing + if ( m_xEmbeddedObject.is() ) + { + sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState(); + bool bIsActive = ( nCurrentState == EmbedStates::ACTIVE ); + + if ( bIsActive ) + { + // exception: new-style reports always create a new document when "open" is executed + Reference< report::XReportDefinition > xReportDefinition( impl_getComponent_throw( false ), UNO_QUERY ); + bool bIsAliveNewStyleReport = ( xReportDefinition.is() && ( bOpen || bOpenForMail ) ); + + if ( !bIsAliveNewStyleReport ) + { + impl_onActivateEmbeddedObject_nothrow( true ); + return Any( getComponent() ); + } + } + } + + m_bOpenInDesign = bOpenInDesign || bOpenForMail; + return onCommandOpenSomething( aCommand.Argument, bActivateObject, Environment ); + } + + osl::MutexGuard aGuard(m_aMutex); + if ( m_bInExecute ) + return aRet; + + if ( aCommand.Name == "copyTo" ) + { + Sequence<Any> aIni; + aCommand.Argument >>= aIni; + if ( aIni.getLength() != 2 ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + Reference< XStorage> xDest(aIni[0],UNO_QUERY); + OUString sPersistentName; + aIni[1] >>= sPersistentName; + Reference< XStorage> xStorage = getContainerStorage(); + + xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xDest,sPersistentName); + } + else if ( aCommand.Name == "preview" ) + { + onCommandPreview(aRet); + } + else if ( aCommand.Name == "insert" ) + { + Sequence<Any> aIni; + aCommand.Argument >>= aIni; + if ( !aIni.hasElements() ) + { + OSL_FAIL( "Wrong argument count!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + OUString sURL; + aIni[0] >>= sURL; + onCommandInsert( sURL, Environment ); + } + else if ( aCommand.Name == "getdocumentinfo" // compatibility + || aCommand.Name == "getDocumentInfo" ) + { + onCommandGetDocumentProperties( aRet ); + } + else if ( aCommand.Name == "delete" ) + { + // delete + closeObject(); + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + xStorage->removeElement(m_pImpl->m_aProps.sPersistentName); + + dispose(); + + } + else if ( aCommand.Name == "storeOwn" // compatibility + || aCommand.Name == "store" + ) + { + impl_store_throw(); + } + else if ( aCommand.Name == "shutdown" // compatibility + || aCommand.Name == "close" + ) + { + aRet <<= impl_close_throw(); + } + else if ( aCommand.Name == "show" ) + { + impl_showOrHideComponent_throw( true ); + } + else if ( aCommand.Name == "hide" ) + { + impl_showOrHideComponent_throw( false ); + } + else + { + aRet = OContentHelper::execute(aCommand,CommandId,Environment); + } + + return aRet; +} + +namespace +{ + void lcl_resetChildFormsToEmptyDataSource( const Reference< XIndexAccess>& _rxFormsContainer ) + { + OSL_PRECOND( _rxFormsContainer.is(), "lcl_resetChildFormsToEmptyDataSource: illegal call!" ); + sal_Int32 count = _rxFormsContainer->getCount(); + for ( sal_Int32 i = 0; i < count; ++i ) + { + Reference< XForm > xForm( _rxFormsContainer->getByIndex( i ), UNO_QUERY ); + if ( !xForm.is() ) + continue; + + // if the element is a form, reset its DataSourceName property to an empty string + try + { + Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW ); + xFormProps->setPropertyValue( PROPERTY_DATASOURCENAME, Any( OUString() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // if the element is a container itself, step down the component hierarchy + Reference< XIndexAccess > xContainer( xForm, UNO_QUERY ); + if ( xContainer.is() ) + lcl_resetChildFormsToEmptyDataSource( xContainer ); + } + } + + void lcl_resetFormsToEmptyDataSource( const Reference< XEmbeddedObject>& _rxEmbeddedObject ) + { + try + { + Reference< XDrawPageSupplier > xSuppPage( _rxEmbeddedObject->getComponent(), UNO_QUERY_THROW ); + // if this interface does not exist, then either getComponent returned NULL, + // or the document is a multi-page document. The latter is allowed, but currently + // simply not handled by this code, as it would not normally happen. + + Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW ); + lcl_resetChildFormsToEmptyDataSource( xForms ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + } +} + +void ODocumentDefinition::onCommandInsert( const OUString& _sURL, const Reference< XCommandEnvironment >& Environment ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + // Check, if all required properties were set. + if ( _sURL.isEmpty() || m_xEmbeddedObject.is() ) + { + OSL_FAIL( "Content::onCommandInsert - property value missing!" ); + + Sequence<OUString> aProps { PROPERTY_URL }; + ucbhelper::cancelCommandExecution( + Any( MissingPropertiesException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + aProps ) ), + Environment ); + // Unreachable + } + + if ( !m_xEmbeddedObject.is() ) + { + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + { + Reference< XEmbeddedObjectCreator> xEmbedFactory = EmbeddedObjectCreator::create(m_aContext); + Sequence<PropertyValue> aEmpty; + Sequence<PropertyValue> aMediaDesc{ comphelper::makePropertyValue(PROPERTY_URL, + _sURL) }; + m_xEmbeddedObject.set(xEmbedFactory->createInstanceInitFromMediaDescriptor( xStorage + ,m_pImpl->m_aProps.sPersistentName + ,aMediaDesc + ,aEmpty),UNO_QUERY); + + lcl_resetFormsToEmptyDataSource( m_xEmbeddedObject ); + // #i57669# + + Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + } + try + { + if ( m_xEmbeddedObject.is() ) + m_xEmbeddedObject->close(true); + } + catch(const Exception&) + { + } + m_xEmbeddedObject = nullptr; + } + } + + aGuard.clear(); +} + +bool ODocumentDefinition::save(bool _bApprove, const css::uno::Reference<css::awt::XTopWindow>& rDialogParent) +{ + // default handling: instantiate an interaction handler and let it handle the parameter request + if ( !m_bOpenInDesign ) + return false; + try + { + + { + ::SolarMutexGuard aSolarGuard; + + // the request + Reference<XNameAccess> xName(m_xParentContainer,UNO_QUERY); + DocumentSaveRequest aRequest; + aRequest.Name = m_pImpl->m_aProps.aTitle; + if ( aRequest.Name.isEmpty() ) + { + if ( m_bForm ) + aRequest.Name = DBA_RES( RID_STR_FORM ); + else + aRequest.Name = DBA_RES( RID_STR_REPORT ); + aRequest.Name = ::dbtools::createUniqueName(xName,aRequest.Name); + } + + aRequest.Content.set(m_xParentContainer,UNO_QUERY); + rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + // two continuations allowed: OK and Cancel + rtl::Reference<ODocumentSaveContinuation> pDocuSave; + + if ( m_pImpl->m_aProps.aTitle.isEmpty() ) + { + pDocuSave = new ODocumentSaveContinuation; + pRequest->addContinuation(pDocuSave); + } + if ( _bApprove ) + { + rtl::Reference<OInteraction< XInteractionApprove >> pApprove = new OInteraction< XInteractionApprove >; + pRequest->addContinuation(pApprove); + } + + rtl::Reference<OInteraction< XInteractionDisapprove >> pDisApprove = new OInteraction< XInteractionDisapprove >; + pRequest->addContinuation(pDisApprove); + + rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort; + pRequest->addContinuation(pAbort); + + Reference<XWindow> xDialogParent(rDialogParent, UNO_QUERY); + + // create the handler, let it handle the request + Reference<XInteractionHandler2> xHandler(InteractionHandler::createWithParent(m_aContext, xDialogParent)); + xHandler->handle(pRequest); + + if ( pAbort->wasSelected() ) + return false; + if ( pDisApprove->wasSelected() ) + return true; + if ( pDocuSave && pDocuSave->wasSelected() ) + { + Reference<XNameContainer> xNC( pDocuSave->getContent(), UNO_QUERY_THROW ); + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + NameChangeNotifier aNameChangeAndNotify( *this, pDocuSave->getName(), aGuard ); + m_pImpl->m_aProps.aTitle = pDocuSave->getName(); + + Reference< XContent> xContent = this; + xNC->insertByName(pDocuSave->getName(),Any(xContent)); + + updateDocumentTitle(); + } + } + + ::osl::MutexGuard aGuard(m_aMutex); + Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!"); + } + return true; +} + +void ODocumentDefinition::saveAs() +{ + // default handling: instantiate an interaction handler and let it handle the parameter request + if ( !m_bOpenInDesign ) + return; + + { + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + if ( m_pImpl->m_aProps.aTitle.isEmpty() ) + { + aGuard.clear(); + save(false, css::uno::Reference<css::awt::XTopWindow>()); // (sal_False) : we don't want an approve dialog + return; + } + } + try + { + ::SolarMutexGuard aSolarGuard; + + // the request + DocumentSaveRequest aRequest; + aRequest.Name = m_pImpl->m_aProps.aTitle; + + aRequest.Content.set(m_xParentContainer,UNO_QUERY); + rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + // two continuations allowed: OK and Cancel + rtl::Reference<ODocumentSaveContinuation> pDocuSave = new ODocumentSaveContinuation; + pRequest->addContinuation(pDocuSave); + rtl::Reference<OInteraction< XInteractionDisapprove >> pDisApprove = new OInteraction< XInteractionDisapprove >; + pRequest->addContinuation(pDisApprove); + rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort; + pRequest->addContinuation(pAbort); + + // create the handler, let it handle the request + Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) ); + xHandler->handle(pRequest); + + if ( pAbort->wasSelected() ) + return; + if ( pDisApprove->wasSelected() ) + return; + if ( pDocuSave->wasSelected() ) + { + ::osl::MutexGuard aGuard(m_aMutex); + Reference<XNameContainer> xNC(pDocuSave->getContent(),UNO_QUERY); + if ( xNC.is() ) + { + if ( m_pImpl->m_aProps.aTitle != pDocuSave->getName() ) + { + try + { + Reference< XStorage> xStorage = getContainerStorage(); + + OUString sPersistentName = ::dbtools::createUniqueName(xStorage,"Obj"); + xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xStorage,sPersistentName); + + OUString sOldName = m_pImpl->m_aProps.aTitle; + rename(pDocuSave->getName()); + updateDocumentTitle(); + + uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {PROPERTY_NAME, uno::Any(sOldName)}, // set as folder + {PROPERTY_PERSISTENT_NAME, uno::Any(sPersistentName)}, + {PROPERTY_AS_TEMPLATE, uno::Any(m_pImpl->m_aProps.bAsTemplate)}, + })); + Reference< XMultiServiceFactory > xORB( m_xParentContainer, UNO_QUERY_THROW ); + Reference< XInterface > xComponent( xORB->createInstanceWithArguments( SERVICE_SDB_DOCUMENTDEFINITION, aArguments ) ); + Reference< XNameContainer > xNameContainer( m_xParentContainer, UNO_QUERY_THROW ); + xNameContainer->insertByName( sOldName, Any( xComponent ) ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } + } + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!"); + } +} + +namespace +{ + void lcl_putLoadArgs( ::comphelper::NamedValueCollection& _io_rArgs, const optional_bool& _bSuppressMacros, const optional_bool& _bReadOnly ) + { + if ( !!_bSuppressMacros ) + { + if ( *_bSuppressMacros ) + { + // if we're to suppress macros, do exactly this + _io_rArgs.put( "MacroExecutionMode", MacroExecMode::NEVER_EXECUTE ); + } + else + { + // otherwise, put the setting only if not already present + if ( !_io_rArgs.has( "MacroExecutionMode" ) ) + { + _io_rArgs.put( "MacroExecutionMode", MacroExecMode::USE_CONFIG ); + } + } + } + + if ( !!_bReadOnly ) + _io_rArgs.put( "ReadOnly", *_bReadOnly ); + } +} + +namespace +{ + Reference< XFrame > lcl_getDatabaseDocumentFrame( ODatabaseModelImpl const & _rImpl ) + { + Reference< XModel > xDatabaseDocumentModel( _rImpl.getModel_noCreate() ); + + Reference< XController > xDatabaseDocumentController; + if ( xDatabaseDocumentModel.is() ) + xDatabaseDocumentController = xDatabaseDocumentModel->getCurrentController(); + + Reference< XFrame > xFrame; + if ( xDatabaseDocumentController.is() ) + xFrame = xDatabaseDocumentController->getFrame(); + + return xFrame; + } +} + +bool ODocumentDefinition::objectSupportsEmbeddedScripts() const +{ + bool bAllowDocumentMacros = !m_pImpl->m_pDataSource + || ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::eSubDocumentMacros ); + + // if *any* of the objects of the database document already has macros, we + // continue to allow it to have them, until the user does a migration. + // If there are no macros, we don't allow them to be created. + + return bAllowDocumentMacros; +} + +OUString ODocumentDefinition::determineContentType() const +{ + return lcl_determineContentType_nothrow( getContainerStorage(), m_pImpl->m_aProps.sPersistentName ); +} + +void ODocumentDefinition::separateOpenCommandArguments( const Sequence< PropertyValue >& i_rOpenCommandArguments, + ::comphelper::NamedValueCollection& o_rDocumentLoadArgs, ::comphelper::NamedValueCollection& o_rEmbeddedObjectDescriptor ) +{ + ::comphelper::NamedValueCollection aOpenCommandArguments( i_rOpenCommandArguments ); + + static const std::u16string_view sObjectDescriptorArgs[] = { u"RecoveryStorage" }; + for (const auto& rObjectDescriptorArg : sObjectDescriptorArgs) + { + const OUString sObjectDescriptorArg(rObjectDescriptorArg); + if ( aOpenCommandArguments.has( sObjectDescriptorArg ) ) + { + o_rEmbeddedObjectDescriptor.put( sObjectDescriptorArg, aOpenCommandArguments.get( sObjectDescriptorArg ) ); + aOpenCommandArguments.remove( sObjectDescriptorArg ); + } + } + + o_rDocumentLoadArgs.merge( aOpenCommandArguments, false ); +} + +Sequence< PropertyValue > ODocumentDefinition::fillLoadArgs( const Reference< XConnection>& _xConnection, const bool _bSuppressMacros, const bool _bReadOnly, + const Sequence< PropertyValue >& i_rOpenCommandArguments, Sequence< PropertyValue >& _out_rEmbeddedObjectDescriptor ) +{ + // (re-)create interceptor, and put it into the descriptor of the embedded object + if ( m_pInterceptor.is() ) + { + m_pInterceptor->dispose(); + m_pInterceptor.clear(); + } + + m_pInterceptor = new OInterceptor( this ); + Reference<XDispatchProviderInterceptor> xInterceptor = m_pInterceptor; + + ::comphelper::NamedValueCollection aEmbeddedDescriptor; + aEmbeddedDescriptor.put( "OutplaceDispatchInterceptor", xInterceptor ); + + ::comphelper::NamedValueCollection aMediaDesc; + separateOpenCommandArguments( i_rOpenCommandArguments, aMediaDesc, aEmbeddedDescriptor ); + + // create the OutplaceFrameProperties, and put them into the descriptor of the embedded object + ::comphelper::NamedValueCollection OutplaceFrameProperties; + OutplaceFrameProperties.put( "TopWindow", true ); + OutplaceFrameProperties.put( "SupportPersistentWindowState", true ); + + Reference< XFrame > xParentFrame; + if ( m_pImpl->m_pDataSource ) + xParentFrame = lcl_getDatabaseDocumentFrame( *m_pImpl->m_pDataSource ); + if ( !xParentFrame.is() ) + { // i87957 we need a parent frame + Reference< XDesktop2 > xDesktop = Desktop::create( m_aContext ); + xParentFrame.set( xDesktop, UNO_QUERY_THROW ); + Reference<util::XCloseable> xCloseable(m_pImpl->m_pDataSource->getModel_noCreate(),UNO_QUERY); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener(this); + m_bRemoveListener = true; + } + } + OSL_ENSURE( xParentFrame.is(), "ODocumentDefinition::fillLoadArgs: no parent frame!" ); + if ( xParentFrame.is() ) + OutplaceFrameProperties.put( "ParentFrame", xParentFrame ); + + aEmbeddedDescriptor.put( "OutplaceFrameProperties", OutplaceFrameProperties.getNamedValues() ); + + // tell the embedded object to have (or not have) script support + aEmbeddedDescriptor.put( "EmbeddedScriptSupport", objectSupportsEmbeddedScripts() ); + + // tell the embedded object to not participate in the document recovery game - the DB doc will handle it + aEmbeddedDescriptor.put( "DocumentRecoverySupport", false ); + + // pass the descriptor of the embedded object to the caller + aEmbeddedDescriptor >>= _out_rEmbeddedObjectDescriptor; + + // create the ComponentData, and put it into the document's media descriptor + { + ::comphelper::NamedValueCollection aComponentData; + aComponentData.put( "ActiveConnection", _xConnection ); + aComponentData.put( "ApplyFormDesignMode", !_bReadOnly ); + aMediaDesc.put( "ComponentData", aComponentData.getPropertyValues() ); + } + + if ( !m_pImpl->m_aProps.aTitle.isEmpty() ) + aMediaDesc.put( "DocumentTitle", m_pImpl->m_aProps.aTitle ); + + aMediaDesc.put( "DocumentBaseURL", m_pImpl->m_pDataSource->getURL() ); + + // put the common load arguments into the document's media descriptor + lcl_putLoadArgs( aMediaDesc, optional_bool( _bSuppressMacros ), optional_bool( _bReadOnly ) ); + + return aMediaDesc.getPropertyValues(); +} + +void ODocumentDefinition::loadEmbeddedObject( const Reference< XConnection >& i_rConnection, const Sequence< sal_Int8 >& _aClassID, + const Sequence< PropertyValue >& i_rOpenCommandArguments, const bool _bSuppressMacros, const bool _bReadOnly ) +{ + if ( !m_xEmbeddedObject.is() ) + { + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + { + Reference< XEmbeddedObjectCreator> xEmbedFactory = OOoEmbeddedObjectFactory::create(m_aContext); + OUString sDocumentService; + bool bSetSize = false; + sal_Int32 nEntryConnectionMode = EntryInitModes::DEFAULT_INIT; + Sequence< sal_Int8 > aClassID = _aClassID; + if ( aClassID.hasElements() ) + { + nEntryConnectionMode = EntryInitModes::TRUNCATE_INIT; + bSetSize = true; + } + else + { + sDocumentService = GetDocumentServiceFromMediaType( getContentType(), m_aContext, aClassID ); + // check if we are not a form and + // the org.libreoffice.report.pentaho.SOReportJobFactory is not present. + if ( !m_bForm && sDocumentService != "com.sun.star.text.TextDocument") + { + // we seem to be a "new style" report, check if report extension is present. + Reference< XContentEnumerationAccess > xEnumAccess( m_aContext->getServiceManager(), UNO_QUERY ); + const OUString sReportEngineServiceName = ::dbtools::getDefaultReportEngineServiceName(m_aContext); + Reference< XEnumeration > xEnumDrivers = xEnumAccess->createContentEnumeration(sReportEngineServiceName); + if ( !xEnumDrivers.is() || !xEnumDrivers->hasMoreElements() ) + { + css::io::WrongFormatException aWFE; + aWFE.Message = DBA_RES( RID_STR_MISSING_EXTENSION ); + throw aWFE; + } + } + if ( !aClassID.hasElements() ) + { + if ( m_bForm ) + aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_SW_CLASSID); + else + { + aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_RPT_CLASSID_90); + } + } + } + + OSL_ENSURE( aClassID.hasElements(),"No Class ID" ); + + Sequence< PropertyValue > aEmbeddedObjectDescriptor; + Sequence< PropertyValue > aLoadArgs( fillLoadArgs( + i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) ); + + m_xEmbeddedObject.set(xEmbedFactory->createInstanceUserInit(aClassID + ,sDocumentService + ,xStorage + ,m_pImpl->m_aProps.sPersistentName + ,nEntryConnectionMode + ,aLoadArgs + ,aEmbeddedObjectDescriptor + ),UNO_QUERY); + if ( m_xEmbeddedObject.is() ) + { + if ( !m_pClientHelper.is() ) + { + m_pClientHelper = new OEmbeddedClientHelper; + } + m_xEmbeddedObject->setClientSite(m_pClientHelper); + m_xEmbeddedObject->changeState(EmbedStates::RUNNING); + if ( bSetSize ) + { + LockModifiable aLockModify( impl_getComponent_throw( false ) ); + + awt::Size aSize( DEFAULT_WIDTH, DEFAULT_HEIGHT ); + m_xEmbeddedObject->setVisualAreaSize(Aspects::MSOLE_CONTENT,aSize); + } + } + } + } + else + { + sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState(); + if ( nCurrentState == EmbedStates::LOADED ) + { + if ( !m_pClientHelper.is() ) + { + m_pClientHelper = new OEmbeddedClientHelper; + } + m_xEmbeddedObject->setClientSite(m_pClientHelper); + + Sequence< PropertyValue > aEmbeddedObjectDescriptor; + Sequence< PropertyValue > aLoadArgs( fillLoadArgs( + i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) ); + + Reference<XCommonEmbedPersist> xCommon(m_xEmbeddedObject,UNO_QUERY); + OSL_ENSURE(xCommon.is(),"unsupported interface!"); + if ( xCommon.is() ) + xCommon->reload( aLoadArgs, aEmbeddedObjectDescriptor ); + m_xEmbeddedObject->changeState(EmbedStates::RUNNING); + } + else + { + OSL_ENSURE( ( nCurrentState == EmbedStates::RUNNING ) || ( nCurrentState == EmbedStates::ACTIVE ), + "ODocumentDefinition::loadEmbeddedObject: unexpected state!" ); + + // if the document was already loaded (which means the embedded object is in state RUNNING or ACTIVE), + // then just re-set some model parameters + try + { + // ensure the media descriptor doesn't contain any values which are intended for the + // EmbeddedObjectDescriptor only + ::comphelper::NamedValueCollection aEmbeddedObjectDescriptor; + ::comphelper::NamedValueCollection aNewMediaDesc; + separateOpenCommandArguments( i_rOpenCommandArguments, aNewMediaDesc, aEmbeddedObjectDescriptor ); + + // merge the new media descriptor into the existing media descriptor + const Reference< XModel > xModel( getComponent(), UNO_QUERY_THROW ); + const Sequence< PropertyValue > aArgs = xModel->getArgs(); + ::comphelper::NamedValueCollection aExistentMediaDesc( aArgs ); + aExistentMediaDesc.merge( aNewMediaDesc, false ); + + lcl_putLoadArgs( aExistentMediaDesc, optional_bool(), optional_bool() ); + // don't put _bSuppressMacros and _bReadOnly here - if the document was already + // loaded, we should not tamper with its settings. + // #i88977# #i86872# + + xModel->attachResource( xModel->getURL(), aExistentMediaDesc.getPropertyValues() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + // set the OfficeDatabaseDocument instance as parent of the embedded document + // #i40358# + Reference< XChild > xDepdendDocAsChild( getComponent(), UNO_QUERY ); + if ( xDepdendDocAsChild.is() ) + { + try + { + if ( !xDepdendDocAsChild->getParent().is() ) + { // first encounter + xDepdendDocAsChild->setParent( getDataSource( m_xParentContainer ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + if ( i_rConnection.is() ) + m_xLastKnownConnection = i_rConnection; +} + +void ODocumentDefinition::onCommandPreview(Any& _rImage) +{ + loadEmbeddedObjectForPreview(); + if ( !m_xEmbeddedObject.is() ) + return; + + try + { + Reference<XTransferable> xTransfer(getComponent(),UNO_QUERY); + if ( xTransfer.is() ) + { + DataFlavor aFlavor; + aFlavor.MimeType = "image/png"; + aFlavor.HumanPresentableName = "Portable Network Graphics"; + aFlavor.DataType = cppu::UnoType<Sequence < sal_Int8 >>::get(); + + _rImage = xTransfer->getTransferData( aFlavor ); + } + } + catch( const Exception& ) + { + } +} + +void ODocumentDefinition::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const +{ + _rDefault.clear(); +} + +void ODocumentDefinition::onCommandGetDocumentProperties( Any& _rProps ) +{ + loadEmbeddedObjectForPreview(); + if ( !m_xEmbeddedObject.is() ) + return; + + try + { + Reference<XDocumentPropertiesSupplier> xDocSup( + getComponent(), UNO_QUERY ); + if ( xDocSup.is() ) + _rProps <<= xDocSup->getDocumentProperties(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< util::XCloseable > ODocumentDefinition::impl_getComponent_throw( const bool i_ForceCreate ) +{ + OSL_ENSURE(m_xEmbeddedObject.is(),"Illegal call for embeddedObject"); + Reference< util::XCloseable > xComp; + if ( m_xEmbeddedObject.is() ) + { + int nState = m_xEmbeddedObject->getCurrentState(); + if ( ( nState == EmbedStates::LOADED ) && i_ForceCreate ) + { + m_xEmbeddedObject->changeState( EmbedStates::RUNNING ); + nState = m_xEmbeddedObject->getCurrentState(); + OSL_ENSURE( nState == EmbedStates::RUNNING, "ODocumentDefinition::impl_getComponent_throw: could not switch to RUNNING!" ); + } + + if ( nState == EmbedStates::ACTIVE || nState == EmbedStates::RUNNING ) + { + if ( m_xEmbeddedObject.is() ) + { + xComp = m_xEmbeddedObject->getComponent(); + OSL_ENSURE(xComp.is(),"No valid component"); + } + } + } + return xComp; +} + +Reference< util::XCloseable > ODocumentDefinition::getComponent() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getComponent_throw(); +} + +namespace +{ + Reference< XDatabaseDocumentUI > lcl_getDatabaseDocumentUI( ODatabaseModelImpl const & _rModelImpl ) + { + Reference< XDatabaseDocumentUI > xUI; + + Reference< XModel > xModel( _rModelImpl.getModel_noCreate() ); + if ( xModel.is() ) + xUI.set( xModel->getCurrentController(), UNO_QUERY ); + return xUI; + } +} + +Reference< XComponent > ODocumentDefinition::impl_openUI_nolck_throw( bool _bForEditing ) +{ + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( !m_pImpl || !m_pImpl->m_pDataSource ) + throw DisposedException(); + + Reference< XComponent > xComponent; + try + { + Reference< XDatabaseDocumentUI > xUI( lcl_getDatabaseDocumentUI( *m_pImpl->m_pDataSource ) ); + if ( !xUI.is() ) + { + // no XDatabaseDocumentUI -> just execute the respective command + m_bOpenInDesign = _bForEditing; + xComponent = Reference<XComponent>(onCommandOpenSomething(Any(), true, nullptr), UNO_QUERY); + OSL_ENSURE( xComponent.is(), "ODocumentDefinition::impl_openUI_nolck_throw: opening the thingie failed." ); + return xComponent; + } + + + OUString sName( impl_getHierarchicalName( false ) ); + sal_Int32 nObjectType = m_bForm ? DatabaseObject::FORM : DatabaseObject::REPORT; + aGuard.clear(); + + xComponent = xUI->loadComponent( + nObjectType, sName, _bForEditing + ); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } + + return xComponent; +} + +void ODocumentDefinition::impl_store_throw() +{ + Reference<XEmbedPersist> xPersist( m_xEmbeddedObject, UNO_QUERY ); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } +} + +bool ODocumentDefinition::impl_close_throw() +{ + bool bSuccess = prepareClose(); + if ( bSuccess && m_xEmbeddedObject.is() ) + { + m_xEmbeddedObject->changeState( EmbedStates::LOADED ); + bSuccess = m_xEmbeddedObject->getCurrentState() == EmbedStates::LOADED; + } + return bSuccess; +} + +Reference< XComponent > SAL_CALL ODocumentDefinition::open( ) +{ + return impl_openUI_nolck_throw( false ); +} + +Reference< XComponent > SAL_CALL ODocumentDefinition::openDesign( ) +{ + return impl_openUI_nolck_throw( true ); +} + +void SAL_CALL ODocumentDefinition::store( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + try + { + impl_store_throw(); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } +} + +sal_Bool SAL_CALL ODocumentDefinition::close( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bSuccess = false; + try + { + bSuccess = impl_close_throw(); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } + return bSuccess; +} + +OUString SAL_CALL ODocumentDefinition::getHierarchicalName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getHierarchicalName( false ); +} + +OUString SAL_CALL ODocumentDefinition::composeHierarchicalName( const OUString& i_rRelativeName ) +{ + return getHierarchicalName() + "/" + i_rRelativeName; +} + +void SAL_CALL ODocumentDefinition::rename( const OUString& _rNewName ) +{ + try + { + ::osl::ResettableMutexGuard aGuard(m_aMutex); + if ( _rNewName == m_pImpl->m_aProps.aTitle ) + return; + + // document definitions are organized in a hierarchical way, so reject names + // which contain a /, as this is reserved for hierarchy level separation + if ( _rNewName.indexOf( '/' ) != -1 ) + m_aErrorHelper.raiseException( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES, *this ); + + NameChangeNotifier aNameChangeAndNotify( *this, _rNewName, aGuard ); + m_pImpl->m_aProps.aTitle = _rNewName; + + if ( m_xEmbeddedObject.is() && m_xEmbeddedObject->getCurrentState() == EmbedStates::ACTIVE ) + updateDocumentTitle(); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(_rNewName,*this); + } +} + +Reference< XStorage> ODocumentDefinition::getContainerStorage() const +{ + return m_pImpl->m_pDataSource + ? m_pImpl->m_pDataSource->getStorage( m_bForm ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + : Reference< XStorage>(); +} + +bool ODocumentDefinition::isModified() +{ + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + bool bRet = false; + if ( m_xEmbeddedObject.is() ) + { + Reference<XModifiable> xModel(getComponent(),UNO_QUERY); + bRet = xModel.is() && xModel->isModified(); + } + return bRet; +} + +bool ODocumentDefinition::prepareClose() +{ + if ( !m_xEmbeddedObject.is() ) + return true; + + try + { + // suspend the controller. Embedded objects are not allowed to raise + // own UI at their own discretion, instead, this has always to be triggered + // by the embedding component. Thus, we do the suspend call here. + // #i49370# + + Reference< util::XCloseable > xComponent( impl_getComponent_throw( false ) ); + if ( !xComponent.is() ) + return true; + + Reference< XModel > xModel( xComponent, UNO_QUERY ); + Reference< XController > xController; + if ( xModel.is() ) + xController = xModel->getCurrentController(); + + OSL_ENSURE( xController.is() || ( m_xEmbeddedObject->getCurrentState() < EmbedStates::ACTIVE ), + "ODocumentDefinition::prepareClose: no controller!" ); + if ( !xController.is() ) + // document has not yet been activated, i.e. has no UI, yet + return true; + + if (!xController->suspend(true)) + // controller vetoed the closing + return false; + + if ( isModified() ) + { + Reference< XFrame > xFrame( xController->getFrame() ); + Reference<XTopWindow> xTopWindow; + if ( xFrame.is() ) + { + xTopWindow = Reference<XTopWindow>(xFrame->getContainerWindow(), UNO_QUERY_THROW); + xTopWindow->toFront(); + } + if (!save(true, xTopWindow)) + { + // revert suspension + xController->suspend(false); + // saving failed or was cancelled + return false; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return true; +} + +void ODocumentDefinition::fillReportData( const Reference< XComponentContext >& _rContext, + const Reference< util::XCloseable >& _rxComponent, + const Reference< XConnection >& _rxActiveConnection ) +{ + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"TextDocument", uno::Any(_rxComponent)}, + {"ActiveConnection", uno::Any(_rxActiveConnection)} + })); + try + { + Reference< XJobExecutor > xExecutable( + _rContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.wizards.report.CallReportWizard", aArgs, _rContext), UNO_QUERY_THROW ); + xExecutable->trigger( "fill" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODocumentDefinition::updateDocumentTitle() +{ + OUString sName = m_pImpl->m_aProps.aTitle; + if ( m_pImpl->m_pDataSource ) + { + if ( sName.isEmpty() ) + { + if ( m_bForm ) + sName = DBA_RES( RID_STR_FORM ); + else + sName = DBA_RES( RID_STR_REPORT ); + Reference< XUntitledNumbers > xUntitledProvider(m_pImpl->m_pDataSource->getModel_noCreate(), UNO_QUERY ); + if ( xUntitledProvider.is() ) + sName += OUString::number( xUntitledProvider->leaseNumber(getComponent()) ); + } + + Reference< XTitle > xDatabaseDocumentModel(m_pImpl->m_pDataSource->getModel_noCreate(),uno::UNO_QUERY); + if ( xDatabaseDocumentModel.is() ) + sName = xDatabaseDocumentModel->getTitle() + " : " + sName; + } + Reference< XTitle> xTitle(getComponent(),UNO_QUERY); + if ( xTitle.is() ) + xTitle->setTitle(sName); +} + +void SAL_CALL ODocumentDefinition::queryClosing( const lang::EventObject&, sal_Bool ) +{ + try + { + if ( !close() ) + throw util::CloseVetoException(); + } + catch(const lang::WrappedTargetException&) + { + throw util::CloseVetoException(); + } +} + +void SAL_CALL ODocumentDefinition::notifyClosing( const lang::EventObject& /*Source*/ ) +{ +} + +void SAL_CALL ODocumentDefinition::disposing( const lang::EventObject& /*Source*/ ) +{ +} + +void ODocumentDefinition::firePropertyChange( sal_Int32 i_nHandle, const Any& i_rNewValue, const Any& i_rOldValue, + bool i_bVetoable, const NotifierAccess& ) +{ + fire( &i_nHandle, &i_rNewValue, &i_rOldValue, 1, i_bVetoable ); +} + +// NameChangeNotifier +NameChangeNotifier::NameChangeNotifier( ODocumentDefinition& i_rDocumentDefinition, const OUString& i_rNewName, + ::osl::ResettableMutexGuard& i_rClearForNotify ) + :m_rDocumentDefinition( i_rDocumentDefinition ) + ,m_aOldValue( Any( i_rDocumentDefinition.getCurrentName() ) ) + ,m_aNewValue( Any( i_rNewName ) ) + ,m_rClearForNotify( i_rClearForNotify ) +{ + impl_fireEvent_throw( true ); +} + +NameChangeNotifier::~NameChangeNotifier() +{ + impl_fireEvent_throw( false ); +} + +void NameChangeNotifier::impl_fireEvent_throw( const bool i_bVetoable ) +{ + m_rClearForNotify.clear(); + m_rDocumentDefinition.firePropertyChange( + PROPERTY_ID_NAME, m_aNewValue, m_aOldValue, i_bVetoable, ODocumentDefinition::NotifierAccess() ); + m_rClearForNotify.reset(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentdefinition.hxx b/dbaccess/source/core/dataaccess/documentdefinition.hxx new file mode 100644 index 000000000..5569276d4 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentdefinition.hxx @@ -0,0 +1,356 @@ +/* -*- 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/propshlp.hxx> +#include <cppuhelper/implbase4.hxx> +#include <ContentHelper.hxx> +#include <comphelper/propertystatecontainer.hxx> +#include <comphelper/proparrhlp.hxx> +#include <apitools.hxx> +#include <comphelper/uno3.hxx> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/embed/XStateChangeListener.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/sdb/XSubDocument.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/container/XHierarchicalName.hpp> +#include <rtl/ref.hxx> + +namespace comphelper +{ + class NamedValueCollection; +} + +namespace dbaccess +{ + + class OInterceptor; + class OEmbeddedClientHelper; +// ODocumentDefinition - a database "document" which is simply a link to a real +// document + +typedef ::cppu::ImplHelper4 < css::embed::XComponentSupplier + , css::sdb::XSubDocument + , css::util::XCloseListener + , css::container::XHierarchicalName + > ODocumentDefinition_Base; + +class ODocumentDefinition + :public OContentHelper + ,public ::comphelper::OPropertyStateContainer + ,public ::comphelper::OPropertyArrayUsageHelper< ODocumentDefinition > + ,public ODocumentDefinition_Base +{ + css::uno::Reference< css::embed::XEmbeddedObject> m_xEmbeddedObject; + css::uno::Reference< css::embed::XStateChangeListener > m_xListener; + css::uno::Reference< css::sdbc::XConnection > m_xLastKnownConnection; + + rtl::Reference<OInterceptor> m_pInterceptor; + bool m_bForm; // <TRUE/> if it is a form + bool m_bOpenInDesign; + bool m_bInExecute; + bool m_bRemoveListener; + rtl::Reference<OEmbeddedClientHelper> m_pClientHelper; + +protected: + virtual ~ODocumentDefinition() override; + +public: + + ODocumentDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer, + const css::uno::Reference< css::uno::XComponentContext >&, + const TContentPtr& _pImpl, + bool _bForm + ); + + void initialLoad( + const css::uno::Sequence< sal_Int8 >& i_rClassID, + const css::uno::Sequence< css::beans::PropertyValue >& i_rCreationArgs, + const css::uno::Reference< css::sdbc::XConnection >& i_rConnection + ); + + 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 void SAL_CALL getFastPropertyValue( + css::uno::Any& o_rValue, + sal_Int32 i_nHandle + ) const override; + + // XComponentSupplier + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent( ) override; + + // XSubDocument + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL open( ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL openDesign( ) override; + virtual void SAL_CALL store( ) override; + virtual sal_Bool SAL_CALL close( ) override; + + // XHierarchicalName + virtual OUString SAL_CALL getHierarchicalName( ) override; + virtual OUString SAL_CALL composeHierarchicalName( const OUString& aRelativeName ) override; + +// OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override ; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // XCloseListener + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + /** returns the forms/reports container storage, depending on m_bForm. Our own storage + inside this container storage is the one with the name as indicated by m_pImpl->m_aProps.sPersistentName. + */ + css::uno::Reference< css::embed::XStorage > + getContainerStorage() const; + + bool save(bool _bApprove, const css::uno::Reference<css::awt::XTopWindow>& rDialogParent); + void saveAs(); + void closeObject(); + bool isModified(); + bool isNewReport() const { return !m_bForm && !m_pImpl->m_aProps.bAsTemplate; } + + static void fillReportData( + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + const css::uno::Reference< css::util::XCloseable >& _rxComponent, + const css::uno::Reference< css::sdbc::XConnection >& _rxActiveConnection + ); + + const css::uno::Reference< css::sdbc::XConnection >& + getConnection() const { return m_xLastKnownConnection; } + + /** prepares closing the document component + + The method suspends the controller associated with the document, and saves the document + if necessary. + + @return + <TRUE/> if and only if the document component can be closed + */ + bool prepareClose(); + + static OUString GetDocumentServiceFromMediaType( + const OUString& _rMediaType, + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + css::uno::Sequence< sal_Int8 >& _rClassId + ); + static OUString GetDocumentServiceFromMediaType( + const css::uno::Reference< css::embed::XStorage >& _rxContainerStorage, + const OUString& _rEntityName, + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + css::uno::Sequence< sal_Int8 >& _rClassId + ); + + struct NotifierAccess { friend class NameChangeNotifier; private: NotifierAccess() { } }; + const OUString& getCurrentName() const { return m_pImpl->m_aProps.aTitle; } + void firePropertyChange( + sal_Int32 i_nHandle, + const css::uno::Any& i_rNewValue, + const css::uno::Any& i_rOldValue, + bool i_bVetoable, + const NotifierAccess& + ); + +private: + /** does necessary initializations after our embedded object has been switched to ACTIVE + */ + void impl_onActivateEmbeddedObject_nothrow( const bool i_bReactivated ); + + /** initializes a newly created view/controller of a form which is displaying our embedded object + + Has only to be called if the respective embedded object has been loaded for design (and + not for data entry) + + @param _rxController + the controller which belongs to the XModel of our (active) embedded object + */ + static void impl_initFormEditView( const css::uno::Reference< css::frame::XController >& _rxController ); + + /** removes the given frame from the desktop's frame collection + @throws css::uno::RuntimeException + */ + static void impl_removeFrameFromDesktop_throw( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::frame::XFrame >& _rxFrame + ); + + /** opens the UI for this sub document + */ + css::uno::Reference< css::lang::XComponent > + impl_openUI_nolck_throw( bool _bForEditing ); + + /** stores our document, if it's already loaded + */ + void impl_store_throw(); + + /** closes our document, if it's open + */ + bool impl_close_throw(); + + /** returns our component, creates it if necessary + */ + css::uno::Reference< css::util::XCloseable > + impl_getComponent_throw( const bool i_ForceCreate = true ); + + /** shows or hides our component + + The embedded object must exist, and be in state LOADED, at least. + */ + void impl_showOrHideComponent_throw( const bool i_bShow ); + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + // helper + virtual void SAL_CALL disposing() override; + + // OContentHelper overridables + virtual OUString determineContentType() const override; + + /** fills the load arguments + */ + css::uno::Sequence< css::beans::PropertyValue > + fillLoadArgs( + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const bool _bSuppressMacros, + const bool _bReadOnly, + const css::uno::Sequence< css::beans::PropertyValue >& i_rOpenCommandArguments, + css::uno::Sequence< css::beans::PropertyValue >& _out_rEmbeddedObjectDescriptor + ); + + /** splits the given arguments to an "open*" command into arguments for loading the document, and arguments to be + put into the EmbeddedObjectDescriptor + + Any values already present in <code>o_rDocumentLoadArgs</code> and <code>o_rEmbeddedObjectDescriptor</code> + will be overwritten by values from <code>i_rOpenCommandArguments</code>, if applicable, otherwise they will + be preserved. + + @param i_rOpenCommandArguments + the arguments passed to the "open*" command at the content + @param o_rDocumentLoadArgs + the arguments to be passed when actually loading the embedded document. + @param o_rEmbeddedObjectDescriptor + the EmbeddedObjectDescriptor to be passed when initializing the embedded object + */ + static void separateOpenCommandArguments( + const css::uno::Sequence< css::beans::PropertyValue >& i_rOpenCommandArguments, + ::comphelper::NamedValueCollection& o_rDocumentLoadArgs, + ::comphelper::NamedValueCollection& o_rEmbeddedObjectDescriptor + ); + + /** loads the EmbeddedObject if not already loaded + @param _aClassID + If set, it will be used to create the embedded object. + */ + void loadEmbeddedObject( + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const css::uno::Sequence< sal_Int8 >& _aClassID, + const css::uno::Sequence< css::beans::PropertyValue >& _rAdditionalArgs, + const bool _bSuppressMacros, + const bool _bReadOnly + ); + + /** loads the embedded object for preview. Macros will be suppressed, and the document will + be read-only. + */ + void loadEmbeddedObjectForPreview() + { + loadEmbeddedObject( + nullptr, + css::uno::Sequence< sal_Int8 >(), + css::uno::Sequence< css::beans::PropertyValue >(), + true, + true + ); + } + + /** searches for read-only flag in the args of the model and sets it to the given value, + if the value was not found, it will be appended. + @param _bReadOnly + If <TRUE/> the document will be switched to readonly mode + */ + void updateDocumentTitle(); + + void registerProperties(); + + /** determines whether the document we represent supports embedded scripts and macros + */ + bool objectSupportsEmbeddedScripts() const; + + //- commands + + void onCommandGetDocumentProperties( css::uno::Any& _rProps ); + /// @throws css::uno::Exception + void onCommandInsert( const OUString& _sURL, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ); + void onCommandPreview( css::uno::Any& _rImage ); + css::uno::Any + onCommandOpenSomething( + const css::uno::Any& _rArgument, + const bool _bActivate, + const css::uno::Reference< css::ucb::XCommandEnvironment >& _rxEnvironment + ); +private: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + +class NameChangeNotifier +{ +public: + NameChangeNotifier( + ODocumentDefinition& i_rDocumentDefinition, + const OUString& i_rNewName, + ::osl::ResettableMutexGuard& i_rClearForNotify + ); + ~NameChangeNotifier(); + +private: + ODocumentDefinition& m_rDocumentDefinition; + const css::uno::Any m_aOldValue; + const css::uno::Any m_aNewValue; + ::osl::ResettableMutexGuard& m_rClearForNotify; + + void impl_fireEvent_throw( const bool i_bVetoable ); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventexecutor.cxx b/dbaccess/source/core/dataaccess/documenteventexecutor.cxx new file mode 100644 index 000000000..3d16ee6eb --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventexecutor.cxx @@ -0,0 +1,199 @@ +/* -*- 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 "documenteventexecutor.hxx" + +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/weakref.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::WeakReference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::document::XDocumentEventBroadcaster; + using ::com::sun::star::document::XEventsSupplier; + using ::com::sun::star::container::XNameAccess; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::util::URLTransformer; + using ::com::sun::star::util::XURLTransformer; + using ::com::sun::star::frame::XDispatchProvider; + using ::com::sun::star::frame::XDispatch; + using ::com::sun::star::util::URL; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::frame::XController; + using ::com::sun::star::document::DocumentEvent; + + using namespace ::com::sun::star; + + // DocumentEventExecutor_Data + struct DocumentEventExecutor_Data + { + WeakReference< XEventsSupplier > xDocument; + Reference< XURLTransformer > xURLTransformer; + + explicit DocumentEventExecutor_Data( const Reference< XEventsSupplier >& _rxDocument ) + :xDocument( _rxDocument ) + { + } + }; + + namespace + { + void lcl_dispatchScriptURL_throw( DocumentEventExecutor_Data const & _rDocExecData, + const OUString& _rScriptURL, const DocumentEvent& _rTrigger ) + { + Reference< XModel > xDocument( _rDocExecData.xDocument.get(), UNO_QUERY_THROW ); + + Reference< XController > xController( xDocument->getCurrentController() ); + Reference< XDispatchProvider > xDispProv; + if ( xController.is() ) + xDispProv.set( xController->getFrame(), UNO_QUERY ); + if ( !xDispProv.is() ) + { + OSL_FAIL( "lcl_dispatchScriptURL_throw: no controller/frame? How should I dispatch?" ); + return; + } + + URL aScriptURL; + aScriptURL.Complete = _rScriptURL; + if ( _rDocExecData.xURLTransformer.is() ) + _rDocExecData.xURLTransformer->parseStrict( aScriptURL ); + + // unfortunately, executing a script can trigger all kind of complex stuff, and unfortunately, not + // every component involved into this properly cares for thread safety. To be on the safe side, + // we lock the solar mutex here. + SolarMutexGuard aSolarGuard; + + Reference< XDispatch > xDispatch( xDispProv->queryDispatch( aScriptURL, OUString(), 0 ) ); + if ( !xDispatch.is() ) + { + OSL_FAIL( "lcl_dispatchScriptURL_throw: no dispatcher for the script URL!" ); + return; + } + + PropertyValue aEventParam; + aEventParam.Value <<= _rTrigger; + Sequence< PropertyValue > aDispatchArgs( &aEventParam, 1 ); + xDispatch->dispatch( aScriptURL, aDispatchArgs ); + } + } + + // DocumentEventExecutor + DocumentEventExecutor::DocumentEventExecutor( const Reference<XComponentContext> & _rContext, + const Reference< XEventsSupplier >& _rxDocument ) + :m_pData( new DocumentEventExecutor_Data( _rxDocument ) ) + { + Reference< XDocumentEventBroadcaster > xBroadcaster( _rxDocument, UNO_QUERY_THROW ); + + osl_atomic_increment( &m_refCount ); + { + xBroadcaster->addDocumentEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + + try + { + m_pData->xURLTransformer = URLTransformer::create(_rContext); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + DocumentEventExecutor::~DocumentEventExecutor() + { + } + + void SAL_CALL DocumentEventExecutor::documentEventOccured( const DocumentEvent& Event ) + { + Reference< XEventsSupplier > xEventsSupplier( m_pData->xDocument.get(), UNO_QUERY ); + if ( !xEventsSupplier.is() ) + { + OSL_FAIL( "DocumentEventExecutor::documentEventOccurred: no document anymore, but still being notified?" ); + return; + } + + Reference< XModel > xDocument( xEventsSupplier, UNO_QUERY_THROW ); + + try + { + Reference< XNameAccess > xDocEvents( xEventsSupplier->getEvents(), UNO_SET_THROW ); + if ( !xDocEvents->hasByName( Event.EventName ) ) + { + // this is worth an assertion: We are listener at the very same document which we just asked + // for its events. So when EventName is fired, why isn't it supported by xDocEvents? + OSL_FAIL( "DocumentEventExecutor::documentEventOccurred: an unsupported event is notified!" ); + return; + } + + const ::comphelper::NamedValueCollection aScriptDescriptor( xDocEvents->getByName( Event.EventName ) ); + + OUString sEventType; + bool bScriptAssigned = aScriptDescriptor.get_ensureType( "EventType", sEventType ); + + OUString sScript; + bScriptAssigned = bScriptAssigned && aScriptDescriptor.get_ensureType( "Script", sScript ); + + if ( !bScriptAssigned ) + // no script is assigned to this event + return; + + bool bDispatchScriptURL = ( sEventType == "Script" || sEventType == "Service" ); + bool bNonEmptyScript = !sScript.isEmpty(); + + OSL_ENSURE( bDispatchScriptURL && bNonEmptyScript, + "DocumentEventExecutor::documentEventOccurred: invalid/unsupported script descriptor" ); + + if ( bDispatchScriptURL && bNonEmptyScript ) + { + lcl_dispatchScriptURL_throw( *m_pData, sScript, Event ); + } + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void SAL_CALL DocumentEventExecutor::disposing( const lang::EventObject& /*_Source*/ ) + { + // not interested in + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventexecutor.hxx b/dbaccess/source/core/dataaccess/documenteventexecutor.hxx new file mode 100644 index 000000000..90dc9083c --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventexecutor.hxx @@ -0,0 +1,59 @@ +/* -*- 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/document/XDocumentEventListener.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <memory> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace dbaccess +{ + + struct DocumentEventExecutor_Data; + // DocumentEventExecutor + typedef ::cppu::WeakImplHelper < css::document::XDocumentEventListener + > DocumentEventExecutor_Base; + class DocumentEventExecutor : public DocumentEventExecutor_Base + { + public: + DocumentEventExecutor( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::document::XEventsSupplier >& _rxDocument ); + + protected: + virtual ~DocumentEventExecutor() override; + + // css.document.XDocumentEventListener + virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override; + // css.lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + std::unique_ptr< DocumentEventExecutor_Data > m_pData; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventnotifier.cxx b/dbaccess/source/core/dataaccess/documenteventnotifier.cxx new file mode 100644 index 000000000..6a7088f95 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventnotifier.cxx @@ -0,0 +1,290 @@ +/* -*- 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 "documenteventnotifier.hxx" + +#include <com/sun/star/frame/DoubleInitializationException.hpp> + +#include <comphelper/asyncnotification.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/weak.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::frame::DoubleInitializationException; + using ::com::sun::star::document::XDocumentEventListener; + using ::com::sun::star::document::DocumentEvent; + using ::com::sun::star::frame::XController2; + + using namespace ::com::sun::star; + + // DocumentEventHolder + typedef ::comphelper::EventHolder< DocumentEvent > DocumentEventHolder; + + // DocumentEventNotifier_Impl + class DocumentEventNotifier_Impl : public ::comphelper::IEventProcessor + { + oslInterlockedCount m_refCount; + ::cppu::OWeakObject& m_rDocument; + ::osl::Mutex& m_rMutex; + bool m_bInitialized; + bool m_bDisposed; + std::shared_ptr<::comphelper::AsyncEventNotifierAutoJoin> m_pEventBroadcaster; + ::comphelper::OInterfaceContainerHelper3<css::document::XEventListener> m_aLegacyEventListeners; + ::comphelper::OInterfaceContainerHelper3<XDocumentEventListener> m_aDocumentEventListeners; + + public: + DocumentEventNotifier_Impl( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ) + :m_refCount( 0 ) + ,m_rDocument( _rBroadcasterDocument ) + ,m_rMutex( _rMutex ) + ,m_bInitialized( false ) + ,m_bDisposed( false ) + ,m_aLegacyEventListeners( _rMutex ) + ,m_aDocumentEventListeners( _rMutex ) + { + } + + // IEventProcessor + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + void addLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_aLegacyEventListeners.addInterface( Listener ); + } + + void removeLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_aLegacyEventListeners.removeInterface( Listener ); + } + + void addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_aDocumentEventListeners.addInterface( Listener ); + } + + void removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_aDocumentEventListeners.removeInterface( Listener ); + } + + void disposing(); + + void onDocumentInitialized(); + + void notifyDocumentEvent( const OUString& EventName, const Reference< XController2 >& ViewController, + const Any& Supplement ) + { + impl_notifyEvent_nothrow( DocumentEvent( + m_rDocument, EventName, ViewController, Supplement ) ); + } + + void notifyDocumentEventAsync( const OUString& EventName, const Reference< XController2 >& ViewController, + const Any& Supplement ) + { + impl_notifyEventAsync_nothrow( DocumentEvent( + m_rDocument, EventName, ViewController, Supplement ) ); + } + + protected: + virtual ~DocumentEventNotifier_Impl() + { + } + + // IEventProcessor + virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override; + + private: + void impl_notifyEvent_nothrow( const DocumentEvent& _rEvent ); + void impl_notifyEventAsync_nothrow( const DocumentEvent& _rEvent ); + }; + + void SAL_CALL DocumentEventNotifier_Impl::acquire() noexcept + { + osl_atomic_increment( &m_refCount ); + } + + void SAL_CALL DocumentEventNotifier_Impl::release() noexcept + { + if ( 0 == osl_atomic_decrement( &m_refCount ) ) + delete this; + } + + void DocumentEventNotifier_Impl::disposing() + { + // SYNCHRONIZED -> + // cancel any pending asynchronous events + ::osl::ResettableMutexGuard aGuard( m_rMutex ); + if (m_pEventBroadcaster) + { + m_pEventBroadcaster->removeEventsForProcessor( this ); + m_pEventBroadcaster->terminate(); + } + + auto xEventBroadcaster = std::exchange(m_pEventBroadcaster, {}); + + lang::EventObject aEvent( m_rDocument ); + aGuard.clear(); + // <-- SYNCHRONIZED + + if (xEventBroadcaster) + { + comphelper::SolarMutex& rSolarMutex = Application::GetSolarMutex(); + // unblock threads blocked on that so we can join + sal_uInt32 nLockCount = (rSolarMutex.IsCurrentThread()) ? rSolarMutex.release(true) : 0; + xEventBroadcaster->join(); + if (nLockCount) + rSolarMutex.acquire(nLockCount); + xEventBroadcaster.reset(); + } + m_aLegacyEventListeners.disposeAndClear( aEvent ); + m_aDocumentEventListeners.disposeAndClear( aEvent ); + + // SYNCHRONIZED -> + aGuard.reset(); + m_bDisposed = true; + // <-- SYNCHRONIZED + } + + void DocumentEventNotifier_Impl::onDocumentInitialized() + { + if ( m_bInitialized ) + throw DoubleInitializationException(); + + m_bInitialized = true; + if (m_pEventBroadcaster) + { + // there are already pending asynchronous events + ::comphelper::AsyncEventNotifierAutoJoin::launch(m_pEventBroadcaster); + } + } + + void DocumentEventNotifier_Impl::impl_notifyEvent_nothrow( const DocumentEvent& _rEvent ) + { + OSL_PRECOND( m_bInitialized, + "DocumentEventNotifier_Impl::impl_notifyEvent_nothrow: only to be called when the document is already initialized!" ); + try + { + document::EventObject aLegacyEvent( _rEvent.Source, _rEvent.EventName ); + m_aLegacyEventListeners.notifyEach( &document::XEventListener::notifyEvent, aLegacyEvent ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + try + { + m_aDocumentEventListeners.notifyEach( &XDocumentEventListener::documentEventOccured, _rEvent ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void DocumentEventNotifier_Impl::impl_notifyEventAsync_nothrow( const DocumentEvent& _rEvent ) + { + if (!m_pEventBroadcaster) + { + m_pEventBroadcaster = ::comphelper::AsyncEventNotifierAutoJoin + ::newAsyncEventNotifierAutoJoin("DocumentEventNotifier"); + if ( m_bInitialized ) + { + // start processing the events if and only if we (our document, respectively) are + // already initialized + ::comphelper::AsyncEventNotifierAutoJoin::launch(m_pEventBroadcaster); + } + } + m_pEventBroadcaster->addEvent( new DocumentEventHolder( _rEvent ), this ); + } + + void DocumentEventNotifier_Impl::processEvent( const ::comphelper::AnyEvent& _rEvent ) + { + // beware, this is called from the notification thread + { + ::osl::MutexGuard aGuard( m_rMutex ); + if ( m_bDisposed ) + return; + } + const DocumentEventHolder& rEventHolder = dynamic_cast< const DocumentEventHolder& >( _rEvent ); + impl_notifyEvent_nothrow( rEventHolder.getEventObject() ); + } + + // DocumentEventNotifier + DocumentEventNotifier::DocumentEventNotifier( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ) + :m_pImpl( new DocumentEventNotifier_Impl( _rBroadcasterDocument, _rMutex ) ) + { + } + + DocumentEventNotifier::~DocumentEventNotifier() + { + } + + void DocumentEventNotifier::disposing() + { + m_pImpl->disposing(); + } + + void DocumentEventNotifier::onDocumentInitialized() + { + m_pImpl->onDocumentInitialized(); + } + + void DocumentEventNotifier::addLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_pImpl->addLegacyEventListener( Listener ); + } + + void DocumentEventNotifier::removeLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_pImpl->removeLegacyEventListener( Listener ); + } + + void DocumentEventNotifier::addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_pImpl->addDocumentEventListener( Listener ); + } + + void DocumentEventNotifier::removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_pImpl->removeDocumentEventListener( Listener ); + } + + void DocumentEventNotifier::notifyDocumentEvent( const OUString& EventName, + const Reference< XController2 >& ViewController, const Any& Supplement ) + { + m_pImpl->notifyDocumentEvent( EventName, ViewController, Supplement ); + } + + void DocumentEventNotifier::notifyDocumentEventAsync( const OUString& EventName, + const Reference< XController2 >& ViewController, const Any& Supplement ) + { + m_pImpl->notifyDocumentEventAsync( EventName, ViewController, Supplement ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventnotifier.hxx b/dbaccess/source/core/dataaccess/documenteventnotifier.hxx new file mode 100644 index 000000000..6f3bffc4c --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventnotifier.hxx @@ -0,0 +1,128 @@ +/* -*- 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/document/XEventListener.hpp> +#include <com/sun/star/document/XDocumentEventListener.hpp> + +#include <rtl/ref.hxx> + +namespace cppu +{ + class OWeakObject; +} + +namespace dbaccess +{ + + class DocumentEventNotifier_Impl; + // DocumentEventNotifier + class DocumentEventNotifier + { + public: + DocumentEventNotifier( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ); + ~DocumentEventNotifier(); + + void addLegacyEventListener( const css::uno::Reference< css::document::XEventListener >& Listener ); + void removeLegacyEventListener( const css::uno::Reference< css::document::XEventListener >& Listener ); + void addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ); + void removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ); + + /** disposes the instance + @precond + the mutex is not locked + */ + void disposing(); + + /** tells the instance that its document is completely initialized now. + + Before you call this method, no notification will actually happen + + @precond + the mutex is locked + */ + void onDocumentInitialized(); + + /** notifies a document event described by the given parameters + + @precond + the mutex is not locked + @precond + ->onDocumentInitialized has been called + */ + void notifyDocumentEvent( + const OUString& EventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController, + const css::uno::Any& Supplement + ); + + /** notifies a document event, described by the given parameters, asynchronously + + Note that no event is actually notified before you called ->onDocumentInitialized. + + @precond + the mutex is locked + */ + void notifyDocumentEventAsync( + const OUString& EventName, + const css::uno::Reference< css::frame::XController2 >& ViewController, + const css::uno::Any& Supplement + ); + + /** notifies a document event to all registered listeners + + @precond + the mutex is not locked + @precond + ->onDocumentInitialized has been called + */ + void notifyDocumentEvent( + const char* _pAsciiEventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController = nullptr, + const css::uno::Any& _rSupplement = css::uno::Any() + ) + { + notifyDocumentEvent( OUString::createFromAscii( _pAsciiEventName ), _rxViewController, _rSupplement ); + } + + /** notifies a document event to all registered listeners, asynchronously + + Note that no event is actually notified before you called ->onDocumentInitialized. + + @precond + the mutex is locked + */ + void notifyDocumentEventAsync( + const char* _pAsciiEventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController = nullptr, + const css::uno::Any& _rSupplement = css::uno::Any() + ) + { + notifyDocumentEventAsync( OUString::createFromAscii( _pAsciiEventName ), _rxViewController, _rSupplement ); + } + + private: + ::rtl::Reference< DocumentEventNotifier_Impl > m_pImpl; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentevents.cxx b/dbaccess/source/core/dataaccess/documentevents.cxx new file mode 100644 index 000000000..571ad2c6f --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentevents.cxx @@ -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 . + */ + +#include <documentevents.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/sequence.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Any; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Type; + + // DocumentEvents_Data + struct DocumentEvents_Data + { + ::cppu::OWeakObject& rParent; + ::osl::Mutex& rMutex; + DocumentEventsData& rEventsData; + + DocumentEvents_Data( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, DocumentEventsData& _rEventsData ) + :rParent( _rParent ) + ,rMutex( _rMutex ) + ,rEventsData( _rEventsData ) + { + } + DocumentEvents_Data(const DocumentEvents_Data&) = delete; + const DocumentEvents_Data& operator=(const DocumentEvents_Data&) = delete; + }; + + namespace { + + // helper + struct DocumentEventData + { + const char* pAsciiEventName; + bool bNeedsSyncNotify; + }; + + const DocumentEventData* lcl_getDocumentEventData() + { + static const DocumentEventData s_aData[] = { + { "OnCreate", true }, + { "OnLoadFinished", true }, + { "OnNew", false }, // compatibility, see https://bz.apache.org/ooo/show_bug.cgi?id=46484 + { "OnLoad", false }, // compatibility, see https://bz.apache.org/ooo/show_bug.cgi?id=46484 + { "OnSaveAs", true }, + { "OnSaveAsDone", false }, + { "OnSaveAsFailed", false }, + { "OnSave", true }, + { "OnSaveDone", false }, + { "OnSaveFailed", false }, + { "OnSaveTo", true }, + { "OnSaveToDone", false }, + { "OnSaveToFailed", false }, + { "OnPrepareUnload", true }, + { "OnUnload", true }, + { "OnFocus", false }, + { "OnUnfocus", false }, + { "OnModifyChanged", false }, + { "OnViewCreated", false }, + { "OnPrepareViewClosing", true }, + { "OnViewClosed", false }, + { "OnTitleChanged", false }, + { "OnSubComponentOpened", false }, + { "OnSubComponentClosed", false }, + { nullptr, false } + }; + return s_aData; + } + } + + // DocumentEvents + DocumentEvents::DocumentEvents( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, DocumentEventsData& _rEventsData ) + :m_pData( new DocumentEvents_Data( _rParent, _rMutex, _rEventsData ) ) + { + const DocumentEventData* pEventData = lcl_getDocumentEventData(); + while ( pEventData->pAsciiEventName ) + { + OUString sEventName = OUString::createFromAscii( pEventData->pAsciiEventName ); + DocumentEventsData::const_iterator existingPos = m_pData->rEventsData.find( sEventName ); + if ( existingPos == m_pData->rEventsData.end() ) + m_pData->rEventsData[ sEventName ] = Sequence< PropertyValue >(); + ++pEventData; + } + } + + DocumentEvents::~DocumentEvents() + { + } + + void SAL_CALL DocumentEvents::acquire() noexcept + { + m_pData->rParent.acquire(); + } + + void SAL_CALL DocumentEvents::release() noexcept + { + m_pData->rParent.release(); + } + + bool DocumentEvents::needsSynchronousNotification( std::u16string_view _rEventName ) + { + const DocumentEventData* pEventData = lcl_getDocumentEventData(); + while ( pEventData->pAsciiEventName ) + { + if ( o3tl::equalsAscii( _rEventName, pEventData->pAsciiEventName ) ) + return pEventData->bNeedsSyncNotify; + ++pEventData; + } + + // this is an unknown event ... assume async notification + return false; + } + + void SAL_CALL DocumentEvents::replaceByName( const OUString& Name, const Any& Element ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + DocumentEventsData::iterator elementPos = m_pData->rEventsData.find( Name ); + if ( elementPos == m_pData->rEventsData.end() ) + throw NoSuchElementException( Name, *this ); + + Sequence< PropertyValue > aEventDescriptor; + if ( Element.hasValue() && !( Element >>= aEventDescriptor ) ) + throw IllegalArgumentException( Element.getValueTypeName(), *this, 2 ); + + // Weird enough, the event assignment UI has (well: had) the idea of using an empty "EventType"/"Script" + // to indicate the event descriptor should be reset, instead of just passing an empty event descriptor. + ::comphelper::NamedValueCollection aCheck( aEventDescriptor ); + if ( aCheck.has( "EventType" ) ) + { + OUString sEventType = aCheck.getOrDefault( "EventType", OUString() ); + OSL_ENSURE( !sEventType.isEmpty(), "DocumentEvents::replaceByName: doing a reset via an empty EventType is weird!" ); + if ( sEventType.isEmpty() ) + aEventDescriptor.realloc( 0 ); + } + if ( aCheck.has( "Script" ) ) + { + OUString sScript = aCheck.getOrDefault( "Script", OUString() ); + OSL_ENSURE( !sScript.isEmpty(), "DocumentEvents::replaceByName: doing a reset via an empty Script is weird!" ); + if ( sScript.isEmpty() ) + aEventDescriptor.realloc( 0 ); + } + + elementPos->second = aEventDescriptor; + } + + Any SAL_CALL DocumentEvents::getByName( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + DocumentEventsData::const_iterator elementPos = m_pData->rEventsData.find( Name ); + if ( elementPos == m_pData->rEventsData.end() ) + throw NoSuchElementException( Name, *this ); + + Any aReturn; + const Sequence< PropertyValue >& rEventDesc( elementPos->second ); + if ( rEventDesc.hasElements() ) + aReturn <<= rEventDesc; + return aReturn; + } + + Sequence< OUString > SAL_CALL DocumentEvents::getElementNames( ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + return comphelper::mapKeysToSequence( m_pData->rEventsData ); + } + + sal_Bool SAL_CALL DocumentEvents::hasByName( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + return m_pData->rEventsData.find( Name ) != m_pData->rEventsData.end(); + } + + Type SAL_CALL DocumentEvents::getElementType( ) + { + return ::cppu::UnoType< Sequence< PropertyValue > >::get(); + } + + sal_Bool SAL_CALL DocumentEvents::hasElements( ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + return !m_pData->rEventsData.empty(); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/intercept.cxx b/dbaccess/source/core/dataaccess/intercept.cxx new file mode 100644 index 000000000..bef079d47 --- /dev/null +++ b/dbaccess/source/core/dataaccess/intercept.cxx @@ -0,0 +1,363 @@ +/* -*- 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 "intercept.hxx" + +#include <tools/diagnose_ex.h> + +#include <memory> + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::comphelper; +using namespace ::cppu; + +#define DISPATCH_SAVEAS 0 +#define DISPATCH_SAVE 1 +#define DISPATCH_CLOSEDOC 2 +#define DISPATCH_CLOSEWIN 3 +#define DISPATCH_CLOSEFRAME 4 +#define DISPATCH_RELOAD 5 +// the OSL_ENSURE in CTOR has to be changed too, when adding new defines + +void OInterceptor::dispose() +{ + EventObject aEvt( *this ); + + osl::MutexGuard aGuard(m_aMutex); + + if ( m_pStatCL ) + m_pStatCL->disposeAndClear( aEvt ); + + m_xSlaveDispatchProvider.clear(); + m_xMasterDispatchProvider.clear(); + + m_pContentHolder = nullptr; +} + + +OInterceptor::OInterceptor( ODocumentDefinition* _pContentHolder ) + :m_pContentHolder( _pContentHolder ) + ,m_aInterceptedURL{ /* DISPATCH_SAVEAS */ ".uno:SaveAs", + /* DISPATCH_SAVE */ ".uno:Save", + /* DISPATCH_CLOSEDOC */ ".uno:CloseDoc", + /* DISPATCH_CLOSEWIN */ ".uno:CloseWin", + /* DISPATCH_CLOSEFRAME */ ".uno:CloseFrame", + /* DISPATCH_RELOAD */ ".uno:Reload" } +{ + OSL_ENSURE(DISPATCH_RELOAD < m_aInterceptedURL.getLength(),"Illegal size."); +} + + +OInterceptor::~OInterceptor() +{ +} + +namespace { + +struct DispatchHelper +{ + URL aURL; + Sequence<PropertyValue > aArguments; +}; + +} + +//XDispatch +void SAL_CALL OInterceptor::dispatch( const URL& URL,const Sequence<PropertyValue >& Arguments ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_pContentHolder ) + return; + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_SAVE ] ) + { + m_pContentHolder->save(false, css::uno::Reference<css::awt::XTopWindow>()); + return; + } + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_RELOAD ] ) + { + ODocumentDefinition::fillReportData( + m_pContentHolder->getContext(), + m_pContentHolder->getComponent(), + m_pContentHolder->getConnection() + ); + return; + } + + if( URL.Complete == m_aInterceptedURL[ DISPATCH_SAVEAS ] ) + { + if ( m_pContentHolder->isNewReport() ) + { + m_pContentHolder->saveAs(); + } + else if ( m_xSlaveDispatchProvider.is() ) + { + Sequence< PropertyValue > aNewArgs = Arguments; + sal_Int32 nInd = 0; + + while( nInd < aNewArgs.getLength() ) + { + if ( aNewArgs[nInd].Name == "SaveTo" ) + { + aNewArgs.getArray()[nInd].Value <<= true; + break; + } + nInd++; + } + + if ( nInd == aNewArgs.getLength() ) + { + aNewArgs.realloc( nInd + 1 ); + auto pNewArgs = aNewArgs.getArray(); + pNewArgs[nInd].Name = "SaveTo"; + pNewArgs[nInd].Value <<= true; + } + + Reference< XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch(URL, "_self", 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( URL, aNewArgs ); + } + return; + } + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEDOC ] + || URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEWIN ] + || URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEFRAME ] + ) + { + DispatchHelper* pHelper = new DispatchHelper; + pHelper->aArguments = Arguments; + pHelper->aURL = URL; + Application::PostUserEvent( LINK( this, OInterceptor, OnDispatch ), pHelper ); + return; + } +} + +IMPL_LINK( OInterceptor, OnDispatch, void*, _pDispatcher, void ) +{ + std::unique_ptr<DispatchHelper> pHelper( static_cast< DispatchHelper* >( _pDispatcher ) ); + try + { + if ( m_pContentHolder && m_pContentHolder->prepareClose() && m_xSlaveDispatchProvider.is() ) + { + Reference< XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch(pHelper->aURL, "_self", 0 ); + if ( xDispatch.is() ) + { + Reference< XInterface > xKeepContentHolderAlive( *m_pContentHolder ); + xDispatch->dispatch( pHelper->aURL,pHelper->aArguments); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL OInterceptor::addStatusListener( + const Reference< + XStatusListener >& Control, + const URL& URL ) +{ + if(!Control.is()) + return; + + if ( m_pContentHolder && URL.Complete == m_aInterceptedURL[DISPATCH_SAVEAS] ) + { // SaveAs + + if ( !m_pContentHolder->isNewReport() ) + { + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[DISPATCH_SAVEAS]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= OUString("($3)"); + Control->statusChanged(aStateEvent); + } + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + } + else if ( m_pContentHolder && URL.Complete == m_aInterceptedURL[DISPATCH_SAVE] ) + { // Save + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[DISPATCH_SAVE]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + + Control->statusChanged(aStateEvent); + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + } + else + { + sal_Int32 i = 2; + if(URL.Complete == m_aInterceptedURL[i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[i = DISPATCH_RELOAD] ) + { // Close and return + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + Control->statusChanged(aStateEvent); + + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + } +} + + +void SAL_CALL OInterceptor::removeStatusListener( + const Reference< + XStatusListener >& Control, + const URL& URL ) +{ + if(!(Control.is() && m_pStatCL)) + return; + else + { + m_pStatCL->removeInterface(URL.Complete,Control); + return; + } +} + + +//XInterceptorInfo +Sequence< OUString > SAL_CALL OInterceptor::getInterceptedURLs( ) +{ + // now implemented as update + return m_aInterceptedURL; +} + + +// XDispatchProvider + +Reference< XDispatch > SAL_CALL OInterceptor::queryDispatch( const URL& URL,const OUString& TargetFrameName,sal_Int32 SearchFlags ) +{ + osl::MutexGuard aGuard(m_aMutex); + const OUString* pIter = m_aInterceptedURL.getConstArray(); + const OUString* pEnd = pIter + m_aInterceptedURL.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( URL.Complete == *pIter ) + return static_cast<XDispatch*>(this); + } + + if(m_xSlaveDispatchProvider.is()) + return m_xSlaveDispatchProvider->queryDispatch(URL,TargetFrameName,SearchFlags); + else + return Reference<XDispatch>(); +} + +Sequence< Reference< XDispatch > > SAL_CALL OInterceptor::queryDispatches( const Sequence<DispatchDescriptor >& Requests ) +{ + osl::MutexGuard aGuard(m_aMutex); + typedef Sequence<Reference<XDispatch>> DispatchSeq; + DispatchSeq aRet = m_xSlaveDispatchProvider.is() ? + m_xSlaveDispatchProvider->queryDispatches(Requests) : + DispatchSeq(Requests.getLength()); + + auto aRetRange = asNonConstRange(aRet); + for(sal_Int32 i = 0; i < Requests.getLength(); ++i) + { + const OUString* pIter = m_aInterceptedURL.getConstArray(); + const OUString* pEnd = pIter + m_aInterceptedURL.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( Requests[i].FeatureURL.Complete == *pIter ) + { + aRetRange[i] = static_cast<XDispatch*>(this); + break; + } + } + } + + return aRet; +} + + +//XDispatchProviderInterceptor + +Reference< XDispatchProvider > SAL_CALL OInterceptor::getSlaveDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xSlaveDispatchProvider; +} + +void SAL_CALL +OInterceptor::setSlaveDispatchProvider( const Reference< XDispatchProvider >& NewDispatchProvider ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xSlaveDispatchProvider = NewDispatchProvider; +} + + +Reference< XDispatchProvider > SAL_CALL OInterceptor::getMasterDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xMasterDispatchProvider; +} + + +void SAL_CALL OInterceptor::setMasterDispatchProvider( + const Reference< XDispatchProvider >& NewSupplier ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xMasterDispatchProvider = NewSupplier; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/intercept.hxx b/dbaccess/source/core/dataaccess/intercept.hxx new file mode 100644 index 000000000..7ce53752f --- /dev/null +++ b/dbaccess/source/core/dataaccess/intercept.hxx @@ -0,0 +1,113 @@ +/* -*- 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 <osl/mutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/interfacecontainer.hxx> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include "documentdefinition.hxx" +#include <vcl/svapp.hxx> + +namespace dbaccess +{ + + +class OInterceptor : public ::cppu::WeakImplHelper< css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::frame::XDispatch > +{ + DECL_LINK( OnDispatch, void*, void ); +protected: + virtual ~OInterceptor() override; +public: + + explicit OInterceptor( ODocumentDefinition* _pContentHolder ); + + /// @throws css::uno::RuntimeException + void dispose(); + + //XDispatch + virtual void SAL_CALL + dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + virtual void SAL_CALL + addStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + virtual void SAL_CALL + removeStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + //XInterceptorInfo + virtual css::uno::Sequence< OUString > + SAL_CALL getInterceptedURLs( ) override; + + //XDispatchProvider ( inherited by XDispatchProviderInterceptor ) + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL + queryDispatch( + const css::util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) override; + + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override; + + //XDispatchProviderInterceptor + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getSlaveDispatchProvider( ) override; + + virtual void SAL_CALL + setSlaveDispatchProvider( + const css::uno::Reference< css::frame::XDispatchProvider >& NewDispatchProvider ) override; + + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider( ) override; + + virtual void SAL_CALL + setMasterDispatchProvider( + const css::uno::Reference< css::frame::XDispatchProvider >& NewSupplier ) override; + +private: + + osl::Mutex m_aMutex; + + ODocumentDefinition* m_pContentHolder; + + css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatchProvider; + css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatchProvider; + + css::uno::Sequence< OUString > m_aInterceptedURL; + + typedef comphelper::OMultiTypeInterfaceContainerHelperVar3<css::frame::XStatusListener, OUString> + StatusListenerContainer; + std::unique_ptr<StatusListenerContainer> m_pStatCL; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx b/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx new file mode 100644 index 000000000..e0d32d569 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx @@ -0,0 +1,330 @@ +/* -*- 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 <vector> + +#include <ucbhelper/contentidentifier.hxx> + +#include "myucp_datasupplier.hxx" +#include <ContentHelper.hxx> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +// @@@ Adjust namespace name. +using namespace dbaccess; + +// @@@ Adjust namespace name. +namespace dbaccess +{ + +namespace { + +// struct ResultListEntry. +struct ResultListEntry +{ + OUString aId; + Reference< XContentIdentifier > xId; + ::rtl::Reference< OContentHelper > xContent; + Reference< XRow > xRow; + const ContentProperties& rData; + + explicit ResultListEntry(const ContentProperties& rEntry) : rData( rEntry ) {} +}; + +} + +// struct DataSupplier_Impl. +struct DataSupplier_Impl +{ + osl::Mutex m_aMutex; + std::vector< std::unique_ptr<ResultListEntry> > m_aResults; + rtl::Reference< ODocumentContainer > m_xContent; + bool m_bCountFinal; + + explicit DataSupplier_Impl(const rtl::Reference< ODocumentContainer >& rContent) + : m_xContent(rContent) + , m_bCountFinal(false) + { + } +}; + +} + +// DataSupplier Implementation. + +DataSupplier::DataSupplier( const rtl::Reference< ODocumentContainer >& rContent ) +: m_pImpl( new DataSupplier_Impl( rContent ) ) +{ + +} + +DataSupplier::~DataSupplier() +{ + +} + +OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(nIndex) < m_pImpl->m_aResults.size() ) + { + OUString aId = m_pImpl->m_aResults[ nIndex ]->aId; + if ( !aId.isEmpty() ) + { + // Already cached. + return aId; + } + } + + if ( getResult( nIndex ) ) + { + OUString aId = m_pImpl->m_xContent->getIdentifier()->getContentIdentifier(); + + if ( !aId.isEmpty() ) + aId += "/"; + + aId += m_pImpl->m_aResults[ nIndex ]->rData.aTitle; + + m_pImpl->m_aResults[ nIndex ]->aId = aId; + return aId; + } + return OUString(); +} + +Reference< XContentIdentifier > +DataSupplier::queryContentIdentifier( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XContentIdentifier > xId = m_pImpl->m_aResults[ nIndex ]->xId; + if ( xId.is() ) + { + // Already cached. + return xId; + } + } + + OUString aId = queryContentIdentifierString( nIndex ); + if ( !aId.isEmpty() ) + { + Reference< XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( aId ); + m_pImpl->m_aResults[ nIndex ]->xId = xId; + return xId; + } + return Reference< XContentIdentifier >(); +} + +Reference< XContent > +DataSupplier::queryContent( sal_uInt32 _nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(_nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XContent > xContent = m_pImpl->m_aResults[ _nIndex ]->xContent; + if ( xContent.is() ) + { + // Already cached. + return xContent; + } + } + + Reference< XContentIdentifier > xId = queryContentIdentifier( _nIndex ); + if ( xId.is() ) + { + try + { + Reference< XContent > xContent; + OUString sName = xId->getContentIdentifier(); + sName = sName.copy(sName.lastIndexOf('/')+1); + + m_pImpl->m_aResults[ _nIndex ]->xContent = m_pImpl->m_xContent->getContent(sName); + + xContent = m_pImpl->m_aResults[ _nIndex ]->xContent.get(); + return xContent; + + } + catch ( IllegalIdentifierException& ) + { + } + } + return Reference< XContent >(); +} + +bool DataSupplier::getResult( sal_uInt32 nIndex ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(nIndex) < m_pImpl->m_aResults.size() ) + { + // Result already present. + return true; + } + + // Result not (yet) present. + + if ( m_pImpl->m_bCountFinal ) + return false; + + // Try to obtain result... + + sal_uInt32 nOldCount = m_pImpl->m_aResults.size(); + bool bFound = false; + sal_uInt32 nPos = nOldCount; + + // @@@ Obtain data and put it into result list... + Sequence< OUString> aSeq = m_pImpl->m_xContent->getElementNames(); + if ( nIndex < sal::static_int_cast< sal_uInt32 >( aSeq.getLength() ) ) + { + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(pIter = pIter + nPos;pIter != pEnd;++pIter,++nPos) + { + m_pImpl->m_aResults.emplace_back( + new ResultListEntry( m_pImpl->m_xContent->getContent(*pIter)->getContentProperties() ) ); + + if ( nPos == nIndex ) + { + // Result obtained. + bFound = true; + break; + } + } + } + + if ( !bFound ) + m_pImpl->m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + aGuard.clear(); + + if ( static_cast<size_t>(nOldCount) < m_pImpl->m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_pImpl->m_aResults.size() ); + + if ( m_pImpl->m_bCountFinal ) + xResultSet->rowCountFinal(); + } + + return bFound; +} + +sal_uInt32 DataSupplier::totalCount() +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( m_pImpl->m_bCountFinal ) + return m_pImpl->m_aResults.size(); + + sal_uInt32 nOldCount = m_pImpl->m_aResults.size(); + + // @@@ Obtain data and put it into result list... + Sequence< OUString> aSeq = m_pImpl->m_xContent->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + m_pImpl->m_aResults.emplace_back( + new ResultListEntry( m_pImpl->m_xContent->getContent(*pIter)->getContentProperties() ) ); + + m_pImpl->m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + aGuard.clear(); + + if ( static_cast<size_t>(nOldCount) < m_pImpl->m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_pImpl->m_aResults.size() ); + + xResultSet->rowCountFinal(); + } + + return m_pImpl->m_aResults.size(); +} + +sal_uInt32 DataSupplier::currentCount() +{ + return m_pImpl->m_aResults.size(); +} + +bool DataSupplier::isCountFinal() +{ + return m_pImpl->m_bCountFinal; +} + +Reference< XRow > +DataSupplier::queryPropertyValues( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XRow > xRow = m_pImpl->m_aResults[ nIndex ]->xRow; + if ( xRow.is() ) + { + // Already cached. + return xRow; + } + } + + if ( getResult( nIndex ) ) + { + if ( !m_pImpl->m_aResults[ nIndex ]->xContent.is() ) + queryContent(nIndex); + + Reference< XRow > xRow = m_pImpl->m_aResults[ nIndex ]->xContent->getPropertyValues(getResultSet()->getProperties()); + m_pImpl->m_aResults[ nIndex ]->xRow = xRow; + return xRow; + } + + return Reference< XRow >(); +} + +void DataSupplier::releasePropertyValues( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast<size_t>(nIndex) < m_pImpl->m_aResults.size() ) + m_pImpl->m_aResults[ nIndex ]->xRow.clear(); +} + +void DataSupplier::close() +{ +} + +void DataSupplier::validate() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx b/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx new file mode 100644 index 000000000..8083f5ef5 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx @@ -0,0 +1,59 @@ +/* -*- 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 <ucbhelper/resultset.hxx> +#include "documentcontainer.hxx" +#include <memory> + +namespace dbaccess +{ +struct DataSupplier_Impl; + +class DataSupplier : public ucbhelper::ResultSetDataSupplier +{ + std::unique_ptr<DataSupplier_Impl> m_pImpl; + +public: + explicit DataSupplier(const rtl::Reference<ODocumentContainer>& rxContent); + virtual ~DataSupplier() override; + + virtual OUString queryContentIdentifierString(sal_uInt32 nIndex) override; + virtual css::uno::Reference<css::ucb::XContentIdentifier> + queryContentIdentifier(sal_uInt32 nIndex) override; + virtual css::uno::Reference<css::ucb::XContent> queryContent(sal_uInt32 nIndex) override; + + virtual bool getResult(sal_uInt32 nIndex) override; + + virtual sal_uInt32 totalCount() override; + virtual sal_uInt32 currentCount() override; + virtual bool isCountFinal() override; + + virtual css::uno::Reference<css::sdbc::XRow> queryPropertyValues(sal_uInt32 nIndex) override; + virtual void releasePropertyValues(sal_uInt32 nIndex) override; + + virtual void close() override; + + virtual void validate() override; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_resultset.cxx b/dbaccess/source/core/dataaccess/myucp_resultset.cxx new file mode 100644 index 000000000..67c1ad7c8 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_resultset.cxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +/************************************************************************** + TODO + ************************************************************************** + + - This implementation is not a dynamic result set!!! It only implements + the necessary interfaces, but never recognizes/notifies changes!!! + + *************************************************************************/ + +#include "myucp_datasupplier.hxx" +#include "myucp_resultset.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +using namespace dbaccess; + +// DynamicResultSet Implementation. +DynamicResultSet::DynamicResultSet( + const Reference< XComponentContext >& rxContext, + const rtl::Reference< ODocumentContainer >& rxContent, + const OpenCommandArgument2& rCommand, + const Reference< XCommandEnvironment >& rxEnv ) + :ResultSetImplHelper( rxContext, rCommand ) + ,m_xContent(rxContent) + ,m_xEnv( rxEnv ) +{ +} + +// Non-interface methods. +void DynamicResultSet::initStatic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContent ), + m_xEnv ); +} + +void DynamicResultSet::initDynamic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContent ), + m_xEnv ); + m_xResultSet2 = m_xResultSet1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_resultset.hxx b/dbaccess/source/core/dataaccess/myucp_resultset.hxx new file mode 100644 index 000000000..ae269ffb8 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_resultset.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 <rtl/ref.hxx> +#include <ucbhelper/resultsethelper.hxx> +#include "documentcontainer.hxx" + + +// @@@ Adjust namespace name. +namespace dbaccess { + +class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper +{ + rtl::Reference< ODocumentContainer > m_xContent; + css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv; + +private: + virtual void initStatic() override; + virtual void initDynamic() override; + +public: + DynamicResultSet( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const rtl::Reference< ODocumentContainer >& rxContent, + const css::ucb::OpenCommandArgument2& rCommand, + const css::uno::Reference< css::ucb::XCommandEnvironment >& rxEnv ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/ContainerMediator.hxx b/dbaccess/source/core/inc/ContainerMediator.hxx new file mode 100644 index 000000000..37bfc0eba --- /dev/null +++ b/dbaccess/source/core/inc/ContainerMediator.hxx @@ -0,0 +1,79 @@ +/* -*- 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/container/XContainerListener.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +#include <map> + +namespace dbaccess +{ + + class OPropertyForward; + + class OContainerMediator : public ::cppu::BaseMutex + ,public ::cppu::WeakImplHelper< css::container::XContainerListener > + { + private: + typedef std::map< OUString, ::rtl::Reference< OPropertyForward > > PropertyForwardList; + PropertyForwardList m_aForwardList; + css::uno::Reference< css::container::XNameAccess > m_xSettings; // can not be weak + css::uno::Reference< css::container::XContainer > m_xContainer; // can not be weak + + protected: + virtual ~OContainerMediator() override; + + public: + OContainerMediator( + const css::uno::Reference< css::container::XContainer >& _xContainer, + const css::uno::Reference< css::container::XNameAccess >& _xSettings + ); + + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& _rEvent ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& _rEvent ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& _rEvent ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + void notifyElementCreated(const OUString& _sElementName + ,const css::uno::Reference< css::beans::XPropertySet>& _xElement); + + private: + /** cleans up the instance, by deregistering as listener at the containers, + and resetting them to <NULL/> + */ + void impl_cleanup_nothrow(); + + /** initializes the properties of the given object from its counterpart in our settings container + */ + void impl_initSettings_nothrow( + const OUString& _rName, + const css::uno::Reference< css::beans::XPropertySet >& _rxDestination + ); + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/ContentHelper.hxx b/dbaccess/source/core/inc/ContentHelper.hxx new file mode 100644 index 000000000..004b785ef --- /dev/null +++ b/dbaccess/source/core/inc/ContentHelper.hxx @@ -0,0 +1,183 @@ +/* -*- 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/ucb/XContent.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/beans/XPropertiesChangeNotifier.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/multiinterfacecontainer3.hxx> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <connectivity/sqlerror.hxx> +#include <memory> + +namespace com::sun::star::beans { struct PropertyValue; } + +namespace dbaccess +{ + class ODatabaseModelImpl; + struct ContentProperties + { + OUString aTitle; // Title + ::std::optional< OUString > + aContentType; // ContentType (aka MediaType aka MimeType) + bool bIsDocument; // IsDocument + bool bIsFolder; // IsFolder + bool bAsTemplate; // AsTemplate + OUString sPersistentName;// persistent name of the document + + ContentProperties() + :bIsDocument( true ) + ,bIsFolder( false ) + ,bAsTemplate( false ) + { + } + }; + + class OContentHelper_Impl + { + public: + OContentHelper_Impl(); + virtual ~OContentHelper_Impl(); + + ContentProperties m_aProps; + ODatabaseModelImpl* m_pDataSource; // this will stay alive as long as the content exists + }; + + typedef std::shared_ptr<OContentHelper_Impl> TContentPtr; + + typedef comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XPropertiesChangeListener, OUString> + PropertyChangeListenerContainer; + typedef ::cppu::WeakComponentImplHelper< css::ucb::XContent + , css::ucb::XCommandProcessor + , css::lang::XServiceInfo + , css::beans::XPropertiesChangeNotifier + , css::beans::XPropertyContainer + , css::lang::XInitialization + , css::lang::XUnoTunnel + , css::container::XChild + , css::sdbcx::XRename + > OContentHelper_COMPBASE; + + class OContentHelper : public ::cppu::BaseMutex + ,public OContentHelper_COMPBASE + { + css::uno::Sequence< css::uno::Any > + setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& rValues ); + + void impl_rename_throw(const OUString& _sNewName,bool _bNotify = true); + + protected: + ::comphelper::OInterfaceContainerHelper3<css::ucb::XContentEventListener> m_aContentListeners; + PropertyChangeListenerContainer m_aPropertyChangeListeners; + css::uno::Reference< css::uno::XInterface > + m_xParentContainer; + const css::uno::Reference< css::uno::XComponentContext > + m_aContext; + const ::connectivity::SQLError m_aErrorHelper; + TContentPtr m_pImpl; + sal_uInt32 m_nCommandId; + + // helper + virtual void SAL_CALL disposing() override; + + void notifyDataSourceModified(); + + /** + * This method can be used to propagate changes of property values. + * + * @param evt is a sequence of property change events. + */ + void notifyPropertiesChange( const css::uno::Sequence< css::beans::PropertyChangeEvent >& evt ) const; + + OUString impl_getHierarchicalName( bool _includingRootContainer ) const; + + public: + + OContentHelper( const css::uno::Reference< css::uno::XComponentContext >& _xORB + ,const css::uno::Reference< css::uno::XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ); + + // css::lang::XTypeProvider + virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override; + + // css::lang::XServiceInfo + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + virtual OUString SAL_CALL getImplementationName( ) override; + + // XContent + virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL getIdentifier( ) override ; + virtual OUString SAL_CALL getContentType( ) override ; + virtual void SAL_CALL addContentEventListener( const css::uno::Reference< css::ucb::XContentEventListener >& Listener ) override ; + virtual void SAL_CALL removeContentEventListener( const css::uno::Reference< css::ucb::XContentEventListener >& Listener ) override ; + + // XCommandProcessor + virtual sal_Int32 SAL_CALL createCommandIdentifier( ) override ; + virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override ; + virtual void SAL_CALL abort( sal_Int32 CommandId ) override ; + + // XPropertiesChangeNotifier + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& PropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& Listener ) override ; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Sequence< OUString >& PropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& Listener ) override ; + + // XPropertyContainer + virtual void SAL_CALL addProperty( const OUString& Name, sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override ; + virtual void SAL_CALL removeProperty( const OUString& Name ) override ; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) 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::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + const ContentProperties& getContentProperties() const { return m_pImpl->m_aProps; } + css::uno::Reference< css::sdbc::XRow > + getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties ); + + const css::uno::Reference< css::uno::XComponentContext >& getContext() const { return m_aContext; } + + const TContentPtr& getImpl() const { return m_pImpl; } + + protected: + virtual OUString determineContentType() const = 0; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/DatabaseDataProvider.hxx b/dbaccess/source/core/inc/DatabaseDataProvider.hxx new file mode 100644 index 000000000..8c2029634 --- /dev/null +++ b/dbaccess/source/core/inc/DatabaseDataProvider.hxx @@ -0,0 +1,262 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp> +#include <com/sun/star/chart2/XInternalDataProvider.hpp> +#include <com/sun/star/chart/XComplexDescriptionAccess.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/container/XChild.hpp> + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/propertysetmixin.hxx> + +#include <connectivity/parameters.hxx> +#include <connectivity/filtermanager.hxx> + + +namespace dbaccess +{ + +typedef ::cppu::WeakComponentImplHelper< css::chart2::data::XDatabaseDataProvider + , css::container::XChild + , css::chart::XComplexDescriptionAccess + , css::lang::XServiceInfo > TDatabaseDataProvider; + +class DatabaseDataProvider: private ::cppu::BaseMutex, + public TDatabaseDataProvider, + public ::cppu::PropertySetMixin< css::chart2::data::XDatabaseDataProvider > +{ +public: + explicit DatabaseDataProvider(css::uno::Reference< css::uno::XComponentContext > const & context); + +private: + // css::uno::XInterface: + virtual css::uno::Any SAL_CALL queryInterface(css::uno::Type const & type) override; + virtual void SAL_CALL acquire() noexcept override + { TDatabaseDataProvider::acquire(); } + virtual void SAL_CALL release() noexcept override + { TDatabaseDataProvider::release(); } + + // 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::chart2::data::XDataProvider: + virtual sal_Bool SAL_CALL createDataSourcePossible(const css::uno::Sequence< css::beans::PropertyValue > & aArguments) override; + virtual css::uno::Reference< css::chart2::data::XDataSource > SAL_CALL createDataSource(const css::uno::Sequence< css::beans::PropertyValue > & aArguments) override; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL detectArguments(const css::uno::Reference< css::chart2::data::XDataSource > & xDataSource) override; + virtual sal_Bool SAL_CALL createDataSequenceByRangeRepresentationPossible(const OUString & aRangeRepresentation) override; + virtual css::uno::Reference< css::chart2::data::XDataSequence > SAL_CALL createDataSequenceByRangeRepresentation(const OUString & aRangeRepresentation) override; + + virtual css::uno::Reference<css::chart2::data::XDataSequence> SAL_CALL + createDataSequenceByValueArray( + const OUString& aRole, const OUString & aRangeRepresentation, const OUString& aRoleQualifier) override; + + virtual css::uno::Reference< css::sheet::XRangeSelection > SAL_CALL getRangeSelection() override; + + // css::chart2::data::XRangeXMLConversion: + virtual OUString SAL_CALL convertRangeToXML(const OUString & aRangeRepresentation) override; + virtual OUString SAL_CALL convertRangeFromXML(const OUString & aXMLRange) override; + + // css::lang::XInitialization: + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override; + + // css::beans::XPropertySet: + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue(const OUString & aPropertyName, const css::uno::Any & aValue) override; + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString & PropertyName) override; + virtual void SAL_CALL addPropertyChangeListener(const OUString & aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener > & xListener) override; + virtual void SAL_CALL removePropertyChangeListener(const OUString & aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener > & aListener) override; + virtual void SAL_CALL addVetoableChangeListener(const OUString & PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener > & aListener) override; + virtual void SAL_CALL removeVetoableChangeListener(const OUString & PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener > & aListener) override; + + // css::chart2::data::XDatabaseDataProvider: + virtual css::uno::Sequence< OUString > SAL_CALL getMasterFields() override; + virtual void SAL_CALL setMasterFields(const css::uno::Sequence< OUString > & the_value) override; + virtual css::uno::Sequence< OUString > SAL_CALL getDetailFields() override; + virtual void SAL_CALL setDetailFields(const css::uno::Sequence< OUString > & the_value) override; + virtual OUString SAL_CALL getCommand() override; + virtual void SAL_CALL setCommand(const OUString & the_value) override; + virtual ::sal_Int32 SAL_CALL getCommandType() override; + virtual void SAL_CALL setCommandType(::sal_Int32 the_value) override; + virtual OUString SAL_CALL getFilter() override; + virtual void SAL_CALL setFilter(const OUString & the_value) override; + virtual sal_Bool SAL_CALL getApplyFilter() override; + virtual void SAL_CALL setApplyFilter( sal_Bool _applyfilter ) override; + virtual OUString SAL_CALL getHavingClause() override; + virtual void SAL_CALL setHavingClause( const OUString& _havingclause ) override; + virtual OUString SAL_CALL getGroupBy() override; + virtual void SAL_CALL setGroupBy( const OUString& _groupby ) override; + virtual OUString SAL_CALL getOrder() override; + virtual void SAL_CALL setOrder( const OUString& _order ) override; + virtual sal_Bool SAL_CALL getEscapeProcessing() override; + virtual void SAL_CALL setEscapeProcessing(sal_Bool the_value) override; + virtual ::sal_Int32 SAL_CALL getRowLimit() override; + virtual void SAL_CALL setRowLimit( ::sal_Int32 _rowlimit ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getActiveConnection() override; + virtual void SAL_CALL setActiveConnection(const css::uno::Reference< css::sdbc::XConnection > & the_value) override; + virtual OUString SAL_CALL getDataSourceName() override; + virtual void SAL_CALL setDataSourceName( const OUString& _datasourcename ) override; + + // css::sdbc::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; + + // css::sdbc::XRowSet + virtual void SAL_CALL execute() override; + virtual void SAL_CALL addRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) override; + virtual void SAL_CALL removeRowSetListener(const css::uno::Reference< css::sdbc::XRowSetListener>& _rxListener) 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; + + // container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // ____ XComplexDescriptionAccess ____ + virtual css::uno::Sequence< css::uno::Sequence< OUString > > SAL_CALL getComplexRowDescriptions() override; + virtual void SAL_CALL setComplexRowDescriptions( const css::uno::Sequence< css::uno::Sequence< OUString > >& aRowDescriptions ) override; + virtual css::uno::Sequence< css::uno::Sequence< OUString > > SAL_CALL getComplexColumnDescriptions() override; + virtual void SAL_CALL setComplexColumnDescriptions( const css::uno::Sequence< css::uno::Sequence< OUString > >& aColumnDescriptions ) override; + + // ____ XChartDataArray (base of XComplexDescriptionAccess) ____ + virtual css::uno::Sequence< css::uno::Sequence< double > > SAL_CALL getData() override; + virtual void SAL_CALL setData( const css::uno::Sequence< css::uno::Sequence< double > >& aData ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getRowDescriptions() override; + virtual void SAL_CALL setRowDescriptions( const css::uno::Sequence< OUString >& aRowDescriptions ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getColumnDescriptions() override; + virtual void SAL_CALL setColumnDescriptions( const css::uno::Sequence< OUString >& aColumnDescriptions ) override; + + // ____ XChartData (base of XChartDataArray) ____ + virtual void SAL_CALL addChartDataChangeEventListener(const css::uno::Reference< css::chart::XChartDataChangeEventListener >& aListener ) override; + virtual void SAL_CALL removeChartDataChangeEventListener(const css::uno::Reference< css::chart::XChartDataChangeEventListener >& aListener ) override; + virtual double SAL_CALL getNotANumber() override; + virtual sal_Bool SAL_CALL isNotANumber(double nNumber ) override; +private: + DatabaseDataProvider(DatabaseDataProvider const &) = delete; + DatabaseDataProvider& operator =(DatabaseDataProvider const &) = delete; + + virtual ~DatabaseDataProvider() override {} + + // This function is called upon disposing the component, + // if your component needs special work when it becomes + // disposed, do it here. + virtual void SAL_CALL disposing() override; + + void impl_fillRowSet_throw(); + bool impl_fillParameters_nothrow( ::osl::ResettableMutexGuard& _rClearForNotifies); + void impl_fillInternalDataProvider_throw(bool _bHasCategories,const css::uno::Sequence< OUString >& i_aColumnNames); + void impl_invalidateParameter_nothrow(); + css::uno::Any impl_getNumberFormatKey_nothrow(const OUString & _sRangeRepresentation) const; + + template <typename T> void set( const OUString& _sProperty + ,const T& Value + ,T& _member) + { + BoundListeners l; + { + ::osl::MutexGuard aGuard(m_aMutex); + if ( _member != Value ) + { + prepareSet(_sProperty, css::uno::Any(_member), css::uno::Any(Value), &l); + _member = Value; + } + } + l.notify(); + } + + ::dbtools::ParameterManager m_aParameterManager; + ::dbtools::FilterManager m_aFilterManager; + std::map< OUString, css::uno::Any> m_aNumberFormats; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::sdbc::XConnection > m_xActiveConnection; + css::uno::Reference< css::sdbc::XRowSet > m_xRowSet; + css::uno::Reference< css::chart2::XInternalDataProvider > m_xInternal; + css::uno::Reference< css::chart::XComplexDescriptionAccess > m_xComplexDescriptionAccess; + css::uno::Reference< css::chart2::data::XRangeXMLConversion> m_xRangeConversion; + css::uno::Reference< css::task::XInteractionHandler> m_xHandler; + // the object doin' most of the work - an SDB-rowset + css::uno::Reference< css::uno::XAggregation> m_xAggregate; + css::uno::Reference< css::beans::XPropertySet> m_xAggregateSet; + css::uno::Reference< css::uno::XInterface> m_xParent; + css::uno::Sequence< OUString > m_MasterFields; + css::uno::Sequence< OUString > m_DetailFields; + + OUString m_Command; + OUString m_DataSourceName; + sal_Int32 m_CommandType; + sal_Int32 m_RowLimit; + OUString m_Filter; + OUString m_HavingClause; + OUString m_Order; + OUString m_GroupBy; + bool m_EscapeProcessing; + bool m_ApplyFilter; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/FilteredContainer.hxx b/dbaccess/source/core/inc/FilteredContainer.hxx new file mode 100644 index 000000000..968f3a1d6 --- /dev/null +++ b/dbaccess/source/core/inc/FilteredContainer.hxx @@ -0,0 +1,136 @@ +/* -*- 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 <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> + +#include <connectivity/sdbcx/VCollection.hxx> + +namespace dbtools +{ + class WarningsContainer; +} + +namespace dbaccess +{ + class IRefreshListener; + + class OFilteredContainer : public ::connectivity::sdbcx::OCollection + { + private: + mutable bool m_bConstructed; // late ctor called + + protected: + IRefreshListener* m_pRefreshListener; + std::atomic<std::size_t>& m_nInAppend; + + // holds the original container which where set in construct but they can be null + css::uno::Reference< css::container::XNameAccess > m_xMasterContainer; + css::uno::WeakReference< css::sdbc::XConnection > m_xConnection; + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + + /** returns a string denoting the only type of tables allowed in this container, or an empty string + if there is no such restriction + */ + virtual OUString getTableTypeRestriction() const = 0; + + virtual void addMasterContainerListener(){} + virtual void removeMasterContainerListener(){} + + // ::connectivity::sdbcx::OCollection + virtual void impl_refresh() override; + + virtual OUString getNameForObject(const ::connectivity::sdbcx::ObjectType& _xObject) override; + + /** tell the container to free all elements and all additional resources.<BR> + After using this method the object may be reconstructed by calling one of the <code>construct</code> methods. + */ + virtual void disposing() override; + + class EnsureReset + { + public: + EnsureReset( std::atomic<std::size_t>& _rValueLocation) + :m_rValue( _rValueLocation ) + { + ++m_rValue; + } + + ~EnsureReset() + { + --m_rValue; + } + + private: + std::atomic<std::size_t>& m_rValue; + }; + + /** retrieve a table type filter to pass to <member scope="css::sdbc">XDatabaseMetaData::getTables</member>, + according to the current data source settings + */ + void getAllTableTypeFilter( css::uno::Sequence< OUString >& /* [out] */ _rFilter ) const; + + public: + /** ctor of the container. The parent has to support the <type scope="css::sdbc">XConnection</type> + interface.<BR> + @param _rParent the object which acts as parent for the container. + all refcounting is rerouted to this object + @param _rMutex the access safety object of the parent + @param _rTableFilter restricts the visible tables by name + @param _rTableTypeFilter restricts the visible tables by type + @see construct + */ + OFilteredContainer( ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const css::uno::Reference< css::sdbc::XConnection >& _xCon, + bool _bCase, + IRefreshListener* _pRefreshListener, + std::atomic<std::size_t>& _nInAppend + ); + + void dispose() { disposing(); } + + /** late ctor. The container will fill itself with the data got by the connection meta data, considering the + filters given (the connection is the parent object you passed in the ctor). + */ + void construct( + const css::uno::Sequence< OUString >& _rTableFilter, + const css::uno::Sequence< OUString >& _rTableTypeFilter + ); + + /** late ctor. The container will fill itself with wrapper objects for the tables returned by the given + name container. + */ + void construct( + const css::uno::Reference< css::container::XNameAccess >& _rxMasterContainer, + const css::uno::Sequence< OUString >& _rTableFilter, + const css::uno::Sequence< OUString >& _rTableTypeFilter + ); + + bool isInitialized() const { return m_bConstructed; } + }; +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/ModelImpl.hxx b/dbaccess/source/core/inc/ModelImpl.hxx new file mode 100644 index 000000000..b6e2fe729 --- /dev/null +++ b/dbaccess/source/core/inc/ModelImpl.hxx @@ -0,0 +1,542 @@ +/* -*- 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 "ContentHelper.hxx" +#include "documentevents.hxx" + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertyBag.hpp> +#include <com/sun/star/document/XDocumentSubStorageSupplier.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/script/XStorageBasedLibraryContainer.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/weakref.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/docmacromode.hxx> +#include <sfx2/docstoragemodifylistener.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <rtl/ref.hxx> + +namespace comphelper +{ + class NamedValueCollection; +} + +namespace dbaccess +{ + +typedef std::vector< css::uno::WeakReference< css::sdbc::XConnection > > OWeakConnectionArray; + +struct AsciiPropertyValue +{ + // note: the canonic member order would be AsciiName / DefaultValue, but + // this crashes on unxlngi6.pro, since there's a bug which somehow results in + // getDefaultDataSourceSettings returning corrupted Any instances then. + css::uno::Any DefaultValue; + const char* AsciiName; + const css::uno::Type& ValueType; + + AsciiPropertyValue() + :DefaultValue( ) + ,AsciiName( nullptr ) + ,ValueType( ::cppu::UnoType<void>::get() ) + { + } + + AsciiPropertyValue( const char* _pAsciiName, const css::uno::Any& _rDefaultValue ) + :DefaultValue( _rDefaultValue ) + ,AsciiName( _pAsciiName ) + ,ValueType( _rDefaultValue.getValueType() ) + { + OSL_ENSURE( ValueType.getTypeClass() != css::uno::TypeClass_VOID, + "AsciiPropertyValue::AsciiPropertyValue: NULL values not allowed here, use the other CTOR for this!" ); + } + AsciiPropertyValue( const char* _pAsciiName, const css::uno::Type& _rValeType ) + :DefaultValue() + ,AsciiName( _pAsciiName ) + ,ValueType( _rValeType ) + { + OSL_ENSURE( ValueType.getTypeClass() != css::uno::TypeClass_VOID, + "AsciiPropertyValue::AsciiPropertyValue: VOID property values not supported!" ); + } +}; + +class ODatabaseContext; +class OSharedConnectionManager; + +// ODatabaseModelImpl +typedef ::utl::SharedUNOComponent< css::embed::XStorage > SharedStorage; + +class ODatabaseContext; +class DocumentStorageAccess; +class OSharedConnectionManager; +class ODatabaseModelImpl :public ::sfx2::IMacroDocumentAccess + ,public ::sfx2::IModifiableDocument +{ +public: + enum ObjectType + { + E_FORM = 0, + E_REPORT = 1, + E_QUERY = 2, + E_TABLE = 3 + }; + + enum EmbeddedMacros + { + // the database document (storage) itself contains macros + eDocumentWideMacros, + // there are sub document( storage)s containing macros + eSubDocumentMacros, + // there are no known macro( storage)s + eNoMacros + }; + +private: + css::uno::WeakReference< css::frame::XModel > m_xModel; + css::uno::WeakReference< css::sdbc::XDataSource > m_xDataSource; + + rtl::Reference<DocumentStorageAccess> m_pStorageAccess; + std::vector< TContentPtr > m_aContainer; // one for each ObjectType + ::sfx2::DocumentMacroMode m_aMacroMode; + sal_Int16 m_nImposedMacroExecMode; + + css::uno::Reference< css::script::XStorageBasedLibraryContainer > m_xBasicLibraries; + css::uno::Reference< css::script::XStorageBasedLibraryContainer > m_xDialogLibraries; + + SharedStorage m_xDocumentStorage; + ::rtl::Reference< ::sfx2::DocumentStorageModifyListener > m_pStorageModifyListener; + ODatabaseContext& m_rDBContext; + DocumentEventsData m_aDocumentEvents; + + ::comphelper::NamedValueCollection m_aMediaDescriptor; + /// the URL the document was loaded from + OUString m_sDocFileLocation; + + oslInterlockedCount m_refCount; + + /// do we have any object (forms/reports) which contains macros? + ::std::optional< EmbeddedMacros > m_aEmbeddedMacros; + + /// true if setting the Modified flag of the document is currently locked + bool m_bModificationLock; + + /// true if and only if a database document existed previously (though meanwhile disposed), and was already initialized + bool m_bDocumentInitialized; + + /** the URL which the document should report as its URL + + This might differ from ->m_sDocFileLocation in case the document was loaded + as part of a crash recovery process. In this case, ->m_sDocFileLocation points to + the temporary file where the DB had been saved to, after a crash. + ->m_sDocumentURL then is the URL of the document which actually had + been recovered. + */ + OUString m_sDocumentURL; + + SignatureState m_nScriptingSignatureState; + +public: + OWeakConnectionArray m_aConnections; + const css::uno::Reference< css::uno::XComponentContext > m_aContext; + +public: + css::uno::WeakReference< css::container::XNameAccess > m_xCommandDefinitions; + css::uno::WeakReference< css::container::XNameAccess > m_xTableDefinitions; + + css::uno::Reference< css::util::XNumberFormatsSupplier > + m_xNumberFormatsSupplier; + OUString m_sConnectURL; + OUString m_sName; // transient, our creator has to tell us the title + OUString m_sUser; + OUString m_aPassword; // transient ! + OUString m_sFailedPassword; + css::uno::Sequence< css::beans::PropertyValue> + m_aLayoutInformation; + sal_Int32 m_nLoginTimeout; + bool m_bReadOnly : 1; + bool m_bPasswordRequired : 1; + bool m_bSuppressVersionColumns : 1; + bool m_bModified : 1; + bool m_bDocumentReadOnly : 1; + bool m_bMacroCallsSeenWhileLoading : 1; + css::uno::Reference< css::beans::XPropertyBag > + m_xSettings; + css::uno::Sequence< OUString > m_aTableFilter; + css::uno::Sequence< OUString > m_aTableTypeFilter; + OSharedConnectionManager* m_pSharedConnectionManager; + css::uno::Reference< css::lang::XEventListener > + m_xSharedConnectionManager; + css::uno::Reference<css::awt::XWindow> + m_xDialogParent; + sal_uInt16 m_nControllerLockCount; + + void reset(); + + /** determines whether the database document has an embedded data storage + */ + bool isEmbeddedDatabase() const { return m_sConnectURL.startsWith("sdbc:embedded:"); } + + /** stores the embedded storage ("database") + + @param _bPreventRootCommits + Normally, committing the embedded storage results in also committing the root storage + - this is an automatism for data safety reasons. + If you pass <TRUE/> here, committing the root storage is prevented for this particular + call. + @return <TRUE/> if the storage could be committed, otherwise <FALSE/> + */ + bool commitEmbeddedStorage( bool _bPreventRootCommits = false ); + + /// commits all sub storages + void commitStorages(); + + ODatabaseModelImpl( + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + ODatabaseContext& _pDBContext + ); + virtual ~ODatabaseModelImpl(); + + ODatabaseModelImpl( + const OUString& _rRegistrationName, + const css::uno::Reference< css::uno::XComponentContext >& _rxContext, + ODatabaseContext& _rDBContext + ); + + // XEventListener + /// @throws css::uno::RuntimeException + void disposing( const css::lang::EventObject& Source ); + + void setModified( bool bModified ); + + void dispose(); + + const OUString& getURL() const { return m_sDocumentURL; } + const OUString& getDocFileLocation() const { return m_sDocFileLocation; } + + css::uno::Reference< css::embed::XStorage > + getStorage( const ObjectType _eType ); + +// helper + const css::uno::Reference< css::util::XNumberFormatsSupplier >& + getNumberFormatsSupplier(); + + DocumentEventsData& + getDocumentEvents() { return m_aDocumentEvents; } + + const ::comphelper::NamedValueCollection& + getMediaDescriptor() const { return m_aMediaDescriptor; } + + void setResource( + const OUString& _rURL, + const css::uno::Sequence< css::beans::PropertyValue >& _rArgs + ); + void setDocFileLocation( + const OUString& i_rLoadedFrom + ); + + static ::comphelper::NamedValueCollection + stripLoadArguments( const ::comphelper::NamedValueCollection& _rArguments ); + +// other stuff + + // disposes all elements in m_aStorages, and clears it + void disposeStorages(); + + /// creates a ->css::embed::StorageFactory + css::uno::Reference< css::lang::XSingleServiceFactory > + createStorageFactory() const; + + /// commits our storage + void commitRootStorage(); + + /// commits a given storage if it's not readonly, ignoring (but asserting) all errors + bool commitStorageIfWriteable_ignoreErrors( + const css::uno::Reference< css::embed::XStorage >& _rxStorage + ); + + void clearConnections(); + + css::uno::Reference< css::embed::XStorage > const & getOrCreateRootStorage(); + css::uno::Reference< css::embed::XStorage > const & getRootStorage() const { return m_xDocumentStorage.getTyped(); } + void resetRootStorage() { impl_switchToStorage_throw( nullptr ); } + + /** returns the data source. If it doesn't exist it will be created + */ + css::uno::Reference< css::sdbc::XDataSource> getOrCreateDataSource(); + + /** returns the model, if there already exists one + */ + css::uno::Reference< css::frame::XModel > getModel_noCreate() const; + + /** returns a new ->ODatabaseDocument + + @precond + No ->ODatabaseDocument exists so far + + @seealso + getModel_noCreate + */ + css::uno::Reference< css::frame::XModel > createNewModel_deliverOwnership(); + + struct ResetModelAccess { friend class ODatabaseDocument; private: ResetModelAccess() { } }; + + /** resets the model to NULL + + Only to be called when the model is being disposed + */ + void modelIsDisposing( const bool _wasInitialized, ResetModelAccess ); + + bool hadInitializedDocument() const { return m_bDocumentInitialized; } + + DocumentStorageAccess* + getDocumentStorageAccess(); + + css::uno::Reference< css::document::XDocumentSubStorageSupplier > + getDocumentSubStorageSupplier(); + + void acquire(); + + void release(); + + /// returns all known data source settings, including their default values + static const AsciiPropertyValue* getDefaultDataSourceSettings(); + + /** retrieves the requested container of objects (forms/reports/tables/queries) + */ + TContentPtr& getObjectContainer( const ObjectType _eType ); + + /** returns the name of the storage which is used to stored objects of the given type, if applicable + */ + static OUString + getObjectContainerStorageName( const ObjectType _eType ); + + /** determines whether a given object storage contains macros + */ + static bool objectHasMacros( + const css::uno::Reference< css::embed::XStorage >& _rxContainerStorage, + const OUString& _rPersistentName + ); + + /** determines which kind of embedded macros are present in the document + */ + EmbeddedMacros determineEmbeddedMacros(); + + /** checks our document's macro execution mode, using the interaction handler as supplied with our + load arguments + */ + bool checkMacrosOnLoading(); + + /** adjusts our document's macro execution mode, without using any UI, assuming the user + would reject execution of macros, if she would have been asked. + + If checkMacrosOnLoading has been called before (and thus the macro execution mode + is already adjusted), then the current execution mode is simply returned. + + @return + whether or not macro execution is allowed + */ + bool adjustMacroMode_AutoReject(); + + /** resets our macro execute mode, so next time the checkMacrosOnLoading is called, it will + behave as if it has never been called before + */ + void resetMacroExecutionMode(); + + /** ensures that ->m_xBasicLibraries resp. m_xDialogLibraries exists + + @return + the requested library container. Is never <NULL/>. + + @throws RuntimeException + if something does wrong, which indicates a server error in the installation + */ + css::uno::Reference< css::script::XStorageBasedLibraryContainer > + getLibraryContainer( bool _bScript ); + + /** lets our library containers store themself into the given root storage + */ + void storeLibraryContainersTo( const css::uno::Reference< css::embed::XStorage >& _rxToRootStorage ); + + /** rebases the document to the given storage + + No actual committing, copying, saving, whatsoever happens. The storage is just remembered as the documents + new storage, nothing more. + + @throws css::lang::IllegalArgumentException + if the given storage is <NULL/> + @throws css::lang::RuntimeException + if any of the invoked operations does so + */ + css::uno::Reference< css::embed::XStorage > + switchToStorage( + const css::uno::Reference< css::embed::XStorage >& _rxNewRootStorage + ); + + /** returns the macro mode imposed by an external instance, which passed it to attachResource + */ + sal_Int16 getImposedMacroExecMode() const + { + return m_nImposedMacroExecMode; + } + void setImposedMacroExecMode( const sal_Int16 _nMacroMode ) + { + m_nImposedMacroExecMode = _nMacroMode; + } + +public: + // IMacroDocumentAccess overridables + virtual sal_Int16 getCurrentMacroExecMode() const override; + virtual void setCurrentMacroExecMode( sal_uInt16 ) override; + virtual OUString getDocumentLocation() const override; + virtual bool documentStorageHasMacros() const override; + virtual bool macroCallsSeenWhileLoading() const override; + virtual css::uno::Reference< css::document::XEmbeddedScripts > getEmbeddedDocumentScripts() const override; + virtual SignatureState getScriptingSignatureState() override; + virtual bool hasTrustedScriptingSignature( bool bAllowUIToAddAuthor ) override; + + // IModifiableDocument + virtual void storageIsModified() override; + + // don't use directly, use the ModifyLock class instead + void lockModify() { m_bModificationLock = true; } + void unlockModify() { m_bModificationLock = false; } + bool isModifyLocked() const { return m_bModificationLock; } + + weld::Window* GetFrameWeld(); + +private: + void impl_construct_nothrow(); + css::uno::Reference< css::embed::XStorage > const & + impl_switchToStorage_throw( const css::uno::Reference< css::embed::XStorage >& _rxNewRootStorage ); + + /** switches to the given document URL, which denotes the logical URL of the document, not necessarily the + URL where the doc was loaded/recovered from + */ + void impl_switchToLogicalURL( + const OUString& i_rDocumentURL + ); + +}; + +/** a small base class for UNO components whose functionality depends on an ODatabaseModelImpl +*/ +class ModelDependentComponent +{ +protected: + ::rtl::Reference< ODatabaseModelImpl > m_pImpl; + ::osl::Mutex m_aMutex; // only use this to init WeakComponentImplHelper + +protected: + explicit ModelDependentComponent( const ::rtl::Reference< ODatabaseModelImpl >& _model ); + virtual ~ModelDependentComponent(); + + /** returns the component itself + */ + virtual css::uno::Reference< css::uno::XInterface > getThis() const = 0; + + ::osl::Mutex& getMutex() + { + return m_aMutex; + } + +public: + /// checks whether the component is already disposed, throws a DisposedException if so + void checkDisposed() const + { + if ( !m_pImpl.is() ) + throw css::lang::DisposedException( "Component is already disposed.", getThis() ); + } + + void lockModify() + { + m_pImpl->lockModify(); + } + + void unlockModify() + { + m_pImpl->unlockModify(); + } +}; + +class ModifyLock +{ +public: + explicit ModifyLock( ModelDependentComponent& _component ) + :m_rComponent( _component ) + { + m_rComponent.lockModify(); + } + + ~ModifyLock() + { + m_rComponent.unlockModify(); + } + +private: + ModelDependentComponent& m_rComponent; +}; + +/** a guard for public methods of objects dependent on an ODatabaseModelImpl instance + + Just put this guard onto the stack at the beginning of your method. Don't bother yourself + with a MutexGuard, checks for being disposed, and the like. +*/ +class ModelMethodGuard +{ +private: + // to avoid deadlocks, lock SolarMutex + SolarMutexResettableGuard m_SolarGuard; + +public: + /** constructs the guard + + @param _component + the component whose functionality depends on an ODatabaseModelImpl instance + + @throws css::lang::DisposedException + If the given component is already disposed + */ + explicit ModelMethodGuard( const ModelDependentComponent& _component ) + { + _component.checkDisposed(); + } + + void clear() + { + // note: this only releases *once* so may still be locked + m_SolarGuard.clear(); + } + + void reset() + { + m_SolarGuard.reset(); + } +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/PropertyForward.hxx b/dbaccess/source/core/inc/PropertyForward.hxx new file mode 100644 index 000000000..b8e602fc0 --- /dev/null +++ b/dbaccess/source/core/inc/PropertyForward.hxx @@ -0,0 +1,69 @@ +/* -*- 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/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <vector> + +namespace dbaccess +{ + + // OPropertyForward + typedef ::cppu::WeakImplHelper< css::beans::XPropertyChangeListener + > OPropertyForward_Base; + class OPropertyForward :public ::cppu::BaseMutex + ,public OPropertyForward_Base + { + css::uno::Reference< css::beans::XPropertySet > m_xSource; + css::uno::Reference< css::beans::XPropertySet > m_xDest; + css::uno::Reference< css::beans::XPropertySetInfo > m_xDestInfo; + css::uno::Reference< css::container::XNameAccess > m_xDestContainer; + OUString m_sName; + bool m_bInInsert; + + protected: + virtual ~OPropertyForward() override; + + public: + OPropertyForward( const css::uno::Reference< css::beans::XPropertySet>& _xSource, + const css::uno::Reference< css::container::XNameAccess>& _xDestContainer, + const OUString& _sName, + const std::vector< OUString >& _aPropertyList + ); + + // 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; + + void setName( const OUString& _sName ) { m_sName = _sName; } + void setDefinition( const css::uno::Reference< css::beans::XPropertySet >& _xDest); + const css::uno::Reference< css::beans::XPropertySet >& getDefinition() const { return m_xDest; } + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/RefreshListener.hxx b/dbaccess/source/core/inc/RefreshListener.hxx new file mode 100644 index 000000000..03836c308 --- /dev/null +++ b/dbaccess/source/core/inc/RefreshListener.hxx @@ -0,0 +1,42 @@ +/* -*- 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/uno/Reference.h> + +namespace com::sun::star::container { class XNameAccess; } + + +namespace dbaccess +{ + + // IRefreshListener + class SAL_NO_VTABLE IRefreshListener + { + public: + virtual void refresh(const css::uno::Reference< css::container::XNameAccess >& _rToBeRefreshed) = 0; + + protected: + ~IRefreshListener() {} + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/SingleSelectQueryComposer.hxx b/dbaccess/source/core/inc/SingleSelectQueryComposer.hxx new file mode 100644 index 000000000..a0edfd195 --- /dev/null +++ b/dbaccess/source/core/inc/SingleSelectQueryComposer.hxx @@ -0,0 +1,263 @@ +/* -*- 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/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <cppuhelper/implbase5.hxx> +#include <connectivity/sqliterator.hxx> +#include <connectivity/sqlparse.hxx> +#include <apitools.hxx> +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <svx/ParseContext.hxx> + +namespace com::sun::star::util { + class XNumberFormatsSupplier; + class XNumberFormatter; +} + +namespace dbaccess +{ + typedef ::cppu::ImplHelper5< css::sdb::XSingleSelectQueryComposer, + css::sdb::XParametersSupplier, + css::sdbcx::XColumnsSupplier, + css::sdbcx::XTablesSupplier, + css::lang::XServiceInfo > OSingleSelectQueryComposer_BASE; + + class OPrivateColumns; + class OPrivateTables; + + class OSingleSelectQueryComposer : public ::comphelper::OMutexAndBroadcastHelper + ,public OSubComponent + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper < OSingleSelectQueryComposer > + ,public OSingleSelectQueryComposer_BASE + { + enum SQLPart + { + Where = 0, // the 0 is important, as it will be used as index into arrays + Group, + Having, + Order, + + SQLPartCount + }; + static void incSQLPart( SQLPart& e ) { e = static_cast<SQLPart>(1 + static_cast<size_t>(e)); } + enum EColumnType + { + SelectColumns = 0, + GroupByColumns = 1, + OrderColumns = 2, + ParameterColumns = 3 + }; + typedef std::function<const ::connectivity::OSQLParseNode*(::connectivity::OSQLParseTreeIterator *)> + TGetParseNode; + ::svxform::OSystemParseContext m_aParseContext; + ::connectivity::OSQLParser m_aSqlParser; + ::connectivity::OSQLParseTreeIterator m_aSqlIterator; // the iterator for the complete statement + ::connectivity::OSQLParseTreeIterator m_aAdditiveIterator; // the iterator for the "additive statement" (means without the clauses of the elementary statement) + std::vector<std::unique_ptr<OPrivateColumns>> + m_aColumnsCollection; // used for columns and parameters of old queries + std::vector<std::unique_ptr<OPrivateTables>> + m_aTablesCollection; + + std::vector< OUString > m_aElementaryParts; // the filter/groupby/having/order of the elementary statement + + css::uno::Reference< css::sdbc::XConnection> m_xConnection; + css::uno::Reference< css::sdbc::XDatabaseMetaData> m_xMetaData; + css::uno::Reference< css::container::XNameAccess> m_xConnectionTables; + css::uno::Reference< css::container::XNameAccess> m_xConnectionQueries; + css::uno::Reference< css::util::XNumberFormatsSupplier > m_xNumberFormatsSupplier; + css::uno::Reference< css::uno::XComponentContext> m_aContext; + css::uno::Reference< css::script::XTypeConverter > m_xTypeConverter; + + std::vector<std::unique_ptr<OPrivateColumns>> m_aCurrentColumns; + std::unique_ptr<OPrivateTables> m_pTables; // currently used tables + + OUString m_aPureSelectSQL; // the pure select statement, without filter/order/groupby/having + OUString m_sDecimalSep; + OUString m_sCommand; + css::lang::Locale m_aLocale; + sal_Int32 m_nBoolCompareMode; // how to compare bool values + sal_Int32 m_nCommandType; + + // <properties> + OUString m_sOriginal; + // </properties> + + + bool setORCriteria(::connectivity::OSQLParseNode const * pCondition, ::connectivity::OSQLParseTreeIterator& _rIterator, + std::vector< std::vector < css::beans::PropertyValue > >& rFilters, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter) const; + bool setANDCriteria(::connectivity::OSQLParseNode const * pCondition, ::connectivity::OSQLParseTreeIterator& _rIterator, + std::vector < css::beans::PropertyValue > & rFilters, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter) const; + bool setLikePredicate(::connectivity::OSQLParseNode const * pCondition, ::connectivity::OSQLParseTreeIterator const & _rIterator, + std::vector < css::beans::PropertyValue > & rFilters, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter) const; + bool setComparisonPredicate(::connectivity::OSQLParseNode const * pCondition, ::connectivity::OSQLParseTreeIterator const & _rIterator, + std::vector < css::beans::PropertyValue > & rFilters, const css::uno::Reference< css::util::XNumberFormatter > & xFormatter) const; + + static OUString getColumnName(::connectivity::OSQLParseNode const * pColumnRef, ::connectivity::OSQLParseTreeIterator const & _rIterator); + OUString getTableAlias(const css::uno::Reference< css::beans::XPropertySet >& column ) const; + static sal_Int32 getPredicateType(::connectivity::OSQLParseNode const * _pPredicate); + // clears all Columns,Parameters and tables and insert it to their vectors + void clearCurrentCollections(); + // clears the columns collection given by EColumnType + void clearColumns( const EColumnType _eType ); + + /** retrieves a particular part of a statement + @param _rIterator + the iterator to use. + */ + OUString getStatementPart( TGetParseNode const & _aGetFunctor, ::connectivity::OSQLParseTreeIterator& _rIterator ); + void setQuery_Impl( const OUString& command ); + + void setConditionByColumn( const css::uno::Reference< css::beans::XPropertySet >& column + , bool andCriteria + , std::function<bool(OSingleSelectQueryComposer *, const OUString&)> const & _aSetFunctor + ,sal_Int32 filterOperator); + + /** getStructuredCondition returns the structured condition for the where or having clause + @param _aGetFunctor + A member function to get the correct parse node. + + @return + The structured filter + */ + css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > + getStructuredCondition( TGetParseNode const & _aGetFunctor ); + + css::uno::Reference< css::container::XIndexAccess > + setCurrentColumns( EColumnType _eType, const ::rtl::Reference< ::connectivity::OSQLColumns >& _rCols ); + + //helper methods for mem_fun_t + bool implSetFilter(const OUString& _sFilter) { setFilter(_sFilter); return true;} + bool implSetHavingClause(const OUString& _sFilter) { setHavingClause(_sFilter); return true;} + + /** returns the part of the select statement + @param _ePart + Which part should be returned. + @param _bWithKeyword + If <TRUE/> the keyword will be added too. Otherwise not. + @param _rIterator + The iterator to use. + + @return + The part of the select statement. + */ + OUString getSQLPart( SQLPart _ePart, ::connectivity::OSQLParseTreeIterator& _rIterator, bool _bWithKeyword ); + + /** retrieves the keyword for the given SQLPart + */ + static OUString getKeyword( SQLPart _ePart ); + + /** sets a single "additive" clause, means a filter/groupby/having/order clause + */ + void setSingleAdditiveClause( SQLPart _ePart, const OUString& _rClause ); + + /** composes a statement from m_aPureSelectSQL and the 4 usual clauses + */ + OUString composeStatementFromParts( const std::vector< OUString >& _rParts ); + + /** return the name of the column in the *source* *table*. + + That is, for (SELECT a AS b FROM t), it returns A or "t"."A", as appropriate. + + Use e.g. for WHERE, GROUP BY and HAVING clauses. + + @param bGroupBy: for GROUP BY clause? In that case, throw exception if trying to use an unrelated column and the database does not support that. + */ + OUString impl_getColumnRealName_throw(const css::uno::Reference< css::beans::XPropertySet >& column, bool bGroupBy); + + /** return the name of the column in the *query* for ORDER BY clause. + + That is, for (SELECT a AS b FROM t), it returns "b" + + Throws exception if trying to use an unrelated column and the database does not support that. + */ + OUString impl_getColumnNameOrderBy_throw(const css::uno::Reference< css::beans::XPropertySet >& column); + + protected: + virtual ~OSingleSelectQueryComposer() override; + public: + + OSingleSelectQueryComposer( const css::uno::Reference< css::container::XNameAccess>& _xTableSupplier, + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const css::uno::Reference< css::uno::XComponentContext>& _rContext); + + + void SAL_CALL disposing() override; + + 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( ) + + // XServiceInfo + DECLARE_SERVICE_INFO(); + + virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + + + // css::sdb::XSingleSelectQueryComposer + virtual OUString SAL_CALL getElementaryQuery() override; + virtual void SAL_CALL setElementaryQuery( const OUString& _rElementary ) override; + virtual void SAL_CALL setFilter( const OUString& filter ) override; + virtual void SAL_CALL setStructuredFilter( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& filter ) override; + virtual void SAL_CALL appendFilterByColumn( const css::uno::Reference< css::beans::XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator ) override; + virtual void SAL_CALL appendGroupByColumn( const css::uno::Reference< css::beans::XPropertySet >& column ) override; + virtual void SAL_CALL setGroup( const OUString& group ) override; + virtual void SAL_CALL setHavingClause( const OUString& filter ) override; + virtual void SAL_CALL setStructuredHavingClause( const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > >& filter ) override; + virtual void SAL_CALL appendHavingClauseByColumn( const css::uno::Reference< css::beans::XPropertySet >& column, sal_Bool andCriteria,sal_Int32 filterOperator ) override; + virtual void SAL_CALL appendOrderByColumn( const css::uno::Reference< css::beans::XPropertySet >& column, sal_Bool ascending ) override; + virtual void SAL_CALL setOrder( const OUString& order ) override; + + // XSingleSelectQueryAnalyzer + virtual OUString SAL_CALL getQuery( ) override; + virtual void SAL_CALL setQuery( const OUString& command ) override; + virtual void SAL_CALL setCommand( const OUString& command,sal_Int32 CommandType ) override; + virtual OUString SAL_CALL getFilter( ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getStructuredFilter( ) override; + virtual OUString SAL_CALL getGroup( ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getGroupColumns( ) override; + virtual OUString SAL_CALL getHavingClause( ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getStructuredHavingClause( ) override; + virtual OUString SAL_CALL getOrder( ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getOrderColumns( ) override; + virtual OUString SAL_CALL getQueryWithSubstitution( ) override; + + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + // XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; + // XParametersSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getParameters( ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/TableDeco.hxx b/dbaccess/source/core/inc/TableDeco.hxx new file mode 100644 index 000000000..e40fac802 --- /dev/null +++ b/dbaccess/source/core/inc/TableDeco.hxx @@ -0,0 +1,164 @@ +/* -*- 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 <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XIndexesSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/sdbcx/XAlterTable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <cppuhelper/compbase.hxx> +#include "datasettings.hxx" +#include "column.hxx" + +#include <connectivity/CommonTools.hxx> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <comphelper/IdPropArrayHelper.hxx> + +namespace dbaccess +{ + typedef ::cppu::WeakComponentImplHelper< css::sdbcx::XColumnsSupplier, + css::sdbcx::XKeysSupplier, + css::container::XNamed, + css::lang::XServiceInfo, + css::sdbcx::XDataDescriptorFactory, + css::sdbcx::XIndexesSupplier, + css::sdbcx::XRename, + css::lang::XUnoTunnel, + css::sdbcx::XAlterTable> OTableDescriptor_BASE; + // OTables + class ODBTableDecorator; + typedef ::comphelper::OIdPropertyArrayUsageHelper< ODBTableDecorator > ODBTableDecorator_PROP; + + class ODBTableDecorator :public cppu::BaseMutex + ,public OTableDescriptor_BASE + ,public ODataSettings //ODataSettings_Base + ,public IColumnFactory + ,public ::connectivity::sdbcx::IRefreshableColumns + ,public ODBTableDecorator_PROP + { + void fillPrivileges() const; + protected: + css::uno::Reference< css::container::XContainerListener > m_xColumnMediator; + css::uno::Reference< css::sdbcx::XColumnsSupplier > m_xTable; + css::uno::Reference< css::container::XNameAccess > m_xColumnDefinitions; + css::uno::Reference< css::sdbc::XConnection > m_xConnection; + css::uno::Reference< css::sdbc::XDatabaseMetaData > m_xMetaData; + css::uno::Reference< css::util::XNumberFormatsSupplier > m_xNumberFormats; + + // <properties> + mutable sal_Int32 m_nPrivileges; + // </properties> + // note: this thing uses the ref-count of "this", see OCollection::acquire()! + std::unique_ptr<::connectivity::sdbcx::OCollection> m_pColumns; + + // 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; + + virtual void refreshColumns() override; + + virtual ::cppu::IPropertyArrayHelper* createArrayHelper(sal_Int32 _nId) const override; + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + // OPropertySetHelper + 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; + + virtual ~ODBTableDecorator() override; + public: + /** constructs a wrapper supporting the com.sun.star.sdb.Table service. + + @param _rxConn + the connection the table belongs to. Must not be <NULL/> + @param _rxTable + the table from the driver can be <NULL/> + @throws css::sdbc::SQLException + */ + ODBTableDecorator( + const css::uno::Reference< css::sdbc::XConnection >& _rxConn, + const css::uno::Reference< css::sdbcx::XColumnsSupplier >& _rxTable, + const css::uno::Reference< css::util::XNumberFormatsSupplier >& _rxNumberFormats, + const css::uno::Reference< css::container::XNameAccess >& _rxColumnDefinitions + ); + + // ODescriptor + void construct(); + + //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; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // css::sdbcx::XRename, + virtual void SAL_CALL rename( const OUString& _rNewName ) override; + + // css::sdbcx::XAlterTable, + virtual void SAL_CALL alterColumnByName( const OUString& _rName, const css::uno::Reference< css::beans::XPropertySet >& _rxDescriptor ) override; + virtual void SAL_CALL alterColumnByIndex( sal_Int32 _nIndex, const css::uno::Reference< css::beans::XPropertySet >& _rxDescriptor ) override; + + // XNamed + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName( const OUString& aName ) 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(); + + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + // XKeysSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getKeys( ) override; + // XIndexesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getIndexes( ) override; + // XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + + protected: + using ODataSettings::getFastPropertyValue; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/View.hxx b/dbaccess/source/core/inc/View.hxx new file mode 100644 index 000000000..d3889ff56 --- /dev/null +++ b/dbaccess/source/core/inc/View.hxx @@ -0,0 +1,71 @@ +/* -*- 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/VView.hxx> + +#include <com/sun/star/sdbcx/XAlterView.hpp> +#include <com/sun/star/sdb/tools/XViewAccess.hpp> + +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase1.hxx> + +namespace dbaccess +{ + + // View + typedef ::connectivity::sdbcx::OView View_Base; + typedef ::cppu::ImplHelper1< css::sdbcx::XAlterView > View_IBASE; + class View :public View_Base + ,public View_IBASE + { + public: + View( + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, + bool _bCaseSensitive, + const OUString& _rCatalogName, + const OUString& _rSchemaName, + const OUString& _rName + ); + + // UNO + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XAlterView + virtual void SAL_CALL alterCommand( const OUString& NewCommand ) override; + + protected: + virtual ~View() override; + + protected: + // OPropertyContainer + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override; + + private: + css::uno::Reference< css::sdb::tools::XViewAccess> m_xViewAccess; + sal_Int32 m_nCommandHandle; + private: + using View_Base::getFastPropertyValue; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/bookmarkcontainer.hxx b/dbaccess/source/core/inc/bookmarkcontainer.hxx new file mode 100644 index 000000000..464192542 --- /dev/null +++ b/dbaccess/source/core/inc/bookmarkcontainer.hxx @@ -0,0 +1,148 @@ +/* -*- 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 <vector> + +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase.hxx> +#include <osl/mutex.hxx> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +namespace dbaccess +{ + +// OBookmarkContainer - base class of collections of database definition +// documents +typedef ::cppu::WeakImplHelper< + css::container::XIndexAccess + , css::container::XNameContainer + , css::container::XEnumerationAccess + , css::container::XContainer + , css::lang::XServiceInfo + , css::container::XChild + > OBookmarkContainer_Base; + +class OBookmarkContainer final + :public OBookmarkContainer_Base +{ + typedef std::map<OUString, OUString> MapString2String; + typedef std::vector<MapString2String::iterator> MapIteratorVector; + + MapString2String m_aBookmarks; // the bookmarks itself + MapIteratorVector m_aBookmarksIndexed; // for index access to the + + ::cppu::OWeakObject& m_rParent; // for the ref counting + ::comphelper::OInterfaceContainerHelper3<css::container::XContainerListener> + m_aContainerListeners; + ::osl::Mutex& m_rMutex; + +public: + /** constructs the container.<BR> + after the construction of the object the creator has to call <code>initialize</code>. + @param _rParent the parent object which is used for ref counting + @param _rMutex the parent's mutex object for access safety + */ + OBookmarkContainer( + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex + ); + + /** looks like the dtor ... + */ + virtual ~OBookmarkContainer() override; + +// css::uno::XInterface + 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::container::XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + +// css::container::XEnumerationAccess + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override; + +// css::container::XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 _nIndex ) override; + +// css::container::XNameContainer + virtual void SAL_CALL insertByName( const OUString& _rName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& _rName ) override; + +// css::container::XNameReplace + virtual void SAL_CALL replaceByName( const OUString& _rName, const css::uno::Any& aElement ) override; + +// css::container::XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + +// css::container::XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + +// css::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + +private: + /** quickly checks if there already is an element with a given name. No access to the configuration occurs, i.e. + if there is such an object which is not already loaded, it won't be loaded now. + @param _rName the object name to check + @return sal_True if there already exists such an object + */ + inline bool checkExistence(const OUString& _rName); + + void implAppend( + const OUString& _rName, + const OUString& _rDocumentLocation + ); + + void implRemove(const OUString& _rName); + + void implReplace( + const OUString& _rName, + const OUString& _rNewLink); + +}; + +inline bool OBookmarkContainer::checkExistence(const OUString& _rName) +{ + return m_aBookmarks.find(_rName) != m_aBookmarks.end(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/callablestatement.hxx b/dbaccess/source/core/inc/callablestatement.hxx new file mode 100644 index 000000000..58e21189a --- /dev/null +++ b/dbaccess/source/core/inc/callablestatement.hxx @@ -0,0 +1,80 @@ +/* -*- 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/XOutParameters.hpp> +#include "preparedstatement.hxx" + +namespace dbaccess +{ + + // OCallableStatement + + class OCallableStatement : public OPreparedStatement, + public css::sdbc::XRow, + public css::sdbc::XOutParameters + { + public: + OCallableStatement(const css::uno::Reference< css::sdbc::XConnection > & _xConn, + const css::uno::Reference< css::uno::XInterface > & _xStatement) + :OPreparedStatement(_xConn, _xStatement){} + + // 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 css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // css::sdbc::XOutParameters + virtual void SAL_CALL registerOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName ) override; + virtual void SAL_CALL registerNumericOutParameter( sal_Int32 parameterIndex, sal_Int32 sqlType, sal_Int32 scale ) 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; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/column.hxx b/dbaccess/source/core/inc/column.hxx new file mode 100644 index 000000000..aebc376b1 --- /dev/null +++ b/dbaccess/source/core/inc/column.hxx @@ -0,0 +1,217 @@ +/* -*- 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 "columnsettings.hxx" + +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <comphelper/propertycontainer.hxx> +#include <connectivity/TColumnsHelper.hxx> +#include <connectivity/sdbcx/IRefreshable.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase1.hxx> + +namespace dbaccess +{ + + + // OColumn + + typedef ::cppu::WeakComponentImplHelper< css::lang::XServiceInfo, + css::container::XNamed + > OColumnBase; + + class OColumn :public cppu::BaseMutex + ,public OColumnBase + ,public ::comphelper::OPropertyContainer + ,public IPropertyContainer // convenience for the derived class which also derive from OColumnSettings + { + friend class OColumns; + + protected: + // <properties> + OUString m_sName; + // </properties> + + protected: + OColumn( const bool _bNameIsReadOnly ); + + public: + virtual ~OColumn() 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 = 0; + + // 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::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // cppu::OComponentHelper + virtual void SAL_CALL disposing() 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; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& _rName ) override; + + protected: + // IPropertyContainer + virtual void registerProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, void* _pPointerToMember, const css::uno::Type& _rMemberType ) override; + virtual void registerMayBeVoidProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, css::uno::Any* _pPointerToMember, const css::uno::Type& _rExpectedType ) override; + }; + + // IColumnFactory - used by OColumns for creating new columns + class SAL_NO_VTABLE IColumnFactory + { + public: + /** creates an OColumn object which should represent the column with a given name + */ + virtual rtl::Reference<OColumn> + createColumn( const OUString& _rName ) const = 0; + + /** creates a column descriptor object. + + A column descriptor object is used to append new columns to the collection. If such an append + actually happened, columnAppended is called afterwards. + */ + virtual css::uno::Reference< css::beans::XPropertySet > createColumnDescriptor() = 0; + + /** notifies that a column, created from a column descriptor, has been appended + */ + virtual void columnAppended( const css::uno::Reference< css::beans::XPropertySet >& _rxSourceDescriptor ) = 0; + + /** notifies that a column with a given name has been dropped + */ + virtual void columnDropped( const OUString& _sName ) = 0; + + protected: + ~IColumnFactory() {} + }; + + class OContainerMediator; + typedef ::cppu::ImplHelper1< css::container::XChild > TXChild; + typedef connectivity::OColumnsHelper OColumns_BASE; + + class OColumns : public OColumns_BASE + ,public TXChild + { + OContainerMediator* m_pMediator; + + protected: + // comes from the driver can be null + css::uno::Reference< css::container::XNameAccess > m_xDrvColumns; + css::uno::WeakReference< css::uno::XInterface > m_xParent; + IColumnFactory* m_pColFactoryImpl; + ::connectivity::sdbcx::IRefreshableColumns* m_pRefreshColumns; + + bool m_bInitialized : 1; + bool m_bAddColumn : 1; + bool m_bDropColumn : 1; + + virtual void impl_refresh() override; + 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: + connectivity::sdbcx::ObjectType createBaseObject(const OUString& _rName) + { + return OColumns_BASE::createObject(_rName); + } + /** flag which determines whether the container is filled or not + */ + bool isInitialized() const { return m_bInitialized; } + void setInitialized() {m_bInitialized = true;} + void setMediator(OContainerMediator* _pMediator) { m_pMediator = _pMediator; } + + public: + /** constructs an empty container without configuration location. + @param rParent the parent object. This instance will be used for refcounting, so the parent + cannot die before the container does. + @param _rMutex the mutex of the parent. + @param _bCaseSensitive the initial case sensitivity flag + @see setCaseSensitive + */ + OColumns( + ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + bool _bCaseSensitive, + const std::vector< OUString>& _rVector, + IColumnFactory* _pColFactory, + ::connectivity::sdbcx::IRefreshableColumns* _pRefresh, + bool _bAddColumn = false, + bool _bDropColumn = false, + bool _bUseHardRef = true); + + 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 = false, + bool _bDropColumn = false, + bool _bUseHardRef = true); + virtual ~OColumns() override; + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override { OColumns_BASE::acquire(); } + virtual void SAL_CALL release() noexcept override { OColumns_BASE::release(); } + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) 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::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + void append(const OUString& rName, OColumn*); + void clearColumns(); + // only the name is identical to ::cppu::OComponentHelper + virtual void disposing() override; + + private: + using OColumns_BASE::setParent; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/columnsettings.hxx b/dbaccess/source/core/inc/columnsettings.hxx new file mode 100644 index 000000000..6b6c5d4c1 --- /dev/null +++ b/dbaccess/source/core/inc/columnsettings.hxx @@ -0,0 +1,88 @@ +/* -*- 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/beans/XPropertySet.hpp> + +namespace dbaccess +{ + + // TODO: move the following to comphelper/propertycontainerhelper.hxx + class IPropertyContainer + { + public: + virtual void registerProperty( + const OUString& _rName, + sal_Int32 _nHandle, + sal_Int32 _nAttributes, + void* _pPointerToMember, + const css::uno::Type& _rMemberType + ) = 0; + + virtual void registerMayBeVoidProperty( + const OUString& _rName, + sal_Int32 _nHandle, + sal_Int32 _nAttributes, + css::uno::Any* _pPointerToMember, + const css::uno::Type& _rExpectedType + ) = 0; + + protected: + ~IPropertyContainer() {} + }; + + // OColumnSettings + class OColumnSettings + { + // <properties> + css::uno::Any m_aWidth; // sal_Int32 or void + css::uno::Any m_aFormatKey; // sal_Int32 or void + css::uno::Any m_aRelativePosition; // sal_Int32 or void + css::uno::Any m_aAlignment; // sal_Int32 (css::awt::TextAlign) or void + css::uno::Any m_aHelpText; // the description of the column which is visible in the helptext of the column + css::uno::Any m_aControlDefault; // the default value which should be displayed as by a control when moving to a new row + css::uno::Reference< css::beans::XPropertySet > + m_xControlModel; + bool m_bHidden; + // </properties> + + protected: + virtual ~OColumnSettings(); + + public: + OColumnSettings(); + + protected: + void registerProperties( IPropertyContainer& _rPropertyContainer ); + + /** determines whether the property with the given handle is handled by the class + */ + static bool isColumnSettingProperty( const sal_Int32 _nPropertyHandle ); + static bool isDefaulted( const sal_Int32 _nPropertyHandle, const css::uno::Any& _rPropertyValue ); + + public: + /** check if the persistent settings have their default value + */ + static bool hasDefaultSettings( const css::uno::Reference< css::beans::XPropertySet >& _rxColumn ); + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/commandbase.hxx b/dbaccess/source/core/inc/commandbase.hxx new file mode 100644 index 000000000..1a0bd91d8 --- /dev/null +++ b/dbaccess/source/core/inc/commandbase.hxx @@ -0,0 +1,50 @@ +/* -*- 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/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + +namespace dbaccess +{ + +// OCommandBase - a base class (in fact just a container for some members) +// for classes implementing the sdb.CommandDefinition service +class OCommandBase +{ +public: // need public access +// <properties> + css::uno::Sequence< css::beans::PropertyValue> + m_aLayoutInformation; + OUString m_sCommand; + bool m_bEscapeProcessing; // no BitField! So it can be used with an OPropertyStateContainer + OUString m_sUpdateTableName; + OUString m_sUpdateSchemaName; + OUString m_sUpdateCatalogName; +// </properties> + +protected: + OCommandBase() : m_bEscapeProcessing(true) { } + +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/composertools.hxx b/dbaccess/source/core/inc/composertools.hxx new file mode 100644 index 000000000..337064757 --- /dev/null +++ b/dbaccess/source/core/inc/composertools.hxx @@ -0,0 +1,121 @@ +/* -*- 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/ustrbuf.hxx> +#include <osl/diagnose.h> + +namespace dbaccess +{ + + // TokenComposer + struct TokenComposer + { + private: + #ifdef DBG_UTIL + bool m_bUsed; + #endif + + protected: + OUStringBuffer m_aBuffer; + + public: + OUString getComposedAndClear() + { + #ifdef DBG_UTIL + m_bUsed = true; + #endif + return m_aBuffer.makeStringAndClear(); + } + + void clear() + { + #ifdef DBG_UTIL + m_bUsed = false; + #endif + m_aBuffer.setLength(0); + } + + public: + TokenComposer() + #ifdef DBG_UTIL + :m_bUsed( false ) + #endif + { + } + + virtual ~TokenComposer() + { + } + + TokenComposer(TokenComposer const &) = default; + TokenComposer(TokenComposer &&) = default; + TokenComposer & operator =(TokenComposer const &) = default; + TokenComposer & operator =(TokenComposer &&) = default; + + void operator() (const OUString& lhs) + { + append(lhs); + } + + void append( const OUString& lhs ) + { + #ifdef DBG_UTIL + OSL_ENSURE( !m_bUsed, "FilterCreator::append: already used up!" ); + #endif + if ( !lhs.isEmpty() ) + { + if ( !m_aBuffer.isEmpty() ) + appendNonEmptyToNonEmpty( lhs ); + else + m_aBuffer.append( lhs ); + } + } + + /// append the given part. Only to be called when both the part and our buffer so far are not empty + virtual void appendNonEmptyToNonEmpty( const OUString& lhs ) = 0; + }; + + // FilterCreator + struct FilterCreator : public TokenComposer + { + virtual void appendNonEmptyToNonEmpty( const OUString& lhs ) override + { + m_aBuffer.insert( 0, ' ' ); + m_aBuffer.insert( 0, '(' ); + m_aBuffer.append( " ) AND ( " ); + m_aBuffer.append( lhs ); + m_aBuffer.append( " )" ); + } + }; + + // FilterCreator + struct OrderCreator : public TokenComposer + { + virtual void appendNonEmptyToNonEmpty( const OUString& lhs ) override + { + m_aBuffer.append( ", " ); + m_aBuffer.append( lhs ); + } + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/containerapprove.hxx b/dbaccess/source/core/inc/containerapprove.hxx new file mode 100644 index 000000000..f780e224a --- /dev/null +++ b/dbaccess/source/core/inc/containerapprove.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 <rtl/ustring.hxx> + +#include <memory> + +namespace dbaccess +{ + + // IContainerApprove + /** interface for approving elements to be inserted into a container + + On the long run, one could imagine that this interface completely encapsulates + container/element approvals in all our various container classes herein (document + containers, definition containers, table containers, query containers, + command definition containers, bookmark containers). This would decrease coupling + of the respective classes. + */ + class SAL_NO_VTABLE IContainerApprove + { + public: + virtual ~IContainerApprove() {} + + /** approves a given element for insertion into the container + @param _rName + specifies the name under which the element is going to be inserted + @throws Exception + if the name or the object are invalid, or not eligible for insertion + into the container + */ + virtual void approveElement( const OUString& _rName ) = 0; + }; + + typedef std::shared_ptr< IContainerApprove > PContainerApprove; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/databasecontext.hxx b/dbaccess/source/core/inc/databasecontext.hxx new file mode 100644 index 000000000..e15585c81 --- /dev/null +++ b/dbaccess/source/core/inc/databasecontext.hxx @@ -0,0 +1,180 @@ +/* -*- 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 <config_features.h> + +#include <map> + +#include "ModelImpl.hxx" + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/sdb/XDatabaseContext.hpp> +#include <com/sun/star/sdb/XDatabaseRegistrations.hpp> +#include <com/sun/star/uno/XAggregation.hpp> + +#if HAVE_FEATURE_SCRIPTING +#include <basic/basrdll.hxx> +#endif + +#include <basic/basicmanagerrepository.hxx> +#include <cppuhelper/compbase.hxx> + +// needed for registration +namespace com::sun::star { + namespace lang + { + class XMultiServiceFactory; + class IllegalArgumentException; + } +} + +namespace dbaccess +{ +class DatabaseDocumentLoader; + +typedef ::cppu::WeakComponentImplHelper< css::lang::XServiceInfo + , css::sdb::XDatabaseContext + , css::lang::XUnoTunnel + > DatabaseAccessContext_Base; + +class ODatabaseContext :public DatabaseAccessContext_Base + ,public ::basic::BasicManagerCreationListener +{ +private: + /** loads the given object from the given URL + @throws WrappedTargetException + if an error occurs accessing the URL via the UCB + */ + css::uno::Reference< css::uno::XInterface > loadObjectFromURL(const OUString& _rName,const OUString& _sURL); + css::uno::Reference< css::uno::XInterface > getObject( const OUString& _rURL ); + + /** sets all properties which were transient at the data source. e.g. password + @param _sURL The file URL of the data source + @param _xObject The data source itself. + */ + void setTransientProperties(const OUString& _sURL, ODatabaseModelImpl& _rDataSourceModel ); + + /** creates a new data source + */ + css::uno::Reference< css::uno::XInterface > + impl_createNewDataSource(); + +#if HAVE_FEATURE_SCRIPTING + BasicDLL m_aBasicDLL; +#endif + +protected: + ::osl::Mutex m_aMutex; + css::uno::Reference< css::uno::XComponentContext > + m_aContext; + + css::uno::Reference< css::uno::XAggregation > + m_xDBRegistrationAggregate; + css::uno::Reference< css::sdb::XDatabaseRegistrations > + m_xDatabaseRegistrations; + + typedef std::map<OUString, ODatabaseModelImpl*> ObjectCache; + ObjectCache m_aDatabaseObjects; + + typedef std::map< OUString, css::uno::Sequence< css::beans::PropertyValue > > PropertyCache; + PropertyCache m_aDatasourceProperties; + // as we hold our data sources weak, we have to cache all properties on the data sources which are + // transient but stored as long as the session lasts. The database context is the session (as it lives + // as long as the session does), but the data sources may die before the session does, and then be + // recreated afterwards. So it's our (the context's) responsibility to store the session-persistent + // properties. + + ::comphelper::OInterfaceContainerHelper3<css::container::XContainerListener> m_aContainerListeners; + rtl::Reference<DatabaseDocumentLoader> m_xDatabaseDocumentLoader; + +public: + explicit ODatabaseContext( const css::uno::Reference< css::uno::XComponentContext >& ); + virtual ~ODatabaseContext() override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& _rArguments ) override; + + // 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; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // XEnumerationAccess + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XNamingService + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRegisteredObject( const OUString& Name ) override; + virtual void SAL_CALL registerObject( const OUString& Name, const css::uno::Reference< css::uno::XInterface >& Object ) override; + virtual void SAL_CALL revokeObject( const OUString& Name ) override; + + // XDatabaseRegistrations + virtual sal_Bool SAL_CALL hasRegisteredDatabase( const OUString& Name ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getRegistrationNames() override; + virtual OUString SAL_CALL getDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL registerDatabaseLocation( const OUString& Name, const OUString& Location ) override; + virtual void SAL_CALL revokeDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) override; + virtual sal_Bool SAL_CALL isDatabaseRegistrationReadOnly( const OUString& Name ) override; + virtual void SAL_CALL addDatabaseRegistrationsListener( const css::uno::Reference< css::sdb::XDatabaseRegistrationsListener >& Listener ) override; + virtual void SAL_CALL removeDatabaseRegistrationsListener( const css::uno::Reference< css::sdb::XDatabaseRegistrationsListener >& Listener ) override; + + // XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) 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(); + + void registerDatabaseDocument( ODatabaseModelImpl& _rModelImpl); + void revokeDatabaseDocument( const ODatabaseModelImpl& _rModelImpl); + void databaseDocumentURLChange(const OUString& _sOldName, const OUString& _sNewName); + void storeTransientProperties( ODatabaseModelImpl& _rModelImpl); + void appendAtTerminateListener(const ODatabaseModelImpl& _rDataSourceModel); + void removeFromTerminateListener(const ODatabaseModelImpl& _rDataSourceModel); + +private: + // BasicManagerCreationListener + virtual void onBasicManagerCreated( + const css::uno::Reference< css::frame::XModel >& _rxForDocument, + BasicManager& _rBasicManager + ) override; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/datasettings.hxx b/dbaccess/source/core/inc/datasettings.hxx new file mode 100644 index 000000000..8e24ebb71 --- /dev/null +++ b/dbaccess/source/core/inc/datasettings.hxx @@ -0,0 +1,77 @@ +/* -*- 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/awt/FontDescriptor.hpp> +#include <rtl/ustring.hxx> +#include <comphelper/propertystatecontainer.hxx> + +namespace dbaccess +{ + +// ODataSettings_Base - a base class which implements the property member +// for an object implementing the sdb::DataSettings +// service +// the properties have to be registered when used +class ODataSettings_Base +{ +public: +// <properties> + OUString m_sFilter; + OUString m_sHavingClause; + OUString m_sGroupBy; + OUString m_sOrder; + bool m_bApplyFilter; // no BitField ! the base class needs a pointer to this member ! + bool m_bAutoGrow; + css::awt::FontDescriptor m_aFont; + css::uno::Any m_aRowHeight; + css::uno::Any m_aTextColor; + css::uno::Any m_aTextLineColor; + sal_Int16 m_nFontEmphasis; + sal_Int16 m_nFontRelief; +// </properties> + +protected: + ODataSettings_Base(); + ODataSettings_Base(const ODataSettings_Base& _rSource) = delete; + ~ODataSettings_Base(); +}; +// ODataSettings - a base class which implements the property handling +// for an object implementing the sdb::DataSettings +// service + +class ODataSettings : public ::comphelper::OPropertyStateContainer + , public ODataSettings_Base +{ + bool m_bQuery; +protected: + ODataSettings(::cppu::OBroadcastHelper& _rBHelper,bool _bQuery = false); + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + /** register the properties from the param given. The parameter instance must be alive as long as its object lives. + @param _pItem + The database settings, can be <br>this</br> + */ + void registerPropertiesFor(ODataSettings_Base* _pItem); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/definitioncolumn.hxx b/dbaccess/source/core/inc/definitioncolumn.hxx new file mode 100644 index 000000000..4d7ff3e17 --- /dev/null +++ b/dbaccess/source/core/inc/definitioncolumn.hxx @@ -0,0 +1,291 @@ +/* -*- 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 "columnsettings.hxx" + +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/container/XChild.hpp> + +#include <comphelper/IdPropArrayHelper.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase1.hxx> + +namespace dbaccess +{ + + typedef ::cppu::ImplHelper1< css::container::XChild > TXChild; + // OTableColumnDescriptor + /** + * provides the properties for description. A descriptor could be used to create a new table column. + */ + class OTableColumnDescriptor : public OColumn + ,public OColumnSettings + ,public ::comphelper::OPropertyArrayUsageHelper < OTableColumnDescriptor > + ,public TXChild + { + css::uno::Reference< css::uno::XInterface > m_xParent; + const bool m_bActAsDescriptor; + + protected: + // <properties> + OUString m_aTypeName; + OUString m_aDescription; + OUString m_aDefaultValue; + OUString m_aAutoIncrementValue; + sal_Int32 m_nType; + sal_Int32 m_nPrecision; + sal_Int32 m_nScale; + sal_Int32 m_nIsNullable; + bool m_bAutoIncrement; + bool m_bRowVersion; + bool m_bCurrency; + // </properties> + + public: + OTableColumnDescriptor( const bool _bActAsDescriptor ) + :OColumn( !_bActAsDescriptor ) + ,m_bActAsDescriptor( _bActAsDescriptor ) + ,m_nType( css::sdbc::DataType::SQLNULL ) + ,m_nPrecision( 0 ) + ,m_nScale( 0 ) + ,m_nIsNullable( css::sdbc::ColumnValue::NULLABLE_UNKNOWN ) + ,m_bAutoIncrement( false ) + ,m_bRowVersion( false ) + ,m_bCurrency( false ) + { + impl_registerProperties(); + } + + DECLARE_XINTERFACE( ) + + // 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; + + // css::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // ::comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + + // ::cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override; + + private: + void impl_registerProperties(); + }; + + // OTableColumn + class OTableColumn; + typedef ::comphelper::OPropertyArrayUsageHelper < OTableColumn > OTableColumn_PBase; + /** describes a column of a table + */ + class OTableColumn :public OTableColumnDescriptor + ,public OTableColumn_PBase + { + protected: + virtual ~OTableColumn() override; + + public: + OTableColumn(const OUString& _rName); + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + }; + + // OQueryColumn + class OQueryColumn; + typedef ::comphelper::OPropertyArrayUsageHelper< OQueryColumn > OQueryColumn_PBase; + /** a column of a Query, with additional information obtained from parsing the query statement + */ + class OQueryColumn :public OTableColumnDescriptor + ,public OQueryColumn_PBase + { + // <properties> + OUString m_sCatalogName; + OUString m_sSchemaName; + OUString m_sTableName; + OUString m_sRealName; + OUString m_sLabel; + // </properties> + + css::uno::Reference< css::beans::XPropertySet > m_xOriginalTableColumn; + + protected: + virtual ~OQueryColumn() override; + + public: + OQueryColumn( + const css::uno::Reference< css::beans::XPropertySet>& _rxParserColumn, + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, + const OUString &i_sLabel + ); + + // XTypeProvider + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + + // *Property* + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + + private: + css::uno::Reference< css::beans::XPropertySet > + impl_determineOriginalTableColumn( + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection + ); + + using ::cppu::OPropertySetHelper::getFastPropertyValue; + }; + + // OColumnWrapper + /** + * describes all properties for a columns of a table. Only the view parts are provided + * directly, all the other parts are derived from a driver implementation + */ + class OColumnWrapper :public OColumn + { + protected: + // definition which is provided by a driver! + css::uno::Reference< css::beans::XPropertySet > + m_xAggregate; + + sal_Int32 m_nColTypeID; + + protected: + OColumnWrapper( const css::uno::Reference< css::beans::XPropertySet >& _rCol, const bool _bNameIsReadOnly ); + virtual ~OColumnWrapper() override; + + public: + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle + ) const 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; + + protected: + OUString impl_getPropertyNameFromHandle( const sal_Int32 _nHandle ) const; + + protected: + using OColumn::getFastPropertyValue; + }; + + // OTableColumnDescriptorWrapper + /** + * provides the properties for description. A descriptor could be used to create a new table column. + */ + class OTableColumnDescriptorWrapper :public OColumnWrapper + ,public OColumnSettings + ,public ::comphelper::OIdPropertyArrayUsageHelper < OTableColumnDescriptorWrapper > + { + const bool m_bPureWrap; + const bool m_bIsDescriptor; + + public: + OTableColumnDescriptorWrapper(const css::uno::Reference< css::beans::XPropertySet >& rCol, + const bool _bPureWrap, const bool _bIsDescriptor ); + + // 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; + + // OIdPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper(sal_Int32 nId) 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; + 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; + + protected: + using OColumnWrapper::getFastPropertyValue; + }; + + // OTableColumnWrapper + /** + * describes all properties for a columns of a table. Only the view parts are provided + * directly, all the other parts are derived from a driver implementation + */ + class OTableColumnWrapper :public OTableColumnDescriptorWrapper + ,public ::comphelper::OIdPropertyArrayUsageHelper < OTableColumnWrapper > + { + protected: + virtual ~OTableColumnWrapper() override; + + public: + OTableColumnWrapper( const css::uno::Reference< css::beans::XPropertySet >& rCol, + const css::uno::Reference< css::beans::XPropertySet >& rColDefinition, + const bool _bPureWrap ); + + // 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; + + // OIdPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual ::cppu::IPropertyArrayHelper* createArrayHelper(sal_Int32 nId) const override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/definitioncontainer.hxx b/dbaccess/source/core/inc/definitioncontainer.hxx new file mode 100644 index 000000000..9c871601a --- /dev/null +++ b/dbaccess/source/core/inc/definitioncontainer.hxx @@ -0,0 +1,323 @@ +/* -*- 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 <vector> + +#include <cppuhelper/implbase7.hxx> +#include <osl/mutex.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/beans/XVetoableChangeListener.hpp> +#include <com/sun/star/container/XContainerApproveBroadcaster.hpp> +#include "ContentHelper.hxx" +#include "containerapprove.hxx" +#include <comphelper/uno3.hxx> +#include <comphelper/interfacecontainer2.hxx> + +namespace dbaccess +{ + +class ODefinitionContainer_Impl : public OContentHelper_Impl +{ +public: + typedef std::map< OUString, TContentPtr > NamedDefinitions; + typedef NamedDefinitions::iterator iterator; + typedef NamedDefinitions::const_iterator const_iterator; + +private: + NamedDefinitions m_aDefinitions; + +public: + size_t size() const { return m_aDefinitions.size(); } + + const_iterator begin() const { return m_aDefinitions.begin(); } + const_iterator end() const { return m_aDefinitions.end(); } + + const_iterator find( const OUString& _rName ) const { return m_aDefinitions.find( _rName ); } + const_iterator find( const TContentPtr& _pDefinition ) const; + + void erase( const OUString& _rName ) { m_aDefinitions.erase( _rName ); } + void erase( const TContentPtr& _pDefinition ); + + void insert( const OUString& _rName, TContentPtr _pDefinition ) + { + m_aDefinitions.emplace( _rName, _pDefinition ); + } + +private: + iterator find( const TContentPtr& _pDefinition ); + // (for the moment, this is private. Make it public if needed. If really needed.) +}; + +// ODefinitionContainer - base class of collections of database definition +// documents +typedef ::cppu::ImplHelper7 < css::container::XIndexAccess + , css::container::XNameContainer + , css::container::XEnumerationAccess + , css::container::XContainer + , css::container::XContainerApproveBroadcaster + , css::beans::XPropertyChangeListener + , css::beans::XVetoableChangeListener + > ODefinitionContainer_Base; + +class ODefinitionContainer + :public OContentHelper + ,public ODefinitionContainer_Base +{ +protected: + typedef std::map< OUString, css::uno::WeakReference< css::ucb::XContent > > Documents; + + enum ContainerOperation + { + E_REPLACED, + E_REMOVED, + E_INSERTED + }; + + enum ListenerType + { + ApproveListeners, + ContainerListemers + }; + +private: + PContainerApprove m_pElementApproval; + +protected: + // we can't just hold a vector of XContentRefs, as after initialization they're all empty + // cause we load them only on access + std::vector<Documents::iterator> + m_aDocuments; // for an efficient index access + Documents m_aDocumentMap; // for an efficient name access + + ::comphelper::OInterfaceContainerHelper2 + m_aApproveListeners; + ::comphelper::OInterfaceContainerHelper2 + m_aContainerListeners; + + bool m_bInPropertyChange; + bool m_bCheckSlash; + +protected: + /** Additionally to our own approvals which new elements must pass, derived classes + can specify an additional approval instance here. + + Every time a new element is inserted into the container (or an element is replaced + with a new one), this new element must pass our own internal approval, plus the approval + given here. + */ + void setElementApproval( PContainerApprove _pElementApproval ) { m_pElementApproval = _pElementApproval; } + const PContainerApprove& getElementApproval() const { return m_pElementApproval; } + +protected: + virtual ~ODefinitionContainer() override; + + const ODefinitionContainer_Impl& getDefinitions() const + { + return dynamic_cast< const ODefinitionContainer_Impl& >( *m_pImpl ); + } + + ODefinitionContainer_Impl& getDefinitions() + { + return dynamic_cast< ODefinitionContainer_Impl& >( *m_pImpl ); + } +public: + /** constructs the container. + */ + ODefinitionContainer( + const css::uno::Reference< css::uno::XComponentContext >& _xORB + , const css::uno::Reference< css::uno::XInterface >& _xParentContainer + , const TContentPtr& _pImpl + , bool _bCheckSlash = true + ); + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + + virtual css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + 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; + +// css::container::XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + +// css::container::XEnumerationAccess + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override; + +// css::container::XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 _nIndex ) override; + +// css::container::XNameContainer + virtual void SAL_CALL insertByName( const OUString& _rName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& _rName ) override; + +// css::container::XNameReplace + virtual void SAL_CALL replaceByName( const OUString& _rName, const css::uno::Any& aElement ) override; + +// css::container::XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + +// css::container::XContainer + virtual void SAL_CALL addContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + virtual void SAL_CALL removeContainerListener( const css::uno::Reference< css::container::XContainerListener >& xListener ) override; + + // XContainerApproveBroadcaster + virtual void SAL_CALL addContainerApproveListener( const css::uno::Reference< css::container::XContainerApproveListener >& Listener ) override; + virtual void SAL_CALL removeContainerApproveListener( const css::uno::Reference< css::container::XContainerApproveListener >& Listener ) override; + +// css::lang::XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + // XVetoableChangeListener + virtual void SAL_CALL vetoableChange( const css::beans::PropertyChangeEvent& aEvent ) override; + +protected: + // helper + virtual void SAL_CALL disposing() override; + + /** create an object from its persistent data within the configuration. To be overwritten by derived classes. + @param _rName the name the object has within the container + @return the newly created object or an empty reference if something went wrong + */ + virtual css::uno::Reference< css::ucb::XContent > createObject( + const OUString& _rName) = 0; + + /** get the object specified by the given name. If desired, the object will be read if not already done so.<BR> + @param _rName the object name + @param _bReadIfNecessary if sal_True, the object will be created if necessary + @return the property set interface of the object. Usually the return value is not NULL, but + if so, then the object could not be read from the configuration + @throws NoSuchElementException if there is no object with the given name. + @see createObject + */ + css::uno::Reference< css::ucb::XContent > + implGetByName(const OUString& _rName, bool _bCreateIfNecessary); + + /** quickly checks if there already is an element with a given name. No access to the configuration occurs, i.e. + if there is such an object which is not already loaded, it won't be loaded now. + @param _rName the object name to check + @return sal_True if there already exists such an object + */ + virtual bool checkExistence(const OUString& _rName); + + /** append a new object to the container. No plausibility checks are done, e.g. if the object is non-NULL or + if the name is already used by another object or anything like this. This method is for derived classes + which may support different methods to create and/or append objects, and don't want to deal with the + internal structures of this class.<BR> + The old component will not be disposed, this is the callers responsibility, too. + @param _rName the name of the new object + @param _rxNewObject the new object (not surprising, is it ?) + @see createConfigKey + @see implReplace + @see implRemove + */ + void implAppend( + const OUString& _rName, + const css::uno::Reference< css::ucb::XContent >& _rxNewObject + ); + + /** remove all references to an object from the container. No plausibility checks are done, e.g. whether + or not there exists an object with the given name. This is the responsibility of the caller.<BR> + Additionally the node for the given object will be removed from the registry (including all sub nodes).<BR> + The old component will not be disposed, this is the callers responsibility, too. + @param _rName the objects name + @see implReplace + @see implAppend + */ + void implRemove(const OUString& _rName); + + /** remove an object in the container. No plausibility checks are done, e.g. whether + or not there exists an object with the given name or the object is non-NULL. This is the responsibility of the caller.<BR> + Additionally all object-related information within the registry will be deleted. The new object config node, + where the caller may want to store the new objects information, is returned.<BR> + The old component will not be disposed, this is the callers responsibility, too. + @param _rName the objects name + @param _rxNewObject the new object + @param _rNewObjectNode the configuration node where the new object may be stored + @see implAppend + @see implRemove + */ + void implReplace( + const OUString& _rName, + const css::uno::Reference< css::ucb::XContent >& _rxNewObject + ); + + /** notifies our container/approve listeners + */ + void notifyByName( + ::osl::ResettableMutexGuard& _rGuard, + const OUString& _rName, + const css::uno::Reference< css::ucb::XContent >& _xNewElement, + const css::uno::Reference< css::ucb::XContent >& xOldElement, + ContainerOperation _eOperation, + ListenerType _eType + ); + + operator css::uno::Reference< css::uno::XInterface > () const + { + return const_cast< XContainer* >( static_cast< const XContainer* >( this ) ); + } + +private: + void addObjectListener(const css::uno::Reference< css::ucb::XContent >& _xNewObject); + void removeObjectListener(const css::uno::Reference< css::ucb::XContent >& _xNewObject); + + /** approve that the object given may be inserted into the container. + Should be overridden by derived classes, + the default implementation just checks the object to be non-void. + + @throws IllegalArgumentException + if the name or the object are invalid + @throws ElementExistException + if the object already exists in the container, or another object with the same name + already exists + @throws WrappedTargetException + if another error occurs which prevents insertion of the object into the container + */ + void approveNewObject( + const OUString& _sName, + const css::uno::Reference< css::ucb::XContent >& _rxObject + ) const; + + bool impl_haveAnyListeners_nothrow() const + { + return ( m_aContainerListeners.getLength() > 0 ) || ( m_aApproveListeners.getLength() > 0 ); + } +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/documentevents.hxx b/dbaccess/source/core/inc/documentevents.hxx new file mode 100644 index 000000000..e7f7ef9ff --- /dev/null +++ b/dbaccess/source/core/inc/documentevents.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 <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <memory> +#include <map> + +namespace dbaccess +{ + + typedef std::map< OUString, css::uno::Sequence< css::beans::PropertyValue > > + DocumentEventsData; + + // DocumentEvents + struct DocumentEvents_Data; + + typedef ::cppu::WeakImplHelper< css::container::XNameReplace + > DocumentEvents_Base; + + class DocumentEvents :public DocumentEvents_Base + { + public: + DocumentEvents( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, DocumentEventsData& _rEventsData ); + virtual ~DocumentEvents() override; + + DocumentEvents(const DocumentEvents&) = delete; + const DocumentEvents& operator=(const DocumentEvents&) = delete; + + static bool needsSynchronousNotification( std::u16string_view _rEventName ); + + // XInterface + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + private: + std::unique_ptr< DocumentEvents_Data > m_pData; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/migrwarndlg.hxx b/dbaccess/source/core/inc/migrwarndlg.hxx new file mode 100644 index 000000000..24bacf300 --- /dev/null +++ b/dbaccess/source/core/inc/migrwarndlg.hxx @@ -0,0 +1,24 @@ +/* -*- 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/. + */ +#pragma once + +#include <vcl/weld.hxx> + +namespace dbaccess +{ +class MigrationWarnDialog : public weld::MessageDialogController +{ + std::unique_ptr<weld::Button> m_xLater; + +public: + MigrationWarnDialog(weld::Window* pParent); +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/object.hxx b/dbaccess/source/core/inc/object.hxx new file mode 100644 index 000000000..a947f9c57 --- /dev/null +++ b/dbaccess/source/core/inc/object.hxx @@ -0,0 +1,30 @@ +/* -*- 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 + +enum ObjectType +{ + dbaTable, + dbaQuery, + dbaForm, + dbaReport +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/objectnameapproval.hxx b/dbaccess/source/core/inc/objectnameapproval.hxx new file mode 100644 index 000000000..ddd538c79 --- /dev/null +++ b/dbaccess/source/core/inc/objectnameapproval.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 <memory> +#include "containerapprove.hxx" + +#include <com/sun/star/sdbc/XConnection.hpp> + +namespace dbaccess +{ + + // ObjectNameApproval + struct ObjectNameApproval_Impl; + /** implementation of the IContainerApprove interface which approves + elements for insertion into a query or tables container. + + The only check done by this instance is whether the query name is + not already used, taking into account that in some databases, queries + and tables share the same namespace. + + The class is not thread-safe. + */ + class ObjectNameApproval : public IContainerApprove + { + std::unique_ptr< ObjectNameApproval_Impl > m_pImpl; + + public: + enum ObjectType + { + TypeQuery, + TypeTable + }; + + public: + /** constructs the instance + + @param _rxConnection + the connection relative to which the names should be checked. This connection + will be held weak. In case it is closed, subsequent calls to this instance's + methods throw a DisposedException. + @param _eType + specifies which type of objects is to be approved with this instance + */ + ObjectNameApproval( + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, + ObjectType _eType + ); + virtual ~ObjectNameApproval() override; + + // IContainerApprove + virtual void approveElement( const OUString& _rName ) override; + + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/preparedstatement.hxx b/dbaccess/source/core/inc/preparedstatement.hxx new file mode 100644 index 000000000..cea06d79a --- /dev/null +++ b/dbaccess/source/core/inc/preparedstatement.hxx @@ -0,0 +1,105 @@ +/* -*- 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/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/XParameters.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include "statement.hxx" +#include "column.hxx" + +namespace dbaccess +{ + + // OPreparedStatement + + class OPreparedStatement : public OStatementBase, + public css::sdbc::XPreparedStatement, + public css::sdbc::XParameters, + public css::sdbc::XResultSetMetaDataSupplier, + public css::sdbcx::XColumnsSupplier, + public css::lang::XServiceInfo + { + std::unique_ptr<OColumns> m_pColumns; + css::uno::Reference< css::sdbc::XParameters > m_xAggregateAsParameters; + + public: + OPreparedStatement(const css::uno::Reference< css::sdbc::XConnection > & _xConn, + const css::uno::Reference< css::uno::XInterface > & _xStatement); + virtual ~OPreparedStatement() 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; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // css::sdbc::XPreparedStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( ) override; + virtual sal_Int32 SAL_CALL executeUpdate( ) override; + virtual sal_Bool SAL_CALL execute( ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + + // css::sdbcx::XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + + // css::sdbc::XResultSetMetaDataSupplier + virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL getMetaData( ) override; + + // css::sdbc::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; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/querycomposer.hxx b/dbaccess/source/core/inc/querycomposer.hxx new file mode 100644 index 000000000..c5d764d98 --- /dev/null +++ b/dbaccess/source/core/inc/querycomposer.hxx @@ -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 . + */ +#pragma once + +#include <com/sun/star/sdb/XSQLQueryComposer.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase5.hxx> +#include <cppuhelper/basemutex.hxx> +#include <connectivity/sqliterator.hxx> +#include <apitools.hxx> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> + + +namespace dbaccess +{ + typedef ::cppu::ImplHelper5< css::sdb::XSQLQueryComposer, + css::sdb::XParametersSupplier, + css::sdbcx::XTablesSupplier, + css::sdbcx::XColumnsSupplier, + css::lang::XServiceInfo > OQueryComposer_BASE; + + class OQueryComposer : public ::cppu::BaseMutex, + public OSubComponent, + public OQueryComposer_BASE + { + std::vector< OUString> m_aFilters; + std::vector< OUString> m_aOrders; + OUString m_sOrgFilter; + OUString m_sOrgOrder; + css::uno::Reference< css::sdb::XSingleSelectQueryComposer> m_xComposer; + css::uno::Reference< css::sdb::XSingleSelectQueryComposer> m_xComposerHelper; + + protected: + virtual void SAL_CALL disposing() override; + virtual ~OQueryComposer() override; + public: + + OQueryComposer( const css::uno::Reference< css::sdbc::XConnection>& _xConnection ); + + // 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; + // 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; + // XSQLQueryComposer + virtual OUString SAL_CALL getQuery( ) override; + virtual void SAL_CALL setQuery( const OUString& command ) override; + virtual OUString SAL_CALL getComposedQuery( ) override; + virtual OUString SAL_CALL getFilter( ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getStructuredFilter( ) override; + virtual OUString SAL_CALL getOrder( ) override; + virtual void SAL_CALL appendFilterByColumn( const css::uno::Reference< css::beans::XPropertySet >& column ) override; + virtual void SAL_CALL appendOrderByColumn( const css::uno::Reference< css::beans::XPropertySet >& column, sal_Bool ascending ) override; + virtual void SAL_CALL setFilter( const OUString& filter ) override; + virtual void SAL_CALL setOrder( const OUString& order ) override; + // XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + // XParametersSupplier + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getParameters( ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/querycontainer.hxx b/dbaccess/source/core/inc/querycontainer.hxx new file mode 100644 index 000000000..a04a1ddf3 --- /dev/null +++ b/dbaccess/source/core/inc/querycontainer.hxx @@ -0,0 +1,165 @@ +/* -*- 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/implbase5.hxx> +#include <connectivity/CommonTools.hxx> +#include <rtl/ref.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/container/XContainerApproveListener.hpp> + +#include "definitioncontainer.hxx" + +namespace dbtools +{ + class WarningsContainer; +} + +namespace dbaccess +{ + + typedef ::cppu::ImplHelper5 < css::container::XContainerListener + , css::container::XContainerApproveListener + , css::sdbcx::XDataDescriptorFactory + , css::sdbcx::XAppend + , css::sdbcx::XDrop + > OQueryContainer_Base; + + // OQueryContainer + class OQueryContainer : public ODefinitionContainer + , public OQueryContainer_Base + { + private: + ::dbtools::WarningsContainer* m_pWarnings; + css::uno::Reference< css::container::XNameContainer > + m_xCommandDefinitions; + css::uno::Reference< css::sdbc::XConnection > + m_xConnection; + // possible actions on our "aggregate" + enum class AggregateAction { NONE, Inserting }; + 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 + { + OQueryContainer& m_rActor; + public: + OAutoActionReset(OQueryContainer& _rActor) : m_rActor(_rActor) { } + ~OAutoActionReset() { m_rActor.m_eDoingCurrently = AggregateAction::NONE; } + }; + + // ODefinitionContainer + virtual css::uno::Reference< css::ucb::XContent > createObject( const OUString& _rName) override; + virtual bool checkExistence(const OUString& _rName) override; + + // helper + virtual void SAL_CALL disposing() override; + virtual ~OQueryContainer() override; + + /** ctor of the container. The parent has to support the <type scope="css::sdbc">XConnection</type> + interface.<BR> + + @param _pWarnings + specifies a warnings container (May be <NULL/>) + + Any errors which occur during the lifetime of the query container, + which cannot be reported as exceptions (for instance in methods where throwing an SQLException is + not allowed) will be appended to this container.</p> + <p>The caller is responsible for ensuring the lifetime of the object pointed to by this parameter. + */ + OQueryContainer( + const css::uno::Reference< css::container::XNameContainer >& _rxCommandDefinitions, + const css::uno::Reference< css::sdbc::XConnection >& _rxConn, + const css::uno::Reference< css::uno::XComponentContext >& _rxORB, + ::dbtools::WarningsContainer* _pWarnings + ); + + void init(); + + public: + static rtl::Reference<OQueryContainer> create( + const css::uno::Reference< css::container::XNameContainer >& _rxCommandDefinitions, + const css::uno::Reference< css::sdbc::XConnection >& _rxConn, + const css::uno::Reference< css::uno::XComponentContext >& _rxORB, + ::dbtools::WarningsContainer* _pWarnings + ); + + DECLARE_XINTERFACE( ) + DECLARE_XTYPEPROVIDER( ) + DECLARE_SERVICE_INFO(); + + // css::container::XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // XContainerApproveListener + virtual css::uno::Reference< css::util::XVeto > SAL_CALL approveInsertElement( const css::container::ContainerEvent& Event ) override; + virtual css::uno::Reference< css::util::XVeto > SAL_CALL approveReplaceElement( const css::container::ContainerEvent& Event ) override; + virtual css::uno::Reference< css::util::XVeto > SAL_CALL approveRemoveElement( const css::container::ContainerEvent& Event ) override; + + // css::lang::XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // css::sdbcx::XDataDescriptorFactory + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL createDataDescriptor( ) override; + + // css::sdbcx::XAppend + virtual void SAL_CALL appendByDescriptor( const css::uno::Reference< css::beans::XPropertySet >& descriptor ) override; + + // css::sdbcx::XDrop + virtual void SAL_CALL dropByName( const OUString& elementName ) override; + virtual void SAL_CALL dropByIndex( sal_Int32 index ) override; + + // css::container::XElementAccess + virtual sal_Bool SAL_CALL hasElements( ) override; + // css::container::XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + // css::container::XNameAccess + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + + private: + // OContentHelper overridables + virtual OUString determineContentType() const override; + + // helper + /** create a query object wrapping a CommandDefinition given by name. To retrieve the object, the CommandDescription + container will be asked for the given name.<BR> + The returned object is acquired once. + */ + css::uno::Reference< css::ucb::XContent > implCreateWrapper(const OUString& _rName); + /// create a query object wrapping a CommandDefinition. The returned object is acquired once. + css::uno::Reference< css::ucb::XContent > implCreateWrapper(const css::uno::Reference< css::ucb::XContent >& _rxCommandDesc); + + }; +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/recovery/dbdocrecovery.hxx b/dbaccess/source/core/inc/recovery/dbdocrecovery.hxx new file mode 100644 index 000000000..e5f924650 --- /dev/null +++ b/dbaccess/source/core/inc/recovery/dbdocrecovery.hxx @@ -0,0 +1,71 @@ +/* -*- 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/embed/XStorage.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <vector> +#include <memory> + +namespace dbaccess +{ + + // DatabaseDocumentRecovery + struct DatabaseDocumentRecovery_Data; + class DatabaseDocumentRecovery + { + public: + DatabaseDocumentRecovery( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext + ); + ~DatabaseDocumentRecovery(); + + /** saves the modified sub components of the given controller(s) to the "recovery" sub storage of the document + storage. + + @throws css::uno::Exception + in case of an error. + */ + void saveModifiedSubComponents( + const css::uno::Reference< css::embed::XStorage >& i_rTargetStorage, + const std::vector< css::uno::Reference< css::frame::XController > >& i_rControllers + ); + + /** recovery sub components from the given document storage, if applicable + + If the given document storage does not contain a recovery folder, the method silently returns. + + @throws css::uno::Exception + in case of an error. + */ + void recoverSubDocuments( + const css::uno::Reference< css::embed::XStorage >& i_rDocumentStorage, + const css::uno::Reference< css::frame::XController >& i_rTargetController + ); + + private: + const std::unique_ptr< DatabaseDocumentRecovery_Data > m_pData; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/sdbcoretools.hxx b/dbaccess/source/core/inc/sdbcoretools.hxx new file mode 100644 index 000000000..5b100dcf1 --- /dev/null +++ b/dbaccess/source/core/inc/sdbcoretools.hxx @@ -0,0 +1,55 @@ +/* -*- 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/embed/XStorage.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace dbaccess +{ + + void notifyDataSourceModified(const css::uno::Reference< css::uno::XInterface >& _rxObject); + + css::uno::Reference< css::uno::XInterface > + getDataSource( const css::uno::Reference< css::uno::XInterface >& _rxDependentObject ); + + /** retrieves a to-be-displayed string for a given caught exception; + */ + OUString extractExceptionMessage( const css::uno::Reference< css::uno::XComponentContext >& _rContext, const css::uno::Any& _rError ); + + namespace tools + { + namespace stor + { + bool storageIsWritable_nothrow( + const css::uno::Reference< css::embed::XStorage >& _rxStorage + ); + + /// commits a given storage if it's not readonly + bool commitStorageIfWriteable( + const css::uno::Reference< css::embed::XStorage >& _rxStorage + ); + } + + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/statement.hxx b/dbaccess/source/core/inc/statement.hxx new file mode 100644 index 000000000..f43704068 --- /dev/null +++ b/dbaccess/source/core/inc/statement.hxx @@ -0,0 +1,179 @@ +/* -*- 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/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XStatement.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/sdbc/XMultipleResults.hpp> +#include <com/sun/star/sdbc/XPreparedBatchExecution.hpp> +#include <com/sun/star/sdbc/XBatchExecution.hpp> +#include <com/sun/star/sdbc/XGeneratedResultSet.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <cppuhelper/propshlp.hxx> +#include <comphelper/proparrhlp.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase3.hxx> +#include <apitools.hxx> + + +// OStatementBase + +class OStatementBase : public cppu::BaseMutex, + public OSubComponent, + public ::cppu::OPropertySetHelper, + public ::comphelper::OPropertyArrayUsageHelper < OStatementBase >, + public css::util::XCancellable, + public css::sdbc::XWarningsSupplier, + public css::sdbc::XPreparedBatchExecution, + public css::sdbc::XMultipleResults, + public css::sdbc::XCloseable, + public css::sdbc::XGeneratedResultSet +{ +protected: + ::osl::Mutex m_aCancelMutex; + + css::uno::WeakReferenceHelper m_aResultSet; + css::uno::Reference< css::beans::XPropertySet > m_xAggregateAsSet; + css::uno::Reference< css::util::XCancellable > m_xAggregateAsCancellable; + bool m_bUseBookmarks; + bool m_bEscapeProcessing; + + virtual ~OStatementBase() override; + +public: + OStatementBase(const css::uno::Reference< css::sdbc::XConnection > & _xConn, + const css::uno::Reference< css::uno::XInterface > & _xStatement); + + +// 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; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + +// OComponentHelper + virtual void SAL_CALL disposing() 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::util::XCancellable + virtual void SAL_CALL cancel( ) override; + +// css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + +// css::sdbc::XMultipleResults + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getResultSet( ) override; + virtual sal_Int32 SAL_CALL getUpdateCount( ) override; + virtual sal_Bool SAL_CALL getMoreResults( ) override; + +// css::sdbc::XPreparedBatchExecution + virtual void SAL_CALL addBatch( ) override; + virtual void SAL_CALL clearBatch( ) override; + virtual css::uno::Sequence< sal_Int32 > SAL_CALL executeBatch( ) override; +// css::sdbc::XGeneratedResultSet + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL getGeneratedValues( ) override; + +// Helper + void disposeResultSet(); + +protected: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + + +// OStatement + +typedef ::cppu::ImplHelper3 < css::sdbc::XStatement + , css::lang::XServiceInfo + , css::sdbc::XBatchExecution + > OStatement_IFACE; +class OStatement :public OStatementBase + ,public OStatement_IFACE +{ +private: + css::uno::Reference< css::sdbc::XStatement > m_xAggregateStatement; + css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xComposer; + bool m_bAttemptedComposerCreation; + +public: + OStatement(const css::uno::Reference< css::sdbc::XConnection > & _xConn, + const css::uno::Reference< css::uno::XInterface > & _xStatement); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + +// 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::sdbc::XStatement + virtual css::uno::Reference< css::sdbc::XResultSet > SAL_CALL executeQuery( const OUString& sql ) override; + virtual sal_Int32 SAL_CALL executeUpdate( const OUString& sql ) override; + virtual sal_Bool SAL_CALL execute( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( ) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + + // XBatchExecution + virtual void SAL_CALL addBatch( const OUString& sql ) override; + virtual void SAL_CALL clearBatch( ) override; + virtual css::uno::Sequence< sal_Int32 > SAL_CALL executeBatch( ) override; + + using OStatementBase::addBatch; + +private: + /** does escape processing for the given SQL command, if the our EscapeProcessing + property allows so. + */ + OUString impl_doEscapeProcessing_nothrow( const OUString& _rSQL ) const; + bool impl_ensureComposer_nothrow() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/table.hxx b/dbaccess/source/core/inc/table.hxx new file mode 100644 index 000000000..8c29bff44 --- /dev/null +++ b/dbaccess/source/core/inc/table.hxx @@ -0,0 +1,140 @@ +/* -*- 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/XConnection.hpp> + +#include "datasettings.hxx" +#include "column.hxx" +#include <connectivity/CommonTools.hxx> +#include <connectivity/TTableHelper.hxx> +#include <comphelper/IdPropArrayHelper.hxx> + +namespace dbaccess +{ + + // OTables + class ODBTable; + class OContainerMediator; + typedef ::comphelper::OIdPropertyArrayUsageHelper< ODBTable > ODBTable_PROP; + typedef ::connectivity::OTableHelper OTable_Base; + + class ODBTable :public ODataSettings_Base + ,public ODBTable_PROP + ,public OTable_Base + ,public IColumnFactory + { + private: + ::rtl::Reference< OContainerMediator > m_pColumnMediator; + + protected: + css::uno::Reference< css::container::XNameAccess > m_xColumnDefinitions; + css::uno::Reference< css::container::XNameAccess > m_xDriverColumns; + + // <properties> + sal_Int32 m_nPrivileges; + // </properties> + + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( sal_Int32 _nId) const override; + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + // 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; + + /** creates the column collection for the table + @param _rNames + The column names. + */ + virtual ::connectivity::sdbcx::OCollection* createColumns(const ::std::vector< OUString>& _rNames) override; + + /** creates the key collection for the table + @param _rNames + The key names. + */ + virtual ::connectivity::sdbcx::OCollection* createKeys(const ::std::vector< OUString>& _rNames) override; + + /** creates the index collection for the table + @param _rNames + The index names. + */ + virtual ::connectivity::sdbcx::OCollection* createIndexes(const ::std::vector< OUString>& _rNames) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + public: + /** constructs a wrapper supporting the com.sun.star.sdb.Table service.<BR> + @param _rxConn the connection the table belongs to + @param _rxTable the table from the driver can be null + @param _rCatalog the name of the catalog the table belongs to. May be empty. + @param _rSchema the name of the schema the table belongs to. May be empty. + @param _rName the name of the table + @param _rType the type of the table, as supplied by the driver + @param _rDesc the description of the table, as supplied by the driver + @throws css::sdbc::SQLException + */ + ODBTable(connectivity::sdbcx::OCollection* _pTables + ,const css::uno::Reference< css::sdbc::XConnection >& _rxConn + ,const OUString& _rCatalog + , const OUString& _rSchema + , const OUString& _rName + ,const OUString& _rType + , const OUString& _rDesc + ,const css::uno::Reference< css::container::XNameAccess >& _rxColumnDefinitions); + + /// @throws css::sdbc::SQLException + ODBTable(connectivity::sdbcx::OCollection* _pTables + ,const css::uno::Reference< css::sdbc::XConnection >& _rxConn); + virtual ~ODBTable() override; + + // ODescriptor + virtual void construct() override; + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + + // css::beans::XPropertySet + virtual void SAL_CALL getFastPropertyValue(css::uno::Any& rValue, sal_Int32 nHandle) const override; + + // css::sdbcx::XRename, + virtual void SAL_CALL rename( const OUString& _rNewName ) override; + + // css::sdbcx::XAlterTable, + virtual void SAL_CALL alterColumnByName( const OUString& _rName, const css::uno::Reference< css::beans::XPropertySet >& _rxDescriptor ) override; + + // css::lang::XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + private: + using OTable_Base::createArrayHelper; + using OTable_Base::getFastPropertyValue; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/tablecontainer.hxx b/dbaccess/source/core/inc/tablecontainer.hxx new file mode 100644 index 000000000..44358e4e2 --- /dev/null +++ b/dbaccess/source/core/inc/tablecontainer.hxx @@ -0,0 +1,96 @@ +/* -*- 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 <cppuhelper/implbase1.hxx> +#include <rtl/ref.hxx> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include "FilteredContainer.hxx" +#include "RefreshListener.hxx" + +namespace dbaccess +{ + // OTableContainer + class OContainerMediator; + + class OTableContainer : public OFilteredContainer, + public ::cppu::ImplHelper1< css::container::XContainerListener> + { + css::uno::Reference< css::container::XNameContainer > m_xTableDefinitions; + ::rtl::Reference< OContainerMediator > m_pTableMediator; + + // OFilteredContainer + virtual void addMasterContainerListener() override; + virtual void removeMasterContainerListener() override; + virtual OUString getTableTypeRestriction() const override; + + // ::connectivity::sdbcx::OCollection + 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; + + virtual void disposing() override; + + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + public: + virtual void SAL_CALL acquire() noexcept override { OFilteredContainer::acquire();} + virtual void SAL_CALL release() noexcept override { OFilteredContainer::release();} + + /** ctor of the container. The parent has to support the <type scope="css::sdbc">XConnection</type> + interface.<BR> + @param _rParent the object which acts as parent for the container. + all refcounting is rerouted to this object + @param _rMutex the access safety object of the parent + @param _rTableFilter restricts the visible tables by name + @param _rTableTypeFilter restricts the visible tables by type + @see construct + */ + OTableContainer( ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const css::uno::Reference< css::sdbc::XConnection >& _xCon, + bool _bCase, + const css::uno::Reference< css::container::XNameContainer >& _xTableDefinitions, + IRefreshListener* _pRefreshListener, + std::atomic<std::size_t>& _nInAppend + ); + + virtual ~OTableContainer() override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/veto.hxx b/dbaccess/source/core/inc/veto.hxx new file mode 100644 index 000000000..5608e87d1 --- /dev/null +++ b/dbaccess/source/core/inc/veto.hxx @@ -0,0 +1,55 @@ +/* -*- 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/util/XVeto.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace dbaccess +{ + + // Veto + typedef ::cppu::WeakImplHelper< css::util::XVeto + > Veto_Base; + /** implements css::util::XVeto + */ + class Veto : public Veto_Base + { + private: + const css::uno::Any m_aDetails; + + public: + Veto( const css::uno::Any& _rDetails ); + + virtual OUString SAL_CALL getReason() override; + virtual css::uno::Any SAL_CALL getDetails() override; + + protected: + virtual ~Veto() override; + + private: + Veto( const Veto& ) = delete; + Veto& operator=( const Veto& ) = delete; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/inc/viewcontainer.hxx b/dbaccess/source/core/inc/viewcontainer.hxx new file mode 100644 index 000000000..2f7f48cec --- /dev/null +++ b/dbaccess/source/core/inc/viewcontainer.hxx @@ -0,0 +1,94 @@ +/* -*- 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 <cppuhelper/implbase1.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContainerListener.hpp> + +#include "FilteredContainer.hxx" + +namespace dbtools +{ + class WarningsContainer; +} + +namespace dbaccess +{ + // OViewContainer + class OViewContainer : public OFilteredContainer, + public ::cppu::ImplHelper1< css::container::XContainerListener> + { + public: + /** ctor of the container. The parent has to support the <type scope="css::sdbc">XConnection</type> + interface.<BR> + @param _rParent the object which acts as parent for the container. + all refcounting is rerouted to this object + @param _rMutex the access safety object of the parent + @param _rTableFilter restricts the visible tables by name + @param _rTableTypeFilter restricts the visible tables by type + @see construct + */ + OViewContainer( ::cppu::OWeakObject& _rParent, + ::osl::Mutex& _rMutex, + const css::uno::Reference< css::sdbc::XConnection >& _xCon, + bool _bCase, + IRefreshListener* _pRefreshListener, + std::atomic<std::size_t>& _nInAppend + ); + + virtual ~OViewContainer() override; + + protected: + // OFilteredContainer overridables + virtual OUString getTableTypeRestriction() const override; + + private: + virtual void SAL_CALL acquire() noexcept override { OFilteredContainer::acquire();} + virtual void SAL_CALL release() noexcept override { OFilteredContainer::release();} + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + // ::connectivity::sdbcx::OCollection + 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; + + using OFilteredContainer::disposing; + + bool m_bInElementRemoved; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/ContainerMediator.cxx b/dbaccess/source/core/misc/ContainerMediator.cxx new file mode 100644 index 000000000..338858c87 --- /dev/null +++ b/dbaccess/source/core/misc/ContainerMediator.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <PropertyForward.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <comphelper/property.hxx> +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + +OContainerMediator::OContainerMediator( const Reference< XContainer >& _xContainer, const Reference< XNameAccess >& _xSettings ) + : m_xSettings( _xSettings ) + , m_xContainer( _xContainer ) +{ + + if ( _xSettings.is() && _xContainer.is() ) + { + osl_atomic_increment(&m_refCount); + try + { + m_xContainer->addContainerListener(this); + Reference< XContainer > xContainer(_xSettings, UNO_QUERY); + if ( xContainer.is() ) + xContainer->addContainerListener(this); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "OContainerMediator::OContainerMediator"); + } + osl_atomic_decrement( &m_refCount ); + } + else + { + m_xSettings.clear(); + m_xContainer.clear(); + } +} + +OContainerMediator::~OContainerMediator() +{ + acquire(); + impl_cleanup_nothrow(); +} + +void OContainerMediator::impl_cleanup_nothrow() +{ + try + { + Reference< XContainer > xContainer( m_xSettings, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->removeContainerListener( this ); + m_xSettings.clear(); + + xContainer = m_xContainer; + if ( xContainer.is() ) + xContainer->removeContainerListener( this ); + m_xContainer.clear(); + + m_aForwardList.clear(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL OContainerMediator::elementInserted( const ContainerEvent& _rEvent ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( _rEvent.Source == m_xSettings && m_xSettings.is() ) + { + OUString sElementName; + _rEvent.Accessor >>= sElementName; + PropertyForwardList::const_iterator aFind = m_aForwardList.find(sElementName); + if ( aFind != m_aForwardList.end() ) + { + Reference< XPropertySet> xDest(_rEvent.Element,UNO_QUERY); + aFind->second->setDefinition( xDest ); + } + } +} + +void SAL_CALL OContainerMediator::elementRemoved( const ContainerEvent& _rEvent ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + Reference< XContainer > xContainer = m_xContainer; + if ( !(_rEvent.Source == xContainer && xContainer.is()) ) + return; + + OUString sElementName; + _rEvent.Accessor >>= sElementName; + m_aForwardList.erase(sElementName); + try + { + Reference<XNameContainer> xNameContainer( m_xSettings, UNO_QUERY ); + if ( xNameContainer.is() && m_xSettings->hasByName( sElementName ) ) + xNameContainer->removeByName( sElementName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL OContainerMediator::elementReplaced( const ContainerEvent& _rEvent ) +{ + Reference< XContainer > xContainer = m_xContainer; + if ( !(_rEvent.Source == xContainer && xContainer.is()) ) + return; + + OUString sElementName; + _rEvent.ReplacedElement >>= sElementName; + + PropertyForwardList::const_iterator aFind = m_aForwardList.find(sElementName); + if ( aFind == m_aForwardList.end() ) + return; + + OUString sNewName; + _rEvent.Accessor >>= sNewName; + try + { + Reference<XNameContainer> xNameContainer( m_xSettings, UNO_QUERY_THROW ); + if ( xNameContainer.is() && m_xSettings->hasByName( sElementName ) ) + { + Reference<XRename> xSource(m_xSettings->getByName(sElementName),UNO_QUERY_THROW); + xSource->rename(sNewName); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + aFind->second->setName(sNewName); +} + +void SAL_CALL OContainerMediator::disposing( const EventObject& /*Source*/ ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + + impl_cleanup_nothrow(); +} + +void OContainerMediator::impl_initSettings_nothrow( const OUString& _rName, const Reference< XPropertySet >& _rxDestination ) +{ + try + { + if ( m_xSettings.is() && m_xSettings->hasByName( _rName ) ) + { + Reference< XPropertySet > xSettings( m_xSettings->getByName( _rName ), UNO_QUERY_THROW ); + ::comphelper::copyProperties( xSettings, _rxDestination ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OContainerMediator::notifyElementCreated( const OUString& _sName, const Reference< XPropertySet >& _xDest ) +{ + if ( !m_xSettings.is() ) + return; + + PropertyForwardList::const_iterator aFind = m_aForwardList.find( _sName ); + if ( aFind != m_aForwardList.end() + && aFind->second->getDefinition().is() + ) + { + OSL_FAIL( "OContainerMediator::notifyElementCreated: is this really a valid case?" ); + return; + } + + std::vector< OUString > aPropertyList; + try + { + // initially copy from the settings object (if existent) to the newly created object + impl_initSettings_nothrow( _sName, _xDest ); + + // collect the to-be-monitored properties + Reference< XPropertySetInfo > xPSI( _xDest->getPropertySetInfo(), UNO_SET_THROW ); + const Sequence< Property > aProperties( xPSI->getProperties() ); + for ( auto const & property : aProperties ) + { + if ( ( property.Attributes & PropertyAttribute::READONLY ) != 0 ) + continue; + if ( ( property.Attributes & PropertyAttribute::BOUND ) == 0 ) + continue; + + aPropertyList.push_back( property.Name ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + ::rtl::Reference pForward( new OPropertyForward( _xDest, m_xSettings, _sName, aPropertyList ) ); + m_aForwardList[ _sName ] = pForward; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/DatabaseDataProvider.cxx b/dbaccess/source/core/misc/DatabaseDataProvider.cxx new file mode 100644 index 000000000..8695d518c --- /dev/null +++ b/dbaccess/source/core/misc/DatabaseDataProvider.cxx @@ -0,0 +1,1066 @@ +/* -*- 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 <DatabaseDataProvider.hxx> +#include <strings.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/types.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <connectivity/FValue.hxx> +#include <sal/macros.h> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/sdbc/XColumnLocate.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart/XChartDataArray.hpp> + +#include <vector> + +// TODO: update for new HavingClause-aware FilterManager + +namespace dbaccess +{ +using namespace ::com::sun::star; +using ::com::sun::star::uno::Reference; + +DatabaseDataProvider::DatabaseDataProvider(uno::Reference< uno::XComponentContext > const & context) : + TDatabaseDataProvider(m_aMutex), + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >( + context, IMPLEMENTS_PROPERTY_SET, uno::Sequence< OUString >()), + m_aParameterManager( m_aMutex, context ), + m_xContext(context), + m_CommandType(sdb::CommandType::COMMAND), // #i94114 + m_RowLimit(0), + m_EscapeProcessing(true), + m_ApplyFilter(true) +{ + m_xInternal.set( m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.chart.InternalDataProvider",m_xContext ), uno::UNO_QUERY ); + m_xRangeConversion.set(m_xInternal,uno::UNO_QUERY); + m_xComplexDescriptionAccess.set(m_xInternal,uno::UNO_QUERY); + + osl_atomic_increment( &m_refCount ); + { + m_xRowSet.set( m_xContext->getServiceManager()->createInstanceWithContext(SERVICE_SDB_ROWSET,m_xContext ), uno::UNO_QUERY ); + m_xAggregate.set(m_xRowSet,uno::UNO_QUERY); + m_xAggregateSet.set(m_xRowSet,uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xProp(static_cast< ::cppu::OWeakObject* >( this ),uno::UNO_QUERY); + m_aFilterManager.initialize( m_xAggregateSet ); + m_aParameterManager.initialize( xProp, m_xAggregate ); + m_xAggregateSet->setPropertyValue(PROPERTY_COMMAND_TYPE,uno::Any(m_CommandType)); + m_xAggregateSet->setPropertyValue(PROPERTY_ESCAPE_PROCESSING,uno::Any(m_EscapeProcessing)); + } + osl_atomic_decrement( &m_refCount ); +} + +void SAL_CALL DatabaseDataProvider::disposing() +{ + m_aParameterManager.dispose(); // (to free any references it may have to me) + m_aFilterManager.dispose(); // (ditto) + + m_xParent.clear(); + m_xAggregateSet.clear(); + m_xAggregate.clear(); + m_xRangeConversion.clear(); + ::comphelper::disposeComponent(m_xRowSet); + ::comphelper::disposeComponent(m_xInternal); + m_xActiveConnection.clear(); +} + +uno::Any DatabaseDataProvider::queryInterface(uno::Type const & type) +{ + return TDatabaseDataProvider::queryInterface(type); +} + +// XServiceInfo +OUString SAL_CALL DatabaseDataProvider::getImplementationName( ) +{ + return "com.sun.star.comp.dbaccess.DatabaseDataProvider"; +} + +sal_Bool SAL_CALL DatabaseDataProvider::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +uno::Sequence< OUString > SAL_CALL DatabaseDataProvider::getSupportedServiceNames( ) +{ + return { "com.sun.star.chart2.data.DatabaseDataProvider" }; +} + +// lang::XInitialization: +void SAL_CALL DatabaseDataProvider::initialize(const uno::Sequence< uno::Any > & aArguments) +{ + osl::MutexGuard g(m_aMutex); + const uno::Any* pIter = aArguments.getConstArray(); + const uno::Any* pEnd = pIter + aArguments.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( !m_xActiveConnection.is() ) + (*pIter) >>= m_xActiveConnection; + else if ( !m_xHandler.is() ) + (*pIter) >>= m_xHandler; + } + m_xAggregateSet->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, uno::Any( m_xActiveConnection ) ); +} + +// chart2::data::XDataProvider: +sal_Bool SAL_CALL DatabaseDataProvider::createDataSourcePossible(const uno::Sequence< beans::PropertyValue > & _aArguments) +{ + const beans::PropertyValue* pArgIter = _aArguments.getConstArray(); + const beans::PropertyValue* pArgEnd = pArgIter + _aArguments.getLength(); + for(;pArgIter != pArgEnd;++pArgIter) + { + if ( pArgIter->Name == "DataRowSource" ) + { + css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_COLUMNS; + pArgIter->Value >>= eRowSource; + if ( eRowSource != css::chart::ChartDataRowSource_COLUMNS ) + return false; + } + else if ( pArgIter->Name == "CellRangeRepresentation" ) + { + OUString sRange; + pArgIter->Value >>= sRange; + if ( sRange != "all" ) + return false; + } + else if ( pArgIter->Name == "FirstCellAsLabel" ) + { + bool bFirstCellAsLabel = true; + pArgIter->Value >>= bFirstCellAsLabel; + if ( !bFirstCellAsLabel ) + return false; + } + } + return true; +} + +uno::Reference< chart2::data::XDataSource > SAL_CALL DatabaseDataProvider::createDataSource(const uno::Sequence< beans::PropertyValue > & _aArguments) +{ + osl::ResettableMutexGuard aClearForNotifies(m_aMutex); + if ( createDataSourcePossible(_aArguments) ) + { + try + { + uno::Reference< chart::XChartDataArray> xChartData( m_xInternal, uno::UNO_QUERY_THROW ); + xChartData->setData( uno::Sequence< uno::Sequence< double > >() ); + xChartData->setColumnDescriptions( uno::Sequence< OUString >() ); + if ( m_xInternal->hasDataByRangeRepresentation( OUString::number( 0 ) ) ) + m_xInternal->deleteSequence(0); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + ::comphelper::NamedValueCollection aArgs( _aArguments ); + const bool bHasCategories = aArgs.getOrDefault( "HasCategories", true ); + uno::Sequence< OUString > aColumnNames = + aArgs.getOrDefault( "ColumnDescriptions", uno::Sequence< OUString >() ); + + bool bRet = false; + if ( !m_Command.isEmpty() && m_xActiveConnection.is() ) + { + try + { + impl_fillRowSet_throw(); + if ( impl_fillParameters_nothrow(aClearForNotifies) ) + m_xRowSet->execute(); + impl_fillInternalDataProvider_throw(bHasCategories,aColumnNames); + bRet = true; + } + catch(const uno::Exception& /*e*/) + { + } + } + if ( !bRet ) // no command set or an error occurred, use Internal data handler + { + uno::Reference< lang::XInitialization> xIni(m_xInternal,uno::UNO_QUERY); + if ( xIni.is() ) + { + beans::NamedValue aParam("CreateDefaultData",uno::Any(true)); + uno::Sequence< uno::Any > aInitArgs{ uno::Any(aParam) }; + xIni->initialize(aInitArgs); + } + } + + } + return m_xInternal->createDataSource(_aArguments); +} + +uno::Sequence< beans::PropertyValue > SAL_CALL DatabaseDataProvider::detectArguments(const uno::Reference< chart2::data::XDataSource > & _xDataSource) +{ + ::comphelper::NamedValueCollection aArguments; + aArguments.put( "CellRangeRepresentation", uno::Any( OUString( "all" ) ) ); + aArguments.put( "DataRowSource", uno::Any( chart::ChartDataRowSource_COLUMNS ) ); + // internal data always contains labels + aArguments.put( "FirstCellAsLabel", uno::Any( true ) ); + + bool bHasCategories = false; + if( _xDataSource.is()) + { + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences(_xDataSource->getDataSequences()); + const sal_Int32 nCount( aSequences.getLength()); + for( sal_Int32 nIdx=0; nIdx<nCount; ++nIdx ) + { + if( aSequences[nIdx].is() ) + { + uno::Reference< beans::XPropertySet > xSeqProp( aSequences[nIdx]->getValues(), uno::UNO_QUERY ); + OUString aRole; + if ( xSeqProp.is() + && ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) + && aRole == "categories" + ) + { + bHasCategories = true; + break; + } + } + } + } + aArguments.put( "HasCategories", uno::Any( bHasCategories ) ); + return aArguments.getPropertyValues(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::createDataSequenceByRangeRepresentationPossible(const OUString & /*aRangeRepresentation*/) +{ + return true; +} + +uno::Any DatabaseDataProvider::impl_getNumberFormatKey_nothrow(const OUString & _sRangeRepresentation) const +{ + std::map< OUString,css::uno::Any>::const_iterator aFind = m_aNumberFormats.find(_sRangeRepresentation); + if ( aFind != m_aNumberFormats.end() ) + return aFind->second; + return uno::Any(sal_Int32(0)); +} + +uno::Reference< chart2::data::XDataSequence > SAL_CALL DatabaseDataProvider::createDataSequenceByRangeRepresentation(const OUString & _sRangeRepresentation) +{ + osl::MutexGuard g(m_aMutex); + uno::Reference< chart2::data::XDataSequence > xData = m_xInternal->createDataSequenceByRangeRepresentation(_sRangeRepresentation); + uno::Reference<beans::XPropertySet> xProp(xData,uno::UNO_QUERY); + static constexpr OUStringLiteral s_sNumberFormatKey = u"NumberFormatKey"; + if ( xProp.is() && xProp->getPropertySetInfo()->hasPropertyByName(s_sNumberFormatKey) ) + { + xProp->setPropertyValue(s_sNumberFormatKey,impl_getNumberFormatKey_nothrow(_sRangeRepresentation)); + } + return xData; +} + +uno::Reference<chart2::data::XDataSequence> +SAL_CALL DatabaseDataProvider::createDataSequenceByValueArray( + const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/, const OUString& /*aRoleQualifier*/ ) +{ + return uno::Reference<chart2::data::XDataSequence>(); +} + +uno::Sequence< uno::Sequence< OUString > > SAL_CALL DatabaseDataProvider::getComplexRowDescriptions() +{ + return m_xComplexDescriptionAccess->getComplexRowDescriptions(); +} + +void SAL_CALL DatabaseDataProvider::setComplexRowDescriptions( const uno::Sequence< uno::Sequence< OUString > >& aRowDescriptions ) +{ + m_xComplexDescriptionAccess->setComplexRowDescriptions(aRowDescriptions); +} + +uno::Sequence< uno::Sequence< OUString > > SAL_CALL DatabaseDataProvider::getComplexColumnDescriptions() +{ + return m_xComplexDescriptionAccess->getComplexColumnDescriptions(); +} + +void SAL_CALL DatabaseDataProvider::setComplexColumnDescriptions( const uno::Sequence< uno::Sequence< OUString > >& aColumnDescriptions ) +{ + m_xComplexDescriptionAccess->setComplexColumnDescriptions(aColumnDescriptions); +} + +// ____ XChartDataArray ____ +uno::Sequence< uno::Sequence< double > > SAL_CALL DatabaseDataProvider::getData() +{ + return m_xComplexDescriptionAccess->getData(); +} + +void SAL_CALL DatabaseDataProvider::setData( const uno::Sequence< uno::Sequence< double > >& rDataInRows ) +{ + m_xComplexDescriptionAccess->setData(rDataInRows); +} + +void SAL_CALL DatabaseDataProvider::setRowDescriptions( const uno::Sequence< OUString >& aRowDescriptions ) +{ + m_xComplexDescriptionAccess->setRowDescriptions(aRowDescriptions); +} + +void SAL_CALL DatabaseDataProvider::setColumnDescriptions( const uno::Sequence< OUString >& aColumnDescriptions ) +{ + m_xComplexDescriptionAccess->setColumnDescriptions(aColumnDescriptions); +} + +uno::Sequence< OUString > SAL_CALL DatabaseDataProvider::getRowDescriptions() +{ + return m_xComplexDescriptionAccess->getRowDescriptions(); +} + +uno::Sequence< OUString > SAL_CALL DatabaseDataProvider::getColumnDescriptions() +{ + return m_xComplexDescriptionAccess->getColumnDescriptions(); +} + +// ____ XChartData (base of XChartDataArray) ____ +void SAL_CALL DatabaseDataProvider::addChartDataChangeEventListener(const uno::Reference< css::chart::XChartDataChangeEventListener >& x) +{ + m_xComplexDescriptionAccess->addChartDataChangeEventListener(x); +} + +void SAL_CALL DatabaseDataProvider::removeChartDataChangeEventListener(const uno::Reference< css::chart::XChartDataChangeEventListener >& x) +{ + m_xComplexDescriptionAccess->removeChartDataChangeEventListener(x); +} + +double SAL_CALL DatabaseDataProvider::getNotANumber() +{ + return m_xComplexDescriptionAccess->getNotANumber(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::isNotANumber( double nNumber ) +{ + return m_xComplexDescriptionAccess->isNotANumber(nNumber); +} + +uno::Reference< sheet::XRangeSelection > SAL_CALL DatabaseDataProvider::getRangeSelection() +{ + // TODO: Exchange the default return implementation for "getRangeSelection" !!! + // Exchange the default return implementation. + // NOTE: Default initialized polymorphic structs can cause problems because of + // missing default initialization of primitive types of some C++ compilers or + // different Any initialization in Java and C++ polymorphic structs. + return uno::Reference< sheet::XRangeSelection >(); +} + +// chart2::data::XRangeXMLConversion: +OUString SAL_CALL DatabaseDataProvider::convertRangeToXML(const OUString & _sRangeRepresentation) +{ + osl::MutexGuard g(m_aMutex); + return m_xRangeConversion->convertRangeToXML(_sRangeRepresentation); +} + +OUString SAL_CALL DatabaseDataProvider::convertRangeFromXML(const OUString & _sXMLRange) +{ + osl::MutexGuard g(m_aMutex); + return m_xRangeConversion->convertRangeFromXML(_sXMLRange); +} + +// com.sun.star.beans.XPropertySet: +uno::Reference< beans::XPropertySetInfo > SAL_CALL DatabaseDataProvider::getPropertySetInfo() +{ + return ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::getPropertySetInfo(); +} + +void SAL_CALL DatabaseDataProvider::setPropertyValue(const OUString & aPropertyName, const uno::Any & aValue) +{ + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::setPropertyValue(aPropertyName, aValue); +} + +uno::Any SAL_CALL DatabaseDataProvider::getPropertyValue(const OUString & aPropertyName) +{ + return ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::getPropertyValue(aPropertyName); +} + +void SAL_CALL DatabaseDataProvider::addPropertyChangeListener(const OUString & aPropertyName, const uno::Reference< beans::XPropertyChangeListener > & xListener) +{ + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::addPropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL DatabaseDataProvider::removePropertyChangeListener(const OUString & aPropertyName, const uno::Reference< beans::XPropertyChangeListener > & xListener) +{ + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::removePropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL DatabaseDataProvider::addVetoableChangeListener(const OUString & aPropertyName, const uno::Reference< beans::XVetoableChangeListener > & xListener) +{ + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::addVetoableChangeListener(aPropertyName, xListener); +} + +void SAL_CALL DatabaseDataProvider::removeVetoableChangeListener(const OUString & aPropertyName, const uno::Reference< beans::XVetoableChangeListener > & xListener) +{ + ::cppu::PropertySetMixin< chart2::data::XDatabaseDataProvider >::removeVetoableChangeListener(aPropertyName, xListener); +} + +// chart2::data::XDatabaseDataProvider: +uno::Sequence< OUString > SAL_CALL DatabaseDataProvider::getMasterFields() +{ + osl::MutexGuard g(m_aMutex); + return m_MasterFields; +} + +void SAL_CALL DatabaseDataProvider::setMasterFields(const uno::Sequence< OUString > & the_value) +{ + impl_invalidateParameter_nothrow(); + set("MasterFields",the_value,m_MasterFields); +} + +uno::Sequence< OUString > SAL_CALL DatabaseDataProvider::getDetailFields() +{ + osl::MutexGuard g(m_aMutex); + return m_DetailFields; +} + +void SAL_CALL DatabaseDataProvider::setDetailFields(const uno::Sequence< OUString > & the_value) +{ + set("DetailFields",the_value,m_DetailFields); +} + +OUString SAL_CALL DatabaseDataProvider::getCommand() +{ + osl::MutexGuard g(m_aMutex); + return m_Command; +} + +void SAL_CALL DatabaseDataProvider::setCommand(const OUString & the_value) +{ + { + osl::MutexGuard g(m_aMutex); + impl_invalidateParameter_nothrow(); + m_xAggregateSet->setPropertyValue( PROPERTY_COMMAND, uno::Any( the_value ) ); + } + set(PROPERTY_COMMAND,the_value,m_Command); +} + +::sal_Int32 SAL_CALL DatabaseDataProvider::getCommandType() +{ + osl::MutexGuard g(m_aMutex); + return m_CommandType; +} + +void SAL_CALL DatabaseDataProvider::setCommandType(::sal_Int32 the_value) +{ + { + osl::MutexGuard g(m_aMutex); + m_xAggregateSet->setPropertyValue( PROPERTY_COMMAND_TYPE, uno::Any( the_value ) ); + } + set(PROPERTY_COMMAND_TYPE,the_value,m_CommandType); +} + +OUString SAL_CALL DatabaseDataProvider::getFilter() +{ + osl::MutexGuard g(m_aMutex); + return m_aFilterManager.getFilterComponent( dbtools::FilterManager::FilterComponent::PublicFilter ); +} + +void SAL_CALL DatabaseDataProvider::setFilter(const OUString & the_value) +{ + { + osl::MutexGuard g(m_aMutex); + m_aFilterManager.setFilterComponent( dbtools::FilterManager::FilterComponent::PublicFilter, the_value ); + } + set(PROPERTY_FILTER,the_value,m_Filter); +} + +sal_Bool SAL_CALL DatabaseDataProvider::getApplyFilter() +{ + osl::MutexGuard g(m_aMutex); + return m_ApplyFilter; +} + +void SAL_CALL DatabaseDataProvider::setApplyFilter( sal_Bool the_value ) +{ + { + osl::MutexGuard g(m_aMutex); + m_xAggregateSet->setPropertyValue( PROPERTY_APPLYFILTER, uno::Any( the_value ) ); + } + set(PROPERTY_APPLYFILTER,static_cast<bool>(the_value),m_ApplyFilter); +} + +OUString SAL_CALL DatabaseDataProvider::getHavingClause() +{ + osl::MutexGuard g(m_aMutex); + return m_HavingClause; +} + +void SAL_CALL DatabaseDataProvider::setHavingClause( const OUString& the_value ) +{ + { + osl::MutexGuard g(m_aMutex); + m_xAggregateSet->setPropertyValue( PROPERTY_HAVING_CLAUSE, uno::Any( the_value ) ); + } + set(PROPERTY_HAVING_CLAUSE,the_value,m_HavingClause); +} + +OUString SAL_CALL DatabaseDataProvider::getGroupBy() +{ + osl::MutexGuard g(m_aMutex); + return m_GroupBy; +} + +void SAL_CALL DatabaseDataProvider::setGroupBy( const OUString& the_value ) +{ + { + osl::MutexGuard g(m_aMutex); + m_xAggregateSet->setPropertyValue( PROPERTY_GROUP_BY, uno::Any( the_value ) ); + } + set(PROPERTY_GROUP_BY,the_value,m_GroupBy); +} + +OUString SAL_CALL DatabaseDataProvider::getOrder() +{ + osl::MutexGuard g(m_aMutex); + return m_Order; +} + +void SAL_CALL DatabaseDataProvider::setOrder( const OUString& the_value ) +{ + { + osl::MutexGuard g(m_aMutex); + m_xAggregateSet->setPropertyValue( PROPERTY_ORDER, uno::Any( the_value ) ); + } + set(PROPERTY_ORDER,the_value,m_Order); +} + +sal_Bool SAL_CALL DatabaseDataProvider::getEscapeProcessing() +{ + osl::MutexGuard g(m_aMutex); + return m_EscapeProcessing; +} + +void SAL_CALL DatabaseDataProvider::setEscapeProcessing(sal_Bool the_value) +{ + set(PROPERTY_ESCAPE_PROCESSING,static_cast<bool>(the_value),m_EscapeProcessing); +} + +::sal_Int32 SAL_CALL DatabaseDataProvider::getRowLimit() +{ + osl::MutexGuard g(m_aMutex); + return m_RowLimit; +} + +void SAL_CALL DatabaseDataProvider::setRowLimit(::sal_Int32 the_value) +{ + set("RowLimit",the_value,m_RowLimit); +} + +uno::Reference< sdbc::XConnection > SAL_CALL DatabaseDataProvider::getActiveConnection() +{ + osl::MutexGuard g(m_aMutex); + return m_xActiveConnection; +} + +void SAL_CALL DatabaseDataProvider::setActiveConnection(const uno::Reference< sdbc::XConnection > & the_value) +{ + if ( !the_value.is() ) + throw lang::IllegalArgumentException(); + set(PROPERTY_ACTIVE_CONNECTION,the_value,m_xActiveConnection); +} + +OUString SAL_CALL DatabaseDataProvider::getDataSourceName() +{ + osl::MutexGuard g(m_aMutex); + return m_DataSourceName; +} + +void SAL_CALL DatabaseDataProvider::setDataSourceName(const OUString& the_value) +{ + set(PROPERTY_DATASOURCENAME,the_value,m_DataSourceName); +} + +namespace +{ + struct ColumnDescription + { + OUString sName; + sal_Int32 nResultSetPosition; + sal_Int32 nDataType; + + ColumnDescription() + :nResultSetPosition( 0 ) + ,nDataType( sdbc::DataType::VARCHAR ) + { + } + explicit ColumnDescription( const OUString& i_rName ) + :sName( i_rName ) + ,nResultSetPosition( 0 ) + ,nDataType( sdbc::DataType::VARCHAR ) + { + } + }; + + struct CreateColumnDescription + { + ColumnDescription operator()( const OUString& i_rName ) + { + return ColumnDescription( i_rName ); + } + }; + + struct SelectColumnName + { + const OUString& operator()( const ColumnDescription& i_rColumn ) + { + return i_rColumn.sName; + } + }; +} + +void DatabaseDataProvider::impl_fillInternalDataProvider_throw(bool _bHasCategories,const uno::Sequence< OUString >& i_aColumnNames) +{ + // clear the data before fill the new one + uno::Reference< sdbcx::XColumnsSupplier > xColSup(m_xRowSet,uno::UNO_QUERY_THROW); + uno::Reference< container::XNameAccess > xColumns( xColSup->getColumns(), uno::UNO_SET_THROW ); + const uno::Sequence< OUString > aRowSetColumnNames( xColumns->getElementNames() ); + + typedef std::vector< ColumnDescription > ColumnDescriptions; + ColumnDescriptions aColumns; + bool bFirstColumnIsCategory = _bHasCategories; + if ( i_aColumnNames.hasElements() ) + { + // some normalizations ... + uno::Sequence< OUString > aImposedColumnNames( i_aColumnNames ); + + // strangely, there exist documents where the ColumnDescriptions end with a number of empty strings. /me + // thinks they're generated when you have a chart based on a result set with n columns, but remove some + // of those columns from the chart - it looks like a bug in the report XML export to me. + // So, get rid of the "trailing" empty columns + sal_Int32 nLastNonEmptyColName = aImposedColumnNames.getLength() - 1; + for ( ; nLastNonEmptyColName >= 0; --nLastNonEmptyColName ) + { + if ( !aImposedColumnNames[ nLastNonEmptyColName ].isEmpty() ) + break; + } + aImposedColumnNames.realloc( nLastNonEmptyColName + 1 ); + + // second, for X-Y-charts the ColumnDescriptions exported by chart miss the name of the first (non-category) + // column. This, this results in a ColumnDescriptions array like <"", "col2", "col3">, where you'd expect + // <"col1", "col2", "col3">. + // Fix this with some heuristics: + if ( aImposedColumnNames.hasElements() && ( !aImposedColumnNames[0].isEmpty() ) ) + { + const sal_Int32 nAssumedRowSetColumnIndex = _bHasCategories ? 1 : 0; + if ( nAssumedRowSetColumnIndex < aRowSetColumnNames.getLength() ) + aImposedColumnNames.getArray()[0] = aRowSetColumnNames[ nAssumedRowSetColumnIndex ]; + } + + const sal_Int32 nCount = aImposedColumnNames.getLength(); + for ( sal_Int32 i = 0 ; i < nCount; ++i ) + { + const OUString sColumnName( aImposedColumnNames[i] ); + if ( !xColumns->hasByName( sColumnName ) ) + continue; + + if ( _bHasCategories && aColumns.empty() ) + { + if ( aRowSetColumnNames.hasElements() ) + aColumns.emplace_back( aRowSetColumnNames[0] ); + else + aColumns.emplace_back( sColumnName ); + bFirstColumnIsCategory = true; + } + aColumns.emplace_back( sColumnName ); + } + } + if ( aColumns.empty() ) + { + aColumns.resize( aRowSetColumnNames.getLength() ); + std::transform( + aRowSetColumnNames.begin(), + aRowSetColumnNames.end(), + aColumns.begin(), + CreateColumnDescription() + ); + } + + // fill the data + uno::Reference< sdbc::XResultSet> xRes( m_xRowSet, uno::UNO_QUERY_THROW ); + uno::Reference< sdbc::XRow> xRow( m_xRowSet,uno::UNO_QUERY_THROW ); + uno::Reference< sdbc::XResultSetMetaDataSupplier > xSuppMeta( m_xRowSet,uno::UNO_QUERY_THROW ); + uno::Reference< sdbc::XColumnLocate > xColumnLocate( m_xRowSet, uno::UNO_QUERY_THROW ); + + sal_Int32 columnIndex = 0; + for (auto & column : aColumns) + { + column.nResultSetPosition = xColumnLocate->findColumn( column.sName ); + + const uno::Reference< beans::XPropertySet > xColumn( xColumns->getByName( column.sName ), uno::UNO_QUERY_THROW ); + const uno::Any aNumberFormat( xColumn->getPropertyValue( PROPERTY_NUMBERFORMAT ) ); + OSL_VERIFY( xColumn->getPropertyValue( PROPERTY_TYPE ) >>= column.nDataType ); + + const OUString sRangeName = OUString::number( columnIndex ); + m_aNumberFormats.emplace( sRangeName, aNumberFormat ); + ++columnIndex; + } + + std::vector< OUString > aRowLabels; + std::vector< std::vector< double > > aDataValues; + sal_Int32 nRowCount = 0; + ::connectivity::ORowSetValue aValue; + while( xRes->next() && (!m_RowLimit || nRowCount < m_RowLimit) ) + { + ++nRowCount; + + aValue.fill( aColumns[0].nResultSetPosition, aColumns[0].nDataType, xRow ); + aRowLabels.push_back( aValue.getString() ); + + std::vector< double > aRow; + bool bFirstLoop = true; + for (auto const& column : aColumns) + { + if (bFirstLoop) + { + bFirstLoop = false; + if (bFirstColumnIsCategory) + continue; + } + + aValue.fill( column.nResultSetPosition, column.nDataType, xRow ); + if ( aValue.isNull() ) + aRow.push_back( std::numeric_limits<double>::quiet_NaN() ); + else + aRow.push_back( aValue.getDouble() ); + } + + aDataValues.push_back( aRow ); + } + + // insert default data when no rows exist + if ( !nRowCount ) + { + nRowCount = 3; + static const double fDefaultData[ ] = + { 9.10, 3.20, 4.54, + 2.40, 8.80, 9.65, + 3.10, 1.50, 3.70, + 4.30, 9.02, 6.20 }; + for(sal_Int32 h = 0,k = 0; h < nRowCount; ++h,++k ) + { + aRowLabels.push_back(OUString::number(h+1)); + std::vector< double > aRow; + const sal_Int32 nSize = SAL_N_ELEMENTS(fDefaultData); + for (size_t j = 0; j < (aColumns.size()-1); ++j,++k) + { + if ( k >= nSize ) + k = 0; + aRow.push_back(fDefaultData[k]); + } + aDataValues.push_back(aRow); + } + } + + uno::Reference< chart::XChartDataArray> xData(m_xInternal,uno::UNO_QUERY); + xData->setRowDescriptions(comphelper::containerToSequence(aRowLabels)); + + const size_t nOffset = bFirstColumnIsCategory ? 1 : 0; + uno::Sequence< OUString > aColumnDescriptions( aColumns.size() - nOffset ); + std::transform( + aColumns.begin() + nOffset, + aColumns.end(), + aColumnDescriptions.getArray(), + SelectColumnName() + ); + xData->setColumnDescriptions( aColumnDescriptions ); + + uno::Sequence< uno::Sequence< double > > aData(aDataValues.size()); + uno::Sequence< double >* pDataIter = aData.getArray(); + uno::Sequence< double >* pDataEnd = pDataIter + aData.getLength(); + for(sal_Int32 i= 0;pDataIter != pDataEnd; ++pDataIter,++i ) + { + if ( !aDataValues[i].empty() ) + *pDataIter = comphelper::containerToSequence(aDataValues[i]); + } + xData->setData(aData); +} + +void DatabaseDataProvider::impl_fillRowSet_throw() +{ + m_xAggregateSet->setPropertyValue( PROPERTY_FILTER, uno::Any( getFilter() ) ); + uno::Reference< sdbc::XParameters> xParam(m_xRowSet,uno::UNO_QUERY_THROW); + xParam->clearParameters( ); +} + +bool DatabaseDataProvider::impl_fillParameters_nothrow( ::osl::ResettableMutexGuard& _rClearForNotifies) +{ + // do we have to fill the parameters again? + if ( !m_aParameterManager.isUpToDate() ) + m_aParameterManager.updateParameterInfo( m_aFilterManager ); + + if ( m_aParameterManager.isUpToDate() ) + return m_aParameterManager.fillParameterValues( m_xHandler, _rClearForNotifies ); + + return true; +} + +// css::sdbc::XParameters +void SAL_CALL DatabaseDataProvider::setNull(sal_Int32 parameterIndex, sal_Int32 sqlType) +{ + m_aParameterManager.setNull(parameterIndex, sqlType); +} + +void SAL_CALL DatabaseDataProvider::setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName) +{ + m_aParameterManager.setObjectNull(parameterIndex, sqlType, typeName); +} + +void SAL_CALL DatabaseDataProvider::setBoolean(sal_Int32 parameterIndex, sal_Bool x) +{ + m_aParameterManager.setBoolean(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setByte(sal_Int32 parameterIndex, sal_Int8 x) +{ + m_aParameterManager.setByte(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setShort(sal_Int32 parameterIndex, sal_Int16 x) +{ + m_aParameterManager.setShort(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setInt(sal_Int32 parameterIndex, sal_Int32 x) +{ + m_aParameterManager.setInt(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setLong(sal_Int32 parameterIndex, sal_Int64 x) +{ + m_aParameterManager.setLong(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setFloat(sal_Int32 parameterIndex, float x) +{ + m_aParameterManager.setFloat(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setDouble(sal_Int32 parameterIndex, double x) +{ + m_aParameterManager.setDouble(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setString(sal_Int32 parameterIndex, const OUString& x) +{ + m_aParameterManager.setString(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setBytes(sal_Int32 parameterIndex, const uno::Sequence< sal_Int8 >& x) +{ + m_aParameterManager.setBytes(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setDate(sal_Int32 parameterIndex, const util::Date& x) +{ + m_aParameterManager.setDate(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setTime(sal_Int32 parameterIndex, const util::Time& x) +{ + m_aParameterManager.setTime(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setTimestamp(sal_Int32 parameterIndex, const util::DateTime& x) +{ + m_aParameterManager.setTimestamp(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setBinaryStream(sal_Int32 parameterIndex, const uno::Reference<io::XInputStream>& x, sal_Int32 length) +{ + m_aParameterManager.setBinaryStream(parameterIndex, x, length); +} + +void SAL_CALL DatabaseDataProvider::setCharacterStream(sal_Int32 parameterIndex, const uno::Reference<io::XInputStream>& x, sal_Int32 length) +{ + m_aParameterManager.setCharacterStream(parameterIndex, x, length); +} + +void SAL_CALL DatabaseDataProvider::setObjectWithInfo(sal_Int32 parameterIndex, const uno::Any& x, sal_Int32 targetSqlType, sal_Int32 scale) +{ + m_aParameterManager.setObjectWithInfo(parameterIndex, x, targetSqlType, scale); +} + +void SAL_CALL DatabaseDataProvider::setObject(sal_Int32 parameterIndex, const uno::Any& x) +{ + m_aParameterManager.setObject(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setRef(sal_Int32 parameterIndex, const uno::Reference<sdbc::XRef>& x) +{ + m_aParameterManager.setRef(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setBlob(sal_Int32 parameterIndex, const uno::Reference<sdbc::XBlob>& x) +{ + m_aParameterManager.setBlob(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setClob(sal_Int32 parameterIndex, const uno::Reference<sdbc::XClob>& x) +{ + m_aParameterManager.setClob(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::setArray(sal_Int32 parameterIndex, const Reference<sdbc::XArray>& x) +{ + m_aParameterManager.setArray(parameterIndex, x); +} + +void SAL_CALL DatabaseDataProvider::clearParameters() +{ + m_aParameterManager.clearParameters(); +} + +// css::sdbc::XRowSet +void SAL_CALL DatabaseDataProvider::execute() +{ + uno::Sequence< beans::PropertyValue > aEmpty; + createDataSource(aEmpty); +} + +void SAL_CALL DatabaseDataProvider::addRowSetListener(const uno::Reference<sdbc::XRowSetListener>& _rListener) +{ + if (m_xRowSet.is()) + m_xRowSet->addRowSetListener(_rListener); +} + +void SAL_CALL DatabaseDataProvider::removeRowSetListener(const uno::Reference<sdbc::XRowSetListener>& _rListener) +{ + if (m_xRowSet.is()) + m_xRowSet->removeRowSetListener(_rListener); +} + +// css::sdbc::XResultSet +sal_Bool SAL_CALL DatabaseDataProvider::next() +{ + return m_xRowSet->next(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::isBeforeFirst() +{ + return m_xRowSet->isBeforeFirst(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::isAfterLast() +{ + return m_xRowSet->isAfterLast(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::isFirst() +{ + return m_xRowSet->isFirst(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::isLast() +{ + return m_xRowSet->isLast(); +} + +void SAL_CALL DatabaseDataProvider::beforeFirst() +{ + m_xRowSet->beforeFirst(); +} + +void SAL_CALL DatabaseDataProvider::afterLast() +{ + m_xRowSet->afterLast(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::first() +{ + return m_xRowSet->first(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::last() +{ + return m_xRowSet->last(); +} + +sal_Int32 SAL_CALL DatabaseDataProvider::getRow() +{ + return m_xRowSet->getRow(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::absolute(sal_Int32 row) +{ + return m_xRowSet->absolute(row); +} + +sal_Bool SAL_CALL DatabaseDataProvider::relative(sal_Int32 rows) +{ + return m_xRowSet->relative(rows); +} + +sal_Bool SAL_CALL DatabaseDataProvider::previous() +{ + return m_xRowSet->previous(); +} + +void SAL_CALL DatabaseDataProvider::refreshRow() +{ + m_xRowSet->refreshRow(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::rowUpdated() +{ + return m_xRowSet->rowUpdated(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::rowInserted() +{ + return m_xRowSet->rowInserted(); +} + +sal_Bool SAL_CALL DatabaseDataProvider::rowDeleted() +{ + return m_xRowSet->rowDeleted(); +} + +uno::Reference< uno::XInterface > SAL_CALL DatabaseDataProvider::getStatement() +{ + return m_xRowSet->getStatement(); +} + +uno::Reference< uno::XInterface > SAL_CALL DatabaseDataProvider::getParent( ) +{ + return m_xParent; +} + +void SAL_CALL DatabaseDataProvider::setParent( const uno::Reference< uno::XInterface >& _xParent ) +{ + osl::MutexGuard g(m_aMutex); + m_xParent = _xParent; +} + +void DatabaseDataProvider::impl_invalidateParameter_nothrow() +{ + osl::MutexGuard g(m_aMutex); + m_aParameterManager.clearAllParameterInformation(); +} + +} // namespace dbaccess + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dbaccess_DatabaseDataProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new dbaccess::DatabaseDataProvider(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/PropertyForward.cxx b/dbaccess/source/core/misc/PropertyForward.cxx new file mode 100644 index 000000000..239bd422f --- /dev/null +++ b/dbaccess/source/core/misc/PropertyForward.cxx @@ -0,0 +1,146 @@ +/* -*- 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 <PropertyForward.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> + +#include <comphelper/property.hxx> +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::lang; + + + OPropertyForward::OPropertyForward( const Reference< XPropertySet>& _xSource, const Reference< XNameAccess>& _xDestContainer, + const OUString& _sName, const std::vector< OUString>& _aPropertyList ) + :m_xSource( _xSource, UNO_SET_THROW ) + ,m_xDestContainer( _xDestContainer, UNO_SET_THROW ) + ,m_sName( _sName ) + ,m_bInInsert( false ) + { + + osl_atomic_increment(&m_refCount); + try + { + if ( _aPropertyList.empty() ) + _xSource->addPropertyChangeListener( OUString(), this ); + else + { + for (auto const& property : _aPropertyList) + _xSource->addPropertyChangeListener(property, this); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + osl_atomic_decrement( &m_refCount ); + } + + OPropertyForward::~OPropertyForward() + { + } + + void SAL_CALL OPropertyForward::propertyChange( const PropertyChangeEvent& evt ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !m_xDestContainer.is() ) + throw DisposedException( OUString(), *this ); + + try + { + if ( !m_xDest.is() ) + { + if ( m_xDestContainer->hasByName( m_sName ) ) + { + m_xDest.set( m_xDestContainer->getByName( m_sName ), UNO_QUERY_THROW ); + } + else + { + Reference< XDataDescriptorFactory > xFactory( m_xDestContainer, UNO_QUERY_THROW ); + m_xDest.set( xFactory->createDataDescriptor(), UNO_SET_THROW ); + + ::comphelper::copyProperties( m_xSource, m_xDest ); + + m_bInInsert = true; + Reference< XAppend > xAppend( m_xDestContainer, UNO_QUERY_THROW ); + xAppend->appendByDescriptor( m_xDest ); + m_bInInsert = false; + } + + m_xDestInfo.set( m_xDest->getPropertySetInfo(), UNO_SET_THROW ); + } + + if ( m_xDestInfo->hasPropertyByName( evt.PropertyName ) ) + { + m_xDest->setPropertyValue( evt.PropertyName, evt.NewValue ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void SAL_CALL OPropertyForward::disposing( const css::lang::EventObject& /*_rSource*/ ) + { + ::osl::MutexGuard aGuard(m_aMutex); + + if ( !m_xSource.is() ) + throw DisposedException( OUString(), *this ); + + m_xSource->removePropertyChangeListener( OUString(), this ); + m_xSource = nullptr; + m_xDestContainer = nullptr; + m_xDestInfo = nullptr; + m_xDest = nullptr; + } + + void OPropertyForward::setDefinition( const css::uno::Reference< css::beans::XPropertySet>& _xDest ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bInInsert ) + return; + + OSL_ENSURE( !m_xDest.is(), "OPropertyForward::setDefinition: definition object is already set!" ); + try + { + m_xDest.set( _xDest, UNO_SET_THROW ); + m_xDestInfo.set( m_xDest->getPropertySetInfo(), UNO_SET_THROW ); + ::comphelper::copyProperties( m_xDest, m_xSource ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/apitools.cxx b/dbaccess/source/core/misc/apitools.cxx new file mode 100644 index 000000000..efec92cf5 --- /dev/null +++ b/dbaccess/source/core/misc/apitools.cxx @@ -0,0 +1,114 @@ +/* -*- 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 <apitools.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace cppu; +using namespace osl; + +// various helper functions +// OSubComponent +OSubComponent::OSubComponent(Mutex& _rMutex, const Reference< XInterface > & xParent) + :OComponentHelper(_rMutex) + ,m_xParent(xParent) +{ + +} + +OSubComponent::~OSubComponent() +{ + m_xParent = nullptr; + +} + +// css::lang::XTypeProvider +Sequence< Type > OSubComponent::getTypes() +{ + OTypeCollection aTypes(cppu::UnoType<XComponent>::get(), + cppu::UnoType<XTypeProvider>::get(), + cppu::UnoType<XWeak>::get()); + + return aTypes.getTypes(); +} + +// XInterface + +void OSubComponent::release() noexcept +{ + Reference< XInterface > x( xDelegator ); + if (! x.is()) + { + if (osl_atomic_decrement( &m_refCount ) == 0 ) + { + if (! rBHelper.bDisposed) + { + // *before* again incrementing our ref count, ensure that our weak connection point + // will not create references to us anymore (via XAdapter::queryAdapted) + disposeWeakConnectionPoint(); + + Reference< XInterface > xHoldAlive( *this ); + // remember the parent + Reference< XInterface > xParent; + { + MutexGuard aGuard( rBHelper.rMutex ); + xParent = m_xParent; + m_xParent = nullptr; + } + + SAL_WARN_IF( m_refCount != 1, "dbaccess.core", "OSubComponent::release: invalid ref count (before dispose)!" ); + + // First dispose + dispose(); + + // only the alive ref holds the object + SAL_WARN_IF( m_refCount != 1, "dbaccess.core", "OSubComponent::release: invalid ref count (after dispose)!" ); + + // release the parent in the ~ + if (xParent.is()) + { + MutexGuard aGuard( rBHelper.rMutex ); + m_xParent = xParent; + } + + // destroy the object if xHoldAlive decrement the refcount to 0 + return; + } + } + // restore the reference count + osl_atomic_increment( &m_refCount ); + } + + // as we cover the job of the componenthelper we use the ... + OWeakAggObject::release(); +} + +Any OSubComponent::queryInterface( const Type & rType ) +{ + Any aReturn; + if (!rType.equals(cppu::UnoType<XAggregation>::get())) + aReturn = OComponentHelper::queryInterface(rType); + + return aReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/dsntypes.cxx b/dbaccess/source/core/misc/dsntypes.cxx new file mode 100644 index 000000000..d8ceed402 --- /dev/null +++ b/dbaccess/source/core/misc/dsntypes.cxx @@ -0,0 +1,565 @@ +/* -*- 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 <config_java.h> +#include <dsntypes.hxx> +#include <unotools/confignode.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tools/wldcrd.hxx> +#include <osl/file.hxx> +#include <officecfg/Office/Common.hxx> +#include <comphelper/string.hxx> + +namespace dbaccess +{ + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + + namespace + { + void lcl_extractHostAndPort(std::u16string_view _sUrl, OUString& _sHostname, sal_Int32& _nPortNumber) + { + if ( comphelper::string::getTokenCount(_sUrl, ':') >= 2 ) + { + sal_Int32 nPos {0}; + _sHostname = o3tl::getToken(_sUrl, 0, ':', nPos); + _nPortNumber = o3tl::toInt32(o3tl::getToken(_sUrl, 0, ':', nPos)); + } + } + } +// ODsnTypeCollection +ODsnTypeCollection::ODsnTypeCollection(const css::uno::Reference< css::uno::XComponentContext >& _xContext) +:m_aDriverConfig(_xContext) +#if OSL_DEBUG_LEVEL > 0 +,m_nLivingIterators(0) +#endif +{ + const uno::Sequence< OUString > aURLs = m_aDriverConfig.getURLs(); + const OUString* pIter = aURLs.getConstArray(); + const OUString* pEnd = pIter + aURLs.getLength(); + for(;pIter != pEnd;++pIter ) + { + m_aDsnPrefixes.push_back(*pIter); + m_aDsnTypesDisplayNames.push_back(m_aDriverConfig.getDriverTypeDisplayName(*pIter)); + } + + OSL_ENSURE(m_aDsnTypesDisplayNames.size() == m_aDsnPrefixes.size(), + "ODsnTypeCollection::ODsnTypeCollection : invalid resources !"); +} + +ODsnTypeCollection::~ODsnTypeCollection() +{ +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE(0 == m_nLivingIterators, "ODsnTypeCollection::~ODsnTypeCollection : there are still living iterator objects!"); +#endif +} + +OUString ODsnTypeCollection::getTypeDisplayName(std::u16string_view _sURL) const +{ + return m_aDriverConfig.getDriverTypeDisplayName(_sURL); +} + +OUString ODsnTypeCollection::cutPrefix(std::u16string_view _sURL) const +{ + OUString sRet; + OUString sOldPattern; + + // on Windows or with gen rendering, the urls may begin with an ~ + std::u16string_view sCleanURL = comphelper::string::stripStart(_sURL, '~'); + + for (auto const& dsnPrefix : m_aDsnPrefixes) + { + WildCard aWildCard(dsnPrefix); + if ( sOldPattern.getLength() < dsnPrefix.getLength() && aWildCard.Matches(sCleanURL) ) + { + // This relies on the fact that all patterns are of the form + // foo* + // that is, the very concept of "prefix" applies. + OUString prefix(comphelper::string::stripEnd(dsnPrefix, '*')); + OSL_ENSURE(o3tl::make_unsigned(prefix.getLength()) <= sCleanURL.size(), "How can A match B when A shorter than B?"); + sRet = sCleanURL.substr(prefix.getLength()); + sOldPattern = dsnPrefix; + } + } + + return sRet; +} + +OUString ODsnTypeCollection::getPrefix(const OUString& _sURL) const +{ + OUString sRet; + OUString sOldPattern; + for (auto const& dsnPrefix : m_aDsnPrefixes) + { + WildCard aWildCard(dsnPrefix); + if ( sOldPattern.getLength() < dsnPrefix.getLength() && aWildCard.Matches(_sURL) ) + { + // This relies on the fact that all patterns are of the form + // foo* + // that is, the very concept of "prefix" applies. + sRet = comphelper::string::stripEnd(dsnPrefix, '*'); + OSL_ENSURE(sRet.getLength() <= _sURL.getLength(), "How can A match B when A shorter than B?"); + sOldPattern = dsnPrefix; + } + } + + return sRet; +} + +bool ODsnTypeCollection::hasDriver( const char* _pAsciiPattern ) const +{ + OUString sPrefix( getPrefix( OUString::createFromAscii( _pAsciiPattern ) ) ); + return !sPrefix.isEmpty(); +} + +bool ODsnTypeCollection::isConnectionUrlRequired(std::u16string_view _sURL) const +{ + OUString sRet; + OUString sOldPattern; + for (auto const& dsnPrefix : m_aDsnPrefixes) + { + WildCard aWildCard(dsnPrefix); + if ( sOldPattern.getLength() < dsnPrefix.getLength() && aWildCard.Matches(_sURL) ) + { + sRet = dsnPrefix; + sOldPattern = dsnPrefix; + } + } + return !sRet.isEmpty() && sRet[sRet.getLength()-1] == '*'; +} + +OUString ODsnTypeCollection::getMediaType(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("MediaType",OUString()); +} + +OUString ODsnTypeCollection::getDatasourcePrefixFromMediaType(std::u16string_view _sMediaType,std::u16string_view _sExtension) +{ + OUString sURL, sFallbackURL; + const uno::Sequence< OUString > aURLs = m_aDriverConfig.getURLs(); + const OUString* pIter = aURLs.getConstArray(); + const OUString* pEnd = pIter + aURLs.getLength(); + for(;pIter != pEnd;++pIter ) + { + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(*pIter); + if ( aFeatures.getOrDefault("MediaType",OUString()) == _sMediaType ) + { + const OUString sFileExtension = aFeatures.getOrDefault("Extension",OUString()); + if ( _sExtension == sFileExtension ) + { + sURL = *pIter; + break; + } + if ( sFileExtension.isEmpty() && !_sExtension.empty() ) + sFallbackURL = *pIter; + } + } + + if ( sURL.isEmpty() && !sFallbackURL.isEmpty() ) + sURL = sFallbackURL; + + sURL = comphelper::string::stripEnd(sURL, '*'); + return sURL; +} + +bool ODsnTypeCollection::isShowPropertiesEnabled( const OUString& _sURL ) +{ + return !( _sURL.startsWithIgnoreAsciiCase("sdbc:embedded:hsqldb") + || _sURL.startsWithIgnoreAsciiCase("sdbc:embedded:firebird") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:outlook") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:outlookexp") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:mozilla:") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:kab") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:evolution:local") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:evolution:groupwise") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:evolution:ldap") + || _sURL.startsWithIgnoreAsciiCase("sdbc:address:macab") ); +} + +void ODsnTypeCollection::extractHostNamePort(const OUString& _rDsn,OUString& _sDatabaseName,OUString& _rsHostname,sal_Int32& _nPortNumber) const +{ + OUString sUrl = cutPrefix(_rDsn); + if ( _rDsn.startsWithIgnoreAsciiCase("jdbc:oracle:thin:") ) + { + lcl_extractHostAndPort(sUrl,_rsHostname,_nPortNumber); + const sal_Int32 nUrlTokens {comphelper::string::getTokenCount(sUrl, ':')}; + if ( _rsHostname.isEmpty() && nUrlTokens == 2 ) + { + _nPortNumber = -1; + _rsHostname = sUrl.getToken(0,':'); + } + if ( !_rsHostname.isEmpty() ) + _rsHostname = _rsHostname.copy(_rsHostname.lastIndexOf('@')+1); + _sDatabaseName = sUrl.copy(sUrl.lastIndexOf(':')+1); + } + else if ( _rDsn.startsWithIgnoreAsciiCase("sdbc:address:ldap:") ) + { + lcl_extractHostAndPort(sUrl,_sDatabaseName,_nPortNumber); + } + else if ( _rDsn.startsWithIgnoreAsciiCase("sdbc:mysql:mysqlc:") + || _rDsn.startsWithIgnoreAsciiCase("sdbc:mysql:jdbc:") ) + { + lcl_extractHostAndPort(sUrl,_rsHostname,_nPortNumber); + + const sal_Int32 nUrlTokens {comphelper::string::getTokenCount(sUrl, '/')}; + if ( _nPortNumber == -1 && _rsHostname.isEmpty() && nUrlTokens == 2 ) + _rsHostname = sUrl.getToken(0,'/'); + _sDatabaseName = sUrl.copy(sUrl.lastIndexOf('/')+1); + } + else if ( _rDsn.startsWithIgnoreAsciiCase("sdbc:ado:access:Provider=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=") + || _rDsn.startsWithIgnoreAsciiCase("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=") ) + { + OUString sNewFileName; + if ( ::osl::FileBase::getFileURLFromSystemPath( sUrl, sNewFileName ) == ::osl::FileBase::E_None ) + { + _sDatabaseName = sNewFileName; + } + } +} + +OUString ODsnTypeCollection::getJavaDriverClass(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getProperties(_sURL); + return aFeatures.getOrDefault("JavaDriverClass",OUString()); +} + +bool ODsnTypeCollection::isFileSystemBased(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("FileSystemBased",false); +} + +bool ODsnTypeCollection::supportsTableCreation(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("SupportsTableCreation",false); +} + +bool ODsnTypeCollection::supportsColumnDescription(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("SupportsColumnDescription",false); +} + +bool ODsnTypeCollection::supportsBrowsing(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("SupportsBrowsing",false); +} + +bool ODsnTypeCollection::supportsDBCreation(std::u16string_view _sURL) const +{ + const ::comphelper::NamedValueCollection& aFeatures = m_aDriverConfig.getMetaData(_sURL); + return aFeatures.getOrDefault("SupportsDBCreation",false); +} + +Sequence<PropertyValue> ODsnTypeCollection::getDefaultDBSettings( std::u16string_view _sURL ) const +{ + const ::comphelper::NamedValueCollection& aProperties = m_aDriverConfig.getProperties(_sURL); + return aProperties.getPropertyValues(); +} + +bool ODsnTypeCollection::isEmbeddedDatabase( std::u16string_view _sURL ) +{ + return o3tl::starts_with( _sURL, u"sdbc:embedded:" ); +} + +OUString ODsnTypeCollection::getEmbeddedDatabase() +{ + if (!HAVE_FEATURE_JAVA || officecfg::Office::Common::Misc::ExperimentalMode::get()) + return "sdbc:embedded:firebird"; + else + return "sdbc:embedded:hsqldb"; +} + + +DATASOURCE_TYPE ODsnTypeCollection::determineType(std::u16string_view _rDsn) const +{ + OUString sDsn(comphelper::string::stripEnd(_rDsn, '*')); + sal_Int32 nSeparator = sDsn.indexOf(u':'); + if (-1 == nSeparator) + { + if (!sDsn.isEmpty()) + { + // there should be at least one such separator + OSL_FAIL("ODsnTypeCollection::implDetermineType : missing the colon !"); + } + + return DST_UNKNOWN; + } + + // find first : + if (sDsn.startsWithIgnoreAsciiCase("jdbc:oracle:thin:")) + return DST_ORACLE_JDBC; + + if (sDsn.startsWithIgnoreAsciiCase("jdbc:")) + return DST_JDBC; + + if (sDsn.equalsIgnoreAsciiCase("sdbc:embedded:hsqldb")) + return DST_EMBEDDED_HSQLDB; + + if (sDsn.equalsIgnoreAsciiCase("sdbc:embedded:firebird")) + return DST_EMBEDDED_FIREBIRD; + + // find second : + nSeparator = sDsn.indexOf(u':', nSeparator + 1); + if (-1 == nSeparator) + { + // at the moment only jdbc is allowed to have just one separator + OSL_FAIL("ODsnTypeCollection::implDetermineType : missing the second colon !"); + return DST_UNKNOWN; + } + + if (sDsn.startsWithIgnoreAsciiCase("sdbc:ado:")) + { + if (sDsn.startsWithIgnoreAsciiCase("sdbc:ado:access:")) + { + if (sDsn.startsWithIgnoreAsciiCase("sdbc:ado:access:Provider=Microsoft.ACE.OLEDB.12.0;")) + return DST_MSACCESS_2007; + else + return DST_MSACCESS; + } + return DST_ADO; + } + + struct KnownPrefix + { + const OUString sPrefix; + const DATASOURCE_TYPE eType; + const bool bMatchComplete; + + KnownPrefix( const OUString &_s, const DATASOURCE_TYPE _t, const bool _m ) + :sPrefix( _s ) + ,eType ( _t ) + ,bMatchComplete( _m ) + { + } + + bool match( const OUString &url) const + { + if(bMatchComplete) + { + return url.equalsIgnoreAsciiCase(sPrefix); + } + else + { + return url.startsWithIgnoreAsciiCase(sPrefix); + } + } + }; + const KnownPrefix aKnowPrefixes[] = + { + KnownPrefix( "sdbc:calc:", DST_CALC, false ), + KnownPrefix( "sdbc:writer:", DST_WRITER, false ), + KnownPrefix( "sdbc:flat:", DST_FLAT, false ), + KnownPrefix( "sdbc:odbc:", DST_ODBC, false ), + KnownPrefix( "sdbc:dbase:", DST_DBASE, false ), + KnownPrefix( "sdbc:firebird:", DST_FIREBIRD, false ), + KnownPrefix( "sdbc:mysql:odbc:", DST_MYSQL_ODBC, false ), + KnownPrefix( "sdbc:mysql:jdbc:", DST_MYSQL_JDBC, false ), + KnownPrefix( "sdbc:mysql:mysqlc:", DST_MYSQL_NATIVE, false ), + KnownPrefix( "sdbc:mysqlc:", DST_MYSQL_NATIVE_DIRECT,false ), + KnownPrefix( "sdbc:postgresql:", DST_POSTGRES ,false ), + + KnownPrefix( "sdbc:address:mozilla:", DST_MOZILLA, true ), + KnownPrefix( "sdbc:address:thunderbird:", DST_THUNDERBIRD, true ), + KnownPrefix( "sdbc:address:ldap:", DST_LDAP, true ), + KnownPrefix( "sdbc:address:outlook", DST_OUTLOOK, true ), + KnownPrefix( "sdbc:address:outlookexp", DST_OUTLOOKEXP, true ), + KnownPrefix( "sdbc:address:evolution:ldap", DST_EVOLUTION_LDAP, true ), + KnownPrefix( "sdbc:address:evolution:groupwise",DST_EVOLUTION_GROUPWISE,true ), + KnownPrefix( "sdbc:address:evolution:local", DST_EVOLUTION, true ), + KnownPrefix( "sdbc:address:kab", DST_KAB, true ), + KnownPrefix( "sdbc:address:macab", DST_MACAB, true ) + }; + + for (const auto & aKnowPrefixe : aKnowPrefixes) + { + if( aKnowPrefixe.match(sDsn) ) + { + return aKnowPrefixe.eType; + } + } + + return DST_UNKNOWN; +} + +void ODsnTypeCollection::fillPageIds(std::u16string_view _sURL,std::vector<sal_Int16>& _rOutPathIds) const +{ + DATASOURCE_TYPE eType = determineType(_sURL); + switch(eType) + { + case DST_ADO: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_ADO); + break; + case DST_DBASE: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_DBASE); + break; + case DST_FLAT: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_TEXT); + break; + case DST_CALC: + case DST_WRITER: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_DOCUMENT_OR_SPREADSHEET); + break; + case DST_ODBC: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_ODBC); + break; + case DST_JDBC: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_JDBC); + break; + case DST_MYSQL_ODBC: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_INTRO); + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_ODBC); + break; + case DST_MYSQL_JDBC: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_INTRO); + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_JDBC); + break; + case DST_MYSQL_NATIVE: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_INTRO); + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MYSQL_NATIVE); + break; + case DST_ORACLE_JDBC: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_ORACLE); + break; + case DST_LDAP: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_LDAP); + break; + case DST_MSACCESS: + case DST_MSACCESS_2007: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_MSACCESS); + break; + case DST_OUTLOOKEXP: + case DST_OUTLOOK: + case DST_MOZILLA: + case DST_THUNDERBIRD: + case DST_EVOLUTION: + case DST_EVOLUTION_GROUPWISE: + case DST_EVOLUTION_LDAP: + case DST_KAB: + case DST_MACAB: + case DST_EMBEDDED_HSQLDB: + case DST_EMBEDDED_FIREBIRD: + break; + default: + _rOutPathIds.push_back(PAGE_DBSETUPWIZARD_USERDEFINED); + break; + } +} + +OUString ODsnTypeCollection::getType(std::u16string_view _sURL) const +{ + OUString sOldPattern; + for (auto const& dsnPrefix : m_aDsnPrefixes) + { + WildCard aWildCard(dsnPrefix); + if ( sOldPattern.getLength() < dsnPrefix.getLength() && aWildCard.Matches(_sURL) ) + { + sOldPattern = dsnPrefix; + } + } + return sOldPattern; +} + +sal_Int32 ODsnTypeCollection::getIndexOf(std::u16string_view _sURL) const +{ + sal_Int32 nRet = -1; + OUString sOldPattern; + sal_Int32 i = 0; + for (auto const& dsnPrefix : m_aDsnPrefixes) + { + WildCard aWildCard(dsnPrefix); + if ( sOldPattern.getLength() < dsnPrefix.getLength() && aWildCard.Matches(_sURL) ) + { + nRet = i; + sOldPattern = dsnPrefix; + } + ++i; + } + + return nRet; +} + +sal_Int32 ODsnTypeCollection::size() const +{ + return m_aDsnPrefixes.size(); +} + +// ODsnTypeCollection::TypeIterator +ODsnTypeCollection::TypeIterator::TypeIterator(const ODsnTypeCollection* _pContainer, sal_Int32 _nInitialPos) + :m_pContainer(_pContainer) + ,m_nPosition(_nInitialPos) +{ + OSL_ENSURE(m_pContainer, "ODsnTypeCollection::TypeIterator::TypeIterator : invalid container!"); +#if OSL_DEBUG_LEVEL > 0 + ++const_cast<ODsnTypeCollection*>(m_pContainer)->m_nLivingIterators; +#endif +} + +ODsnTypeCollection::TypeIterator::TypeIterator(const TypeIterator& _rSource) + :m_pContainer(_rSource.m_pContainer) + ,m_nPosition(_rSource.m_nPosition) +{ +#if OSL_DEBUG_LEVEL > 0 + ++const_cast<ODsnTypeCollection*>(m_pContainer)->m_nLivingIterators; +#endif +} + +ODsnTypeCollection::TypeIterator::~TypeIterator() +{ +#if OSL_DEBUG_LEVEL > 0 + --const_cast<ODsnTypeCollection*>(m_pContainer)->m_nLivingIterators; +#endif +} + +OUString const & ODsnTypeCollection::TypeIterator::getDisplayName() const +{ + OSL_ENSURE(m_nPosition < static_cast<sal_Int32>(m_pContainer->m_aDsnTypesDisplayNames.size()), "ODsnTypeCollection::TypeIterator::getDisplayName : invalid position!"); + return m_pContainer->m_aDsnTypesDisplayNames[m_nPosition]; +} + +OUString const & ODsnTypeCollection::TypeIterator::getURLPrefix() const +{ + OSL_ENSURE(m_nPosition < static_cast<sal_Int32>(m_pContainer->m_aDsnPrefixes.size()), "ODsnTypeCollection::TypeIterator::getDisplayName : invalid position!"); + return m_pContainer->m_aDsnPrefixes[m_nPosition]; +} + +const ODsnTypeCollection::TypeIterator& ODsnTypeCollection::TypeIterator::operator++() +{ + OSL_ENSURE(m_nPosition < static_cast<sal_Int32>(m_pContainer->m_aDsnTypesDisplayNames.size()), "ODsnTypeCollection::TypeIterator::operator++ : invalid position!"); + if (m_nPosition < static_cast<sal_Int32>(m_pContainer->m_aDsnTypesDisplayNames.size())) + ++m_nPosition; + return *this; +} + +bool operator==(const ODsnTypeCollection::TypeIterator& lhs, const ODsnTypeCollection::TypeIterator& rhs) +{ + return (lhs.m_pContainer == rhs.m_pContainer) && (lhs.m_nPosition == rhs.m_nPosition); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/migrwarndlg.cxx b/dbaccess/source/core/misc/migrwarndlg.cxx new file mode 100644 index 000000000..d1712fba3 --- /dev/null +++ b/dbaccess/source/core/misc/migrwarndlg.cxx @@ -0,0 +1,22 @@ +/* -*- 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/. + */ + +#include <migrwarndlg.hxx> + +namespace dbaccess +{ +MigrationWarnDialog::MigrationWarnDialog(weld::Window* pParent) + : MessageDialogController(pParent, "dbaccess/ui/migrwarndlg.ui", "MigrationWarnDialog") + , m_xLater(m_xBuilder->weld_button("no")) +{ + m_xLater->grab_focus(); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/objectnameapproval.cxx b/dbaccess/source/core/misc/objectnameapproval.cxx new file mode 100644 index 000000000..3ec15caaa --- /dev/null +++ b/dbaccess/source/core/misc/objectnameapproval.cxx @@ -0,0 +1,74 @@ +/* -*- 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 <objectnameapproval.hxx> + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/sdb/tools/XConnectionTools.hpp> +#include <com/sun/star/sdb/CommandType.hpp> + +#include <cppuhelper/weakref.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::uno::WeakReference; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::sdb::tools::XConnectionTools; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::sdb::tools::XObjectNames; + using ::com::sun::star::uno::XInterface; + + namespace CommandType = com::sun::star::sdb::CommandType; + + // ObjectNameApproval_Impl + struct ObjectNameApproval_Impl + { + WeakReference< XConnection > aConnection; + sal_Int32 nCommandType; + }; + + // ObjectNameApproval + ObjectNameApproval::ObjectNameApproval( const Reference< XConnection >& _rxConnection, ObjectType _eType ) + :m_pImpl( new ObjectNameApproval_Impl ) + { + m_pImpl->aConnection = _rxConnection; + m_pImpl->nCommandType = _eType == TypeQuery ? CommandType::QUERY : CommandType::TABLE; + } + + ObjectNameApproval::~ObjectNameApproval() + { + } + + void ObjectNameApproval::approveElement( const OUString& _rName ) + { + Reference< XConnection > xConnection( m_pImpl->aConnection ); + if ( !xConnection.is() ) + throw DisposedException(); + + Reference< XConnectionTools > xConnectionTools( xConnection, UNO_QUERY_THROW ); + Reference< XObjectNames > xObjectNames( xConnectionTools->getObjectNames(), css::uno::UNO_SET_THROW ); + xObjectNames->checkNameForCreate( m_pImpl->nCommandType, _rName ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/sdbcoretools.cxx b/dbaccess/source/core/misc/sdbcoretools.cxx new file mode 100644 index 000000000..1789aea84 --- /dev/null +++ b/dbaccess/source/core/misc/sdbcoretools.cxx @@ -0,0 +1,142 @@ +/* -*- 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 <sdbcoretools.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <com/sun/star/task/InteractionRequestStringResolver.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/ElementModes.hpp> + +#include <tools/diagnose_ex.h> +#include <comphelper/interaction.hxx> +#include <rtl/ref.hxx> + +namespace dbaccess +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::io; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::task; + using namespace ::com::sun::star::embed; + using namespace ::com::sun::star::container; + + void notifyDataSourceModified(const css::uno::Reference< css::uno::XInterface >& _rxObject) + { + Reference< XInterface > xDs = getDataSource( _rxObject ); + Reference<XDocumentDataSource> xDocumentDataSource(xDs,UNO_QUERY); + if ( xDocumentDataSource.is() ) + xDs = xDocumentDataSource->getDatabaseDocument(); + Reference< XModifiable > xModi( xDs, UNO_QUERY ); + if ( xModi.is() ) + xModi->setModified(true); + } + + Reference< XInterface > getDataSource( const Reference< XInterface >& _rxDependentObject ) + { + Reference< XInterface > xParent = _rxDependentObject; + Reference< XInterface > xReturn; + while( xParent.is() ) + { + xReturn = xParent; + Reference<XChild> xChild(xParent,UNO_QUERY); + xParent.set(xChild.is() ? xChild->getParent() : Reference< XInterface >(),UNO_QUERY); + } + return xReturn; + } + + OUString extractExceptionMessage( const Reference<XComponentContext> & _rContext, const Any& _rError ) + { + OUString sDisplayMessage; + + try + { + Reference< XInteractionRequestStringResolver > xStringResolver = InteractionRequestStringResolver::create(_rContext); + + ::rtl::Reference pRequest( new ::comphelper::OInteractionRequest( _rError ) ); + ::rtl::Reference pApprove( new ::comphelper::OInteractionApprove ); + pRequest->addContinuation( pApprove ); + Optional< OUString > aMessage = xStringResolver->getStringFromInformationalRequest( pRequest ); + if ( aMessage.IsPresent ) + sDisplayMessage = aMessage.Value; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( sDisplayMessage.isEmpty() ) + { + Exception aExcept; + _rError >>= aExcept; + + sDisplayMessage = _rError.getValueTypeName() + + ":\n" + + aExcept.Message; + } + + return sDisplayMessage; + } + + namespace tools::stor { + + bool storageIsWritable_nothrow( const Reference< XStorage >& _rxStorage ) + { + if ( !_rxStorage.is() ) + return false; + + sal_Int32 nMode = ElementModes::READ; + try + { + Reference< XPropertySet > xStorageProps( _rxStorage, UNO_QUERY_THROW ); + xStorageProps->getPropertyValue( "OpenMode" ) >>= nMode; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return ( nMode & ElementModes::WRITE ) != 0; + } + + bool commitStorageIfWriteable( const Reference< XStorage >& _rxStorage ) + { + bool bSuccess = false; + Reference< XTransactedObject > xTrans( _rxStorage, UNO_QUERY ); + if ( xTrans.is() ) + { + if ( storageIsWritable_nothrow( _rxStorage ) ) + xTrans->commit(); + bSuccess = true; + } + return bSuccess; + } + +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/misc/veto.cxx b/dbaccess/source/core/misc/veto.cxx new file mode 100644 index 000000000..d2596d4e1 --- /dev/null +++ b/dbaccess/source/core/misc/veto.cxx @@ -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 . + */ + +#include <veto.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Any; + + // Veto + Veto::Veto( const Any& _rDetails ) + :m_aDetails( _rDetails ) + { + } + + Veto::~Veto() + { + } + + OUString SAL_CALL Veto::getReason() + { + return OUString(); + } + + Any SAL_CALL Veto::getDetails() + { + return m_aDetails; + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/dbdocrecovery.cxx b/dbaccess/source/core/recovery/dbdocrecovery.cxx new file mode 100644 index 000000000..db7bb53bd --- /dev/null +++ b/dbaccess/source/core/recovery/dbdocrecovery.cxx @@ -0,0 +1,347 @@ +/* -*- 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 <recovery/dbdocrecovery.hxx> +#include <sdbcoretools.hxx> +#include "storagetextstream.hxx" +#include "subcomponentrecovery.hxx" +#include "subcomponents.hxx" + +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/io/TextInputStream.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using css::uno::Reference; + using css::uno::UNO_QUERY; + using css::uno::UNO_QUERY_THROW; + using css::uno::UNO_SET_THROW; + using css::uno::Exception; + using css::uno::Sequence; + using css::uno::XComponentContext; + using css::embed::XStorage; + using css::frame::XController; + using css::sdb::application::XDatabaseDocumentUI; + using css::lang::XComponent; + using css::io::XStream; + using css::io::TextInputStream; + using css::io::XTextInputStream2; + using css::util::XModifiable; + + namespace ElementModes = css::embed::ElementModes; + + // helpers + namespace + { + void lcl_getPersistentRepresentation( const MapStringToCompDesc::value_type& i_rComponentDesc, OUStringBuffer& o_rBuffer ) + { + o_rBuffer.append( i_rComponentDesc.first ); + o_rBuffer.append( '=' ); + o_rBuffer.append( i_rComponentDesc.second.sName ); + o_rBuffer.append( ',' ); + o_rBuffer.append( sal_Unicode( i_rComponentDesc.second.bForEditing ? '1' : '0' ) ); + } + + bool lcl_extractCompDesc( const OUString& i_rIniLine, OUString& o_rStorName, SubComponentDescriptor& o_rCompDesc ) + { + const sal_Int32 nEqualSignPos = i_rIniLine.indexOf( '=' ); + if ( nEqualSignPos < 1 ) + { + OSL_FAIL( "lcl_extractCompDesc: invalid map file entry - unexpected pos of '='" ); + return false; + } + o_rStorName = i_rIniLine.copy( 0, nEqualSignPos ); + + const sal_Int32 nCommaPos = i_rIniLine.lastIndexOf( ',' ); + if ( nCommaPos != i_rIniLine.getLength() - 2 ) + { + OSL_FAIL( "lcl_extractCompDesc: invalid map file entry - unexpected pos of ','" ); + return false; + } + o_rCompDesc.sName = i_rIniLine.copy( nEqualSignPos + 1, nCommaPos - nEqualSignPos - 1 ); + o_rCompDesc.bForEditing = ( i_rIniLine[ nCommaPos + 1 ] == '1' ); + return true; + } + + constexpr OUStringLiteral sRecoveryDataSubStorageName = u"recovery"; + + constexpr OUStringLiteral sObjectMapStreamName = u"storage-component-map.ini"; + + void lcl_writeObjectMap_throw( const Reference<XComponentContext> & i_rContext, const Reference< XStorage >& i_rStorage, + const MapStringToCompDesc& i_mapStorageToCompDesc ) + { + if ( i_mapStorageToCompDesc.empty() ) + // nothing to do + return; + + StorageTextOutputStream aTextOutput( i_rContext, i_rStorage, sObjectMapStreamName ); + + aTextOutput.writeLine( "[storages]" ); + + for (auto const& elem : i_mapStorageToCompDesc) + { + OUStringBuffer aLine; + lcl_getPersistentRepresentation(elem, aLine); + + aTextOutput.writeLine( aLine.makeStringAndClear() ); + } + + aTextOutput.writeLine(); + } + + bool lcl_isSectionStart( const OUString& i_rIniLine, OUString& o_rSectionName ) + { + const sal_Int32 nLen = i_rIniLine.getLength(); + if ( i_rIniLine.startsWith("[") && i_rIniLine.endsWith("]") ) + { + o_rSectionName = i_rIniLine.copy( 1, nLen -2 ); + return true; + } + return false; + } + + void lcl_stripTrailingLineFeed( OUString& io_rLine ) + { + const sal_Int32 nLen = io_rLine.getLength(); + if ( io_rLine.endsWith("\n") ) + io_rLine = io_rLine.copy( 0, nLen - 1 ); + } + + void lcl_readObjectMap_throw( const Reference<XComponentContext> & i_rxContext, const Reference< XStorage >& i_rStorage, + MapStringToCompDesc& o_mapStorageToObjectName ) + { + ENSURE_OR_THROW( i_rStorage.is(), "invalid storage" ); + if ( !i_rStorage->hasByName( sObjectMapStreamName ) ) + { // nothing to do, though suspicious + OSL_FAIL( "lcl_readObjectMap_throw: if there's no map file, then there's expected to be no storage, too!" ); + return; + } + + Reference< XStream > xIniStream( i_rStorage->openStreamElement( + sObjectMapStreamName, ElementModes::READ ), UNO_SET_THROW ); + + Reference< XTextInputStream2 > xTextInput = TextInputStream::create( i_rxContext ); + xTextInput->setEncoding( "UTF-8" ); + xTextInput->setInputStream( xIniStream->getInputStream() ); + + OUString sCurrentSection; + bool bCurrentSectionIsKnownToBeUnsupported = true; + while ( !xTextInput->isEOF() ) + { + OUString sLine = xTextInput->readLine(); + lcl_stripTrailingLineFeed( sLine ); + + if ( sLine.isEmpty() ) + continue; + + if ( lcl_isSectionStart( sLine, sCurrentSection ) ) + { + bCurrentSectionIsKnownToBeUnsupported = false; + continue; + } + + if ( bCurrentSectionIsKnownToBeUnsupported ) + continue; + + // the only section we support so far is "storages" + if ( sCurrentSection != "storages" ) + { + bCurrentSectionIsKnownToBeUnsupported = true; + continue; + } + + OUString sStorageName; + SubComponentDescriptor aCompDesc; + if ( !lcl_extractCompDesc( sLine, sStorageName, aCompDesc ) ) + continue; + o_mapStorageToObjectName[ sStorageName ] = aCompDesc; + } + } + + void lcl_markModified( const Reference< XComponent >& i_rSubComponent ) + { + const Reference< XModifiable > xModify( i_rSubComponent, UNO_QUERY ); + if ( !xModify.is() ) + { + OSL_FAIL( "lcl_markModified: unhandled case!" ); + return; + } + + xModify->setModified( true ); + } + } + + // DatabaseDocumentRecovery_Data + struct DatabaseDocumentRecovery_Data + { + const Reference<XComponentContext> aContext; + + explicit DatabaseDocumentRecovery_Data( const Reference<XComponentContext> & i_rContext ) + :aContext( i_rContext ) + { + } + }; + + // DatabaseDocumentRecovery + DatabaseDocumentRecovery::DatabaseDocumentRecovery( const Reference<XComponentContext> & i_rContext ) + :m_pData( new DatabaseDocumentRecovery_Data( i_rContext ) ) + { + } + + DatabaseDocumentRecovery::~DatabaseDocumentRecovery() + { + } + + void DatabaseDocumentRecovery::saveModifiedSubComponents( const Reference< XStorage >& i_rTargetStorage, + const std::vector< Reference< XController > >& i_rControllers ) + { + ENSURE_OR_THROW( i_rTargetStorage.is(), "invalid document storage" ); + + // create a sub storage for recovery data + if ( i_rTargetStorage->hasByName( sRecoveryDataSubStorageName ) ) + i_rTargetStorage->removeElement( sRecoveryDataSubStorageName ); + Reference< XStorage > xRecoveryStorage = i_rTargetStorage->openStorageElement( sRecoveryDataSubStorageName, ElementModes::READWRITE ); + + // store recovery data for open sub components of the given controller(s) + if ( !i_rControllers.empty() ) + { + ENSURE_OR_THROW( i_rControllers.size() == 1, "can't handle more than one controller" ); + // At the moment, there can be only one view to a database document. If we ever allow for more than this, + // then we need a concept for sub documents opened from different controllers (i.e. two document views, + // and the user opens the very same form in both views). And depending on this, we need a concept for + // how those are saved to the recovery file. + + MapCompTypeToCompDescs aMapCompDescs; + + for (auto const& controller : i_rControllers) + { + Reference< XDatabaseDocumentUI > xDatabaseUI(controller, UNO_QUERY_THROW); + const Sequence< Reference< XComponent > > aComponents( xDatabaseUI->getSubComponents() ); + + for ( auto const & component : aComponents ) + { + SubComponentRecovery aComponentRecovery( m_pData->aContext, xDatabaseUI, component ); + aComponentRecovery.saveToRecoveryStorage( xRecoveryStorage, aMapCompDescs ); + } + } + + for (auto const& elem : aMapCompDescs) + { + Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement( + SubComponentRecovery::getComponentsStorageName( elem.first ), ElementModes::WRITE | ElementModes::NOCREATE ) ); + lcl_writeObjectMap_throw( m_pData->aContext, xComponentsStor, elem.second ); + tools::stor::commitStorageIfWriteable( xComponentsStor ); + } + } + + // commit the recovery storage + tools::stor::commitStorageIfWriteable( xRecoveryStorage ); + } + + void DatabaseDocumentRecovery::recoverSubDocuments( const Reference< XStorage >& i_rDocumentStorage, + const Reference< XController >& i_rTargetController ) + { + ENSURE_OR_THROW( i_rDocumentStorage.is(), "illegal document storage" ); + Reference< XDatabaseDocumentUI > xDocumentUI( i_rTargetController, UNO_QUERY_THROW ); + + if ( !i_rDocumentStorage->hasByName( sRecoveryDataSubStorageName ) ) + // that's allowed + return; + + // the "recovery" sub storage + Reference< XStorage > xRecoveryStorage = i_rDocumentStorage->openStorageElement( sRecoveryDataSubStorageName, ElementModes::READ ); + + // read the map from sub storages to object names + MapCompTypeToCompDescs aMapCompDescs; + const SubComponentType aKnownTypes[] = { TABLE, QUERY, FORM, REPORT, RELATION_DESIGN }; + for (SubComponentType aKnownType : aKnownTypes) + { + if ( !xRecoveryStorage->hasByName( SubComponentRecovery::getComponentsStorageName( aKnownType ) ) ) + continue; + + Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement( + SubComponentRecovery::getComponentsStorageName( aKnownType ), ElementModes::READ ) ); + lcl_readObjectMap_throw( m_pData->aContext, xComponentsStor, aMapCompDescs[ aKnownType ] ); + xComponentsStor->dispose(); + } + + // recover all sub components as indicated by the map + for (auto const& elemMapCompDescs : aMapCompDescs) + { + const SubComponentType eComponentType = elemMapCompDescs.first; + + // the storage for all components of the current type + Reference< XStorage > xComponentsStor( xRecoveryStorage->openStorageElement( + SubComponentRecovery::getComponentsStorageName( eComponentType ), ElementModes::READ ), UNO_SET_THROW ); + + // loop through all components of this type + for (auto const& elem : elemMapCompDescs.second) + { + const OUString sComponentName(elem.second.sName); + if ( !xComponentsStor->hasByName(elem.first) ) + { + SAL_WARN( "dbaccess", + "DatabaseDocumentRecovery::recoverSubDocuments: inconsistent recovery storage: storage '" << + elem.first << + "' not found in '" << + SubComponentRecovery::getComponentsStorageName( eComponentType ) << + "', but required per map file!" ); + continue; + } + + // the controller needs to have a connection to be able to open sub components + if ( !xDocumentUI->isConnected() ) + xDocumentUI->connect(); + + // recover the single component + Reference< XStorage > xCompStor( xComponentsStor->openStorageElement( elem.first, ElementModes::READ ) ); + SubComponentRecovery aComponentRecovery( m_pData->aContext, xDocumentUI, eComponentType ); + Reference< XComponent > xSubComponent( aComponentRecovery.recoverFromStorage( xCompStor, sComponentName, elem.second.bForEditing ) ); + + // at the moment, we only store, during session save, sub components which are modified. So, set this + // recovered sub component to "modified", too. + lcl_markModified( xSubComponent ); + } + + xComponentsStor->dispose(); + } + + xRecoveryStorage->dispose(); + + // now that we successfully recovered, removed the "recovery" sub storage + try + { + i_rDocumentStorage->removeElement( sRecoveryDataSubStorageName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/settingsimport.cxx b/dbaccess/source/core/recovery/settingsimport.cxx new file mode 100644 index 000000000..821dea045 --- /dev/null +++ b/dbaccess/source/core/recovery/settingsimport.cxx @@ -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 . + */ + +#include "settingsimport.hxx" + +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <sax/tools/converter.hxx> +#include <xmloff/xmltoken.hxx> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Any; + using ::com::sun::star::xml::sax::XAttributeList; + + // SettingsImport + SettingsImport::SettingsImport() + { + } + + SettingsImport::~SettingsImport() + { + } + + void SettingsImport::startElement( const Reference< XAttributeList >& i_rAttributes ) + { + // find the name of the setting + if ( i_rAttributes.is() ) + { + m_sItemName = i_rAttributes->getValueByName( "config:name" ); + m_sItemType = i_rAttributes->getValueByName( "config:type" ); + } + } + + void SettingsImport::endElement() + { + } + + void SettingsImport::characters( std::u16string_view i_rCharacters ) + { + m_aCharacters.append( i_rCharacters ); + } + + void SettingsImport::split( const OUString& i_rElementName, OUString& o_rNamespace, OUString& o_rLocalName ) + { + o_rNamespace.clear(); + o_rLocalName = i_rElementName; + const sal_Int32 nSeparatorPos = i_rElementName.indexOf( ':' ); + if ( nSeparatorPos > -1 ) + { + o_rNamespace = i_rElementName.copy( 0, nSeparatorPos ); + o_rLocalName = i_rElementName.copy( nSeparatorPos + 1 ); + } + + OSL_ENSURE( o_rNamespace == "config", "SettingsImport::split: unexpected namespace!" ); + // our recovery file is kind of hand-made, so there shouldn't be anything else than "config". + // If there is, then just ignore it ... + } + + // IgnoringSettingsImport + ::rtl::Reference< SettingsImport > IgnoringSettingsImport::nextState( const OUString& ) + { + return this; + } + + // OfficeSettingsImport + OfficeSettingsImport::OfficeSettingsImport( ::comphelper::NamedValueCollection& o_rSettings ) + :m_rSettings( o_rSettings ) + { + } + + OfficeSettingsImport::~OfficeSettingsImport() + { + } + + ::rtl::Reference< SettingsImport > OfficeSettingsImport::nextState( const OUString& i_rElementName ) + { + // separate the namespace part from the element name + OUString sNamespace; + OUString sLocalName; + split( i_rElementName, sNamespace, sLocalName ); + + if ( sLocalName == "config-item-set" ) + return new ConfigItemSetImport( m_rSettings ); + + SAL_WARN( "dbaccess", "unknown (or unsupported at this place) element name '" + << i_rElementName + << "', ignoring"); + return new IgnoringSettingsImport; + } + + // ConfigItemImport + ConfigItemImport::ConfigItemImport( ::comphelper::NamedValueCollection& o_rSettings ) + :m_rSettings( o_rSettings ) + { + } + + ConfigItemImport::~ConfigItemImport() + { + } + + ::rtl::Reference< SettingsImport > ConfigItemImport::nextState( const OUString& ) + { + OSL_FAIL( "ConfigItemImport::nextState: unexpected: this class is responsible for child-less items only!" ); + return new IgnoringSettingsImport; + } + + void ConfigItemImport::endElement() + { + SettingsImport::endElement(); + + const OUString sItemName( getItemName() ); + ENSURE_OR_RETURN_VOID( !sItemName.isEmpty(), "no item name -> no item value" ); + Any aValue; + getItemValue( aValue ); + m_rSettings.put( sItemName, aValue ); + } + + void ConfigItemImport::getItemValue( css::uno::Any& o_rValue ) const + { + o_rValue.clear(); + + // the characters building up th evalue + std::u16string_view sValue = getAccumulatedCharacters(); + + const OUString& rItemType( getItemType() ); + ENSURE_OR_RETURN_VOID( !rItemType.isEmpty(), "no item type -> no item value" ); + + if ( ::xmloff::token::IsXMLToken( rItemType, ::xmloff::token::XML_INT ) ) + { + sal_Int32 nValue(0); + if (::sax::Converter::convertNumber( nValue, sValue )) + { + o_rValue <<= nValue; + } + else + { + OSL_FAIL( "ConfigItemImport::getItemValue: could not convert an int value!" ); + } + } + else if ( ::xmloff::token::IsXMLToken( rItemType, ::xmloff::token::XML_BOOLEAN ) ) + { + bool bValue(false); + if (::sax::Converter::convertBool( bValue, sValue )) + { + o_rValue <<= bValue; + } + else + { + OSL_FAIL( "ConfigItemImport::getItemValue: could not convert a boolean value!" ); + } + } + else if ( ::xmloff::token::IsXMLToken( rItemType, ::xmloff::token::XML_STRING ) ) + { + o_rValue <<= OUString(sValue); + } + else + { + SAL_WARN( "dbaccess", "ConfigItemImport::getItemValue: unsupported item type '" + << rItemType << "', ignoring"); + } + } + + // ConfigItemSetImport + ConfigItemSetImport::ConfigItemSetImport( ::comphelper::NamedValueCollection& o_rSettings ) + :ConfigItemImport( o_rSettings ) + { + } + + ConfigItemSetImport::~ConfigItemSetImport() + { + } + + ::rtl::Reference< SettingsImport > ConfigItemSetImport::nextState( const OUString& i_rElementName ) + { + // separate the namespace part from the element name + OUString sNamespace; + OUString sLocalName; + split( i_rElementName, sNamespace, sLocalName ); + + if ( sLocalName == "config-item-set" ) + return new ConfigItemSetImport( m_aChildSettings ); + if ( sLocalName == "config-item" ) + return new ConfigItemImport( m_aChildSettings ); + + SAL_WARN( "dbaccess", "unknown element name '" + << i_rElementName << "', ignoring"); + return new IgnoringSettingsImport; + } + + void ConfigItemSetImport::getItemValue( Any& o_rValue ) const + { + o_rValue <<= m_aChildSettings.getPropertyValues(); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/settingsimport.hxx b/dbaccess/source/core/recovery/settingsimport.hxx new file mode 100644 index 000000000..bc29bd727 --- /dev/null +++ b/dbaccess/source/core/recovery/settingsimport.hxx @@ -0,0 +1,163 @@ +/* -*- 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/xml/sax/XAttributeList.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <salhelper/simplereferenceobject.hxx> + +namespace dbaccess +{ + + // SettingsImport + /** a simplified version of xmloff/DocumentSettingsContext + + It would be nice if the DocumentSettingsContext would not be that tightly interwoven with the SvXMLImport + class, so we could re-use it here ... + */ + class SettingsImport : public salhelper::SimpleReferenceObject + { + public: + SettingsImport(); + + // own overridables + virtual ::rtl::Reference< SettingsImport > nextState( + const OUString& i_rElementName + ) = 0; + void startElement( + const css::uno::Reference< css::xml::sax::XAttributeList >& i_rAttributes + ); + virtual void endElement(); + void characters( std::u16string_view i_rCharacters ); + + protected: + virtual ~SettingsImport() override; + + protected: + static void split( const OUString& i_rElementName, OUString& o_rNamespace, OUString& o_rLocalName ); + + protected: + const OUString& getItemName() const { return m_sItemName; } + const OUString& getItemType() const { return m_sItemType; } + const OUStringBuffer& getAccumulatedCharacters() const { return m_aCharacters; } + + private: + // value of the config:name attribute, if any + OUString m_sItemName; + // value of the config:type attribute, if any + OUString m_sItemType; + // accumulated characters, if any + OUStringBuffer m_aCharacters; + }; + + // IgnoringSettingsImport + class IgnoringSettingsImport : public SettingsImport + { + public: + IgnoringSettingsImport() + { + } + + // SettingsImport overridables + virtual ::rtl::Reference< SettingsImport > nextState( + const OUString& i_rElementName + ) override; + + private: + virtual ~IgnoringSettingsImport() override + { + } + }; + + // OfficeSettingsImport + class OfficeSettingsImport : public SettingsImport + { + public: + explicit OfficeSettingsImport( ::comphelper::NamedValueCollection& o_rSettings ); + + // SettingsImport overridables + virtual ::rtl::Reference< SettingsImport > nextState( + const OUString& i_rElementName + ) override; + + protected: + virtual ~OfficeSettingsImport() override; + + private: + // the settings collection to which |this| will contribute a single setting + ::comphelper::NamedValueCollection& m_rSettings; + }; + + // ConfigItemSetImport + class ConfigItemImport : public SettingsImport + { + public: + explicit ConfigItemImport( ::comphelper::NamedValueCollection& o_rSettings ); + + protected: + virtual ~ConfigItemImport() override; + + public: + // SettingsImport overridables + virtual ::rtl::Reference< SettingsImport > nextState( + const OUString& i_rElementName + ) override; + virtual void endElement() override; + + protected: + // own overridables + /// retrieves the value represented by the element + virtual void getItemValue( css::uno::Any& o_rValue ) const; + + private: + // the settings collection to which |this| will contribute a single setting + ::comphelper::NamedValueCollection& m_rSettings; + }; + + // ConfigItemSetImport + class ConfigItemSetImport : public ConfigItemImport + { + public: + explicit ConfigItemSetImport( ::comphelper::NamedValueCollection& o_rSettings ); + + protected: + virtual ~ConfigItemSetImport() override; + + public: + // SettingsImport overridables + virtual ::rtl::Reference< SettingsImport > nextState( + const OUString& i_rElementName + ) override; + + protected: + // ConfigItemImport overridables + virtual void getItemValue( css::uno::Any& o_rValue ) const override; + + private: + /// the settings represented by our child elements + ::comphelper::NamedValueCollection m_aChildSettings; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagestream.cxx b/dbaccess/source/core/recovery/storagestream.cxx new file mode 100644 index 000000000..2831f406b --- /dev/null +++ b/dbaccess/source/core/recovery/storagestream.cxx @@ -0,0 +1,55 @@ +/* -*- 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 "storagestream.hxx" + +#include <com/sun/star/embed/ElementModes.hpp> + +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::embed::XStorage; + using ::com::sun::star::io::XStream; + + namespace ElementModes = ::com::sun::star::embed::ElementModes; + + // StorageOutputStream + StorageOutputStream::StorageOutputStream( const Reference< XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ) + { + ENSURE_OR_THROW( i_rParentStorage.is(), "illegal stream" ); + + const Reference< XStream > xStream( + i_rParentStorage->openStreamElement( i_rStreamName, ElementModes::READWRITE ), UNO_SET_THROW ); + m_xOutputStream.set( xStream->getOutputStream(), UNO_SET_THROW ); + } + + StorageOutputStream::~StorageOutputStream() + { + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagestream.hxx b/dbaccess/source/core/recovery/storagestream.hxx new file mode 100644 index 000000000..64c7e369d --- /dev/null +++ b/dbaccess/source/core/recovery/storagestream.hxx @@ -0,0 +1,51 @@ +/* -*- 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/embed/XStorage.hpp> + +namespace dbaccess +{ + + // StorageOutputStream + /** convenience wrapper around a stream living in a storage + */ + class StorageOutputStream + { + public: + StorageOutputStream( + const css::uno::Reference< css::embed::XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ); + virtual ~StorageOutputStream(); + + protected: + const css::uno::Reference< css::io::XOutputStream >& + getOutputStream() const { return m_xOutputStream; } + + private: + css::uno::Reference< css::io::XOutputStream > + m_xOutputStream; + }; + + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagetextstream.cxx b/dbaccess/source/core/recovery/storagetextstream.cxx new file mode 100644 index 000000000..32f68da9a --- /dev/null +++ b/dbaccess/source/core/recovery/storagetextstream.cxx @@ -0,0 +1,71 @@ +/* -*- 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 "storagetextstream.hxx" + +#include <com/sun/star/io/TextOutputStream.hpp> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::embed::XStorage; + using ::com::sun::star::io::TextOutputStream; + using ::com::sun::star::io::XTextOutputStream2; + + // StorageTextOutputStream_Data + struct StorageTextOutputStream_Data + { + Reference< XTextOutputStream2 > xTextOutput; + }; + + constexpr OUStringLiteral sLineFeed = u"\n"; + + // StorageTextOutputStream + StorageTextOutputStream::StorageTextOutputStream( const Reference<XComponentContext>& i_rContext, + const Reference< XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ) + :StorageOutputStream( i_rParentStorage, i_rStreamName ) + ,m_pData( new StorageTextOutputStream_Data ) + { + m_pData->xTextOutput = TextOutputStream::create( i_rContext ); + m_pData->xTextOutput->setEncoding( "UTF-8" ); + m_pData->xTextOutput->setOutputStream( getOutputStream() ); + } + + StorageTextOutputStream::~StorageTextOutputStream() + { + } + + void StorageTextOutputStream::writeLine( const OUString& i_rLine ) + { + m_pData->xTextOutput->writeString( i_rLine ); + m_pData->xTextOutput->writeString( sLineFeed ); + } + + void StorageTextOutputStream::writeLine() + { + m_pData->xTextOutput->writeString( sLineFeed ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagetextstream.hxx b/dbaccess/source/core/recovery/storagetextstream.hxx new file mode 100644 index 000000000..46c3e5b2e --- /dev/null +++ b/dbaccess/source/core/recovery/storagetextstream.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 "storagestream.hxx" + +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <memory> + +namespace dbaccess +{ + + // StorageTextStream + struct StorageTextOutputStream_Data; + class StorageTextOutputStream : public StorageOutputStream + { + public: + StorageTextOutputStream( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext, + const css::uno::Reference< css::embed::XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ); + virtual ~StorageTextOutputStream() override; + + void writeLine( const OUString& i_rLine ); + void writeLine(); + + private: + std::unique_ptr< StorageTextOutputStream_Data > m_pData; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagexmlstream.cxx b/dbaccess/source/core/recovery/storagexmlstream.cxx new file mode 100644 index 000000000..e76dabefe --- /dev/null +++ b/dbaccess/source/core/recovery/storagexmlstream.cxx @@ -0,0 +1,151 @@ +/* -*- 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 "storagexmlstream.hxx" + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> + +#include <rtl/ref.hxx> +#include <tools/diagnose_ex.h> +#include <xmloff/attrlist.hxx> + +#include <stack> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::embed::XStorage; + using ::com::sun::star::xml::sax::XDocumentHandler; + using ::com::sun::star::xml::sax::XWriter; + using ::com::sun::star::xml::sax::Writer; + using ::com::sun::star::xml::sax::Parser; + using ::com::sun::star::xml::sax::InputSource; + + // StorageXMLOutputStream_Data + struct StorageXMLOutputStream_Data + { + Reference< XDocumentHandler > xHandler; + std::stack< OUString > aElements; + ::rtl::Reference< SvXMLAttributeList > xAttributes; + }; + + // StorageXMLOutputStream + StorageXMLOutputStream::StorageXMLOutputStream( const Reference<XComponentContext>& i_rContext, + const Reference< XStorage >& i_rParentStorage, + const OUString& i_rStreamName ) + :StorageOutputStream( i_rParentStorage, i_rStreamName ) + ,m_pData( new StorageXMLOutputStream_Data ) + { + const Reference< XWriter > xSaxWriter = Writer::create( i_rContext ); + xSaxWriter->setOutputStream( getOutputStream() ); + + m_pData->xHandler.set( xSaxWriter, UNO_QUERY_THROW ); + m_pData->xHandler->startDocument(); + + m_pData->xAttributes = new SvXMLAttributeList; + } + + StorageXMLOutputStream::~StorageXMLOutputStream() + { + } + + void StorageXMLOutputStream::close() + { + ENSURE_OR_RETURN_VOID( m_pData->xHandler.is(), "illegal document handler" ); + m_pData->xHandler->endDocument(); + // do not call the base class, it would call closeOutput on the output stream, which is already done by + // endDocument + } + + void StorageXMLOutputStream::addAttribute( const OUString& i_rName, const OUString& i_rValue ) const + { + m_pData->xAttributes->AddAttribute( i_rName, i_rValue ); + } + + void StorageXMLOutputStream::startElement( const OUString& i_rElementName ) const + { + ENSURE_OR_RETURN_VOID( m_pData->xHandler.is(), "no document handler" ); + + m_pData->xHandler->startElement( i_rElementName, m_pData->xAttributes ); + m_pData->xAttributes = new SvXMLAttributeList; + m_pData->aElements.push( i_rElementName ); + } + + void StorageXMLOutputStream::endElement() const + { + ENSURE_OR_RETURN_VOID( m_pData->xHandler.is(), "no document handler" ); + ENSURE_OR_RETURN_VOID( !m_pData->aElements.empty(), "no element on the stack" ); + + const OUString sElementName( m_pData->aElements.top() ); + m_pData->xHandler->endElement( sElementName ); + m_pData->aElements.pop(); + } + + void StorageXMLOutputStream::ignorableWhitespace( const OUString& i_rWhitespace ) const + { + ENSURE_OR_RETURN_VOID( m_pData->xHandler.is(), "no document handler" ); + + m_pData->xHandler->ignorableWhitespace( i_rWhitespace ); + } + + void StorageXMLOutputStream::characters( const OUString& i_rCharacters ) const + { + ENSURE_OR_RETURN_VOID( m_pData->xHandler.is(), "no document handler" ); + + m_pData->xHandler->characters( i_rCharacters ); + } + + // StorageXMLInputStream + StorageXMLInputStream::StorageXMLInputStream( const Reference<XComponentContext>& i_rContext, + const Reference< XStorage >& i_rParentStorage, + const OUString& i_rStreamName ) + { + ENSURE_OR_THROW( i_rParentStorage.is(), "illegal stream" ); + + const Reference< css::io::XStream > xStream( + i_rParentStorage->openStreamElement( i_rStreamName, css::embed::ElementModes::READ ), css::uno::UNO_SET_THROW ); + m_xInputStream.set( xStream->getInputStream(), css::uno::UNO_SET_THROW ); + + m_xParser.set( Parser::create(i_rContext) ); + } + + void StorageXMLInputStream::import( const Reference< XDocumentHandler >& i_rHandler ) + { + ENSURE_OR_THROW( i_rHandler.is(), "illegal document handler (NULL)" ); + + InputSource aInputSource; + aInputSource.aInputStream = m_xInputStream; + + m_xParser->setDocumentHandler( i_rHandler ); + m_xParser->parseStream( aInputSource ); + } + + StorageXMLInputStream::~StorageXMLInputStream() + { + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/storagexmlstream.hxx b/dbaccess/source/core/recovery/storagexmlstream.hxx new file mode 100644 index 000000000..fd762ae05 --- /dev/null +++ b/dbaccess/source/core/recovery/storagexmlstream.hxx @@ -0,0 +1,88 @@ +/* -*- 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 "storagestream.hxx" + +#include <com/sun/star/xml/sax/XParser.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> + +#include <memory> + +namespace dbaccess +{ + + // StorageXMLOutputStream + struct StorageXMLOutputStream_Data; + class StorageXMLOutputStream : public StorageOutputStream + { + public: + StorageXMLOutputStream( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext, + const css::uno::Reference< css::embed::XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ); + virtual ~StorageXMLOutputStream() override; + + void close(); + + void addAttribute( const OUString& i_rName, const OUString& i_rValue ) const; + + void startElement( const OUString& i_rElementName ) const; + void endElement() const; + + void ignorableWhitespace( const OUString& i_rWhitespace ) const; + void characters( const OUString& i_rCharacters ) const; + + private: + StorageXMLOutputStream( const StorageXMLOutputStream& ) = delete; + StorageXMLOutputStream& operator=( const StorageXMLOutputStream& ) = delete; + + private: + std::unique_ptr< StorageXMLOutputStream_Data > m_pData; + }; + + class StorageXMLInputStream + { + public: + StorageXMLInputStream( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext, + const css::uno::Reference< css::embed::XStorage >& i_rParentStorage, + const OUString& i_rStreamName + ); + ~StorageXMLInputStream(); + + void import( + const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_rHandler + ); + + StorageXMLInputStream( const StorageXMLInputStream& ) = delete; + StorageXMLInputStream& operator=( const StorageXMLInputStream& ) = delete; + + private: + css::uno::Reference< css::xml::sax::XParser > m_xParser; + css::uno::Reference< css::io::XInputStream > m_xInputStream; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/subcomponentloader.cxx b/dbaccess/source/core/recovery/subcomponentloader.cxx new file mode 100644 index 000000000..3393c3d13 --- /dev/null +++ b/dbaccess/source/core/recovery/subcomponentloader.cxx @@ -0,0 +1,124 @@ +/* -*- 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 "subcomponentloader.hxx" + +#include <com/sun/star/ucb/Command.hpp> +#include <com/sun/star/frame/XController2.hpp> + +#include <tools/diagnose_ex.h> + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::frame::XController; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::awt::XWindow; + using ::com::sun::star::awt::WindowEvent; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::ucb::Command; + using ::com::sun::star::ucb::XCommandProcessor; + using ::com::sun::star::frame::XController2; + using ::com::sun::star::lang::XComponent; + + // SubComponentLoader + SubComponentLoader::SubComponentLoader( const Reference< XController >& i_rApplicationController, + const Reference< XCommandProcessor >& i_rSubDocumentDefinition ) + :mxDocDefCommands( i_rSubDocumentDefinition ) + { + // add as window listener to the controller's container window, so we get notified when it is shown + Reference< XController2 > xController( i_rApplicationController, UNO_QUERY_THROW ); + mxAppComponentWindow.set( xController->getComponentWindow(), UNO_SET_THROW ); + + osl_atomic_increment( &m_refCount ); + { + mxAppComponentWindow->addWindowListener( this ); + } + osl_atomic_decrement( &m_refCount ); + } + + SubComponentLoader::SubComponentLoader( const Reference< XController >& i_rApplicationController, + const Reference< XComponent >& i_rNonDocumentComponent ) + :mxNonDocComponent( i_rNonDocumentComponent ) + { + // add as window listener to the controller's container window, so we get notified when it is shown + Reference< XController2 > xController( i_rApplicationController, UNO_QUERY_THROW ); + mxAppComponentWindow.set( xController->getComponentWindow(), UNO_SET_THROW ); + + osl_atomic_increment( &m_refCount ); + { + mxAppComponentWindow->addWindowListener( this ); + } + osl_atomic_decrement( &m_refCount ); + } + + SubComponentLoader::~SubComponentLoader() + { + } + + void SAL_CALL SubComponentLoader::windowResized( const WindowEvent& ) + { + } + + void SAL_CALL SubComponentLoader::windowMoved( const WindowEvent& ) + { + } + + void SAL_CALL SubComponentLoader::windowShown( const EventObject& ) + { + try + { + if ( mxDocDefCommands.is() ) + { + Command aCommandOpen; + aCommandOpen.Name = "show"; + + const sal_Int32 nCommandIdentifier = mxDocDefCommands->createCommandIdentifier(); + mxDocDefCommands->execute( aCommandOpen, nCommandIdentifier, nullptr ); + } + else + { + const Reference< XController > xController( mxNonDocComponent, UNO_QUERY_THROW ); + const Reference< XFrame > xFrame( xController->getFrame(), UNO_SET_THROW ); + const Reference< XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); + xWindow->setVisible( true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + mxAppComponentWindow->removeWindowListener( this ); + } + + void SAL_CALL SubComponentLoader::windowHidden( const EventObject& ) + { + } + + void SAL_CALL SubComponentLoader::disposing( const EventObject& ) + { + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/subcomponentloader.hxx b/dbaccess/source/core/recovery/subcomponentloader.hxx new file mode 100644 index 000000000..4ab867880 --- /dev/null +++ b/dbaccess/source/core/recovery/subcomponentloader.hxx @@ -0,0 +1,71 @@ +/* -*- 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/awt/XWindowListener.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace dbaccess +{ + + // SubComponentLoader + typedef ::cppu::WeakImplHelper< css::awt::XWindowListener + > SubComponentLoader_Base; + /** is a helper class which loads/opens a given sub component as soon as the main application + window becomes visible. + */ + class SubComponentLoader : public SubComponentLoader_Base + { + public: + SubComponentLoader( + const css::uno::Reference< css::frame::XController >& i_rApplicationController, + const css::uno::Reference< css::ucb::XCommandProcessor >& i_rSubDocumentDefinition + ); + + SubComponentLoader( + const css::uno::Reference< css::frame::XController >& i_rApplicationController, + const css::uno::Reference< css::lang::XComponent >& i_rNonDocumentComponent + ); + + // XWindowListener + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + protected: + virtual ~SubComponentLoader() override; + + private: + const css::uno::Reference< css::ucb::XCommandProcessor > mxDocDefCommands; + const css::uno::Reference< css::lang::XComponent > mxNonDocComponent; + css::uno::Reference< css::awt::XWindow > mxAppComponentWindow; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/subcomponentrecovery.cxx b/dbaccess/source/core/recovery/subcomponentrecovery.cxx new file mode 100644 index 000000000..e7ffffd61 --- /dev/null +++ b/dbaccess/source/core/recovery/subcomponentrecovery.cxx @@ -0,0 +1,628 @@ +/* -*- 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 "subcomponentrecovery.hxx" + +#include <sdbcoretools.hxx> +#include "storagexmlstream.hxx" +#include "subcomponentloader.hxx" +#include "settingsimport.hxx" + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/sdb/XFormDocumentsSupplier.hpp> +#include <com/sun/star/sdb/XReportDocumentsSupplier.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/implbase.hxx> +#include <connectivity/dbtools.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <xmloff/XMLSettingsExportContext.hxx> +#include <xmloff/SettingsExportHelper.hxx> + +#include <stack> + +namespace dbaccess +{ + + using css::uno::Reference; + using css::uno::XInterface; + using css::uno::UNO_QUERY; + using css::uno::UNO_QUERY_THROW; + using css::uno::UNO_SET_THROW; + using css::uno::Exception; + using css::uno::RuntimeException; + using css::uno::Any; + using css::uno::Sequence; + using css::uno::XComponentContext; + using css::embed::XStorage; + using css::sdb::application::XDatabaseDocumentUI; + using css::beans::Pair; + using css::frame::ModuleManager; + using css::frame::XModuleManager2; + using css::lang::XComponent; + using css::frame::XModel; + using css::frame::XController; + using css::beans::XPropertySet; + using css::beans::PropertyValue; + using css::document::XStorageBasedDocument; + using css::ucb::XCommandProcessor; + using css::container::XHierarchicalNameAccess; + using css::sdb::XFormDocumentsSupplier; + using css::sdb::XReportDocumentsSupplier; + using css::xml::sax::XLocator; + using css::xml::sax::XDocumentHandler; + using css::xml::sax::XAttributeList; + + namespace ElementModes = css::embed::ElementModes; + namespace DatabaseObject = css::sdb::application::DatabaseObject; + + // helper + namespace + { + OUString lcl_getComponentStorageBaseName( const SubComponentType i_eType ) + { + switch ( i_eType ) + { + case FORM: + return "form"; + case REPORT: + return "report"; + case TABLE: + return "table"; + case QUERY: + return "query"; + default: + break; + } + + OSL_FAIL( "lcl_getComponentStorageBaseName: unimplemented case!" ); + return OUString(); + } + + SubComponentType lcl_databaseObjectToSubComponentType( const sal_Int32 i_nObjectType ) + { + switch ( i_nObjectType ) + { + case DatabaseObject::TABLE: return TABLE; + case DatabaseObject::QUERY: return QUERY; + case DatabaseObject::FORM: return FORM; + case DatabaseObject::REPORT:return REPORT; + default: + break; + } + return UNKNOWN; + } + + bool lcl_determineReadOnly( const Reference< XComponent >& i_rComponent ) + { + Reference< XModel > xDocument( i_rComponent, UNO_QUERY ); + if ( !xDocument.is() ) + { + Reference< XController > xController( i_rComponent, UNO_QUERY_THROW ); + xDocument = xController->getModel(); + } + + if ( !xDocument.is() ) + return false; + + ::comphelper::NamedValueCollection aDocArgs( xDocument->getArgs() ); + return aDocArgs.getOrDefault( "ReadOnly", false ); + } + + Reference< XCommandProcessor > lcl_getSubComponentDef_nothrow( const Reference< XDatabaseDocumentUI >& i_rAppUI, + const SubComponentType i_eType, const OUString& i_rName ) + { + Reference< XController > xController( i_rAppUI, UNO_QUERY_THROW ); + ENSURE_OR_RETURN( ( i_eType == FORM ) || ( i_eType == REPORT ), "lcl_getSubComponentDef_nothrow: illegal controller", nullptr ); + + Reference< XCommandProcessor > xCommandProcessor; + try + { + Reference< XHierarchicalNameAccess > xDefinitionContainer; + if ( i_eType == FORM ) + { + Reference< XFormDocumentsSupplier > xSuppForms( xController->getModel(), UNO_QUERY_THROW ); + xDefinitionContainer.set( xSuppForms->getFormDocuments(), UNO_QUERY_THROW ); + } + else + { + Reference< XReportDocumentsSupplier > xSuppReports( xController->getModel(), UNO_QUERY_THROW ); + xDefinitionContainer.set( xSuppReports->getReportDocuments(), UNO_QUERY_THROW ); + } + xCommandProcessor.set( xDefinitionContainer->getByHierarchicalName( i_rName ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return xCommandProcessor; + } + + constexpr OUStringLiteral sSettingsStreamName = u"settings.xml"; + constexpr OUStringLiteral sCurrentQueryDesignName = u"ooo:current-query-design"; + } + + namespace { + + // SettingsExportContext + class SettingsExportContext : public ::xmloff::XMLSettingsExportContext + { + public: + SettingsExportContext( const Reference<XComponentContext>& i_rContext, const StorageXMLOutputStream& i_rDelegator ) + :m_rContext( i_rContext ) + ,m_rDelegator( i_rDelegator ) + ,m_aNamespace( ::xmloff::token::GetXMLToken( ::xmloff::token::XML_NP_CONFIG ) ) + { + } + + virtual ~SettingsExportContext() + { + } + + public: + virtual void AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName, const OUString& i_rValue ) override; + virtual void AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName, enum ::xmloff::token::XMLTokenEnum i_eValue ) override; + virtual void StartElement( enum ::xmloff::token::XMLTokenEnum i_eName ) override; + virtual void EndElement ( const bool i_bIgnoreWhitespace ) override; + virtual void Characters( const OUString& i_rCharacters ) override; + + virtual css::uno::Reference< css::uno::XComponentContext > + GetComponentContext() const override; + + private: + OUString impl_prefix( const ::xmloff::token::XMLTokenEnum i_eToken ) + { + return m_aNamespace + ":" + ::xmloff::token::GetXMLToken( i_eToken ); + } + + private: + const Reference<XComponentContext>& m_rContext; + const StorageXMLOutputStream& m_rDelegator; + const OUString m_aNamespace; + }; + + } + + void SettingsExportContext::AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName, const OUString& i_rValue ) + { + m_rDelegator.addAttribute( impl_prefix( i_eName ), i_rValue ); + } + + void SettingsExportContext::AddAttribute( enum ::xmloff::token::XMLTokenEnum i_eName, enum ::xmloff::token::XMLTokenEnum i_eValue ) + { + m_rDelegator.addAttribute( impl_prefix( i_eName ), ::xmloff::token::GetXMLToken( i_eValue ) ); + } + + void SettingsExportContext::StartElement( enum ::xmloff::token::XMLTokenEnum i_eName ) + { + m_rDelegator.ignorableWhitespace( " " ); + + m_rDelegator.startElement( impl_prefix( i_eName ) ); + } + + void SettingsExportContext::EndElement( const bool i_bIgnoreWhitespace ) + { + if ( i_bIgnoreWhitespace ) + m_rDelegator.ignorableWhitespace( " " ); + m_rDelegator.endElement(); + } + + void SettingsExportContext::Characters( const OUString& i_rCharacters ) + { + m_rDelegator.characters( i_rCharacters ); + } + + Reference< css::uno::XComponentContext > SettingsExportContext::GetComponentContext() const + { + return m_rContext; + } + + // SettingsDocumentHandler + typedef ::cppu::WeakImplHelper< XDocumentHandler + > SettingsDocumentHandler_Base; + + namespace { + + class SettingsDocumentHandler : public SettingsDocumentHandler_Base + { + public: + SettingsDocumentHandler() + { + } + + protected: + virtual ~SettingsDocumentHandler() override + { + } + + public: + // XDocumentHandler + virtual void SAL_CALL startDocument( ) override; + virtual void SAL_CALL endDocument( ) override; + virtual void SAL_CALL startElement( const OUString& aName, const Reference< XAttributeList >& xAttribs ) override; + virtual void SAL_CALL endElement( const OUString& aName ) override; + virtual void SAL_CALL characters( const OUString& aChars ) override; + virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) override; + virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) override; + virtual void SAL_CALL setDocumentLocator( const Reference< XLocator >& xLocator ) override; + + const ::comphelper::NamedValueCollection& getSettings() const { return m_aSettings; } + + private: + std::stack< ::rtl::Reference< SettingsImport > > m_aStates; + ::comphelper::NamedValueCollection m_aSettings; + }; + + } + + void SAL_CALL SettingsDocumentHandler::startDocument( ) + { + } + + void SAL_CALL SettingsDocumentHandler::endDocument( ) + { + } + + void SAL_CALL SettingsDocumentHandler::startElement( const OUString& i_Name, const Reference< XAttributeList >& i_Attribs ) + { + ::rtl::Reference< SettingsImport > pNewState; + + if ( m_aStates.empty() ) + { + if ( i_Name == "office:settings" ) + { + pNewState = new OfficeSettingsImport( m_aSettings ); + } + else + { + OSL_FAIL( "SettingsDocumentHandler::startElement: invalid settings file!" ); + // Yes, that's not correct. Somebody could, in theory, give us a document which starts with "foo:settings", + // where "foo" is mapped to the proper namespace URL. + // However, there's no need to bother with this. The "recovery" sub storage we're recovering from is + // not part of ODF, so we can impose any format restrictions on it ... + } + } + else + { + ::rtl::Reference< SettingsImport > pCurrentState( m_aStates.top() ); + pNewState = pCurrentState->nextState( i_Name ); + } + + ENSURE_OR_THROW( pNewState.is(), "no new state - aborting import" ); + pNewState->startElement( i_Attribs ); + + m_aStates.push( pNewState ); + } + + void SAL_CALL SettingsDocumentHandler::endElement( const OUString& ) + { + ENSURE_OR_THROW( !m_aStates.empty(), "no active element" ); + + ::rtl::Reference< SettingsImport > pCurrentState( m_aStates.top() ); + pCurrentState->endElement(); + m_aStates.pop(); + } + + void SAL_CALL SettingsDocumentHandler::characters( const OUString& i_Chars ) + { + ENSURE_OR_THROW( !m_aStates.empty(), "no active element" ); + + ::rtl::Reference< SettingsImport > pCurrentState( m_aStates.top() ); + pCurrentState->characters( i_Chars ); + } + + void SAL_CALL SettingsDocumentHandler::ignorableWhitespace( const OUString& ) + { + // ignore them - that's why they're called "ignorable" + } + + void SAL_CALL SettingsDocumentHandler::processingInstruction( const OUString&, const OUString& ) + { + OSL_FAIL( "SettingsDocumentHandler::processingInstruction: unexpected ..." ); + } + + void SAL_CALL SettingsDocumentHandler::setDocumentLocator( const Reference< XLocator >& ) {} + + // SubComponentRecovery + OUString SubComponentRecovery::getComponentsStorageName( const SubComponentType i_eType ) + { + switch ( i_eType ) + { + case FORM: + return "forms"; + case REPORT: + return "reports"; + case TABLE: + return "tables"; + case QUERY: + return "queries"; + case RELATION_DESIGN: + return "relations"; + default: + break; + } + + OSL_FAIL( "SubComponentRecovery::getComponentsStorageName: unimplemented case!" ); + return OUString(); + } + + void SubComponentRecovery::saveToRecoveryStorage( const Reference< XStorage >& i_rRecoveryStorage, + MapCompTypeToCompDescs& io_mapCompDescs ) + { + if ( m_eType == UNKNOWN ) + // quite fatal, but has already been reported (as assertion) before + return; + + // open the sub storage for the given kind of components + const OUString& rStorageName( getComponentsStorageName( m_eType ) ); + const Reference< XStorage > xComponentsStorage( i_rRecoveryStorage->openStorageElement( + rStorageName, ElementModes::READWRITE ), UNO_SET_THROW ); + + // find a free sub storage name, and create Yet Another Sub Storage + const OUString& rBaseName( lcl_getComponentStorageBaseName( m_eType ) ); + const OUString sStorName = ::dbtools::createUniqueName( xComponentsStorage, rBaseName ); + const Reference< XStorage > xObjectStor( xComponentsStorage->openStorageElement( + sStorName, ElementModes::READWRITE ), UNO_SET_THROW ); + + switch ( m_eType ) + { + case FORM: + case REPORT: + impl_saveSubDocument_throw( xObjectStor ); + break; + + case QUERY: + impl_saveQueryDesign_throw( xObjectStor ); + break; + + default: + // TODO + OSL_FAIL( "SubComponentRecoverys::saveToRecoveryStorage: unimplemented case!" ); + break; + } + + // commit the storage(s) + tools::stor::commitStorageIfWriteable( xObjectStor ); + tools::stor::commitStorageIfWriteable( xComponentsStorage ); + + // remember the relationship from the component name to the storage name + MapStringToCompDesc& rMapCompDescs = io_mapCompDescs[ m_eType ]; + OSL_ENSURE( rMapCompDescs.find( sStorName ) == rMapCompDescs.end(), + "SubComponentRecoverys::saveToRecoveryStorage: object name already used!" ); + rMapCompDescs[ sStorName ] = m_aCompDesc; + } + + void SubComponentRecovery::impl_identifyComponent_throw() + { + // ask the controller + Pair< sal_Int32, OUString > aComponentIdentity = m_xDocumentUI->identifySubComponent( m_xComponent ); + m_eType = lcl_databaseObjectToSubComponentType( aComponentIdentity.First ); + m_aCompDesc.sName = aComponentIdentity.Second; + + // what the controller didn't give us is the information whether this is in edit mode or not ... + Reference< XModuleManager2 > xModuleManager( ModuleManager::create(m_rContext) ); + const OUString sModuleIdentifier = xModuleManager->identify( m_xComponent ); + + switch ( m_eType ) + { + case TABLE: + m_aCompDesc.bForEditing = sModuleIdentifier == "com.sun.star.sdb.TableDesign"; + break; + + case QUERY: + m_aCompDesc.bForEditing = sModuleIdentifier == "com.sun.star.sdb.QueryDesign"; + break; + + case REPORT: + if ( sModuleIdentifier == "com.sun.star.report.ReportDefinition" ) + { + // it's an SRB report designer + m_aCompDesc.bForEditing = true; + break; + } + [[fallthrough]]; + + case FORM: + m_aCompDesc.bForEditing = !lcl_determineReadOnly( m_xComponent ); + break; + + default: + if ( sModuleIdentifier == "com.sun.star.sdb.RelationDesign" ) + { + m_eType = RELATION_DESIGN; + m_aCompDesc.bForEditing = true; + } + else + { + OSL_FAIL( "SubComponentRecovery::impl_identifyComponent_throw: couldn't classify the given sub component!" ); + } + break; + } + + SAL_WARN_IF( m_eType == UNKNOWN, "dbaccess", + "SubComponentRecovery::impl_identifyComponent_throw: couldn't classify the component!" ); + } + + void SubComponentRecovery::impl_saveQueryDesign_throw( const Reference< XStorage >& i_rObjectStorage ) + { + ENSURE_OR_THROW( m_eType == QUERY, "illegal sub component type" ); + ENSURE_OR_THROW( i_rObjectStorage.is(), "illegal storage" ); + + // retrieve the current query design (which might differ from what we can retrieve as ActiveCommand property, since + // the latter is updated only upon successful save of the design) + Reference< XPropertySet > xDesignerProps( m_xComponent, UNO_QUERY_THROW ); + Sequence< PropertyValue > aCurrentQueryDesign; + OSL_VERIFY( xDesignerProps->getPropertyValue( "CurrentQueryDesign" ) >>= aCurrentQueryDesign ); + + // write the query design + StorageXMLOutputStream aDesignOutput( m_rContext, i_rObjectStorage, sSettingsStreamName ); + SettingsExportContext aSettingsExportContext( m_rContext, aDesignOutput ); + + static const OUStringLiteral sWhitespace( u" " ); + + aDesignOutput.startElement( "office:settings" ); + aDesignOutput.ignorableWhitespace( sWhitespace ); + + XMLSettingsExportHelper aSettingsExporter( aSettingsExportContext ); + aSettingsExporter.exportAllSettings( aCurrentQueryDesign, sCurrentQueryDesignName ); + + aDesignOutput.ignorableWhitespace( sWhitespace ); + aDesignOutput.endElement(); + aDesignOutput.close(); + } + + void SubComponentRecovery::impl_saveSubDocument_throw( const Reference< XStorage >& i_rObjectStorage ) + { + ENSURE_OR_THROW( ( m_eType == FORM ) || ( m_eType == REPORT ), "illegal sub component type" ); + ENSURE_OR_THROW( i_rObjectStorage.is(), "illegal storage" ); + + // store the document into the storage + Reference< XStorageBasedDocument > xStorageDocument( m_xComponent, UNO_QUERY_THROW ); + xStorageDocument->storeToStorage( i_rObjectStorage, Sequence< PropertyValue >() ); + } + + Reference< XComponent > SubComponentRecovery::impl_recoverSubDocument_throw( const Reference< XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, const bool i_bForEditing ) + { + Reference< XComponent > xSubComponent; + Reference< XCommandProcessor > xDocDefinition; + + ::comphelper::NamedValueCollection aLoadArgs; + aLoadArgs.put( "RecoveryStorage", i_rRecoveryStorage ); + + // load/create the sub component hidden. We'll show it when the main app window is shown. + aLoadArgs.put( "Hidden", true ); + + if ( !i_rComponentName.isEmpty() ) + { + xDocDefinition = lcl_getSubComponentDef_nothrow( m_xDocumentUI, m_eType, i_rComponentName ); + xSubComponent.set( m_xDocumentUI->loadComponentWithArguments( + m_eType, + i_rComponentName, + i_bForEditing, + aLoadArgs.getPropertyValues() + ), + UNO_SET_THROW + ); + } + else + { + Reference< XComponent > xDocDefComponent; + xSubComponent.set( m_xDocumentUI->createComponentWithArguments( + m_eType, + aLoadArgs.getPropertyValues(), + xDocDefComponent + ), + UNO_SET_THROW + ); + + xDocDefinition.set( xDocDefComponent, UNO_QUERY ); + OSL_ENSURE( xDocDefinition.is(), "DatabaseDocumentRecovery::recoverSubDocuments: loaded a form/report, but don't have a document definition?!" ); + } + + if ( xDocDefinition.is() ) + { + Reference< XController > xController( m_xDocumentUI, UNO_QUERY_THROW ); + rtl::Reference( new SubComponentLoader( xController, xDocDefinition ) ); + } + + return xSubComponent; + } + + Reference< XComponent > SubComponentRecovery::impl_recoverQueryDesign_throw( const Reference< XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, const bool i_bForEditing ) + { + Reference< XComponent > xSubComponent; + + // first read the settings query design settings from the storage + StorageXMLInputStream aDesignInput( m_rContext, i_rRecoveryStorage, sSettingsStreamName ); + + ::rtl::Reference< SettingsDocumentHandler > pDocHandler( new SettingsDocumentHandler ); + aDesignInput.import( pDocHandler ); + + const ::comphelper::NamedValueCollection& rSettings( pDocHandler->getSettings() ); + const Any& aCurrentQueryDesign = rSettings.get( sCurrentQueryDesignName ); +#if OSL_DEBUG_LEVEL > 0 + Sequence< PropertyValue > aQueryDesignLayout; + OSL_VERIFY( aCurrentQueryDesign >>= aQueryDesignLayout ); +#endif + + // then load the query designer + ::comphelper::NamedValueCollection aLoadArgs; + aLoadArgs.put( "CurrentQueryDesign", aCurrentQueryDesign ); + aLoadArgs.put( "Hidden", true ); + + if ( !i_rComponentName.isEmpty() ) + { + xSubComponent.set( m_xDocumentUI->loadComponentWithArguments( + m_eType, + i_rComponentName, + i_bForEditing, + aLoadArgs.getPropertyValues() + ), + UNO_SET_THROW + ); + } + else + { + Reference< XComponent > xDummy; + xSubComponent.set( m_xDocumentUI->createComponentWithArguments( + m_eType, + aLoadArgs.getPropertyValues(), + xDummy + ), + UNO_SET_THROW + ); + } + + Reference< XController > xController( m_xDocumentUI, UNO_QUERY_THROW ); + rtl::Reference( new SubComponentLoader( xController, xSubComponent ) ); + + return xSubComponent; + } + + Reference< XComponent > SubComponentRecovery::recoverFromStorage( const Reference< XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, const bool i_bForEditing ) + { + Reference< XComponent > xSubComponent; + switch ( m_eType ) + { + case FORM: + case REPORT: + xSubComponent = impl_recoverSubDocument_throw( i_rRecoveryStorage, i_rComponentName, i_bForEditing ); + break; + case QUERY: + xSubComponent = impl_recoverQueryDesign_throw( i_rRecoveryStorage, i_rComponentName, i_bForEditing ); + break; + default: + OSL_FAIL( "SubComponentRecovery::recoverFromStorage: unimplemented case!" ); + break; + } + return xSubComponent; + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/subcomponentrecovery.hxx b/dbaccess/source/core/recovery/subcomponentrecovery.hxx new file mode 100644 index 000000000..26b81c63f --- /dev/null +++ b/dbaccess/source/core/recovery/subcomponentrecovery.hxx @@ -0,0 +1,114 @@ +/* -*- 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 "subcomponents.hxx" + +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace dbaccess +{ + + // SubComponentRecovery + class SubComponentRecovery + { + public: + SubComponentRecovery( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext, + const css::uno::Reference< css::sdb::application::XDatabaseDocumentUI >& i_rController, + const css::uno::Reference< css::lang::XComponent >& i_rComponent ) + :m_rContext( i_rContext ) + ,m_xDocumentUI( i_rController, css::uno::UNO_SET_THROW ) + ,m_xComponent( i_rComponent ) + ,m_eType( UNKNOWN ) + ,m_aCompDesc() + { + impl_identifyComponent_throw(); + } + + SubComponentRecovery( + const css::uno::Reference< css::uno::XComponentContext >& i_rContext, + const css::uno::Reference< css::sdb::application::XDatabaseDocumentUI >& i_rController, + const SubComponentType i_eType ) + :m_rContext( i_rContext ) + ,m_xDocumentUI( i_rController, css::uno::UNO_SET_THROW ) + ,m_xComponent() + ,m_eType( i_eType ) + ,m_aCompDesc() + { + } + + // only to be used after being constructed with a component + void saveToRecoveryStorage( + const css::uno::Reference< css::embed::XStorage >& i_rRecoveryStorage, + MapCompTypeToCompDescs& io_mapCompDescs + ); + + // only to be used after being constructed with a type + css::uno::Reference< css::lang::XComponent > + recoverFromStorage( + const css::uno::Reference< css::embed::XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, + const bool i_bForEditing + ); + + static OUString getComponentsStorageName( const SubComponentType i_eType ); + + private: + void impl_saveSubDocument_throw( + const css::uno::Reference< css::embed::XStorage >& i_rObjectStorage + ); + + void impl_saveQueryDesign_throw( + const css::uno::Reference< css::embed::XStorage >& i_rObjectStorage + ); + + css::uno::Reference< css::lang::XComponent > + impl_recoverSubDocument_throw( + const css::uno::Reference< css::embed::XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, + const bool i_bForEditing + ); + + css::uno::Reference< css::lang::XComponent > + impl_recoverQueryDesign_throw( + const css::uno::Reference< css::embed::XStorage >& i_rRecoveryStorage, + const OUString& i_rComponentName, + const bool i_bForEditing + ); + + void impl_identifyComponent_throw(); + + private: + const css::uno::Reference< css::uno::XComponentContext >& + m_rContext; + css::uno::Reference< css::sdb::application::XDatabaseDocumentUI > + m_xDocumentUI; + const css::uno::Reference< css::lang::XComponent > + m_xComponent; + SubComponentType m_eType; + SubComponentDescriptor m_aCompDesc; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/recovery/subcomponents.hxx b/dbaccess/source/core/recovery/subcomponents.hxx new file mode 100644 index 000000000..5e4173605 --- /dev/null +++ b/dbaccess/source/core/recovery/subcomponents.hxx @@ -0,0 +1,61 @@ +/* -*- 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/sdb/application/DatabaseObject.hpp> + +#include <rtl/ustring.hxx> + +#include <map> +#include <unordered_map> + +namespace dbaccess +{ + + enum SubComponentType + { + TABLE = css::sdb::application::DatabaseObject::TABLE, + QUERY = css::sdb::application::DatabaseObject::QUERY, + FORM = css::sdb::application::DatabaseObject::FORM, + REPORT = css::sdb::application::DatabaseObject::REPORT, + + RELATION_DESIGN = 1000, + + UNKNOWN = 10001 + }; + + struct SubComponentDescriptor + { + OUString sName; + bool bForEditing; + + SubComponentDescriptor() + :sName() + ,bForEditing( false ) + { + } + }; + + typedef std::unordered_map< OUString, SubComponentDescriptor > MapStringToCompDesc; + typedef std::map< SubComponentType, MapStringToCompDesc > MapCompTypeToCompDescs; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/resource/core_resource.cxx b/dbaccess/source/core/resource/core_resource.cxx new file mode 100644 index 000000000..fdef07e1f --- /dev/null +++ b/dbaccess/source/core/resource/core_resource.cxx @@ -0,0 +1,41 @@ +/* -*- 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 <core_resource.hxx> + +#include <unotools/resmgr.hxx> + +namespace dbaccess +{ + OUString ResourceManager::loadString(TranslateId pResId) + { + return Translate::get(pResId, Translate::Create("dba")); + } + + OUString ResourceManager::loadString(TranslateId pResId, const char* _pPlaceholderAscii1, std::u16string_view _rReplace1, + const char* _pPlaceholderAscii2, std::u16string_view _rReplace2) + { + OUString sString(loadString(pResId)); + sString = sString.replaceFirst( OUString::createFromAscii(_pPlaceholderAscii1), _rReplace1 ); + sString = sString.replaceFirst( OUString::createFromAscii(_pPlaceholderAscii2), _rReplace2 ); + return sString; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |