From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- dbaccess/source/core/dataaccess/datasource.cxx | 1437 ++++++++++++++++++++++++ 1 file changed, 1437 insertions(+) create mode 100644 dbaccess/source/core/dataaccess/datasource.cxx (limited to 'dbaccess/source/core/dataaccess/datasource.cxx') 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 +#include +#include +#include +#include "connection.hxx" +#include "SharedConnection.hxx" +#include "databasedocument.hxx" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +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 getConnection( const OUString& url, + const OUString& user, + const OUString& password, + const Sequence< PropertyValue >& _aInfo, + ODatabaseSource* _pDataSource); + void addEventListener(const Reference& _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(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 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 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& _rxConnection, TConnectionMap::iterator const & _rIter) +{ + Reference 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& _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::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ::comphelper::concatSequences( + ODatabaseSource_Base::getTypes(), + aPropertyHelperTypes.getTypes() + ); +} + +Sequence< sal_Int8 > ODatabaseSource::getImplementationId() +{ + return css::uno::Sequence(); +} + +// 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(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 = getModel_noCreate(); + if (!xModel.is()) + return nullptr; + Reference xController(xModel->getCurrentController()); + if (!xController.is()) + return nullptr; + Reference xFrame(xController->getFrame()); + if (!xFrame.is()) + return nullptr; + Reference 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 const xRootStorage = m_pImpl->getOrCreateRootStorage(); + OUString sMigrEnvVal; + osl_getEnvironment(OUString("DBACCESS_HSQL_MIGRATION").pData, + &sMigrEnvVal.pData); + if(!sMigrEnvVal.isEmpty()) + bNeedMigration = true; + else + { + Reference 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>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISPASSWORDREQUIRED, PROPERTY_ID_ISPASSWORDREQUIRED, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_NUMBERFORMATSSUPPLIER, PROPERTY_ID_NUMBERFORMATSSUPPLIER, cppu::UnoType::get(), + css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_PASSWORD, PROPERTY_ID_PASSWORD, cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_SETTINGS, PROPERTY_ID_SETTINGS, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SUPPRESSVERSIONCL, PROPERTY_ID_SUPPRESSVERSIONCL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLEFILTER, PROPERTY_ID_TABLEFILTER, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLETYPEFILTER, PROPERTY_ID_TABLETYPEFILTER, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_URL, PROPERTY_ID_URL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_USER, PROPERTY_ID_USER, cppu::UnoType::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 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 +
  • removed from the bag, if it's a removable property
  • +
  • orreset to its default value, if it's not a removable property
  • +
. + + @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 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 pAbort = new OInteractionAbort; + rtl::Reference 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 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 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(&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 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 const &) +{ + css::uno::Reference inst( + DatabaseContext::create(context)->createInstance()); + inst->acquire(); + return inst.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3